1 /* GNU gettext for Java 2 * Copyright (C) 2001-2003, 2007 Free Software Foundation, Inc. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18 package gnu.gettext; 19 20 import java.lang.reflect.*; 21 import java.util.*; 22 import java.io.*; 23 24 /** 25 * This programs dumps a resource as a PO file. The resource must be 26 * accessible through the CLASSPATH. 27 * 28 * @author Bruno Haible 29 */ 30 public class DumpResource { 31 private Writer out; dumpString(String str)32 private void dumpString (String str) throws IOException { 33 int n = str.length(); 34 out.write('"'); 35 for (int i = 0; i < n; i++) { 36 char c = str.charAt(i); 37 if (c == 0x0008) { 38 out.write('\\'); out.write('b'); 39 } else if (c == 0x000c) { 40 out.write('\\'); out.write('f'); 41 } else if (c == 0x000a) { 42 out.write('\\'); out.write('n'); 43 } else if (c == 0x000d) { 44 out.write('\\'); out.write('r'); 45 } else if (c == 0x0009) { 46 out.write('\\'); out.write('t'); 47 } else if (c == '\\' || c == '"') { 48 out.write('\\'); out.write(c); 49 } else 50 out.write(c); 51 } 52 out.write('"'); 53 } dumpMessage(String msgid, String msgid_plural, Object msgstr)54 private void dumpMessage (String msgid, String msgid_plural, Object msgstr) throws IOException { 55 int separatorPos = msgid.indexOf('\u0004'); 56 if (separatorPos >= 0) { 57 String msgctxt = msgid.substring(0,separatorPos); 58 msgid = msgid.substring(separatorPos+1); 59 out.write("msgctxt "); dumpString(msgctxt); 60 } 61 out.write("msgid "); dumpString(msgid); out.write('\n'); 62 if (msgid_plural != null) { 63 out.write("msgid_plural "); dumpString(msgid_plural); out.write('\n'); 64 for (int i = 0; i < ((String[])msgstr).length; i++) { 65 out.write("msgstr[" + i + "] "); 66 dumpString(((String[])msgstr)[i]); 67 out.write('\n'); 68 } 69 } else { 70 out.write("msgstr "); dumpString((String)msgstr); out.write('\n'); 71 } 72 out.write('\n'); 73 } 74 private ResourceBundle catalog; 75 private Method lookupMethod; 76 // Lookup the value corresponding to a key found in catalog.getKeys(). 77 // Here we assume that the catalog returns a non-inherited value for 78 // these keys. FIXME: Not true. Better see whether handleGetObject is 79 // public - it is in ListResourceBundle and PropertyResourceBundle. lookup(String key)80 private Object lookup (String key) { 81 Object value = null; 82 if (lookupMethod != null) { 83 try { 84 value = lookupMethod.invoke(catalog, new Object[] { key }); 85 } catch (IllegalAccessException e) { 86 e.printStackTrace(); 87 } catch (InvocationTargetException e) { 88 e.getTargetException().printStackTrace(); 89 } 90 } else { 91 try { 92 value = catalog.getObject(key); 93 } catch (MissingResourceException e) { 94 } 95 } 96 return value; 97 } dump()98 private void dump () throws IOException { 99 lookupMethod = null; 100 try { 101 lookupMethod = catalog.getClass().getMethod("lookup", new Class[] { java.lang.String.class }); 102 } catch (NoSuchMethodException e) { 103 } catch (SecurityException e) { 104 } 105 Method pluralMethod = null; 106 try { 107 pluralMethod = catalog.getClass().getMethod("get_msgid_plural_table", new Class[0]); 108 } catch (NoSuchMethodException e) { 109 } catch (SecurityException e) { 110 } 111 Field pluralField = null; 112 try { 113 pluralField = catalog.getClass().getField("plural"); 114 } catch (NoSuchFieldException e) { 115 } catch (SecurityException e) { 116 } 117 // Search for the header entry. 118 { 119 Object header_entry = null; 120 Enumeration keys = catalog.getKeys(); 121 while (keys.hasMoreElements()) 122 if ("".equals(keys.nextElement())) { 123 header_entry = lookup(""); 124 break; 125 } 126 // If there is no header entry, fake one. 127 // FIXME: This is not needed; right after po_lex_charset_init set 128 // the PO charset to UTF-8. 129 if (header_entry == null) 130 header_entry = "Content-Type: text/plain; charset=UTF-8\n"; 131 dumpMessage("",null,header_entry); 132 } 133 // Now the other messages. 134 { 135 Enumeration keys = catalog.getKeys(); 136 Object plural = null; 137 if (pluralMethod != null) { 138 // msgfmt versions > 0.13.1 create a static get_msgid_plural_table() 139 // method. 140 try { 141 plural = pluralMethod.invoke(catalog, new Object[0]); 142 } catch (IllegalAccessException e) { 143 e.printStackTrace(); 144 } catch (InvocationTargetException e) { 145 e.getTargetException().printStackTrace(); 146 } 147 } else if (pluralField != null) { 148 // msgfmt versions <= 0.13.1 create a static plural field. 149 try { 150 plural = pluralField.get(catalog); 151 } catch (IllegalAccessException e) { 152 e.printStackTrace(); 153 } 154 } 155 if (plural instanceof String[]) { 156 // A GNU gettext created class with plural handling, Java2 format. 157 int i = 0; 158 while (keys.hasMoreElements()) { 159 String key = (String)keys.nextElement(); 160 Object value = lookup(key); 161 String key_plural = (value instanceof String[] ? ((String[])plural)[i++] : null); 162 if (!"".equals(key)) 163 dumpMessage(key,key_plural,value); 164 } 165 if (i != ((String[])plural).length) 166 throw new RuntimeException("wrong plural field length"); 167 } else if (plural instanceof Hashtable) { 168 // A GNU gettext created class with plural handling, Java format. 169 while (keys.hasMoreElements()) { 170 String key = (String)keys.nextElement(); 171 if (!"".equals(key)) { 172 Object value = lookup(key); 173 String key_plural = (value instanceof String[] ? (String)((Hashtable)plural).get(key) : null); 174 dumpMessage(key,key_plural,value); 175 } 176 } 177 } else if (plural == null) { 178 // No plural handling. 179 while (keys.hasMoreElements()) { 180 String key = (String)keys.nextElement(); 181 if (!"".equals(key)) 182 dumpMessage(key,null,lookup(key)); 183 } 184 } else 185 throw new RuntimeException("wrong plural field value"); 186 } 187 } 188 DumpResource(String resource_name, String locale_name)189 public DumpResource (String resource_name, String locale_name) { 190 // Split locale_name into language_country_variant. 191 String language; 192 String country; 193 String variant; 194 language = locale_name; 195 { 196 int i = language.indexOf('_'); 197 if (i >= 0) { 198 country = language.substring(i+1); 199 language = language.substring(0,i); 200 } else 201 country = ""; 202 } 203 { 204 int j = country.indexOf('_'); 205 if (j >= 0) { 206 variant = country.substring(j+1); 207 country = country.substring(0,j); 208 } else 209 variant = ""; 210 } 211 Locale locale = new Locale(language,country,variant); 212 // Get the resource. 213 ResourceBundle catalog = ResourceBundle.getBundle(resource_name,locale); 214 // We are only interested in the messsages belonging to the locale 215 // itself, not in the inherited messages. But catalog.getLocale() exists 216 // only in Java2 and sometimes differs from the given locale. 217 try { 218 Writer w1 = new OutputStreamWriter(System.out,"UTF8"); 219 Writer w2 = new BufferedWriter(w1); 220 this.out = w2; 221 this.catalog = catalog; 222 dump(); 223 w2.close(); 224 w1.close(); 225 System.out.flush(); 226 } catch (IOException e) { 227 e.printStackTrace(); 228 System.exit(1); 229 } 230 } 231 main(String[] args)232 public static void main (String[] args) { 233 new DumpResource(args[0], args.length > 1 ? args[1] : ""); 234 System.exit(0); 235 } 236 } 237