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

Last change on this file since 3294 was 3294, checked in by bastiK, 14 years ago

fixed #5093 - translation for open file

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