1 /* GNU gettext for Java 2 * Copyright (C) 2001 Free Software Foundation, Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU Library General Public License as published 6 * by the Free Software Foundation; either version 2, or (at your option) 7 * 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 GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public 15 * License along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 17 * USA. 18 */ 19 20 package gnu.gettext; 21 22 import java.lang.reflect.*; 23 import java.util.*; 24 25 /** 26 * This class implements the main GNU libintl functions in Java. 27 * <P> 28 * Using the GNU gettext approach, compiled message catalogs are normal 29 * Java ResourceBundle classes and are thus interoperable with standard 30 * ResourceBundle based code. 31 * <P> 32 * The main differences between the Sun ResourceBundle approach and the 33 * GNU gettext approach are: 34 * <UL> 35 * <LI>In the Sun approach, the keys are abstract textual shortcuts. 36 * In the GNU gettext approach, the keys are the English/ASCII version 37 * of the messages. 38 * <LI>In the Sun approach, the translation files are called 39 * "<VAR>Resource</VAR>_<VAR>locale</VAR>.properties" and have non-ASCII 40 * characters encoded in the Java 41 * <CODE>\</CODE><CODE>u<VAR>nnnn</VAR></CODE> syntax. Very few editors 42 * can natively display international characters in this format. In the 43 * GNU gettext approach, the translation files are called 44 * "<VAR>Resource</VAR>.<VAR>locale</VAR>.po" 45 * and are in the encoding the translator has chosen. Many editors 46 * can be used. There are at least three GUI translating tools 47 * (Emacs PO mode, KDE KBabel, GNOME gtranslator). 48 * <LI>In the Sun approach, the function 49 * <CODE>ResourceBundle.getString</CODE> throws a 50 * <CODE>MissingResourceException</CODE> when no translation is found. 51 * In the GNU gettext approach, the <CODE>gettext</CODE> function 52 * returns the (English) message key in that case. 53 * <LI>In the Sun approach, there is no support for plural handling. 54 * Even the most elaborate MessageFormat strings cannot provide decent 55 * plural handling. In the GNU gettext approach, we have the 56 * <CODE>ngettext</CODE> function. 57 * </UL> 58 * <P> 59 * To compile GNU gettext message catalogs into Java ResourceBundle classes, 60 * the <CODE>msgfmt</CODE> program can be used. 61 * 62 * @author Bruno Haible 63 */ 64 public abstract class GettextResource extends ResourceBundle { 65 66 public static boolean verbose = false; 67 68 /** 69 * Returns the translation of <VAR>msgid</VAR>. 70 * @param catalog a ResourceBundle 71 * @param msgid the key string to be translated, an ASCII string 72 * @return the translation of <VAR>msgid</VAR>, or <VAR>msgid</VAR> if 73 * none is found 74 */ gettext(ResourceBundle catalog, String msgid)75 public static String gettext (ResourceBundle catalog, String msgid) { 76 try { 77 String result = (String)catalog.getObject(msgid); 78 if (result != null) 79 return result; 80 } catch (MissingResourceException e) { 81 } 82 return msgid; 83 } 84 85 /** 86 * Returns the plural form for <VAR>n</VAR> of the translation of 87 * <VAR>msgid</VAR>. 88 * @param catalog a ResourceBundle 89 * @param msgid the key string to be translated, an ASCII string 90 * @param msgid_plural its English plural form 91 * @return the translation of <VAR>msgid</VAR> depending on <VAR>n</VAR>, 92 * or <VAR>msgid</VAR> or <VAR>msgid_plural</VAR> if none is found 93 */ ngettext(ResourceBundle catalog, String msgid, String msgid_plural, long n)94 public static String ngettext (ResourceBundle catalog, String msgid, String msgid_plural, long n) { 95 // The reason why we use so many reflective API calls instead of letting 96 // the GNU gettext generated ResourceBundles implement some interface, 97 // is that we want the generated ResourceBundles to be completely 98 // standalone, so that migration from the Sun approach to the GNU gettext 99 // approach (without use of plurals) is as straightforward as possible. 100 ResourceBundle origCatalog = catalog; 101 do { 102 // Try catalog itself. 103 if (verbose) 104 System.out.println("ngettext on "+catalog); 105 Method handleGetObjectMethod = null; 106 Method getParentMethod = null; 107 try { 108 handleGetObjectMethod = catalog.getClass().getMethod("handleGetObject", new Class[] { java.lang.String.class }); 109 getParentMethod = catalog.getClass().getMethod("getParent", new Class[0]); 110 } catch (NoSuchMethodException e) { 111 } catch (SecurityException e) { 112 } 113 if (verbose) 114 System.out.println("handleGetObject = "+(handleGetObjectMethod!=null)+", getParent = "+(getParentMethod!=null)); 115 if (handleGetObjectMethod != null 116 && Modifier.isPublic(handleGetObjectMethod.getModifiers()) 117 && getParentMethod != null) { 118 // A GNU gettext created class. 119 Method lookupMethod = null; 120 Method pluralEvalMethod = null; 121 try { 122 lookupMethod = catalog.getClass().getMethod("lookup", new Class[] { java.lang.String.class }); 123 pluralEvalMethod = catalog.getClass().getMethod("pluralEval", new Class[] { Long.TYPE }); 124 } catch (NoSuchMethodException e) { 125 } catch (SecurityException e) { 126 } 127 if (verbose) 128 System.out.println("lookup = "+(lookupMethod!=null)+", pluralEval = "+(pluralEvalMethod!=null)); 129 if (lookupMethod != null && pluralEvalMethod != null) { 130 // A GNU gettext created class with plural handling. 131 Object localValue = null; 132 try { 133 localValue = lookupMethod.invoke(catalog, new Object[] { msgid }); 134 } catch (IllegalAccessException e) { 135 e.printStackTrace(); 136 } catch (InvocationTargetException e) { 137 e.getTargetException().printStackTrace(); 138 } 139 if (localValue != null) { 140 if (verbose) 141 System.out.println("localValue = "+localValue); 142 if (localValue instanceof String) 143 // Found the value. It doesn't depend on n in this case. 144 return (String)localValue; 145 else { 146 String[] pluralforms = (String[])localValue; 147 long i = 0; 148 try { 149 i = ((Long) pluralEvalMethod.invoke(catalog, new Object[] { new Long(n) })).longValue(); 150 if (!(i >= 0 && i < pluralforms.length)) 151 i = 0; 152 } catch (IllegalAccessException e) { 153 e.printStackTrace(); 154 } catch (InvocationTargetException e) { 155 e.getTargetException().printStackTrace(); 156 } 157 return pluralforms[(int)i]; 158 } 159 } 160 } else { 161 // A GNU gettext created class without plural handling. 162 Object localValue = null; 163 try { 164 localValue = handleGetObjectMethod.invoke(catalog, new Object[] { msgid }); 165 } catch (IllegalAccessException e) { 166 e.printStackTrace(); 167 } catch (InvocationTargetException e) { 168 e.getTargetException().printStackTrace(); 169 } 170 if (localValue != null) { 171 // Found the value. It doesn't depend on n in this case. 172 if (verbose) 173 System.out.println("localValue = "+localValue); 174 return (String)localValue; 175 } 176 } 177 Object parentCatalog = catalog; 178 try { 179 parentCatalog = getParentMethod.invoke(catalog, new Object[0]); 180 } catch (IllegalAccessException e) { 181 e.printStackTrace(); 182 } catch (InvocationTargetException e) { 183 e.getTargetException().printStackTrace(); 184 } 185 if (parentCatalog != catalog) 186 catalog = (ResourceBundle)parentCatalog; 187 else 188 break; 189 } else 190 // Not a GNU gettext created class. 191 break; 192 } while (catalog != null); 193 // The end of chain of GNU gettext ResourceBundles is reached. 194 if (catalog != null) { 195 // For a non-GNU ResourceBundle we cannot access 'parent' and 196 // 'handleGetObject', so make a single call to catalog and all 197 // its parent catalogs at once. 198 Object value; 199 try { 200 value = catalog.getObject(msgid); 201 } catch (MissingResourceException e) { 202 value = null; 203 } 204 if (value != null) 205 // Found the value. It doesn't depend on n in this case. 206 return (String)value; 207 } 208 // Default: English strings and Germanic plural rule. 209 return (n != 1 ? msgid_plural : msgid); 210 } 211 } 212