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.cms.i18n;
35
36 import info.magnolia.context.MgnlContext;
37 import info.magnolia.jcr.util.PropertyUtil;
38
39 import java.util.Collection;
40 import java.util.HashSet;
41 import java.util.Iterator;
42 import java.util.LinkedHashMap;
43 import java.util.Locale;
44 import java.util.Map;
45 import java.util.Set;
46
47 import javax.jcr.Node;
48 import javax.jcr.Property;
49 import javax.jcr.RepositoryException;
50
51 import org.apache.commons.lang3.StringUtils;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58
59
60
61
62 public abstract class AbstractI18nContentSupport implements I18nContentSupport {
63
64 private static final Logger log = LoggerFactory.getLogger(AbstractI18nContentSupport.class);
65
66
67
68
69 private Locale fallbackLocale = new Locale("en");
70
71
72
73
74 protected Locale defaultLocale;
75
76 private boolean enabled = false;
77
78
79
80
81 private final Map<String, Locale> locales = new LinkedHashMap<String, Locale>();
82
83 @Override
84 public Locale getLocale() {
85 Locale locale = null;
86 if (MgnlContext.getWebContextOrNull() != null) {
87 locale = MgnlContext.getAggregationState().getLocale();
88 }
89 if (locale == null) {
90 return fallbackLocale;
91 }
92 return locale;
93 }
94
95 @Override
96 public void setLocale(Locale locale) {
97 MgnlContext.getAggregationState().setLocale(locale);
98 }
99
100 @Override
101 public Locale getFallbackLocale() {
102 return this.fallbackLocale;
103 }
104
105 @Override
106 public void setFallbackLocale(Locale fallbackLocale) {
107 this.fallbackLocale = fallbackLocale;
108 }
109
110
111
112
113
114
115
116
117
118
119 protected Locale getNextLocale(Locale locale) {
120
121 if (StringUtils.isNotEmpty(locale.getCountry())) {
122
123 Locale langOnlyLocale = new Locale(locale.getLanguage());
124 if (isLocaleSupported(langOnlyLocale)) {
125 return langOnlyLocale;
126 }
127 }
128
129 for (Iterator<Locale> iter = getLocales().iterator(); iter.hasNext(); ) {
130 Locale otherCountryLocale = iter.next();
131
132 if (locale.getLanguage().equals(otherCountryLocale.getLanguage()) && !locale.equals(otherCountryLocale)) {
133 return otherCountryLocale;
134 }
135 }
136 return getFallbackLocale();
137 }
138
139
140
141
142 @Override
143 public Locale determineLocale() {
144 Locale locale;
145
146 locale = onDetermineLocale();
147
148
149 if (locale == null) {
150 locale = getDefaultLocale();
151 }
152
153 if (!isLocaleSupported(locale)) {
154 locale = getNextLocale(locale);
155 }
156 return locale;
157 }
158
159 protected abstract Locale onDetermineLocale();
160
161 protected static Locale determineLocalFromString(String localeStr) {
162 if (StringUtils.isNotEmpty(localeStr)) {
163 String[] localeArr = StringUtils.split(localeStr, "_");
164 if (localeArr.length == 1) {
165 return new Locale(localeArr[0]);
166 } else if (localeArr.length == 2) {
167 return new Locale(localeArr[0], localeArr[1]);
168 }
169 }
170 return null;
171 }
172
173 @Override
174 public String toI18NURI(String uri) {
175 if (!isEnabled()) {
176 return uri;
177 }
178 Locale locale = getLocale();
179 if (isLocaleSupported(locale)) {
180 return toI18NURI(uri, locale);
181 }
182 return uri;
183 }
184
185 protected abstract String toI18NURI(String uri, Locale locale);
186
187
188
189
190 @Override
191 public String toRawURI(String i18nURI) {
192 if (!isEnabled()) {
193 return i18nURI;
194 }
195
196 Locale locale = getLocale();
197 if (isLocaleSupported(locale)) {
198 return toRawURI(i18nURI, locale);
199 }
200 return i18nURI;
201 }
202
203 protected abstract String toRawURI(String i18nURI, Locale locale);
204
205 @Override
206 public Node getNode(Node node, String name) throws RepositoryException {
207 if (isEnabled()) {
208
209 try {
210
211 Locale locale = getLocale();
212 Set<Locale> checkedLocales = new HashSet<Locale>();
213
214
215 while (locale != null) {
216 String localeSpecificChildName = name + "_" + locale;
217 if (node.hasNode(localeSpecificChildName)) {
218 return node.getNode(localeSpecificChildName);
219 }
220 checkedLocales.add(locale);
221 locale = getNextContentLocale(locale, checkedLocales);
222 }
223 } catch (RepositoryException e) {
224 log.error("can't read i18n node {} from node {}", name, node, e);
225 }
226 }
227
228 return node.getNode(name);
229 }
230
231 @Override
232 public boolean hasProperty(Node node, String name) throws RepositoryException {
233 if (!isEnabled()) {
234 return node.hasProperty(name);
235 }
236 try {
237
238 getProperty(node, name);
239 } catch (RepositoryException e) {
240 return false;
241 }
242 return true;
243 }
244
245 @Override
246 public Property getProperty(Node node, String name) throws RepositoryException {
247 if (!isEnabled()) {
248 return node.getProperty(name);
249 }
250 try {
251
252 Locale locale = getLocale();
253 Set<Locale> checkedLocales = new HashSet<Locale>();
254
255
256 while (locale != null) {
257 Property property = getProperty(node, name, locale);
258 if (!isEmpty(property)) {
259 return property;
260 } else if (locale.equals(getDefaultLocale()) && node.hasProperty(name)) {
261 return node.getProperty(name);
262 }
263 checkedLocales.add(locale);
264 locale = getNextContentLocale(locale, checkedLocales);
265 }
266 } catch (RepositoryException e) {
267 log.error("can't read i18n property {} from node {}", name, node, e);
268 }
269
270
271 return node.getProperty(name);
272 }
273
274 @Override
275 public Property getProperty(Node node, String name, Locale locale) throws RepositoryException {
276 String propName = name + "_" + locale;
277 if (node.hasProperty(propName)) {
278 return node.getProperty(propName);
279 }
280 return null;
281 }
282
283
284
285
286
287
288 protected Locale getNextContentLocale(Locale locale, Set<Locale> checkedLocales) {
289 if (locale.equals(getFallbackLocale())) {
290 return null;
291 }
292 Locale candidate = getNextLocale(locale);
293 if (!checkedLocales.contains(candidate)) {
294 return candidate;
295 }
296 return getFallbackLocale();
297 }
298
299 @Override
300 public boolean isEnabled() {
301 return this.enabled;
302 }
303
304 public void setEnabled(boolean enabled) {
305 this.enabled = enabled;
306 }
307
308 @Override
309 public Collection<Locale> getLocales() {
310 return this.locales.values();
311 }
312
313 public void setLocales(Collection<LocaleDefinition> locales) {
314 locales.forEach(this::addLocale);
315 }
316
317
318
319
320 @Deprecated
321 public void setLocales(Map<String, Locale> locales) {
322 this.locales.putAll(locales);
323 }
324
325 public void addLocale(LocaleDefinition ld) {
326 if (ld.isEnabled()) {
327 this.locales.put(ld.getId(), ld.getLocale());
328 }
329 }
330
331 protected boolean isLocaleSupported(Locale locale) {
332 return locale != null && locales.containsKey(locale.toString());
333 }
334
335
336
337
338 protected boolean isEmpty(Property property) {
339 try {
340 if (property != null) {
341 return (property.isMultiple()) ? property.getValues().length == 0 : StringUtils.isEmpty(PropertyUtil.getValueString(property));
342 }
343 } catch (RepositoryException e) {
344 log.warn("Can't read value from property {}", property, e);
345 }
346 return true;
347 }
348
349 @Override
350 public Locale getDefaultLocale() {
351 if (this.defaultLocale == null) {
352 return getFallbackLocale();
353 }
354 return this.defaultLocale;
355 }
356
357 public void setDefaultLocale(Locale defaultLocale) {
358 this.defaultLocale = defaultLocale;
359 }
360 }