001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019
020package lucee.runtime.i18n;
021
022import java.util.Arrays;
023import java.util.Iterator;
024import java.util.LinkedHashMap;
025import java.util.Locale;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.regex.Matcher;
029import java.util.regex.Pattern;
030
031import lucee.runtime.exp.ExpressionException;
032import lucee.runtime.type.util.ListUtil;
033
034
035/**
036 * Factory to create Locales by CFML rules
037 */
038public final class LocaleFactory {
039        //private static Pattern localePattern = Pattern.compile("^\\s*([^\\s\\(]+)\\s*(\\(\\s*([^\\s\\)]+)\\s*\\))?\\s*$");
040        private static Pattern localePattern = Pattern.compile("^\\s*([^\\(]+)\\s*(\\(\\s*([^\\)]+)\\s*\\))?\\s*$");
041        private static Pattern localePattern2 = Pattern.compile("^([a-z]{2})_([a-z]{2,3})$");
042        private static Pattern localePattern3 = Pattern.compile("^([a-z]{2})_([a-z]{2,3})_([a-z]{2,})$");
043        
044        private static Map<String,Locale> locales=new LinkedHashMap<String,Locale>();
045        private static Map<String,Locale> localeAlias=new LinkedHashMap<String,Locale>();
046        
047        private static String list;
048        static {
049                Locale[] ls = Locale.getAvailableLocales();
050                
051                
052                String key;
053                StringBuilder sb=new StringBuilder();
054                for(int i=0;i<ls.length;i++) {
055                        key=ls[i].getDisplayName(Locale.US).toLowerCase();
056                        locales.put(key, ls[i]);
057                        if(key.indexOf(',')!=-1){
058                                key=ls[i].toString();
059                                //print.ln(key);
060                                
061                        }
062                        if(i>0)sb.append(",");
063                        sb.append(key);
064                }
065                list=sb.toString();
066                
067                
068                setLocalAlias("albanian (albania)", LocaleConstant.ALBANIAN_ALBANIA);
069
070                setLocalAlias("arabic (algeria)", LocaleConstant.ARABIC_ALGERIA);
071                setLocalAlias("arabic (bahrain)", LocaleConstant.ARABIC_BAHRAIN);
072                setLocalAlias("arabic (egypt)", LocaleConstant.ARABIC_EGYPT);
073                setLocalAlias("arabic (iraq)", LocaleConstant.ARABIC_IRAQ);
074                setLocalAlias("arabic (jordan)", LocaleConstant.ARABIC_JORDAN);
075                setLocalAlias("arabic (kuwait)", LocaleConstant.ARABIC_KUWAIT);
076                setLocalAlias("arabic (lebanon)", LocaleConstant.ARABIC_LEBANON);
077                setLocalAlias("arabic (libya)", LocaleConstant.ARABIC_LIBYA);
078                setLocalAlias("arabic (morocco)", LocaleConstant.ARABIC_MAROCCO);
079                setLocalAlias("arabic (oman)", LocaleConstant.ARABIC_OMAN);
080                setLocalAlias("arabic (qatar)", LocaleConstant.ARABIC_QATAR);
081                setLocalAlias("arabic (saudi arabia)", LocaleConstant.ARABIC_SAUDI_ARABIA);
082                setLocalAlias("arabic (sudan)", LocaleConstant.ARABIC_SUDAN);
083                setLocalAlias("arabic (syria)", LocaleConstant.ARABIC_SYRIA);
084                setLocalAlias("arabic (tunisia)", LocaleConstant.ARABIC_TUNISIA);
085                setLocalAlias("arabic (united arab emirates)", LocaleConstant.ARABIC_UNITED_ARAB_EMIRATES);
086                setLocalAlias("arabic (yemen)", LocaleConstant.ARABIC_YEMEN);
087                        
088                setLocalAlias("chinese (china)", Locale.CHINA);
089                setLocalAlias("chinese (hong kong)",LocaleConstant.CHINESE_HONG_KONG);
090                setLocalAlias("chinese (singapore)",LocaleConstant.CHINESE_SINGAPORE);
091            setLocalAlias("chinese (taiwan)",LocaleConstant.CHINESE_TAIWAN);
092            setLocalAlias("dutch (belgian)",LocaleConstant.DUTCH_BELGIUM);
093            setLocalAlias("dutch (belgium)",LocaleConstant.DUTCH_BELGIUM);
094            setLocalAlias("dutch (standard)",LocaleConstant.DUTCH_NETHERLANDS);
095            setLocalAlias("english (australian)",LocaleConstant.ENGLISH_AUSTRALIA);
096            setLocalAlias("english (australia)",LocaleConstant.ENGLISH_AUSTRALIA);
097            setLocalAlias("english (canadian)",LocaleConstant.ENGLISH_CANADA);
098            setLocalAlias("english (canada)",LocaleConstant.ENGLISH_CANADA);
099            setLocalAlias("english (new zealand)",LocaleConstant.ENGLISH_NEW_ZEALAND);
100            setLocalAlias("english (uk)",LocaleConstant.ENGLISH_UNITED_KINDOM);
101            setLocalAlias("english (united kingdom)",LocaleConstant.ENGLISH_UNITED_KINDOM);
102            setLocalAlias("english (gb)",LocaleConstant.ENGLISH_UNITED_KINDOM);
103            setLocalAlias("english (great britan)",LocaleConstant.ENGLISH_UNITED_KINDOM);
104            setLocalAlias("english (us)",LocaleConstant.ENGLISH_UNITED_STATES);
105            setLocalAlias("english (united states)",LocaleConstant.ENGLISH_UNITED_STATES);
106            setLocalAlias("english (united states of america)",LocaleConstant.ENGLISH_UNITED_STATES);
107            setLocalAlias("english (usa)",LocaleConstant.ENGLISH_UNITED_STATES);
108            setLocalAlias("french (belgium)",new Locale("fr","BE"));
109            setLocalAlias("french (belgian)",new Locale("fr","BE"));
110                setLocalAlias("french (canadian)",new Locale("fr","CA"));
111                setLocalAlias("french (canadia)",new Locale("fr","CA"));
112            setLocalAlias("french (standard)",new Locale("fr","FRA"));
113            setLocalAlias("french (swiss)",new Locale("fr","CH"));
114            setLocalAlias("german (austrian)",new Locale("de","AT"));
115            setLocalAlias("german (austria)",new Locale("de","AT"));
116            setLocalAlias("german (standard)",new Locale("de","DE"));
117            setLocalAlias("german (swiss)",new Locale("de","CH"));
118            setLocalAlias("italian (standard)",new Locale("it","IT"));
119            setLocalAlias("italian (swiss)",new Locale("it","CH"));
120            setLocalAlias("japanese",new Locale("ja","JP"));  
121            setLocalAlias("korean",Locale.KOREAN);
122            setLocalAlias("norwegian (bokmal)",new Locale("no","NO"));
123            setLocalAlias("norwegian (nynorsk)",new Locale("no","NO"));
124            setLocalAlias("portuguese (brazilian)",LocaleConstant.PORTUGUESE_BRASIL);
125            setLocalAlias("portuguese (brazil)",LocaleConstant.PORTUGUESE_BRASIL);
126                setLocalAlias("portuguese (standard)",LocaleConstant.PORTUGUESE_PORTUGAL);
127            setLocalAlias("rhaeto-romance (swiss)",new Locale("rm","CH"));
128            locales.put("rhaeto-romance (swiss)",new Locale("rm","CH"));
129            setLocalAlias("spanish (modern)",new Locale("es","ES"));
130            setLocalAlias("spanish (standard)",new Locale("es","ES"));
131            setLocalAlias("swedish",new Locale("sv","SE"));
132        }
133        
134        private LocaleFactory(){}
135        
136    private static void setLocalAlias(String name, Locale locale) {
137        if(!localeAlias.containsKey(name))localeAlias.put(name, locale);
138        }
139
140        /**
141     * @param strLocale
142     * @param defaultValue 
143     * @return return locale match to String
144     */
145    public static Locale getLocale(String strLocale, Locale defaultValue) {
146        try {
147            return getLocale(strLocale);
148        } catch (ExpressionException e) {
149            return defaultValue;
150        }
151    }
152        
153        
154        /**
155         * @param strLocale
156         * @return return locale match to String
157         * @throws ExpressionException
158         */
159        public static Locale getLocale(String strLocale) throws ExpressionException {
160                String strLocaleLC = strLocale.toLowerCase().trim();
161                Locale l=locales.get(strLocaleLC);
162                if(l!=null) return l;
163                
164                l=localeAlias.get(strLocaleLC);
165                if(l!=null) return l;
166
167                Matcher matcher = localePattern2.matcher(strLocaleLC);
168                if(matcher.find()) {
169                        int len=matcher.groupCount();
170                        if(len==2) {
171                                String lang=matcher.group(1).trim();
172                                String country=matcher.group(2).trim();
173                                Locale locale=new Locale(lang,country);
174                                
175                                try {
176                                        locale.getISO3Language();
177                                        setLocalAlias(strLocaleLC, locale);
178                                        return locale;
179                                }
180                                catch(Exception e) {}
181                        }
182                }
183                
184                matcher = localePattern3.matcher(strLocaleLC);
185                if(matcher.find()) {
186                        int len=matcher.groupCount();
187                        if(len==3) {
188                                String lang=matcher.group(1).trim();
189                                String country=matcher.group(2).trim();
190                                String variant=matcher.group(3).trim();
191                                Locale locale=new Locale(lang,country,variant);
192                                
193                                try {
194                                        locale.getISO3Language();
195                                        setLocalAlias(strLocaleLC, locale);
196                                        return locale;
197                                }
198                                catch(Exception e) {}
199                        }
200                }
201                        
202                
203                matcher=localePattern.matcher(strLocaleLC);
204                if(matcher.find()) {
205                        int len=matcher.groupCount();
206
207                        if(len==3) {
208                                
209                                String lang=matcher.group(1).trim();
210                                String country=matcher.group(3);
211                                if(country!=null)country=country.trim();
212                                 Object objLocale=null;
213                                 
214                                if(country!=null) objLocale=locales.get(lang.toLowerCase()+" ("+(country.toLowerCase())+")");
215                                else objLocale=locales.get(lang.toLowerCase());
216                                if(objLocale!=null)return (Locale)objLocale;
217                                
218                                Locale locale;
219                                if(country!=null)locale=new Locale(lang.toUpperCase(),country.toLowerCase());
220                                else locale=new Locale(lang);
221                                
222                                try {
223                                        locale.getISO3Language();
224                                }
225                                catch(Exception e) {
226                                        if(strLocale.indexOf('-')!=-1) return getLocale(strLocale.replace('-', '_'));
227                                        throw new ExpressionException("unsupported Locale ["+strLocale+"]","supported Locales are:"+getSupportedLocalesAsString());
228                                }
229                                setLocalAlias(strLocaleLC, locale);
230                                return locale;
231
232                        }
233                }
234                
235
236                throw new ExpressionException("can't cast value ("+strLocale+") to a Locale","supported Locales are:"+getSupportedLocalesAsString());
237        }
238        
239
240        private static String getSupportedLocalesAsString() {
241                // TODO chnge from ArryObject to string
242                String[] arr = locales.keySet().toArray(new String[locales.size()]);
243                Arrays.sort(arr);
244                return ListUtil.arrayToList(arr,",");
245                
246        }
247
248        /**
249         * @param locale
250         * @return cast a Locale to a String 
251         */
252        public static String toString(Locale locale) {
253                String lang=locale.getLanguage();
254                String country=locale.getCountry();
255                
256                synchronized(localeAlias){
257                        Iterator<Entry<String, Locale>> it = localeAlias.entrySet().iterator();
258                        Map.Entry<String, Locale> entry;
259                        while(it.hasNext()) {
260                                entry= it.next();
261                                //Object qkey=it.next();
262                                Locale curr=entry.getValue();
263                                if(lang.equals(curr.getLanguage()) && country.equals(curr.getCountry())) {
264                                        return entry.getKey().toString();
265                                }
266                        }
267                }
268                return locale.getDisplayName(Locale.ENGLISH);
269        }
270        
271    /**
272     * @return Returns the locales.
273     */
274    public static Map getLocales() {
275        return locales;
276    }
277    public static String getLocaleList() {
278        return list;
279    }
280}