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