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