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

com.github.jknack.handlebars.helper.I18nHelper Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/**
 * Copyright (c) 2012-2015 Edgar Espina
 *
 * This file is part of Handlebars.java.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.jknack.handlebars.helper;

import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import static org.apache.commons.lang3.Validate.isTrue;
import static org.apache.commons.lang3.Validate.notEmpty;
import static org.apache.commons.lang3.Validate.notNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
import com.github.jknack.handlebars.internal.Locales;

/**
 * Implementation of i18n helper for Java and JavaScript.
 * 

* The Java implementation use {@link ResourceBundle}. *

*

* The JavaScript version use the I18n library. * {@link ResourceBundle} are converted to JavaScript code. *

* * @author edgar.espina * @since 0.9.0 * @see ResourceBundle */ public enum I18nHelper implements Helper { /** *

* A helper built on top of {@link ResourceBundle}. A {@link ResourceBundle} is the most well * known mechanism for internationalization (i18n). *

* *

messages.properties:

* *
     *  hello=Hola
     * 
* *

Basic Usage:

* *
     *  {{i18n "hello"}}
     * 
* *

* Will result in: Hola *

*

Using a locale:

* *
     *  {{i18n "hello" locale="es_AR"}}
     * 
* *

Using a different bundle:

* *
     *  {{i18n "hello" bundle="myMessages"}}
     * 
* *

Using a message format:

* *
     *  hello=Hola {0}!
     * 
* *
     *  {{i18n "hello" "Handlebars.java"}}
     * 
* * @author edgar.espina * @since 0.9.0 * @see ResourceBundle */ i18n { /** *

* A helper built on top of {@link ResourceBundle}. A {@link ResourceBundle} is the most well * known mechanism for internationalization (i18n). *

*

*

messages.properties:

*

* *
         *  hello=Hola
         * 
* *

Basic Usage:

* *
         *  {{i18n "hello"}}
         * 
* *

* Will result in: Hola *

*

Using a locale:

* *
         * {{i18n "hello" locale="es_AR"}}
         * 
* *

Using a different bundle:

* *
         *  {{i18n "hello" bundle="myMessages"}}
         * 
* *

Using a message format

* *
         *  hello=Hola {0}!
         * 
* *
         *  {{i18n "hello" "Handlebars.java"}}
         * 
* * @param key The bundle's key. Required. * @param options The helper's options. Not null. * @return An i18n message. * @throws IOException If the bundle wasn't resolve. */ @Override public Object apply(final String key, final Options options) throws IOException { notEmpty(key, "found: '%s', expected 'bundle's key'", key); Locale locale = Locales.fromString((String) options.hash("locale", defaultLocale.toString())); String baseName = options.hash("bundle", defaultBundle); ClassLoader classLoader = options.hash("classLoader", getClass().getClassLoader()); I18nSource localSource = source == null ? new DefI18nSource(charset, baseName, locale, classLoader) : source; return localSource.message(key, locale, options.params); } } , /** *

* Translate a {@link ResourceBundle} into JavaScript code. The generated code assume you added * the I18n *

*

* It converts message patterns like: Hi {0} into Hi {{arg0}}. This make * possible to the I18n JS library to interpolate variables. *

*

* Note: make sure you include I18n in your * application. Otherwise, the generated code will fail. *

*

* Usage: *

* *
     *  {{i18nJs locale?}}
     * 
* * If locale argument is present it will translate that locale to JavaScript. Otherwise, the * default locale. */ i18nJs { /** * The message format pattern. */ private final Pattern pattern = Pattern.compile("\\{(\\d+)\\}"); /** *

* Translate a {@link ResourceBundle} into JavaScript code. The generated code assume you added * the I18n *

*

* It converts message patterns like: Hi {0} into Hi {{arg0}}. This * make possible to the I18n JS library to interpolate variables. *

*

* Note: make sure you include I18n in your * application. Otherwise, the generated code will fail. *

*

* Usage: *

* *
         *  {{i18nJs [locale] [bundle=messages] [wrap=true]}}
         * 
* * If locale argument is present it will translate that locale to JavaScript. Otherwise, the * default locale. * * Use wrap=true for wrapping the code with a script tag. * * @param localeName The locale's name. Optional. * @param options The helper's options. Not null. * @return JavaScript code from {@link ResourceBundle}. * @throws IOException If bundle wasn't resolve. */ @Override public Object apply(final String localeName, final Options options) throws IOException { Locale locale = Locales.fromString(defaultIfEmpty(localeName, defaultLocale.toString())); String baseName = options.hash("bundle", defaultBundle); ClassLoader classLoader = options.hash("classLoader", getClass().getClassLoader()); I18nSource localSource = source == null ? new DefI18nSource(charset, baseName, locale, classLoader) : source; StringBuilder buffer = new StringBuilder(); Boolean wrap = options.hash("wrap", true); if (wrap) { buffer.append("\n"); } return new Handlebars.SafeString(buffer); } /** * Convert expression {0} into {{arg0}} and escape EcmaScript * characters. * * @param message The candidate message. * @return A valid I18n message. */ private String message(final String message) { CharSequence escapedMessage = Handlebars.Utils.escapeExpression(message); Matcher matcher = pattern.matcher(escapedMessage); StringBuffer result = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(result, "{{arg" + matcher.group(1) + "}}"); } matcher.appendTail(result); return result.toString(); } } ; /** * The default locale. Required. */ protected Locale defaultLocale = Locale.getDefault(); /** * The default's bundle. Required. */ protected String defaultBundle = "messages"; /** * The message source to use. */ protected I18nSource source; /** * Charset. * */ protected Charset charset = StandardCharsets.UTF_8; /** * Set the charset to use. * * NotThreadSafe Make sure to call this method ONCE at start time. * * @param charset Charset. Required. */ public void setCharset(final Charset charset) { this.charset = notNull(charset, "Charset required."); } /** * Set the message source. * * NotThreadSafe Make sure to call this method ONCE at start time. * * @param source The message source. Required. */ public void setSource(final I18nSource source) { this.source = notNull(source, "The i18n source is required."); } /** * Set the default bundle's name. Default is: messages and this method let you override the * default bundle's name to something else. * * NotThreadSafe Make sure to call this method ONCE at start time. * * @param bundle The default's bundle name. Required. */ public void setDefaultBundle(final String bundle) { this.defaultBundle = notEmpty(bundle, "A bundle's name is required."); } /** * Set the default locale. Default is system dependent and this method let you override the * default bundle's name to something else. * * NotThreadSafe Make sure to call this method ONCE at start time. * * @param locale The default locale name. Required. */ public void setDefaultLocale(final Locale locale) { this.defaultLocale = notNull(locale, "A locale is required."); } } /** * Default implementation of I18nSource. * @deprecated com.github.jknack.handlebars.helper package is deprecated and marked for removal in subsequent releases which will involve removal of the handlebars dependency in AEM. */ @Deprecated(since = "2024-07-10") class DefI18nSource implements I18nSource { /** * UTF8 resource bundle control. * * Source: Source: https://stackoverflow.com/questions/4659929 * * @author edgar * * @deprecated com.github.jknack.handlebars.helper package is deprecated and marked for removal in subsequent releases which will involve removal of the handlebars dependency in AEM. */ @Deprecated(since = "2024-07-10") public static class UTF8Control extends ResourceBundle.Control { /** * Charset. */ private final Charset charset; /** * Creates a new utf8 control. * * @param charset Charset. */ UTF8Control(final Charset charset) { this.charset = charset; } @Override public ResourceBundle newBundle(final String baseName, final Locale locale, final String format, final ClassLoader loader, final boolean reload) throws IOException { // The below is a copy of the default implementation. String bundleName = toBundleName(baseName, locale); String resourceName = toResourceName(bundleName, "properties"); InputStream stream = null; try { stream = loader.getResourceAsStream(resourceName); PropertyResourceBundle bundle = new PropertyResourceBundle(new InputStreamReader(stream, charset)); return bundle; } finally { if (stream != null) { stream.close(); } } } } /** * The resource bundle. */ private ResourceBundle bundle; /** * Creates a new {@link DefI18nSource}. * * @param charset Charset to use. * @param baseName The base name. * @param locale The locale. * @param classLoader The classloader. */ DefI18nSource(final Charset charset, final String baseName, final Locale locale, final ClassLoader classLoader) { bundle = ResourceBundle.getBundle(baseName, locale, classLoader, new UTF8Control(charset)); } @Override public String[] keys(final String basename, final Locale locale) { Enumeration keys = bundle.getKeys(); List result = new ArrayList<>(); while (keys.hasMoreElements()) { String key = keys.nextElement(); result.add(key); } return result.toArray(new String[result.size()]); } @Override public String message(final String key, final Locale locale, final Object... args) { isTrue(bundle.containsKey(key), "no message found: '%s' for locale '%s'.", key, locale); String message = bundle.getString(key); if (args.length == 0) { return message; } MessageFormat format = new MessageFormat(message, locale); return format.format(args); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy