1 /* GNU gettext for Java
2 * Copyright (C) 2001, 2007 Free Software Foundation, Inc.
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)
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.
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,
22 import java.lang.reflect.*;
26 * This class implements the main GNU libintl functions in Java.
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.
32 * The main differences between the Sun ResourceBundle approach and the
33 * GNU gettext approach are:
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
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.
59 * To compile GNU gettext message catalogs into Java ResourceBundle classes,
60 * the <CODE>msgfmt</CODE> program can be used.
62 * @author Bruno Haible
64 public abstract class GettextResource extends ResourceBundle {
66 public static boolean verbose = false;
69 * Like gettext(catalog,msgid), except that it returns <CODE>null</CODE>
70 * when no translation was found.
72 private static String gettextnull (ResourceBundle catalog, String msgid) {
74 return (String)catalog.getObject(msgid);
75 } catch (MissingResourceException e) {
81 * Returns the translation of <VAR>msgid</VAR>.
82 * @param catalog a ResourceBundle
83 * @param msgid the key string to be translated, an ASCII string
84 * @return the translation of <VAR>msgid</VAR>, or <VAR>msgid</VAR> if
87 public static String gettext (ResourceBundle catalog, String msgid) {
88 String result = gettextnull(catalog,msgid);
95 * Like ngettext(catalog,msgid,msgid_plural,n), except that it returns
96 * <CODE>null</CODE> when no translation was found.
98 private static String ngettextnull (ResourceBundle catalog, String msgid, long n) {
99 // The reason why we use so many reflective API calls instead of letting
100 // the GNU gettext generated ResourceBundles implement some interface,
101 // is that we want the generated ResourceBundles to be completely
102 // standalone, so that migration from the Sun approach to the GNU gettext
103 // approach (without use of plurals) is as straightforward as possible.
104 ResourceBundle origCatalog = catalog;
106 // Try catalog itself.
108 System.out.println("ngettext on "+catalog);
109 Method handleGetObjectMethod = null;
110 Method getParentMethod = null;
112 handleGetObjectMethod = catalog.getClass().getMethod("handleGetObject", new Class[] { java.lang.String.class });
113 getParentMethod = catalog.getClass().getMethod("getParent", new Class[0]);
114 } catch (NoSuchMethodException e) {
115 } catch (SecurityException e) {
118 System.out.println("handleGetObject = "+(handleGetObjectMethod!=null)+", getParent = "+(getParentMethod!=null));
119 if (handleGetObjectMethod != null
120 && Modifier.isPublic(handleGetObjectMethod.getModifiers())
121 && getParentMethod != null) {
122 // A GNU gettext created class.
123 Method lookupMethod = null;
124 Method pluralEvalMethod = null;
126 lookupMethod = catalog.getClass().getMethod("lookup", new Class[] { java.lang.String.class });
127 pluralEvalMethod = catalog.getClass().getMethod("pluralEval", new Class[] { Long.TYPE });
128 } catch (NoSuchMethodException e) {
129 } catch (SecurityException e) {
132 System.out.println("lookup = "+(lookupMethod!=null)+", pluralEval = "+(pluralEvalMethod!=null));
133 if (lookupMethod != null && pluralEvalMethod != null) {
134 // A GNU gettext created class with plural handling.
135 Object localValue = null;
137 localValue = lookupMethod.invoke(catalog, new Object[] { msgid });
138 } catch (IllegalAccessException e) {
140 } catch (InvocationTargetException e) {
141 e.getTargetException().printStackTrace();
143 if (localValue != null) {
145 System.out.println("localValue = "+localValue);
146 if (localValue instanceof String)
147 // Found the value. It doesn't depend on n in this case.
148 return (String)localValue;
150 String[] pluralforms = (String[])localValue;
153 i = ((Long) pluralEvalMethod.invoke(catalog, new Object[] { new Long(n) })).longValue();
154 if (!(i >= 0 && i < pluralforms.length))
156 } catch (IllegalAccessException e) {
158 } catch (InvocationTargetException e) {
159 e.getTargetException().printStackTrace();
161 return pluralforms[(int)i];
165 // A GNU gettext created class without plural handling.
166 Object localValue = null;
168 localValue = handleGetObjectMethod.invoke(catalog, new Object[] { msgid });
169 } catch (IllegalAccessException e) {
171 } catch (InvocationTargetException e) {
172 e.getTargetException().printStackTrace();
174 if (localValue != null) {
175 // Found the value. It doesn't depend on n in this case.
177 System.out.println("localValue = "+localValue);
178 return (String)localValue;
181 Object parentCatalog = catalog;
183 parentCatalog = getParentMethod.invoke(catalog, new Object[0]);
184 } catch (IllegalAccessException e) {
186 } catch (InvocationTargetException e) {
187 e.getTargetException().printStackTrace();
189 if (parentCatalog != catalog)
190 catalog = (ResourceBundle)parentCatalog;
194 // Not a GNU gettext created class.
196 } while (catalog != null);
197 // The end of chain of GNU gettext ResourceBundles is reached.
198 if (catalog != null) {
199 // For a non-GNU ResourceBundle we cannot access 'parent' and
200 // 'handleGetObject', so make a single call to catalog and all
201 // its parent catalogs at once.
204 value = catalog.getObject(msgid);
205 } catch (MissingResourceException e) {
209 // Found the value. It doesn't depend on n in this case.
210 return (String)value;
217 * Returns the plural form for <VAR>n</VAR> of the translation of
219 * @param catalog a ResourceBundle
220 * @param msgid the key string to be translated, an ASCII string
221 * @param msgid_plural its English plural form
222 * @return the translation of <VAR>msgid</VAR> depending on <VAR>n</VAR>,
223 * or <VAR>msgid</VAR> or <VAR>msgid_plural</VAR> if none is found
225 public static String ngettext (ResourceBundle catalog, String msgid, String msgid_plural, long n) {
226 String result = ngettextnull(catalog,msgid,n);
229 // Default: English strings and Germanic plural rule.
230 return (n != 1 ? msgid_plural : msgid);
233 /* The separator between msgctxt and msgid. */
234 private static final String CONTEXT_GLUE = "\u0004";
237 * Returns the translation of <VAR>msgid</VAR> in the context of
238 * <VAR>msgctxt</VAR>.
239 * @param catalog a ResourceBundle
240 * @param msgctxt the context for the key string, an ASCII string
241 * @param msgid the key string to be translated, an ASCII string
242 * @return the translation of <VAR>msgid</VAR>, or <VAR>msgid</VAR> if
245 public static String pgettext (ResourceBundle catalog, String msgctxt, String msgid) {
246 String result = gettextnull(catalog,msgctxt+CONTEXT_GLUE+msgid);
253 * Returns the plural form for <VAR>n</VAR> of the translation of
254 * <VAR>msgid</VAR> in the context of <VAR>msgctxt</VAR>.
255 * @param catalog a ResourceBundle
256 * @param msgctxt the context for the key string, an ASCII string
257 * @param msgid the key string to be translated, an ASCII string
258 * @param msgid_plural its English plural form
259 * @return the translation of <VAR>msgid</VAR> depending on <VAR>n</VAR>,
260 * or <VAR>msgid</VAR> or <VAR>msgid_plural</VAR> if none is found
262 public static String npgettext (ResourceBundle catalog, String msgctxt, String msgid, String msgid_plural, long n) {
263 String result = ngettextnull(catalog,msgctxt+CONTEXT_GLUE+msgid,n);
266 // Default: English strings and Germanic plural rule.
267 return (n != 1 ? msgid_plural : msgid);