package org.smartly.commons.util; import java.text.DecimalFormatSymbols; import java.util.*; public class LocaleUtils { public static final Locale DEFAULT = new Locale("en", "US"); private final static InheritableThreadLocal<Locale> _thdLocale = new InheritableThreadLocal<Locale>(); /** * Locales that are found so far. */ private static final Map<Locale, Locale> _founds = Collections.synchronizedMap( new LinkedHashMap<Locale, Locale>(18)); static { final Locale[] ls = new Locale[]{ Locale.UK, Locale.ENGLISH, Locale.US, Locale.JAPAN, Locale.JAPANESE, Locale.KOREA, Locale.KOREAN, Locale.FRANCE, Locale.FRENCH, Locale.GERMANY, Locale.GERMAN, Locale.ITALY, Locale.ITALIAN, Locale.TRADITIONAL_CHINESE, Locale.SIMPLIFIED_CHINESE, Locale.CHINA, Locale.CHINESE }; for (int j = 0; j < ls.length; ++j) { _founds.put(ls[j], ls[j]); } } /** * Returns the current locale; never null. * This is the locale that every other objects shall use, * unless they have special consideration. * <p>Default: If {@link #setThreadLocal} was called with non-null, * the value is returned. Otherwise, Locale.getDefault() is returned, */ public static Locale getCurrent() { final Locale l = (Locale) _thdLocale.get(); return l != null ? l : Locale.getDefault(); } public static void setCurrent(final Locale locale) { if (null != locale) { _thdLocale.set(locale); if (Locale.getDefault() != locale) { Locale.setDefault(locale); } } } /** * Returns whether the current locale ({@link #getCurrent}) belongs * to the specified language and/or country. * * @param lang the language code, e.g., en and zh. Ignored if null. * @param country the country code, e.g., US. Ignored if null. * If empty, it means no country code at all. */ public static boolean testCurrent(String lang, String country) { final Locale l = getCurrent(); return (lang == null || lang.equals(l.getLanguage())) && (country == null || country.equals(l.getCountry())); } /** * Sets the locale for the current thread only. * <p/> * <p>Each thread could have an independent locale, called * the thread locale. * <p/> * <p>When Invoking this method under a thread that serves requests, * remember to clean up the setting upon completing each request. * <p/> * <pre><code>Locale old = Locales.setThreadLocal(newValue); * try { * ... * } finally { * Locales.setThreadLocal(old); * }</code></pre> * * @param locale the thread locale; null to denote no thread locale * @return the previous thread locale */ @SuppressWarnings("unchecked") public static Locale setThreadLocal(Locale locale) { final Locale old = (Locale) _thdLocale.get(); _thdLocale.set(locale); return old; } /** * Converts a Locale to one of them being used before. * To save memory (since locale is used frequently), it is suggested * to pass thru this method after creating a new instance of Locale.<br> * Example, getLocale(new Locale(...)). * <p/> * <p>This method first look for any locale */ public static Locale getLocale(final Locale locale) { synchronized (_founds) { final Locale l = _founds.get(locale); if (l != null) { return l; } _founds.put(locale, locale); return locale; } } public static Locale getLocale(final String lang, final String country) { if (StringUtils.hasText(lang)) { if (StringUtils.hasText(country)) { return getLocale(new Locale(lang, country)); } else { return getLocaleByLang(lang); } } return DEFAULT; } public static Locale getLocaleByLang(final String lang) { if (StringUtils.hasText(lang)) { final Set<Locale> keys = _founds.keySet(); for (final Locale locale : keys) { if (locale.getLanguage().equalsIgnoreCase(lang)) { return _founds.get(locale); } } } return getLocale(new Locale(lang)); } public static Locale getLocaleByCountry(final String country) { if (StringUtils.hasText(country)) { final Set<Locale> keys = _founds.keySet(); for (final Locale locale : keys) { if (locale.getCountry().equalsIgnoreCase(country)) { return _founds.get(locale); } } } return DEFAULT; } public static String getLanguage(final String slocale) { final String[] tokens = StringUtils.split(slocale, new String[]{"_", "-"}); if (tokens.length > 0) { return tokens[0]; } else { final Locale locale = LocaleUtils.getLocaleFromString(slocale); return locale.getLanguage(); } } /** * Return Country from locale even if passed locale has not a country declared. * * @param locale Locale can have only Language. i.e. "it" * @return Country for passed locale. i.e. getCountry(new Locale("it")) returns "IT" */ public static Locale getCountry(final Locale locale) { Locale result = null; final String country = locale.getCountry(); final String language = locale.getLanguage(); if (StringUtils.hasText(country)) { result = LocaleUtils.getLocale(locale); } else { // loop on all available locales searching for best matching final Locale[] locales = Locale.getAvailableLocales(); for (final Locale item : locales) { final String itemcountry = item.getCountry(); final String itemlang = item.getLanguage(); if (StringUtils.hasText(itemcountry) && itemlang.equalsIgnoreCase(language)) { result = LocaleUtils.getLocale(item); if (itemcountry.equalsIgnoreCase(itemlang)) { break; } } } } return null != result ? result : locale; } /** * Create a new Locale from passed string.<br> * Allow a string with language-country-variant, ex: "it-IT", "en", "en-US", "th_TH_TH" * If passed locale is not available, return Default Locale * * @param localeString A string like "it-IT", "en", "en-US", "th_TH_TH" * @return Locale from String */ public static Locale getLocaleFromString(final String localeString) { Locale result = parseLocaleString(null!=localeString?localeString.toString():""); if (null == result) { result = getLocale(getCurrent()); } return result; } /** * Parse the given locale string into a <code>java.util.Locale</code>. * This is the inverse operation of Locale's <code>toString</code>. * * @param localeString the locale string, following * <code>java.util.Locale</code>'s toString format ("en", "en_UK", etc). * Also accepts spaces ' ' as separators, as alternative to underscores '_', or ':' or '-'. * @return a corresponding Locale instance */ public static Locale parseLocaleString(final String localeString) { if (StringUtils.hasText(localeString)) { final String[] parts = StringUtils.split(localeString, "_ -:", true, true); final String language = (parts.length > 0 ? parts[0] : ""); final String country = (parts.length > 1 ? parts[1] : ""); final String variant = (parts.length > 2 ? parts[2] : ""); return (language.length() > 0 ? getLocale(new Locale(language, country, variant)) : null); } return getLocale(Locale.ENGLISH); } /** * Create a new Locale from passed string.<br> * Allow a string with language-country-variant, ex: "it-IT", "en", "en-US", "th_TH_TH" * If passed locale is not available, return 'defaultValue' parameter * * @param sLocale A string like "it-IT", "en", "en-US", "th_TH_TH" * @param defaultValue A default Locale to return if passed string produce an invalid locale * @return Locale from String */ public static Locale getLocaleFromString(final String sLocale, final Locale defaultValue) { Locale result = null; if (sLocale.indexOf("-") > -1) { result = getLocaleFromString(sLocale, "-", defaultValue); } else if (sLocale.indexOf("_") > -1) { result = getLocaleFromString(sLocale, "_", defaultValue); } else if (sLocale.indexOf(":") > -1) { result = getLocaleFromString(sLocale, ":", defaultValue); } else { result = getLocaleFromString(sLocale, " ", defaultValue); } if (null == result) { result = defaultValue; } return result; } /** * Create a new Locale from passed string.<br> * Allow a string with language-country-variant, ex: "it-IT", "en", "en-US", "th_TH_TH" * If passed locale is not available, return US (english) * * @param sLocale A string like "it-IT", "en", "en-US", "th_TH_TH" * @param delimiter Separator of passed char ex: '-', ':', etc.. * @return Locale from String */ public static Locale getLocaleFromString(final String sLocale, final String delimiter) { Locale def = getLocale(getCurrent()); return getLocaleFromString(sLocale, delimiter, def); } /** * Create a new Locale from passed string.<br> * Allow a string with language-country-variant, ex: "it-IT", "en", "en-US", "th_TH_TH".<br> * The sLocale parameter string can use a custom 'delimiter'. * If passed locale is not available, return 'defaultValue' parameter * * @param sLocale A string like "it-IT", "en", "en-US", "th_TH_TH" * @param delimiter Separator of passed string ex: "-", ":", etc.. * @param defaultValue A default Locale to return if passed string produce an invalid locale * @return Locale from String */ public static Locale getLocaleFromString(final String sLocale, final String delimiter, Locale defaultValue) { Locale result = null; String[] arr = sLocale.split(delimiter); if (arr.length == 1) { result = getLocale(new Locale(arr[0])); } else if (arr.length == 2) { result = getLocale(new Locale(arr[0], arr[1])); } else if (arr.length == 3) { result = getLocale(new Locale(arr[0], arr[1], arr[2])); } if (null == result) { result = defaultValue; } if (null == result) { result = getCurrent(); } return result; } /** * Return Loacale from Object instance (Locale or String). * * @param locale * @param defaultValue * @return */ public static Locale getLocaleFromObject(final Object locale, final Locale defaultValue) { if (null != locale) { if (locale instanceof Locale) { return getLocale((Locale) locale); } else { final String slocale = locale.toString(); return getLocaleFromString(slocale); } } return defaultValue; } /** * Check locale compatibility. * * @param locale1 First locale * @param locale2 Second locale * @return true if locales are near or equals. */ public static boolean like(Locale locale1, Locale locale2) { return like(locale1.toString(), locale2.toString()); } /** * Check locale compatibility. * * @param value1 First locale * @param value2 Second locale * @return true if locales are near or equals. */ public static boolean like(final String value1, final String value2) { if (!StringUtils.hasText(value1) && !StringUtils.hasText(value2)) { return true; } if (null == value1 || null == value2) { return false; } // equals? if (value1.equalsIgnoreCase(value2)) { return true; } if (StringUtils.hasText(value2)) { // value1 near value2? if (value1.startsWith(value2)) { return true; } // value2 near value1? if (value2.startsWith(value1)) { return true; } } return false; } public static boolean isISOLanguage(final String language) { final Locale locale = new Locale(language); final String[] langs = Locale.getISOLanguages(); for (String lang : langs) { if (lang.equalsIgnoreCase(locale.getLanguage())) { return true; } } return false; } public static boolean isISOCountry(final String language) { final Locale locale = new Locale(language); final String[] langs = Locale.getISOCountries(); for (String lang : langs) { if (lang.equalsIgnoreCase(locale.getCountry())) { return true; } } return false; } public static DecimalFormatSymbols getDecimalFormatSymbols() { final Locale locale = LocaleUtils.getCurrent(); return getDecimalFormatSymbols(locale); } public static DecimalFormatSymbols getDecimalFormatSymbols(final Locale locale) { final Locale loc = LocaleUtils.getLocale(locale); final DecimalFormatSymbols result = new DecimalFormatSymbols(loc); return result; } public static Currency getCurrency() { final Locale locale = LocaleUtils.getCurrent(); return LocaleUtils.getCurrency(locale); } /** * Returns the <code>Currency</code> instance for the country of the * given locale. The language and variant components of the locale * are ignored. The result may vary over time, as countries change their * currencies. For example, for the original member countries of the * European Monetary Union, the method returns the old national currencies * until December 31, 2001, and the Euro from January 1, 2002, local time * of the respective countries. * <p/> * The method returns <code>null</code> for territories that don't * have a currency, such as Antarctica or if the given locale * is not a supported ISO 3166 country code. * * @param locale the locale for whose country a <code>Currency</code> * instance is needed * @return the <code>Currency</code> instance for the country of the given * locale, or null */ public static Currency getCurrency(final Locale locale) { try { final DecimalFormatSymbols dfs = getDecimalFormatSymbols(locale); final Currency currency = dfs.getCurrency(); return currency; } catch (Throwable t) { } return null; } /** * Return currency code and currency symbol in unique formatted string. * i.e. "€(EUR)" * * @return */ public static String getCurrencyAsString() { final Locale locale = LocaleUtils.getCurrent(); return LocaleUtils.getCurrencyAsString(locale); } /** * Return currency code and currency symbol in unique formatted string. * i.e. "€(EUR)" * * @param locale * @return */ public static String getCurrencyAsString(final Locale locale) { final Locale loc = LocaleUtils.getCountry(locale); final Currency currency = Currency.getInstance(loc); final String symbol = currency.getSymbol(); final String code = currency.getCurrencyCode(); if (StringUtils.hasText(symbol) && !symbol.equalsIgnoreCase(code)) { return symbol + "(" + code + ")"; } else { return code; } } public static String getCurrencySymbol() { final Locale locale = LocaleUtils.getCurrent(); return LocaleUtils.getCurrencySymbol(locale); } public static String getCurrencySymbol(final Locale locale) { final Locale loc = LocaleUtils.getCountry(locale); final Currency currency = Currency.getInstance(loc); return currency.getSymbol(); } public static String getCurrencySymbol(final String currencyCode) { final Currency currency = Currency.getInstance(currencyCode); return currency.getSymbol(); } public static String getCurrencyCode() { final Locale locale = LocaleUtils.getCurrent(); return LocaleUtils.getCurrencyCode(locale); } public static String getCurrencyCode(final Locale locale) { final Locale loc = LocaleUtils.getCountry(locale); final Currency currency = Currency.getInstance(loc); return currency.getCurrencyCode(); } public static String getCurrencyDisplayName() { final Locale locale = LocaleUtils.getCurrent(); return LocaleUtils.getCurrencyDisplayName(locale); } public static String getCurrencyDisplayName(final Locale locale) { final Locale loc = LocaleUtils.getCountry(locale); final Currency currency = Currency.getInstance(loc); return currency.getDisplayName(); } public static String getTimeZoneID() { return TimeZone.getDefault().getID(); } public static TimeZone getTimeZone() { return TimeZone.getDefault(); } public static TimeZone getTimeZone(final String ID) { return TimeZone.getTimeZone(ID); } public static String getTimeZoneDisplay(final String ID) { final TimeZone tz = TimeZone.getTimeZone(ID); final Locale locale = LocaleUtils.getCurrent(); return LocaleUtils.getTimeZoneDisplay(tz, locale); } public static String getTimeZoneDisplay(final String ID, final String langCode) { final TimeZone tz = TimeZone.getTimeZone(ID); final Locale locale = LocaleUtils.getLocaleFromString(langCode); return LocaleUtils.getTimeZoneDisplay(tz, locale); } public static String getTimeZoneDisplay(final TimeZone tz, final Locale locale) { final Locale loc = null != locale ? locale : LocaleUtils.getCurrent(); return null != tz ? tz.getDisplayName(loc) : TimeZone.getDefault().getDisplayName(loc); } }