All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tinygroup.commons.i18n.LocaleUtil Maven / Gradle / Ivy

There is a newer version: 2.2.3
Show newest version
/**
 *  Copyright (c) 1997-2013, www.tinygroup.org ([email protected]).
 *
 *  Licensed under the GPL, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.gnu.org/licenses/gpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.tinygroup.commons.i18n;

import org.tinygroup.commons.io.StreamUtil;
import org.tinygroup.commons.tools.ClassLoaderUtil;
import org.tinygroup.commons.tools.StringUtil;

import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.*;

import static org.tinygroup.commons.tools.BasicConstant.EMPTY_STRING;
import static org.tinygroup.commons.tools.CollectionUtil.createHashSet;
import static org.tinygroup.commons.tools.CollectionUtil.createLinkedList;
import static org.tinygroup.commons.tools.StringUtil.trimToNull;

/**
 * 用来处理地域和字符编码的工具类。
 * 

* 由于系统locale和charset是不可靠的,不同的环境可能会有不同的系统设置,因此应用程序最好不要依赖这个系统值。 * LocaleUtil提供了一个方案,可以“修改”默认locale和charset。 *

*

* LocaleUtil提供了以下几个作用域的locale/charset设定: *

*
    *
  • 系统作用域:由JVM所运行的操作系统环境决定,在JVM生命期内不改变。可通过LocaleUtil.getSystem() * 取得。
  • *
  • 默认作用域:在整个JVM中全局有效,可被改变。可通过LocaleUtil.getDefault() * 取得。如未明确指定,则取“系统作用域”的值。
  • *
  • 线程作用域:在整个线程中全局有效,可被改变。可通过LocaleUtil.getContext() * 取得。如未明确指定,则取“默认作用域”的值。每个线程都可以有自己的locale和charset设置,不会干扰其它线程。
  • *
*

* Util工具箱里的其它工具类,当需要时,将从LocaleUtil.getContext() * 中取得当前的locale和charset设置。例如:StringEscapeUtil.escapeURL(value) * ,如不指定charset * ,将从context中取得charset。这样,框架往往可以修改context值,而所有线程中的方法调用将服从于框架的locale和charset设定。 *

* * @author Michael Zhou */ public class LocaleUtil { private static final LocaleInfo systemLocaleInfo = new LocaleInfo(); private static LocaleInfo defaultLocalInfo = systemLocaleInfo; private static final ThreadLocal contextLocaleInfoHolder = new ThreadLocal(); /** * 判断locale是否被支持。 * * @param locale 要检查的locale */ public static boolean isLocaleSupported(Locale locale) { return locale != null && AvailableLocalesLoader.locales.AVAILABLE_LANGUAGES.contains(locale.getLanguage()) && AvailableLocalesLoader.locales.AVAILABLE_COUNTRIES.contains(locale.getCountry()); } /** * 判断指定的charset是否被支持。 * * @param charset 要检查的charset */ public static boolean isCharsetSupported(String charset) { return charset != null && Charset.isSupported(charset); } /** * 解析locale字符串。 *

* Locale字符串是符合下列格式:language_country_variant。 *

* * @param localeString 要解析的字符串 * @return Locale对象,如果locale字符串为空,则返回null */ public static Locale parseLocale(String localeString) { localeString = trimToNull(localeString); if (localeString == null) { return null; } String language = EMPTY_STRING; String country = EMPTY_STRING; String variant = EMPTY_STRING; // language int start = 0; int index = localeString.indexOf("_"); if (index >= 0) { language = localeString.substring(start, index).trim(); // country start = index + 1; index = localeString.indexOf("_", start); if (index >= 0) { country = localeString.substring(start, index).trim(); // variant variant = localeString.substring(index + 1).trim(); } else { country = localeString.substring(start).trim(); } } else { language = localeString.trim(); } return new Locale(language, country, variant); } /** * 取得正规的字符集名称, 如果指定字符集不存在, 则抛出UnsupportedEncodingException. * * @param charset 字符集名称 * @return 正规的字符集名称 * @throws java.nio.charset.IllegalCharsetNameException 如果指定字符集名称非法 * @throws java.nio.charset.UnsupportedCharsetException 如果指定字符集不存在 */ public static String getCanonicalCharset(String charset) { return Charset.forName(charset).name(); } /** * 取得备选的resource bundle风格的名称列表。 *

* 例如: * calculateBundleNames("hello.jsp", new Locale("zh", "CN", "variant")) * 将返回下面列表: *

    *
  1. hello_zh_CN_variant.jsp
  2. *
  3. hello_zh_CN.jsp
  4. *
  5. hello_zh.jsp
  6. *
  7. hello.jsp
  8. *
*

* * @param baseName bundle的基本名 * @param locale 区域设置 * @return 所有备选的bundle名 */ public static List calculateBundleNames(String baseName, Locale locale) { return calculateBundleNames(baseName, locale, false); } /** * 取得备选的resource bundle风格的名称列表。 *

* 例如: * calculateBundleNames("hello.jsp", new Locale("zh", "CN", "variant"), * false)将返回下面列表: *

    *
  1. hello_zh_CN_variant.jsp
  2. *
  3. hello_zh_CN.jsp
  4. *
  5. hello_zh.jsp
  6. *
  7. hello.jsp
  8. *
*

*

* 当noexttrue时,不计算后缀名,例如 * calculateBundleNames("hello.world", * new Locale("zh", "CN", "variant"), true)将返回下面列表: *

    *
  1. hello.world_zh_CN_variant
  2. *
  3. hello.world_zh_CN
  4. *
  5. hello.world_zh
  6. *
  7. hello.world
  8. *
*

* * @param baseName bundle的基本名 * @param locale 区域设置 * @return 所有备选的bundle名 */ public static List calculateBundleNames(String baseName, Locale locale, boolean noext) { baseName = StringUtil.trimToEmpty(baseName); if (locale == null) { locale = new Locale(EMPTY_STRING); } // 取后缀。 String ext = EMPTY_STRING; int extLength = 0; if (!noext) { int extIndex = baseName.lastIndexOf("."); if (extIndex != -1) { ext = baseName.substring(extIndex, baseName.length()); extLength = ext.length(); baseName = baseName.substring(0, extIndex); if (extLength == 1) { ext = EMPTY_STRING; extLength = 0; } } } // 计算locale后缀。 LinkedList result = createLinkedList(); String language = locale.getLanguage(); int languageLength = language.length(); String country = locale.getCountry(); int countryLength = country.length(); String variant = locale.getVariant(); int variantLength = variant.length(); StringBuilder buffer = new StringBuilder(baseName); buffer.append(ext); result.addFirst(buffer.toString()); buffer.setLength(buffer.length() - extLength); // 如果locale是("", "", ""). if (languageLength + countryLength + variantLength == 0) { return result; } // 加入baseName_language,如果baseName为空,则不加下划线。 if (buffer.length() > 0) { buffer.append('_'); } buffer.append(language); if (languageLength > 0) { buffer.append(ext); result.addFirst(buffer.toString()); buffer.setLength(buffer.length() - extLength); } if (countryLength + variantLength == 0) { return result; } // 加入baseName_language_country buffer.append('_').append(country); if (countryLength > 0) { buffer.append(ext); result.addFirst(buffer.toString()); buffer.setLength(buffer.length() - extLength); } if (variantLength == 0) { return result; } // 加入baseName_language_country_variant buffer.append('_').append(variant); buffer.append(ext); result.addFirst(buffer.toString()); buffer.setLength(buffer.length() - extLength); return result; } /** * 取得操作系统默认的区域。 * * @return 操作系统默认的区域 */ public static LocaleInfo getSystem() { return systemLocaleInfo; } /** * 取得默认的区域。 * * @return 默认的区域 */ public static LocaleInfo getDefault() { return defaultLocalInfo == null ? systemLocaleInfo : defaultLocalInfo; } /** * 设置默认的区域。 * * @param locale 区域 * @return 原来的默认区域 */ public static LocaleInfo setDefault(Locale locale) { LocaleInfo old = getDefault(); setDefaultAndNotify(new LocaleInfo(locale, null, systemLocaleInfo)); return old; } /** * 设置默认的区域。 * * @param locale 区域 * @param charset 编码字符集 * @return 原来的默认区域 */ public static LocaleInfo setDefault(Locale locale, String charset) throws UnsupportedCharsetException { LocaleInfo old = getDefault(); setDefaultAndNotify(new LocaleInfo(locale, charset, systemLocaleInfo)); return old; } /** * 设置默认的区域。 * * @param localeInfo 区域和编码字符集信息 * @return 原来的默认区域 */ public static LocaleInfo setDefault(LocaleInfo localeInfo) throws UnsupportedCharsetException { if (localeInfo == null) { return setDefault(null, null); } else { LocaleInfo old = getDefault(); setDefaultAndNotify(localeInfo); return old; } } private static void setDefaultAndNotify(LocaleInfo localeInfo) throws UnsupportedCharsetException { defaultLocalInfo = localeInfo.assertCharsetSupported(); for (Notifier notifier : notifiers) { notifier.defaultChanged(localeInfo); } } /** * 复位默认的区域设置。 */ public static void resetDefault() { defaultLocalInfo = systemLocaleInfo; for (Notifier notifier : notifiers) { notifier.defaultReset(); } } /** * 取得当前thread默认的区域。 * * @return 当前thread默认的区域 */ public static LocaleInfo getContext() { LocaleInfo contextLocaleInfo = contextLocaleInfoHolder.get(); return contextLocaleInfo == null ? getDefault() : contextLocaleInfo; } /** * 设置当前thread默认的区域。 * * @param locale 区域 * @return 原来的thread默认的区域 */ public static LocaleInfo setContext(Locale locale) { LocaleInfo old = getContext(); setContextAndNotify(new LocaleInfo(locale, null, defaultLocalInfo)); return old; } /** * 设置当前thread默认的区域。 * * @param locale 区域 * @param charset 编码字符集 * @return 原来的thread默认的区域 */ public static LocaleInfo setContext(Locale locale, String charset) throws UnsupportedCharsetException { LocaleInfo old = getContext(); setContextAndNotify(new LocaleInfo(locale, charset, defaultLocalInfo)); return old; } /** * 设置当前thread默认的区域。 * * @param localeInfo 区域和编码字符集信息 * @return 原来的thread默认的区域 */ public static LocaleInfo setContext(LocaleInfo localeInfo) throws UnsupportedCharsetException { if (localeInfo == null) { return setContext(null, null); } else { LocaleInfo old = getContext(); setContextAndNotify(localeInfo); return old; } } private static void setContextAndNotify(LocaleInfo localeInfo) throws UnsupportedCharsetException { contextLocaleInfoHolder.set(localeInfo.assertCharsetSupported()); for (Notifier notifier : notifiers) { notifier.contextChanged(localeInfo); } } /** * 复位当前thread的区域设置。 */ public static void resetContext() { contextLocaleInfoHolder.remove(); for (Notifier notifier : notifiers) { notifier.contextReset(); } } private static Notifier[] notifiers = getNotifiers(); private static Notifier[] getNotifiers() { try { URL[] files = ClassLoaderUtil.getResources("META-INF/services/localeNotifiers", ClassLoaderUtil.class); List list = createLinkedList(); for (URL file : files) { for (String className : StringUtil.split(StreamUtil.readText(file.openStream(), "UTF-8", true), "\r\n ")) { list.add(Notifier.class.cast(ClassLoaderUtil.newInstance(className, ClassLoaderUtil.class))); } } return list.toArray(new Notifier[list.size()]); } catch (Exception e) { System.err.println("Failure in LocaleUtil.getNotifiers()" + e.toString()); return new Notifier[0]; } } /** * 当default或context locale被改变时,通知监听器。 */ public interface Notifier extends EventListener { void defaultChanged(LocaleInfo newValue); void defaultReset(); void contextChanged(LocaleInfo newValue); void contextReset(); } /** * 延迟加载所有可用的国家和语言。 */ private static class AvailableLocalesLoader { private static final AvailableLocales locales = new AvailableLocales(); } private static class AvailableLocales { private final Set AVAILABLE_LANGUAGES = createHashSet(); private final Set AVAILABLE_COUNTRIES = createHashSet(); private AvailableLocales() { Locale[] availableLocales = Locale.getAvailableLocales(); for (Locale locale : availableLocales) { AVAILABLE_LANGUAGES.add(locale.getLanguage()); AVAILABLE_COUNTRIES.add(locale.getCountry()); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy