1 package org.codehaus.plexus.i18n;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import javax.inject.Named;
20 import javax.inject.Singleton;
21
22 import java.text.MessageFormat;
23 import java.util.HashMap;
24 import java.util.Locale;
25 import java.util.Map;
26 import java.util.MissingResourceException;
27 import java.util.ResourceBundle;
28
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32
33
34
35 @Named
36 @Singleton
37 public class DefaultI18N implements I18N {
38
39 private final Logger log = LoggerFactory.getLogger(DefaultI18N.class);
40 private static final Object[] NO_ARGS = new Object[0];
41
42 private Map<String, Map<Locale, ResourceBundle>> bundles;
43
44 private String[] bundleNames;
45
46 private String defaultBundleName;
47
48 private boolean devMode;
49
50 public DefaultI18N() {
51 initialize();
52 }
53
54 public DefaultI18N(String[] bundleNames) {
55 this.bundleNames = bundleNames != null ? bundleNames.clone() : new String[0];
56 initialize();
57 }
58
59
60
61
62 public String getDefaultLanguage() {
63 return Locale.getDefault().getLanguage();
64 }
65
66 public String getDefaultCountry() {
67 return Locale.getDefault().getCountry();
68 }
69
70 public String getDefaultBundleName() {
71 return defaultBundleName;
72 }
73
74 public String[] getBundleNames() {
75 return bundleNames.clone();
76 }
77
78 public ResourceBundle getBundle() {
79 return getBundle(getDefaultBundleName(), (Locale) null);
80 }
81
82 public ResourceBundle getBundle(String bundleName) {
83 return getBundle(bundleName, (Locale) null);
84 }
85
86
87
88
89
90
91
92
93
94
95 public ResourceBundle getBundle(String bundleName, String languageHeader) {
96 return getBundle(bundleName, getLocale(languageHeader));
97 }
98
99
100
101
102
103
104
105
106
107
108
109 public ResourceBundle getBundle(String bundleName, Locale locale) {
110
111 bundleName = (bundleName == null ? getDefaultBundleName() : bundleName.trim());
112
113
114
115
116
117 if (devMode) {
118 ResourceBundle.clearCache();
119 }
120
121 if (locale == null) {
122 locale = getLocale(null);
123 }
124
125
126 ResourceBundle rb;
127
128 Map<Locale, ResourceBundle> bundlesByLocale = bundles.get(bundleName);
129
130 if (bundlesByLocale != null) {
131
132
133 rb = bundlesByLocale.get(locale);
134 if (rb == null) {
135
136 rb = cacheBundle(bundleName, locale);
137 }
138 } else {
139 rb = cacheBundle(bundleName, locale);
140 }
141
142 return rb;
143 }
144
145
146
147
148 public Locale getLocale(String header) {
149 if (header != null && !header.isEmpty()) {
150 I18NTokenizer tok = new I18NTokenizer(header);
151 if (tok.hasNext()) {
152 return tok.next();
153 }
154 }
155
156
157 return Locale.getDefault();
158 }
159
160 public String getString(String key) {
161 return getString(key, null);
162 }
163
164 public String getString(String key, Locale locale) {
165 return getString(getDefaultBundleName(), locale, key);
166 }
167
168
169
170
171
172 public String getString(String bundleName, Locale locale, String key) {
173 String value;
174 if (locale == null) {
175 locale = getLocale(null);
176 }
177
178
179 ResourceBundle rb = getBundle(bundleName, locale);
180
181 value = getStringOrNull(rb, key);
182
183
184 if (value == null) {
185 for (String name : bundleNames) {
186 if (!name.equals(bundleName)) {
187 rb = getBundle(name, locale);
188
189 value = getStringOrNull(rb, key);
190
191 if (value != null) {
192 locale = rb.getLocale();
193
194 break;
195 }
196 }
197 }
198 }
199
200 if (value == null) {
201 log.debug("Noticed missing resource: bundleName={}, locale={}, key={}", bundleName, locale, key);
202
203 value = key;
204 }
205
206 return value;
207 }
208
209 public String format(String key, Object arg1) {
210 return format(defaultBundleName, Locale.getDefault(), key, new Object[] {arg1});
211 }
212
213 public String format(String key, Object arg1, Object arg2) {
214 return format(defaultBundleName, Locale.getDefault(), key, new Object[] {arg1, arg2});
215 }
216
217
218
219
220 public String format(String bundleName, Locale locale, String key, Object arg1) {
221 return format(bundleName, locale, key, new Object[] {arg1});
222 }
223
224
225
226
227 public String format(String bundleName, Locale locale, String key, Object arg1, Object arg2) {
228 return format(bundleName, locale, key, new Object[] {arg1, arg2});
229 }
230
231
232
233
234
235
236
237
238
239
240 public String format(String bundleName, Locale locale, String key, Object[] args) {
241 if (locale == null) {
242
243
244 locale = getLocale(null);
245 }
246
247 String value = getString(bundleName, locale, key);
248 if (args == null) {
249 args = NO_ARGS;
250 }
251 return new MessageFormat(value, locale).format(args);
252 }
253
254
255
256
257 public void initialize() {
258 bundles = new HashMap<>();
259 initializeBundleNames();
260 if ("true".equals(System.getProperty("PLEXUS_DEV_MODE"))) {
261 devMode = true;
262 }
263 }
264
265
266
267
268
269 protected void initializeBundleNames() {
270 if (defaultBundleName != null && !defaultBundleName.isEmpty()) {
271
272 if (bundleNames == null || bundleNames.length <= 0) {
273 bundleNames = new String[] {defaultBundleName};
274 } else {
275
276 String[] array = new String[bundleNames.length + 1];
277 array[0] = defaultBundleName;
278 System.arraycopy(bundleNames, 0, array, 1, bundleNames.length);
279 bundleNames = array;
280 }
281 }
282 if (bundleNames == null) {
283 bundleNames = new String[0];
284 }
285 }
286
287
288
289
290
291
292
293
294 private synchronized ResourceBundle cacheBundle(String bundleName, Locale locale) throws MissingResourceException {
295 Map<Locale, ResourceBundle> bundlesByLocale = bundles.get(bundleName);
296
297 ResourceBundle rb = (bundlesByLocale == null ? null : bundlesByLocale.get(locale));
298 if (rb == null) {
299 bundlesByLocale = (bundlesByLocale == null ? new HashMap<>(3) : new HashMap<>(bundlesByLocale));
300 try {
301 rb = ResourceBundle.getBundle(
302 bundleName,
303 locale,
304 ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT));
305 } catch (MissingResourceException e) {
306 rb = findBundleByLocale(bundleName, locale, bundlesByLocale);
307 if (rb == null) {
308 throw (MissingResourceException) e.fillInStackTrace();
309 }
310 }
311
312 if (rb != null) {
313
314 bundlesByLocale.put(rb.getLocale(), rb);
315 Map<String, Map<Locale, ResourceBundle>> bundlesByName = new HashMap<>(bundles);
316 bundlesByName.put(bundleName, bundlesByLocale);
317 this.bundles = bundlesByName;
318 }
319 }
320
321 return rb;
322 }
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339 private ResourceBundle findBundleByLocale(
340 String bundleName, Locale locale, Map<Locale, ResourceBundle> bundlesByLocale) {
341 ResourceBundle rb = null;
342
343 if (locale.getCountry() != null
344 && !locale.getCountry().isEmpty()
345 && Locale.getDefault().getLanguage().equals(locale.getLanguage())) {
346 Locale withDefaultCountry =
347 new Locale(locale.getLanguage(), Locale.getDefault().getCountry());
348 rb = bundlesByLocale.get(withDefaultCountry);
349 if (rb == null) {
350 rb = getBundleIgnoreException(bundleName, withDefaultCountry);
351 }
352 } else if (locale.getLanguage() != null
353 && !locale.getLanguage().isEmpty()
354 && Locale.getDefault().getCountry().equals(locale.getCountry())) {
355 Locale withDefaultLanguage = new Locale(Locale.getDefault().getLanguage(), locale.getCountry());
356 rb = bundlesByLocale.get(withDefaultLanguage);
357 if (rb == null) {
358 rb = getBundleIgnoreException(bundleName, withDefaultLanguage);
359 }
360 }
361
362 if (rb == null && !Locale.getDefault().equals(locale)) {
363 rb = getBundleIgnoreException(bundleName, Locale.getDefault());
364 }
365
366 return rb;
367 }
368
369
370
371
372
373
374
375 private ResourceBundle getBundleIgnoreException(String bundleName, Locale locale) {
376 try {
377 return ResourceBundle.getBundle(
378 bundleName,
379 locale,
380 ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT));
381 } catch (MissingResourceException ignored) {
382 return null;
383 }
384 }
385
386
387
388
389
390
391 protected final String getStringOrNull(ResourceBundle rb, String key) {
392 if (rb != null) {
393 try {
394 return rb.getString(key);
395 } catch (MissingResourceException ignored) {
396
397 }
398 }
399 return null;
400 }
401 }