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

Last change on this file since 10178 was 9983, checked in by Don-vip, 8 years ago

remove unused code

  • Property svn:eol-style set to native
File size: 31.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.awt.GraphicsEnvironment;
5import java.io.BufferedInputStream;
6import java.io.File;
7import java.io.FileInputStream;
8import java.io.IOException;
9import java.io.InputStream;
10import java.lang.annotation.Retention;
11import java.lang.annotation.RetentionPolicy;
12import java.net.URL;
13import java.nio.charset.StandardCharsets;
14import java.text.MessageFormat;
15import java.util.ArrayList;
16import java.util.Arrays;
17import java.util.Collection;
18import java.util.Comparator;
19import java.util.HashMap;
20import java.util.Locale;
21import java.util.Map;
22import java.util.jar.JarInputStream;
23import java.util.zip.ZipEntry;
24
25import javax.swing.JColorChooser;
26import javax.swing.JFileChooser;
27import javax.swing.UIManager;
28
29import org.openstreetmap.gui.jmapviewer.FeatureAdapter.TranslationAdapter;
30import org.openstreetmap.josm.Main;
31import org.openstreetmap.josm.gui.util.GuiHelper;
32import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
33
34/**
35 * Internationalisation support.
36 *
37 * @author Immanuel.Scholz
38 */
39public final class I18n {
40
41 /**
42 * This annotates strings which do not permit a clean i18n. This is mostly due to strings
43 * containing two nouns which can occur in singular or plural form.
44 * <br>
45 * No behaviour is associated with this annotation.
46 */
47 @Retention(RetentionPolicy.SOURCE)
48 public @interface QuirkyPluralString {
49 }
50
51 private I18n() {
52 // Hide default constructor for utils classes
53 }
54
55 /**
56 * Enumeration of possible plural modes. It allows us to identify and implement logical conditions of
57 * plural forms defined on <a href="https://help.launchpad.net/Translations/PluralForms">Launchpad</a>.
58 * See <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html">CLDR</a>
59 * for another complete list.
60 * @see #pluralEval
61 */
62 private enum PluralMode {
63 /** Plural = Not 1. This is the default for many languages, including English: 1 day, but 0 days or 2 days. */
64 MODE_NOTONE,
65 /** No plural. Mainly for Asian languages (Indonesian, Chinese, Japanese, ...) */
66 MODE_NONE,
67 /** Plural = Greater than 1. For some latin languages (French, Brazilian Portuguese) */
68 MODE_GREATERONE,
69 /* Special mode for
70 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar">Arabic</a>.*
71 MODE_AR,*/
72 /** Special mode for
73 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#cs">Czech</a>. */
74 MODE_CS,
75 /** Special mode for
76 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#pl">Polish</a>. */
77 MODE_PL,
78 /* Special mode for
79 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ro">Romanian</a>.*
80 MODE_RO,*/
81 /** Special mode for
82 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#lt">Lithuanian</a>. */
83 MODE_LT,
84 /** Special mode for
85 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ru">Russian</a>. */
86 MODE_RU,
87 /** Special mode for
88 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#sk">Slovak</a>. */
89 MODE_SK,
90 /* Special mode for
91 * <a href="http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#sl">Slovenian</a>.*
92 MODE_SL,*/
93 }
94
95 private static volatile PluralMode pluralMode = PluralMode.MODE_NOTONE; /* english default */
96 private static volatile String loadedCode = "en";
97
98 /* Localization keys for file chooser (and color chooser). */
99 private static final String[] javaInternalMessageKeys = new String[] {
100 /* JFileChooser windows laf */
101 "FileChooser.detailsViewActionLabelText",
102 "FileChooser.detailsViewButtonAccessibleName",
103 "FileChooser.detailsViewButtonToolTipText",
104 "FileChooser.fileAttrHeaderText",
105 "FileChooser.fileDateHeaderText",
106 "FileChooser.fileNameHeaderText",
107 "FileChooser.fileNameLabelText",
108 "FileChooser.fileSizeHeaderText",
109 "FileChooser.fileTypeHeaderText",
110 "FileChooser.filesOfTypeLabelText",
111 "FileChooser.homeFolderAccessibleName",
112 "FileChooser.homeFolderToolTipText",
113 "FileChooser.listViewActionLabelText",
114 "FileChooser.listViewButtonAccessibleName",
115 "FileChooser.listViewButtonToolTipText",
116 "FileChooser.lookInLabelText",
117 "FileChooser.newFolderAccessibleName",
118 "FileChooser.newFolderActionLabelText",
119 "FileChooser.newFolderToolTipText",
120 "FileChooser.refreshActionLabelText",
121 "FileChooser.saveInLabelText",
122 "FileChooser.upFolderAccessibleName",
123 "FileChooser.upFolderToolTipText",
124 "FileChooser.viewMenuLabelText",
125
126 /* JFileChooser gtk laf */
127 "FileChooser.acceptAllFileFilterText",
128 "FileChooser.cancelButtonText",
129 "FileChooser.cancelButtonToolTipText",
130 "FileChooser.deleteFileButtonText",
131 "FileChooser.filesLabelText",
132 "FileChooser.filterLabelText",
133 "FileChooser.foldersLabelText",
134 "FileChooser.newFolderButtonText",
135 "FileChooser.newFolderDialogText",
136 "FileChooser.openButtonText",
137 "FileChooser.openButtonToolTipText",
138 "FileChooser.openDialogTitleText",
139 "FileChooser.pathLabelText",
140 "FileChooser.renameFileButtonText",
141 "FileChooser.renameFileDialogText",
142 "FileChooser.renameFileErrorText",
143 "FileChooser.renameFileErrorTitle",
144 "FileChooser.saveButtonText",
145 "FileChooser.saveButtonToolTipText",
146 "FileChooser.saveDialogTitleText",
147
148 /* JFileChooser motif laf */
149 //"FileChooser.cancelButtonText",
150 //"FileChooser.cancelButtonToolTipText",
151 "FileChooser.enterFileNameLabelText",
152 //"FileChooser.filesLabelText",
153 //"FileChooser.filterLabelText",
154 //"FileChooser.foldersLabelText",
155 "FileChooser.helpButtonText",
156 "FileChooser.helpButtonToolTipText",
157 //"FileChooser.openButtonText",
158 //"FileChooser.openButtonToolTipText",
159 //"FileChooser.openDialogTitleText",
160 //"FileChooser.pathLabelText",
161 //"FileChooser.saveButtonText",
162 //"FileChooser.saveButtonToolTipText",
163 //"FileChooser.saveDialogTitleText",
164 "FileChooser.updateButtonText",
165 "FileChooser.updateButtonToolTipText",
166
167 /* gtk color chooser */
168 "GTKColorChooserPanel.blueText",
169 "GTKColorChooserPanel.colorNameText",
170 "GTKColorChooserPanel.greenText",
171 "GTKColorChooserPanel.hueText",
172 "GTKColorChooserPanel.nameText",
173 "GTKColorChooserPanel.redText",
174 "GTKColorChooserPanel.saturationText",
175 "GTKColorChooserPanel.valueText",
176
177 /* JOptionPane */
178 "OptionPane.okButtonText",
179 "OptionPane.yesButtonText",
180 "OptionPane.noButtonText",
181 "OptionPane.cancelButtonText"
182 };
183 private static volatile Map<String, String> strings;
184 private static volatile Map<String, String[]> pstrings;
185 private static Map<String, PluralMode> languages = new HashMap<>();
186
187 /**
188 * Translates some text for the current locale.
189 * These strings are collected by a script that runs on the source code files.
190 * After translation, the localizations are distributed with the main program.
191 * <br>
192 * For example, <code>tr("JOSM''s default value is ''{0}''.", val)</code>.
193 * <br>
194 * Use {@link #trn} for distinguishing singular from plural text, i.e.,
195 * do not use {@code tr(size == 1 ? "singular" : "plural")} nor
196 * {@code size == 1 ? tr("singular") : tr("plural")}
197 *
198 * @param text the text to translate.
199 * Must be a string literal. (No constants or local vars.)
200 * Can be broken over multiple lines.
201 * An apostrophe ' must be quoted by another apostrophe.
202 * @param objects the parameters for the string.
203 * Mark occurrences in {@code text} with <code>{0}</code>, <code>{1}</code>, ...
204 * @return the translated string.
205 * @see #trn
206 * @see #trc
207 * @see #trnc
208 */
209 public static String tr(String text, Object... objects) {
210 if (text == null) return null;
211 return MessageFormat.format(gettext(text, null), objects);
212 }
213
214 /**
215 * Translates some text in a context for the current locale.
216 * There can be different translations for the same text within different contexts.
217 *
218 * @param context string that helps translators to find an appropriate
219 * translation for {@code text}.
220 * @param text the text to translate.
221 * @return the translated string.
222 * @see #tr
223 * @see #trn
224 * @see #trnc
225 */
226 public static String trc(String context, String text) {
227 if (context == null)
228 return tr(text);
229 if (text == null)
230 return null;
231 return MessageFormat.format(gettext(text, context), (Object) null);
232 }
233
234 public static String trc_lazy(String context, String text) {
235 if (context == null)
236 return tr(text);
237 if (text == null)
238 return null;
239 return MessageFormat.format(gettext_lazy(text, context), (Object) null);
240 }
241
242 /**
243 * Marks a string for translation (such that a script can harvest
244 * the translatable strings from the source files).
245 *
246 * For example, <code>
247 * String[] options = new String[] {marktr("up"), marktr("down")};
248 * lbl.setText(tr(options[0]));</code>
249 * @param text the string to be marked for translation.
250 * @return {@code text} unmodified.
251 */
252 public static String marktr(String text) {
253 return text;
254 }
255
256 public static String marktrc(String context, String text) {
257 return text;
258 }
259
260 /**
261 * Translates some text for the current locale and distinguishes between
262 * {@code singularText} and {@code pluralText} depending on {@code n}.
263 * <br>
264 * For instance, {@code trn("There was an error!", "There were errors!", i)} or
265 * <code>trn("Found {0} error in {1}!", "Found {0} errors in {1}!", i, Integer.toString(i), url)</code>.
266 *
267 * @param singularText the singular text to translate.
268 * Must be a string literal. (No constants or local vars.)
269 * Can be broken over multiple lines.
270 * An apostrophe ' must be quoted by another apostrophe.
271 * @param pluralText the plural text to translate.
272 * Must be a string literal. (No constants or local vars.)
273 * Can be broken over multiple lines.
274 * An apostrophe ' must be quoted by another apostrophe.
275 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used.
276 * @param objects the parameters for the string.
277 * Mark occurrences in {@code singularText} and {@code pluralText} with <code>{0}</code>, <code>{1}</code>, ...
278 * @return the translated string.
279 * @see #tr
280 * @see #trc
281 * @see #trnc
282 */
283 public static String trn(String singularText, String pluralText, long n, Object... objects) {
284 return MessageFormat.format(gettextn(singularText, pluralText, null, n), objects);
285 }
286
287 /**
288 * Translates some text in a context for the current locale and distinguishes between
289 * {@code singularText} and {@code pluralText} depending on {@code n}.
290 * There can be different translations for the same text within different contexts.
291 *
292 * @param context string that helps translators to find an appropriate
293 * translation for {@code text}.
294 * @param singularText the singular text to translate.
295 * Must be a string literal. (No constants or local vars.)
296 * Can be broken over multiple lines.
297 * An apostrophe ' must be quoted by another apostrophe.
298 * @param pluralText the plural text to translate.
299 * Must be a string literal. (No constants or local vars.)
300 * Can be broken over multiple lines.
301 * An apostrophe ' must be quoted by another apostrophe.
302 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used.
303 * @param objects the parameters for the string.
304 * Mark occurrences in {@code singularText} and {@code pluralText} with <code>{0}</code>, <code>{1}</code>, ...
305 * @return the translated string.
306 * @see #tr
307 * @see #trc
308 * @see #trn
309 */
310 public static String trnc(String context, String singularText, String pluralText, long n, Object... objects) {
311 return MessageFormat.format(gettextn(singularText, pluralText, context, n), objects);
312 }
313
314 private static String gettext(String text, String ctx, boolean lazy) {
315 int i;
316 if (ctx == null && text.startsWith("_:") && (i = text.indexOf('\n')) >= 0) {
317 ctx = text.substring(2, i-1);
318 text = text.substring(i+1);
319 }
320 if (strings != null) {
321 String trans = strings.get(ctx == null ? text : "_:"+ctx+'\n'+text);
322 if (trans != null)
323 return trans;
324 }
325 if (pstrings != null) {
326 i = pluralEval(1);
327 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+'\n'+text);
328 if (trans != null && trans.length > i)
329 return trans[i];
330 }
331 return lazy ? gettext(text, null) : text;
332 }
333
334 private static String gettext(String text, String ctx) {
335 return gettext(text, ctx, false);
336 }
337
338 /* try without context, when context try fails */
339 private static String gettext_lazy(String text, String ctx) {
340 return gettext(text, ctx, true);
341 }
342
343 private static String gettextn(String text, String plural, String ctx, long num) {
344 int i;
345 if (ctx == null && text.startsWith("_:") && (i = text.indexOf('\n')) >= 0) {
346 ctx = text.substring(2, i-1);
347 text = text.substring(i+1);
348 }
349 if (pstrings != null) {
350 i = pluralEval(num);
351 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+'\n'+text);
352 if (trans != null && trans.length > i)
353 return trans[i];
354 }
355
356 return num == 1 ? text : plural;
357 }
358
359 public static String escape(String msg) {
360 if (msg == null) return null;
361 return msg.replace("\'", "\'\'").replace("{", "\'{\'").replace("}", "\'}\'");
362 }
363
364 private static URL getTranslationFile(String lang) {
365 return Main.class.getResource("/data/"+lang.replace('@', '-')+".lang");
366 }
367
368 /**
369 * Get a list of all available JOSM Translations.
370 * @return an array of locale objects.
371 */
372 public static Locale[] getAvailableTranslations() {
373 Collection<Locale> v = new ArrayList<>(languages.size());
374 if (getTranslationFile("en") != null) {
375 for (String loc : languages.keySet()) {
376 if (getTranslationFile(loc) != null) {
377 v.add(LanguageInfo.getLocale(loc));
378 }
379 }
380 }
381 v.add(Locale.ENGLISH);
382 Locale[] l = new Locale[v.size()];
383 l = v.toArray(l);
384 Arrays.sort(l, new Comparator<Locale>() {
385 @Override
386 public int compare(Locale o1, Locale o2) {
387 return o1.toString().compareTo(o2.toString());
388 }
389 });
390 return l;
391 }
392
393 /**
394 * Determines if a language exists for the given code.
395 * @param code The language code
396 * @return {@code true} if a language exists, {@code false} otherwise
397 */
398 public static boolean hasCode(String code) {
399 return languages.containsKey(code);
400 }
401
402 /**
403 * I18n initialization.
404 */
405 public static void init() {
406 // Enable CLDR locale provider on Java 8 to get additional languages, such as Khmer.
407 // http://docs.oracle.com/javase/8/docs/technotes/guides/intl/enhancements.8.html#cldr
408 // FIXME: This can be removed after we switch to a minimal version of Java that enables CLDR by default
409 // or includes all languages we need in the JRE. See http://openjdk.java.net/jeps/252 for Java 9
410 Utils.updateSystemProperty("java.locale.providers", "JRE,CLDR");
411
412 //languages.put("ar", PluralMode.MODE_AR);
413 languages.put("ast", PluralMode.MODE_NOTONE);
414 languages.put("bg", PluralMode.MODE_NOTONE);
415 languages.put("be", PluralMode.MODE_RU);
416 languages.put("ca", PluralMode.MODE_NOTONE);
417 languages.put("ca@valencia", PluralMode.MODE_NOTONE);
418 languages.put("cs", PluralMode.MODE_CS);
419 languages.put("da", PluralMode.MODE_NOTONE);
420 languages.put("de", PluralMode.MODE_NOTONE);
421 languages.put("el", PluralMode.MODE_NOTONE);
422 languages.put("en_AU", PluralMode.MODE_NOTONE);
423 languages.put("en_GB", PluralMode.MODE_NOTONE);
424 languages.put("es", PluralMode.MODE_NOTONE);
425 languages.put("et", PluralMode.MODE_NOTONE);
426 //languages.put("eu", PluralMode.MODE_NOTONE);
427 languages.put("fi", PluralMode.MODE_NOTONE);
428 languages.put("fr", PluralMode.MODE_GREATERONE);
429 languages.put("gl", PluralMode.MODE_NOTONE);
430 //languages.put("he", PluralMode.MODE_NOTONE);
431 languages.put("hu", PluralMode.MODE_NOTONE);
432 languages.put("id", PluralMode.MODE_NONE);
433 //languages.put("is", PluralMode.MODE_NOTONE);
434 languages.put("it", PluralMode.MODE_NOTONE);
435 languages.put("ja", PluralMode.MODE_NONE);
436 // fully supported only with Java 8 and later (needs CLDR)
437 languages.put("km", PluralMode.MODE_NONE);
438 languages.put("lt", PluralMode.MODE_LT);
439 languages.put("nb", PluralMode.MODE_NOTONE);
440 languages.put("nl", PluralMode.MODE_NOTONE);
441 languages.put("pl", PluralMode.MODE_PL);
442 languages.put("pt", PluralMode.MODE_NOTONE);
443 languages.put("pt_BR", PluralMode.MODE_GREATERONE);
444 //languages.put("ro", PluralMode.MODE_RO);
445 languages.put("ru", PluralMode.MODE_RU);
446 languages.put("sk", PluralMode.MODE_SK);
447 //languages.put("sl", PluralMode.MODE_SL);
448 languages.put("sv", PluralMode.MODE_NOTONE);
449 //languages.put("tr", PluralMode.MODE_NONE);
450 languages.put("uk", PluralMode.MODE_RU);
451 languages.put("vi", PluralMode.MODE_NONE);
452 languages.put("zh_CN", PluralMode.MODE_NONE);
453 languages.put("zh_TW", PluralMode.MODE_NONE);
454
455 /* try initial language settings, may be changed later again */
456 if (!load(LanguageInfo.getJOSMLocaleCode())) {
457 Locale.setDefault(Locale.ENGLISH);
458 }
459 }
460
461 public static void addTexts(File source) {
462 if ("en".equals(loadedCode))
463 return;
464 final String enfile = "data/en.lang";
465 final String langfile = "data/"+loadedCode+".lang";
466 try (
467 FileInputStream fis = new FileInputStream(source);
468 JarInputStream jar = new JarInputStream(fis)
469 ) {
470 ZipEntry e;
471 boolean found = false;
472 while (!found && (e = jar.getNextEntry()) != null) {
473 String name = e.getName();
474 if (enfile.equals(name))
475 found = true;
476 }
477 if (found) {
478 try (
479 FileInputStream fisTrans = new FileInputStream(source);
480 JarInputStream jarTrans = new JarInputStream(fisTrans)
481 ) {
482 found = false;
483 while (!found && (e = jarTrans.getNextEntry()) != null) {
484 String name = e.getName();
485 if (name.equals(langfile))
486 found = true;
487 }
488 if (found)
489 load(jar, jarTrans, true);
490 }
491 }
492 } catch (IOException e) {
493 // Ignore
494 if (Main.isTraceEnabled()) {
495 Main.trace(e.getMessage());
496 }
497 }
498 }
499
500 private static boolean load(String l) {
501 if ("en".equals(l) || "en_US".equals(l)) {
502 strings = null;
503 pstrings = null;
504 loadedCode = "en";
505 pluralMode = PluralMode.MODE_NOTONE;
506 return true;
507 }
508 URL en = getTranslationFile("en");
509 if (en == null)
510 return false;
511 URL tr = getTranslationFile(l);
512 if (tr == null || !languages.containsKey(l)) {
513 return false;
514 }
515 try (
516 InputStream enStream = en.openStream();
517 InputStream trStream = tr.openStream()
518 ) {
519 if (load(enStream, trStream, false)) {
520 pluralMode = languages.get(l);
521 loadedCode = l;
522 return true;
523 }
524 } catch (IOException e) {
525 // Ignore exception
526 if (Main.isTraceEnabled()) {
527 Main.trace(e.getMessage());
528 }
529 }
530 return false;
531 }
532
533 private static boolean load(InputStream en, InputStream tr, boolean add) {
534 Map<String, String> s;
535 Map<String, String[]> p;
536 if (add) {
537 s = strings;
538 p = pstrings;
539 } else {
540 s = new HashMap<>();
541 p = new HashMap<>();
542 }
543 /* file format:
544 Files are always a group. English file and translated file must provide identical datasets.
545
546 for all single strings:
547 {
548 unsigned short (2 byte) stringlength
549 - length 0 indicates missing translation
550 - length 0xFFFE indicates translation equal to original, but otherwise is equal to length 0
551 string
552 }
553 unsigned short (2 byte) 0xFFFF (marks end of single strings)
554 for all multi strings:
555 {
556 unsigned char (1 byte) stringcount
557 - count 0 indicates missing translations
558 - count 0xFE indicates translations equal to original, but otherwise is equal to length 0
559 for stringcount
560 unsigned short (2 byte) stringlength
561 string
562 }
563 */
564 try {
565 InputStream ens = new BufferedInputStream(en);
566 InputStream trs = new BufferedInputStream(tr);
567 byte[] enlen = new byte[2];
568 byte[] trlen = new byte[2];
569 boolean multimode = false;
570 byte[] str = new byte[4096];
571 for (;;) {
572 if (multimode) {
573 int ennum = ens.read();
574 int trnum = trs.read();
575 if (trnum == 0xFE) /* marks identical string, handle equally to non-translated */
576 trnum = 0;
577 if ((ennum == -1 && trnum != -1) || (ennum != -1 && trnum == -1)) /* files do not match */
578 return false;
579 if (ennum == -1) {
580 break;
581 }
582 String[] enstrings = new String[ennum];
583 for (int i = 0; i < ennum; ++i) {
584 int val = ens.read(enlen);
585 if (val != 2) /* file corrupt */
586 return false;
587 val = (enlen[0] < 0 ? 256+enlen[0] : enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1] : enlen[1]);
588 if (val > str.length) {
589 str = new byte[val];
590 }
591 int rval = ens.read(str, 0, val);
592 if (rval != val) /* file corrupt */
593 return false;
594 enstrings[i] = new String(str, 0, val, StandardCharsets.UTF_8);
595 }
596 String[] trstrings = new String[trnum];
597 for (int i = 0; i < trnum; ++i) {
598 int val = trs.read(trlen);
599 if (val != 2) /* file corrupt */
600 return false;
601 val = (trlen[0] < 0 ? 256+trlen[0] : trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1] : trlen[1]);
602 if (val > str.length) {
603 str = new byte[val];
604 }
605 int rval = trs.read(str, 0, val);
606 if (rval != val) /* file corrupt */
607 return false;
608 trstrings[i] = new String(str, 0, val, StandardCharsets.UTF_8);
609 }
610 if (trnum > 0 && !p.containsKey(enstrings[0])) {
611 p.put(enstrings[0], trstrings);
612 }
613 } else {
614 int enval = ens.read(enlen);
615 int trval = trs.read(trlen);
616 if (enval != trval) /* files do not match */
617 return false;
618 if (enval == -1) {
619 break;
620 }
621 if (enval != 2) /* files corrupt */
622 return false;
623 enval = (enlen[0] < 0 ? 256+enlen[0] : enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1] : enlen[1]);
624 trval = (trlen[0] < 0 ? 256+trlen[0] : trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1] : trlen[1]);
625 if (trval == 0xFFFE) /* marks identical string, handle equally to non-translated */
626 trval = 0;
627 if (enval == 0xFFFF) {
628 multimode = true;
629 if (trval != 0xFFFF) /* files do not match */
630 return false;
631 } else {
632 if (enval > str.length) {
633 str = new byte[enval];
634 }
635 if (trval > str.length) {
636 str = new byte[trval];
637 }
638 int val = ens.read(str, 0, enval);
639 if (val != enval) /* file corrupt */
640 return false;
641 String enstr = new String(str, 0, enval, StandardCharsets.UTF_8);
642 if (trval != 0) {
643 val = trs.read(str, 0, trval);
644 if (val != trval) /* file corrupt */
645 return false;
646 String trstr = new String(str, 0, trval, StandardCharsets.UTF_8);
647 if (!s.containsKey(enstr))
648 s.put(enstr, trstr);
649 }
650 }
651 }
652 }
653 } catch (IOException e) {
654 return false;
655 }
656 if (!s.isEmpty()) {
657 strings = s;
658 pstrings = p;
659 return true;
660 }
661 return false;
662 }
663
664 /**
665 * Sets the default locale (see {@link Locale#setDefault(Locale)} to the local
666 * given by <code>localName</code>.
667 *
668 * Ignored if localeName is null. If the locale with name <code>localName</code>
669 * isn't found the default local is set to <tt>en</tt> (english).
670 *
671 * @param localeName the locale name. Ignored if null.
672 */
673 public static void set(String localeName) {
674 if (localeName != null) {
675 Locale l = LanguageInfo.getLocale(localeName);
676 if (load(LanguageInfo.getJOSMLocaleCode(l))) {
677 Locale.setDefault(l);
678 } else {
679 if (!"en".equals(l.getLanguage())) {
680 Main.info(tr("Unable to find translation for the locale {0}. Reverting to {1}.",
681 LanguageInfo.getDisplayName(l), LanguageInfo.getDisplayName(Locale.getDefault())));
682 } else {
683 strings = null;
684 pstrings = null;
685 }
686 }
687 }
688 }
689
690 /**
691 * Localizations for file chooser dialog.
692 * For some locales (e.g. de, fr) translations are provided
693 * by Java, but not for others (e.g. ru, uk).
694 */
695 public static void translateJavaInternalMessages() {
696 Locale l = Locale.getDefault();
697
698 AbstractFileChooser.setDefaultLocale(l);
699 JFileChooser.setDefaultLocale(l);
700 JColorChooser.setDefaultLocale(l);
701 for (String key : javaInternalMessageKeys) {
702 String us = UIManager.getString(key, Locale.US);
703 String loc = UIManager.getString(key, l);
704 // only provide custom translation if it is not already localized by Java
705 if (us != null && us.equals(loc)) {
706 UIManager.put(key, tr(us));
707 }
708 }
709 }
710
711 private static int pluralEval(long n) {
712 switch(pluralMode) {
713 case MODE_NOTONE: /* bg, da, de, el, en, en_GB, es, et, eu, fi, gl, is, it, iw_IL, nb, nl, sv */
714 return (n != 1) ? 1 : 0;
715 case MODE_NONE: /* id, vi, ja, km, tr, zh_CN, zh_TW */
716 return 0;
717 case MODE_GREATERONE: /* fr, pt_BR */
718 return (n > 1) ? 1 : 0;
719 case MODE_CS:
720 return (n == 1) ? 0 : (((n >= 2) && (n <= 4)) ? 1 : 2);
721 //case MODE_AR:
722 // return ((n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((((n % 100) >= 3)
723 // && ((n % 100) <= 10)) ? 3 : ((((n % 100) >= 11) && ((n % 100) <= 99)) ? 4 : 5)))));
724 case MODE_PL:
725 return (n == 1) ? 0 : (((((n % 10) >= 2) && ((n % 10) <= 4))
726 && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2);
727 //case MODE_RO:
728 // return ((n == 1) ? 0 : ((((n % 100) > 19) || (((n % 100) == 0) && (n != 0))) ? 2 : 1));
729 case MODE_LT:
730 return ((n % 10) == 1) && ((n % 100) != 11) ? 0 : (((n % 10) >= 2)
731 && (((n % 100) < 10) || ((n % 100) >= 20)) ? 1 : 2);
732 case MODE_RU:
733 return (((n % 10) == 1) && ((n % 100) != 11)) ? 0 : (((((n % 10) >= 2)
734 && ((n % 10) <= 4)) && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2);
735 case MODE_SK:
736 return (n == 1) ? 1 : (((n >= 2) && (n <= 4)) ? 2 : 0);
737 //case MODE_SL:
738 // return (((n % 100) == 1) ? 1 : (((n % 100) == 2) ? 2 : ((((n % 100) == 3)
739 // || ((n % 100) == 4)) ? 3 : 0)));
740 }
741 return 0;
742 }
743
744 public static TranslationAdapter getTranslationAdapter() {
745 return new TranslationAdapter() {
746 @Override
747 public String tr(String text, Object... objects) {
748 return I18n.tr(text, objects);
749 }
750 };
751 }
752
753 /**
754 * Setup special font for Khmer script, as the default Java fonts do not display these characters.
755 *
756 * @since 8282
757 */
758 public static void setupLanguageFonts() {
759 // Use special font for Khmer script, as the default Java font do not display these characters
760 if ("km".equals(LanguageInfo.getJOSMLocaleCode())) {
761 Collection<String> fonts = Arrays.asList(
762 GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
763 for (String f : new String[]{"Khmer UI", "DaunPenh", "MoolBoran"}) {
764 if (fonts.contains(f)) {
765 GuiHelper.setUIFont(f);
766 break;
767 }
768 }
769 }
770 }
771}
Note: See TracBrowser for help on using the repository browser.