1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.portal.service.i18n;
35
36 import fr.paris.lutece.portal.service.util.AppException;
37 import fr.paris.lutece.portal.service.util.AppLogService;
38 import fr.paris.lutece.portal.service.util.AppPathService;
39 import fr.paris.lutece.portal.service.util.AppPropertiesService;
40 import fr.paris.lutece.util.ReferenceList;
41
42 import java.io.File;
43
44 import java.net.MalformedURLException;
45 import java.net.URL;
46 import java.net.URLClassLoader;
47
48 import java.text.DateFormat;
49 import java.text.MessageFormat;
50
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.Collections;
54 import java.util.Date;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Locale;
58 import java.util.Map;
59 import java.util.MissingResourceException;
60 import java.util.ResourceBundle;
61 import java.util.StringTokenizer;
62 import java.util.regex.Matcher;
63 import java.util.regex.Pattern;
64
65
66
67
68
69
70 public final class I18nService
71 {
72 private static final String FORMAT_PACKAGE_PORTAL_RESOURCES_LOCATION = "fr.paris.lutece.portal.resources.{0}_messages";
73 private static final String FORMAT_PACKAGE_PLUGIN_RESOURCES_LOCATION = "fr.paris.lutece.plugins.{0}.resources.{0}_messages";
74 private static final String FORMAT_PACKAGE_MODULE_RESOURCES_LOCATION = "fr.paris.lutece.plugins.{0}.modules.{1}.resources.{1}_messages";
75 private static final Pattern PATTERN_LOCALIZED_KEY = Pattern.compile( "#i18n\\{(.*?)\\}" );
76 private static final String PROPERTY_AVAILABLES_LOCALES = "lutece.i18n.availableLocales";
77 private static final Locale LOCALE_DEFAULT = new Locale( "", "", "" );
78 private static final String PROPERTY_DEFAULT_LOCALE = "lutece.i18n.defaultLocale";
79 private static final String PROPERTY_FORMAT_DATE_SHORT_LIST = "lutece.format.date.short";
80 private static Map<String, String> _pluginBundleNames = Collections.synchronizedMap( new HashMap<String, String>( ) );
81 private static Map<String, String> _moduleBundleNames = Collections.synchronizedMap( new HashMap<String, String>( ) );
82 private static Map<String, String> _portalBundleNames = Collections.synchronizedMap( new HashMap<String, String>( ) );
83 private static final String PROPERTY_PATH_OVERRIDE = "path.i18n.override";
84 private static final ClassLoader _overrideLoader;
85 private static final Map<String, ResourceBundle> _resourceBundleCache = Collections.synchronizedMap( new HashMap<String, ResourceBundle>( ) );
86
87 static
88 {
89 File overridePath = null;
90
91 try
92 {
93 overridePath = new File( AppPathService.getPath( PROPERTY_PATH_OVERRIDE ) );
94 }
95 catch ( AppException e )
96 {
97
98 AppLogService.error( "property " + PROPERTY_PATH_OVERRIDE +
99 " is undefined. Message overriding will be disabled." );
100 }
101
102 URL[] overrideURL = null;
103
104 if ( overridePath != null )
105 {
106 try
107 {
108 overrideURL = new URL[] { overridePath.toURI( ).toURL( ) };
109 }
110 catch ( MalformedURLException e )
111 {
112 AppLogService.error( "Error initializing message overriding: " + e.getMessage( ), e );
113 }
114 }
115
116 if ( overrideURL != null )
117 {
118 _overrideLoader = new URLClassLoader( overrideURL, null );
119 }
120 else
121 {
122 _overrideLoader = null;
123 }
124 }
125
126
127
128
129 private I18nService( )
130 {
131 }
132
133
134
135
136
137
138
139
140
141
142 public static String localize( String strSource, Locale locale )
143 {
144 String result = strSource;
145
146 if ( strSource != null )
147 {
148 Matcher matcher = PATTERN_LOCALIZED_KEY.matcher( strSource );
149
150 if ( matcher.find( ) )
151 {
152 StringBuffer sb = new StringBuffer( );
153
154 do
155 {
156 matcher.appendReplacement( sb, getLocalizedString( matcher.group( 1 ), locale ) );
157 }
158 while ( matcher.find( ) );
159
160 matcher.appendTail( sb );
161 result = sb.toString( );
162 }
163 }
164
165 return result;
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 public static String getLocalizedString( String strKey, Locale theLocale )
183 {
184 Locale locale = theLocale;
185 String strReturn = "";
186
187 try
188 {
189 int nPos = strKey.indexOf( '.' );
190
191 if ( nPos != -1 )
192 {
193 String strBundleKey = strKey.substring( 0, nPos );
194 String strStringKey = strKey.substring( nPos + 1 );
195
196 String strBundle = FORMAT_PACKAGE_PORTAL_RESOURCES_LOCATION;
197
198 if ( !strBundleKey.equals( "portal" ) )
199 {
200 if ( strBundleKey.equals( "module" ) )
201 {
202
203 nPos = strStringKey.indexOf( '.' );
204
205 String strPlugin = strStringKey.substring( 0, nPos );
206 strStringKey = strStringKey.substring( nPos + 1 );
207 nPos = strStringKey.indexOf( '.' );
208
209 String strModule = strStringKey.substring( 0, nPos );
210 strStringKey = strStringKey.substring( nPos + 1 );
211
212 strBundle = getModuleBundleName( strPlugin, strModule );
213 }
214 else
215 {
216
217 strBundle = getPluginBundleName( strBundleKey );
218 }
219 }
220 else
221 {
222 nPos = strStringKey.indexOf( '.' );
223
224 String strElement = strStringKey.substring( 0, nPos );
225 strStringKey = strStringKey.substring( nPos + 1 );
226
227 strBundle = getPortalBundleName( strElement );
228 }
229
230
231
232 if ( locale.getLanguage( ).equals( Locale.ENGLISH.getLanguage( ) ) )
233 {
234 locale = LOCALE_DEFAULT;
235 }
236
237 ResourceBundle rbLabels = getResourceBundle( locale, strBundle );
238 strReturn = rbLabels.getString( strStringKey );
239 }
240 }
241 catch ( Exception e )
242 {
243 String strErrorMessage = "Error localizing key : '" + strKey + "' - " + e.getMessage( );
244
245 if ( e.getCause( ) != null )
246 {
247 strErrorMessage += ( " - cause : " + e.getCause( ).getMessage( ) );
248 }
249
250 AppLogService.error( strErrorMessage );
251 }
252
253 return strReturn;
254 }
255
256
257
258
259
260
261 private static String getPluginBundleName( String strBundleKey )
262 {
263 String strBundle = _pluginBundleNames.get( strBundleKey );
264
265 if ( strBundle == null )
266 {
267 Object[] params = { strBundleKey };
268 MessageFormat format = new MessageFormat( FORMAT_PACKAGE_PLUGIN_RESOURCES_LOCATION );
269 strBundle = format.format( params );
270 _pluginBundleNames.put( strBundleKey, strBundle );
271 }
272
273 return strBundle;
274 }
275
276
277
278
279
280
281
282 private static String getModuleBundleName( String strPlugin, String strModule )
283 {
284 String key = strPlugin + strModule;
285 String strBundle = _moduleBundleNames.get( key );
286
287 if ( strBundle == null )
288 {
289 Object[] params = { strPlugin, strModule };
290 MessageFormat format = new MessageFormat( FORMAT_PACKAGE_MODULE_RESOURCES_LOCATION );
291 strBundle = format.format( params );
292 _moduleBundleNames.put( key, strBundle );
293 }
294
295 return strBundle;
296 }
297
298
299
300
301
302
303 private static String getPortalBundleName( String strElement )
304 {
305 String strBundle = _portalBundleNames.get( strElement );
306
307 if ( strBundle == null )
308 {
309 Object[] params = { strElement };
310 MessageFormat format = new MessageFormat( FORMAT_PACKAGE_PORTAL_RESOURCES_LOCATION );
311 strBundle = format.format( params );
312 _portalBundleNames.put( strElement, strBundle );
313 }
314
315 return strBundle;
316 }
317
318
319
320
321
322
323
324
325
326 public static String getLocalizedString( String strKey, Object[] arguments, Locale locale )
327 {
328 String strMessagePattern = getLocalizedString( strKey, locale );
329
330 return MessageFormat.format( strMessagePattern, arguments );
331 }
332
333
334
335
336
337
338
339
340 public static String getLocalizedDate( Date date, Locale locale, int nDateFormat )
341 {
342 DateFormat dateFormatter = DateFormat.getDateInstance( nDateFormat, locale );
343 String strDate = dateFormatter.format( date );
344
345 return strDate;
346 }
347
348
349
350
351
352
353
354
355
356 public static String getLocalizedDateTime( Date date, Locale locale, int nDateFormat, int nTimeFormat )
357 {
358 DateFormat dateFormatter = DateFormat.getDateTimeInstance( nDateFormat, nTimeFormat, locale );
359 String strDate = dateFormatter.format( date );
360
361 return strDate;
362 }
363
364
365
366
367
368 public static List<Locale> getAdminAvailableLocales( )
369 {
370 String strAvailableLocales = AppPropertiesService.getProperty( PROPERTY_AVAILABLES_LOCALES );
371 StringTokenizer strTokens = new StringTokenizer( strAvailableLocales, "," );
372 List<Locale> list = new ArrayList<Locale>( );
373
374 while ( strTokens.hasMoreTokens( ) )
375 {
376 String strLanguage = strTokens.nextToken( );
377 Locale locale = new Locale( strLanguage );
378 list.add( locale );
379 }
380
381 return list;
382 }
383
384
385
386
387
388 public static Locale getDefaultLocale( )
389 {
390 String strDefaultLocale = AppPropertiesService.getProperty( PROPERTY_DEFAULT_LOCALE );
391
392 return new Locale( strDefaultLocale );
393 }
394
395
396
397
398
399
400 public static String getDateFormatShortPattern( Locale locale )
401 {
402 String strAvailableLocales = AppPropertiesService.getProperty( PROPERTY_FORMAT_DATE_SHORT_LIST );
403
404 if ( ( locale != null ) && ( strAvailableLocales != null ) && !strAvailableLocales.equals( "" ) )
405 {
406 StringTokenizer strTokens = new StringTokenizer( strAvailableLocales, "," );
407 String strToken = null;
408
409 for ( Locale adminLocale : getAdminAvailableLocales( ) )
410 {
411 if ( ( strTokens != null ) && strTokens.hasMoreTokens( ) )
412 {
413 strToken = strTokens.nextToken( );
414 }
415
416 if ( adminLocale.getLanguage( ).equals( locale.getLanguage( ) ) )
417 {
418 return strToken;
419 }
420 }
421 }
422
423 return null;
424 }
425
426
427
428
429
430
431 public static ReferenceList getAdminLocales( Locale locale )
432 {
433 ReferenceList list = new ReferenceList( );
434
435 for ( Locale l : getAdminAvailableLocales( ) )
436 {
437 list.addItem( l.getLanguage( ), l.getDisplayLanguage( l ) );
438 }
439
440 return list;
441 }
442
443
444
445
446
447
448
449 public static Collection localizeCollection( Collection<?extends Localizable> collection, Locale locale )
450 {
451 for ( Localizable object : collection )
452 {
453 object.setLocale( locale );
454 }
455
456 return collection;
457 }
458
459
460
461
462
463
464
465 public static List localizeCollection( List<?extends Localizable> list, Locale locale )
466 {
467 for ( Localizable object : list )
468 {
469 object.setLocale( locale );
470 }
471
472 return list;
473 }
474
475
476
477
478
479
480
481 private static ResourceBundle getResourceBundle( Locale locale, String strBundle )
482 {
483 String key = strBundle + locale.toString( );
484 ResourceBundle rbLabels = _resourceBundleCache.get( key );
485
486 if ( rbLabels == null )
487 {
488 rbLabels = ResourceBundle.getBundle( strBundle, locale );
489
490 if ( _overrideLoader != null )
491 {
492 ResourceBundle overrideBundle = null;
493
494 try
495 {
496 overrideBundle = ResourceBundle.getBundle( strBundle, locale, _overrideLoader );
497 }
498 catch ( MissingResourceException e )
499 {
500
501 _resourceBundleCache.put( key, rbLabels );
502
503 return rbLabels;
504 }
505
506 ResourceBundle res = new CombinedResourceBundle( overrideBundle, rbLabels );
507 _resourceBundleCache.put( key, res );
508
509 return res;
510 }
511
512 _resourceBundleCache.put( key, rbLabels );
513 }
514
515 return rbLabels;
516 }
517
518
519
520
521
522 public static void resetCache( )
523 {
524 ResourceBundle.clearCache( );
525
526 if ( _overrideLoader != null )
527 {
528 ResourceBundle.clearCache( _overrideLoader );
529 }
530
531 _resourceBundleCache.clear( );
532 }
533 }