Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-runtime / intl-java / gnu / gettext / GettextResource.java
1 /* GNU gettext for Java
2  * Copyright (C) 2001, 2007, 2015 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 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.
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 Lesser General Public License for more details.
13  *
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/>.
16  */
17
18 package gnu.gettext;
19
20 import java.lang.reflect.*;
21 import java.util.*;
22
23 /**
24  * This class implements the main GNU libintl functions in Java.
25  * <P>
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.
29  * <P>
30  * The main differences between the Sun ResourceBundle approach and the
31  * GNU gettext approach are:
32  * <UL>
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
35  *       of the messages.
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.
55  * </UL>
56  * <P>
57  * To compile GNU gettext message catalogs into Java ResourceBundle classes,
58  * the <CODE>msgfmt</CODE> program can be used.
59  *
60  * @author Bruno Haible
61  */
62 public abstract class GettextResource extends ResourceBundle {
63
64   public static boolean verbose = false;
65
66   /**
67    * Like gettext(catalog,msgid), except that it returns <CODE>null</CODE>
68    * when no translation was found.
69    */
70   private static String gettextnull (ResourceBundle catalog, String msgid) {
71     try {
72       return (String)catalog.getObject(msgid);
73     } catch (MissingResourceException e) {
74       return null;
75     }
76   }
77
78   /**
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
83    *         none is found
84    */
85   public static String gettext (ResourceBundle catalog, String msgid) {
86     String result = gettextnull(catalog,msgid);
87     if (result != null)
88       return result;
89     return msgid;
90   }
91
92   /**
93    * Like ngettext(catalog,msgid,msgid_plural,n), except that it returns
94    * <CODE>null</CODE> when no translation was found.
95    */
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;
103     do {
104       // Try catalog itself.
105       if (verbose)
106         System.out.println("ngettext on "+catalog);
107       Method handleGetObjectMethod = null;
108       Method getParentMethod = null;
109       try {
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) {
114       }
115       if (verbose)
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;
123         try {
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) {
128         }
129         if (verbose)
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;
134           try {
135             localValue = lookupMethod.invoke(catalog, new Object[] { msgid });
136           } catch (IllegalAccessException e) {
137             e.printStackTrace();
138           } catch (InvocationTargetException e) {
139             e.getTargetException().printStackTrace();
140           }
141           if (localValue != null) {
142             if (verbose)
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;
147             else {
148               String[] pluralforms = (String[])localValue;
149               long i = 0;
150               try {
151                 i = ((Long) pluralEvalMethod.invoke(catalog, new Object[] { new Long(n) })).longValue();
152                 if (!(i >= 0 && i < pluralforms.length))
153                   i = 0;
154               } catch (IllegalAccessException e) {
155                 e.printStackTrace();
156               } catch (InvocationTargetException e) {
157                 e.getTargetException().printStackTrace();
158               }
159               return pluralforms[(int)i];
160             }
161           }
162         } else {
163           // A GNU gettext created class without plural handling.
164           Object localValue = null;
165           try {
166             localValue = handleGetObjectMethod.invoke(catalog, new Object[] { msgid });
167           } catch (IllegalAccessException e) {
168             e.printStackTrace();
169           } catch (InvocationTargetException e) {
170             e.getTargetException().printStackTrace();
171           }
172           if (localValue != null) {
173             // Found the value. It doesn't depend on n in this case.
174             if (verbose)
175               System.out.println("localValue = "+localValue);
176             return (String)localValue;
177           }
178         }
179         Object parentCatalog = catalog;
180         try {
181           parentCatalog = getParentMethod.invoke(catalog, new Object[0]);
182         } catch (IllegalAccessException e) {
183           e.printStackTrace();
184         } catch (InvocationTargetException e) {
185           e.getTargetException().printStackTrace();
186         }
187         if (parentCatalog != catalog)
188           catalog = (ResourceBundle)parentCatalog;
189         else
190           break;
191       } else
192         // Not a GNU gettext created class.
193         break;
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.
200       Object value;
201       try {
202         value = catalog.getObject(msgid);
203       } catch (MissingResourceException e) {
204         value = null;
205       }
206       if (value != null)
207         // Found the value. It doesn't depend on n in this case.
208         return (String)value;
209     }
210     // Default: null.
211     return null;
212   }
213
214   /**
215    * Returns the plural form for <VAR>n</VAR> of the translation of
216    * <VAR>msgid</VAR>.
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
222    */
223   public static String ngettext (ResourceBundle catalog, String msgid, String msgid_plural, long n) {
224     String result = ngettextnull(catalog,msgid,n);
225     if (result != null)
226       return result;
227     // Default: English strings and Germanic plural rule.
228     return (n != 1 ? msgid_plural : msgid);
229   }
230
231   /* The separator between msgctxt and msgid.  */
232   private static final String CONTEXT_GLUE = "\u0004";
233
234   /**
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
241    *         none is found
242    */
243   public static String pgettext (ResourceBundle catalog, String msgctxt, String msgid) {
244     String result = gettextnull(catalog,msgctxt+CONTEXT_GLUE+msgid);
245     if (result != null)
246       return result;
247     return msgid;
248   }
249
250   /**
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
259    */
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);
262     if (result != null)
263       return result;
264     // Default: English strings and Germanic plural rule.
265     return (n != 1 ? msgid_plural : msgid);
266   }
267 }