// // Copyright (c) 2011 Linkeos. // // This file is part of Elveos.org. // Elveos.org is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation, either version 3 of the License, or (at your // option) any later version. // // Elveos.org is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // You should have received a copy of the GNU General Public License along // with Elveos.org. If not, see http://www.gnu.org/licenses/. // package com.bloatit.framework.utils.i18n; import java.math.BigDecimal; import java.text.NumberFormat; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.xnap.commons.i18n.I18n; import org.xnap.commons.i18n.I18nFactory; import com.bloatit.framework.exceptions.highlevel.BadProgrammerException; import com.bloatit.framework.utils.IpLocator; import com.bloatit.framework.webprocessor.annotations.Translator; import com.bloatit.framework.xcgiserver.AvailableLocales; import com.bloatit.framework.xcgiserver.HttpHeader; /** * <p> * Class that encapsulates all translation tools * </p> * <p> * Tools provided are : * <li>All static translation tools (for the UI) implemented with gettext</li> * <li>All Dynamic translation tools (for the content), mostly for dates, * currencies and time</li> * <p> * Class is immutable, if you need to change locale, create a new object * </p> * </p> */ public final class Localizator implements Translator { /** Default user locale */ private static final Locale DEFAULT_LOCALE = new Locale("en", "US"); /** Translation cache */ private static final Map<String, I18n> localesCache = Collections.synchronizedMap(new HashMap<String, I18n>()); static { // By default, the Java default language is used as a fallback for // gettext. // We override this behavior by setting the default locale to english Locale.setDefault(new Locale("en", "US")); } private Locale locale; private I18n i18n; public Localizator(final Locale loc) { this.locale = loc; this.i18n = getI18n(locale); } public Localizator(HttpHeader header) { String language; String country; String ccode = IpLocator.localize(header.getRemoteAddr()).code; if (AvailableLocales.getAvailableCountries().containsKey(ccode)) { country = ccode; } else { country = header.getHttpAcceptLanguage().getPreferedLocal().getCountry(); } if (header.getPageLanguage().equals(HttpHeader.DEFAULT_LANG)) { language = header.getHttpAcceptLanguage().getPreferedLocal().getLanguage(); } else { language = header.getPageLanguage(); } this.locale = new Locale(language, country); this.i18n = getI18n(locale); } /** * Force the language part of a locale to a specific one */ public void forceLanguage(final Locale language) { locale = new Locale(language.getLanguage(), locale.getCountry()); this.i18n = getI18n(locale); } /** * Force the locale to a specific locale */ public void forceLocale(final Locale loc) { locale = loc; this.i18n = getI18n(locale); } /** * Returns the Locale for the localizator * * @return the locale */ public Locale getLocale() { return locale; } /** * @return the ISO code for the language */ public String getCountryCode() { return locale.getCountry(); } // //////////////////// // Working on the language part of the locale /** * @return the ISO code for the language */ public String getLanguageCode() { return locale.getLanguage(); } // //////////////////////// // Some utility functions /** * Returns a DateLocale encapsulating the java date Use to display any date */ public DateLocale getDate(final Date date) { return new DateLocale(date, locale); } /** * Returns a CurrencyLocale to work on <code>euroAmount</code> */ public CurrencyLocale getCurrency(final BigDecimal euroAmount) { try { return new CurrencyLocale(euroAmount, locale); } catch (final CurrencyNotAvailableException e) { try { return new CurrencyLocale(euroAmount, DEFAULT_LOCALE); } catch (final CurrencyNotAvailableException e1) { throw new BadProgrammerException("Fallback locale for currency " + DEFAULT_LOCALE.getLanguage() + "_" + DEFAULT_LOCALE.getCountry() + "not available", e); } } } public NumberFormat getNumberFormat() { return NumberFormat.getInstance(getLocale()); } // ///////////////////////////////////////////// // TR system (gnu gettext) // //////////////////////////////////////////// /** * <p> * Translates a constant String * </p> * <p> * Returns <code>toTranslate</code> translated into the currently selected * language. Every user-visible string in the program must be wrapped into * this function * </p> * * @param toTranslate the string to translate * @return the translated string */ @Override public String tr(final String toTranslate) { return correctTr(i18n.tr(toTranslate)); } /** * <p> * Translates a parametered constant string * </p> * <p> * In <b>MOST CASES</b> the plural handling version should be used : see * {@link #trn(String, String, long, Object...)} * </p> * <p> * Returns <code>toTranslate</code> and replaces the string with the * parameters * </p> * <p> * One example : * <p> * <code>i18n.tr("foo {0} bar", new Integer(1024)));<br> //Will print * "foo 1024 bar"</code> * </p> * For more examples see : * {@link "http://code.google.com/p/gettext-commons/wiki/Tutorial"} </p> * * @param toTranslate the String to translate * @param parameters the list of parameters that will be inserted into the * string * @return the translated String * @see #tr(String) * @see #trn(String, String, long, Object...) * @see org.slf4j.helpers.MessageFormatter */ public String tr(final String toTranslate, final Object... parameters) { return correctTr(i18n.tr(toTranslate, parameters)); } /** * <p> * Translates a constant string using plural * </p> * <p> * Example : * <p> * <code>System.out.println(i18n.trn("Copied file.", "Copied files.", 4));<br> * <code>//will print "Copied files."</code> * </p> * <p> * <code>System.out.println(i18n.trn("Copied file.", "Copied files.", 4));<br> // will * print "Copied files."</code> * </p> * </p> * * @param singular The singular version of the displayed string * @param plural the plural version of the displayed string * @param amount the <i>amount</i> of elements, 0 or 1 will be singular, >1 * will be plural * @return the translated <i>singular</i> or <i>plural</i> string depending * on value of <code>amount</code> * @see #tr(String) */ public String trn(final String singular, final String plural, final long amount) { if (locale.getLanguage().equals("fr")) { // In french, 0 use the singular return correctTr(i18n.trn(singular, plural, (amount > 1 ? amount : 1))); } return correctTr(i18n.trn(singular, plural, amount)); } /** * <p> * Translates a parametered-constant string, and handles plural * </p> * <p> * Uses * {@link org.slf4j.helpers.MessageFormatter#format(String, Object, Object)} * to format * </p> * <p> * Example <br> * <code>System.out.println(i18n.trn("Night {0} of 1001", * "More than 1001 nights! {0} already!", 1002, new Integer(1024)));<br> // Will print * "More than 1001 nights! 1024 already!"</code> * </p> * <p> * For more examples see : * {@link "http://code.google.com/p/gettext-commons/wiki/Tutorial"} * </p> * * @param singular The singular string * @param plural the plural string * @param amount the <i>amount</i> of elements, 0 or 1 will be singular, >1 * will be plural * @param parameters the list of parameters that will be replaced into the * String * @return the translated <i>singular</i> or <i>plural</i> string depending * on value of <code>amount</code>, with the <code>parameters</code> * inserted. * @see #trn(String, String, long) * @see org.slf4j.helpers.MessageFormatter */ public String trn(final String singular, final String plural, final long amount, final Object... parameters) { if (locale.getLanguage().equals("fr")) { // In french, 0 use the singular return correctTr(i18n.trn(singular, plural, (amount > 1 ? amount : 1), parameters)); } return correctTr(i18n.trn(singular, plural, amount, parameters)); } /** * Disambiguates translation keys. * <p> * Sometimes it is necessary to provide different translations of the same * word as some words may have multiple meanings in the native language the * program is written but not in other languages. * </p> * <p> * Example <br> * <code> * System.out.println(i18n.trc("chat (verb)", "chat"));<br> * System.out.println(i18n.trc("chat (noun)", "chat"));</code> * </p> * <p> * For more examples see : * {@link "http://code.google.com/p/gettext-commons/wiki/Tutorial"} * </p> * * @param context the context of the text to be translated * @param text the ambiguous key message in the source locale * @return <code>text</code> if the locale of the underlying resource bundle * equals the source code locale, the disambiguated translation of * <code>text</code> otherwise */ public String trc(final String context, final String text) { return correctTr(i18n.trc(context, text)); } /** * Corrects the translated string and make it ready for html * * @param translation the translated string * @return the string ready to be inputed in Html */ private String correctTr(final String translation) { return translation.replaceAll(" ", " "); } private I18n getI18n(final Locale locale) { if (localesCache.containsKey(locale)) { return localesCache.get(locale); } final I18n newI18n = I18nFactory.getI18n(Localizator.class, "i18n.Messages", locale); localesCache.put(locale.getLanguage(), newI18n); return newI18n; } }