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