Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gettext-runtime / intl-java / gnu / gettext / GettextResource.java
1 /* GNU gettext for Java
2  * Copyright (C) 2001, 2007 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    * Like gettext(catalog,msgid), except that it returns <CODE>null</CODE>
70    * when no translation was found.
71    */
72   private static String gettextnull (ResourceBundle catalog, String msgid) {
73     try {
74       return (String)catalog.getObject(msgid);
75     } catch (MissingResourceException e) {
76       return null;
77     }
78   }
79
80   /**
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
85    *         none is found
86    */
87   public static String gettext (ResourceBundle catalog, String msgid) {
88     String result = gettextnull(catalog,msgid);
89     if (result != null)
90       return result;
91     return msgid;
92   }
93
94   /**
95    * Like ngettext(catalog,msgid,msgid_plural,n), except that it returns
96    * <CODE>null</CODE> when no translation was found.
97    */
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;
105     do {
106       // Try catalog itself.
107       if (verbose)
108         System.out.println("ngettext on "+catalog);
109       Method handleGetObjectMethod = null;
110       Method getParentMethod = null;
111       try {
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) {
116       }
117       if (verbose)
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;
125         try {
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) {
130         }
131         if (verbose)
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;
136           try {
137             localValue = lookupMethod.invoke(catalog, new Object[] { msgid });
138           } catch (IllegalAccessException e) {
139             e.printStackTrace();
140           } catch (InvocationTargetException e) {
141             e.getTargetException().printStackTrace();
142           }
143           if (localValue != null) {
144             if (verbose)
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;
149             else {
150               String[] pluralforms = (String[])localValue;
151               long i = 0;
152               try {
153                 i = ((Long) pluralEvalMethod.invoke(catalog, new Object[] { new Long(n) })).longValue();
154                 if (!(i >= 0 && i < pluralforms.length))
155                   i = 0;
156               } catch (IllegalAccessException e) {
157                 e.printStackTrace();
158               } catch (InvocationTargetException e) {
159                 e.getTargetException().printStackTrace();
160               }
161               return pluralforms[(int)i];
162             }
163           }
164         } else {
165           // A GNU gettext created class without plural handling.
166           Object localValue = null;
167           try {
168             localValue = handleGetObjectMethod.invoke(catalog, new Object[] { msgid });
169           } catch (IllegalAccessException e) {
170             e.printStackTrace();
171           } catch (InvocationTargetException e) {
172             e.getTargetException().printStackTrace();
173           }
174           if (localValue != null) {
175             // Found the value. It doesn't depend on n in this case.
176             if (verbose)
177               System.out.println("localValue = "+localValue);
178             return (String)localValue;
179           }
180         }
181         Object parentCatalog = catalog;
182         try {
183           parentCatalog = getParentMethod.invoke(catalog, new Object[0]);
184         } catch (IllegalAccessException e) {
185           e.printStackTrace();
186         } catch (InvocationTargetException e) {
187           e.getTargetException().printStackTrace();
188         }
189         if (parentCatalog != catalog)
190           catalog = (ResourceBundle)parentCatalog;
191         else
192           break;
193       } else
194         // Not a GNU gettext created class.
195         break;
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.
202       Object value;
203       try {
204         value = catalog.getObject(msgid);
205       } catch (MissingResourceException e) {
206         value = null;
207       }
208       if (value != null)
209         // Found the value. It doesn't depend on n in this case.
210         return (String)value;
211     }
212     // Default: null.
213     return null;
214   }
215
216   /**
217    * Returns the plural form for <VAR>n</VAR> of the translation of
218    * <VAR>msgid</VAR>.
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
224    */
225   public static String ngettext (ResourceBundle catalog, String msgid, String msgid_plural, long n) {
226     String result = ngettextnull(catalog,msgid,n);
227     if (result != null)
228       return result;
229     // Default: English strings and Germanic plural rule.
230     return (n != 1 ? msgid_plural : msgid);
231   }
232
233   /* The separator between msgctxt and msgid.  */
234   private static final String CONTEXT_GLUE = "\u0004";
235
236   /**
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
243    *         none is found
244    */
245   public static String pgettext (ResourceBundle catalog, String msgctxt, String msgid) {
246     String result = gettextnull(catalog,msgctxt+CONTEXT_GLUE+msgid);
247     if (result != null)
248       return result;
249     return msgid;
250   }
251
252   /**
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
261    */
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);
264     if (result != null)
265       return result;
266     // Default: English strings and Germanic plural rule.
267     return (n != 1 ? msgid_plural : msgid);
268   }
269 }