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