001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.tools; 003 004 import java.io.BufferedInputStream; 005 import java.io.File; 006 import java.io.FileInputStream; 007 import java.io.InputStream; 008 import java.io.IOException; 009 import java.net.URL; 010 import java.text.MessageFormat; 011 import java.util.ArrayList; 012 import java.util.Arrays; 013 import java.util.Collection; 014 import java.util.Comparator; 015 import java.util.HashMap; 016 import java.util.jar.JarInputStream; 017 import java.util.zip.ZipEntry; 018 import java.util.Locale; 019 020 import javax.swing.JColorChooser; 021 import javax.swing.JFileChooser; 022 import javax.swing.UIManager; 023 024 import org.openstreetmap.gui.jmapviewer.FeatureAdapter.TranslationAdapter; 025 import org.openstreetmap.josm.Main; 026 027 /** 028 * Internationalisation support. 029 * 030 * @author Immanuel.Scholz 031 */ 032 public class I18n { 033 private enum PluralMode { MODE_NOTONE, MODE_NONE, MODE_GREATERONE, 034 MODE_CS/*, MODE_AR*/, MODE_PL/*, MODE_RO*/, MODE_RU, MODE_SK/*, MODE_SL*/} 035 private static PluralMode pluralMode = PluralMode.MODE_NOTONE; /* english default */ 036 private static String loadedCode = "en"; 037 038 /* Localization keys for file chooser (and color chooser). */ 039 private static final String[] javaInternalMessageKeys = new String[] { 040 /* JFileChooser windows laf */ 041 "FileChooser.detailsViewActionLabelText", 042 "FileChooser.detailsViewButtonAccessibleName", 043 "FileChooser.detailsViewButtonToolTipText", 044 "FileChooser.fileAttrHeaderText", 045 "FileChooser.fileDateHeaderText", 046 "FileChooser.fileNameHeaderText", 047 "FileChooser.fileNameLabelText", 048 "FileChooser.fileSizeHeaderText", 049 "FileChooser.fileTypeHeaderText", 050 "FileChooser.filesOfTypeLabelText", 051 "FileChooser.homeFolderAccessibleName", 052 "FileChooser.homeFolderToolTipText", 053 "FileChooser.listViewActionLabelText", 054 "FileChooser.listViewButtonAccessibleName", 055 "FileChooser.listViewButtonToolTipText", 056 "FileChooser.lookInLabelText", 057 "FileChooser.newFolderAccessibleName", 058 "FileChooser.newFolderActionLabelText", 059 "FileChooser.newFolderToolTipText", 060 "FileChooser.refreshActionLabelText", 061 "FileChooser.saveInLabelText", 062 "FileChooser.upFolderAccessibleName", 063 "FileChooser.upFolderToolTipText", 064 "FileChooser.viewMenuLabelText", 065 066 /* JFileChooser gtk laf */ 067 "FileChooser.acceptAllFileFilterText", 068 "FileChooser.cancelButtonText", 069 "FileChooser.cancelButtonToolTipText", 070 "FileChooser.deleteFileButtonText", 071 "FileChooser.filesLabelText", 072 "FileChooser.filterLabelText", 073 "FileChooser.foldersLabelText", 074 "FileChooser.newFolderButtonText", 075 "FileChooser.newFolderDialogText", 076 "FileChooser.openButtonText", 077 "FileChooser.openButtonToolTipText", 078 "FileChooser.openDialogTitleText", 079 "FileChooser.pathLabelText", 080 "FileChooser.renameFileButtonText", 081 "FileChooser.renameFileDialogText", 082 "FileChooser.renameFileErrorText", 083 "FileChooser.renameFileErrorTitle", 084 "FileChooser.saveButtonText", 085 "FileChooser.saveButtonToolTipText", 086 "FileChooser.saveDialogTitleText", 087 088 /* JFileChooser motif laf */ 089 //"FileChooser.cancelButtonText", 090 //"FileChooser.cancelButtonToolTipText", 091 "FileChooser.enterFileNameLabelText", 092 //"FileChooser.filesLabelText", 093 //"FileChooser.filterLabelText", 094 //"FileChooser.foldersLabelText", 095 "FileChooser.helpButtonText", 096 "FileChooser.helpButtonToolTipText", 097 //"FileChooser.openButtonText", 098 //"FileChooser.openButtonToolTipText", 099 //"FileChooser.openDialogTitleText", 100 //"FileChooser.pathLabelText", 101 //"FileChooser.saveButtonText", 102 //"FileChooser.saveButtonToolTipText", 103 //"FileChooser.saveDialogTitleText", 104 "FileChooser.updateButtonText", 105 "FileChooser.updateButtonToolTipText", 106 107 /* gtk color chooser */ 108 "GTKColorChooserPanel.blueText", 109 "GTKColorChooserPanel.colorNameText", 110 "GTKColorChooserPanel.greenText", 111 "GTKColorChooserPanel.hueText", 112 "GTKColorChooserPanel.nameText", 113 "GTKColorChooserPanel.redText", 114 "GTKColorChooserPanel.saturationText", 115 "GTKColorChooserPanel.valueText", 116 117 /* JOptionPane */ 118 "OptionPane.okButtonText", 119 "OptionPane.yesButtonText", 120 "OptionPane.noButtonText", 121 "OptionPane.cancelButtonText" 122 }; 123 private static HashMap<String, String> strings = null; 124 private static HashMap<String, String[]> pstrings = null; 125 private static HashMap<String, PluralMode> languages = new HashMap<String, PluralMode>(); 126 127 /** 128 * Translates some text for the current locale. 129 * These strings are collected by a script that runs on the source code files. 130 * After translation, the localizations are distributed with the main program. 131 * <br/> 132 * For example, {@code tr("JOSM''s default value is ''{0}''.", val)}. 133 * <br/> 134 * Use {@link #trn} for distinguishing singular from plural text, i.e., 135 * do not use {@code tr(size == 1 ? "singular" : "plural")} nor 136 * {@code size == 1 ? tr("singular") : tr("plural")} 137 * 138 * @param text the text to translate. 139 * Must be a string literal. (No constants or local vars.) 140 * Can be broken over multiple lines. 141 * An apostrophe ' must be quoted by another apostrophe. 142 * @param objects the parameters for the string. 143 * Mark occurrences in {@code text} with {@code {0}}, {@code {1}}, ... 144 * @return the translated string. 145 * @see #trn 146 * @see #trc 147 * @see #trnc 148 */ 149 public static final String tr(String text, Object... objects) { 150 if (text == null) return null; 151 return MessageFormat.format(gettext(text, null), objects); 152 } 153 154 /** 155 * Translates some text in a context for the current locale. 156 * There can be different translations for the same text within different contexts. 157 * 158 * @param context string that helps translators to find an appropriate 159 * translation for {@code text}. 160 * @param text the text to translate. 161 * @return the translated string. 162 * @see #tr 163 * @see #trn 164 * @see #trnc 165 */ 166 public static final String trc(String context, String text) { 167 if (context == null) 168 return tr(text); 169 if (text == null) 170 return null; 171 return MessageFormat.format(gettext(text, context), (Object)null); 172 } 173 174 public static final String trc_lazy(String context, String text) { 175 if (context == null) 176 return tr(text); 177 if (text == null) 178 return null; 179 return MessageFormat.format(gettext_lazy(text, context), (Object)null); 180 } 181 182 /** 183 * Marks a string for translation (such that a script can harvest 184 * the translatable strings from the source files). 185 * 186 * For example, {@code 187 * String[] options = new String[] {marktr("up"), marktr("down")}; 188 * lbl.setText(tr(options[0]));} 189 * @param text the string to be marked for translation. 190 * @return {@code text} unmodified. 191 */ 192 public static final String marktr(String text) { 193 return text; 194 } 195 196 public static final String marktrc(String context, String text) { 197 return text; 198 } 199 200 /** 201 * Translates some text for the current locale and distinguishes between 202 * {@code singularText} and {@code pluralText} depending on {@code n}. 203 * <br/> 204 * For instance, {@code trn("There was an error!", "There were errors!", i)} or 205 * {@code trn("Found {0} error in {1}!", "Found {0} errors in {1}!", i, Integer.toString(i), url)}. 206 * 207 * @param singularText the singular text to translate. 208 * Must be a string literal. (No constants or local vars.) 209 * Can be broken over multiple lines. 210 * An apostrophe ' must be quoted by another apostrophe. 211 * @param pluralText the plural text to translate. 212 * Must be a string literal. (No constants or local vars.) 213 * Can be broken over multiple lines. 214 * An apostrophe ' must be quoted by another apostrophe. 215 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used. 216 * @param objects the parameters for the string. 217 * Mark occurrences in {@code singularText} and {@code pluralText} with {@code {0}}, {@code {1}}, ... 218 * @return the translated string. 219 * @see #tr 220 * @see #trc 221 * @see #trnc 222 */ 223 public static final String trn(String singularText, String pluralText, long n, Object... objects) { 224 return MessageFormat.format(gettextn(singularText, pluralText, null, n), objects); 225 } 226 227 /** 228 * Translates some text in a context for the current locale and distinguishes between 229 * {@code singularText} and {@code pluralText} depending on {@code n}. 230 * There can be different translations for the same text within different contexts. 231 * 232 * @param context string that helps translators to find an appropriate 233 * translation for {@code text}. 234 * @param singularText the singular text to translate. 235 * Must be a string literal. (No constants or local vars.) 236 * Can be broken over multiple lines. 237 * An apostrophe ' must be quoted by another apostrophe. 238 * @param pluralText the plural text to translate. 239 * Must be a string literal. (No constants or local vars.) 240 * Can be broken over multiple lines. 241 * An apostrophe ' must be quoted by another apostrophe. 242 * @param n a number to determine whether {@code singularText} or {@code pluralText} is used. 243 * @param objects the parameters for the string. 244 * Mark occurrences in {@code singularText} and {@code pluralText} with {@code {0}}, {@code {1}}, ... 245 * @return the translated string. 246 * @see #tr 247 * @see #trc 248 * @see #trn 249 */ 250 public static final String trnc(String context, String singularText, String pluralText, long n, Object... objects) { 251 return MessageFormat.format(gettextn(singularText, pluralText, context, n), objects); 252 } 253 254 private static final String gettext(String text, String ctx, boolean lazy) 255 { 256 int i; 257 if(ctx == null && text.startsWith("_:") && (i = text.indexOf("\n")) >= 0) 258 { 259 ctx = text.substring(2,i-1); 260 text = text.substring(i+1); 261 } 262 if(strings != null) 263 { 264 String trans = strings.get(ctx == null ? text : "_:"+ctx+"\n"+text); 265 if(trans != null) 266 return trans; 267 } 268 if(pstrings != null) { 269 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+"\n"+text); 270 if(trans != null) 271 return trans[0]; 272 } 273 return lazy ? gettext(text, null) : text; 274 } 275 276 private static final String gettext(String text, String ctx) { 277 return gettext(text, ctx, false); 278 } 279 280 281 /* try without context, when context try fails */ 282 private static final String gettext_lazy(String text, String ctx) { 283 return gettext(text, ctx, true); 284 } 285 286 private static final String gettextn(String text, String plural, String ctx, long num) 287 { 288 int i; 289 if(ctx == null && text.startsWith("_:") && (i = text.indexOf("\n")) >= 0) 290 { 291 ctx = text.substring(2,i-1); 292 text = text.substring(i+1); 293 } 294 if(pstrings != null) 295 { 296 i = pluralEval(num); 297 String[] trans = pstrings.get(ctx == null ? text : "_:"+ctx+"\n"+text); 298 if(trans != null && trans.length > i) 299 return trans[i]; 300 } 301 302 return num == 1 ? text : plural; 303 } 304 305 public static String escape(String msg) { 306 if (msg == null) return null; 307 return msg.replace("\'", "\'\'").replace("{", "\'{\'").replace("}", "\'}\'"); 308 } 309 310 private static URL getTranslationFile(String lang) { 311 return Main.class.getResource("/data/"+lang+".lang"); 312 } 313 314 /** 315 * Get a list of all available JOSM Translations. 316 * @return an array of locale objects. 317 */ 318 public static final Locale[] getAvailableTranslations() { 319 Collection<Locale> v = new ArrayList<Locale>(languages.size()); 320 if(getTranslationFile("en") != null) 321 { 322 for (String loc : languages.keySet()) { 323 if(getTranslationFile(loc) != null) { 324 v.add(LanguageInfo.getLocale(loc)); 325 } 326 } 327 } 328 v.add(Locale.ENGLISH); 329 Locale[] l = new Locale[v.size()]; 330 l = v.toArray(l); 331 Arrays.sort(l, new Comparator<Locale>() { 332 public int compare(Locale o1, Locale o2) { 333 return o1.toString().compareTo(o2.toString()); 334 } 335 }); 336 return l; 337 } 338 339 public static boolean hasCode(String code) 340 { 341 return languages.containsKey(code); 342 } 343 344 public static void init() 345 { 346 //languages.put("ar", PluralMode.MODE_AR); 347 languages.put("bg", PluralMode.MODE_NOTONE); 348 languages.put("ca", PluralMode.MODE_NOTONE); 349 languages.put("cs", PluralMode.MODE_CS); 350 languages.put("da", PluralMode.MODE_NOTONE); 351 languages.put("de", PluralMode.MODE_NOTONE); 352 languages.put("el", PluralMode.MODE_NOTONE); 353 languages.put("en_AU", PluralMode.MODE_NOTONE); 354 languages.put("en_GB", PluralMode.MODE_NOTONE); 355 languages.put("es", PluralMode.MODE_NOTONE); 356 languages.put("et", PluralMode.MODE_NOTONE); 357 languages.put("eu", PluralMode.MODE_NOTONE); 358 languages.put("fi", PluralMode.MODE_NOTONE); 359 languages.put("fr", PluralMode.MODE_GREATERONE); 360 languages.put("gl", PluralMode.MODE_NOTONE); 361 //languages.put("he", PluralMode.MODE_NOTONE); 362 languages.put("hu", PluralMode.MODE_NOTONE); 363 languages.put("id", PluralMode.MODE_NONE); 364 //languages.put("is", PluralMode.MODE_NOTONE); 365 languages.put("it", PluralMode.MODE_NOTONE); 366 languages.put("ja", PluralMode.MODE_NONE); 367 languages.put("nb", PluralMode.MODE_NOTONE); 368 languages.put("nl", PluralMode.MODE_NOTONE); 369 languages.put("pl", PluralMode.MODE_PL); 370 languages.put("pt", PluralMode.MODE_NOTONE); 371 languages.put("pt_BR", PluralMode.MODE_GREATERONE); 372 //languages.put("ro", PluralMode.MODE_RO); 373 languages.put("ru", PluralMode.MODE_RU); 374 languages.put("sk", PluralMode.MODE_SK); 375 //languages.put("sl", PluralMode.MODE_SL); 376 languages.put("sv", PluralMode.MODE_NOTONE); 377 languages.put("tr", PluralMode.MODE_NONE); 378 languages.put("uk", PluralMode.MODE_RU); 379 languages.put("zh_CN", PluralMode.MODE_NONE); 380 languages.put("zh_TW", PluralMode.MODE_NONE); 381 382 /* try initial language settings, may be changed later again */ 383 if(!load(Locale.getDefault().toString())) { 384 Locale.setDefault(Locale.ENGLISH); 385 } 386 } 387 388 public static void addTexts(File source) 389 { 390 if(loadedCode.equals("en")) 391 return; 392 FileInputStream fis = null; 393 JarInputStream jar = null; 394 FileInputStream fisTrans = null; 395 JarInputStream jarTrans = null; 396 String enfile = "data/en.lang"; 397 String langfile = "data/"+loadedCode+".lang"; 398 try 399 { 400 ZipEntry e; 401 fis = new FileInputStream(source); 402 jar = new JarInputStream(fis); 403 boolean found = false; 404 while(!found && (e = jar.getNextEntry()) != null) 405 { 406 String name = e.getName(); 407 if(name.equals(enfile)) 408 found = true; 409 } 410 if(found) 411 { 412 fisTrans = new FileInputStream(source); 413 jarTrans = new JarInputStream(fisTrans); 414 found = false; 415 while(!found && (e = jarTrans.getNextEntry()) != null) 416 { 417 String name = e.getName(); 418 if(name.equals(langfile)) 419 found = true; 420 } 421 if(found) 422 load(jar, jarTrans, true); 423 } 424 } 425 catch(IOException e) 426 { 427 } 428 finally 429 { 430 try 431 { 432 if(jar != null) 433 jar.close(); 434 if(fis != null) 435 fis.close(); 436 if(jarTrans != null) 437 jarTrans.close(); 438 if(fisTrans != null) 439 fisTrans.close(); 440 } 441 catch(IOException e) 442 { 443 } 444 } 445 } 446 447 private static boolean load(String l) 448 { 449 if(l.equals("en") || l.equals("en_US")) 450 { 451 strings = null; 452 pstrings = null; 453 loadedCode = "en"; 454 pluralMode = PluralMode.MODE_NOTONE; 455 return true; 456 } 457 URL en = getTranslationFile("en"); 458 if(en == null) 459 return false; 460 URL tr = getTranslationFile(l); 461 if(tr == null || !languages.containsKey(l)) 462 { 463 int i = l.indexOf('_'); 464 if (i > 0) { 465 l = l.substring(0, i); 466 } 467 tr = getTranslationFile(l); 468 if(tr == null || !languages.containsKey(l)) 469 return false; 470 } 471 try 472 { 473 if(load(en.openStream(), tr.openStream(), false)) 474 { 475 pluralMode = languages.get(l); 476 loadedCode = l; 477 return true; 478 } 479 } 480 catch(IOException e) 481 { 482 } 483 return false; 484 } 485 486 private static boolean load(InputStream en, InputStream tr, boolean add) 487 { 488 HashMap<String, String> s; 489 HashMap<String, String[]> p; 490 if(add) 491 { 492 s = strings; 493 p = pstrings; 494 } 495 else 496 { 497 s = new HashMap<String, String>(); 498 p = new HashMap<String, String[]>(); 499 } 500 /* file format: 501 Files are always a group. English file and translated file must provide identical datasets. 502 503 for all single strings: 504 { 505 unsigned short (2 byte) stringlength 506 - length 0 indicates missing translation 507 - length 0xFFFE indicates translation equal to original, but otherwise is equal to length 0 508 string 509 } 510 unsigned short (2 byte) 0xFFFF (marks end of single strings) 511 for all multi strings: 512 { 513 unsigned char (1 byte) stringcount 514 - count 0 indicates missing translations 515 - count 0xFE indicates translations equal to original, but otherwise is equal to length 0 516 for stringcount 517 unsigned short (2 byte) stringlength 518 string 519 } 520 */ 521 try 522 { 523 InputStream ens = new BufferedInputStream(en); 524 InputStream trs = new BufferedInputStream(tr); 525 byte[] enlen = new byte[2]; 526 byte[] trlen = new byte[2]; 527 boolean multimode = false; 528 byte[] str = new byte[4096]; 529 for(;;) 530 { 531 if(multimode) 532 { 533 int ennum = ens.read(); 534 int trnum = trs.read(); 535 if(trnum == 0xFE) /* marks identical string, handle equally to non-translated */ 536 trnum = 0; 537 if((ennum == -1 && trnum != -1) || (ennum != -1 && trnum == -1)) /* files do not match */ 538 return false; 539 if(ennum == -1) { 540 break; 541 } 542 String[] enstrings = new String[ennum]; 543 String[] trstrings = new String[trnum]; 544 for(int i = 0; i < ennum; ++i) 545 { 546 int val = ens.read(enlen); 547 if(val != 2) /* file corrupt */ 548 return false; 549 val = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]); 550 if(val > str.length) { 551 str = new byte[val]; 552 } 553 int rval = ens.read(str, 0, val); 554 if(rval != val) /* file corrupt */ 555 return false; 556 enstrings[i] = new String(str, 0, val, "utf-8"); 557 } 558 for(int i = 0; i < trnum; ++i) 559 { 560 int val = trs.read(trlen); 561 if(val != 2) /* file corrupt */ 562 return false; 563 val = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]); 564 if(val > str.length) { 565 str = new byte[val]; 566 } 567 int rval = trs.read(str, 0, val); 568 if(rval != val) /* file corrupt */ 569 return false; 570 trstrings[i] = new String(str, 0, val, "utf-8"); 571 } 572 if(trnum > 0 && !p.containsKey(enstrings[0])) { 573 p.put(enstrings[0], trstrings); 574 } 575 } 576 else 577 { 578 int enval = ens.read(enlen); 579 int trval = trs.read(trlen); 580 if(enval != trval) /* files do not match */ 581 return false; 582 if(enval == -1) { 583 break; 584 } 585 if(enval != 2) /* files corrupt */ 586 return false; 587 enval = (enlen[0] < 0 ? 256+enlen[0]:enlen[0])*256+(enlen[1] < 0 ? 256+enlen[1]:enlen[1]); 588 trval = (trlen[0] < 0 ? 256+trlen[0]:trlen[0])*256+(trlen[1] < 0 ? 256+trlen[1]:trlen[1]); 589 if(trval == 0xFFFE) /* marks identical string, handle equally to non-translated */ 590 trval = 0; 591 if(enval == 0xFFFF) 592 { 593 multimode = true; 594 if(trval != 0xFFFF) /* files do not match */ 595 return false; 596 } 597 else 598 { 599 if(enval > str.length) { 600 str = new byte[enval]; 601 } 602 if(trval > str.length) { 603 str = new byte[trval]; 604 } 605 int val = ens.read(str, 0, enval); 606 if(val != enval) /* file corrupt */ 607 return false; 608 String enstr = new String(str, 0, enval, "utf-8"); 609 if(trval != 0) 610 { 611 val = trs.read(str, 0, trval); 612 if(val != trval) /* file corrupt */ 613 return false; 614 String trstr = new String(str, 0, trval, "utf-8"); 615 if(!s.containsKey(enstr)) 616 s.put(enstr, trstr); 617 } 618 } 619 } 620 } 621 } 622 catch(IOException e) 623 { 624 return false; 625 } 626 if(!s.isEmpty()) 627 { 628 strings = s; 629 pstrings = p; 630 return true; 631 } 632 return false; 633 } 634 635 /** 636 * Sets the default locale (see {@link Locale#setDefault(Locale)} to the local 637 * given by <code>localName</code>. 638 * 639 * Ignored if localeName is null. If the locale with name <code>localName</code> 640 * isn't found the default local is set to <tt>en</tt> (english). 641 * 642 * @param localeName the locale name. Ignored if null. 643 */ 644 public static void set(String localeName){ 645 if (localeName != null) { 646 Locale l = LanguageInfo.getLocale(localeName); 647 if (load(LanguageInfo.getJOSMLocaleCode(l))) { 648 Locale.setDefault(l); 649 } else { 650 if (!l.getLanguage().equals("en")) { 651 System.out.println(tr("Unable to find translation for the locale {0}. Reverting to {1}.", 652 l.getDisplayName(), Locale.getDefault().getDisplayName())); 653 } else { 654 strings = null; 655 pstrings = null; 656 } 657 } 658 } 659 } 660 661 /** 662 * Localizations for file chooser dialog. 663 * For some locales (e.g. de, fr) translations are provided 664 * by Java, but not for others (e.g. ru, uk). 665 */ 666 public static void translateJavaInternalMessages() { 667 Locale l = Locale.getDefault(); 668 669 JFileChooser.setDefaultLocale(l); 670 JColorChooser.setDefaultLocale(l); 671 for (String key : javaInternalMessageKeys) { 672 String us = UIManager.getString(key, Locale.US); 673 String loc = UIManager.getString(key, l); 674 // only provide custom translation if it is not already localized by Java 675 if (us != null && us.equals(loc)) { 676 UIManager.put(key, tr(us)); 677 } 678 } 679 } 680 681 private static int pluralEval(long n) 682 { 683 switch(pluralMode) 684 { 685 case MODE_NOTONE: /* bg, da, de, el, en, en_GB, es, et, eu, fi, gl, is, it, iw_IL, nb, nl, sv */ 686 return ((n != 1) ? 1 : 0); 687 case MODE_NONE: /* ja, tr, zh_CN, zh_TW */ 688 return 0; 689 case MODE_GREATERONE: /* fr, pt_BR */ 690 return ((n > 1) ? 1 : 0); 691 case MODE_CS: 692 return ((n == 1) ? 0 : (((n >= 2) && (n <= 4)) ? 1 : 2)); 693 //case MODE_AR: 694 // return ((n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((((n % 100) >= 3) 695 // && ((n % 100) <= 10)) ? 3 : ((((n % 100) >= 11) && ((n % 100) <= 99)) ? 4 : 5))))); 696 case MODE_PL: 697 return ((n == 1) ? 0 : (((((n % 10) >= 2) && ((n % 10) <= 4)) 698 && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2)); 699 //case MODE_RO: 700 // return ((n == 1) ? 0 : ((((n % 100) > 19) || (((n % 100) == 0) && (n != 0))) ? 2 : 1)); 701 case MODE_RU: 702 return ((((n % 10) == 1) && ((n % 100) != 11)) ? 0 : (((((n % 10) >= 2) 703 && ((n % 10) <= 4)) && (((n % 100) < 10) || ((n % 100) >= 20))) ? 1 : 2)); 704 case MODE_SK: 705 return ((n == 1) ? 1 : (((n >= 2) && (n <= 4)) ? 2 : 0)); 706 //case MODE_SL: 707 // return (((n % 100) == 1) ? 1 : (((n % 100) == 2) ? 2 : ((((n % 100) == 3) 708 // || ((n % 100) == 4)) ? 3 : 0))); 709 } 710 return 0; 711 } 712 713 public static TranslationAdapter getTranslationAdapter() { 714 return new TranslationAdapter() { 715 @Override 716 public String tr(String text, Object... objects) { 717 return I18n.tr(text, objects); 718 } 719 }; 720 } 721 }