/**
* 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.portal.kernel.cache.MultiVMPoolUtil;
import com.liferay.portal.kernel.cache.PortalCache;
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.security.pacl.DoPrivileged;
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.CharPool;
import com.liferay.portal.kernel.util.CookieKeys;
import com.liferay.portal.kernel.util.GetterUtil;
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.StringPool;
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.MessageFormat;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
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.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
*/
@DoPrivileged
public class LanguageImpl implements Language, Serializable {
public void afterPropertiesSet() {
_companyLocalesPortalCache = MultiVMPoolUtil.getPortalCache(
_COMPANY_LOCALES_PORTAL_CACHE_NAME);
PortalCacheMapSynchronizeUtil.synchronize(
_companyLocalesPortalCache, _companyLocalesBags,
_removeSynchronizer);
_groupLocalesPortalCache = MultiVMPoolUtil.getPortalCache(
_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 request 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 request, String pattern, LanguageWrapper argument) {
return format(request, 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 request 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 request, String pattern, LanguageWrapper argument,
boolean translateArguments) {
return format(
request, 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 request 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 request, String pattern,
LanguageWrapper[] arguments) {
return format(request, 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 request 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 request, String pattern, LanguageWrapper[] arguments,
boolean translateArguments) {
if (PropsValues.TRANSLATIONS_DISABLED) {
return pattern;
}
String value = null;
try {
pattern = get(request, pattern);
if (ArrayUtil.isNotEmpty(arguments)) {
pattern = _escapePattern(pattern);
Object[] formattedArguments = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
if (translateArguments) {
formattedArguments[i] =
arguments[i].getBefore() +
get(request, arguments[i].getText()) +
arguments[i].getAfter();
}
else {
formattedArguments[i] =
arguments[i].getBefore() + arguments[i].getText() +
arguments[i].getAfter();
}
}
MessageFormat messageFormat = decorateMessageFormat(
request, pattern, formattedArguments);
value = messageFormat.format(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 request 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 request, String pattern, Object argument) {
return format(request, 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 request 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 request, String pattern, Object argument,
boolean translateArguments) {
return format(
request, 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 request 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 request, String pattern, Object[] arguments) {
return format(request, 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 request 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 request, String pattern, Object[] arguments,
boolean translateArguments) {
if (PropsValues.TRANSLATIONS_DISABLED) {
return pattern;
}
String value = null;
try {
pattern = get(request, pattern);
if (ArrayUtil.isNotEmpty(arguments)) {
pattern = _escapePattern(pattern);
for (int i = 0; i < arguments.length; i++) {
if (translateArguments) {
arguments[i] = get(request, arguments[i].toString());
}
}
MessageFormat messageFormat = decorateMessageFormat(
request, pattern, arguments);
value = messageFormat.format(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)) {
pattern = _escapePattern(pattern);
for (int i = 0; i < arguments.length; i++) {
if (translateArguments) {
arguments[i] = get(locale, arguments[i].toString());
}
}
MessageFormat messageFormat = decorateMessageFormat(
locale, pattern, arguments);
value = messageFormat.format(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)) {
pattern = _escapePattern(pattern);
for (int i = 0; i < arguments.length; i++) {
if (translateArguments) {
arguments[i] = get(
resourceBundle, arguments[i].toString());
}
}
MessageFormat messageFormat = decorateMessageFormat(
resourceBundle.getLocale(), pattern, arguments);
value = messageFormat.format(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 request 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 request, ResourceBundle resourceBundle, String key) {
return get(request, resourceBundle, key, key);
}
@Override
public String get(
HttpServletRequest request, ResourceBundle resourceBundle, String key,
String defaultValue) {
String value = _get(resourceBundle, key);
if (value != null) {
return value;
}
return get(request, key, defaultValue);
}
@Override
public String get(HttpServletRequest request, String key) {
return get(request, 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 request 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 request, String key, String defaultValue) {
if ((request == null) || (key == null)) {
return defaultValue;
}
PortletConfig portletConfig = (PortletConfig)request.getAttribute(
JavaConstants.JAVAX_PORTLET_CONFIG);
Locale locale = _getLocale(request);
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 (value == null) {
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)) {
return getAvailableLocales();
}
}
catch (Exception e) {
}
Map<String, Locale> groupLanguageIdLocalesMap =
_getGroupLanguageIdLocalesMap(groupId);
return new HashSet<>(groupLanguageIdLocalesMap.values());
}
@Override
public String getBCP47LanguageId(HttpServletRequest request) {
Locale locale = PortalUtil.getLocale(request);
return getBCP47LanguageId(locale);
}
@Override
public String getBCP47LanguageId(Locale locale) {
return LocaleUtil.toBCP47LanguageId(locale);
}
@Override
public String getBCP47LanguageId(PortletRequest portletRequest) {
Locale locale = PortalUtil.getLocale(portletRequest);
return getBCP47LanguageId(locale);
}
/**
* 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 request the request used to determine the language ID
* @return the language ID that the request is served with
*/
@Override
public String getLanguageId(HttpServletRequest request) {
String languageId = ParamUtil.getString(request, "languageId");
if (Validator.isNotNull(languageId)) {
CompanyLocalesBag companyLocalesBag = _getCompanyLocalesBag();
if (companyLocalesBag.containsLanguageCode(languageId) ||
companyLocalesBag.containsLanguageId(languageId)) {
return languageId;
}
}
Locale locale = PortalUtil.getLocale(request);
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) {
HttpServletRequest request = PortalUtil.getHttpServletRequest(
portletRequest);
return getLanguageId(request);
}
@Override
public Locale getLocale(long groupId, String languageCode) {
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 request 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 request, long milliseconds) {
return getTimeDescription(request, 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 request 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 request, long milliseconds, boolean approximate) {
String description = Time.getDescription(milliseconds, approximate);
String value = null;
try {
int pos = description.indexOf(CharPool.SPACE);
String x = description.substring(0, pos);
value = x.concat(StringPool.SPACE).concat(
get(
request,
StringUtil.toLowerCase(
description.substring(pos + 1, description.length()))));
}
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 request 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 request, Long milliseconds) {
return getTimeDescription(request, 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 {
int pos = description.indexOf(CharPool.SPACE);
String x = description.substring(0, pos);
value = x.concat(StringPool.SPACE).concat(
get(
locale,
StringUtil.toLowerCase(
description.substring(pos + 1, description.length()))));
}
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.isCompany()) {
return true;
}
return GetterUtil.getBoolean(
group.getTypeSettingsProperty(
GroupConstants.TYPE_SETTINGS_KEY_INHERIT_LOCALES),
true);
}
@Override
public String process(
ResourceBundle resourceBundle, Locale locale, String content) {
StringBundler sb = new StringBundler();
Matcher matcher = _pattern.matcher(content);
int x = 0;
while (matcher.find()) {
int y = matcher.start(0);
String key = matcher.group(1);
sb.append(content.substring(x, y));
sb.append(StringPool.APOSTROPHE);
String value = get(resourceBundle, key);
sb.append(HtmlUtil.escapeJS(value));
sb.append(StringPool.APOSTROPHE);
x = matcher.end(0);
}
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 request, HttpServletResponse response,
Locale locale) {
String languageId = LocaleUtil.toLanguageId(locale);
Cookie languageIdCookie = new Cookie(
CookieKeys.GUEST_LANGUAGE_ID, languageId);
String domain = CookieKeys.getDomain(request);
if (Validator.isNotNull(domain)) {
languageIdCookie.setDomain(domain);
}
languageIdCookie.setMaxAge(CookieKeys.MAX_AGE);
languageIdCookie.setPath(StringPool.SLASH);
CookieKeys.addCookie(request, response, languageIdCookie);
}
protected MessageFormat decorateMessageFormat(
HttpServletRequest request, String pattern,
Object[] formattedArguments) {
Locale locale = _getLocale(request);
return decorateMessageFormat(locale, pattern, formattedArguments);
}
protected MessageFormat decorateMessageFormat(
Locale locale, String pattern, Object[] formattedArguments) {
if (locale == null) {
locale = LocaleUtil.getDefault();
}
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;
}
private static CompanyLocalesBag _getCompanyLocalesBag() {
Long companyId = CompanyThreadLocal.getCompanyId();
CompanyLocalesBag companyLocalesBag = _companyLocalesBags.get(
companyId);
if (companyLocalesBag == null) {
companyLocalesBag = new CompanyLocalesBag(companyId);
_companyLocalesBags.put(companyId, companyLocalesBag);
}
return companyLocalesBag;
}
private ObjectValuePair<HashMap<String, Locale>, HashMap<String, Locale>>
_createGroupLocales(long groupId) {
String[] languageIds = PropsValues.LOCALES_ENABLED;
try {
Group group = GroupLocalServiceUtil.getGroup(groupId);
UnicodeProperties typeSettingsProperties =
group.getTypeSettingsProperties();
languageIds = StringUtil.split(
typeSettingsProperties.getProperty(PropsKeys.LOCALES));
}
catch (Exception e) {
}
HashMap<String, Locale> groupLanguageCodeLocalesMap = new HashMap<>();
HashMap<String, Locale> groupLanguageIdLocalesMap = new HashMap<>();
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);
return new ObjectValuePair<>(
groupLanguageCodeLocalesMap, groupLanguageIdLocalesMap);
}
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 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 request) {
Locale locale = null;
ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
WebKeys.THEME_DISPLAY);
if (themeDisplay != null) {
locale = themeDisplay.getLocale();
}
else {
locale = request.getLocale();
if (!isAvailableLocale(locale)) {
locale = LocaleUtil.getDefault();
}
}
return locale;
}
private void _resetAvailableGroupLocales(long groupId) {
_groupLocalesPortalCache.remove(groupId);
}
private void _resetAvailableLocales(long companyId) {
_companyLocalesPortalCache.remove(companyId);
}
private static final String _COMPANY_LOCALES_PORTAL_CACHE_NAME =
LanguageImpl.class + "._companyLocalesPortalCache";
private static final String _GROUP_LOCALES_PORTAL_CACHE_NAME =
LanguageImpl.class + "._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 final Pattern _pattern = Pattern.compile(
"Liferay\\.Language\\.get\\([\"']([^)]+)[\"']\\)");
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;
}
}
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 HashSet<>(_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 HashMap<>();
private final Set<Locale> _localesBetaSet = new HashSet<>();
private final Set<Locale> _supportedLocalesSet;
}
}