com.day.cq.i18n.I18n Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2011 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.day.cq.i18n;
import java.util.ResourceBundle;
import javax.servlet.http.HttpServletRequest;
import org.apache.sling.api.SlingHttpServletRequest;
/**
* Externalizes strings using {@link ResourceBundle} dictionaries and simulatenously
* acts as marker for the string extraction process. In addition to plain string
* translation, it allows for translation comments and placeholders
* ({0}
, {1}
, etc.). ResourceBundles will be taken from
* a {@link SlingHttpServletRequest} and it's default locale (standard case) or
* can be passed explicitly.
*
*
* There are two kinds of methods, which are identical in their implementation,
* but have a different signature to indicate the proper intention to the
* string extraction tool:
*
*
* -
*
get()
: for use with literal strings, also acts as marker for the string extraction
* Example: {@code i18n.get("Label");}
*
* Note that this below is incorrect - while it works programmatically, it prevents
* the string from being extracted if the literal is not part of the method call directly:
*
* {@code
* String title = "Label";
* i18n.get(title);
* }
*
* -
*
getVar()
: for use with string variables where the actual string is read
* and extracted from another location (e.g. the JCR)
*
* Example: {@code
* String var = properties.get("jcr:title", String.class);
* i18n.getVar(var);
* }
*
* When using this method, make sure the string gets extracted properly from the other
* location (i18n engineers will know).
*
*
*
*
*
* There are basically three ways to use this class:
*
* - static, using {@link HttpServletRequest} as source (will use the
* default language and resource bundle from the request object, ie. using
* {@link SlingHttpServletRequest#getResourceBundle(java.util.Locale)
* slingRequest.getResourceBundle(null)})
* - static, using a {@link ResourceBundle} as source
* - as object instance, created from a {@link HttpServletRequest} (like
* 1, but shorter calls):
*
*
* I18n i18n = new I18n(slingRequest);
*
*
*
* - as object instance, created from a {@link ResourceBundle} (like 2, but
* shorter calls):
*
*
* I18n i18n = new I18n(resourceBundle);
*
*
*
*
*/
public class I18n {
private final HttpServletRequest request;
private final ResourceBundle resourceBundle;
public I18n(final HttpServletRequest request) {
this.request = request;
this.resourceBundle = null;
}
public I18n(final ResourceBundle resourceBundle) {
this.resourceBundle = resourceBundle;
this.request = null;
}
private ResourceBundle getResourceBundle() {
if ( this.request != null ) {
return getResourceBundle(this.request);
}
return this.resourceBundle;
}
/** Request attribute holding the resource bundle. */
private static String BUNDLE_REQ_ATTR = "org.apache.sling.i18n.resourcebundle";
private static ResourceBundle getResourceBundle(final HttpServletRequest req) {
if ( req instanceof SlingHttpServletRequest ) {
return ((SlingHttpServletRequest)req).getResourceBundle(null);
}
return (ResourceBundle) req.getAttribute(BUNDLE_REQ_ATTR);
}
// ----------------------------------------< request-based >
/**
* Translates the given text. Will return the original text if no
* translation was found.
*
*
* If this object was created via {@link #I18n(HttpServletRequest)},
* the default language resource bundle of the sling request will be used,
* otherwise it uses the underlying {@link ResourceBundle} provided in
* {@link #I18n(ResourceBundle)}.
*
*
* @param text
* The text to translate - as string literal "My Label"
* @return the translation, or the original text if no translation was found
*/
public String get(final String text) {
return get(this.getResourceBundle(), text, null, (Object[]) null);
}
/**
* Translates the given text considering a special comment for translators.
* Will return the original text if no translation was found.
*
*
* If this object was created via {@link #I18n(HttpServletRequest)},
* the default language resource bundle of the sling request will be used,
* otherwise it uses the underlying {@link ResourceBundle} provided in
* {@link #I18n(ResourceBundle)}.
*
*
* @param text
* The text to translate - as string literal "My Label"
* @param comment
* A comment for translators to specify the context in which the
* text is used - as string literal "Action button label"
* @return the translation, or the original text if no translation was found
*/
public String get(final String text, final String comment) {
return get(this.getResourceBundle(), text, comment, (Object[]) null);
}
/**
* Translates the given text considering a special comment for translators
* and replaces placeholders ({0}
, {1}
, etc.) with
* the given arguments. Will return the original text if no translation was
* found.
*
*
* If this object was created via {@link #I18n(HttpServletRequest)},
* the default language resource bundle of the sling request will be used,
* otherwise it uses the underlying {@link ResourceBundle} provided in
* {@link #I18n(ResourceBundle)}.
*
*
* @param text
* The text to translate - as string literal "My Label"
* @param comment
* A comment for translators to specify the context in which the
* text is used - as string literal "Action button label"
* (can be null
).
* @param args
* A varargs list that (as Strings) will be used to replace
* numbered placeholders in the text (eg. ({0}
,
* {1}
, etc.)
* @return the translation, or the original text if no translation was found
*/
public String get(final String text, final String comment, final Object... args) {
return get(this.getResourceBundle(), text, comment, args);
}
/**
* Translates the given text. Will return the original text if no
* translation was found.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* If this object was created via {@link #I18n(HttpServletRequest)},
* the default language resource bundle of the sling request will be used,
* otherwise it uses the underlying {@link ResourceBundle} provided in
* {@link #I18n(ResourceBundle)}.
*
*
* @param text
* The text to translate
* @return the translation, or the original text if no translation was found
*/
public String getVar(final String text) {
return getVar(this.getResourceBundle(), text, null);
}
/**
* Note: this variant is only for rare cases.
*
*
* Translates the given text considering a special comment for translators.
* Will return the original text if no translation was found.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Warning: It is very important that the translation comment passed
* as second argument is present in the dictionary (unless it's null
).
* Since in the getVar()
case the strings are read or extracted from a
* different location, this comment must also be present wherever the strings
* are extracted from, and read from there! Because of this, there will usually be
* no comment with getVar() (null
).
*
*
* If this object was created via {@link #I18n(HttpServletRequest)},
* the default language resource bundle of the sling request will be used,
* otherwise it uses the underlying {@link ResourceBundle} provided in
* {@link #I18n(ResourceBundle)}.
*
*
* @param text
* The text to translate
* @param comment
* A comment for translators to specify the context in which the
* text is used (can be null
). Warning: must also be
* present in the exact same form where the actual string is read from, which is
* usually impractical, so by default skip with null
.
* @return the translation, or the original text if no translation was found
*/
public String getVar(final String text, final String comment) {
return getVar(this.getResourceBundle(), text, comment);
}
/**
* Translates the given text considering a special comment for translators
* and replaces placeholders ({0}
, {1}
, etc.) with
* the given arguments. Will return the original text if no translation was
* found.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Warning: It is very important that the translation comment passed
* as second argument is present in the dictionary (unless it's null
).
* Since in the getVar()
case the strings are read or extracted from a
* different location, this comment must also be present wherever the strings
* are extracted from, and read from there! Because of this, there will usually be
* no comment with getVar() (null
):
*
*
i18n.getVar(text, null, variable1, variable2);
*
*
* If this object was created via {@link #I18n(HttpServletRequest)},
* the default language resource bundle of the sling request will be used,
* otherwise it uses the underlying {@link ResourceBundle} provided in
* {@link #I18n(ResourceBundle)}.
*
*
* @param text
* The text to translate
* @param comment
* A comment for translators to specify the context in which the
* text is used (can be null
). Warning: must also be
* present in the exact same form where the actual string is read from, which is
* usually impractical, so by default skip with null
.
* @param args
* A varargs list that (as Strings) will be used to replace
* numbered placeholders in the text (eg. ({0}
,
* {1}
, etc.)
* @return the translation, or the original text if no translation was found
*/
public String getVar(final String text, final String comment, Object... args) {
return getVar(this.getResourceBundle(), text, comment, args);
}
// ----------------------------------------< static, request-based >
/**
* Translates the given text. Will return the original text if no
* translation was found.
*
*
* Uses the default language resource bundle of the sling request, ie. using
* {@link SlingHttpServletRequest#getResourceBundle(java.util.Locale)
* slingRequest.getResourceBundle(null)}.
*
*
* @param request
* The request object of which the default language resource
* bundle will be taken from as translation source
* @param text
* The text to translate - as string literal "My Label"
* @return the translation, or the original text if no translation was found
*/
public static String get(final HttpServletRequest request, final String text) {
return get(getResourceBundle(request), text, null, (Object[]) null);
}
/**
* Translates the given text considering a special comment for translators.
* Will return the original text if no translation was found.
*
*
* Uses the default language resource bundle of the sling request, ie. using
* {@link SlingHttpServletRequest#getResourceBundle(java.util.Locale)
* slingRequest.getResourceBundle(null)}.
*
*
* @param request
* The request object of which the default language resource
* bundle will be taken from as translation source
* @param text
* The text to translate - as string literal "My Label"
* @param comment
* A comment for translators to specify the context in which the
* text is used - as string literal "Action button label"
* @return the translation, or the original text if no translation was found
*/
public static String get(final HttpServletRequest request, final String text, final String comment) {
return get(getResourceBundle(request), text, comment, (Object[]) null);
}
/**
* Translates the given text considering a special comment for translators
* and replaces placeholders ({0}
, {1}
, etc.) with
* the given arguments. Will return the original text if no translation was
* found.
*
*
* Uses the default language resource bundle of the sling request, ie. using
* {@link SlingHttpServletRequest#getResourceBundle(java.util.Locale)
* slingRequest.getResourceBundle(null)}.
*
*
* @param request
* The request object of which the default language resource
* bundle will be taken from as translation source
* @param text
* The text to translate - as string literal "My Label"
* @param comment
* A comment for translators to specify the context in which the
* text is used - as string literal "Action button label"
* (can be null
).
* @param args
* A varargs list that (as Strings) will be used to replace
* numbered placeholders in the text (eg. ({0}
,
* {1}
, etc.)
* @return the translation, or the original text if no translation was found
*/
public static String get(final HttpServletRequest request, final String text, final String comment, Object... args) {
return get(getResourceBundle(request), text, comment, args);
}
/**
* Translates the specified text into the current language.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Uses the default language resource bundle of the sling request, ie. using
* {@link SlingHttpServletRequest#getResourceBundle(java.util.Locale)
* slingRequest.getResourceBundle(null)}.
*
*
* @param request
* The request object of which the default language resource
* bundle will be taken from as translation source
* @param text
* The text to translate
* @return the translation of the specified text into the current language
*/
public static String getVar(final HttpServletRequest request, final String text) {
return getVar(getResourceBundle(request), text, null);
}
/**
* Note: this variant is only for rare cases.
*
*
* Translates the specified text considering a special comment for translators
* into the current language.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Warning: It is very important that the translation comment passed
* as second argument is present in the dictionary (unless it's null
).
* Since in the getVar()
case the strings are read or extracted from a
* different location, this comment must also be present wherever the strings
* are extracted from, and read from there! Because of this, there will usually be
* no comment with getVar() (null
).
*
*
* Uses the default language resource bundle of the sling request, ie. using
* {@link SlingHttpServletRequest#getResourceBundle(java.util.Locale)
* slingRequest.getResourceBundle(null)}.
*
*
* @param request
* The request object of which the default language resource
* bundle will be taken from as translation source
* @param text
* The text to translate
* @param comment
* A comment for translators to specify the context in which the
* text is used (can be null
). Warning: must also be
* present in the exact same form where the actual string is read from, which is
* usually impractical, so by default skip with null
.
* @return the translation of the specified text into the current language
*/
public static String getVar(final HttpServletRequest request, final String text, final String comment) {
return getVar(getResourceBundle(request), text, comment);
}
/**
* Translates the given text considering a special comment for translators
* and replaces placeholders ({0}
, {1}
, etc.) with
* the given arguments. Will return the original text if no translation was
* found.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Warning: It is very important that the translation comment passed
* as second argument is present in the dictionary (unless it's null
).
* Since in the getVar()
case the strings are read or extracted from a
* different location, this comment must also be present wherever the strings
* are extracted from, and read from there! Because of this, there will usually be
* no comment with getVar() (null
):
*
*
I18n.getVar(request, text, null, variable1, variable2);
*
*
* Uses the default language resource bundle of the sling request, ie. using
* {@link SlingHttpServletRequest#getResourceBundle(java.util.Locale)
* slingRequest.getResourceBundle(null)}.
*
*
* @param request
* The request object of which the default language resource
* bundle will be taken from as translation source
* @param text
* The text to translate
* @param comment
* A comment for translators to specify the context in which the
* text is used (can be null
). Warning: must also be
* present in the exact same form where the actual string is read from, which is
* usually impractical, so by default skip with null
.
* @param args
* A varargs list that (as Strings) will be used to replace
* numbered placeholders in the text (eg. ({0}
,
* {1}
, etc.)
* @return the translation, or the original text if no translation was found
*/
public static String getVar(final HttpServletRequest request, final String text, final String comment, Object... args) {
return getVar(getResourceBundle(request), text, comment, args);
}
// ----------------------------------------< static, resource-bundle-based >
/**
* Translates the given text. Will return the original text if no
* translation was found.
*
*
* Uses the given {@link ResourceBundle} as translation source.
*
*
* @param resourceBundle
* The resourceBundle used as translation source
* @param text
* The text to translate - as string literal "My Label"
* @return the translation, or the original text if no translation was found
*/
public static String get(final ResourceBundle resourceBundle, final String text) {
return get(resourceBundle, text, null, (Object[]) null);
}
/**
* Translates the given text considering a special comment for translators.
* Will return the original text if no translation was found.
*
*
* Uses the given {@link ResourceBundle} as translation source.
*
*
* @param resourceBundle
* The resourceBundle used as translation source
* @param text
* The text to translate - as string literal "My Label"
* @param comment
* A comment for translators to specify the context in which the
* text is used - as string literal "Action button label"
* @return the translation, or the original text if no translation was found
*/
public static String get(final ResourceBundle resourceBundle, final String text, final String comment) {
return get(resourceBundle, text, comment, (Object[]) null);
}
/**
* Translates the given text considering a special comment for translators
* and replaces placeholders ({0}
, {1}
, etc.) with
* the given arguments. Will return the original text if no translation was
* found.
*
*
* Uses the given {@link ResourceBundle} as translation source.
*
*
* @param resourceBundle
* The resourceBundle used as translation source
* @param text
* The text to translate - as string literal "My Label"
* @param comment
* A comment for translators to specify the context in which the
* text is used - as string literal "Action button label"
* (can be null
).
* @param args
* A varargs list that (as Strings) will be used to replace
* numbered placeholders in the text (eg. ({0}
,
* {1}
, etc.)
* @return the translation, or the original text if no translation was found
*/
public static String get(final ResourceBundle resourceBundle, final String text, final String comment, final Object... args) {
if (text == null) {
return text;
}
if (resourceBundle == null) {
// if the text comes with args and the resource bundle is null,
// the text with the placeholders replaced by the args should be returned
return patchText(text, args);
}
String msg;
// whether the translation is equal to the original
boolean equals = false;
if (comment != null && comment.length() > 0) {
// if the original text in the source code comes with a comment,
// it will be included in the key for the sling messages because
// different comments indicate different meanings hence a need for
// possibly different translations of the same text depending on
// its context
final String key = text + " ((" + comment + "))";
msg = resourceBundle.getString(key);
// if the translation is the same as the key, it means it wasn't
// translated by the Sling JCR resource bundle and just returned
// as it is; in this case we have to make sure to strip the comment
if (key.equals(msg)) {
equals = true;
msg = text;
}
} else {
msg = resourceBundle.getString(text);
if (text.equals(msg)) {
equals = true;
}
}
// replace placeholders ala "{0} {1}" with the given arguments
try {
return patchText(msg, args);
} catch (IllegalArgumentException e) {
// invalid format for translation
if (equals) {
// original string => throw for developer to fix
throw e;
} else {
// translation => avoid exception, use unformatted string
return msg;
}
}
}
/**
* Translates the specified text into the current language.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Uses the given {@link ResourceBundle} as translation source.
*
*
* @param resourceBundle
* The resourceBundle used as translation source
* @param text
* The text to translate
* @return the translation of the specified text into the current language
*/
public static String getVar(final ResourceBundle resourceBundle, final String text) {
return get(resourceBundle, text, null);
}
/**
* Note: this variant is only for rare cases.
*
*
* Translates the specified text considering a special comment for translators
* into the current language.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Warning: It is very important that the translation comment passed
* as second argument is present in the dictionary (unless it's null
).
* Since in the getVar()
case the strings are read or extracted from a
* different location, this comment must also be present wherever the strings
* are extracted from, and read from there! Because of this, there will usually be
* no comment with getVar() (null
).
*
*
* Uses the given {@link ResourceBundle} as translation source.
*
*
* @param resourceBundle
* The resourceBundle used as translation source
* @param text
* The text to translate
* @param comment
* A comment for translators to specify the context in which the
* text is used (can be null
). Warning: must also be
* present in the exact same form where the actual string is read from, which is
* usually impractical, so by default skip with null
.
* @return the translation of the specified text into the current language
*/
public static String getVar(final ResourceBundle resourceBundle, final String text, final String comment) {
return get(resourceBundle, text, comment);
}
/**
* Translates the given text considering a special comment for translators
* and replaces placeholders ({0}
, {1}
, etc.) with
* the given arguments. Will return the original text if no translation was
* found.
*
*
* Use this variant to translate string variables which have a value read from
* somewhere else, such as the JCR repository. In this case the getVar() signature
* marks it clearly that the extraction tool should not expect a literal string
* here. When adding this, make sure the string gets extracted properly from
* the JCR repository for example (i18n engineers will know).
*
*
* Warning: It is very important that the translation comment passed
* as second argument is present in the dictionary (unless it's null
).
* Since in the getVar()
case the strings are read or extracted from a
* different location, this comment must also be present wherever the strings
* are extracted from, and read from there! Because of this, there will usually be
* no comment with getVar() (null
):
*
*
I18n.getVar(bundle, text, null, variable1, variable2);
*
*
* Uses the given {@link ResourceBundle} as translation source.
*
*
* @param resourceBundle
* The resourceBundle used as translation source
* @param text
* The text to translate
* @param comment
* A comment for translators to specify the context in which the
* text is used (can be null
). Warning: must also be
* present in the exact same form where the actual string is read from, which is
* usually impractical, so by default skip with null
.
* @param args
* A varargs list that (as Strings) will be used to replace
* numbered placeholders in the text (eg. ({0}
,
* {1}
, etc.)
* @return the translation, or the original text if no translation was found
*/
public static String getVar(final ResourceBundle resourceBundle, final String text, final String comment, Object... args) {
return get(resourceBundle, text, comment, args);
}
/**
* Simple variant of MessageFormat that replaces "{n}" with the
* n-th var arg object (converted to string).
*
* Same implementation as for javascript in Granite.Util.patchText().
*
* @param text the text containing the placeholders
* @param args the replacements
* @return the original text but all the placeholders have been replaced with the
* specified arguments
*/
private static String patchText(String text, Object... args) {
if (text == null || args == null) {
return text;
}
for (int i = 0; i < args.length; i++) {
Object o = args[i];
if (o != null) {
text = text.replace("{" + i + "}", o.toString());
}
}
return text;
}
}