source: josm/trunk/src/org/openstreetmap/josm/tools/I18n.java@ 2756

Last change on this file since 2756 was 2756, checked in by stoecker, 14 years ago

minor fix for new locale handling

  • Property svn:eol-style set to native
File size: 14.3 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.tools;
3
4import java.io.BufferedInputStream;
5import java.io.InputStream;
6import java.net.URL;
7import java.text.MessageFormat;
8import java.util.Arrays;
9import java.util.Comparator;
10import java.util.HashMap;
11import java.util.LinkedList;
12import java.util.Locale;
13import java.util.MissingResourceException;
14import java.util.Vector;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.gui.progress.ProgressMonitor;
18
19/**
20 * Internationalisation support.
21 *
22 * @author Immanuel.Scholz
23 */
24public class I18n {
25 private enum PluralMode { MODE_NOTONE, MODE_NONE, MODE_GREATERONE,
26 MODE_CS, MODE_AR, MODE_PL, MODE_RO, MODE_RU, MODE_SK, MODE_SL}
27 private static PluralMode pluralMode = PluralMode.MODE_NOTONE; /* english default */
28 private static HashMap<String, String> strings = null;
29 private static HashMap<String, String[]> pstrings = null;
30 private static HashMap<String, PluralMode> languages = new HashMap<String, PluralMode>();
31
32 /**
33 * Set by MainApplication. Changes here later will probably mess up everything, because
34 * many strings are already loaded.
35 */
36 public static final String tr(String text, Object... objects) {
37 return MessageFormat.format(gettext(text, null), objects);
38 }
39
40 public static final String tr(String text) {
41 return gettext(text, null);
42 }
43
44 public static final String trc(String ctx, String text) {
45 return gettext(text, ctx);
46 }
47
48 /* NOTE: marktr does NOT support context strings - use marktrc instead */
49 public static final String marktr(String text) {
50 return text;
51 }
52
53 public static final String marktrc(String context, String text) {
54 return text;
55 }
56
57 public static final String trn(String text, String pluralText, long n, Object... objects) {
58 return MessageFormat.format(gettextn(text, pluralText, null, n), objects);
59 }
60
61 public static final String trn(String text, String pluralText, long n) {
62 return gettextn(text, pluralText, null, n);
63 }
64
65 public static final String trnc(String ctx, String text, String pluralText, long n, Object... objects) {
66 return MessageFormat.format(gettextn(text, pluralText, ctx, n), objects);
67 }
68
69 public static final String trnc(String ctx, String text, String pluralText, long n) {
70 return gettextn(text, pluralText, ctx, n);
71 }
72
73 public static final String gettext(String text, String ctx)
74 {
75 int i;
76 if(ctx == null && text.startsWith("_:") && (i = text.indexOf("\n")) >= 0)
77 {
78 ctx = text.substring(2,i-1);
79 text = text.substring(i+1);
80 }
81 if(strings != null)
82 {
83 String trans = strings.get(ctx == null ? text : "_:"+ctx+"\n"+text);
84 if(trans != null)
85 return trans;
86 }
87 return text;
88 }
89
90 public static final String gettextn(String text, String plural, String ctx, long num)
91 {
92 int i;
93 if(ctx == null && text.startsWith("_:") && (i = text.indexOf("\n")) >= 0)
94 {
95 ctx = text.substring(2,i-1);
96 text = text.substring(i+1);
97 }
98 if(pstrings != null)
99 {
100 i = pluralEval(num);
101 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+"\n"+text);
102 if(trans != null && trans.length >= i)
103 return trans[i];
104 }
105
106 return num == 1 ? text : plural;
107 }
108
109 /**
110 * Get a list of all available JOSM Translations.
111 * @return an array of locale objects.
112 */
113 public static final Locale[] getAvailableTranslations() {
114 Vector<Locale> v = new Vector<Locale>();
115 if(Main.class.getResource("/data/en.lang") != null)
116 {
117 for (String loc : languages.keySet()) {
118 if(Main.class.getResource("/data/"+loc+".lang") != null) {
119 int i = loc.indexOf('_');
120 if (i > 0) {
121 v.add(new Locale(loc.substring(0, i), loc.substring(i + 1)));
122 } else {
123 v.add(new Locale(loc));
124 }
125 }
126 }
127 }
128 v.add(Locale.ENGLISH);
129 Locale[] l = new Locale[v.size()];
130 l = v.toArray(l);
131 Arrays.sort(l, new Comparator<Locale>() {
132 public int compare(Locale o1, Locale o2) {
133 return o1.toString().compareTo(o2.toString());
134 }
135 });
136 return l;
137 }
138
139 public static void init()
140 {
141 languages.put("ar", PluralMode.MODE_AR);
142 languages.put("bg", PluralMode.MODE_NOTONE);
143 languages.put("cs", PluralMode.MODE_CS);
144 languages.put("da", PluralMode.MODE_NOTONE);
145 languages.put("de", PluralMode.MODE_NOTONE);
146 languages.put("el", PluralMode.MODE_NOTONE);
147 languages.put("en_GB", PluralMode.MODE_NOTONE);
148 languages.put("es", PluralMode.MODE_NOTONE);
149 languages.put("et", PluralMode.MODE_NOTONE);
150 languages.put("fi", PluralMode.MODE_NOTONE);
151 languages.put("fr", PluralMode.MODE_GREATERONE);
152 languages.put("gl", PluralMode.MODE_NOTONE);
153 languages.put("is", PluralMode.MODE_NOTONE);
154 languages.put("it", PluralMode.MODE_NOTONE);
155 languages.put("iw_IL", PluralMode.MODE_NOTONE);
156 languages.put("ja", PluralMode.MODE_NONE);
157 languages.put("nb", PluralMode.MODE_NOTONE);
158 languages.put("nl", PluralMode.MODE_NOTONE);
159 languages.put("pl", PluralMode.MODE_PL);
160 languages.put("pt_BR", PluralMode.MODE_GREATERONE);
161 languages.put("ro", PluralMode.MODE_RO);
162 languages.put("ru", PluralMode.MODE_RU);
163 languages.put("sk", PluralMode.MODE_SK);
164 languages.put("sl", PluralMode.MODE_SL);
165 languages.put("sv", PluralMode.MODE_NOTONE);
166 languages.put("tr", PluralMode.MODE_NONE);
167 languages.put("zh_TW", PluralMode.MODE_NONE);
168
169 /* try initial language settings, may be changed later again */
170 if(!load(Locale.getDefault().toString()))
171 Locale.setDefault(Locale.ENGLISH);
172 }
173
174 public static boolean load(String l)
175 {
176 if(l.equals("en"))
177 {
178 strings = null;
179 pstrings = null;
180 pluralMode = PluralMode.MODE_NOTONE;
181 return true;
182 }
183 URL en = Main.class.getResource("/data/en.lang");
184 if(en == null)
185 return false;
186 URL tr = Main.class.getResource("/data/"+l+".lang");
187 if(tr == null)
188 {
189 int i = l.indexOf('_');
190 if (i > 0)
191 l = l.substring(0, i);
192 tr = Main.class.getResource("/data/"+l+".lang");
193 if(tr == null)
194 return false;
195 }
196
197 HashMap<String, String> s = new HashMap<String, String>();
198 HashMap<String, String[]> p = new HashMap<String, String[]>();
199 /* file format:
200 for all single strings:
201 {
202 unsigned short (2 byte) stringlength
203 string
204 }
205 unsigned short (2 byte) 0xFFFF (marks end of single strings)
206 for all multi strings:
207 {
208 unsigned char (1 byte) stringcount
209 for stringcount
210 unsigned short (2 byte) stringlength
211 string
212 }
213 */
214 try
215 {
216 InputStream ens = new BufferedInputStream(en.openStream());
217 InputStream trs = new BufferedInputStream(tr.openStream());
218 if(ens == null || trs == null)
219 return false;
220 byte[] enlen = new byte[2];
221 byte[] trlen = new byte[2];
222 boolean multimode = false;
223 byte[] str = new byte[4096];
224 for(;;)
225 {
226 if(multimode)
227 {
228 int ennum = ens.read();
229 int trnum = trs.read();
230 if((ennum == -1 && trnum != -1) || (ennum != -1 && trnum == -1)) /* files do not match */
231 return false;
232 if(ennum == -1) /* EOF */
233 break;
234 String[] enstrings = new String[ennum];
235 String[] trstrings = new String[trnum];
236 for(int i = 0; i < ennum; ++i)
237 {
238 int val = ens.read(enlen);
239 if(val != 2) /* file corrupt */
240 return false;
241 val = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]);
242 if(val > str.length)
243 str = new byte[val];
244 int rval = ens.read(str, 0, val);
245 if(rval != val) /* file corrupt */
246 return false;
247 enstrings[i] = new String(str, 0, val, "utf-8");
248 }
249 for(int i = 0; i < trnum; ++i)
250 {
251 int val = trs.read(trlen);
252 if(val != 2) /* file corrupt */
253 return false;
254 val = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]);
255 if(val > str.length)
256 str = new byte[val];
257 int rval = trs.read(str, 0, val);
258 if(rval != val) /* file corrupt */
259 return false;
260 trstrings[i] = new String(str, 0, val, "utf-8");
261 }
262 if(trnum > 0)
263 p.put(enstrings[0], trstrings);
264 }
265 else
266 {
267 int enval = ens.read(enlen);
268 int trval = trs.read(trlen);
269 if(enval != trval) /* files do not match */
270 return false;
271 if(enval == -1) /* EOF */
272 break;
273 if(enval != 2) /* files corrupt */
274 return false;
275 enval = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]);
276 trval = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]);
277 if(enval == 0xFFFF)
278 {
279 multimode = true;
280 if(trval != 0xFFFF) /* files do not match */
281 return false;
282 }
283 else
284 {
285 if(enval > str.length)
286 str = new byte[enval];
287 if(trval > str.length)
288 str = new byte[trval];
289 int val = ens.read(str, 0, enval);
290 if(val != enval) /* file corrupt */
291 return false;
292 String enstr = new String(str, 0, enval, "utf-8");
293 if(trval != 0)
294 {
295 val = trs.read(str, 0, trval);
296 if(val != trval) /* file corrupt */
297 return false;
298 String trstr = new String(str, 0, trval, "utf-8");
299 s.put(enstr, trstr);
300 }
301 }
302 }
303 }
304 }
305 catch(Exception e)
306 {
307 return false;
308 }
309 if(!s.isEmpty())
310 {
311 strings = s;
312 pstrings = p;
313 pluralMode = languages.get(l);
314 return true;
315 }
316 return false;
317 }
318
319 /**
320 * Sets the default locale (see {@see Locale#setDefault(Locale)} to the local
321 * given by <code>localName</code>.
322 *
323 * Ignored if localName is null. If the locale with name <code>localName</code>
324 * isn't found the default local is set to <tt>en</tt> (english).
325 *
326 * @param localeName the locale name. Ignored if null.
327 */
328 public static void set(String localeName){
329 if (localeName != null) {
330 Locale l;
331 if (localeName.equals("he")) {
332 localeName = "iw_IL";
333 }
334 int i = localeName.indexOf('_');
335 if (i > 0) {
336 l = new Locale(localeName.substring(0, i), localeName.substring(i + 1));
337 } else {
338 l = new Locale(localeName);
339 }
340 if(load(localeName)) {
341 Locale.setDefault(l);
342 } else {
343 if (!l.getLanguage().equals("en")) {
344 System.out.println(tr("Unable to find translation for the locale {0}. Reverting to {1}.",
345 l.getDisplayName(), Locale.getDefault().getDisplayName()));
346 } else {
347 strings = null;
348 pstrings = null;
349 }
350 }
351 }
352 }
353
354 private static int pluralEval(long n)
355 {
356 switch(pluralMode)
357 {
358 case MODE_NOTONE: /* bg, da, de, el, en, en_GB, es, et, fi, gl, is, it, iw_IL, nb, nl, sv */
359 return ((n != 1) ? 1 : 0);
360 case MODE_NONE: /* ja, tr, zh_TW */
361 return 0;
362 case MODE_GREATERONE: /* fr, pt_BR */
363 return ((n > 1) ? 1 : 0);
364 case MODE_CS:
365 return ((n == 1) ? 0 : (((n >= 2) && (n <= 4)) ? 1 : 2));
366 case MODE_AR:
367 return ((n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((((n % 100) >= 3)
368 && ((n % 100) <= 10)) ? 3 : ((((n % 100) >= 11) && ((n % 100) <= 99)) ? 4 : 5)))));
369 case MODE_PL:
370 return ((n == 1) ? 0 : (((((n % 10) >= 2) && ((n % 10) <= 4))
371 && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2));
372 case MODE_RO:
373 return ((n == 1) ? 0 : ((((n % 100) > 19) || (((n % 100) == 0) && (n != 0))) ? 2 : 1));
374 case MODE_RU:
375 return ((((n % 10) == 1) && ((n % 100) != 11)) ? 0 : (((((n % 10) >= 2)
376 && ((n % 10) <= 4)) && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2));
377 case MODE_SK:
378 return ((n == 1) ? 1 : (((n >= 2) && (n <= 4)) ? 2 : 0));
379 case MODE_SL:
380 return (((n % 100) == 1) ? 1 : (((n % 100) == 2) ? 2 : ((((n % 100) == 3)
381 || ((n % 100) == 4)) ? 3 : 0)));
382 }
383 return 0;
384 }
385}
Note: See TracBrowser for help on using the repository browser.