001    /**
002     * Jetrix TetriNET Server
003     * Copyright (C) 2001-2004,2010  Emmanuel Bourg
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License
007     * as published by the Free Software Foundation; either version 2
008     * of the License, or (at your option) any later version.
009     *
010     * This program 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
013     * GNU General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
018     */
019    
020    package net.jetrix;
021    
022    import java.util.*;
023    import java.text.*;
024    
025    /**
026     * Helper class to handle and retrieve localized strings.
027     *
028     * @author Emmanuel Bourg
029     * @version $Revision: 846 $, $Date: 2010-05-03 17:29:44 +0200 (lun., 03 mai 2010) $
030     */
031    public class Language
032    {
033        /** The names of the resource bundles imported. */
034        private Set<String> bundleNames = new HashSet<String>();
035    
036        private Map<Locale, MultiResourceBundle> bundles = new HashMap<Locale, MultiResourceBundle>();
037    
038        private static Language instance = new Language();
039    
040        /** The default resource bundle containing the server messages. */
041        private static final String DEFAULT_RESOURCE = "jetrix";
042    
043        private Language()
044        {
045            addResources(DEFAULT_RESOURCE);
046        }
047    
048        /**
049         * Return the unique instance of this class.
050         */
051        public static Language getInstance()
052        {
053            return instance;
054        }
055    
056        /**
057         * Register an extra set of localized messages.
058         * 
059         * @param name the base name of the resource bundle
060         * @since 0.3
061         */
062        public void addResources(String name)
063        {
064            bundleNames.add(name);
065        }
066    
067        /**
068         * Load and return the <tt>ResourceBundle</tt> for the specified locale.
069         * Bundles are cached in a local Map.
070         *
071         * @param locale the locale of the returned bundle if available
072         */
073        protected ResourceBundle load(Locale locale)
074        {
075            MultiResourceBundle bundle = new MultiResourceBundle(locale);
076            bundles.put(locale, bundle);
077            return bundle;
078        }
079    
080        /**
081         * Tell if the specified locale has a corresponding resource file available.
082         *
083         * @param locale the locale to test
084         *
085         * @return <tt>true</tt> if the locale is supported, <tt>false</tt> if not.
086         */
087        public static boolean isSupported(Locale locale)
088        {
089            MultiResourceBundle bundle = instance.bundles.get(locale);
090            if (bundle == null)
091            {
092                bundle = instance.new MultiResourceBundle(locale);
093            }
094            return bundle.isSupported();
095        }
096    
097        /**
098         * Return the list of languages supported by the server.
099         */
100        public static Collection<Locale> getLocales()
101        {
102            Collection<Locale> locales = new ArrayList<Locale>();
103    
104            for (String language : Locale.getISOLanguages())
105            {
106                Locale locale = new Locale(language);
107                if (isSupported(locale))
108                {
109                    locales.add(locale);
110                }
111            }
112    
113            return locales;
114        }
115    
116        /**
117         * Return the specified localized text for a given locale.
118         *
119         * @param key the text key in the resource bundle
120         * @param locale the locale of the message
121         */
122        public static String getText(String key, Locale locale)
123        {
124            try
125            {
126                ResourceBundle bundle = instance.bundles.get(locale);
127                if (bundle == null)
128                {
129                    bundle = instance.load(locale);
130                }
131                
132                return bundle.getString(key);
133            }
134            catch (Exception e)
135            {
136                return "[" + locale + ":" + key + "]";
137            }
138        }
139    
140        /**
141         * Return the specified localized text for a given locale and replace the
142         * parameters with an array of arguments.
143         *
144         * @since 0.2
145         *
146         * @param key the text key in the resource bundle
147         * @param locale the locale of the message
148         * @param arguments the array of arguments
149         */
150        public static String getText(String key, Locale locale, Object ... arguments)
151        {
152            // localize the arguments
153            Object[] arguments2 = new Object[arguments.length];
154            for (int i = 0; i < arguments.length; i++)
155            {            
156                arguments2[i] = getLocalizedArgument(locale, arguments[i]);
157            }
158    
159            return MessageFormat.format(getText(key, locale), arguments2);
160        }
161    
162        /**
163         * Transforms a localized argument into its actual value. Localized
164         * arguments start with the "key:" prefix and refers to another message
165         * in the resource bundle.
166         *
167         * @since 0.3
168         * 
169         * @param locale   the target locale
170         * @param argument the argument to transform
171         */
172        private static Object getLocalizedArgument(Locale locale, Object argument)
173        {
174            if (argument instanceof String && ((String) argument).startsWith("key:"))
175            {
176                return getText(((String) argument).substring(4), locale);
177            }
178            else
179            {
180                return argument;
181            }
182        }
183    
184        /**
185         * A resource bundle merging several property based resource bundles.
186         * 
187         * @since 0.3
188         */
189        private class MultiResourceBundle extends ResourceBundle
190        {
191            private Locale locale;
192    
193            private MultiResourceBundle(Locale locale)
194            {
195                this.locale = locale;
196            }
197    
198            private PropertyResourceBundle getPropertyResourceBundle(String name) {
199                try
200                {
201                    return (PropertyResourceBundle) PropertyResourceBundle.getBundle(name, locale);
202                }
203                catch (MissingResourceException e)
204                {
205                    return null;
206                }
207            }
208    
209            protected Object handleGetObject(String key)
210            {
211                for (String name : bundleNames)
212                {
213                    PropertyResourceBundle bundle = getPropertyResourceBundle(name);
214                    if (bundle != null)
215                    {
216                        Object value = bundle.handleGetObject(key);
217                        if (value != null)
218                        {
219                            return value;
220                        }
221                    }
222                }
223                
224                return null;
225            }
226    
227            public Enumeration<String> getKeys()
228            {
229                return null;
230            }
231    
232            /**
233             * Checks if at least one of the underlying resource bundles supports
234             * the locale assigned to this bundle.
235             */
236            public boolean isSupported()
237            {
238                for (String name : bundleNames)
239                {
240                    PropertyResourceBundle bundle = getPropertyResourceBundle(name);
241                    if (bundle != null && bundle.getLocale().equals(locale))
242                    {
243                        return true;
244                    }
245                }
246                
247                return false;
248            }
249        }
250    }