1 /* 2 ***************************************************************************** 3 * Copyright (C) 2000-2004, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ***************************************************************************** 6 */ 7 package com.ibm.rbm; 8 9 import java.io.*; 10 import java.util.*; 11 import java.text.*; 12 13 import javax.swing.*; 14 import javax.xml.parsers.*; 15 import javax.xml.transform.*; 16 import javax.xml.transform.dom.*; 17 import javax.xml.transform.stream.*; 18 19 import org.w3c.dom.*; 20 21 /** 22 * This class is a plug-in to RBManager that allows the user to export Resource Bundles 23 * along with some of the meta-data associated by RBManager to the XLIFF specification. 24 * For more information on XLIFF visit the web site 25 * <a href="http://www.lisa.org/xliff/">http://www.lisa.org/xliff/</a> 26 * 27 * @author George Rhoten 28 * @see com.ibm.rbm.RBManager 29 */ 30 public class RBxliffExporter extends RBExporter { 31 private static final String VERSION = "0.7"; 32 private static final String XLIFF_DTD = "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd"; 33 private static final String XLIFF_PUBLIC_NAME = "-//XLIFF//DTD XLIFF//EN"; 34 private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 35 36 /** 37 * Default constructor for the XLIFF exporter. 38 */ 39 RBxliffExporter()40 public RBxliffExporter() { 41 super(); 42 43 // Initialize the file chooser if necessary 44 if (chooser == null) { 45 chooser = new JFileChooser(); 46 chooser.setFileFilter(new javax.swing.filechooser.FileFilter(){ 47 public String getDescription() { 48 return "XLIFF Files"; 49 } 50 public boolean accept(File f) { 51 return (f.isDirectory() || f.getName().endsWith(".xlf")); 52 } 53 }); 54 } 55 } 56 convertToISO(Date d)57 private String convertToISO(Date d) { 58 GregorianCalendar gc = new GregorianCalendar(); 59 gc.setTime(d); 60 return convertToISO(gc); 61 } 62 convertToISO(GregorianCalendar gc)63 private String convertToISO(GregorianCalendar gc) { 64 dateFormat.setCalendar(gc); 65 return dateFormat.format(gc.getTime()); 66 } 67 getLocale(Bundle item)68 private String getLocale(Bundle item) { 69 String language = item.getLanguageEncoding(); 70 if (language != null && !language.equals("")) { 71 //language = language.toUpperCase(); 72 String country = item.getCountryEncoding(); 73 if (country != null && !country.equals("")) { 74 //country = country.toUpperCase(); 75 String variant = item.getVariantEncoding(); 76 if (variant != null && !variant.equals("")) { 77 //variant = variant.toUpperCase(); 78 return language + "-" + country + "-" + variant; 79 } 80 return language + "-" + country; 81 } 82 return language; 83 } 84 return ""; 85 } 86 getParentLocale(String locale)87 private String getParentLocale(String locale) { 88 89 int truncIndex = locale.lastIndexOf('-'); 90 if (truncIndex > 0) { 91 locale = locale.substring(0, truncIndex); 92 } 93 else { 94 locale = ""; 95 } 96 return locale; 97 } 98 addTransUnit(Document xml, Element groupElem, BundleItem item, BundleItem parent_item)99 private void addTransUnit(Document xml, Element groupElem, BundleItem item, BundleItem parent_item) { 100 Element transUnit = xml.createElement("trans-unit"); 101 transUnit.setAttribute("date",convertToISO(item.getModifiedDate())); 102 transUnit.setAttribute("id",item.getKey()); 103 104 String sourceOrTarget = "target"; 105 if (parent_item == null) { 106 sourceOrTarget = "source"; 107 } 108 else { 109 Element source = xml.createElement("source"); 110 source.setAttribute("xml:space","preserve"); 111 source.appendChild(xml.createTextNode(parent_item.getTranslation())); 112 transUnit.appendChild(source); 113 } 114 Element target = xml.createElement(sourceOrTarget); 115 target.setAttribute("xml:space","preserve"); 116 // This is different from the translate attribute 117 if (item.isTranslated()) { 118 // TODO Handle the other states in the future. 119 transUnit.setAttribute("state", "translated"); 120 } 121 target.appendChild(xml.createTextNode(item.getTranslation())); 122 transUnit.appendChild(target); 123 124 if (item.getComment() != null && item.getComment().length() > 1) { 125 Element comment_prop = xml.createElement("note"); 126 comment_prop.setAttribute("xml:space","preserve"); 127 comment_prop.appendChild(xml.createTextNode(item.getComment())); 128 transUnit.appendChild(comment_prop); 129 } 130 131 if ((item.getCreator() != null && item.getCreator().length() > 1) 132 || (item.getModifier() != null && item.getModifier().length() > 1)) 133 { 134 Element transUnit_prop_group_elem = xml.createElement("prop-group"); 135 136 if (item.getCreator() != null && item.getCreator().length() > 1) { 137 Element creator_prop = xml.createElement("prop"); 138 creator_prop.setAttribute("prop-type","creator"); 139 creator_prop.appendChild(xml.createTextNode(item.getCreator())); 140 transUnit_prop_group_elem.appendChild(creator_prop); 141 } 142 143 if (item.getCreator() != null && item.getCreator().length() > 1) { 144 Element created_prop = xml.createElement("prop"); 145 created_prop.setAttribute("prop-type","created"); 146 created_prop.appendChild(xml.createTextNode(convertToISO(item.getCreatedDate()))); 147 transUnit_prop_group_elem.appendChild(created_prop); 148 } 149 150 if (item.getModifier() != null && item.getModifier().length() > 1) { 151 Element modifier_prop = xml.createElement("prop"); 152 modifier_prop.setAttribute("prop-type","modifier"); 153 modifier_prop.appendChild(xml.createTextNode(item.getModifier())); 154 transUnit_prop_group_elem.appendChild(modifier_prop); 155 } 156 157 transUnit.appendChild(transUnit_prop_group_elem); 158 } 159 160 groupElem.appendChild(transUnit); 161 } 162 export(RBManager rbm)163 public void export(RBManager rbm) throws IOException { 164 if (rbm == null) 165 return; 166 // Open the Save Dialog 167 int ret_val = chooser.showSaveDialog(null); 168 if (ret_val != JFileChooser.APPROVE_OPTION) 169 return; 170 // Retrieve basic file information 171 File file = chooser.getSelectedFile(); // The file(s) we will be working with 172 File directory = new File(file.getParent()); // The directory we will be writing to 173 String base_name = file.getName(); // The base name of the files we will write 174 if (base_name == null || base_name.equals("")) 175 base_name = rbm.getBaseClass(); 176 if (base_name.endsWith(".xlf")) 177 base_name = base_name.substring(0,base_name.length()-4); 178 179 String file_name = base_name + ".xlf"; 180 181 Vector bundle_v = rbm.getBundles(); 182 Enumeration bundleIter = bundle_v.elements(); 183 while (bundleIter.hasMoreElements()) { 184 exportFile(rbm, directory, base_name, (Bundle)bundleIter.nextElement()); 185 } 186 } 187 addHeaderProperties(Document xml, Element header, Bundle main_bundle)188 private void addHeaderProperties(Document xml, Element header, Bundle main_bundle) { 189 if (main_bundle.comment != null && main_bundle.comment.length() > 0) { 190 Element note = xml.createElement("note"); 191 header.appendChild(note); 192 note.appendChild(xml.createTextNode(main_bundle.comment)); 193 note.setAttribute("xml:space","preserve"); 194 } 195 if ((main_bundle.name != null && main_bundle.name.length() > 0) 196 || (main_bundle.manager != null && main_bundle.manager.length() > 0) 197 || (main_bundle.language != null && main_bundle.language.length() > 0) 198 || (main_bundle.country != null && main_bundle.country.length() > 0) 199 || (main_bundle.variant != null && main_bundle.variant.length() > 0)) 200 { 201 Element prop_group = xml.createElement("prop-group"); 202 header.appendChild(prop_group); 203 if (main_bundle.name != null && main_bundle.name.length() > 0) { 204 Element prop = xml.createElement("prop"); 205 header.appendChild(prop); 206 prop.setAttribute("xml:space","preserve"); 207 prop.setAttribute("prop-type","name"); 208 prop.appendChild(xml.createTextNode(main_bundle.name)); 209 prop_group.appendChild(prop); 210 } 211 if (main_bundle.manager != null && main_bundle.manager.length() > 0) { 212 Element prop = xml.createElement("prop"); 213 header.appendChild(prop); 214 prop.setAttribute("xml:space","preserve"); 215 prop.setAttribute("prop-type","manager"); 216 prop.appendChild(xml.createTextNode(main_bundle.manager)); 217 prop_group.appendChild(prop); 218 } 219 if (main_bundle.language != null && main_bundle.language.length() > 0) { 220 Element prop = xml.createElement("prop"); 221 header.appendChild(prop); 222 prop.setAttribute("xml:space","preserve"); 223 prop.setAttribute("prop-type","language"); 224 prop.appendChild(xml.createTextNode(main_bundle.language)); 225 prop_group.appendChild(prop); 226 } 227 if (main_bundle.country != null && main_bundle.country.length() > 0) { 228 Element prop = xml.createElement("prop"); 229 header.appendChild(prop); 230 prop.setAttribute("xml:space","preserve"); 231 prop.setAttribute("prop-type","country"); 232 prop.appendChild(xml.createTextNode(main_bundle.country)); 233 prop_group.appendChild(prop); 234 } 235 if (main_bundle.variant != null && main_bundle.variant.length() > 0) { 236 Element prop = xml.createElement("prop"); 237 header.appendChild(prop); 238 prop.setAttribute("xml:space","preserve"); 239 prop.setAttribute("prop-type","variant"); 240 prop.appendChild(xml.createTextNode(main_bundle.variant)); 241 prop_group.appendChild(prop); 242 } 243 } 244 } 245 exportFile(RBManager rbm, File directory, String base_name, Bundle main_bundle)246 private void exportFile(RBManager rbm, File directory, String base_name, Bundle main_bundle) 247 throws IOException 248 { 249 Bundle parent_bundle = null; 250 String parent_bundle_name = null; 251 if (!getLocale(main_bundle).equals("")) { 252 // If this isn't the root locale, find the parent 253 parent_bundle_name = getParentLocale(getLocale(main_bundle)); 254 do { 255 parent_bundle = rbm.getBundle(parent_bundle_name); 256 if (parent_bundle != null) { 257 break; 258 } 259 parent_bundle_name = getParentLocale(parent_bundle_name); 260 } while (!parent_bundle_name.equals("")); 261 } 262 263 264 // Find the implementation 265 DocumentBuilder builder; 266 try { 267 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 268 } 269 catch (ParserConfigurationException pce) { 270 throw new IOException(pce.getMessage()); 271 } 272 273 // Create the document 274 Document xml = builder.getDOMImplementation().createDocument(null, "xliff", null); 275 276 // Fill the document 277 Element root = xml.getDocumentElement(); 278 root.setAttribute("version", "1.1"); 279 //root.appendChild(root); 280 281 Element file_elem = xml.createElement("file"); 282 String mainLocale = getLocale(main_bundle); 283 Bundle parentBundle = null; 284 if (mainLocale.equals("")) { 285 file_elem.setAttribute("source-language", getLocale(main_bundle)); 286 } 287 else { 288 file_elem.setAttribute("source-language", parent_bundle_name); 289 file_elem.setAttribute("target-language", getLocale(main_bundle)); 290 } 291 file_elem.setAttribute("datatype", "plaintext"); 292 file_elem.setAttribute("date", convertToISO(new Date())); 293 root.appendChild(file_elem); 294 295 Element header = xml.createElement("header"); 296 Element tool = xml.createElement("tool"); 297 tool.setAttribute("tool-name", "RBManager"); 298 tool.setAttribute("tool-id", "RBManager"); 299 tool.setAttribute("tool-version", VERSION); 300 // TODO Add file attribute 301 //header.setAttribute("file", ""); 302 header.appendChild(tool); 303 addHeaderProperties(xml, header, main_bundle); 304 file_elem.appendChild(header); 305 306 Element body = xml.createElement("body"); 307 file_elem.appendChild(body); 308 309 Vector group_v = main_bundle.getGroupsAsVector(); 310 Vector parent_group_v = null; 311 if (parent_bundle != null) { 312 parent_group_v = parent_bundle.getGroupsAsVector(); 313 } 314 // Loop through each bundle group in main_bundle 315 for (int i=0; i < group_v.size(); i++) { 316 BundleGroup curr_group = (BundleGroup)group_v.elementAt(i); 317 BundleGroup parent_group = null; 318 if (parent_group_v != null) { 319 Enumeration parentGroupIter = parent_group_v.elements(); 320 321 while (parentGroupIter.hasMoreElements()) { 322 BundleGroup groupToFind = (BundleGroup)parentGroupIter.nextElement(); 323 if (groupToFind.getName().equals(curr_group.getName())) { 324 parent_group = groupToFind; 325 break; 326 } 327 } 328 } 329 Element group_elem = xml.createElement("group"); 330 group_elem.setAttribute("id", curr_group.getName()); 331 if (curr_group.getComment() != null && curr_group.getComment().length() > 1) { 332 Element comment_prop = xml.createElement("note"); 333 comment_prop.setAttribute("xml:space","preserve"); 334 comment_prop.appendChild(xml.createTextNode(curr_group.getComment())); 335 group_elem.appendChild(comment_prop); 336 } 337 338 Vector group_items = curr_group.getItemsAsVector(); 339 for (int j=0; j < group_items.size(); j++) { 340 BundleItem main_item = (BundleItem)group_items.get(j); 341 BundleItem parent_item = null; 342 if (parent_group != null) { 343 Enumeration parentIter = parent_group.getItemsAsVector().elements(); 344 BundleItem itemToFind = null; 345 while (parentIter.hasMoreElements()) { 346 itemToFind = (BundleItem)parentIter.nextElement(); 347 if (itemToFind.getKey().equals(main_item.getKey())) { 348 parent_item = itemToFind; 349 break; 350 } 351 } 352 } 353 addTransUnit(xml, group_elem, main_item, parent_item); 354 //group_elem.appendChild(tu); 355 } 356 body.appendChild(group_elem); 357 } // end for - i 358 String suffix = mainLocale; 359 if (!suffix.equals("")) { 360 suffix = '_' + suffix; 361 } 362 char array[] = suffix.toCharArray(); 363 for (int k=0; k < array.length; k++) { 364 if (array[k] == '-') 365 array[k] = '_'; 366 } 367 suffix = String.valueOf(array); 368 369 // serialize document 370 OutputStreamWriter osw = new OutputStreamWriter( 371 new FileOutputStream(new File(directory, base_name + suffix + ".xlf")), "UTF-8"); 372 try { 373 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 374 transformer.setOutputProperty(OutputKeys.METHOD, "xml"); 375 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 376 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); 377 transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, XLIFF_DTD); 378 transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, XLIFF_PUBLIC_NAME); 379 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 380 transformer.transform(new DOMSource(xml), new StreamResult(osw)); 381 } 382 catch (TransformerException te) { 383 throw new IOException(te.getMessage()); 384 } 385 386 osw.close(); 387 } 388 } 389