1 /* GNU gettext for Java
2 * Copyright (C) 2001, 2007, 2015 Free Software Foundation, Inc.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; either version 2.1 of the License, or
7 * (at your option) any later version.
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 Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 import java.lang.reflect.*;
24 * This class implements the main GNU libintl functions in Java.
26 * Using the GNU gettext approach, compiled message catalogs are normal
27 * Java ResourceBundle classes and are thus interoperable with standard
28 * ResourceBundle based code.
30 * The main differences between the Sun ResourceBundle approach and the
31 * GNU gettext approach are:
33 * <LI>In the Sun approach, the keys are abstract textual shortcuts.
34 * In the GNU gettext approach, the keys are the English/ASCII version
36 * <LI>In the Sun approach, the translation files are called
37 * "<VAR>Resource</VAR>_<VAR>locale</VAR>.properties" and have non-ASCII
38 * characters encoded in the Java
39 * <CODE>\</CODE><CODE>u<VAR>nnnn</VAR></CODE> syntax. Very few editors
40 * can natively display international characters in this format. In the
41 * GNU gettext approach, the translation files are called
42 * "<VAR>Resource</VAR>.<VAR>locale</VAR>.po"
43 * and are in the encoding the translator has chosen. Many editors
44 * can be used. There are at least three GUI translating tools
45 * (Emacs PO mode, KDE KBabel, GNOME gtranslator).
46 * <LI>In the Sun approach, the function
47 * <CODE>ResourceBundle.getString</CODE> throws a
48 * <CODE>MissingResourceException</CODE> when no translation is found.
49 * In the GNU gettext approach, the <CODE>gettext</CODE> function
50 * returns the (English) message key in that case.
51 * <LI>In the Sun approach, there is no support for plural handling.
52 * Even the most elaborate MessageFormat strings cannot provide decent
53 * plural handling. In the GNU gettext approach, we have the
54 * <CODE>ngettext</CODE> function.
57 * To compile GNU gettext message catalogs into Java ResourceBundle classes,
58 * the <CODE>msgfmt</CODE> program can be used.
60 * @author Bruno Haible
62 public abstract class GettextResource extends ResourceBundle {
64 public static boolean verbose = false;
67 * Like gettext(catalog,msgid), except that it returns <CODE>null</CODE>
68 * when no translation was found.
70 private static String gettextnull (ResourceBundle catalog, String msgid) {
72 return (String)catalog.getObject(msgid);
73 } catch (MissingResourceException e) {
79 * Returns the translation of <VAR>msgid</VAR>.
80 * @param catalog a ResourceBundle
81 * @param msgid the key string to be translated, an ASCII string
82 * @return the translation of <VAR>msgid</VAR>, or <VAR>msgid</VAR> if
85 public static String gettext (ResourceBundle catalog, String msgid) {
86 String result = gettextnull(catalog,msgid);
93 * Like ngettext(catalog,msgid,msgid_plural,n), except that it returns
94 * <CODE>null</CODE> when no translation was found.
96 private static String ngettextnull (ResourceBundle catalog, String msgid, long n) {
97 // The reason why we use so many reflective API calls instead of letting
98 // the GNU gettext generated ResourceBundles implement some interface,
99 // is that we want the generated ResourceBundles to be completely
100 // standalone, so that migration from the Sun approach to the GNU gettext
101 // approach (without use of plurals) is as straightforward as possible.
102 ResourceBundle origCatalog = catalog;
104 // Try catalog itself.
106 System.out.println("ngettext on "+catalog);
107 Method handleGetObjectMethod = null;
108 Method getParentMethod = null;
110 handleGetObjectMethod = catalog.getClass().getMethod("handleGetObject", new Class[] { java.lang.String.class });
111 getParentMethod = catalog.getClass().getMethod("getParent", new Class[0]);
112 } catch (NoSuchMethodException e) {
113 } catch (SecurityException e) {
116 System.out.println("handleGetObject = "+(handleGetObjectMethod!=null)+", getParent = "+(getParentMethod!=null));
117 if (handleGetObjectMethod != null
118 && Modifier.isPublic(handleGetObjectMethod.getModifiers())
119 && getParentMethod != null) {
120 // A GNU gettext created class.
121 Method lookupMethod = null;
122 Method pluralEvalMethod = null;
124 lookupMethod = catalog.getClass().getMethod("lookup", new Class[] { java.lang.String.class });
125 pluralEvalMethod = catalog.getClass().getMethod("pluralEval", new Class[] { Long.TYPE });
126 } catch (NoSuchMethodException e) {
127 } catch (SecurityException e) {
130 System.out.println("lookup = "+(lookupMethod!=null)+", pluralEval = "+(pluralEvalMethod!=null));
131 if (lookupMethod != null && pluralEvalMethod != null) {
132 // A GNU gettext created class with plural handling.
133 Object localValue = null;
135 localValue = lookupMethod.invoke(catalog, new Object[] { msgid });
136 } catch (IllegalAccessException e) {
138 } catch (InvocationTargetException e) {
139 e.getTargetException().printStackTrace();
141 if (localValue != null) {
143 System.out.println("localValue = "+localValue);
144 if (localValue instanceof String)
145 // Found the value. It doesn't depend on n in this case.
146 return (String)localValue;
148 String[] pluralforms = (String[])localValue;
151 i = ((Long) pluralEvalMethod.invoke(catalog, new Object[] { new Long(n) })).longValue();
152 if (!(i >= 0 && i < pluralforms.length))
154 } catch (IllegalAccessException e) {
156 } catch (InvocationTargetException e) {
157 e.getTargetException().printStackTrace();
159 return pluralforms[(int)i];
163 // A GNU gettext created class without plural handling.
164 Object localValue = null;
166 localValue = handleGetObjectMethod.invoke(catalog, new Object[] { msgid });
167 } catch (IllegalAccessException e) {
169 } catch (InvocationTargetException e) {
170 e.getTargetException().printStackTrace();
172 if (localValue != null) {
173 // Found the value. It doesn't depend on n in this case.
175 System.out.println("localValue = "+localValue);
176 return (String)localValue;
179 Object parentCatalog = catalog;
181 parentCatalog = getParentMethod.invoke(catalog, new Object[0]);
182 } catch (IllegalAccessException e) {
184 } catch (InvocationTargetException e) {
185 e.getTargetException().printStackTrace();
187 if (parentCatalog != catalog)
188 catalog = (ResourceBundle)parentCatalog;
192 // Not a GNU gettext created class.
194 } while (catalog != null);
195 // The end of chain of GNU gettext ResourceBundles is reached.
196 if (catalog != null) {
197 // For a non-GNU ResourceBundle we cannot access 'parent' and
198 // 'handleGetObject', so make a single call to catalog and all
199 // its parent catalogs at once.
202 value = catalog.getObject(msgid);
203 } catch (MissingResourceException e) {
207 // Found the value. It doesn't depend on n in this case.
208 return (String)value;
215 * Returns the plural form for <VAR>n</VAR> of the translation of
217 * @param catalog a ResourceBundle
218 * @param msgid the key string to be translated, an ASCII string
219 * @param msgid_plural its English plural form
220 * @return the translation of <VAR>msgid</VAR> depending on <VAR>n</VAR>,
221 * or <VAR>msgid</VAR> or <VAR>msgid_plural</VAR> if none is found
223 public static String ngettext (ResourceBundle catalog, String msgid, String msgid_plural, long n) {
224 String result = ngettextnull(catalog,msgid,n);
227 // Default: English strings and Germanic plural rule.
228 return (n != 1 ? msgid_plural : msgid);
231 /* The separator between msgctxt and msgid. */
232 private static final String CONTEXT_GLUE = "\u0004";
235 * Returns the translation of <VAR>msgid</VAR> in the context of
236 * <VAR>msgctxt</VAR>.
237 * @param catalog a ResourceBundle
238 * @param msgctxt the context for the key string, an ASCII string
239 * @param msgid the key string to be translated, an ASCII string
240 * @return the translation of <VAR>msgid</VAR>, or <VAR>msgid</VAR> if
243 public static String pgettext (ResourceBundle catalog, String msgctxt, String msgid) {
244 String result = gettextnull(catalog,msgctxt+CONTEXT_GLUE+msgid);
251 * Returns the plural form for <VAR>n</VAR> of the translation of
252 * <VAR>msgid</VAR> in the context of <VAR>msgctxt</VAR>.
253 * @param catalog a ResourceBundle
254 * @param msgctxt the context for the key string, an ASCII string
255 * @param msgid the key string to be translated, an ASCII string
256 * @param msgid_plural its English plural form
257 * @return the translation of <VAR>msgid</VAR> depending on <VAR>n</VAR>,
258 * or <VAR>msgid</VAR> or <VAR>msgid_plural</VAR> if none is found
260 public static String npgettext (ResourceBundle catalog, String msgctxt, String msgid, String msgid_plural, long n) {
261 String result = ngettextnull(catalog,msgctxt+CONTEXT_GLUE+msgid,n);
264 // Default: English strings and Germanic plural rule.
265 return (n != 1 ? msgid_plural : msgid);