001
014
015 package com.liferay.portal.language;
016
017 import com.liferay.portal.kernel.language.LanguageUtil;
018 import com.liferay.portal.kernel.log.Log;
019 import com.liferay.portal.kernel.log.LogFactoryUtil;
020 import com.liferay.portal.kernel.util.CharPool;
021 import com.liferay.portal.kernel.util.GetterUtil;
022 import com.liferay.portal.kernel.util.LocaleUtil;
023 import com.liferay.portal.kernel.util.PropertiesUtil;
024 import com.liferay.portal.kernel.util.ResourceBundleLoader;
025 import com.liferay.portal.kernel.util.ResourceBundleLoaderUtil;
026 import com.liferay.portal.kernel.util.ResourceBundleUtil;
027 import com.liferay.portal.kernel.util.StringBundler;
028 import com.liferay.portal.kernel.util.StringPool;
029 import com.liferay.portal.kernel.util.StringUtil;
030 import com.liferay.portal.kernel.util.Validator;
031 import com.liferay.portal.tools.LangBuilder;
032 import com.liferay.registry.Filter;
033 import com.liferay.registry.Registry;
034 import com.liferay.registry.RegistryUtil;
035 import com.liferay.registry.ServiceReference;
036 import com.liferay.registry.ServiceTracker;
037 import com.liferay.registry.ServiceTrackerCustomizer;
038
039 import java.io.InputStream;
040
041 import java.net.URL;
042
043 import java.util.Collections;
044 import java.util.Enumeration;
045 import java.util.HashMap;
046 import java.util.Locale;
047 import java.util.Map;
048 import java.util.Properties;
049 import java.util.ResourceBundle;
050 import java.util.Set;
051 import java.util.concurrent.ConcurrentHashMap;
052
053
057 public class LanguageResources {
058
059 public static ResourceBundleLoader RESOURCE_BUNDLE_LOADER =
060 new ResourceBundleLoader() {
061
062 @Override
063 public ResourceBundle loadResourceBundle(String languageId) {
064 return LanguageResources.getResourceBundle(
065 LocaleUtil.fromLanguageId(languageId));
066 }
067
068 };
069
070 public static String fixValue(String value) {
071 if (value.endsWith(LangBuilder.AUTOMATIC_COPY)) {
072 value = value.substring(
073 0, value.length() - LangBuilder.AUTOMATIC_COPY.length());
074 }
075
076 if (value.endsWith(LangBuilder.AUTOMATIC_TRANSLATION)) {
077 value = value.substring(
078 0, value.length() - LangBuilder.AUTOMATIC_TRANSLATION.length());
079 }
080
081 return value;
082 }
083
084 public static void fixValues(
085 Map<String, String> languageMap, Properties properties) {
086
087 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
088 String key = (String)entry.getKey();
089 String value = (String)entry.getValue();
090
091 value = fixValue(value);
092
093 languageMap.put(key, value);
094 }
095 }
096
097 public static String getMessage(Locale locale, String key) {
098 if (locale == null) {
099 return null;
100 }
101
102 Map<String, String> languageMap = _languageMaps.get(locale);
103
104 if (languageMap == null) {
105 languageMap = _loadLocale(locale);
106 }
107
108 String value = languageMap.get(key);
109
110 if (value == null) {
111 return getMessage(getSuperLocale(locale), key);
112 }
113 else {
114 return value;
115 }
116 }
117
118 public static ResourceBundle getResourceBundle(Locale locale) {
119 return new LanguageResourcesBundle(locale);
120 }
121
122 public static Locale getSuperLocale(Locale locale) {
123 Locale superLocale = _superLocales.get(locale);
124
125 if (superLocale != null) {
126 if (superLocale == _nullLocale) {
127 return null;
128 }
129
130 return superLocale;
131 }
132
133 superLocale = _getSuperLocale(locale);
134
135 if (superLocale == null) {
136 _superLocales.put(locale, _nullLocale);
137 }
138 else {
139 _superLocales.put(locale, superLocale);
140 }
141
142 return superLocale;
143 }
144
145 public void afterPropertiesSet() {
146 Registry registry = RegistryUtil.getRegistry();
147
148 Filter languageResourceFilter = registry.getFilter(
149 "(&(!(javax.portlet.name=*))(language.id=*)(objectClass=" +
150 ResourceBundle.class.getName() + "))");
151
152 _serviceTracker = registry.trackServices(
153 languageResourceFilter,
154 new LanguageResourceServiceTrackerCustomizer());
155
156 _serviceTracker.open();
157
158 ResourceBundleLoaderUtil.setPortalResourceBundleLoader(
159 RESOURCE_BUNDLE_LOADER);
160 }
161
162 public void setConfig(String config) {
163 _configNames = StringUtil.split(
164 config.replace(CharPool.PERIOD, CharPool.SLASH));
165 }
166
167 private static Locale _getSuperLocale(Locale locale) {
168 String variant = locale.getVariant();
169
170 if (variant.length() > 0) {
171 return new Locale(locale.getLanguage(), locale.getCountry());
172 }
173
174 String country = locale.getCountry();
175
176 if (country.length() > 0) {
177 Locale priorityLocale = LanguageUtil.getLocale(
178 locale.getLanguage());
179
180 if ((priorityLocale != null) && !locale.equals(priorityLocale)) {
181 return new Locale(
182 priorityLocale.getLanguage(), priorityLocale.getCountry());
183 }
184
185 return LocaleUtil.fromLanguageId(locale.getLanguage(), false, true);
186 }
187
188 String language = locale.getLanguage();
189
190 if (language.length() > 0) {
191 return _blankLocale;
192 }
193
194 return null;
195 }
196
197 private static Map<String, String> _loadLocale(Locale locale) {
198 Map<String, String> languageMap = null;
199
200 if (_configNames.length > 0) {
201 String localeName = locale.toString();
202
203 languageMap = new HashMap<>();
204
205 for (String name : _configNames) {
206 StringBundler sb = new StringBundler(4);
207
208 sb.append(name);
209
210 if (localeName.length() > 0) {
211 sb.append(StringPool.UNDERLINE);
212 sb.append(localeName);
213 }
214
215 sb.append(".properties");
216
217 Properties properties = _loadProperties(sb.toString());
218
219 fixValues(languageMap, properties);
220 }
221 }
222 else {
223 languageMap = Collections.emptyMap();
224 }
225
226 _languageMaps.put(locale, languageMap);
227
228 return languageMap;
229 }
230
231 private static Properties _loadProperties(String name) {
232 Properties properties = new Properties();
233
234 try {
235 ClassLoader classLoader = LanguageResources.class.getClassLoader();
236
237 Enumeration<URL> enu = classLoader.getResources(name);
238
239 if (_log.isDebugEnabled() && !enu.hasMoreElements()) {
240 _log.debug("No resources found for " + name);
241 }
242
243 while (enu.hasMoreElements()) {
244 URL url = enu.nextElement();
245
246 if (_log.isInfoEnabled()) {
247 _log.info("Loading " + name + " from " + url);
248 }
249
250 try (InputStream inputStream = url.openStream()) {
251 Properties inputStreamProperties = PropertiesUtil.load(
252 inputStream, StringPool.UTF8);
253
254 properties.putAll(inputStreamProperties);
255
256 if (_log.isInfoEnabled()) {
257 _log.info(
258 "Loading " + url + " with " +
259 inputStreamProperties.size() + " values");
260 }
261 }
262 }
263 }
264 catch (Exception e) {
265 if (_log.isWarnEnabled()) {
266 _log.warn(e, e);
267 }
268 }
269
270 return properties;
271 }
272
273 private static Map<String, String> _putLanguageMap(
274 Locale locale, Map<String, String> languageMap) {
275
276 Map<String, String> oldLanguageMap = _languageMaps.get(locale);
277
278 if (oldLanguageMap == null) {
279 _loadLocale(locale);
280
281 oldLanguageMap = _languageMaps.get(locale);
282 }
283
284 Map<String, String> newLanguageMap = new HashMap<>();
285
286 if (oldLanguageMap != null) {
287 newLanguageMap.putAll(oldLanguageMap);
288 }
289
290 newLanguageMap.putAll(languageMap);
291
292 _languageMaps.put(locale, newLanguageMap);
293
294 return oldLanguageMap;
295 }
296
297 private static final Log _log = LogFactoryUtil.getLog(
298 LanguageResources.class);
299
300 private static final Locale _blankLocale = new Locale(StringPool.BLANK);
301 private static String[] _configNames;
302 private static final Map<Locale, Map<String, String>> _languageMaps =
303 new ConcurrentHashMap<>(64);
304 private static final Locale _nullLocale = new Locale(StringPool.BLANK);
305 private static final Map<Locale, Locale> _superLocales =
306 new ConcurrentHashMap<>();
307
308 private ServiceTracker<ResourceBundle, ResourceBundle> _serviceTracker;
309
310 private static class LanguageResourcesBundle extends ResourceBundle {
311
312 @Override
313 public Enumeration<String> getKeys() {
314 Set<String> keySet = _languageMap.keySet();
315
316 if (parent == null) {
317 return Collections.enumeration(keySet);
318 }
319
320 return new ResourceBundleEnumeration(keySet, parent.getKeys());
321 }
322
323 @Override
324 public Locale getLocale() {
325 return _locale;
326 }
327
328 @Override
329 protected Object handleGetObject(String key) {
330 return _languageMap.get(key);
331 }
332
333 @Override
334 protected Set<String> handleKeySet() {
335 return _languageMap.keySet();
336 }
337
338 private LanguageResourcesBundle(Locale locale) {
339 _locale = locale;
340
341 Map<String, String> languageMap = _languageMaps.get(locale);
342
343 if (languageMap == null) {
344 languageMap = _loadLocale(locale);
345 }
346
347 _languageMap = languageMap;
348
349 Locale superLocale = getSuperLocale(locale);
350
351 if (superLocale != null) {
352 setParent(new LanguageResourcesBundle(superLocale));
353 }
354 }
355
356 private final Map<String, String> _languageMap;
357 private final Locale _locale;
358
359 }
360
361 private static class LanguageResourceServiceTrackerCustomizer
362 implements ServiceTrackerCustomizer<ResourceBundle, ResourceBundle> {
363
364 @Override
365 public ResourceBundle addingService(
366 ServiceReference<ResourceBundle> serviceReference) {
367
368 Registry registry = RegistryUtil.getRegistry();
369
370 ResourceBundle resourceBundle = registry.getService(
371 serviceReference);
372
373 String languageId = GetterUtil.getString(
374 serviceReference.getProperty("language.id"), StringPool.BLANK);
375 Map<String, String> languageMap = new HashMap<>();
376 Locale locale = null;
377
378 if (Validator.isNotNull(languageId)) {
379 locale = LocaleUtil.fromLanguageId(languageId, true);
380 }
381 else {
382 locale = new Locale(StringPool.BLANK);
383 }
384
385 Enumeration<String> keys = resourceBundle.getKeys();
386
387 while (keys.hasMoreElements()) {
388 String key = keys.nextElement();
389
390 String value = ResourceBundleUtil.getString(
391 resourceBundle, key);
392
393 languageMap.put(key, value);
394 }
395
396 Map<String, String> oldLanguageMap = _putLanguageMap(
397 locale, languageMap);
398
399 _oldLanguageMaps.put(serviceReference, oldLanguageMap);
400
401 return resourceBundle;
402 }
403
404 @Override
405 public void modifiedService(
406 ServiceReference<ResourceBundle> serviceReference,
407 ResourceBundle resourceBundle) {
408 }
409
410 @Override
411 public void removedService(
412 ServiceReference<ResourceBundle> serviceReference,
413 ResourceBundle resourceBundle) {
414
415 Registry registry = RegistryUtil.getRegistry();
416
417 registry.ungetService(serviceReference);
418
419 String languageId = GetterUtil.getString(
420 serviceReference.getProperty("language.id"), StringPool.BLANK);
421 Locale locale = null;
422
423 if (Validator.isNotNull(languageId)) {
424 locale = LocaleUtil.fromLanguageId(languageId, true);
425 }
426 else {
427 locale = new Locale(StringPool.BLANK);
428 }
429
430 Map<String, String> languageMap = _oldLanguageMaps.get(
431 serviceReference);
432
433 _putLanguageMap(locale, languageMap);
434 }
435
436 private final Map<ServiceReference<?>, Map<String, String>>
437 _oldLanguageMaps = new HashMap<>();
438
439 }
440
441 }