001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.tools; 003 004import java.util.Collection; 005import java.util.LinkedList; 006import java.util.Locale; 007 008public final class LanguageInfo { 009 010 private LanguageInfo() { 011 // Hide default constructor for utils classes 012 } 013 014 /** 015 * Type of the locale to use 016 * @since 5915 017 */ 018 public enum LocaleType { 019 /** The current default language */ 020 DEFAULT, 021 /** The current default language, but not english */ 022 DEFAULTNOTENGLISH, 023 /** The base language (i.e. pt for pt_BR) */ 024 BASELANGUAGE, 025 /** The standard english texts */ 026 ENGLISH 027 } 028 029 /** 030 * Replies the wiki language prefix for the given locale. The wiki language 031 * prefix has the form 'Xy:' where 'Xy' is a ISO 639 language code in title 032 * case (or Xy_AB: for sub languages). 033 * 034 * @param type the type 035 * @return the wiki language prefix or {@code null} for {@link LocaleType#BASELANGUAGE}, when 036 * base language is identical to default or english 037 * @since 5915 038 */ 039 public static String getWikiLanguagePrefix(LocaleType type) { 040 if (type == LocaleType.ENGLISH) 041 return ""; 042 043 String code = getJOSMLocaleCode(); 044 if (type == LocaleType.BASELANGUAGE) { 045 if (code.matches("[^_]+_[^_]+")) { 046 code = code.substring(0, 2); 047 if ("en".equals(code)) 048 return null; 049 } else { 050 return null; 051 } 052 } else if (type == LocaleType.DEFAULTNOTENGLISH && "en".equals(code)) { 053 return null; 054 } else if (code.matches(".+@.+")) { 055 return code.substring(0, 1).toUpperCase(Locale.ENGLISH) + code.substring(1, 2) 056 + '-' + code.substring(3, 4).toUpperCase(Locale.ENGLISH) + code.substring(4) + ':'; 057 } 058 return code.substring(0, 1).toUpperCase(Locale.ENGLISH) + code.substring(1) + ':'; 059 } 060 061 /** 062 * Replies the wiki language prefix for the current locale. 063 * 064 * @return the wiki language prefix 065 * @see Locale#getDefault() 066 * @see #getWikiLanguagePrefix(LocaleType) 067 */ 068 public static String getWikiLanguagePrefix() { 069 return getWikiLanguagePrefix(LocaleType.DEFAULT); 070 } 071 072 /** 073 * Replies the JOSM locale code for the default locale. 074 * 075 * @return the JOSM locale code for the default locale 076 * @see #getJOSMLocaleCode(Locale) 077 */ 078 public static String getJOSMLocaleCode() { 079 return getJOSMLocaleCode(Locale.getDefault()); 080 } 081 082 /** 083 * Replies the locale code used by JOSM for a given locale. 084 * 085 * In most cases JOSM uses the 2-character ISO 639 language code ({@link Locale#getLanguage()} 086 * to identify the locale of a localized resource, but in some cases it may use the 087 * programmatic name for locales, as replied by {@link Locale#toString()}. 088 * 089 * For unknown country codes and variants this function already does fallback to 090 * internally known translations. 091 * 092 * @param locale the locale. Replies "en" if null. 093 * @return the JOSM code for the given locale 094 */ 095 public static String getJOSMLocaleCode(Locale locale) { 096 if (locale == null) return "en"; 097 for (String full : getLanguageCodes(locale)) { 098 if ("iw_IL".equals(full)) 099 return "he"; 100 else if ("in".equals(full)) 101 return "id"; 102 else if (I18n.hasCode(full)) // catch all non-single codes 103 return full; 104 } 105 106 // return single code as fallback 107 return locale.getLanguage(); 108 } 109 110 /** 111 * Replies the locale code used by Java for a given locale. 112 * 113 * In most cases JOSM and Java uses the same codes, but for some exceptions this is needed. 114 * 115 * @param localeName the locale. Replies "en" if null. 116 * @return the Java code for the given locale 117 * @since 8232 118 */ 119 public static String getJavaLocaleCode(String localeName) { 120 if (localeName == null) return "en"; 121 if ("ca@valencia".equals(localeName)) { 122 localeName = "ca__valencia"; 123 } else if ("he".equals(localeName)) { 124 localeName = "iw_IL"; 125 } else if ("id".equals(localeName)) { 126 localeName = "in"; 127 } 128 return localeName; 129 } 130 131 /** 132 * Replies the display string used by JOSM for a given locale. 133 * 134 * In most cases returns text replied by {@link Locale#getDisplayName()}, for some 135 * locales an override is used (i.e. when unsupported by Java). 136 * 137 * @param locale the locale. Replies "en" if null. 138 * @return the display string for the given locale 139 * @since 8232 140 */ 141 public static String getDisplayName(Locale locale) { 142 return locale.getDisplayName(); 143 } 144 145 /** 146 * Replies the locale used by Java for a given language code. 147 * 148 * Accepts JOSM and Java codes as input. 149 * 150 * @param localeName the locale code. 151 * @return the resulting locale 152 */ 153 public static Locale getLocale(String localeName) { 154 int country = localeName.indexOf('_'); 155 int variant = localeName.indexOf('@'); 156 if (variant < 0 && country >= 0) 157 variant = localeName.indexOf('_', country+1); 158 Locale l; 159 if (variant > 0 && country > 0) { 160 l = new Locale(localeName.substring(0, country), localeName.substring(country+1, variant), localeName.substring(variant + 1)); 161 } else if (variant > 0) { 162 l = new Locale(localeName.substring(0, variant), "", localeName.substring(variant + 1)); 163 } else if (country > 0) { 164 l = new Locale(localeName.substring(0, country), localeName.substring(country + 1)); 165 } else { 166 l = new Locale(localeName); 167 } 168 return l; 169 } 170 171 /** 172 * Check if a new language is better than a previous existing. Can be used in classes where 173 * multiple user supplied language marked strings appear and the best one is searched. Following 174 * priorities: current language, english, any other 175 * 176 * @param oldLanguage the language code of the existing string 177 * @param newLanguage the language code of the new string 178 * @return true if new one is better 179 * @since 8091 180 */ 181 public static boolean isBetterLanguage(String oldLanguage, String newLanguage) { 182 if (oldLanguage == null) 183 return true; 184 String want = getJOSMLocaleCode(); 185 return want.equals(newLanguage) || (!want.equals(oldLanguage) && newLanguage.startsWith("en")); 186 } 187 188 /** 189 * Replies the language prefix for use in XML elements (with a dot appended). 190 * 191 * @return the XML language prefix 192 * @see #getJOSMLocaleCode() 193 */ 194 public static String getLanguageCodeXML() { 195 String code = getJOSMLocaleCode(); 196 code = code.replace('@', '-'); 197 return code+'.'; 198 } 199 200 /** 201 * Replies the language prefix for use in manifests (with an underscore appended). 202 * 203 * @return the manifest language prefix 204 * @see #getJOSMLocaleCode() 205 */ 206 public static String getLanguageCodeManifest() { 207 String code = getJOSMLocaleCode(); 208 code = code.replace('@', '-'); 209 return code+'_'; 210 } 211 212 /** 213 * Replies a list of language codes for local names. Prefixes range from very specific 214 * to more generic. 215 * <ul> 216 * <li>lang_COUNTRY@variant of the current locale</li> 217 * <li>lang@variant of the current locale</li> 218 * <li>lang_COUNTRY of the current locale</li> 219 * <li>lang of the current locale</li> 220 * </ul> 221 * 222 * @param l the locale to use, <code>null</code> for default locale 223 * @return list of codes 224 * @since 8283 225 */ 226 public static Collection<String> getLanguageCodes(Locale l) { 227 Collection<String> list = new LinkedList<String>(); 228 if (l == null) 229 l = Locale.getDefault(); 230 String lang = l.getLanguage(); 231 String c = l.getCountry(); 232 String v = l.getVariant(); 233 if (c.isEmpty()) 234 c = null; 235 if (v != null && !v.isEmpty()) { 236 if (c != null) 237 list.add(lang+'_'+c+'@'+v); 238 list.add(lang+'@'+v); 239 } 240 if (c != null) 241 list.add(lang+'_'+c); 242 list.add(lang); 243 return list; 244 } 245}