Java tutorial
/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.language; import com.liferay.petra.string.CharPool; import com.liferay.petra.string.StringPool; import com.liferay.portal.kernel.cache.PortalCache; import com.liferay.portal.kernel.cache.PortalCacheHelperUtil; import com.liferay.portal.kernel.cache.PortalCacheManagerNames; import com.liferay.portal.kernel.cache.PortalCacheMapSynchronizeUtil; import com.liferay.portal.kernel.cache.PortalCacheMapSynchronizeUtil.Synchronizer; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.language.Language; import com.liferay.portal.kernel.language.LanguageWrapper; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.CompanyConstants; import com.liferay.portal.kernel.model.Group; import com.liferay.portal.kernel.model.GroupConstants; import com.liferay.portal.kernel.security.auth.CompanyThreadLocal; import com.liferay.portal.kernel.service.GroupLocalServiceUtil; import com.liferay.portal.kernel.theme.ThemeDisplay; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.CookieKeys; import com.liferay.portal.kernel.util.FastDateFormatConstants; import com.liferay.portal.kernel.util.FastDateFormatFactoryUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.HashMapBuilder; import com.liferay.portal.kernel.util.HtmlUtil; import com.liferay.portal.kernel.util.JavaConstants; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.ObjectValuePair; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.ResourceBundleLoader; import com.liferay.portal.kernel.util.ResourceBundleUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Time; import com.liferay.portal.kernel.util.UnicodeProperties; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.util.WebKeys; import com.liferay.portal.util.PrefsPropsUtil; import com.liferay.portal.util.PropsValues; import java.io.Serializable; import java.text.Format; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.portlet.PortletConfig; import javax.portlet.PortletRequest; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Provides various translation related functionalities for language keys * specified in portlet configurations and portal resource bundles. * * <p> * You can disable translations by setting the * <code>translations.disabled</code> property to <code>true</code> in * <code>portal.properties</code>. * </p> * * <p> * Depending on the context passed into these methods, the lookup might be * limited to the portal's resource bundle (e.g. when only a locale is passed), * or extended to include an individual portlet's resource bundle (e.g. when a * request object is passed). A portlet's resource bundle overrides the portal's * resources when both are present. * </p> * * @author Brian Wing Shun Chan * @author Andrius Vitkauskas * @author Eduardo Lundgren */ public class LanguageImpl implements Language, Serializable { public void afterPropertiesSet() { _companyLocalesPortalCache = PortalCacheHelperUtil.getPortalCache(PortalCacheManagerNames.MULTI_VM, _COMPANY_LOCALES_PORTAL_CACHE_NAME); PortalCacheMapSynchronizeUtil.synchronize(_companyLocalesPortalCache, _companyLocalesBags, _removeSynchronizer); _groupLocalesPortalCache = PortalCacheHelperUtil.getPortalCache(PortalCacheManagerNames.MULTI_VM, _GROUP_LOCALES_PORTAL_CACHE_NAME); PortalCacheMapSynchronizeUtil.synchronize(_groupLocalesPortalCache, _groupLanguageCodeLocalesMapMap, _removeSynchronizer); PortalCacheMapSynchronizeUtil.synchronize(_groupLocalesPortalCache, _groupLanguageIdLocalesMap, _removeSynchronizer); } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param argument the single argument to be substituted into the pattern * and translated, if possible * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, LanguageWrapper argument) { return format(httpServletRequest, pattern, new LanguageWrapper[] { argument }, true); } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param argument the single argument to be substituted into the pattern * and translated, if possible * @param translateArguments whether the argument is translated * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, LanguageWrapper argument, boolean translateArguments) { return format(httpServletRequest, pattern, new LanguageWrapper[] { argument }, translateArguments); } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern and * translated, if possible * @return the translated pattern, with the arguments substituted in for the * pattern's placeholders */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, LanguageWrapper[] arguments) { return format(httpServletRequest, pattern, arguments, true); } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern * @param translateArguments whether the arguments are translated * @return the translated pattern, with the arguments substituted in for the * pattern's placeholders */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, LanguageWrapper[] arguments, boolean translateArguments) { if (PropsValues.TRANSLATIONS_DISABLED) { return pattern; } String value = null; try { pattern = get(httpServletRequest, pattern); if (ArrayUtil.isNotEmpty(arguments)) { Object[] formattedArguments = new Object[arguments.length]; for (int i = 0; i < arguments.length; i++) { if (translateArguments) { formattedArguments[i] = StringBundler.concat(arguments[i].getBefore(), get(httpServletRequest, arguments[i].getText()), arguments[i].getAfter()); } else { formattedArguments[i] = StringBundler.concat(arguments[i].getBefore(), arguments[i].getText(), arguments[i].getAfter()); } } value = _decorateMessageFormat(httpServletRequest, pattern, formattedArguments); } else { value = pattern; } } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } return value; } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param argument the single argument to be substituted into the pattern * and translated, if possible * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, Object argument) { return format(httpServletRequest, pattern, new Object[] { argument }, true); } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param argument the single argument to be substituted into the pattern * and translated, if possible * @param translateArguments whether the argument is translated * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, Object argument, boolean translateArguments) { return format(httpServletRequest, pattern, new Object[] { argument }, translateArguments); } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern and * translated, if possible * @return the translated pattern, with the arguments substituted in for the * pattern's placeholders */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, Object[] arguments) { return format(httpServletRequest, pattern, arguments, true); } /** * Returns the translated pattern using the current request's locale or, if * the current request locale is not available, the server's default locale. * * <p> * The lookup is done on the portlet configuration first, and if it's not * found, it is done on the portal's resource bundle. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param httpServletRequest the request used to determine the current * locale * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern * @param translateArguments whether the arguments are translated * @return the translated pattern, with the arguments substituted in for the * pattern's placeholders */ @Override public String format(HttpServletRequest httpServletRequest, String pattern, Object[] arguments, boolean translateArguments) { if (PropsValues.TRANSLATIONS_DISABLED) { return pattern; } String value = null; try { pattern = get(httpServletRequest, pattern); if (ArrayUtil.isNotEmpty(arguments)) { for (int i = 0; i < arguments.length; i++) { if (translateArguments) { arguments[i] = get(httpServletRequest, arguments[i].toString()); } } value = _decorateMessageFormat(httpServletRequest, pattern, arguments); } else { value = pattern; } } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } return value; } /** * Returns the translated pattern using the locale or, if the locale is not * available, the server's default locale. * * <p> * The lookup is done on the portal's resource bundle. If a translation for * a given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param locale the locale to translate to * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern * @return the translated pattern, with the arguments substituted in for the * pattern's placeholders */ @Override public String format(Locale locale, String pattern, List<Object> arguments) { return format(locale, pattern, arguments.toArray(), true); } /** * Returns the translated pattern using the locale or, if the locale is not * available, the server's default locale. * * <p> * The lookup is done on the portal's resource bundle. If a translation for * a given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param locale the locale to translate to * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param argument the argument to be substituted into the pattern * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(Locale locale, String pattern, Object argument) { return format(locale, pattern, new Object[] { argument }, true); } /** * Returns the translated pattern using the locale or, if the locale is not * available, the server's default locale. * * <p> * The lookup is done on the portal's resource bundle. If a translation for * a given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param locale the locale to translate to * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param argument the argument to be substituted into the pattern * @param translateArguments whether the argument is translated * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(Locale locale, String pattern, Object argument, boolean translateArguments) { return format(locale, pattern, new Object[] { argument }, translateArguments); } /** * Returns the translated pattern using the locale or, if the locale is not * available, the server's default locale. * * <p> * The lookup is done on the portal's resource bundle. If a translation for * a given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param locale the locale to translate to * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern * @return the translated pattern, with the arguments substituted in for the * pattern's placeholders */ @Override public String format(Locale locale, String pattern, Object[] arguments) { return format(locale, pattern, arguments, true); } /** * Returns the translated pattern using the locale or, if the locale is not * available, the server's default locale. * * <p> * The lookup is done on the portal's resource bundle. If a translation for * a given key does not exist, this method returns the requested key as the * translation. * </p> * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param locale the locale to translate to * @param pattern the key to look up in the current locale's resource file. * The key follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern * @param translateArguments whether the arguments are translated * @return the translated pattern, with the arguments substituted in for the * pattern's placeholders */ @Override public String format(Locale locale, String pattern, Object[] arguments, boolean translateArguments) { if (PropsValues.TRANSLATIONS_DISABLED) { return pattern; } String value = null; try { pattern = get(locale, pattern); if (ArrayUtil.isNotEmpty(arguments)) { for (int i = 0; i < arguments.length; i++) { if (translateArguments) { arguments[i] = get(locale, arguments[i].toString()); } } value = _decorateMessageFormat(locale, pattern, arguments); } else { value = pattern; } } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } return value; } /** * Returns the translated pattern in the resource bundle or, if the resource * bundle is not available, the untranslated key. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param resourceBundle the requested key's resource bundle * @param pattern the key to look up in the resource bundle. The key * follows the standard Java resource specification. * @param argument the argument to be substituted into the pattern * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(ResourceBundle resourceBundle, String pattern, Object argument) { return format(resourceBundle, pattern, new Object[] { argument }, true); } /** * Returns the translated pattern in the resource bundle or, if the resource * bundle is not available, the untranslated key. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * * <p> * The substitute placeholder (e.g. <code>{0}</code>) is replaced with the * argument, following the standard Java {@link ResourceBundle} notion of * index based substitution. * </p> * * @param resourceBundle the requested key's resource bundle * @param pattern the key to look up in the resource bundle. The key * follows the standard Java resource specification. * @param argument the argument to be substituted into the pattern * @param translateArguments whether the argument is translated * @return the translated pattern, with the argument substituted in for the * pattern's placeholder */ @Override public String format(ResourceBundle resourceBundle, String pattern, Object argument, boolean translateArguments) { return format(resourceBundle, pattern, new Object[] { argument }, translateArguments); } /** * Returns the translated pattern in the resource bundle or, if the resource * bundle is not available, the untranslated key. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param resourceBundle the requested key's resource bundle * @param pattern the key to look up in the resource bundle. The key * follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern * @return the translated pattern, with the arguments substituted in for the * pattern's placeholder */ @Override public String format(ResourceBundle resourceBundle, String pattern, Object[] arguments) { return format(resourceBundle, pattern, arguments, true); } /** * Returns the translated pattern in the resource bundle or, if the resource * bundle is not available, the untranslated key. If a translation for a * given key does not exist, this method returns the requested key as the * translation. * * <p> * The substitute placeholders (e.g. <code>{0}</code>, <code>{1}</code>, * <code>{2}</code>, etc.) are replaced with the arguments, following the * standard Java {@link ResourceBundle} notion of index based substitution. * </p> * * @param resourceBundle the requested key's resource bundle * @param pattern the key to look up in the resource bundle. The key * follows the standard Java resource specification. * @param arguments the arguments to be substituted into the pattern * @param translateArguments whether the arguments are translated * @return the translated pattern, with the arguments substituted in for the * pattern's placeholder */ @Override public String format(ResourceBundle resourceBundle, String pattern, Object[] arguments, boolean translateArguments) { if (PropsValues.TRANSLATIONS_DISABLED) { return pattern; } String value = null; try { pattern = get(resourceBundle, pattern); if (ArrayUtil.isNotEmpty(arguments)) { for (int i = 0; i < arguments.length; i++) { if (translateArguments) { arguments[i] = get(resourceBundle, arguments[i].toString()); } } value = _decorateMessageFormat(resourceBundle.getLocale(), pattern, arguments); } else { value = pattern; } } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } return value; } /** * Returns the key's translation from the portlet configuration, or from the * portal's resource bundle if the portlet configuration is unavailable. * * @param httpServletRequest the request used to determine the key's * context and locale * @param resourceBundle the requested key's resource bundle * @param key the translation key * @return the key's translation, or the key if the translation is * unavailable */ @Override public String get(HttpServletRequest httpServletRequest, ResourceBundle resourceBundle, String key) { return get(httpServletRequest, resourceBundle, key, key); } @Override public String get(HttpServletRequest httpServletRequest, ResourceBundle resourceBundle, String key, String defaultValue) { String value = _get(resourceBundle, key); if (value != null) { return value; } return get(httpServletRequest, key, defaultValue); } @Override public String get(HttpServletRequest httpServletRequest, String key) { return get(httpServletRequest, key, key); } /** * Returns the key's translation from the portlet configuration, or from the * portal's resource bundle if the portlet configuration is unavailable. * * @param httpServletRequest the request used to determine the key's * context and locale * @param key the translation key * @param defaultValue the value to return if there is no matching * translation * @return the key's translation, or the default value if the translation is * unavailable */ @Override public String get(HttpServletRequest httpServletRequest, String key, String defaultValue) { if ((httpServletRequest == null) || (key == null)) { return defaultValue; } PortletConfig portletConfig = (PortletConfig) httpServletRequest .getAttribute(JavaConstants.JAVAX_PORTLET_CONFIG); Locale locale = _getLocale(httpServletRequest); if (portletConfig == null) { return get(locale, key, defaultValue); } ResourceBundle resourceBundle = portletConfig.getResourceBundle(locale); if (resourceBundle.containsKey(key)) { return _get(resourceBundle, key); } return get(locale, key, defaultValue); } /** * Returns the key's translation from the portal's resource bundle. * * @param locale the key's locale * @param key the translation key * @return the key's translation */ @Override public String get(Locale locale, String key) { return get(locale, key, key); } /** * Returns the key's translation from the portal's resource bundle. * * @param locale the key's locale * @param key the translation key * @param defaultValue the value to return if there is no matching * translation * @return the key's translation, or the default value if the translation is * unavailable */ @Override public String get(Locale locale, String key, String defaultValue) { if (PropsValues.TRANSLATIONS_DISABLED) { return key; } if ((locale == null) || (key == null)) { return defaultValue; } String value = LanguageResources.getMessage(locale, key); if (value != null) { return LanguageResources.fixValue(value); } if ((key.length() > 0) && (key.charAt(key.length() - 1) == CharPool.CLOSE_BRACKET)) { int pos = key.lastIndexOf(CharPool.OPEN_BRACKET); if (pos != -1) { key = key.substring(0, pos); return get(locale, key, defaultValue); } } return defaultValue; } /** * Returns the key's translation from the resource bundle. * * @param resourceBundle the requested key's resource bundle * @param key the translation key * @return the key's translation */ @Override public String get(ResourceBundle resourceBundle, String key) { return get(resourceBundle, key, key); } /** * Returns the key's translation from the resource bundle. * * @param resourceBundle the requested key's resource bundle * @param key the translation key * @param defaultValue the value to return if there is no matching * translation * @return the key's translation, or the default value if the translation is * unavailable */ @Override public String get(ResourceBundle resourceBundle, String key, String defaultValue) { String value = _get(resourceBundle, key); if (value != null) { return value; } return defaultValue; } /** * Returns the locales configured for the portal. Locales can be configured * in <code>portal.properties</code> using the <code>locales</code> and * <code>locales.enabled</code> keys. * * @return the locales configured for the portal */ @Override public Set<Locale> getAvailableLocales() { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); return companyLocalesBag.getAvailableLocales(); } @Override public Set<Locale> getAvailableLocales(long groupId) { if (groupId <= 0) { return getAvailableLocales(); } try { if (isInheritLocales(groupId)) { Group group = GroupLocalServiceUtil.getGroup(groupId); CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(group.getCompanyId()); return companyLocalesBag.getAvailableLocales(); } } catch (Exception e) { } Map<String, Locale> groupLanguageIdLocalesMap = _getGroupLanguageIdLocalesMap(groupId); return new LinkedHashSet<>(groupLanguageIdLocalesMap.values()); } @Override public String getBCP47LanguageId(HttpServletRequest httpServletRequest) { return getBCP47LanguageId(PortalUtil.getLocale(httpServletRequest)); } @Override public String getBCP47LanguageId(Locale locale) { return LocaleUtil.toBCP47LanguageId(locale); } @Override public String getBCP47LanguageId(PortletRequest portletRequest) { return getBCP47LanguageId(PortalUtil.getLocale(portletRequest)); } @Override public Set<Locale> getCompanyAvailableLocales(long companyId) { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(companyId); return companyLocalesBag.getAvailableLocales(); } /** * Returns the language ID that the request is served with. The language ID * is returned as a language code (e.g. <code>en</code>) or a specific * variant (e.g. <code>en_GB</code>). * * @param httpServletRequest the request used to determine the language ID * @return the language ID that the request is served with */ @Override public String getLanguageId(HttpServletRequest httpServletRequest) { String languageId = ParamUtil.getString(httpServletRequest, "languageId"); if (Validator.isNotNull(languageId)) { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); if (companyLocalesBag.containsLanguageCode(languageId) || companyLocalesBag.containsLanguageId(languageId)) { return languageId; } } Locale locale = PortalUtil.getLocale(httpServletRequest, null, false); return getLanguageId(locale); } /** * Returns the language ID from the locale. The language ID is returned as a * language code (e.g. <code>en</code>) or a specific variant (e.g. * <code>en_GB</code>). * * @param locale the locale used to determine the language ID * @return the language ID from the locale */ @Override public String getLanguageId(Locale locale) { return LocaleUtil.toLanguageId(locale); } /** * Returns the language ID that the {@link PortletRequest} is served with. * The language ID is returned as a language code (e.g. <code>en</code>) or * a specific variant (e.g. <code>en_GB</code>). * * @param portletRequest the portlet request used to determine the language * ID * @return the language ID that the portlet request is served with */ @Override public String getLanguageId(PortletRequest portletRequest) { return getLanguageId(PortalUtil.getHttpServletRequest(portletRequest)); } /** * Returns the last time (in milliseconds) there was a change in the * language's list, company, or group. * * @return the last moodified time in milliseconds */ @Override public long getLastModified() { return _lastModified; } @Override public Locale getLocale(long groupId, String languageCode) { try { if (isInheritLocales(groupId)) { return getLocale(languageCode); } } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn("Unable to check if group inherits locales"); } } Map<String, Locale> groupLanguageCodeLocalesMap = _getGroupLanguageCodeLocalesMap(groupId); return groupLanguageCodeLocalesMap.get(languageCode); } /** * Returns the locale associated with the language code. * * @param languageCode the code representation of a language (e.g. * <code>en</code> and <code>en_GB</code>) * @return the locale associated with the language code */ @Override public Locale getLocale(String languageCode) { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); return companyLocalesBag.getByLanguageCode(languageCode); } @Override public ResourceBundleLoader getPortalResourceBundleLoader() { return LanguageResources.RESOURCE_BUNDLE_LOADER; } @Override public Set<Locale> getSupportedLocales() { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); return companyLocalesBag._supportedLocalesSet; } /** * Returns an exact localized description of the time interval (in * milliseconds) in the largest unit possible. * * <p> * For example, the following time intervals would be converted to the * following time descriptions, using the English locale: * </p> * * <ul> * <li> * 1000 = 1 Second * </li> * <li> * 1001 = 1001 Milliseconds * </li> * <li> * 86400000 = 1 Day * </li> * <li> * 86401000 = 86401 Seconds * </li> * </ul> * * @param httpServletRequest the request used to determine the current * locale * @param milliseconds the time interval in milliseconds to describe * @return an exact localized description of the time interval in the * largest unit possible */ @Override public String getTimeDescription(HttpServletRequest httpServletRequest, long milliseconds) { return getTimeDescription(httpServletRequest, milliseconds, false); } /** * Returns an approximate or exact localized description of the time * interval (in milliseconds) in the largest unit possible. * * <p> * Approximate descriptions round the time to the largest possible unit and * ignores the rest. For example, using the English locale: * </p> * * <ul> * <li> * Any time interval 1000-1999 = 1 Second * </li> * <li> * Any time interval 86400000-172799999 = 1 Day * </li> * </ul> * * <p> * Otherwise, exact descriptions would follow a similar conversion pattern * as below: * </p> * * <ul> * <li> * 1000 = 1 Second * </li> * <li> * 1001 = 1001 Milliseconds * </li> * <li> * 86400000 = 1 Day * </li> * <li> * 86401000 = 86401 Seconds * </li> * </ul> * * @param httpServletRequest the request used to determine the current * locale * @param milliseconds the time interval in milliseconds to describe * @param approximate whether the time description is approximate * @return a localized description of the time interval in the largest unit * possible */ @Override public String getTimeDescription(HttpServletRequest httpServletRequest, long milliseconds, boolean approximate) { String description = Time.getDescription(milliseconds, approximate); String value = null; try { String[] parts = description.split(StringPool.SPACE, 2); String unit = StringUtil.toLowerCase(parts[1]); if (unit.equals("second")) { unit += "[time]"; } value = format(httpServletRequest, "x-" + unit, parts[0]); } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } return value; } /** * Returns an exact localized description of the time interval (in * milliseconds) in the largest unit possible. * * <p> * For example, the following time intervals would be converted to the * following time descriptions, using the English locale: * </p> * * <ul> * <li> * 1000 = 1 Second * </li> * <li> * 1001 = 1001 Milliseconds * </li> * <li> * 86400000 = 1 Day * </li> * <li> * 86401000 = 86401 Seconds * </li> * </ul> * * @param httpServletRequest the request used to determine the current * locale * @param milliseconds the time interval in milliseconds to describe * @return an exact localized description of the time interval in the * largest unit possible */ @Override public String getTimeDescription(HttpServletRequest httpServletRequest, Long milliseconds) { return getTimeDescription(httpServletRequest, milliseconds.longValue()); } /** * Returns an exact localized description of the time interval (in * milliseconds) in the largest unit possible. * * <p> * For example, the following time intervals would be converted to the * following time descriptions, using the English locale: * </p> * * <ul> * <li> * 1000 = 1 Second * </li> * <li> * 1001 = 1001 Milliseconds * </li> * <li> * 86400000 = 1 Day * </li> * <li> * 86401000 = 86401 Seconds * </li> * </ul> * * @param locale the locale used to determine the language * @param milliseconds the time interval in milliseconds to describe * @return an exact localized description of the time interval in the * largest unit possible */ @Override public String getTimeDescription(Locale locale, long milliseconds) { return getTimeDescription(locale, milliseconds, false); } /** * Returns an approximate or exact localized description of the time * interval (in milliseconds) in the largest unit possible. * * <p> * Approximate descriptions round the time to the largest possible unit and * ignores the rest. For example, using the English locale: * </p> * * <ul> * <li> * Any time interval 1000-1999 = 1 Second * </li> * <li> * Any time interval 86400000-172799999 = 1 Day * </li> * </ul> * * <p> * Otherwise, exact descriptions would follow a similar conversion pattern * as below: * </p> * * <ul> * <li> * 1000 = 1 Second * </li> * <li> * 1001 = 1001 Milliseconds * </li> * <li> * 86400000 = 1 Day * </li> * <li> * 86401000 = 86401 Seconds * </li> * </ul> * * @param locale the locale used to determine the language * @param milliseconds the time interval in milliseconds to describe * @param approximate whether the time description is approximate * @return a localized description of the time interval in the largest unit * possible */ @Override public String getTimeDescription(Locale locale, long milliseconds, boolean approximate) { String description = Time.getDescription(milliseconds, approximate); String value = null; try { String[] parts = description.split(StringPool.SPACE, 2); String unit = StringUtil.toLowerCase(parts[1]); if (unit.equals("second")) { unit += "[time]"; } value = format(locale, "x-" + unit, parts[0]); } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } return value; } /** * Returns an exact localized description of the time interval (in * milliseconds) in the largest unit possible. * * <p> * For example, the following time intervals would be converted to the * following time descriptions, using the English locale: * </p> * * <ul> * <li> * 1000 = 1 Second * </li> * <li> * 1001 = 1001 Milliseconds * </li> * <li> * 86400000 = 1 Day * </li> * <li> * 86401000 = 86401 Seconds * </li> * </ul> * * @param locale the locale used to determine the language * @param milliseconds the time interval in milliseconds to describe * @return an exact localized description of the time interval in the * largest unit possible */ @Override public String getTimeDescription(Locale locale, Long milliseconds) { return getTimeDescription(locale, milliseconds.longValue()); } @Override public void init() { _companyLocalesBags.clear(); } /** * Returns <code>true</code> if the language code is configured to be * available. Locales can be configured in <code>portal.properties</code> * using the <code>locales</code> and <code>locales.enabled</code> keys. * * @param languageCode the code representation of a language (e.g. * <code>en</code> and <code>en_GB</code>) to search for * @return <code>true</code> if the language code is configured to be * available; <code>false</code> otherwise */ @Override public boolean isAvailableLanguageCode(String languageCode) { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); return companyLocalesBag.containsLanguageCode(languageCode); } /** * Returns <code>true</code> if the locale is configured to be available. * Locales can be configured in <code>portal.properties</code> using the * <code>locales</code> and <code>locales.enabled</code> keys. * * @param locale the locale to search for * @return <code>true</code> if the locale is configured to be available; * <code>false</code> otherwise */ @Override public boolean isAvailableLocale(Locale locale) { if (locale == null) { return false; } return isAvailableLocale(LocaleUtil.toLanguageId(locale)); } /** * Returns <code>true</code> if the locale is configured to be available in * the group. * * @param groupId the primary key of the group * @param locale the locale to search for * @return <code>true</code> if the locale is configured to be available in * the group; <code>false</code> otherwise */ @Override public boolean isAvailableLocale(long groupId, Locale locale) { if (locale == null) { return false; } return isAvailableLocale(groupId, LocaleUtil.toLanguageId(locale)); } /** * Returns <code>true</code> if the language ID is configured to be * available in the group. * * @param groupId the primary key of the group * @param languageId the language ID to search for * @return <code>true</code> if the language ID is configured to be * available in the group; <code>false</code> otherwise */ @Override public boolean isAvailableLocale(long groupId, String languageId) { if (groupId <= 0) { return isAvailableLocale(languageId); } try { if (isInheritLocales(groupId)) { return isAvailableLocale(languageId); } } catch (Exception e) { } Map<String, Locale> groupLanguageIdLocalesMap = _getGroupLanguageIdLocalesMap(groupId); return groupLanguageIdLocalesMap.containsKey(languageId); } /** * Returns <code>true</code> if the language ID is configured to be * available. * * @param languageId the language ID to search for * @return <code>true</code> if the language ID is configured to be * available; <code>false</code> otherwise */ @Override public boolean isAvailableLocale(String languageId) { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); return companyLocalesBag.containsLanguageId(languageId); } /** * Returns <code>true</code> if the locale is configured to be a beta * language. * * @param locale the locale to search for * @return <code>true</code> if the locale is configured to be a beta * language; <code>false</code> otherwise */ @Override public boolean isBetaLocale(Locale locale) { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); return companyLocalesBag.isBetaLocale(locale); } @Override public boolean isDuplicateLanguageCode(String languageCode) { CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag(); return companyLocalesBag.isDuplicateLanguageCode(languageCode); } @Override public boolean isInheritLocales(long groupId) throws PortalException { Group group = GroupLocalServiceUtil.getGroup(groupId); if (group.isStagingGroup()) { group = group.getLiveGroup(); } if ((!group.isSite() && (group.getType() != GroupConstants.TYPE_DEPOT)) || group.isCompany()) { return true; } return GetterUtil .getBoolean(group.getTypeSettingsProperty(GroupConstants.TYPE_SETTINGS_KEY_INHERIT_LOCALES), true); } @Override public boolean isSameLanguage(Locale locale1, Locale locale2) { if ((locale1 == null) || (locale2 == null)) { return false; } String language1 = locale1.getLanguage(); String language2 = locale2.getLanguage(); return language1.equals(language2); } @Override public String process(Supplier<ResourceBundle> resourceBundleSupplier, Locale locale, String content) { StringBundler sb = null; ResourceBundle resourceBundle = null; Matcher matcher = _pattern.matcher(content); int x = 0; while (matcher.find()) { int y = matcher.start(0); String key = matcher.group(1); if (sb == null) { sb = new StringBundler(); } sb.append(content.substring(x, y)); sb.append(StringPool.APOSTROPHE); if (resourceBundle == null) { resourceBundle = resourceBundleSupplier.get(); } String value = get(resourceBundle, key); sb.append(HtmlUtil.escapeJS(value)); sb.append(StringPool.APOSTROPHE); x = matcher.end(0); } if (sb == null) { return content; } sb.append(content.substring(x)); return sb.toString(); } @Override public void resetAvailableGroupLocales(long groupId) { _resetAvailableGroupLocales(groupId); } @Override public void resetAvailableLocales(long companyId) { _resetAvailableLocales(companyId); } @Override public void updateCookie(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { String languageId = LocaleUtil.toLanguageId(locale); Cookie languageIdCookie = new Cookie(CookieKeys.GUEST_LANGUAGE_ID, languageId); String domain = CookieKeys.getDomain(httpServletRequest); if (Validator.isNotNull(domain)) { languageIdCookie.setDomain(domain); } languageIdCookie.setMaxAge(CookieKeys.MAX_AGE); languageIdCookie.setPath(StringPool.SLASH); CookieKeys.addCookie(httpServletRequest, httpServletResponse, languageIdCookie); } private static CompanyLocalesBag _getCompanyLocalesBag() { return _getCompanyLocalesBag(CompanyThreadLocal.getCompanyId()); } private static CompanyLocalesBag _getCompanyLocalesBag(long companyId) { CompanyLocalesBag companyLocalesBag = _companyLocalesBags.get(companyId); if (companyLocalesBag == null) { companyLocalesBag = new CompanyLocalesBag(companyId); _companyLocalesBags.put(companyId, companyLocalesBag); } return companyLocalesBag; } private static void _updateLastModified() { _lastModified = System.currentTimeMillis(); } private ObjectValuePair<HashMap<String, Locale>, HashMap<String, Locale>> _createGroupLocales(long groupId) { String[] languageIds = PropsValues.LOCALES_ENABLED; Locale defaultLocale = LocaleUtil.getDefault(); try { Group group = GroupLocalServiceUtil.getGroup(groupId); defaultLocale = PortalUtil.getSiteDefaultLocale(group); UnicodeProperties typeSettingsProperties = group.getTypeSettingsProperties(); String groupLanguageIds = typeSettingsProperties.getProperty(PropsKeys.LOCALES); if (groupLanguageIds != null) { languageIds = StringUtil.split(groupLanguageIds); } } catch (Exception e) { } HashMap<String, Locale> groupLanguageIdLocalesMap = new LinkedHashMap<>(); HashMap<String, Locale> groupLanguageCodeLocalesMap = HashMapBuilder .put(defaultLocale.getLanguage(), defaultLocale).build(); for (String languageId : languageIds) { Locale locale = LocaleUtil.fromLanguageId(languageId, false); String languageCode = languageId; int pos = languageId.indexOf(CharPool.UNDERLINE); if (pos > 0) { languageCode = languageId.substring(0, pos); } if (!groupLanguageCodeLocalesMap.containsKey(languageCode)) { groupLanguageCodeLocalesMap.put(languageCode, locale); } groupLanguageIdLocalesMap.put(languageId, locale); } _groupLanguageCodeLocalesMapMap.put(groupId, groupLanguageCodeLocalesMap); _groupLanguageIdLocalesMap.put(groupId, groupLanguageIdLocalesMap); _updateLastModified(); return new ObjectValuePair<>(groupLanguageCodeLocalesMap, groupLanguageIdLocalesMap); } private String _decorateMessageFormat(HttpServletRequest httpServletRequest, String pattern, Object[] formattedArguments) { Locale locale = _getLocale(httpServletRequest); return _decorateMessageFormat(locale, pattern, formattedArguments); } private String _decorateMessageFormat(Locale locale, String pattern, Object[] formattedArguments) { if (locale == null) { locale = LocaleUtil.getDefault(); } String value = _getFastFormattedMessage(locale, pattern, formattedArguments); if (value != null) { return value; } pattern = _escapePattern(pattern); MessageFormat messageFormat = new MessageFormat(pattern, locale); for (int i = 0; i < formattedArguments.length; i++) { Object formattedArgument = formattedArguments[i]; if (formattedArgument instanceof Number) { messageFormat.setFormat(i, NumberFormat.getInstance(locale)); } } return messageFormat.format(formattedArguments); } private String _escapePattern(String pattern) { return StringUtil.replace(pattern, CharPool.APOSTROPHE, StringPool.DOUBLE_APOSTROPHE); } private String _get(ResourceBundle resourceBundle, String key) { if (PropsValues.TRANSLATIONS_DISABLED) { return key; } if ((resourceBundle == null) || (key == null)) { return null; } String value = ResourceBundleUtil.getString(resourceBundle, key); if (value != null) { return LanguageResources.fixValue(value); } if ((key.length() > 0) && (key.charAt(key.length() - 1) == CharPool.CLOSE_BRACKET)) { int pos = key.lastIndexOf(CharPool.OPEN_BRACKET); if (pos != -1) { key = key.substring(0, pos); return _get(resourceBundle, key); } } return null; } private String _getFastFormattedMessage(Locale locale, String pattern, Object[] arguments) { Format dateFormat = null; Format numberFormat = null; int pos = 0; StringBuilder sb = new StringBuilder(16 * arguments.length + pattern.length()); int start = pattern.indexOf(CharPool.OPEN_CURLY_BRACE); while (start != -1) { int endIndex = start + 2; if ((endIndex > pattern.length()) || (pattern.charAt(endIndex) != CharPool.CLOSE_CURLY_BRACE)) { return null; } int argumentIndex = pattern.charAt(start + 1) - CharPool.NUMBER_0; if ((argumentIndex < 0) || (arguments.length <= argumentIndex)) { return null; } sb.append(pattern, pos, start); Object argument = arguments[argumentIndex]; if (argument instanceof Number) { if (numberFormat == null) { numberFormat = NumberFormat.getNumberInstance(locale); } sb.append(numberFormat.format(argument)); } else if (argument instanceof Date) { if (dateFormat == null) { dateFormat = FastDateFormatFactoryUtil.getDateTime(FastDateFormatConstants.SHORT, FastDateFormatConstants.LONG, locale, null); } sb.append(dateFormat.format(argument)); } else { sb.append(argument); } pos = endIndex + 1; start = pattern.indexOf(CharPool.OPEN_CURLY_BRACE, pos); } if (pos < pattern.length()) { sb.append(pattern, pos, pattern.length()); } return sb.toString(); } private Map<String, Locale> _getGroupLanguageCodeLocalesMap(long groupId) { Map<String, Locale> groupLanguageCodeLocalesMap = _groupLanguageCodeLocalesMapMap.get(groupId); if (groupLanguageCodeLocalesMap == null) { ObjectValuePair<HashMap<String, Locale>, HashMap<String, Locale>> objectValuePair = _createGroupLocales( groupId); groupLanguageCodeLocalesMap = objectValuePair.getKey(); } return groupLanguageCodeLocalesMap; } private Map<String, Locale> _getGroupLanguageIdLocalesMap(long groupId) { Map<String, Locale> groupLanguageIdLocalesMap = _groupLanguageIdLocalesMap.get(groupId); if (groupLanguageIdLocalesMap == null) { ObjectValuePair<HashMap<String, Locale>, HashMap<String, Locale>> objectValuePair = _createGroupLocales( groupId); groupLanguageIdLocalesMap = objectValuePair.getValue(); } return groupLanguageIdLocalesMap; } private Locale _getLocale(HttpServletRequest httpServletRequest) { Locale locale = null; ThemeDisplay themeDisplay = (ThemeDisplay) httpServletRequest.getAttribute(WebKeys.THEME_DISPLAY); if (themeDisplay != null) { locale = themeDisplay.getLocale(); } else { locale = httpServletRequest.getLocale(); if (!isAvailableLocale(locale)) { locale = LocaleUtil.getDefault(); } } return locale; } private void _resetAvailableGroupLocales(long groupId) { _groupLocalesPortalCache.remove(groupId); _updateLastModified(); } private void _resetAvailableLocales(long companyId) { _companyLocalesPortalCache.remove(companyId); _updateLastModified(); } private static final String _COMPANY_LOCALES_PORTAL_CACHE_NAME = LanguageImpl.class.getName() + "._companyLocalesPortalCache"; private static final String _GROUP_LOCALES_PORTAL_CACHE_NAME = LanguageImpl.class.getName() + "._groupLocalesPortalCache"; private static final Log _log = LogFactoryUtil.getLog(LanguageImpl.class); private static final Map<Long, CompanyLocalesBag> _companyLocalesBags = new ConcurrentHashMap<>(); private static PortalCache<Long, Serializable> _companyLocalesPortalCache; private static PortalCache<Long, Serializable> _groupLocalesPortalCache; private static volatile long _lastModified = System.currentTimeMillis(); private static final Pattern _pattern = Pattern.compile( "Liferay\\s*\\.\\s*Language\\s*\\.\\s*get\\s*" + "\\(\\s*[\"']([^)]+)[\"']\\s*\\)", Pattern.MULTILINE); private static final Synchronizer<Long, Serializable> _removeSynchronizer = new Synchronizer<Long, Serializable>() { @Override public void onSynchronize(Map<? extends Long, ? extends Serializable> map, Long key, Serializable value, int timeToLive) { map.remove(key); } }; private final Map<Long, HashMap<String, Locale>> _groupLanguageCodeLocalesMapMap = new ConcurrentHashMap<>(); private final Map<Long, HashMap<String, Locale>> _groupLanguageIdLocalesMap = new ConcurrentHashMap<>(); private static class CompanyLocalesBag implements Serializable { public boolean containsLanguageCode(String languageCode) { return _languageCodeLocalesMap.containsKey(languageCode); } public boolean containsLanguageId(String languageId) { return _languageIdLocalesMap.containsKey(languageId); } public Set<Locale> getAvailableLocales() { return _availableLocales; } public Locale getByLanguageCode(String languageCode) { return _languageCodeLocalesMap.get(languageCode); } public boolean isBetaLocale(Locale locale) { return _localesBetaSet.contains(locale); } public boolean isDuplicateLanguageCode(String languageCode) { return _duplicateLanguageCodes.contains(languageCode); } private CompanyLocalesBag(long companyId) { String[] languageIds = PropsValues.LOCALES; if (companyId != CompanyConstants.SYSTEM) { try { languageIds = PrefsPropsUtil.getStringArray(companyId, PropsKeys.LOCALES, StringPool.COMMA, PropsValues.LOCALES_ENABLED); } catch (SystemException se) { // LPS-52675 if (_log.isDebugEnabled()) { _log.debug(se, se); } languageIds = PropsValues.LOCALES_ENABLED; } } Locale defaultLocale = LocaleUtil.getDefault(); String defaultLanguageId = LocaleUtil.toLanguageId(defaultLocale); _languageCodeLocalesMap.put(defaultLocale.getLanguage(), defaultLocale); _languageIdLocalesMap.put(defaultLanguageId, defaultLocale); languageIds = ArrayUtil.remove(languageIds, defaultLanguageId); Set<String> duplicateLanguageCodes = new HashSet<>(); for (String languageId : languageIds) { Locale locale = LocaleUtil.fromLanguageId(languageId, false); String languageCode = languageId; int pos = languageId.indexOf(CharPool.UNDERLINE); if (pos > 0) { languageCode = languageId.substring(0, pos); } if (_languageCodeLocalesMap.containsKey(languageCode)) { duplicateLanguageCodes.add(languageCode); } else { _languageCodeLocalesMap.put(languageCode, locale); } _languageIdLocalesMap.put(languageId, locale); } if (duplicateLanguageCodes.isEmpty()) { _duplicateLanguageCodes = Collections.emptySet(); } else { _duplicateLanguageCodes = duplicateLanguageCodes; } for (String languageId : PropsValues.LOCALES_BETA) { _localesBetaSet.add(LocaleUtil.fromLanguageId(languageId, false)); } _availableLocales = Collections.unmodifiableSet(new LinkedHashSet<>(_languageIdLocalesMap.values())); Set<Locale> supportedLocalesSet = new HashSet<>(_languageIdLocalesMap.values()); supportedLocalesSet.removeAll(_localesBetaSet); _supportedLocalesSet = Collections.unmodifiableSet(supportedLocalesSet); } private final Set<Locale> _availableLocales; private final Set<String> _duplicateLanguageCodes; private final Map<String, Locale> _languageCodeLocalesMap = new HashMap<>(); private final Map<String, Locale> _languageIdLocalesMap = new LinkedHashMap<>(); private final Set<Locale> _localesBetaSet = new HashSet<>(); private final Set<Locale> _supportedLocalesSet; } }