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 info.magnolia.i18nsystem;
35
36 import info.magnolia.cms.i18n.MessagesManager;
37 import info.magnolia.event.EventBus;
38 import info.magnolia.event.SystemEventBus;
39 import info.magnolia.i18nsystem.module.I18nModule;
40 import info.magnolia.objectfactory.ComponentProvider;
41 import info.magnolia.objectfactory.Components;
42 import info.magnolia.resourceloader.ResourceOrigin;
43
44 import java.util.Arrays;
45 import java.util.Locale;
46 import java.util.Properties;
47
48 import javax.inject.Inject;
49 import javax.inject.Named;
50 import javax.inject.Provider;
51 import javax.inject.Singleton;
52
53 import org.apache.commons.lang3.StringUtils;
54 import org.jsoup.Jsoup;
55 import org.jsoup.safety.Whitelist;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59
60
61
62
63
64
65
66
67
68 @Singleton
69 public class TranslationServiceImpl implements TranslationService {
70 private static final Logger log = LoggerFactory.getLogger(TranslationServiceImpl.class);
71
72 private final Provider<I18nModule> i18nModuleProvider;
73 private final Provider<DefaultMessageBundlesLoader> defaultMessageBundlesLoaderProvider;
74
75 @Inject
76 public TranslationServiceImpl(final Provider<I18nModule> i18nModuleProvider, final Provider<DefaultMessageBundlesLoader> defaultMessageBundlesLoaderProvider) {
77 this.i18nModuleProvider = i18nModuleProvider;
78 this.defaultMessageBundlesLoaderProvider = defaultMessageBundlesLoaderProvider;
79 }
80
81
82
83
84 @Deprecated
85 public TranslationServiceImpl(final Provider<I18nModule> i18nModuleProvider, final ComponentProvider componentProvider, final ResourceOrigin resourceOrigin, @Named(SystemEventBus.NAME) EventBus systemEventBus) {
86 this(i18nModuleProvider, new Provider<DefaultMessageBundlesLoader>() {
87 @Override
88 public DefaultMessageBundlesLoader get() {
89 return Components.getComponent(DefaultMessageBundlesLoader.class);
90 }
91 });
92 }
93
94
95
96
97 @Deprecated
98 public TranslationServiceImpl(Provider<I18nModule> i18nModuleProvider) {
99 this(i18nModuleProvider, new Provider<DefaultMessageBundlesLoader>() {
100 @Override
101 public DefaultMessageBundlesLoader get() {
102 return Components.getComponent(DefaultMessageBundlesLoader.class);
103 }
104 });
105 }
106
107
108
109
110 @Deprecated
111 public TranslationServiceImpl() {
112 this(new Provider<I18nModule>() {
113 @Override
114 public I18nModule get() {
115 return Components.getComponent(I18nModule.class);
116 }
117 }, new Provider<DefaultMessageBundlesLoader>() {
118 @Override
119 public DefaultMessageBundlesLoader get() {
120 return Components.getComponent(DefaultMessageBundlesLoader.class);
121 }
122 });
123 }
124
125
126
127
128 @Deprecated
129 protected DefaultMessageBundlesLoader setupMessageBundles() {
130 return defaultMessageBundlesLoaderProvider.get();
131 }
132
133 @Override
134 public String translate(LocaleProvider localeProvider, String[] keys) {
135 return translate(localeProvider, null, keys);
136 }
137
138 @Override
139 public String translate(LocaleProvider localeProvider, String basename, String[] keys) {
140 final Locale locale = localeProvider.getLocale();
141 if (locale == null) {
142 throw new IllegalArgumentException("Locale can't be null");
143 }
144 if (keys == null || keys.length < 1) {
145 throw new IllegalArgumentException("Keys can't be null or empty");
146 }
147
148 if (basename != null) {
149 log.debug("Got an explicit basename ({}) for keys {}", basename, Arrays.asList(keys));
150 }
151
152 final String message = lookUpKeyUntilFound(keys, locale, basename);
153 if (message != null) {
154 return message;
155 } else {
156 return handleUnknownKey(locale, basename, keys);
157 }
158 }
159
160 private String lookUpKeyUntilFound(final String[] keys, final Locale locale, final String basename) {
161 String message = null;
162
163 if (StringUtils.isNotBlank(basename)) {
164 log.debug("Looking up key [{}] with basename [{}] and Locale [{}] - legacy method", keys[0], basename, locale);
165 message = legacyLookup(locale, basename, keys[0]);
166 if (message != null) {
167 return message + (isDebug() ? this.addDebugInfo(keys, keys[0], locale, basename) : StringUtils.EMPTY);
168 }
169 }
170
171 if (message == null) {
172 log.debug("Looking up in global i18n message bundle with key [{}] and Locale [{}]", Arrays.asList(keys), locale);
173 message = doGetMessage(keys, locale);
174 }
175
176 if (message == null) {
177 final String country = locale.getCountry();
178 if (country != null) {
179 final Locale newLocale = new Locale(locale.getLanguage(), country);
180 message = doGetMessage(keys, newLocale);
181 }
182 }
183
184 if (message == null) {
185 final Locale newLocale = new Locale(locale.getLanguage());
186 message = doGetMessage(keys, newLocale);
187 }
188
189 if (message == null) {
190 message = doGetMessage(keys, getFallbackLocale());
191 }
192
193 if (message == null) {
194 message = handleUnknownKey(locale, basename, keys);
195 }
196
197 return message;
198 }
199
200 private String addDebugInfo(final String[] keys, String currentlyUsedKey, final Locale locale, final String basename) {
201 return "\n" + Arrays.asList(keys).toString()
202 .replaceFirst("(?s)" + currentlyUsedKey + "(?!.*?" + currentlyUsedKey + ")", ">" + currentlyUsedKey + "<")
203 + locale.getClass().getSimpleName() + ":" + locale + (basename == null ? StringUtils.EMPTY : ",Using legacy i18n basename:" + basename);
204 }
205
206 protected String handleUnknownKey(Locale locale, String basename, String[] keys) {
207
208 log.debug("No translation found for any of {} with locale {} and basename {}", keys, locale, basename != null ? basename : "<unspecified>");
209 return keys[0] + (isDebug() ? this.addDebugInfo(keys, null, locale, basename) : StringUtils.EMPTY);
210 }
211
212
213
214
215 private String legacyLookup(Locale locale, String basename, String key) {
216
217 String message = MessagesManager.getMessages(basename, locale).get(key);
218 if (legacyMessageNotFound(message)) {
219 message = MessagesManager.getMessages(MessagesManager.DEFAULT_BASENAME, locale).get(key);
220 }
221 if (legacyMessageNotFound(message)) {
222
223 return null;
224 } else {
225 if (!Jsoup.isValid(message, Whitelist.basic())) {
226 return Jsoup.clean(message, Whitelist.basic());
227 } else {
228 return message;
229 }
230 }
231 }
232
233 private boolean legacyMessageNotFound(final String message) {
234 return message == null || message.startsWith("???");
235 }
236
237 private String doGetMessage(String[] keys, Locale locale) {
238 final Properties properties = defaultMessageBundlesLoaderProvider.get().getMessages().get(locale);
239 if (properties != null) {
240 for (String key : keys) {
241 if (key == null) {
242
243 continue;
244 }
245 final String message = properties.getProperty(key);
246 if (message != null) {
247 return message + (isDebug() ? this.addDebugInfo(keys, key, locale, null) : StringUtils.EMPTY);
248 }
249 }
250 }
251 return null;
252 }
253
254 private boolean isDebug() {
255 try {
256 return i18nModuleProvider.get() != null && i18nModuleProvider.get().isDebug();
257 } catch (RuntimeException exception) {
258
259
260
261 return false;
262 }
263 }
264
265
266 private Locale getFallbackLocale() {
267 return MessagesManager.getInstance().getDefaultLocale();
268 }
269
270
271
272
273 @Deprecated
274 @Override
275 public void reloadMessageBundles() {
276 log.warn("Not reloading message bundles. Please make sure to update [{}] instead.", DefaultMessageBundlesLoader.class.getName());
277 }
278
279 }