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