xref: /netbsd-src/external/gpl2/gettext/dist/gettext-runtime/intl-java/gnu/gettext/GettextResource.java (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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