001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.language;
016    
017    import com.liferay.portal.kernel.exception.SystemException;
018    import com.liferay.portal.kernel.language.Language;
019    import com.liferay.portal.kernel.language.LanguageWrapper;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
023    import com.liferay.portal.kernel.util.CharPool;
024    import com.liferay.portal.kernel.util.JavaConstants;
025    import com.liferay.portal.kernel.util.LocaleUtil;
026    import com.liferay.portal.kernel.util.ParamUtil;
027    import com.liferay.portal.kernel.util.PropsKeys;
028    import com.liferay.portal.kernel.util.ResourceBundleUtil;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.StringUtil;
031    import com.liferay.portal.kernel.util.Time;
032    import com.liferay.portal.kernel.util.Validator;
033    import com.liferay.portal.model.CompanyConstants;
034    import com.liferay.portal.model.Portlet;
035    import com.liferay.portal.security.auth.CompanyThreadLocal;
036    import com.liferay.portal.service.PortletLocalServiceUtil;
037    import com.liferay.portal.theme.ThemeDisplay;
038    import com.liferay.portal.util.CookieKeys;
039    import com.liferay.portal.util.PortalUtil;
040    import com.liferay.portal.util.PortletKeys;
041    import com.liferay.portal.util.PrefsPropsUtil;
042    import com.liferay.portal.util.PropsValues;
043    import com.liferay.portal.util.WebKeys;
044    import com.liferay.portlet.PortletConfigFactoryUtil;
045    
046    import java.text.MessageFormat;
047    
048    import java.util.ArrayList;
049    import java.util.HashMap;
050    import java.util.HashSet;
051    import java.util.List;
052    import java.util.Locale;
053    import java.util.Map;
054    import java.util.ResourceBundle;
055    import java.util.Set;
056    import java.util.concurrent.ConcurrentHashMap;
057    
058    import javax.portlet.PortletConfig;
059    import javax.portlet.PortletRequest;
060    
061    import javax.servlet.http.Cookie;
062    import javax.servlet.http.HttpServletRequest;
063    import javax.servlet.http.HttpServletResponse;
064    import javax.servlet.jsp.PageContext;
065    
066    /**
067     * @author Brian Wing Shun Chan
068     * @author Andrius Vitkauskas
069     */
070    @DoPrivileged
071    public class LanguageImpl implements Language {
072    
073            @Override
074            public String format(Locale locale, String pattern, Object argument) {
075                    return format(locale, pattern, new Object[] {argument}, true);
076            }
077    
078            @Override
079            public String format(
080                    Locale locale, String pattern, Object argument,
081                    boolean translateArguments) {
082    
083                    return format(
084                            locale, pattern, new Object[] {argument}, translateArguments);
085            }
086    
087            @Override
088            public String format(Locale locale, String pattern, Object[] arguments) {
089                    return format(locale, pattern, arguments, true);
090            }
091    
092            @Override
093            public String format(
094                    Locale locale, String pattern, Object[] arguments,
095                    boolean translateArguments) {
096    
097                    if (PropsValues.TRANSLATIONS_DISABLED) {
098                            return pattern;
099                    }
100    
101                    String value = null;
102    
103                    try {
104                            pattern = get(locale, pattern);
105    
106                            if ((arguments != null) && (arguments.length > 0)) {
107                                    pattern = _escapePattern(pattern);
108    
109                                    Object[] formattedArguments = new Object[arguments.length];
110    
111                                    for (int i = 0; i < arguments.length; i++) {
112                                            if (translateArguments) {
113                                                    formattedArguments[i] = get(
114                                                            locale, arguments[i].toString());
115                                            }
116                                            else {
117                                                    formattedArguments[i] = arguments[i];
118                                            }
119                                    }
120    
121                                    value = MessageFormat.format(pattern, formattedArguments);
122                            }
123                            else {
124                                    value = pattern;
125                            }
126                    }
127                    catch (Exception e) {
128                            if (_log.isWarnEnabled()) {
129                                    _log.warn(e, e);
130                            }
131                    }
132    
133                    return value;
134            }
135    
136            @Override
137            public String format(
138                    PageContext pageContext, String pattern, LanguageWrapper argument) {
139    
140                    return format(
141                            pageContext, pattern, new LanguageWrapper[] {argument}, true);
142            }
143    
144            @Override
145            public String format(
146                    PageContext pageContext, String pattern, LanguageWrapper argument,
147                    boolean translateArguments) {
148    
149                    return format(
150                            pageContext, pattern, new LanguageWrapper[] {argument},
151                            translateArguments);
152            }
153    
154            @Override
155            public String format(
156                    PageContext pageContext, String pattern, LanguageWrapper[] arguments) {
157    
158                    return format(pageContext, pattern, arguments, true);
159            }
160    
161            @Override
162            public String format(
163                    PageContext pageContext, String pattern, LanguageWrapper[] arguments,
164                    boolean translateArguments) {
165    
166                    if (PropsValues.TRANSLATIONS_DISABLED) {
167                            return pattern;
168                    }
169    
170                    String value = null;
171    
172                    try {
173                            pattern = get(pageContext, pattern);
174    
175                            if ((arguments != null) && (arguments.length > 0)) {
176                                    pattern = _escapePattern(pattern);
177    
178                                    Object[] formattedArguments = new Object[arguments.length];
179    
180                                    for (int i = 0; i < arguments.length; i++) {
181                                            if (translateArguments) {
182                                                    formattedArguments[i] =
183                                                            arguments[i].getBefore() +
184                                                            get(pageContext, arguments[i].getText()) +
185                                                            arguments[i].getAfter();
186                                            }
187                                            else {
188                                                    formattedArguments[i] =
189                                                            arguments[i].getBefore() +
190                                                            arguments[i].getText() +
191                                                            arguments[i].getAfter();
192                                            }
193                                    }
194    
195                                    value = MessageFormat.format(pattern, formattedArguments);
196                            }
197                            else {
198                                    value = pattern;
199                            }
200                    }
201                    catch (Exception e) {
202                            if (_log.isWarnEnabled()) {
203                                    _log.warn(e, e);
204                            }
205                    }
206    
207                    return value;
208            }
209    
210            @Override
211            public String format(
212                    PageContext pageContext, String pattern, Object argument) {
213    
214                    return format(pageContext, pattern, new Object[] {argument}, true);
215            }
216    
217            @Override
218            public String format(
219                    PageContext pageContext, String pattern, Object argument,
220                    boolean translateArguments) {
221    
222                    return format(
223                            pageContext, pattern, new Object[] {argument}, translateArguments);
224            }
225    
226            @Override
227            public String format(
228                    PageContext pageContext, String pattern, Object[] arguments) {
229    
230                    return format(pageContext, pattern, arguments, true);
231            }
232    
233            @Override
234            public String format(
235                    PageContext pageContext, String pattern, Object[] arguments,
236                    boolean translateArguments) {
237    
238                    if (PropsValues.TRANSLATIONS_DISABLED) {
239                            return pattern;
240                    }
241    
242                    String value = null;
243    
244                    try {
245                            pattern = get(pageContext, pattern);
246    
247                            if ((arguments != null) && (arguments.length > 0)) {
248                                    pattern = _escapePattern(pattern);
249    
250                                    Object[] formattedArguments = new Object[arguments.length];
251    
252                                    for (int i = 0; i < arguments.length; i++) {
253                                            if (translateArguments) {
254                                                    formattedArguments[i] = get(
255                                                            pageContext, arguments[i].toString());
256                                            }
257                                            else {
258                                                    formattedArguments[i] = arguments[i];
259                                            }
260                                    }
261    
262                                    value = MessageFormat.format(pattern, formattedArguments);
263                            }
264                            else {
265                                    value = pattern;
266                            }
267                    }
268                    catch (Exception e) {
269                            if (_log.isWarnEnabled()) {
270                                    _log.warn(e, e);
271                            }
272                    }
273    
274                    return value;
275            }
276    
277            @Override
278            public String format(
279                    PortletConfig portletConfig, Locale locale, String pattern,
280                    Object argument) {
281    
282                    return format(
283                    portletConfig, locale, pattern, new Object[] {argument}, true);
284            }
285    
286            @Override
287            public String format(
288                    PortletConfig portletConfig, Locale locale, String pattern,
289                    Object argument, boolean translateArguments) {
290    
291                    return format(
292                            portletConfig, locale, pattern, new Object[] {argument},
293                            translateArguments);
294            }
295    
296            @Override
297            public String format(
298                    PortletConfig portletConfig, Locale locale, String pattern,
299                    Object[] arguments) {
300    
301                    return format(portletConfig, locale, pattern, arguments, true);
302            }
303    
304            @Override
305            public String format(
306                    PortletConfig portletConfig, Locale locale, String pattern,
307                    Object[] arguments, boolean translateArguments) {
308    
309                    if (PropsValues.TRANSLATIONS_DISABLED) {
310                            return pattern;
311                    }
312    
313                    String value = null;
314    
315                    try {
316                            pattern = get(portletConfig, locale, pattern);
317    
318                            if ((arguments != null) && (arguments.length > 0)) {
319                                    pattern = _escapePattern(pattern);
320    
321                                    Object[] formattedArguments = new Object[arguments.length];
322    
323                                    for (int i = 0; i < arguments.length; i++) {
324                                            if (translateArguments) {
325                                                    formattedArguments[i] = get(
326                                                            locale, arguments[i].toString());
327                                            }
328                                            else {
329                                                    formattedArguments[i] = arguments[i];
330                                            }
331                                    }
332    
333                                    value = MessageFormat.format(pattern, formattedArguments);
334                            }
335                            else {
336                                    value = pattern;
337                            }
338                    }
339                    catch (Exception e) {
340                            if (_log.isWarnEnabled()) {
341                                    _log.warn(e, e);
342                            }
343                    }
344    
345                    return value;
346            }
347    
348            @Override
349            public String get(Locale locale, String key) {
350                    return get(locale, key, key);
351            }
352    
353            @Override
354            public String get(Locale locale, String key, String defaultValue) {
355                    if (PropsValues.TRANSLATIONS_DISABLED) {
356                            return key;
357                    }
358    
359                    if (key == null) {
360                            return null;
361                    }
362    
363                    String value = LanguageResources.getMessage(locale, key);
364    
365                    while ((value == null) || value.equals(defaultValue)) {
366                            if ((key.length() > 0) &&
367                                    (key.charAt(key.length() - 1) == CharPool.CLOSE_BRACKET)) {
368    
369                                    int pos = key.lastIndexOf(CharPool.OPEN_BRACKET);
370    
371                                    if (pos != -1) {
372                                            key = key.substring(0, pos);
373    
374                                            value = LanguageResources.getMessage(locale, key);
375    
376                                            continue;
377                                    }
378                            }
379    
380                            break;
381                    }
382    
383                    if (value == null) {
384                            value = defaultValue;
385                    }
386    
387                    return value;
388            }
389    
390            @Override
391            public String get(PageContext pageContext, String key) {
392                    return get(pageContext, key, key);
393            }
394    
395            @Override
396            public String get(
397                    PageContext pageContext, String key, String defaultValue) {
398    
399                    try {
400                            return _get(pageContext, null, null, key, defaultValue);
401                    }
402                    catch (Exception e) {
403                            if (_log.isWarnEnabled()) {
404                                    _log.warn(e, e);
405                            }
406    
407                            return defaultValue;
408                    }
409            }
410    
411            @Override
412            public String get(PortletConfig portletConfig, Locale locale, String key) {
413                    return get(portletConfig, locale, key, key);
414            }
415    
416            @Override
417            public String get(
418                    PortletConfig portletConfig, Locale locale, String key,
419                    String defaultValue) {
420    
421                    try {
422                            return _get(null, portletConfig, locale, key, defaultValue);
423                    }
424                    catch (Exception e) {
425                            if (_log.isWarnEnabled()) {
426                                    _log.warn(e, e);
427                            }
428    
429                            return defaultValue;
430                    }
431            }
432    
433            @Override
434            public Locale[] getAvailableLocales() {
435                    return _getInstance()._locales;
436            }
437    
438            @Override
439            public String getCharset(Locale locale) {
440                    return _getInstance()._getCharset(locale);
441            }
442    
443            @Override
444            public String getLanguageId(HttpServletRequest request) {
445                    String languageId = ParamUtil.getString(request, "languageId");
446    
447                    if (Validator.isNotNull(languageId)) {
448                            if (_localesMap.containsKey(languageId) ||
449                                    _charEncodings.containsKey(languageId)) {
450    
451                                    return languageId;
452                            }
453                    }
454    
455                    Locale locale = PortalUtil.getLocale(request);
456    
457                    return getLanguageId(locale);
458            }
459    
460            @Override
461            public String getLanguageId(Locale locale) {
462                    return LocaleUtil.toLanguageId(locale);
463            }
464    
465            @Override
466            public String getLanguageId(PortletRequest portletRequest) {
467                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
468                            portletRequest);
469    
470                    return getLanguageId(request);
471            }
472    
473            @Override
474            public Locale getLocale(String languageCode) {
475                    return _getInstance()._getLocale(languageCode);
476            }
477    
478            @Override
479            public Locale[] getSupportedLocales() {
480                    List<Locale> supportedLocales = new ArrayList<Locale>();
481    
482                    Locale[] locales = getAvailableLocales();
483    
484                    for (Locale locale : locales) {
485                            if (!isBetaLocale(locale)) {
486                                    supportedLocales.add(locale);
487                            }
488                    }
489    
490                    return supportedLocales.toArray(new Locale[supportedLocales.size()]);
491            }
492    
493            @Override
494            public String getTimeDescription(Locale locale, long milliseconds) {
495                    return getTimeDescription(locale, milliseconds, false);
496            }
497    
498            @Override
499            public String getTimeDescription(
500                    Locale locale, long milliseconds, boolean approximate) {
501    
502                    String description = Time.getDescription(milliseconds, approximate);
503    
504                    String value = null;
505    
506                    try {
507                            int pos = description.indexOf(CharPool.SPACE);
508    
509                            String x = description.substring(0, pos);
510    
511                            value = x.concat(StringPool.SPACE).concat(
512                                    get(
513                                            locale,
514                                            description.substring(
515                                                    pos + 1, description.length()).toLowerCase()));
516                    }
517                    catch (Exception e) {
518                            if (_log.isWarnEnabled()) {
519                                    _log.warn(e, e);
520                            }
521                    }
522    
523                    return value;
524            }
525    
526            @Override
527            public String getTimeDescription(Locale locale, Long milliseconds) {
528                    return getTimeDescription(locale, milliseconds.longValue());
529            }
530    
531            @Override
532            public String getTimeDescription(
533                    PageContext pageContext, long milliseconds) {
534    
535                    return getTimeDescription(pageContext, milliseconds, false);
536            }
537    
538            @Override
539            public String getTimeDescription(
540                    PageContext pageContext, long milliseconds, boolean approximate) {
541    
542                    String description = Time.getDescription(milliseconds, approximate);
543    
544                    String value = null;
545    
546                    try {
547                            int pos = description.indexOf(CharPool.SPACE);
548    
549                            String x = description.substring(0, pos);
550    
551                            value = x.concat(StringPool.SPACE).concat(
552                                    get(
553                                            pageContext,
554                                            description.substring(
555                                                    pos + 1, description.length()).toLowerCase()));
556                    }
557                    catch (Exception e) {
558                            if (_log.isWarnEnabled()) {
559                                    _log.warn(e, e);
560                            }
561                    }
562    
563                    return value;
564            }
565    
566            @Override
567            public String getTimeDescription(
568                    PageContext pageContext, Long milliseconds) {
569    
570                    return getTimeDescription(pageContext, milliseconds.longValue());
571            }
572    
573            @Override
574            public void init() {
575                    _instances.clear();
576            }
577    
578            @Override
579            public boolean isAvailableLanguageCode(String languageCode) {
580                    return _getInstance()._localesMap.containsKey(languageCode);
581            }
582    
583            @Override
584            public boolean isAvailableLocale(Locale locale) {
585                    return _getInstance()._localesSet.contains(locale);
586            }
587    
588            @Override
589            public boolean isAvailableLocale(String languageId) {
590                    Locale[] locales = getAvailableLocales();
591    
592                    for (Locale locale : locales) {
593                            if (languageId.equals(locale.toString())) {
594                                    return true;
595                            }
596                    }
597    
598                    return false;
599            }
600    
601            @Override
602            public boolean isBetaLocale(Locale locale) {
603                    return _getInstance()._localesBetaSet.contains(locale);
604            }
605    
606            @Override
607            public boolean isDuplicateLanguageCode(String languageCode) {
608                    return _getInstance()._duplicateLanguageCodes.contains(languageCode);
609            }
610    
611            @Override
612            public void resetAvailableLocales(long companyId) {
613                    _resetAvailableLocales(companyId);
614            }
615    
616            @Override
617            public void updateCookie(
618                    HttpServletRequest request, HttpServletResponse response,
619                    Locale locale) {
620    
621                    String languageId = LocaleUtil.toLanguageId(locale);
622    
623                    Cookie languageIdCookie = new Cookie(
624                            CookieKeys.GUEST_LANGUAGE_ID, languageId);
625    
626                    languageIdCookie.setPath(StringPool.SLASH);
627                    languageIdCookie.setMaxAge(CookieKeys.MAX_AGE);
628    
629                    CookieKeys.addCookie(request, response, languageIdCookie);
630            }
631    
632            private static LanguageImpl _getInstance() {
633                    Long companyId = CompanyThreadLocal.getCompanyId();
634    
635                    LanguageImpl instance = _instances.get(companyId);
636    
637                    if (instance == null) {
638                            instance = new LanguageImpl(companyId);
639    
640                            _instances.put(companyId, instance);
641                    }
642    
643                    return instance;
644            }
645    
646            private LanguageImpl() {
647                    this(CompanyConstants.SYSTEM);
648            }
649    
650            private LanguageImpl(long companyId) {
651                    String[] localesArray = PropsValues.LOCALES;
652    
653                    if (companyId != CompanyConstants.SYSTEM) {
654                            try {
655                                    localesArray = PrefsPropsUtil.getStringArray(
656                                            companyId, PropsKeys.LOCALES, StringPool.COMMA,
657                                            PropsValues.LOCALES);
658                            }
659                            catch (SystemException se) {
660                                    localesArray = PropsValues.LOCALES;
661                            }
662                    }
663    
664                    _charEncodings = new HashMap<String, String>();
665                    _duplicateLanguageCodes = new HashSet<String>();
666                    _locales = new Locale[localesArray.length];
667                    _localesMap = new HashMap<String, Locale>(localesArray.length);
668                    _localesSet = new HashSet<Locale>(localesArray.length);
669    
670                    for (int i = 0; i < localesArray.length; i++) {
671                            String languageId = localesArray[i];
672    
673                            Locale locale = LocaleUtil.fromLanguageId(languageId, false);
674    
675                            _charEncodings.put(locale.toString(), StringPool.UTF8);
676    
677                            String language = languageId;
678    
679                            int pos = languageId.indexOf(CharPool.UNDERLINE);
680    
681                            if (pos > 0) {
682                                    language = languageId.substring(0, pos);
683                            }
684    
685                            if (_localesMap.containsKey(language)) {
686                                    _duplicateLanguageCodes.add(language);
687                            }
688    
689                            _locales[i] = locale;
690    
691                            if (!_localesMap.containsKey(language)) {
692                                    _localesMap.put(language, locale);
693                            }
694    
695                            _localesSet.add(locale);
696                    }
697    
698                    String[] localesBetaArray = PropsValues.LOCALES_BETA;
699    
700                    _localesBetaSet = new HashSet<Locale>(localesBetaArray.length);
701    
702                    for (String languageId : localesBetaArray) {
703                            Locale locale = LocaleUtil.fromLanguageId(languageId, false);
704    
705                            _localesBetaSet.add(locale);
706                    }
707            }
708    
709            private String _escapePattern(String pattern) {
710                    return StringUtil.replace(
711                            pattern, StringPool.APOSTROPHE, StringPool.DOUBLE_APOSTROPHE);
712            }
713    
714            private String _get(
715                            PageContext pageContext, PortletConfig portletConfig, Locale locale,
716                            String key, String defaultValue)
717                    throws Exception {
718    
719                    if (PropsValues.TRANSLATIONS_DISABLED) {
720                            return key;
721                    }
722    
723                    if (key == null) {
724                            return null;
725                    }
726    
727                    String value = null;
728    
729                    if (pageContext != null) {
730                            HttpServletRequest request =
731                                    (HttpServletRequest)pageContext.getRequest();
732    
733                            ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
734                                    WebKeys.THEME_DISPLAY);
735    
736                            locale = themeDisplay.getLocale();
737    
738                            portletConfig = (PortletConfig)request.getAttribute(
739                                    JavaConstants.JAVAX_PORTLET_CONFIG);
740                    }
741    
742                    if (portletConfig != null) {
743                            ResourceBundle resourceBundle = portletConfig.getResourceBundle(
744                                    locale);
745    
746                            value = ResourceBundleUtil.getString(resourceBundle, key);
747    
748                            // LEP-7393
749    
750                            String portletName = portletConfig.getPortletName();
751    
752                            if (((value == null) || value.equals(defaultValue)) &&
753                                    portletName.equals(PortletKeys.PORTLET_CONFIGURATION)) {
754    
755                                    value = _getPortletConfigurationValue(pageContext, locale, key);
756                            }
757    
758                            if (value != null) {
759                                    value = LanguageResources.fixValue(value);
760                            }
761                    }
762    
763                    if ((value == null) || value.equals(defaultValue)) {
764                            value = LanguageResources.getMessage(locale, key);
765                    }
766    
767                    if ((value == null) || value.equals(defaultValue)) {
768                            if ((key.length() > 0) &&
769                                    (key.charAt(key.length() - 1) == CharPool.CLOSE_BRACKET)) {
770    
771                                    int pos = key.lastIndexOf(CharPool.OPEN_BRACKET);
772    
773                                    if (pos != -1) {
774                                            key = key.substring(0, pos);
775    
776                                            return _get(
777                                                    pageContext, portletConfig, locale, key, defaultValue);
778                                    }
779                            }
780                    }
781    
782                    if ((value == null) || value.equals(key)) {
783                            value = defaultValue;
784                    }
785    
786                    return value;
787            }
788    
789            private String _getCharset(Locale locale) {
790                    return StringPool.UTF8;
791            }
792    
793            private Locale _getLocale(String languageCode) {
794                    return _localesMap.get(languageCode);
795            }
796    
797            private String _getPortletConfigurationValue(
798                            PageContext pageContext, Locale locale, String key)
799                    throws Exception {
800    
801                    if (PropsValues.TRANSLATIONS_DISABLED) {
802                            return key;
803                    }
804    
805                    HttpServletRequest request =
806                            (HttpServletRequest)pageContext.getRequest();
807    
808                    String portletResource = ParamUtil.getString(
809                            request, "portletResource");
810    
811                    long companyId = PortalUtil.getCompanyId(request);
812    
813                    Portlet portlet = PortletLocalServiceUtil.getPortletById(
814                            companyId, portletResource);
815    
816                    PortletConfig portletConfig = PortletConfigFactoryUtil.create(
817                            portlet, pageContext.getServletContext());
818    
819                    ResourceBundle resourceBundle = portletConfig.getResourceBundle(locale);
820    
821                    return ResourceBundleUtil.getString(resourceBundle, key);
822            }
823    
824            private void _resetAvailableLocales(long companyId) {
825                    _instances.remove(companyId);
826            }
827    
828            private static Log _log = LogFactoryUtil.getLog(LanguageImpl.class);
829    
830            private static Map<Long, LanguageImpl> _instances =
831                    new ConcurrentHashMap<Long, LanguageImpl>();
832    
833            private Map<String, String> _charEncodings;
834            private Set<String> _duplicateLanguageCodes;
835            private Locale[] _locales;
836            private Set<Locale> _localesBetaSet;
837            private Map<String, Locale> _localesMap;
838            private Set<Locale> _localesSet;
839    
840    }