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