All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.i18n.I18n Maven / Gradle / Ivy

/*************************************************************************
 *
 * 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: *

    *
  1. 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)})
  2. *
  3. static, using a {@link ResourceBundle} as source
  4. *
  5. as object instance, created from a {@link HttpServletRequest} (like * 1, but shorter calls): * *
     * I18n i18n = new I18n(slingRequest);
     * 
    * *
  6. *
  7. as object instance, created from a {@link ResourceBundle} (like 2, but * shorter calls): * *
     * I18n i18n = new I18n(resourceBundle);
     * 
    * *
  8. *
*/ 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy