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 setConfig(String config) {
133 _configNames = StringUtil.split(
134 config.replace(CharPool.PERIOD, CharPool.SLASH));
135 }
136
137 private static Locale _getSuperLocale(Locale locale) {
138 String variant = locale.getVariant();
139
140 if (variant.length() > 0) {
141 return new Locale(locale.getLanguage(), locale.getCountry());
142 }
143
144 String country = locale.getCountry();
145
146 if (country.length() > 0) {
147 Locale priorityLocale = LanguageUtil.getLocale(
148 locale.getLanguage());
149
150 if ((priorityLocale != null) && !locale.equals(priorityLocale)) {
151 return new Locale(
152 priorityLocale.getLanguage(), priorityLocale.getCountry());
153 }
154
155 return LocaleUtil.fromLanguageId(locale.getLanguage(), false, true);
156 }
157
158 String language = locale.getLanguage();
159
160 if (language.length() > 0) {
161 return _blankLocale;
162 }
163
164 return null;
165 }
166
167 private static Map<String, String> _loadLocale(Locale locale) {
168 Map<String, String> languageMap = null;
169
170 if (_configNames.length > 0) {
171 String localeName = locale.toString();
172
173 languageMap = new HashMap<>();
174
175 for (String name : _configNames) {
176 StringBundler sb = new StringBundler(4);
177
178 sb.append(name);
179
180 if (localeName.length() > 0) {
181 sb.append(StringPool.UNDERLINE);
182 sb.append(localeName);
183 }
184
185 sb.append(".properties");
186
187 Properties properties = _loadProperties(sb.toString());
188
189 fixValues(languageMap, properties);
190 }
191 }
192 else {
193 languageMap = Collections.emptyMap();
194 }
195
196 _languageMaps.put(locale, languageMap);
197
198 return languageMap;
199 }
200
201 private static Properties _loadProperties(String name) {
202 Properties properties = new Properties();
203
204 try {
205 ClassLoader classLoader = LanguageResources.class.getClassLoader();
206
207 Enumeration<URL> enu = classLoader.getResources(name);
208
209 if (_log.isDebugEnabled() && !enu.hasMoreElements()) {
210 _log.debug("No resources found for " + name);
211 }
212
213 while (enu.hasMoreElements()) {
214 URL url = enu.nextElement();
215
216 if (_log.isInfoEnabled()) {
217 _log.info("Loading " + name + " from " + url);
218 }
219
220 try (InputStream inputStream = url.openStream()) {
221 Properties inputStreamProperties = PropertiesUtil.load(
222 inputStream, StringPool.UTF8);
223
224 properties.putAll(inputStreamProperties);
225
226 if (_log.isInfoEnabled()) {
227 _log.info(
228 "Loading " + url + " with " +
229 inputStreamProperties.size() + " values");
230 }
231 }
232 }
233 }
234 catch (Exception e) {
235 if (_log.isWarnEnabled()) {
236 _log.warn(e, e);
237 }
238 }
239
240 return properties;
241 }
242
243 private static Map<String, String> _putLanguageMap(
244 Locale locale, Map<String, String> languageMap) {
245
246 Map<String, String> oldLanguageMap = _languageMaps.get(locale);
247
248 if (oldLanguageMap == null) {
249 _loadLocale(locale);
250
251 oldLanguageMap = _languageMaps.get(locale);
252 }
253
254 Map<String, String> newLanguageMap = new HashMap<>();
255
256 if (oldLanguageMap != null) {
257 newLanguageMap.putAll(oldLanguageMap);
258 }
259
260 newLanguageMap.putAll(languageMap);
261
262 _languageMaps.put(locale, newLanguageMap);
263
264 return oldLanguageMap;
265 }
266
267 private static final Log _log = LogFactoryUtil.getLog(
268 LanguageResources.class);
269
270 private static final Locale _blankLocale = new Locale(StringPool.BLANK);
271 private static String[] _configNames;
272 private static final Map<Locale, Map<String, String>> _languageMaps =
273 new ConcurrentHashMap<>(64);
274 private static final Locale _nullLocale = new Locale(StringPool.BLANK);
275 private static final ServiceTracker<ResourceBundle, ResourceBundle>
276 _serviceTracker;
277 private static final Map<Locale, Locale> _superLocales =
278 new ConcurrentHashMap<>();
279
280 static {
281 Registry registry = RegistryUtil.getRegistry();
282
283 Filter languageResourceFilter = registry.getFilter(
284 "(&(!(javax.portlet.name=*))(language.id=*)(objectClass=" +
285 ResourceBundle.class.getName() + "))");
286
287 _serviceTracker = registry.trackServices(
288 languageResourceFilter,
289 new LanguageResourceServiceTrackerCustomizer());
290
291 _serviceTracker.open();
292 }
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 }