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