1   /**
2    * Distribution License:
3    * BibleDesktop is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: Translations.java 1738 2008-01-15 02:56:55Z dmsmith $
21   */
22  package org.crosswire.common.util;
23  
24  import java.io.IOException;
25  import java.net.URI;
26  import java.net.URL;
27  import java.util.Locale;
28  import java.util.Map;
29  
30  import org.crosswire.common.config.ChoiceFactory;
31  
32  /**
33   * Translations provides a list of locales that BibleDesktop has been translated
34   * into.
35   * 
36   * @see gnu.lgpl.License for license details.<br>
37   *      The copyright to this program is held by it's authors.
38   * @author DM Smith [dmsmith555 at yahoo dot com]
39   */
40  public class Translations {
41      /**
42       * Singleton classes have private constructors.
43       */
44      private Translations() {
45          try {
46              loadSupportedTranslations();
47              PropertyMap props = ResourceUtil.getProperties(getClass());
48              translation = props.get(TRANSLATION_KEY);
49              if (translation == null || translation.length() == 0) {
50                  // check for a match against language and country
51                  // This pertains to zh_TW and zh_CN
52                  for (int i = 0; i < translations.length; i++) {
53                      Locale supportedLocale = new Locale(translations[i]);
54                      if (supportedLocale.getLanguage().equals(originalLocale.getLanguage()) && supportedLocale.getCountry().equals(originalLocale.getCountry())) {
55                          translation = translations[i];
56                          return;
57                      }
58                  }
59  
60                  // check for a match against just language
61                  for (int i = 0; i < translations.length; i++) {
62                      Locale supportedLocale = new Locale(translations[i]);
63                      if (supportedLocale.getLanguage().equals(originalLocale.getLanguage())) {
64                          translation = translations[i];
65                          return;
66                      }
67                  }
68  
69                  // if we don't have a matching locale then just use the default.
70                  translation = DEFAULT_TRANSLATION;
71              }
72          } catch (IOException e) {
73              translation = DEFAULT_TRANSLATION;
74          }
75      }
76  
77      /**
78       * All access to Translations is through this single instance.
79       * 
80       * @return the singleton instance
81       */
82      public static Translations instance() {
83          return instance;
84      }
85  
86      /**
87       * Gets a listing of all the translations that Bible Desktop supports.
88       * 
89       * @return an string array of translations in locale friendly names.
90       */
91      public PropertyMap getSupported() {
92          loadSupportedTranslations();
93  
94          // I18N(DMS) Collate these according to the current locale, putting the
95          // current locale's locale first.
96          PropertyMap names = new PropertyMap();
97  
98          for (int i = 0; i < translations.length; i++) {
99              names.put(translations[i], toString(translations[i]));
100         }
101 
102         return names;
103     }
104 
105     /**
106      * Get the locale for the current translation.
107      * 
108      * @return the translation's locale
109      */
110     public Locale getCurrentLocale() {
111         // If there is no particular translation, then return the default
112         // locale.
113         if (translation == null || DEFAULT_TRANSLATION.equals(translation)) {
114             return DEFAULT_LOCALE;
115         }
116 
117         // If the local consists of a language and a country then use both
118         if (translation.indexOf('_') != -1) {
119             String[] locale = StringUtil.split(translation, '_');
120             return new Locale(locale[0], locale[1]);
121         }
122 
123         // otherwise just use the country.
124         return new Locale(translation);
125     }
126 
127     /**
128      * Get the current translation as a human readable string.
129      * 
130      * @return the current translation
131      */
132     public String getCurrent() {
133         return toString(translation);
134     }
135 
136     /**
137      * Set the current translation, using human readable string.
138      * 
139      * @param newTranslation
140      *            the translation to use
141      */
142     public void setCurrent(String newTranslation) {
143         String found = DEFAULT_TRANSLATION;
144         String currentTranslation = "";
145         for (int i = 0; i < translations.length; i++) {
146             String trans = translations[i];
147             currentTranslation = toString(translation);
148 
149             if (trans.equals(newTranslation) || currentTranslation.equals(newTranslation)) {
150                 found = trans;
151                 break;
152             }
153         }
154 
155         try {
156             translation = found;
157             PropertyMap props = new PropertyMap();
158             if (!DEFAULT_TRANSLATION.equals(translation)) {
159                 props.put(TRANSLATION_KEY, translation);
160             }
161 
162             URI outputURI = CWProject.instance().getWritableURI(getClass().getName(), FileUtil.EXTENSION_PROPERTIES);
163             NetUtil.storeProperties(props, outputURI, "BibleDesktop UI Translation");
164         } catch (IOException ex) {
165             log.error("Failed to save BibleDesktop UI Translation", ex);
166         }
167     }
168 
169     /**
170      * Set the locale for the program to the one the user has selected. But
171      * don't set it to the default translation, so that the user's actual
172      * locale, is used for Bible book names.
173      * 
174      * This only makes sense after config has called setCurrentTranslation.
175      */
176     public void setLocale() {
177         Locale.setDefault(getCurrentLocale());
178     }
179 
180     /**
181      * Register this class with the common config engine.
182      */
183     public void register() {
184         ChoiceFactory.getDataMap().put(TRANSLATION_KEY, getSupportedTranslations());
185     }
186 
187     /**
188      * Get the current translation as a human readable string.
189      * 
190      * @return the current translation
191      */
192     public static String getCurrentTranslation() {
193         return Translations.instance().getCurrent();
194     }
195 
196     /**
197      * Set the current translation, using human readable string.
198      * 
199      * @param newTranslation
200      *            the translation to use
201      */
202     public static void setCurrentTranslation(String newTranslation) {
203         Translations.instance().setCurrent(newTranslation);
204     }
205 
206     /**
207      * Gets a listing of all the translations that Bible Desktop supports.
208      * 
209      * @return an string array of translations in locale friendly names.
210      */
211     public static Map<String, String> getSupportedTranslations() {
212         return Translations.instance().getSupported();
213     }
214 
215     /**
216      * Get a list of the supported translations
217      */
218     private void loadSupportedTranslations() {
219         if (translations == null) {
220             try {
221                 URL index = ResourceUtil.getResource(Translations.class, "translations.txt");
222                 translations = NetUtil.listByIndexFile(NetUtil.toURI(index));
223             } catch (IOException ex) {
224                 translations = new String[0];
225             }
226         }
227     }
228 
229     public String toString(String translationCode) {
230         StringBuilder currentTranslation = new StringBuilder(Languages.getLanguageName(translationCode));
231 
232         if (translationCode.indexOf('_') != -1) {
233             String[] locale = StringUtil.split(translationCode, '_');
234             currentTranslation.append(", ");
235             currentTranslation.append(Countries.getCountry(locale[1]));
236         }
237 
238         return currentTranslation.toString();
239     }
240 
241     /**
242      * The key used in config.xml
243      */
244     private static final String TRANSLATION_KEY = "translation-codes";
245 
246     /**
247      * The default translation, if the user has not chosen anything else.
248      */
249     public static final String DEFAULT_TRANSLATION = "en";
250 
251     /**
252      * The default Locale, it the user has not chosen anything else.
253      */
254     public static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
255 
256     /**
257      * The translation that BibleDesktop should use.
258      */
259     private String translation;
260 
261     /**
262      * List of available translations.
263      */
264     private String[] translations;
265 
266     /**
267      * The locale that the program starts with. This needs to precede
268      * "instance."
269      */
270     private static Locale originalLocale = Locale.getDefault();
271 
272     private static Translations instance = new Translations();
273 
274     /**
275      * The log stream
276      */
277     private static final Logger log = Logger.getLogger(Translations.class);
278 }
279