Commit | Line | Data |
---|---|---|
ec1f3444 NR |
1 | package be.nikiroo.utils.resources; |
2 | ||
3 | import java.io.File; | |
4 | import java.io.IOException; | |
5 | import java.io.Writer; | |
6 | import java.util.LinkedList; | |
7 | import java.util.List; | |
8 | import java.util.Locale; | |
9 | import java.util.regex.Pattern; | |
10 | ||
11 | import be.nikiroo.utils.resources.Bundles; | |
12 | ||
13 | /** | |
14 | * This class manages a translation-dedicated Bundle. | |
15 | * <p> | |
16 | * Two special cases are handled for the used enum: | |
17 | * <ul> | |
18 | * <li>NULL will always will return an empty {@link String}</li> | |
19 | * <li>DUMMY will return "[DUMMY]" (maybe with a suffix and/or "NOUTF")</li> | |
20 | * </ul> | |
21 | * | |
22 | * @author niki | |
23 | */ | |
24 | public class TransBundle<E extends Enum<E>> extends Bundle<E> { | |
25 | private boolean utf = true; | |
26 | private Locale locale; | |
27 | private boolean defaultLocale = false; | |
28 | ||
29 | /** | |
30 | * Create a translation service with the default language. | |
31 | * | |
32 | * @param type | |
33 | * a runtime instance of the class of E | |
34 | * @param name | |
35 | * the name of the {@link Bundles} | |
36 | */ | |
37 | public TransBundle(Class<E> type, Enum<?> name) { | |
38 | super(type, name); | |
39 | setLanguage(null); | |
40 | } | |
41 | ||
42 | /** | |
43 | * Create a translation service for the given language (will fall back to | |
44 | * the default one i not found). | |
45 | * | |
46 | * @param type | |
47 | * a runtime instance of the class of E | |
48 | * @param name | |
49 | * the name of the {@link Bundles} | |
50 | * @param language | |
51 | * the language to use | |
52 | */ | |
53 | public TransBundle(Class<E> type, Enum<?> name, String language) { | |
54 | super(type, name); | |
55 | setLanguage(language); | |
56 | } | |
57 | ||
58 | /** | |
59 | * Translate the given id into user text. | |
60 | * | |
61 | * @param stringId | |
62 | * the ID to translate | |
63 | * @param values | |
64 | * the values to insert instead of the place holders in the | |
65 | * translation | |
66 | * | |
67 | * @return the translated text with the given value where required or NULL | |
68 | * if not found (not present in the resource file) | |
69 | */ | |
70 | public String getString(E stringId, Object... values) { | |
71 | return getStringX(stringId, "", values); | |
72 | } | |
73 | ||
74 | /** | |
75 | * Translate the given id into user text. | |
76 | * | |
77 | * @param stringId | |
78 | * the ID to translate | |
79 | * @param values | |
80 | * the values to insert instead of the place holders in the | |
81 | * translation | |
82 | * | |
83 | * @return the translated text with the given value where required or NULL | |
84 | * if not found (not present in the resource file) | |
85 | */ | |
86 | public String getStringNOUTF(E stringId, Object... values) { | |
87 | return getStringX(stringId, "NOUTF", values); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Translate the given id suffixed with the runtime value "_suffix" (that | |
92 | * is, "_" and suffix) into user text. | |
93 | * | |
94 | * @param stringId | |
95 | * the ID to translate | |
96 | * @param values | |
97 | * the values to insert instead of the place holders in the | |
98 | * translation | |
99 | * @param suffix | |
100 | * the runtime suffix | |
101 | * | |
102 | * @return the translated text with the given value where required or NULL | |
103 | * if not found (not present in the resource file) | |
104 | */ | |
105 | public String getStringX(E stringId, String suffix, Object... values) { | |
106 | E id = stringId; | |
107 | String result = ""; | |
108 | ||
109 | String key = id.name() | |
110 | + ((suffix == null || suffix.isEmpty()) ? "" : "_" | |
111 | + suffix.toUpperCase()); | |
112 | ||
113 | if (!isUnicode()) { | |
114 | if (containsKey(key + "_NOUTF")) { | |
115 | key += "_NOUTF"; | |
116 | } | |
117 | } | |
118 | ||
119 | if ("NULL".equals(id.name().toUpperCase())) { | |
120 | result = ""; | |
121 | } else if ("DUMMY".equals(id.name().toUpperCase())) { | |
122 | result = "[" + key.toLowerCase() + "]"; | |
123 | } else if (containsKey(key)) { | |
124 | result = getString(key); | |
125 | } else { | |
126 | result = null; | |
127 | } | |
128 | ||
129 | if (values != null && values.length > 0 && result != null) | |
130 | return String.format(locale, result, values); | |
131 | else | |
132 | return result; | |
133 | } | |
134 | ||
135 | /** | |
136 | * Check if unicode characters should be used. | |
137 | * | |
138 | * @return TRUE to allow unicode | |
139 | */ | |
140 | public boolean isUnicode() { | |
141 | return utf; | |
142 | } | |
143 | ||
144 | /** | |
145 | * Allow or disallow unicode characters in the program. | |
146 | * | |
147 | * @param utf | |
148 | * TRUE to allow unuciode, FALSE to only allow ASCII characters | |
149 | */ | |
150 | public void setUnicode(boolean utf) { | |
151 | this.utf = utf; | |
152 | } | |
153 | ||
154 | /** | |
155 | * Return all the languages known by the program. | |
156 | * | |
157 | * | |
158 | * @return the known language codes | |
159 | */ | |
160 | public List<String> getKnownLanguages() { | |
161 | return getKnownLanguages(name); | |
162 | } | |
163 | ||
164 | /** | |
165 | * Initialise the translation mappings for the given language. | |
166 | * | |
167 | * @param language | |
168 | * the language to initialise, in the form "en-GB" or "fr" for | |
169 | * instance | |
170 | */ | |
171 | private void setLanguage(String language) { | |
172 | defaultLocale = (language == null || language.length() == 0); | |
173 | locale = getLocaleFor(language); | |
174 | setBundle(name, locale); | |
175 | } | |
176 | ||
80383c14 NR |
177 | @Override |
178 | public void reload() { | |
179 | setBundle(name, locale); | |
180 | } | |
181 | ||
ec1f3444 NR |
182 | @Override |
183 | public String getString(E id) { | |
184 | return getString(id, (Object[]) null); | |
185 | } | |
186 | ||
187 | /** | |
188 | * Create/update the .properties files for each supported language and for | |
189 | * the default language. | |
190 | * <p> | |
191 | * Note: this method is <b>NOT</b> thread-safe. | |
192 | * | |
193 | * @param path | |
194 | * the path where the .properties files are | |
195 | * | |
196 | * @throws IOException | |
197 | * in case of IO errors | |
198 | */ | |
199 | @Override | |
200 | public void updateFile(String path) throws IOException { | |
201 | String prev = locale.getLanguage(); | |
202 | ||
203 | setLanguage(null); // default locale | |
204 | super.updateFile(path); | |
205 | ||
206 | for (String lang : getKnownLanguages()) { | |
207 | setLanguage(lang); | |
208 | super.updateFile(path); | |
209 | } | |
210 | ||
211 | setLanguage(prev); | |
212 | } | |
213 | ||
214 | @Override | |
215 | protected File getUpdateFile(String path) { | |
216 | String code = locale.toString(); | |
217 | File file = null; | |
218 | if (!defaultLocale && code.length() > 0) { | |
219 | file = new File(path, name.name() + "_" + code + ".properties"); | |
220 | } else { | |
221 | // Default properties file: | |
222 | file = new File(path, name.name() + ".properties"); | |
223 | } | |
224 | ||
225 | return file; | |
226 | } | |
227 | ||
228 | @Override | |
229 | protected void writeHeader(Writer writer) throws IOException { | |
230 | String code = locale.toString(); | |
231 | String name = locale.getDisplayCountry(locale); | |
232 | ||
233 | if (name.length() == 0) { | |
234 | name = locale.getDisplayLanguage(locale); | |
235 | } | |
236 | ||
237 | if (name.length() == 0) { | |
238 | name = "default"; | |
239 | } | |
240 | ||
241 | if (code.length() > 0) { | |
242 | name = name + " (" + code + ")"; | |
243 | } | |
244 | ||
245 | name = (name + " " + getBundleDisplayName()).trim(); | |
246 | ||
247 | writer.write("# " + name + " translation file (UTF-8)\n"); | |
248 | writer.write("# \n"); | |
249 | writer.write("# Note that any key can be doubled with a _NOUTF suffix\n"); | |
250 | writer.write("# to use when the NOUTF env variable is set to 1\n"); | |
251 | writer.write("# \n"); | |
252 | writer.write("# Also, the comments always refer to the key below them.\n"); | |
253 | writer.write("# \n"); | |
254 | } | |
255 | ||
256 | @Override | |
257 | protected void writeValue(Writer writer, E id) throws IOException { | |
258 | super.writeValue(writer, id); | |
259 | ||
260 | String name = id.name() + "_NOUTF"; | |
261 | if (containsKey(name)) { | |
262 | String value = getString(name); | |
263 | writeValue(writer, name, value); | |
264 | } | |
265 | } | |
266 | ||
267 | /** | |
268 | * Return the {@link Locale} representing the given language. | |
269 | * | |
270 | * @param language | |
271 | * the language to initialise, in the form "en-GB" or "fr" for | |
272 | * instance | |
273 | * | |
274 | * @return the corresponding {@link Locale} or the default {@link Locale} if | |
275 | * it is not known | |
276 | */ | |
277 | static private Locale getLocaleFor(String language) { | |
278 | Locale locale; | |
279 | ||
280 | if (language == null) { | |
281 | locale = Locale.getDefault(); | |
282 | } else { | |
283 | language = language.replaceAll("_", "-"); | |
284 | String lang = language; | |
285 | String country = null; | |
286 | if (language.contains("-")) { | |
287 | lang = language.split("-")[0]; | |
288 | country = language.split("-")[1]; | |
289 | } | |
290 | ||
291 | if (country != null) | |
292 | locale = new Locale(lang, country); | |
293 | else | |
294 | locale = new Locale(lang); | |
295 | } | |
296 | ||
297 | return locale; | |
298 | } | |
299 | ||
300 | /** | |
301 | * Return all the languages known by the program. | |
302 | * | |
303 | * @param name | |
304 | * the enumeration on which we translate | |
305 | * | |
306 | * @return the known language codes | |
307 | */ | |
308 | static protected List<String> getKnownLanguages(Enum<?> name) { | |
309 | List<String> resources = new LinkedList<String>(); | |
310 | ||
311 | String regex = ".*" + name.name() + "[_a-zA-Za]*\\.properties$"; | |
312 | ||
313 | for (String res : TransBundle_ResourceList.getResources(Pattern | |
314 | .compile(regex))) { | |
315 | String resource = res; | |
316 | int index = resource.lastIndexOf('/'); | |
317 | if (index >= 0 && index < (resource.length() - 1)) | |
318 | resource = resource.substring(index + 1); | |
319 | if (resource.startsWith(name.name())) { | |
320 | resource = resource.substring(0, resource.length() | |
321 | - ".properties".length()); | |
322 | resource = resource.substring(name.name().length()); | |
323 | if (resource.startsWith("_")) { | |
324 | resource = resource.substring(1); | |
325 | resources.add(resource); | |
326 | } | |
327 | } | |
328 | } | |
329 | ||
330 | return resources; | |
331 | } | |
332 | } |