I18n-commits
Threads by month
- ----- 2026 -----
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- 802 discussions
r1702 - in trunk/nuiton-i18n-api: . src/main/java/org/nuiton/i18n src/main/java/org/nuiton/i18n/bundle src/main/java/org/nuiton/i18n/init src/main/java/org/nuiton/util src/test/java/org/nuiton/i18n src/test/java/org/nuiton/i18n/bundle src/test/java/org/nuiton/util src/test/resources src/test/resources/META-INF
by tchemit@users.nuiton.org 07 Mar '10
by tchemit@users.nuiton.org 07 Mar '10
07 Mar '10
Author: tchemit
Date: 2010-03-07 17:02:33 +0100 (Sun, 07 Mar 2010)
New Revision: 1702
Log:
- reformat code (80 car per line max)
- Evolution #343: Integrates class org.nuiton.i18n.I18n from nuiton-utils
- Evolution #344: Introduces I18nInitializer api
Added:
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18n.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nBundleBridge.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nLanguage.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nStore.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleUtil.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/ClassPathI18nInitializer.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/DefaultI18nInitializer.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/I18nInitializer.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/UserI18nInitializer.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/package.html
trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/I18nStoreTest.java
trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBundleScopeTest.java
trunk/nuiton-i18n-api/src/test/resources/META-INF/
trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-definition.properties
trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-en_GB.properties
trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-fr_FR.properties
Removed:
trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java
Modified:
trunk/nuiton-i18n-api/pom.xml
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/CountryEnum.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFileReader.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nUtil.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/LanguageEnum.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java
trunk/nuiton-i18n-api/src/main/java/org/nuiton/util/LocaleConverter.java
trunk/nuiton-i18n-api/src/test/java/org/nuiton/util/LocaleConverterTest.java
trunk/nuiton-i18n-api/src/test/resources/log4j.properties
Modified: trunk/nuiton-i18n-api/pom.xml
===================================================================
--- trunk/nuiton-i18n-api/pom.xml 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/pom.xml 2010-03-07 16:02:33 UTC (rev 1702)
@@ -10,7 +10,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>i18n</artifactId>
- <version>1.0.2-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</parent>
<groupId>org.nuiton.i18n</groupId>
@@ -29,10 +29,21 @@
</dependency>
<dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/CountryEnum.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/CountryEnum.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/CountryEnum.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,7 +1,7 @@
/*
* *##%
* I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
+ * Copyright (C) 2004 - 2010 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -16,20 +16,23 @@
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
- * ##%* */
+ * ##%*
+ */
package org.nuiton.i18n;
/**
* Une énumération pour représenter le pays d'une locale
* <p/>
- * <a href="http://www.iso.org/iso/country_codes"><code>ISO 3166-1:1998 (ICS n° 01.140.20)</code></a>.
+ * <a href="http://www.iso.org/iso/country_codes"><code>ISO 3166-1:1998 (ICS n°
+ * 01.140.20)</code></a>.
* <p/>
- * <a href="http://www.iso.org/iso/french_country_names_and_code_elements">la liste des codes</a>
+ * <a href="http://www.iso.org/iso/french_country_names_and_code_elements">la
+ * liste des codes</a>
* <p/>
* Chaque pays est repésenté ainsi :
* <pre>A2, // A3 Number Country name</pre>
*
- * @author chemit
+ * @author tchemit <chemit(a)codelutin.com>
*/
public enum CountryEnum {
@@ -272,14 +275,17 @@
ZM, // ZMB 894 ZAMBIA
ZW; // ZWE 716 ZIMBABWE
- public static CountryEnum valueOf(String country, CountryEnum defaultValue) {
+ public static CountryEnum valueOf(String country,
+ CountryEnum defaultValue) {
CountryEnum countryValue = null;
try {
- countryValue = CountryEnum.valueOf(country.toUpperCase());
+ countryValue = valueOf(country.toUpperCase());
} catch (IllegalArgumentException e) {
- System.err.println("unfound country " + country + ", will use default one : " + defaultValue);
+ System.err.println("unfound country " + country +
+ ", will use default one : " + defaultValue);
} catch (NullPointerException e) {
- System.err.println("unfound country " + country + ", will use default one : " + defaultValue);
+ System.err.println("unfound country " + country +
+ ", will use default one : " + defaultValue);
}
return countryValue == null ? defaultValue : countryValue;
}
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18n.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18n.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18n.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,345 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.init.ClassPathI18nInitializer;
+import org.nuiton.i18n.init.DefaultI18nInitializer;
+import org.nuiton.i18n.init.I18nInitializer;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * New generation I18n class.
+ * <p/>
+ * <b>Note:</b> This class replace the previous one in project {@code
+ * nuiton-utils}.
+ * <p/>
+ * This class is a facility for internationalization. To use it in your soft,
+ * you can either : <ul> <li> import the org.nuiton.i18n.I18n class, <li> init
+ * the translation support with the init(String language) or init(String
+ * language, String country), init(Localelocale) static methods in your main, (
+ * eg: I18n.init("fr","FR") ) <li> call the translate static method for each
+ * sentence, ( eg: I18n._("hello you !") ) <li> create a resource file for each
+ * language following the naming convention given in the
+ * java.util.ResourceBundle javadoc and translate all the sentence. </ul>
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class I18n {
+
+ /** Logger */
+ private static final Log log = LogFactory.getLog(I18n.class);
+
+ public static final String ISO_8859_1_ENCONDING = "ISO-8859-1";
+
+ public static final String UTF_8_ENCONDING = "UTF-8";
+
+ public static final String DEFAULT_ENCODING = ISO_8859_1_ENCONDING;
+
+ public static final Locale DEFAULT_LOCALE = Locale.UK;
+
+ /** I18n initializer */
+ protected static I18nInitializer initializer;
+
+ /** I18n store of languages */
+ protected static I18nStore store;
+
+ /** Filtre a appliquer avant de retourner les chaines */
+ protected static I18nFilter filter;
+
+ /**
+ * Sets the initializer to use to init the I18n system.
+ * <p/>
+ * You must inovke this method before any invocation of methods {@code
+ * init(XXX)}.
+ *
+ * @param initializer the initializer to set.
+ * @see DefaultI18nInitializer
+ */
+ public static void setInitializer(I18nInitializer initializer) {
+ I18n.initializer = initializer;
+ }
+
+ /**
+ * Change le filtre des chaines traduites
+ *
+ * @param filter l'objet filtre a utiliser
+ */
+ public static void setFilter(I18nFilter filter) {
+ I18n.filter = filter;
+ }
+
+ /** Initialise la librairie avec encoding par defaut et locale par defaut */
+ public static void init() {
+ init(null);
+ }
+
+ /**
+ * Initialise la librairie
+ *
+ * @param language une chaine representant la langue à utiliser fr, en, ...
+ * @param country une chaine representant le pays à utiliser FR, GB, ...
+ */
+ public static void init(String language, String country) {
+ init(I18nUtil.newLocale(language, country));
+ }
+
+ /**
+ * Initialize the library for given <code>locale</code> with {@link
+ * #DEFAULT_ENCODING}.
+ *
+ * @param locale language to use
+ */
+ public static void init(Locale locale) {
+ if (initializer == null) {
+ initializer = new ClassPathI18nInitializer();
+ }
+
+ if (locale == null) {
+ // use default locale
+ locale = I18nUtil.newLocale(null, null);
+ }
+
+ // let the store use this locale
+ getStore().setLanguage(locale);
+ }
+
+ /**
+ * Retourne la chaine traduite si possible.
+ *
+ * @param message la chaine a traduire
+ * @return la traduction si possible ou la chaine passee en parametre
+ * sinon.
+ */
+ public static String _(String message) {
+
+ // if the key to translate is null, just return null
+ if (message == null) {
+ return null;
+ }
+
+ I18nLanguage language = getCurrentLanguage();
+ String result = message;
+ if (language != null) {
+ result = language.translate(message);
+ }
+ return applyFilter(result);
+ }
+
+ /**
+ * Retourne la chaine traduite si possible.
+ *
+ * @param message message formate avec la meme syntaxe que {@link
+ * String#format}
+ * @param args les parametres pour le message.
+ * @return la traduction si possible ou la chaine passee en parametre
+ * sinon.
+ */
+ public static String _(String message, Object... args) {
+
+ // if the key to translate is null, just return null
+ if (message == null) {
+ return null;
+ }
+
+ I18nLanguage language = getCurrentLanguage();
+ String result = message;
+ if (language != null) {
+ result = language.translate(message);
+ }
+ try {
+ return applyFilter(String.format(result, args));
+ } catch (Exception eee) {
+ try {
+ return applyFilter(String.format(message, args));
+ } catch (Exception zzz) {
+ log.warn(
+ _("nuitonutil.error.i18n.untranslated.message", message),
+ zzz);
+ return applyFilter(message);
+ }
+ }
+ }
+
+ /**
+ * Retourne la chaine passée en argument.
+ * <p/>
+ * Utile surtout pour collecter les chaines et ne pas les traduires à leur
+ * apparition.
+ * <p/>
+ * Par exemple :
+ * <pre>String key = "nuitonutils.key";
+ * String result = _(key)</pre>
+ * fonctionnera, mais la chaine n'aura pas été marquée comme devant être
+ * internationalisé.
+ * <p/>
+ * Tres utile par exemple, pour crée des objets non internationnalisé, et
+ * devant être traduit seulement à leur lecture suivant la locale du lecteur
+ * et non du créateur.
+ *
+ * @param message message formate avec la meme syntaxe que {@link
+ * String#format(String, Object...)}
+ * @param args les parametres pour le message.
+ * @return le message passe en argument mais formatté avec les parametres
+ */
+ public static String n_(String message, Object... args) {
+ try {
+ return String.format(message, args);
+ } catch (Exception eee) {
+ log.warn(
+ _("nuitonutil.error.i18n.unformated.message", message, Arrays.toString(args)),
+ eee);
+ return message;
+ }
+ }
+
+ /**
+ * Retourne la chaine passé en argument.
+ * <p/>
+ * Utile surtout pour collecter les chaines et ne pas les traduires à leur
+ * apparition.
+ * <p/>
+ * Par exemple :
+ * <pre>String key = "nuitonutils.key";
+ * String result = _(key)</pre>
+ * fonctionnera, mais la chaine n'aura pas été marquée comme devant être
+ * internationalisé.
+ * <p/>
+ * Tres utile par exemple, pour crée des objets non internationnalisé, et
+ * devant être traduit seulement à leur lecture suivant la locale du lecteur
+ * et non du créateur.
+ *
+ * @param message la chaine à traduire
+ * @return la chaine passée en argument.
+ */
+ public static String n_(String message) {
+ return message;
+ }
+
+ /**
+ * close i18n caches, says the store if exists
+ * <p/>
+ * This method should be called to reset all caches (languages,
+ * bundles,...)
+ */
+ public static void close() {
+ if (store != null) {
+ store.close();
+ store = null;
+ }
+ }
+
+ public static I18nInitializer getInitializer() {
+ return initializer;
+ }
+
+ /**
+ * Get the i18n store.
+ * <p/>
+ * If store is not init, then instanciate it.
+ *
+ * @return the instanciated i18n store
+ */
+ public static synchronized I18nStore getStore() {
+
+ if (store == null) {
+ store = new I18nStore(DEFAULT_LOCALE, initializer);
+ }
+ return store;
+ }
+
+ /**
+ * Applique le filtre s'il y en a un
+ *
+ * @param message le message qui devrait etre retourne avant application du
+ * filtre.
+ * @return le message filtre
+ */
+ protected static String applyFilter(String message) {
+ if (getFilter() != null) {
+ return getFilter().applyFilter(message);
+ }
+ return message;
+ }
+
+ /**
+ * @return the current language of the store, or {@code null} if store is
+ * not init.
+ */
+ protected static I18nLanguage getCurrentLanguage() {
+ I18nLanguage language = store == null ? null : store.getLanguage();
+ return language;
+ }
+
+ protected static I18nFilter getFilter() {
+ return filter;
+ }
+
+ /**
+ * @return the current store
+ * @deprecated since 1.1, only keep for compatibility
+ */
+ @Deprecated
+ public static I18nStore getLoader() {
+ return getStore();
+ }
+
+ /**
+ * Parse a list of {@link Locale} seperated by comma.
+ * <p/>
+ * Example : fr_FR,en_GB
+ *
+ * @param str the string representation of locale separated by comma
+ * @return list of available locales
+ * @throws IllegalArgumentException ia a locale is not valid
+ * @deprecated since 1.1, prefer use the {@link I18nUtil#parseLocales(String)}
+ */
+ @Deprecated
+ public static Locale[] parseLocales(String str)
+ throws IllegalArgumentException {
+ return I18nUtil.parseLocales(str);
+ }
+
+ /**
+ * @param str the text representation of the locale
+ * @return the locale, or default locale if could not parse the given one
+ * @deprecated since 1.1, prefer use the {@link I18nUtil#newLocale(String)}
+ */
+ @Deprecated
+ public static Locale newLocale(String str) {
+ return I18nUtil.newLocale(str);
+ }
+
+ /**
+ * @param language the language of the locale
+ * @param country the country of the locale
+ * @return the required locale
+ * @deprecated since 1.1, prefer use the {@link I18nUtil#newLocale(String,String)}
+ */
+ @Deprecated
+ public static Locale newLocale(String language, String country) {
+ return I18nUtil.newLocale(language, country);
+ }
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18n.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nBundleBridge.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nBundleBridge.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nBundleBridge.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,51 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+
+/* *
+* BundleBridge.java
+*
+* Created: 6 sept. 06
+*
+* @author Arnaud Thimel <thimel(a)codelutin.com>
+* @version $Revision$
+*
+* Mise a jour: $Date$
+* par : $Author: tchemit $
+*/
+
+package org.nuiton.i18n;
+
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+
+public class I18nBundleBridge extends ResourceBundle {
+
+ @Override
+ public Enumeration<String> getKeys() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object handleGetObject(String key) {
+ return I18n._(key);
+ }
+
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nBundleBridge.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFileReader.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFileReader.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nFileReader.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -31,40 +31,48 @@
package org.nuiton.i18n;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.io.*;
import java.nio.charset.Charset;
import java.util.Properties;
import java.util.regex.Pattern;
-/** Classe assurant la lecture et les possibles traitement nécessaires à I18n. */
+/**
+ * Classe assurant la lecture et les possibles traitement nécessaires à I18n.
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @deprecated since 1.1, no more use of this class.
+ */
+@Deprecated
public class I18nFileReader extends Properties {
protected static final Pattern commentPattern = Pattern.compile("[^\\\\]#");
protected static final Pattern splitPattern = Pattern.compile("[^\\\\]=");
+
private static final long serialVersionUID = 3611718334066783394L;
public void load(InputStream inStream, String encodingTo) throws IOException {
Charset charsetTo = Charset.forName(encodingTo);
- BufferedReader readerFile;
- readerFile = new BufferedReader(new InputStreamReader(inStream, charsetTo));
- String lineFile;
- StringBuilder builderFile;
- builderFile = new StringBuilder();
- while ((lineFile = readerFile.readLine()) != null) {
- builderFile.append(lineFile).append('\n');
+ BufferedReader readerFile =
+ new BufferedReader(new InputStreamReader(inStream, charsetTo));
+ try {
+ String lineFile;
+ StringBuilder builderFile;
+ builderFile = new StringBuilder();
+ while ((lineFile = readerFile.readLine()) != null) {
+ builderFile.append(lineFile).append('\n');
+ }
+ load(new ByteArrayInputStream(builderFile.toString().getBytes()));
+ } finally {
+ readerFile.close();
}
- readerFile.close();
- super.load(new ByteArrayInputStream(builderFile.toString().getBytes()));
+// super.load(new ByteArrayInputStream(builderFile.toString().getBytes()));
}
protected String interpretBackslashes(String message) {
int backslashIndex = -1;
- while ((backslashIndex = message.indexOf("\\", backslashIndex + 1)) != -1) {
+ while ((backslashIndex =
+ message.indexOf("\\", backslashIndex + 1)) != -1) {
if (message.length() >= backslashIndex + 1) {
char charNextToBackslash = message.charAt(backslashIndex + 1);
char replacementChar;
@@ -91,7 +99,9 @@
replacementChar = '\\';
break;
}
- message = message.substring(0, backslashIndex) + replacementChar + message.substring(backslashIndex + 2);
+ message = message.substring(0, backslashIndex) +
+ replacementChar +
+ message.substring(backslashIndex + 2);
}
}
return message;
@@ -124,7 +134,8 @@
replacementString = "\\:";
break;
}
- message = message.substring(0, charIndex) + replacementString + message.substring(charIndex + 1);
+ message = message.substring(0, charIndex) + replacementString +
+ message.substring(charIndex + 1);
}
}
return message;
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nLanguage.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nLanguage.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nLanguage.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,253 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.bundle.I18nBundleEntry;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.Properties;
+
+/**
+ * Represents a language in i18n system with all his translations.
+ * <p/>
+ * To obtain a translation, use the method {@link #translate(String)}
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class I18nLanguage {
+
+ /** to use log facility, just put in your code: log.info(\"...\"); */
+ private static final Log log = LogFactory.getLog(I18nLanguage.class);
+
+ /** toutes les traductions pour cette langue */
+ protected Properties resource;
+
+ /** la locale de la langue */
+ protected final Locale locale;
+
+ /** Indique le chemin du fichier dans lequel ecrire les entrees non trouvees */
+ protected String recordFilePath;
+
+ public I18nLanguage(Locale l) {
+ this(l, null);
+ }
+
+ public I18nLanguage(Locale locale, String recordFilePath) {
+ this.locale = locale;
+ this.recordFilePath = recordFilePath;
+ }
+
+ /**
+ * charge les traductions de la languea partir d'une liste donnee de
+ * fichiers de traduction.
+ *
+ * @param bundleEntries the used bundles entries to load
+ */
+ public void load(I18nBundleEntry[] bundleEntries) {
+
+ // use a recursive properties
+ // inspired from nuiton-utils:org.nuiton.util.RecursiveProperties
+ // thanks to Arnaud Thimel
+ resource = new Properties() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public String getProperty(String key) {
+ String result = super.getProperty(key);
+ if (result == null) {
+ return null;
+ }
+ //Ex : result="My name is ${myName}."
+ int pos = result.indexOf("${", 0);
+ //Ex : pos=11
+ while (pos != -1) {
+ int posEnd = result.indexOf("}", pos + 1);
+ //Ex : posEnd=19
+ if (posEnd != -1) {
+ String value =
+ getProperty(result.substring(pos + 2, posEnd));
+ // Ex : getProperty("myName");
+ if (value != null) {
+ // Ex : value="Thimel"
+ result = result.substring(0, pos) + value +
+ result.substring(posEnd + 1);
+ // Ex : result="My name is " + "Thimel" + "."
+ pos = result.indexOf("${", pos + value.length());
+ // Ex : pos=-1
+ } else {
+ // Ex : value=null
+ pos = result.indexOf("${", posEnd + 1);
+ // Ex : pos=-1
+ }
+ // Ex : pos=-1
+ }
+ }
+ return result;
+ }
+ };
+
+ // load resources
+
+ try {
+ for (I18nBundleEntry e : bundleEntries) {
+ e.load(resource);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Translate takes a sentence and returns its translation if found, the very
+ * same string otherwise.
+ *
+ * @param sentence sentence to translate
+ * @return translated sentence
+ */
+ public String translate(String sentence) {
+ if (resource == null) {
+ recordNotFound(sentence);
+ return sentence;
+ }
+ try {
+ String result = resource.getProperty(sentence);
+ if (null != result && !"".equals(result)) {
+ return result;
+ }
+ recordNotFound(sentence);
+ return sentence;
+ } catch (MissingResourceException eee) {
+ log.warn("Resource " + sentence + " unavailable", eee);
+ return sentence;
+ } catch (Exception eee) {
+ log.error("Unexpected error while translating : ", eee);
+ return sentence;
+ }
+ }
+
+ protected void recordNotFound(String key) {
+ if (recordFilePath != null && key != null && !"".equals(key)) {
+ File f = new File(recordFilePath);
+ Properties recordProps = new Properties();
+ try {
+ if (f.exists()) {
+ FileInputStream fis = new FileInputStream(f);
+ try {
+ recordProps.load(fis);
+ } finally {
+ fis.close();
+ }
+ }
+ recordProps.put(key, "");
+ FileOutputStream fos = new FileOutputStream(f);
+ try {
+ recordProps.store(fos, "Adding the key : " + key);
+ } finally {
+ fos.close();
+ }
+ } catch (FileNotFoundException e) {
+ if (log.isErrorEnabled()) {
+ log.error(e);
+ }
+ } catch (IOException e) {
+ if (log.isErrorEnabled()) {
+ log.error(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Untranslate takes a translated sentence and returns the original one if
+ * found, the very same string otherwise.
+ *
+ * @param sentence sentence to untranslate
+ * @return untranslated sentence
+ */
+ public String untranslate(String sentence) {
+ if (resource == null) {
+ return sentence;
+ }
+ try {
+ Enumeration<?> e = resource.propertyNames();
+ // Look for the given sentence through all translations
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ String translation = resource.getProperty(key);
+ // If found returns the corresponding key
+ if (sentence.equals(translation)) {
+ return key;
+ }
+ }
+ } catch (MissingResourceException eee) {
+ // Well, this can't happen...
+ }
+ // No such translated sentence in our resourceBundle
+ return sentence;
+ }
+
+ public Locale getLocale() {
+ return locale;
+ }
+
+ public int size() {
+ return resource == null ? 0 : resource.size();
+ }
+
+ public void close() {
+ if (resource != null) {
+ if (log.isInfoEnabled()) {
+ log.info("closing " + this);
+ }
+ resource.clear();
+ resource = null;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ close();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return this == o ||
+ o instanceof I18nLanguage &&
+ locale.equals(((I18nLanguage) o).locale);
+ }
+
+ @Override
+ public int hashCode() {
+ return locale.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "I18nLanguage <locale: " + locale + ", nbStences:" + size() + '>';
+ }
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nLanguage.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nStore.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nStore.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nStore.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,230 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.bundle.I18nBundle;
+import org.nuiton.i18n.bundle.I18nBundleEntry;
+import org.nuiton.i18n.bundle.I18nBundleUtil;
+import org.nuiton.i18n.init.I18nInitializer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Represents the store of languages of the i18n system.
+ * <p/>
+ * Replace the {@code org.nuiton.i18n.I18nLoader} class from project
+ * http://maven-site.nuiton.org/nuiton-utils
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class I18nStore {
+
+ /** Logger */
+ private static final Log log = LogFactory.getLog(I18nStore.class);
+
+ /** le language actuellement utilise */
+ protected I18nLanguage language;
+
+ /** le cache de languages deja charges */
+ protected List<I18nLanguage> languages;
+
+ /** le cache de bundles deja charges */
+ protected I18nBundle[] bundles;
+
+ /** la locale par defaut a utiliser */
+ protected Locale defaultLocale;
+
+ /** le resolver de bundles */
+ protected I18nInitializer resolver;
+
+ public I18nStore(Locale defaultLocale, I18nInitializer resolver) {
+ this.defaultLocale = defaultLocale;
+ this.resolver = resolver;
+ }
+
+ /** @return current language loaded or null, if no language was load */
+ public I18nLanguage getLanguage() {
+ return language;
+ }
+
+ /** @return le cache de language avec instanciation paresseuse */
+ public List<I18nLanguage> getLanguages() {
+ if (languages == null) {
+ languages = new ArrayList<I18nLanguage>();
+ }
+ return languages;
+ }
+
+ public Locale getDefaultLocale() {
+ return defaultLocale;
+ }
+
+ public boolean isEmpty() {
+ boolean isEmpty = I18nBundleUtil.isEmpty(getBundles());
+ return isEmpty;
+ }
+
+ /** @return array of all locales loaded */
+ public Locale[] getLocales() {
+ Locale[] result = I18nBundleUtil.getLocales(getBundles());
+ return result;
+ }
+
+ public I18nBundle[] getBundles() {
+ checkInit();
+ return bundles;
+ }
+
+ public I18nBundle[] getBundles(Locale l) {
+ I18nBundle[] result = I18nBundleUtil.getBundles(l, getBundles());
+ return result;
+ }
+
+ public I18nBundleEntry[] getBundleEntries() {
+ I18nBundleEntry[] result = I18nBundleUtil.getBundleEntries(getBundles());
+ return result;
+ }
+
+ public I18nBundleEntry[] getBundleEntries(Locale l) {
+ I18nBundleEntry[] result =
+ I18nBundleUtil.getBundleEntries(l, getDefaultLocale(),
+ getBundles());
+ return result;
+ }
+
+ protected void init() {
+
+ if (isInit()) {
+ // already init
+ return;
+ }
+
+ if (resolver == null) {
+ throw new NullPointerException(
+ "resolver can not be null in " +
+ "org.nuiton.i18n.I18nStore.init method");
+ }
+
+ try {
+ bundles = resolver.resolvBundles();
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Could not init store for reason " + e.getMessage(), e);
+ }
+
+ if (log.isInfoEnabled()) {
+ log.info(bundles.length + " bundle(s) found, [" +
+ getBundleEntries().length + " file(s)].");
+ }
+ }
+
+ /**
+ * Set a new language in store, given a locale.
+ *
+ * @param locale la locale du language requis
+ */
+ protected synchronized void setLanguage(Locale locale) {
+ init();
+ if (log.isDebugEnabled()) {
+ log.debug("locale: " + locale);
+ }
+ I18nLanguage result = getLanguage(locale);
+ if (result == null) {
+ result = addLanguage(locale);
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("using cached language : " + result);
+ }
+ }
+ language = result;
+ //TC-20090702 the selected langue is the default locale, usefull for
+ // objects dealing with the default locale (swing widgets,...)
+ Locale.setDefault(locale);
+ }
+
+ /**
+ * Close store and release cache ofg language.
+ * <p/>
+ * Current language will be also clean.
+ */
+ protected void close() {
+ if (languages != null) {
+ if (log.isInfoEnabled()) {
+ log.info("will close " + languages.size() + " language(s).");
+ }
+ for (I18nLanguage l : languages) {
+ l.close();
+ }
+ languages.clear();
+ languages = null;
+ }
+ if (bundles != null) {
+ bundles = null;
+ }
+ language = null;
+ }
+
+ /**
+ * @param locale la locale du language recherche
+ * @return le language trouve dans le cache, ou null.
+ */
+ protected I18nLanguage getLanguage(Locale locale) {
+
+ if (!(languages == null || languages.isEmpty())) {
+ for (I18nLanguage l : languages) {
+ if (locale.equals(l.getLocale())) {
+ return l;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected I18nLanguage addLanguage(Locale locale) {
+ I18nLanguage result;
+ result = new I18nLanguage(locale);
+ I18nBundleEntry[] entries = getBundleEntries(locale);
+ result.load(entries);
+
+ if (log.isInfoEnabled()) {
+ log.info(result + ", nbEntries: " + entries.length +
+ ", nbSentences: " + result.size() + ".");
+ }
+ getLanguages().add(result);
+ return result;
+ }
+
+ protected boolean isInit() {
+ return bundles != null;
+ }
+
+ protected void checkInit() {
+ if (!isInit()) {
+ throw new IllegalStateException(
+ "should call init method on " + I18nStore.class);
+ }
+ }
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nStore.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nUtil.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nUtil.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/I18nUtil.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,7 +1,7 @@
/*
* *##%
* I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
+ * Copyright (C) 2004 - 2010 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -20,52 +20,60 @@
*/
package org.nuiton.i18n;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.util.LocaleConverter;
+
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.net.MalformedURLException;
+import java.net.URISyntaxException;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.logging.Logger;
+import java.net.URLClassLoader;
+import java.util.*;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
import java.util.zip.ZipFile;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.nuiton.util.LocaleConverter;
-/**
- *
- * @author chemit
- */
+/** @author tchemit <chemit(a)codelutin.com> */
public class I18nUtil {
/** to use log facility, just put in your code: log.info(\"...\"); */
private static final Log log = LogFactory.getLog(I18nUtil.class);
+
public static final String ISO_8859_1_ENCONDING = "ISO-8859-1";
+
public static final String UTF_8_ENCONDING = "UTF-8";
+
public static final String DEFAULT_ENCODING = ISO_8859_1_ENCONDING;
+
public static final Locale DEFAULT_LOCALE = Locale.UK;
/**
* Parse a list of {@link Locale} seperated by comma.
- *
+ * <p/>
* Example : fr_FR,en_GB
*
* @param str the string representation of locale separated by comma
* @return list of available locales
* @throws IllegalArgumentException ia a locale is not valid
*/
- public static Locale[] parseLocales(String str) throws IllegalArgumentException {
- List<Locale> result = new java.util.ArrayList<Locale>();
+ public static Locale[] parseLocales(String str) throws
+ IllegalArgumentException {
+ List<Locale> result = new ArrayList<Locale>();
String[] bundlesToUse = str.split(",");
+ LocaleConverter converter = new LocaleConverter();
for (int i = 0, j = bundlesToUse.length; i < j; i++) {
String s = bundlesToUse[i].trim();
// on devrait verifier que le bundle existe
try {
- Locale l = (Locale) new LocaleConverter().convert(Locale.class, s);
+ Locale l = (Locale) converter.convert(Locale.class, s);
result.add(l);
} catch (Exception e) {
- throw new IllegalArgumentException("bundle " + s + " is not a valid locale,e");
+ throw new IllegalArgumentException("bundle " + s +
+ " is not a valid locale,e");
}
}
return result.toArray(new Locale[result.size()]);
@@ -79,7 +87,8 @@
try {
return (Locale) new LocaleConverter().convert(Locale.class, str);
} catch (Exception e) {
- Logger.getLogger("org.nuiton.i18n.I18n").warning("could not load locale '" + str + " for reason : " + e.getMessage());
+ log.warn("could not load locale '" + str + " for reason : " +
+ e.getMessage());
// use default locale
return DEFAULT_LOCALE;
}
@@ -88,8 +97,10 @@
public static Locale newLocale(String language, String country) {
if (language == null) {
// get user locale
- language = System.getProperty("user.language", DEFAULT_LOCALE.getLanguage());
- country = System.getProperty("user.country", DEFAULT_LOCALE.getCountry());
+ language = System.getProperty("user.language",
+ DEFAULT_LOCALE.getLanguage());
+ country = System.getProperty("user.country",
+ DEFAULT_LOCALE.getCountry());
}
return newLocale(language + (country == null ? "" : '_' + country));
}
@@ -99,12 +110,14 @@
*
* @param url the url to seek
* @param directory the directory to find
- * @return <code>true</code> if directory was found, <code>false</code> otherwise.
- * @throws java.io.IOException if any io pb
+ * @return {@code true} if directory was found, {@code false} otherwise.
+ * @throws IOException if any io pb
*/
- public static boolean containsDirectDirectory(URL url, String directory) throws IOException {
+ public static boolean containsDirectDirectory(URL url, String directory)
+ throws IOException {
String fileName = url.getFile();
- // TODO deal with encoding in windows, this is very durty, but it works...
+ // TODO deal with encoding in windows, this is very durty,
+ // TODO but it works...
File file = new File(fileName.replaceAll("%20", " "));
if (!file.exists()) {
return false;
@@ -163,12 +176,14 @@
* ordre ne doit être supposé sur les fichiers.
*
* @param repository repertoire dans lequel on recherche les fichiers
- * @param pattern le nom du fichier a extraire du fichier du repertoire doit
- * correspondre au pattern (repertoire + nom compris). si le
- * pattern est null, tous les fichiers trouvé sont retourné.
+ * @param pattern le nom du fichier a extraire du fichier du repertoire
+ * doit correspondre au pattern (repertoire + nom
+ * compris). si le pattern est null, tous les fichiers
+ * trouvé sont retourné.
* @return la liste des urls correspondant au pattern
*/
- static public List<URL> getURLsFromDirectory(File repository, String pattern) {
+ static public List<URL> getURLsFromDirectory(File repository,
+ String pattern) {
try {
if (log.isTraceEnabled()) {
log.trace("search '" + pattern + "' in " + repository);
@@ -184,19 +199,21 @@
String name = file.getAbsolutePath();
if (log.isTraceEnabled()) {
- log.trace("directory: " + repository + " name: " + name);
+ log.trace("directory: " + repository + " name: " +
+ name);
}
// cas de recursivite : repertoire dans un repertoire
if (file.exists() && file.isDirectory()) {
urlList.addAll(getURLsFromDirectory(file,
- pattern));
+ pattern));
// si le fichier du repertoire n'est pas un repertoire
// on verifie s'il correspond au pattern
} else if (pattern == null || name.matches(pattern)) {
URL url = file.toURI().toURL();
if (log.isTraceEnabled()) {
- log.trace("directory: " + repository + " url: " + url);
+ log.trace("directory: " + repository + " url: " +
+ url);
}
urlList.add(url);
}
@@ -207,8 +224,147 @@
}
return urlList;
} catch (MalformedURLException eee) {
- throw new IllegalArgumentException("Erreur lors de la conversion de l'url " + repository + " (pattern " + pattern + ") " + eee.getMessage(), eee);
+ throw new IllegalArgumentException(
+ "Erreur lors de la conversion de l'url " + repository +
+ " (pattern " + pattern + ") " + eee.getMessage(), eee);
//throw new ResourceException("Le fichier n'a pu être converti en URL", eee);
}
}
+
+ /**
+ * Returns the all urls to be used in a {@link URLClassLoader}.
+ * <p/>
+ * If classloader has only one url and the url is a jar, try to load in
+ * manifest class-path.
+ *
+ * @param loader the classloader (if null will use system one)
+ * @return all the url found in the classloader
+ */
+ static public URL[] getDeepURLs(URLClassLoader loader) {
+ Stack<URL> urlToTreate = new Stack<URL>();
+ List<URL> urlTreated = new ArrayList<URL>();
+
+ // first get the urls from classloader
+ URL[] result = getURLs(loader);
+
+ urlToTreate.addAll(Arrays.asList(result));
+ while (!urlToTreate.isEmpty()) {
+ URL currentUrl = urlToTreate.pop();
+ // save the url
+ urlTreated.add(currentUrl);
+ if (isJar(currentUrl.toString())) {
+ // jar invocation
+ try {
+ URL[] newArrayURLs =
+ getClassPathURLsFromJarManifest(
+ currentUrl);
+ if (newArrayURLs == null) {
+ continue;
+ }
+ List<URL> newURLs = Arrays.asList(newArrayURLs);
+ for (URL newURL : newURLs) {
+ if (!urlTreated.contains(newURL) &&
+ !urlToTreate.contains(newURL)) {
+ urlToTreate.add(newURL);
+ }
+ }
+ } catch (Exception e) {
+ if (log.isDebugEnabled()) {
+ // this is not a such error, but some jar can not be
+ log.debug("error with url" + currentUrl +
+ " for reason : " + e.getMessage());
+ }
+ }
+ }
+ }
+ return urlTreated.toArray(new URL[urlToTreate.size()]);
+ }
+
+ /**
+ * Recupere la liste des urls d'un {@link URLClassLoader}.
+ * <p/>
+ * Note : Un cas particulier est positionné pour JBoss qui utilise la method
+ * getAllURLs.
+ *
+ * @param classLoader le class loader a scanner
+ * @return les urls du classloade.
+ */
+ static public URL[] getURLs(URLClassLoader classLoader) {
+ if (classLoader == null) {
+ classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
+ }
+ Method m;
+ try {
+ // Essai de récupération de la méthode getAllURLs() de
+ // RepositoryClassLoader (JBoss)
+ m = classLoader.getClass().getMethod("getAllURLs");
+ } catch (Exception e) {
+ m = null;
+ }
+ URL[] result;
+ if (m == null) {
+ result = classLoader.getURLs();
+ } else {
+ try {
+ result = (URL[]) m.invoke(classLoader);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return result;
+ }
+
+ static public URL[] getClassPathURLsFromJarManifest(URL jarURL)
+ throws IOException, URISyntaxException {
+ JarFile jar = null;
+ URL[] result;
+ try {
+ String jarPath = jarURL.toURI().getPath();
+ File jarFile = new File(jarPath);
+ if (log.isDebugEnabled()) {
+ log.debug("class-path jar to scan " + jarPath);
+ }
+ jar = new JarFile(jarFile);
+ File container = jarFile.getParentFile();
+ Manifest mf = jar.getManifest();
+ String classPath = null;
+ if (mf != null && mf.getMainAttributes() != null) {
+ classPath = mf.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
+ }
+ String[] paths;
+ if (classPath != null) {
+ paths = classPath.split(" ");
+ } else {
+ paths = new String[0];
+ }
+ result = new URL[paths.length + 1];
+ result[0] = jarURL;
+ File path;
+ for (int i = 0; i < paths.length; i++) {
+ String s = paths[i];
+ // test de l'existence d'un protocole dans le path (genre file:...)
+ if (s.indexOf(':') != -1) {
+ result[i + 1] = new URL(s);
+ continue;
+ }
+
+ if (s.startsWith(".") || !s.startsWith("/")) {
+ // relative url
+ path = new File(container, s);
+ } else {
+ path = new File(s);
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(path);
+ }
+ result[i + 1] = path.toURI().toURL();
+ }
+ jar.close();
+ } finally {
+ if (jar != null) {
+ jar.close();
+ }
+ }
+ return result;
+ }
}
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/LanguageEnum.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/LanguageEnum.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/LanguageEnum.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,7 +1,7 @@
/*
* *##%
* I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
+ * Copyright (C) 2004 - 2010 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -16,16 +16,19 @@
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
- * ##%* */
+ * ##%*
+ */
package org.nuiton.i18n;
/**
* Une énumération pour représenter le langue d'une locale définie dans la norme
- * <a href="http://www.iso.org/iso/iso_catalogue/catalogue_ics/catalogue_detail_ics.htm…"><code>ISO 639-1:1998 (ICS n° 01.140.20)</code></a>.
+ * <a href="http://www.iso.org/iso/iso_catalogue/catalogue_ics/catalogue_detail_ics.htm…"><code>ISO
+ * 639-1:1998 (ICS n° 01.140.20)</code></a>.
* <p/>
- * <a href="http://www.loc.gov/standards/iso639-2/php/French_list.php">la liste des codes</a>
+ * <a href="http://www.loc.gov/standards/iso639-2/php/French_list.php">la liste
+ * des codes</a>
*
- * @author chemit
+ * @author tchemit <chemit(a)codelutin.com>
*/
public enum LanguageEnum {
@@ -169,14 +172,17 @@
zh, // Chinese
zu; // Zulu
- public static LanguageEnum valueOf(String language, LanguageEnum defaultValue) {
+ public static LanguageEnum valueOf(String language,
+ LanguageEnum defaultValue) {
LanguageEnum languageValue = null;
try {
- languageValue = LanguageEnum.valueOf(language.toLowerCase());
+ languageValue = valueOf(language.toLowerCase());
} catch (IllegalArgumentException e) {
- System.err.println("Unfound language " + language + ", will use default one " + defaultValue);
+ System.err.println("Unfound language " + language +
+ ", will use default one " + defaultValue);
} catch (NullPointerException e) {
- System.err.println("Unfound language " + language + ", will use default one " + defaultValue);
+ System.err.println("Unfound language " + language +
+ ", will use default one " + defaultValue);
}
return languageValue == null ? defaultValue : languageValue;
}
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundle.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,7 +1,7 @@
/*
-* *##%
+ * *##%
* I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
+ * Copyright (C) 2004 - 2010 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -16,43 +16,49 @@
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
- * ##%* */
+ * ##%*
+ */
package org.nuiton.i18n.bundle;
-import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
/**
* Class to represent a i18n Bundle.
* <p/>
- * A bundle is defined by a resource prefix (eg /tmp/bundle.properties), and a list of locale implemented entries.
+ * A bundle is defined by a resource prefix (eg /tmp/bundle.properties), and a
+ * list of locale implemented entries.
* <p/>
* The property {@link #bundlePrefix} is the equals order property.
* <p/>
* The property {@link #entries} contains all entries defined for this bundle.
* <p/>
- * The method {@link #getEntries(java.util.Locale)} filter entries for a given locale, including scope inclusive property.
+ * The method {@link #getEntries(Locale)} filter entries for a given locale,
+ * including scope inclusive property.
* <p/>
- * The method {@link #getEntries(I18nBundleScope)} filter entries for a givne scope, with no inclusive logi.
+ * The method {@link #getEntries(I18nBundleScope)} filter entries for a given
+ * scope, with no inclusive logi.
* <p/>
- * Thoses filter methods return result in the order defines in {@link I18nBundleEntry}, e.g
+ * Thoses filter methods return result in the order defines in {@link
+ * I18nBundleEntry}, e.g
* <pre>
* XXX.properties
* XXX-fr.properties
* XXX-fr_FR.properties
* </pre>
- * In that way, we can load resource in the good order : load before more general scope to more specialized.
+ * In that way, we can load resource in the good order : load before more
+ * general scope to more specialized.
*
- * @author chemit
+ * @author tchemit <chemit(a)codelutin.com>
* @see I18nBundleScope
* @see I18nBundleEntry
*/
-public class I18nBundle implements Iterable<I18nBundleEntry>{
+public class I18nBundle implements Iterable<I18nBundleEntry> {
/** to use log facility, just put in your code: log.info(\"...\"); */
static final Log log = LogFactory.getLog(I18nBundle.class);
@@ -77,7 +83,8 @@
* The order of result respect {@link I18nBundleEntry} order.
*
* @param locale the required locale
- * @return the array of entries matching extacly the locale or one of the lesser scope one.
+ * @return the array of entries matching extacly the locale or one of the
+ * lesser scope one.
*/
public I18nBundleEntry[] getEntries(Locale locale) {
I18nBundleScope scope = I18nBundleScope.valueOf(locale);
@@ -85,8 +92,11 @@
List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
for (I18nBundleEntry entry : entries) {
I18nBundleScope i18nBundleScope = entry.getScope();
- // load from general to the max scope and always if there is only one bundle entry found
- if ((i18nBundleScope == scope || i18nBundleScope.ordinal() < scope.ordinal()) && entry.matchLocale(locale, scope)) {
+ // load from general to the max scope and always if there is
+ // only one bundle entry found
+ if ((i18nBundleScope == scope ||
+ i18nBundleScope.ordinal() < scope.ordinal()) &&
+ entry.matchLocale(locale, scope)) {
result.add(entry);
}
}
@@ -94,7 +104,8 @@
}
/**
- * Obtain the entries for a given <code>scope</code> <ith no incluvie logic.
+ * Obtain the entries for a given <code>scope</code> with no incluvie
+ * logic.
* <p/>
* The order of result respect {@link I18nBundleEntry} order.
*
@@ -105,7 +116,8 @@
List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
for (I18nBundleEntry entry : entries) {
I18nBundleScope i18nBundleScope = entry.getScope();
- // load from general to the max scope and always if there is only one bundle entry found
+ // load from general to the max scope and always if there is
+ // only one bundle entry found
if (i18nBundleScope == scope) {
result.add(entry);
}
@@ -121,7 +133,8 @@
@Override
public String toString() {
String s = super.toString();
- return "<" + s.substring(s.lastIndexOf(".") + 1) + ", bundlePrefix:" + bundlePrefix + ", size:" + size() + ">";
+ return "<" + s.substring(s.lastIndexOf(".") + 1) + ", bundlePrefix:" +
+ bundlePrefix + ", size:" + size() + ">";
}
protected List<I18nBundleEntry> getEntries() {
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleEntry.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,7 +1,7 @@
/*
* *##%
* I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
+ * Copyright (C) 2004 - 2010 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -16,53 +16,53 @@
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
- * ##%* */
+ * ##%*
+ */
package org.nuiton.i18n.bundle;
-import java.io.IOException;
-import java.io.InputStream;
+import org.nuiton.i18n.I18nUtil;
+
+import java.io.*;
import java.net.URL;
+import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Properties;
-import org.nuiton.i18n.I18nUtil;
-import org.nuiton.i18n.I18nFileReader;
/**
* A class to represent an entry in a bundle.
* <p/>
* The object matches exactly one resource file in a given scope.
* <p/>
- * The object has three properties :
- * <ul>
- * <li> {@link #path} : the path to resource file where to find transaltion for the entry.
- * <li> {@link #locale} : the locale of the entry
- * <li> {link #scope} ; the scope of the entry
- * </ul>
+ * The object has three properties : <ul> <li> {@link #path} : the path to
+ * resource file where to find transaltion for the entry. <li> {@link #locale} :
+ * the locale of the entry <li> {link #scope} ; the scope of the entry </ul>
* This object defines a equals order base on property {@link #path}.
* <p/>
* This object is {@link Comparable}, the order relation is defined like this :
- * <ul>
- * <li> sort first on {@link #scope}, in the scope order (see {@link I18nBundleScope}),
- * <li> if scopes are equals, sort on {@link #locale} string representation.
- * </ul>
+ * <ul> <li> sort first on {@link #scope}, in the scope order (see {@link
+ * I18nBundleScope}), <li> if scopes are equals, sort on {@link #locale} string
+ * representation. </ul>
*
- * @author chemit
+ * @author tchemit <chemit(a)codelutin.com>
* @see I18nBundleScope
*/
public class I18nBundleEntry implements Comparable<I18nBundleEntry> {
/** path to resource file */
protected URL path;
+
/** local of the entry, can be null if general scope */
protected Locale locale;
+
/** scope of the entry */
protected I18nBundleScope scope;
/**
* Constructor if an bundle entry.
* <p/>
- * It is defined by a <code>path</code> of the resource file, a scope and a locale.
+ * It is defined by a <code>path</code> of the resource file, a scope and a
+ * locale.
*
* @param path the path of the resource file fo the bundle entry
* @param locale the given locale of the bundle entry
@@ -89,13 +89,12 @@
/**
* Method to match or not a bundle entry for a given scope and locale.
* <p/>
- * We use the inclusive property of scope, means that we accept all entries on the path
- * to the generalest entry for a givne locale.
+ * We use the inclusive property of scope, means that we accept all entries
+ * on the path to the generalest entry for a givne locale.
*
* @param locale the locale to match
* @param scope the highest scope to match
- * @return <code>true</code> if the entry match the scope and locale
- * *
+ * @return <code>true</code> if the entry match the scope and locale *
*/
public boolean matchLocale(Locale locale, I18nBundleScope scope) {
if (this.locale == null) {
@@ -108,12 +107,13 @@
}
// match full locale, or at least a language
return this.locale.equals(locale) ||
- (this.scope.ordinal() < scope.ordinal() && locale.getLanguage().equals(this.locale.getLanguage()));
+ this.scope.ordinal() < scope.ordinal() &&
+ locale.getLanguage().equals(this.locale.getLanguage());
}
/**
- * For a given language, load the resource file of this entry into the <code>resource</code>
- * properties object.
+ * For a given language, load the resource file of this entry into the
+ * <code>resource</code> properties object.
*
* @param resource the save of resources already loaded
* @throws IOException if any pb while reading resource file
@@ -122,18 +122,48 @@
InputStream inputStream = null;
StringBuilder sb = new StringBuilder();
try {
- I18nFileReader fileReader = new I18nFileReader();
+// I18nFileReader fileReader = new I18nFileReader();
+ Properties fileReader = new Properties() {
+
+ private static final long serialVersionUID = 1L;
+
+ public void load(InputStream inStream) throws IOException {
+ String charset = I18nUtil.ISO_8859_1_ENCONDING;
+ Charset charsetTo = Charset.forName(charset);
+
+ BufferedReader readerFile = new BufferedReader(
+ new InputStreamReader(inStream, charsetTo));
+ try {
+ String lineFile;
+ StringBuilder builderFile;
+ builderFile = new StringBuilder();
+ while ((lineFile = readerFile.readLine()) != null) {
+ builderFile.append(lineFile).append('\n');
+ }
+ super.load(new ByteArrayInputStream(
+ builderFile.toString().getBytes()));
+ } finally {
+ readerFile.close();
+ }
+ }
+ };
+
inputStream = getPath().openStream();
//String encoding = language.getEncoding();
if (I18nBundle.log.isDebugEnabled()) {
sb.append(getPath()).append("\n");
}
- // TC 20081117 always use ISO_8859_1_ENCONDING, since java does it like this.
- fileReader.load(inputStream, I18nUtil.ISO_8859_1_ENCONDING);
+ // TC 20081117 always use ISO_8859_1_ENCONDING, since java does
+ // it like this.
+ fileReader.load(inputStream);
+// fileReader.load(inputStream, I18nUtil.ISO_8859_1_ENCONDING);
if (I18nBundle.log.isDebugEnabled()) {
for (Entry<Object, Object> entry : fileReader.entrySet()) {
- sb.append(I18nUtil.ISO_8859_1_ENCONDING).append(" : ").append(entry).append("\n");
+ sb.append(I18nUtil.ISO_8859_1_ENCONDING);
+ sb.append(" : ");
+ sb.append(entry);
+ sb.append("\n");
}
}
for (Entry<Object, Object> entry : fileReader.entrySet()) {
@@ -151,7 +181,9 @@
}
//resource.putAll(fileReader);
if (I18nBundle.log.isDebugEnabled()) {
- sb.append("nbSentences : ").append(fileReader.size()).append("\n");
+ sb.append("nbSentences : ");
+ sb.append(fileReader.size());
+ sb.append("\n");
sb.append("=====================================");
}
fileReader.clear();
@@ -178,7 +210,9 @@
@Override
public boolean equals(Object o) {
- return this == o || o instanceof I18nBundleEntry && path.equals(((I18nBundleEntry) o).path);
+ return this == o ||
+ o instanceof I18nBundleEntry &&
+ path.equals(((I18nBundleEntry) o).path);
}
@Override
@@ -189,6 +223,7 @@
@Override
public String toString() {
String s = super.toString();
- return "<" + s.substring(s.lastIndexOf(".") + 1) + ", locale:" + locale + ", scope " + scope + ", path:" + path + ">";
+ return "<" + s.substring(s.lastIndexOf(".") + 1) + ", locale:" +
+ locale + ", scope " + scope + ", path:" + path + ">";
}
}
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleFactory.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,7 +1,7 @@
/*
* *##%
* I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
+ * Copyright (C) 2004 - 2010 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -20,230 +20,95 @@
*/
package org.nuiton.i18n.bundle;
-import java.io.File;
-import java.io.FileInputStream;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.I18nUtil;
+import org.nuiton.i18n.init.I18nInitializer;
+
import java.io.InputStream;
-import java.net.MalformedURLException;
import java.net.URL;
-import java.net.URLClassLoader;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.nuiton.i18n.I18nUtil;
/**
- * Classe qui est responsable de la detection et construction
- * de {@link I18nBundle}.
- *
+ * Classe qui est responsable de la detection et construction de {@link
+ * I18nBundle}.
+ * <p/>
* On retrouve aussi ici des méthodes utiles de parcours de bundles.
*
- * @author chemit
- *
+ * @author tchemit <chemit(a)codelutin.com>
* @since 1.0.6
+ * @deprecated since 1.1, prefer use the {@link I18nBundleUtil} class instead.
*/
-public class I18nBundleFactory {
+@Deprecated
+public class I18nBundleFactory extends I18nBundleUtil {
/** to use log facility, just put in your code: log.info(\"...\"); */
private static final Log log = LogFactory.getLog(I18nBundleFactory.class);
- /** pattern to find all i18n bundles in classloader class path */
- public static final String SEARCH_BUNDLE_PATTERN = ".*i18n/.+\\.properties";
- public static final String DIRECTORY_SEARCH_BUNDLE_PATTERN = "i18n";
+
protected static String UNIQUE_BUNDLE_PATH = "/META-INF/";
+
public static String UNIQUE_BUNDLE_DEF = "%1$s-definition.properties";
+
public static String UNIQUE_BUNDLE_ENTRY = "%1$s-%2$s.properties";
+
public static String BUNDLE_DEF_LOCALES = "locales";
+
public static String BUNDLES_FOR_LOCALE = "bundles.";
/**
- * Récuperation de toutes les locales connus par un ensemble de bundles.
- *
- * @param bundles les bundles a parcourir
- * @return la liste des locales rencontrées
- */
- public static Locale[] getLocales(I18nBundle... bundles) {
- Set<Locale> result = new java.util.HashSet<Locale>();
- for (I18nBundle i18nBundle : bundles) {
- for (I18nBundleEntry entry : i18nBundle.getEntries()) {
- Locale o = entry.getLocale();
- if (o != null) {
- result.add(o);
- }
- }
- }
- return result.toArray(new Locale[result.size()]);
- }
-
- /**
- * Récuperation des noms de bundle par un ensemble de bundles.
- *
- * @param bundles les bundles a parcourir
- * @return la liste des noms de bundle rencontrées
- */
- public static String[] getBundleNames(I18nBundle... bundles) {
- List<String> result = new ArrayList<String>();
- for (I18nBundle i18nBundle : bundles) {
- result.add(i18nBundle.getBundlePrefix());
- }
- return result.toArray(new String[result.size()]);
- }
-
- /**
- * Filtrage des bundles qui correspondante à la locale donnée.
- *
- * @param l la locale à filtrer
- * @param bundles les bundles a parcourir
- * @return les bundles qui correspondent à la locale donnée.
- */
- public static I18nBundle[] getBundles(Locale l, I18nBundle... bundles) {
- List<I18nBundle> result = new ArrayList<I18nBundle>();
- for (I18nBundle i18nBundle : bundles) {
- if (i18nBundle.matchLocale(l)) {
- result.add(i18nBundle);
- }
- }
- return result.toArray(new I18nBundle[result.size()]);
- }
-
- /**
- * Récupération de toutes les entrées de bundles pour les bundles données.
- *
- * @param bundles les bundles a parcourir
- * @return toutes les entrées de bundles.
- */
- public static I18nBundleEntry[] getBundleEntries(I18nBundle... bundles) {
- List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
- for (I18nBundle i18nBundle : bundles) {
- List<I18nBundleEntry> list = i18nBundle.getEntries();
- if (!list.isEmpty()) {
- result.addAll(list);
- }
- }
- return result.toArray(new I18nBundleEntry[result.size()]);
- }
-
- /**
- * Filtrage des entrées de bundles pour une locale donnée.
- *
- * On essaye de trouver les meilleurs entrées possibles (possibilité de
- * promotion).
- *
- * Note: Cette méthode doit être utilisé pour trouver toutes les entrées à
- * charger par le système i18n pour une locale donnée.
- *
- * Note: Par defaut, on n'effectue pas les promotions générales
- * ({@link #getBundleEntries(Locale, Locale, boolean, I18nBundle[])}
- *
- * @param l la locale à filtrer
- * @param defaultLocale la locale à utiliser pour les promotions
- * @param bundles les bundles a parcourir
- * @return les entrées de bundles filtrés.
- */
- public static I18nBundleEntry[] getBundleEntries(Locale l, Locale defaultLocale, I18nBundle... bundles) {
- return getBundleEntries(l, defaultLocale, false, bundles);
- }
-
- /**
- * Filtrage des entrées de bundles pour une locale donnée.
- *
- * On essaye de trouver les meilleurs entrées possibles (possibilité de
- * promotion).
- *
- * Note: Cette méthode doit être utilisé pour trouver toutes les entrées à
- * charger par le système i18n pour une locale donnée.
- *
- * @param l la locale à filtrer
- * @param defaultLocale la locale à utiliser pour les promotions
- * @param promuteGeneral un drapeau pour indiquer si l'on autorise le
- * chargement de la locale par defaut si pour un bundle donne on a
- * pas trouve de traductions pour la locale donnee.
- * @param bundles les bundles a parcourir
- * @return les entrées de bundles filtrés.
- */
- public static I18nBundleEntry[] getBundleEntries(Locale l, Locale defaultLocale, boolean promuteGeneral, I18nBundle... bundles) {
-
- List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
- for (I18nBundle i18nBundle : bundles) {
- I18nBundleEntry[] entries = i18nBundle.getEntries(l);
- if (entries.length == 0) {
- //no entry found for the bundle, try pomotion
- entries = promuteBundle(i18nBundle, l, defaultLocale, promuteGeneral);
- }
- result.addAll(Arrays.asList(entries));
- }
- return result.toArray(new I18nBundleEntry[result.size()]);
- }
-
- /**
- * Teste si un ensemble de bundles contient au moins une entrée.
- *
- * @param bundles les bundles a parcourir
- * @return <code>true</code> si aucune entree trouvee, <code>false</code>
- * autrement.
- */
- public static boolean isEmpty(I18nBundle... bundles) {
- for (I18nBundle i18nBundle : bundles) {
- if (!i18nBundle.getEntries().isEmpty()) {
- // on a trouve au moins une entree
- return false;
- }
- }
- return true;
- }
-
- /**
- * Recherche la liste des url de toutes les resources i18n, i.e les urls
- * des fichiers de traduction en mode uniqueBundleName.
- *
+ * Recherche la liste des url de toutes les resources i18n, i.e les urls des
+ * fichiers de traduction en mode uniqueBundleName.
+ * <p/>
* On va d'abord rechercher un fichier /META-INF/unqiueBundleName-definition.properties
- *
- * Dans ce fichier il y a une entree locales qui contient les locales du bundle
- *
+ * <p/>
+ * Dans ce fichier il y a une entree locales qui contient les locales du
+ * bundle
+ * <p/>
* Ensuite pour chaque locale on recupere l'url du fichier :
- *
+ * <p/>
* /META-INF/uniqueBundleName-locale.properties
- *
+ * <p/>
* Exemple :
- *
- * <code>
- * fichier de définition : /META-INF/monAppli-definition.properties
+ * <p/>
+ * <code> fichier de définition : /META-INF/monAppli-definition.properties
* locales=fr_fr,es_ES
- *
- * fichiers de traduction
- * /META-INF/monAppli-fr_FR.properties
+ * <p/>
+ * fichiers de traduction /META-INF/monAppli-fr_FR.properties
* /META-INF/monAppli-es_ES.properties
- *
+ * <p/>
* </code>
*
* @param uniqueBundleName le nom de l'unique bundle a charger
* @return la liste des urls de bundle i18n
+ * @deprecated since 1.1, prefer use the {@link I18nInitializer} api
+ * instead.
*/
+ @Deprecated
public static URL[] getURLs(String uniqueBundleName) {
+ String definitionFileName = String.format(UNIQUE_BUNDLE_DEF,
+ uniqueBundleName);
+ URL[] urls;
- String definitionFileName = String.format(UNIQUE_BUNDLE_DEF, uniqueBundleName);
- URL[] urls = null;
-
try {
- URL defURL = I18nBundleFactory.class.getResource(UNIQUE_BUNDLE_PATH + definitionFileName);
+ URL defURL = I18nBundleFactory.class.getResource(
+ UNIQUE_BUNDLE_PATH + definitionFileName);
Properties p = loadUniqueNameDefFile(uniqueBundleName);
String localesAsStr = p.getProperty(BUNDLE_DEF_LOCALES);
Locale[] locales = I18nUtil.parseLocales(localesAsStr);
- List<URL> lUrls = new java.util.ArrayList<URL>(1);
+ List<URL> lUrls = new ArrayList<URL>(1);
String prefixURL = defURL.toString();
- prefixURL = prefixURL.substring(0, prefixURL.length() - definitionFileName.length());
+ prefixURL = prefixURL.substring(0, prefixURL.length() -
+ definitionFileName.length());
//FIXME on devrait tester que la resource est disponible ?
for (Locale l : locales) {
- String url = prefixURL + String.format(UNIQUE_BUNDLE_ENTRY, uniqueBundleName, l.toString());
+ String url = prefixURL + String.format(UNIQUE_BUNDLE_ENTRY,
+ uniqueBundleName,
+ l.toString());
log.info("detected bundle properties file : " + url);
URL u = new URL(url);
// //FIXME on devrait tester que la resource est disponible ?
@@ -261,373 +126,37 @@
}
} catch (Exception ex) {
- log.warn("could not load unique bundle " + uniqueBundleName + " for reason " + ex.getMessage(), ex);
+ log.warn("could not load unique bundle " + uniqueBundleName +
+ " for reason " + ex.getMessage(), ex);
urls = null;
}
return urls;
}
+ /**
+ * @param uniqueBundleName the unique bundle name
+ * @return the properties file loaded
+ * @deprecated since 1.1, prefer use the {@link I18nInitializer} api
+ * instead.
+ */
+ @Deprecated
public static Properties loadUniqueNameDefFile(String uniqueBundleName) {
- String definitionFileName = String.format(UNIQUE_BUNDLE_DEF, uniqueBundleName);
+ String definitionFileName = String.format(UNIQUE_BUNDLE_DEF,
+ uniqueBundleName);
Properties p = new Properties();
try {
- URL defURL = I18nBundleFactory.class.getResource(UNIQUE_BUNDLE_PATH + definitionFileName);
+ URL defURL = I18nBundleFactory.class.getResource(
+ UNIQUE_BUNDLE_PATH + definitionFileName);
log.info("definition i18n file : " + defURL);
InputStream stream = defURL.openStream();
p.load(stream);
stream.close();
} catch (Exception ex) {
- log.warn("could not load unique bundle " + uniqueBundleName + " for reason " + ex.getMessage(), ex);
+ log.warn("could not load unique bundle " + uniqueBundleName +
+ " for reason " + ex.getMessage(), ex);
}
return p;
}
- /**
- * Recherche la liste des url de toutes les resources i18n, i.e les urls
- * des fichiers de traduction.
- *
- * @param urls des urls de resources i18n deja calcule, à ajouter au resultat sans traitement particulier
- * @return la liste des urls de bundle i18n
- */
- public static URL[] getURLs(URL... urls) {
-
- try {
- // on calcule toutes les urls utilisable dans le classloader donnee
- List<URL> urlToSeek = new ArrayList<URL>();
- urlToSeek.addAll(Arrays.asList(urls));
-
- // on va maintenant supprimer toutes les urls qui ne respectent pas
- // le pattern i18n : il faut que la resource contienne un repertoire i18n
- // ce simple test permet de restreindre la recherche des resources
- // i18n qui est tres couteuse
- int size = urlToSeek.size();
- for (Iterator<URL> it = urlToSeek.iterator(); it.hasNext();) {
- URL url = it.next();
- if (!I18nUtil.containsDirectDirectory(url, DIRECTORY_SEARCH_BUNDLE_PATTERN)) {
- if (log.isDebugEnabled()) {
- log.debug("skip url with no " + DIRECTORY_SEARCH_BUNDLE_PATTERN + " directory : " + url);
- }
- it.remove();
- }
- }
-
- if (log.isDebugEnabled()) {
- log.debug("detect " + urlToSeek.size() + " i18n capable url (out of " + size + ")");
- }
-
- List<URL> listURLs = new java.util.ArrayList<URL>();
-
- for (URL url : urlToSeek) {
- // on recherche tous les fichiers de traduction pour cet url
-
- List<URL> result = null;
-
- if (log.isDebugEnabled()) {
- log.debug("seek in : " + url);
- }
-
- String fileName = url.getFile();
- // TODO deal with encoding in windows, this is very durty, but it
- // works...
- File file = new File(fileName.replaceAll("%20", " "));
-
- if (I18nUtil.isJar(fileName)) {
- // cas ou le ichier du classLoader est un fichier jar
- if (log.isDebugEnabled()) {
- log.debug("jar to search " + file);
- }
- result = getURLsFromJar(url, file);
-
- } else if (file.isDirectory()) {
- // cas ou le ichier du classLoader est un repertoire
- if (log.isDebugEnabled()) {
- log.debug("directory to search " + file);
- }
- // on traite le cas ou il peut y avoir des repertoire dans ce
- // repertoire
- result = getURLsFromDirectory(url, file);
- }
- if (result != null && !result.isEmpty()) {
- listURLs.addAll(result);
- }
-
- }
- return listURLs.toArray(new URL[listURLs.size()]);
- } catch (Exception eee) {
- log.warn("Unable to find urls for urls : " + urls + " for reason " + eee.getMessage(), eee);
- return new URL[0];
- }
- }
-
- /**
- * Detecte les bundles i18n a partir des urls des fichiers de traduction
- * donnes.
- *
- * Tous les entrées de bundles sont triees dans l'ordre des scopes i18n.
- *
- * @param urls les urls des fichiers de traductions
- * @return la liste des bundle i18n construits à partir des fichiers de
- * traduction donnes.
- */
- public static List<I18nBundle> detectBundles(URL... urls) {
-
- List<String> bundleNames = new ArrayList<String>();
- List<I18nBundle> bundles = new ArrayList<I18nBundle>();
-
- for (URL url : urls) {
-
- if (addBundleEntry(url, I18nBundleScope.FULL, bundleNames, bundles)) {
- // found a full bundle
- continue;
- }
- if (addBundleEntry(url, I18nBundleScope.LANGUAGE, bundleNames, bundles)) {
- // found a language bundle
- continue;
- }
- // must be a general bundle with no locale defined
- addBundleEntry(url, I18nBundleScope.GENERAL, bundleNames, bundles);
- }
- bundleNames.clear();
-
- // once for all, sort entries from general to full
- for (I18nBundle bundle : bundles) {
- java.util.Collections.sort(bundle.getEntries());
- }
-
- return bundles;
- }
-
- protected static boolean addBundleEntry(URL url, I18nBundleScope scope, List<String> bundleNames, List<I18nBundle> bundles) {
- String path = url.toString();
- Matcher matcher = scope.getMatcher(path);
- if (!matcher.matches()) {
- // no match at this scope
- return false;
- }
- // create a new bundle entry
- I18nBundleEntry entry = new I18nBundleEntry(url, scope.getLocale(matcher), scope);
- if (log.isDebugEnabled()) {
- log.debug("bundle (" + bundles.size() + ") : " + entry);
- }
- // get the associated bundle
- I18nBundle bundle = addBundle(scope.getBundlePrefix(matcher), bundleNames, bundles);
- // add entry to bundle
- bundle.addEntry(entry);
- return true;
- }
-
- protected static I18nBundle addBundle(String bundleName, List<String> bundleNames, List<I18nBundle> bundles) {
- I18nBundle bundle;
- int index = bundleNames.indexOf(bundleName);
- if (index > -1) {
- bundle = bundles.get(index);
- } else {
- bundle = new I18nBundle(bundleName);
- if (log.isDebugEnabled()) {
- log.debug("bundle (" + bundles.size() + ") : " + bundle);
- }
- bundles.add(bundle);
- bundleNames.add(bundleName);
- }
- return bundle;
- }
-
- /**
- * Obtain some rescue entries for a given locale.
- * <p/>
- * Note: <b>Calling this method implies there is no entry matched by the common method
- * {@link #getBundleEntries(Locale, Locale, I18nBundle[])} return a empty array.
- *
- * @param bundle the bundle to promute
- * @param l the locale required
- * @param defaultLocale the default locale to used for promotion
- * @param promuteGeneral a flag to authorize promotion to default locale
- * @return the table of entries promuted for the given locale
- */
- protected static I18nBundleEntry[] promuteBundle(I18nBundle bundle, Locale l, Locale defaultLocale, boolean promuteGeneral) {
-
- I18nBundleScope scope = I18nBundleScope.valueOf(l);
-
- if (log.isDebugEnabled()) {
- log.debug('[' + bundle.getBundlePrefix() + "] did not find matching entries for locale " + l + ". Try to detect best entries...");
- }
-
- if (bundle.size() == 0) {
- // there is no entry to take...
- log.warn("PROMUTE NO ENTRY FOUND");
- return new I18nBundleEntry[0];
- }
-
- if (bundle.size() == 1) {
- // there is one entry take it,what ever...
- I18nBundleEntry entry = bundle.getEntries().get(0);
- log.warn("PROMUTE" + l + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']');
- return new I18nBundleEntry[]{entry};
- }
-
- List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
-
- switch (scope) {
- case FULL:
- promuteFull(bundle, l, defaultLocale, result, promuteGeneral);
- break;
- case LANGUAGE:
- promuteLanguage(bundle, l, defaultLocale, result, promuteGeneral);
- break;
- case GENERAL:
- if (promuteGeneral) {
- promuteGeneral(bundle, l, defaultLocale, result);
- }
- break;
- }
- return result.toArray(new I18nBundleEntry[result.size()]);
- }
-
- protected static void promuteFull(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result, boolean promuteGeneral) {
- if (bundle.size() == 0) {
- return;
- }
- // try with a another FULL matching locale ?
- for (I18nBundleEntry entry : bundle.getEntries()) {
- I18nBundleScope i18nBundleScope = entry.getScope();
- // load from general to the max scope and always if there is only one bundle entry found
- if (i18nBundleScope == I18nBundleScope.FULL &&
- !entry.getLocale().getCountry().equals(locale.getCountry()) &&
- entry.getLocale().getLanguage().equals(locale.getLanguage())) {
- log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']');
- result.add(entry);
- // we take the first one, this is a resuce!!!
- break;
- }
- }
- if (result.isEmpty()) {
- // full promotion failed,trylanguage promotion
- promuteLanguage(bundle, locale, defaultLocale, result, promuteGeneral);
- }
-
- }
-
- protected static void promuteLanguage(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result, boolean promuteGeneral) {
- if (bundle.size() == 0) {
- return;
- }
- for (I18nBundleEntry entry : bundle.getEntries()) {
- I18nBundleScope i18nBundleScope = entry.getScope();
- // load from general to the max scope and always if there is only one bundle entry found
- if (i18nBundleScope == I18nBundleScope.FULL && entry.getLocale().getLanguage().equals(locale.getLanguage())) {
- result.add(entry);
- log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']');
- // we take the first one, this is a resuce!!!
- break;
- }
- }
- if (result.isEmpty() && promuteGeneral) {
- // language promotion failed,try general promotion
- promuteGeneral(bundle, locale, defaultLocale, result);
- }
- }
-
- protected static void promuteGeneral(I18nBundle bundle, Locale locale, Locale defaultLocale, List<I18nBundleEntry> result) {
- if (bundle.size() == 0) {
- return;
- }
- if (bundle.size() == 1) {
- // there is one entry take it,what ever...
- I18nBundleEntry entry = bundle.getEntries().get(0);
- result.add(entry);
- log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']');
- return;
- }
- I18nBundleScope scope = I18nBundleScope.valueOf(defaultLocale);
- for (I18nBundleEntry entry : bundle.getEntries(scope)) {
- if (entry.getLocale().equals(defaultLocale)) {
- // default locale found
- log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']');
- result.add(entry);
- return;
- }
- }
-
- // default locale not found, take the first one ?
- I18nBundleEntry entry = bundle.getEntries().get(0);
- result.add(entry);
- log.warn(locale + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']');
- //TODO Should try to load default en_GB from I18nLoader ?
- //I18n.DEFAULT_LOCALE.getCountry()
- }
-
- protected static List<URL> getURLsFromJar(URL incomingURL, File jarfile) {
-
- String pattern = SEARCH_BUNDLE_PATTERN;
- try {
-
- List<URL> result = new ArrayList<URL>();
- InputStream in = new FileInputStream(jarfile);
- ZipInputStream zis = new ZipInputStream(in);
- ClassLoader cl = new URLClassLoader(new URL[]{incomingURL}, I18nBundleFactory.class.getClassLoader());
- while (zis.available() != 0) {
- ZipEntry entry = zis.getNextEntry();
-
- if (entry == null) {
- break;
- }
-
- String name = entry.getName();
-
- if (pattern == null || name.matches(pattern)) {
- // on recupere le fichier correspondant au pattern dans le
- // classloader
- if (log.isDebugEnabled()) {
- log.debug(name + " accepted for pattern " + pattern);
- }
- URL url = cl.getResource(name);
- // on ajoute le fichier correspondant au pattern dans la
- // liste
- result.add(url);
- }
- }
-
- return result;
- } catch (Exception eee) {
- throw new RuntimeException("n'a pas pu trouve la resource dans le jar " + jarfile.getAbsolutePath(), eee);
- }
- }
-
- protected static List<URL> getURLsFromDirectory(URL incomingURL, File repository) {
- String pattern = SEARCH_BUNDLE_PATTERN;
- try {
- if (log.isDebugEnabled()) {
- log.debug("search '" + pattern + "' in " + repository);
- }
-
- List<URL> urlList = new ArrayList<URL>();
- File[] filesList = repository.listFiles();
-
- if (filesList != null) {
-
- for (File file : filesList) {
-
- String name = file.getAbsolutePath();
-
- // cas de recursivite : repertoire dans un repertoire
- if (file.exists() && file.isDirectory()) {
- urlList.addAll(I18nUtil.getURLsFromDirectory(file,
- pattern));
- // si le fichier du repertoire n'est pas un repertoire
- // on verifie s'il correspond au pattern
- } else if (pattern == null || name.matches(pattern)) {
- URL url = file.toURI().toURL();
- if (log.isDebugEnabled()) {
- log.debug("directory: " + repository + " url: " + url);
- }
- urlList.add(url);
- }
- }
- }
- return urlList;
- } catch (MalformedURLException eee) {
- throw new RuntimeException("n'a pas pu trouve la resource dans le repertoire " + repository.getAbsolutePath(), eee);
- }
- }
}
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleScope.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,5 +1,5 @@
/*
-* *##%
+ * *##%
* I18n :: Api
* Copyright (C) 2004 - 2009 CodeLutin
*
@@ -16,45 +16,49 @@
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-3.0.html>.
- * ##%* */
+ * ##%*
+ */
package org.nuiton.i18n.bundle;
+import org.nuiton.i18n.I18nUtil;
+
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.nuiton.i18n.I18nUtil;
/**
* The enumaration defines the scope of a bundle entry.
* <p/>
- * There is three scope possible:
- * <ul>
- * <li>{@link #GENERAL} : for a bundle entry with no locale specialized information, eg : <code>bundle.properties</code></li>
- * <li>{@link #LANGUAGE} : for a bundle entry with language locale specialized information, eg : <code>bundle-en.properties</code></li>
- * <li>{@link #FULL} : for a bundle entry with full locale specialized information, eg : <code>bundle-en_GB.properties</code></li>
- * </ul>
+ * There is three scope possible: <ul> <li>{@link #GENERAL} : for a bundle entry
+ * with no locale specialized information, eg : <code>bundle.properties</code></li>
+ * <li>{@link #LANGUAGE} : for a bundle entry with language locale specialized
+ * information, eg : <code>bundle-en.properties</code></li> <li>{@link #FULL} :
+ * for a bundle entry with full locale specialized information, eg :
+ * <code>bundle-en_GB.properties</code></li> </ul>
* <p/>
* We define a order relation, from general to full scope :
* <p/>
* {@link #GENERAL} < {@link #LANGUAGE} < {@link #FULL}
* <p/>
- * Scopes are inclusives, in a search of entries, eg the search of <code>en_GB</code> will include <code>en</code> scope...
+ * Scopes are inclusives, in a search of entries, eg the search of
+ * <code>en_GB</code> will include <code>en</code> scope...
* <p/>
* The {@link #patternAll} is the searching pattern of bundle of the scope.
* <p/>
- * The method {@link #getMatcher(String)} obtain from the {@link #patternAll} the matcher for a bundle path.
+ * The method {@link #getMatcher(String)} obtain from the {@link #patternAll}
+ * the matcher for a bundle path.
* <p/>
- * The method {@link #getLocale(Matcher)} obtain from the {@link #patternAll} matched in a bundle path, the
- * corresponding locale.
+ * The method {@link #getLocale(Matcher)} obtain from the {@link #patternAll}
+ * matched in a bundle path, the corresponding locale.
* <p/>
- * The class offer also a static method {@link #valueOf(java.util.Locale)} to obtain the scope of a locale.
+ * The class offer also a static method {@link #valueOf(Locale)} to obtain the
+ * scope of a locale.
*
- * @author chemit
+ * @author tchemit <chemit(a)codelutin.com>
*/
public enum I18nBundleScope {
/** default scope (with no language, nor country information) */
-// GENERAL("(.*18n/.+)\\.properties") {
GENERAL("(.*/.+)\\.properties") {
@Override
public Locale getLocale(Matcher matcher) {
@@ -64,7 +68,6 @@
},
/** language scope (no country information) */
-// LANGUAGE("(.*18n/.+)-(\\w\\w)\\.properties") {
LANGUAGE("(.*/.+)-(\\w\\w)\\.properties") {
@Override
public Locale getLocale(Matcher matcher) {
@@ -77,7 +80,6 @@
},
/** full scope : language + country */
-// FULL("(.*18n/.+)-(\\w\\w_\\w\\w)\\.properties") {
FULL("(.*/.+)-(\\w\\w_\\w\\w)\\.properties") {
@Override
public Locale getLocale(Matcher matcher) {
@@ -95,13 +97,15 @@
/**
* Obtain the scope of a given <code>locale</code>.
* <p/>
- * The given locale can be null, which means {@link I18nBundleScope#GENERAL} scope.
+ * The given locale can be null, which means {@link I18nBundleScope#GENERAL}
+ * scope.
*
* @param locale given locale to convert
* @return the scope of given locale
*/
public static I18nBundleScope valueOf(Locale locale) {
- if (locale == null || locale.getLanguage() == null || locale.getLanguage().length() == 0) {
+ if (locale == null || locale.getLanguage() == null ||
+ locale.getLanguage().length() == 0) {
return GENERAL;
}
if (locale.getCountry() == null || locale.getCountry().length() == 0) {
@@ -140,7 +144,7 @@
return result;
}
- private I18nBundleScope(String patternAll) {
+ I18nBundleScope(String patternAll) {
this.patternAll = Pattern.compile(patternAll);
}
}
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleUtil.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleUtil.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleUtil.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,603 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n.bundle;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.I18nUtil;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Usefull methods on bundles.
+ * <p/>
+ * <b>Note:</b> Replace the previous class {@code org.nuiton.i18n.bundle.I18nBundleFactory}.
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class I18nBundleUtil {
+
+ /** Logger */
+ private static final Log log = LogFactory.getLog(I18nBundleUtil.class);
+
+ public static final String DIRECTORY_SEARCH_BUNDLE_PATTERN = "i18n";
+
+ public static final String SEARCH_BUNDLE_PATTERN = ".*i18n/.+\\.properties";
+
+ private static final I18nBundleEntry[] EMPTY_I18N_BUNDLE_ENTRYS_ARRAY =
+ new I18nBundleEntry[0];
+
+ public static final URL[] EMPTY_URL_ARRAY = new URL[0];
+
+ /**
+ * Récuperation de toutes les locales connus par un ensemble de bundles.
+ *
+ * @param bundles les bundles a parcourir
+ * @return la liste des locales rencontrées
+ */
+ public static Locale[] getLocales(I18nBundle... bundles) {
+ Set<Locale> result = new HashSet<Locale>();
+ for (I18nBundle i18nBundle : bundles) {
+ for (I18nBundleEntry entry : i18nBundle.getEntries()) {
+ Locale o = entry.getLocale();
+ if (o != null) {
+ result.add(o);
+ }
+ }
+ }
+ return result.toArray(new Locale[result.size()]);
+ }
+
+ /**
+ * Récuperation des noms de bundle par un ensemble de bundles.
+ *
+ * @param bundles les bundles a parcourir
+ * @return la liste des noms de bundle rencontrées
+ */
+ public static String[] getBundleNames(I18nBundle... bundles) {
+ List<String> result = new ArrayList<String>();
+ for (I18nBundle i18nBundle : bundles) {
+ result.add(i18nBundle.getBundlePrefix());
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Filtrage des bundles qui correspondante à la locale donnée.
+ *
+ * @param l la locale à filtrer
+ * @param bundles les bundles a parcourir
+ * @return les bundles qui correspondent à la locale donnée.
+ */
+ public static I18nBundle[] getBundles(Locale l, I18nBundle... bundles) {
+ List<I18nBundle> result = new ArrayList<I18nBundle>();
+ for (I18nBundle i18nBundle : bundles) {
+ if (i18nBundle.matchLocale(l)) {
+ result.add(i18nBundle);
+ }
+ }
+ return result.toArray(new I18nBundle[result.size()]);
+ }
+
+ /**
+ * Récupération de toutes les entrées de bundles pour les bundles données.
+ *
+ * @param bundles les bundles a parcourir
+ * @return toutes les entrées de bundles.
+ */
+ public static I18nBundleEntry[] getBundleEntries(I18nBundle... bundles) {
+ List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
+ for (I18nBundle i18nBundle : bundles) {
+ List<I18nBundleEntry> list = i18nBundle.getEntries();
+ if (!list.isEmpty()) {
+ result.addAll(list);
+ }
+ }
+ return result.toArray(new I18nBundleEntry[result.size()]);
+ }
+
+ /**
+ * Filtrage des entrées de bundles pour une locale donnée.
+ * <p/>
+ * On essaye de trouver les meilleurs entrées possibles (possibilité de
+ * promotion).
+ * <p/>
+ * Note: Cette méthode doit être utilisé pour trouver toutes les entrées à
+ * charger par le système i18n pour une locale donnée.
+ * <p/>
+ * Note: Par defaut, on n'effectue pas les promotions générales ({@link
+ * #getBundleEntries(Locale, Locale, boolean, I18nBundle...)}
+ *
+ * @param l la locale à filtrer
+ * @param defaultLocale la locale à utiliser pour les promotions
+ * @param bundles les bundles a parcourir
+ * @return les entrées de bundles filtrés.
+ */
+ public static I18nBundleEntry[] getBundleEntries(Locale l,
+ Locale defaultLocale,
+ I18nBundle... bundles) {
+ return getBundleEntries(l, defaultLocale, false, bundles);
+ }
+
+ /**
+ * Filtrage des entrées de bundles pour une locale donnée.
+ * <p/>
+ * On essaye de trouver les meilleurs entrées possibles (possibilité de
+ * promotion).
+ * <p/>
+ * Note: Cette méthode doit être utilisé pour trouver toutes les entrées à
+ * charger par le système i18n pour une locale donnée.
+ *
+ * @param l la locale à filtrer
+ * @param defaultLocale la locale à utiliser pour les promotions
+ * @param promuteGeneral un drapeau pour indiquer si l'on autorise le
+ * chargement de la locale par defaut si pour un
+ * bundle donne on a pas trouve de traductions pour la
+ * locale donnee.
+ * @param bundles les bundles a parcourir
+ * @return les entrées de bundles filtrés.
+ */
+ public static I18nBundleEntry[] getBundleEntries(Locale l,
+ Locale defaultLocale,
+ boolean promuteGeneral,
+ I18nBundle... bundles) {
+
+ List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
+ for (I18nBundle i18nBundle : bundles) {
+ I18nBundleEntry[] entries = i18nBundle.getEntries(l);
+ if (entries.length == 0) {
+ //no entry found for the bundle, try pomotion
+ entries = promuteBundle(i18nBundle, l, defaultLocale,
+ promuteGeneral);
+ }
+ result.addAll(Arrays.asList(entries));
+ }
+ return result.toArray(new I18nBundleEntry[result.size()]);
+ }
+
+ /**
+ * Recherche la liste des url de toutes les resources i18n, i.e les urls des
+ * fichiers de traduction.
+ *
+ * @param urls les urls à inspecter pour trouver des resources i18n
+ * @return la liste des urls de bundle i18n
+ */
+ public static URL[] getURLs(URL... urls) {
+
+ try {
+ // on calcule toutes les urls utilisable dans le classloader donnee
+ List<URL> urlToSeek = new ArrayList<URL>();
+ urlToSeek.addAll(Arrays.asList(urls));
+
+ // on va maintenant supprimer toutes les urls qui ne respectent pas
+ // le pattern i18n : il faut que la resource contienne un
+ // repertoire i18n, ce simple test permet de restreindre la
+ // recherche des resources
+ // i18n qui est tres couteuse
+ int size = urlToSeek.size();
+ for (Iterator<URL> it = urlToSeek.iterator(); it.hasNext();) {
+ URL url = it.next();
+ if (!I18nUtil.containsDirectDirectory(
+ url, DIRECTORY_SEARCH_BUNDLE_PATTERN)) {
+ if (log.isDebugEnabled()) {
+ log.debug("skip url with no " +
+ DIRECTORY_SEARCH_BUNDLE_PATTERN +
+ " directory : " + url);
+ }
+ it.remove();
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("detect " + urlToSeek.size() +
+ " i18n capable url (out of " + size + ")");
+ }
+
+ List<URL> listURLs = new ArrayList<URL>();
+
+ for (URL url : urlToSeek) {
+ // on recherche tous les fichiers de traduction pour cet url
+
+ List<URL> result = null;
+
+ if (log.isDebugEnabled()) {
+ log.debug("seek in : " + url);
+ }
+
+ String fileName = url.getFile();
+ // TODO deal with encoding in windows, this is very durty,
+ // TODO but it works...
+ File file = new File(fileName.replaceAll("%20", " "));
+
+ if (I18nUtil.isJar(fileName)) {
+ // cas ou le ichier du classLoader est un fichier jar
+ if (log.isDebugEnabled()) {
+ log.debug("jar to search " + file);
+ }
+ result = getURLsFromJar(url, file);
+
+ } else if (file.isDirectory()) {
+ // cas ou le ichier du classLoader est un repertoire
+ if (log.isDebugEnabled()) {
+ log.debug("directory to search " + file);
+ }
+ // on traite le cas ou il peut y avoir des repertoire
+ // dans ce repertoire
+ result = getURLsFromDirectory(url, file);
+ }
+ if (result != null && !result.isEmpty()) {
+ listURLs.addAll(result);
+ }
+
+ }
+ return listURLs.toArray(new URL[listURLs.size()]);
+ } catch (Exception eee) {
+ log.warn("Unable to find urls for urls : " + Arrays.toString(urls) +
+ " for reason " + eee.getMessage(), eee);
+ return EMPTY_URL_ARRAY;
+ }
+ }
+
+ /**
+ * Teste si un ensemble de bundles contient au moins une entrée.
+ *
+ * @param bundles les bundles a parcourir
+ * @return <code>true</code> si aucune entree trouvee, <code>false</code>
+ * autrement.
+ */
+ public static boolean isEmpty(I18nBundle... bundles) {
+ for (I18nBundle i18nBundle : bundles) {
+ if (!i18nBundle.getEntries().isEmpty()) {
+ // on a trouve au moins une entree
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Detecte les bundles i18n a partir des urls des fichiers de traduction
+ * donnes.
+ * <p/>
+ * Tous les entrées de bundles sont triees dans l'ordre des scopes i18n.
+ *
+ * @param urls les urls des fichiers de traductions
+ * @return la liste des bundle i18n construits à partir des fichiers de
+ * traduction donnes.
+ */
+ public static List<I18nBundle> detectBundles(URL... urls) {
+
+ List<String> bundleNames = new ArrayList<String>();
+ List<I18nBundle> bundles = new ArrayList<I18nBundle>();
+
+ for (URL url : urls) {
+
+ if (addBundleEntry(url, I18nBundleScope.FULL, bundleNames,
+ bundles)) {
+ // found a full bundle
+ continue;
+ }
+ if (addBundleEntry(url, I18nBundleScope.LANGUAGE, bundleNames,
+ bundles)) {
+ // found a language bundle
+ continue;
+ }
+ // must be a general bundle with no locale defined
+ addBundleEntry(url, I18nBundleScope.GENERAL, bundleNames, bundles);
+ }
+ bundleNames.clear();
+
+ // once for all, sort entries from general to full
+ for (I18nBundle bundle : bundles) {
+ Collections.sort(bundle.getEntries());
+ }
+
+ return bundles;
+ }
+
+ protected static boolean addBundleEntry(URL url,
+ I18nBundleScope scope,
+ List<String> bundleNames,
+ List<I18nBundle> bundles) {
+ String path = url.toString();
+ Matcher matcher = scope.getMatcher(path);
+ if (!matcher.matches()) {
+ // no match at this scope
+ return false;
+ }
+ // create a new bundle entry
+ I18nBundleEntry entry =
+ new I18nBundleEntry(url, scope.getLocale(matcher), scope);
+ if (log.isDebugEnabled()) {
+ log.debug("bundle (" + bundles.size() + ") : " + entry);
+ }
+ // get the associated bundle
+ I18nBundle bundle =
+ addBundle(scope.getBundlePrefix(matcher), bundleNames, bundles);
+ // add entry to bundle
+ bundle.addEntry(entry);
+ return true;
+ }
+
+ protected static I18nBundle addBundle(String bundleName,
+ List<String> bundleNames,
+ List<I18nBundle> bundles) {
+ I18nBundle bundle;
+ int index = bundleNames.indexOf(bundleName);
+ if (index > -1) {
+ bundle = bundles.get(index);
+ } else {
+ bundle = new I18nBundle(bundleName);
+ if (log.isDebugEnabled()) {
+ log.debug("bundle (" + bundles.size() + ") : " + bundle);
+ }
+ bundles.add(bundle);
+ bundleNames.add(bundleName);
+ }
+ return bundle;
+ }
+
+ /**
+ * Obtain some rescue entries for a given locale.
+ * <p/>
+ * Note: <b>Calling this method implies there is no entry matched by the
+ * common method {@link #getBundleEntries(Locale, Locale, I18nBundle...)}
+ * returns a empty array.</b>
+ *
+ * @param bundle the bundle to promute
+ * @param l the locale required
+ * @param defaultLocale the default locale to used for promotion
+ * @param promuteGeneral a flag to authorize promotion to default locale
+ * @return the table of entries promuted for the given locale
+ */
+ protected static I18nBundleEntry[] promuteBundle(I18nBundle bundle,
+ Locale l,
+ Locale defaultLocale,
+ boolean promuteGeneral) {
+
+ I18nBundleScope scope = I18nBundleScope.valueOf(l);
+
+ if (log.isDebugEnabled()) {
+ log.debug('[' + bundle.getBundlePrefix() + "] did not find" +
+ " matching entries for locale " + l +
+ ". Try to detect best entries...");
+ }
+
+ if (bundle.size() == 0) {
+ // there is no entry to take...
+ log.warn("PROMUTE NO ENTRY FOUND");
+ return EMPTY_I18N_BUNDLE_ENTRYS_ARRAY;
+ }
+
+ if (bundle.size() == 1) {
+ // there is one entry take it,what ever...
+ I18nBundleEntry entry = bundle.getEntries().get(0);
+ log.warn("PROMUTE" + l + " to " + entry.getLocale() +
+ " [" + bundle.getBundlePrefix() + ']');
+ return new I18nBundleEntry[]{entry};
+ }
+
+ List<I18nBundleEntry> result = new ArrayList<I18nBundleEntry>();
+
+ switch (scope) {
+ case FULL:
+ promuteFull(bundle, l, defaultLocale, result, promuteGeneral);
+ break;
+ case LANGUAGE:
+ promuteLanguage(bundle, l, defaultLocale, result,
+ promuteGeneral);
+ break;
+ case GENERAL:
+ if (promuteGeneral) {
+ promuteGeneral(bundle, l, defaultLocale, result);
+ }
+ break;
+ }
+ return result.toArray(new I18nBundleEntry[result.size()]);
+ }
+
+ protected static void promuteFull(I18nBundle bundle,
+ Locale locale,
+ Locale defaultLocale,
+ List<I18nBundleEntry> result,
+ boolean promuteGeneral) {
+ if (bundle.size() == 0) {
+ return;
+ }
+ // try with a another FULL matching locale ?
+ for (I18nBundleEntry entry : bundle.getEntries()) {
+ I18nBundleScope i18nBundleScope = entry.getScope();
+ // load from general to the max scope and always if there is only
+ // one bundle entry found
+ Locale locale1 = entry.getLocale();
+ if (i18nBundleScope == I18nBundleScope.FULL &&
+ !locale1.getCountry().equals(locale.getCountry()) &&
+ locale1.getLanguage().equals(locale.getLanguage())) {
+ log.warn(locale + " to " + locale1 +
+ " [" + bundle.getBundlePrefix() + ']');
+ result.add(entry);
+ // we take the first one, this is a resuce!!!
+ break;
+ }
+ }
+ if (result.isEmpty()) {
+ // full promotion failed,trylanguage promotion
+ promuteLanguage(bundle, locale, defaultLocale, result,
+ promuteGeneral);
+ }
+
+ }
+
+ protected static void promuteLanguage(I18nBundle bundle,
+ Locale locale,
+ Locale defaultLocale,
+ List<I18nBundleEntry> result,
+ boolean promuteGeneral) {
+ if (bundle.size() == 0) {
+ return;
+ }
+ for (I18nBundleEntry entry : bundle.getEntries()) {
+ I18nBundleScope i18nBundleScope = entry.getScope();
+ // load from general to the max scope and always if there is only
+ // one bundle entry found
+ Locale locale1 = entry.getLocale();
+ if (i18nBundleScope == I18nBundleScope.FULL &&
+ locale1.getLanguage().equals(locale.getLanguage())) {
+ result.add(entry);
+ log.warn(locale + " to " + locale1 + " [" +
+ bundle.getBundlePrefix() + ']');
+ // we take the first one, this is a resuce!!!
+ break;
+ }
+ }
+ if (result.isEmpty() && promuteGeneral) {
+ // language promotion failed,try general promotion
+ promuteGeneral(bundle, locale, defaultLocale, result);
+ }
+ }
+
+ protected static void promuteGeneral(I18nBundle bundle,
+ Locale locale,
+ Locale defaultLocale,
+ List<I18nBundleEntry> result) {
+ if (bundle.size() == 0) {
+ return;
+ }
+ if (bundle.size() == 1) {
+ // there is one entry take it,what ever...
+ I18nBundleEntry entry = bundle.getEntries().get(0);
+ result.add(entry);
+ log.warn(locale + " to " + entry.getLocale() + " [" +
+ bundle.getBundlePrefix() + ']');
+ return;
+ }
+ I18nBundleScope scope = I18nBundleScope.valueOf(defaultLocale);
+ for (I18nBundleEntry entry : bundle.getEntries(scope)) {
+ if (entry.getLocale().equals(defaultLocale)) {
+ // default locale found
+ log.warn(locale + " to " + entry.getLocale() + " [" +
+ bundle.getBundlePrefix() + ']');
+ result.add(entry);
+ return;
+ }
+ }
+
+ // default locale not found, take the first one ?
+ I18nBundleEntry entry = bundle.getEntries().get(0);
+ result.add(entry);
+ log.warn(locale + " to " + entry.getLocale() + " [" +
+ bundle.getBundlePrefix() + ']');
+ //TODO Should try to load default en_GB from I18nLoader ?
+ //I18n.DEFAULT_LOCALE.getCountry()
+ }
+
+ protected static List<URL> getURLsFromJar(URL incomingURL, File jarfile) {
+
+ String pattern = SEARCH_BUNDLE_PATTERN;
+ try {
+
+ List<URL> result = new ArrayList<URL>();
+ InputStream in = new FileInputStream(jarfile);
+ ZipInputStream zis = new ZipInputStream(in);
+ ClassLoader cl = new URLClassLoader(
+ new URL[]{incomingURL},
+ I18nBundleUtil.class.getClassLoader());
+ while (zis.available() != 0) {
+ ZipEntry entry = zis.getNextEntry();
+
+ if (entry == null) {
+ break;
+ }
+
+ String name = entry.getName();
+
+ if (name.matches(pattern)) {
+ // on recupere le fichier correspondant au pattern dans le
+ // classloader
+ if (log.isDebugEnabled()) {
+ log.debug(name + " accepted for pattern " + pattern);
+ }
+ URL url = cl.getResource(name);
+ // on ajoute le fichier correspondant au pattern dans la
+ // liste
+ result.add(url);
+ }
+ }
+
+ return result;
+ } catch (Exception eee) {
+ throw new RuntimeException(
+ "n'a pas pu trouve la resource dans le jar " +
+ jarfile.getAbsolutePath(), eee);
+ }
+ }
+
+ protected static List<URL> getURLsFromDirectory(URL incomingURL,
+ File repository) {
+ String pattern = SEARCH_BUNDLE_PATTERN;
+ try {
+ if (log.isDebugEnabled()) {
+ log.debug("search '" + pattern + "' in " + repository);
+ }
+
+ List<URL> urlList = new ArrayList<URL>();
+ File[] filesList = repository.listFiles();
+
+ if (filesList != null) {
+
+ for (File file : filesList) {
+
+ String name = file.getAbsolutePath();
+
+ // cas de recursivite : repertoire dans un repertoire
+ if (file.exists() && file.isDirectory()) {
+ urlList.addAll(I18nUtil.getURLsFromDirectory(file,
+ pattern));
+ // si le fichier du repertoire n'est pas un repertoire
+ // on verifie s'il correspond au pattern
+ } else if (name.matches(pattern)) {
+ URL url = file.toURI().toURL();
+ if (log.isDebugEnabled()) {
+ log.debug("directory: " + repository + " url: " +
+ url);
+ }
+ urlList.add(url);
+ }
+ }
+ }
+ return urlList;
+ } catch (MalformedURLException eee) {
+ throw new RuntimeException(
+ "n'a pas pu trouve la resource dans le repertoire " +
+ repository.getAbsolutePath(), eee);
+ }
+ }
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/bundle/I18nBundleUtil.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/ClassPathI18nInitializer.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/ClassPathI18nInitializer.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/ClassPathI18nInitializer.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,132 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n.init;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.I18nUtil;
+import org.nuiton.i18n.bundle.I18nBundle;
+import org.nuiton.i18n.bundle.I18nBundleUtil;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Implementation of a {@link I18nInitializer} using all i18n resources (from
+ * artifacts) discovered in classpath.
+ * <p/>
+ * Will scan all classpath.
+ * <p/>
+ * <b>Note:</b> No order can be predicted with this implementation on bundles.
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class ClassPathI18nInitializer extends I18nInitializer {
+
+ /** Logger */
+ private static final Log log =
+ LogFactory.getLog(DefaultI18nInitializer.class);
+
+ /** class loader to use (optional) */
+ protected ClassLoader loader;
+
+ protected URL[] extraURLs;
+
+ public ClassPathI18nInitializer() {
+ this(null, null);
+ }
+
+ public ClassPathI18nInitializer(ClassLoader loader) {
+ this(loader, null);
+ }
+
+ public ClassPathI18nInitializer(ClassLoader loader, URL[] extraURLs) {
+ this.loader = loader == null ? getClass().getClassLoader() : loader;
+ this.extraURLs = extraURLs;
+ }
+
+ public URL[] resolvURLs() throws Exception {
+ // on calcule toutes les urls utilisable dans le classloader donnee
+ List<URL> urlToSeek = new ArrayList<URL>();
+ URLClassLoader loader = (URLClassLoader) getLoader();
+ urlToSeek.addAll(Arrays.asList(I18nUtil.getDeepURLs(loader)));
+
+ // on va maintenant supprimer toutes les urls qui ne respectent pas
+ // le pattern i18n : il faut que la resource contienne un repertoire i18n
+ // ce simple test permet de restreindre la recherche des resources
+ // i18n qui est tres couteuse
+ int size = urlToSeek.size();
+ for (Iterator<URL> it = urlToSeek.iterator(); it.hasNext();) {
+ URL url = it.next();
+ if (!I18nUtil.containsDirectDirectory(
+ url, I18nBundleUtil.DIRECTORY_SEARCH_BUNDLE_PATTERN)) {
+ if (log.isDebugEnabled()) {
+ log.debug("skip url with no " +
+ I18nBundleUtil.DIRECTORY_SEARCH_BUNDLE_PATTERN +
+ " directory : " + url);
+ }
+ it.remove();
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("detect " + urlToSeek.size() +
+ " i18n capable url (out of " + size + ")");
+ }
+ // on effectue la recherche des urls des resources i18n (tous les
+ // fichiers de traductions) sur toutes les urls precedemment calculees)
+ URL[] url1 = urlToSeek.toArray(new URL[urlToSeek.size()]);
+ URL[] result = I18nBundleUtil.getURLs(url1);
+ if (log.isDebugEnabled()) {
+ for (URL url : result) {
+ log.debug(url.toString());
+ }
+ }
+ return result;
+
+ }
+
+ @Override
+ public I18nBundle[] resolvBundles() throws Exception {
+ // detect bundles urls
+
+ URL[] urls = resolvURLs();
+
+ // detect bundles
+
+ I18nBundle[] result = resolvBundles(urls);
+ return result;
+ }
+
+ public URL[] getExtraURLs() {
+ return extraURLs;
+ }
+
+ public ClassLoader getLoader() {
+ return loader;
+ }
+
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/ClassPathI18nInitializer.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/DefaultI18nInitializer.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/DefaultI18nInitializer.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/DefaultI18nInitializer.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,215 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n.init;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.I18nUtil;
+import org.nuiton.i18n.bundle.I18nBundle;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * Default implementation of a {@link I18nInitializer} using the default i18n
+ * implementation (one unique bundle with a definition properties file).
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class DefaultI18nInitializer extends I18nInitializer {
+
+ /** Logger */
+ private static final Log log =
+ LogFactory.getLog(DefaultI18nInitializer.class);
+
+ public static String UNIQUE_BUNDLE_DEF = "%1$s-definition.properties";
+
+ public static String UNIQUE_BUNDLE_ENTRY = "%1$s-%2$s.properties";
+
+ public static String BUNDLE_DEF_LOCALES = "locales";
+
+ public static String BUNDLE_DEF_VERSION = "version";
+
+ public static String BUNDLES_FOR_LOCALE = "bundles.";
+
+ /** the name of the bundle */
+ protected final String bundleName;
+
+ /** class loader to use (optional) */
+ protected ClassLoader loader;
+
+ /** i18n path where to seek for resources (optional) */
+ protected String i18nPath;
+
+ protected Properties definition;
+
+ protected URL definitionURL;
+
+ public static final String DEFAULT_I18N_PATH = "META-INF/";
+
+ public DefaultI18nInitializer(String bundleName) throws
+ NullPointerException {
+ this(bundleName, null, null);
+ }
+
+ public DefaultI18nInitializer(String bundleName,
+ ClassLoader loader) throws
+ NullPointerException {
+ this(bundleName, loader, null);
+ }
+
+ public DefaultI18nInitializer(String bundleName,
+ ClassLoader loader,
+ String i18nPath) throws NullPointerException {
+ if (bundleName == null) {
+ throw new NullPointerException(
+ "parameter 'bundleName' can not be null");
+ }
+ this.bundleName = bundleName;
+ this.loader = loader == null ? getClass().getClassLoader() : loader;
+ this.i18nPath = i18nPath == null ? DEFAULT_I18N_PATH : i18nPath;
+ }
+
+ public String getBundleName() {
+ return bundleName;
+ }
+
+ public ClassLoader getLoader() {
+ return loader;
+ }
+
+ public String getI18nPath() {
+ return i18nPath;
+ }
+
+ protected void setLoader(ClassLoader loader) {
+ this.loader = loader;
+ }
+
+ protected void setI18nPath(String i18nPath) {
+ this.i18nPath = i18nPath;
+ }
+
+ protected URL getResourceURL(String resource) {
+ if (log.isInfoEnabled()) {
+ log.info("resource to seek : " + resource);
+ }
+ URL url = getLoader().getResource(resource);
+ return url;
+ }
+
+ public String resolvDefinition(Properties p) throws Exception {
+
+ String filename = String.format(UNIQUE_BUNDLE_DEF, getBundleName());
+
+ URL url = getDefinitionURL();
+
+ if (log.isInfoEnabled()) {
+ log.info("definition file to seek : " + url);
+ }
+
+ // load definition file
+ InputStream stream = url.openStream();
+ try {
+ p.load(stream);
+ stream.close();
+ } finally {
+ stream.close();
+ }
+
+ String prefix = url.toString();
+ prefix = prefix.substring(0, prefix.length() - filename.length());
+
+ return prefix;
+ }
+
+ public URL getDefinitionURL() throws NullPointerException {
+ if (definitionURL == null) {
+ String filename = String.format(UNIQUE_BUNDLE_DEF, getBundleName());
+
+ String path = getI18nPath() + filename;
+
+ definitionURL = getResourceURL(path);
+
+ if (definitionURL == null) {
+ throw new NullPointerException(
+ "could not find bundle definition file at " + path);
+ }
+ }
+ return definitionURL;
+ }
+
+// @Override
+
+ public URL[] resolvURLs(String prefixURL, Properties definition) throws Exception {
+
+ // get locales from properties
+
+ String localesAsStr = definition.getProperty(BUNDLE_DEF_LOCALES);
+
+ Locale[] locales = I18nUtil.parseLocales(localesAsStr);
+
+ if (log.isDebugEnabled()) {
+ log.debug("Detected locales : " + Arrays.toString(locales));
+ }
+
+ List<URL> lUrls = new ArrayList<URL>(locales.length);
+
+ // for each locale found in definition file, add resource url
+
+ String bundleName = getBundleName();
+
+ for (Locale l : locales) {
+ String url = prefixURL +
+ String.format(UNIQUE_BUNDLE_ENTRY, bundleName, l);
+ if (log.isInfoEnabled()) {
+ log.info("Detected resource for locale " + l + " : " + url);
+ }
+ URL u = new URL(url);
+ //FIXME on devrait tester que la resource est disponible ?
+
+ lUrls.add(u);
+ }
+
+ URL[] urls = lUrls.toArray(new URL[lUrls.size()]);
+ return urls;
+ }
+
+ @Override
+ public I18nBundle[] resolvBundles() throws Exception {
+
+ Properties definition = new Properties();
+
+ String prefixURL = resolvDefinition(definition);
+
+ // detect bundles urls
+
+ URL[] urls = resolvURLs(prefixURL, definition);
+
+ // detect bundles
+
+ I18nBundle[] result = resolvBundles(urls);
+ return result;
+ }
+
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/DefaultI18nInitializer.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/I18nInitializer.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/I18nInitializer.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/I18nInitializer.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,69 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n.init;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.bundle.I18nBundle;
+import org.nuiton.i18n.bundle.I18nBundleEntry;
+import org.nuiton.i18n.bundle.I18nBundleUtil;
+
+import java.net.URL;
+import java.util.List;
+
+/**
+ * Contract of a resolver of {@link I18nBundle}.
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public abstract class I18nInitializer {
+
+ /** Logger */
+ private static final Log log = LogFactory.getLog(I18nInitializer.class);
+
+ /**
+ * Resolv the bundles.
+ *
+ * @return the bundles detected
+ * @throws Exception if any pb while getting bundles
+ */
+ public abstract I18nBundle[] resolvBundles() throws Exception;
+
+ public I18nBundle[] resolvBundles(URL... urls) throws Exception {
+
+ // detect bundles
+
+ List<I18nBundle> bundles = I18nBundleUtil.detectBundles(urls);
+ I18nBundle[] result = bundles.toArray(new I18nBundle[bundles.size()]);
+
+ if (log.isInfoEnabled()) {
+
+ I18nBundleEntry[] entries = I18nBundleUtil.getBundleEntries(result);
+
+ log.info(bundles.size() + " bundle(s) found, in " +
+ entries.length + " file(s).");
+ }
+
+ return result;
+ }
+
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/I18nInitializer.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/UserI18nInitializer.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/UserI18nInitializer.java (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/UserI18nInitializer.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,210 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n.init;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.i18n.bundle.I18nBundle;
+import org.nuiton.i18n.bundle.I18nBundleEntry;
+import org.nuiton.i18n.bundle.I18nBundleUtil;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Properties;
+
+/**
+ * Initializer which expose i18n resources in the {@link #userDirectory}, then
+ * use resources from this user directories.
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class UserI18nInitializer extends DefaultI18nInitializer {
+
+ /** Logger */
+ private static final Log log = LogFactory.getLog(UserI18nInitializer.class);
+
+ /** the user directory where to store the i18n resources. */
+ protected final File userDirectory;
+
+ /**
+ * the delegate initializer to obtain default i18n resources to put in user
+ * directory.
+ */
+ protected final DefaultI18nInitializer delegate;
+
+ public UserI18nInitializer(File userDirectory,
+ DefaultI18nInitializer delegate)
+ throws NullPointerException {
+ this(null, userDirectory, delegate);
+ }
+
+ public UserI18nInitializer(String i18nPath,
+ File userDirectory,
+ DefaultI18nInitializer delegate)
+ throws NullPointerException {
+
+ super(delegate == null ? null : delegate.getBundleName(),
+ null,
+ i18nPath == null ? "" : i18nPath
+ );
+
+ if (userDirectory == null) {
+ throw new NullPointerException(
+ "parameter 'userDirectory' can not be null");
+ }
+ if (delegate == null) {
+ throw new NullPointerException(
+ "parameter 'delegate' can not be null");
+ }
+ this.userDirectory = userDirectory;
+ this.delegate = delegate;
+ }
+
+ public File getUserDirectory() {
+ return userDirectory;
+ }
+
+ public DefaultI18nInitializer getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public I18nBundle[] resolvBundles() throws Exception {
+
+ File directory = getUserDirectory();
+
+ boolean isNew = !directory.exists();
+
+ if (isNew) {
+
+ // creates the user directory and fill it with i18n resources
+ // coming from default initializer
+ createUserI18nLayout(directory);
+ }
+
+ // use a new classloader directly on the directory
+ setLoader(new URLClassLoader(new URL[]{directory.toURI().toURL()}));
+
+ I18nBundle[] bundles = super.resolvBundles();
+
+ if (!isNew) {
+ updateUserI18nLayout(directory, bundles);
+ }
+ return bundles;
+ }
+
+ /**
+ * Creates the user i18n structure.
+ * <p/>
+ * will use the default initializer to obtain i18n resources from default
+ * system, then copy them to the user directory.
+ *
+ * @param directory the directory where to export i18n resources
+ * @throws Exception if any pb
+ */
+ protected void createUserI18nLayout(File directory) throws Exception {
+
+ // user i18n directory does not exists
+ // create it and fill it
+
+ boolean b = directory.mkdirs();
+ if (!b) {
+ throw new IOException("could not create directory " + directory);
+ }
+
+ Properties definition = new Properties();
+
+ String prefixURL = getDelegate().resolvDefinition(definition);
+
+ String filename = String.format(UNIQUE_BUNDLE_DEF, getBundleName());
+
+ File f = new File(directory, filename);
+
+ FileOutputStream outStream = new FileOutputStream(f);
+ try {
+
+ definition.store(outStream, "Generated by " + getClass().getName());
+ } finally {
+
+ outStream.close();
+ }
+
+ // detect bundles urls
+
+ URL[] urls = resolvURLs(prefixURL, definition);
+
+ // detect bundles
+
+ I18nBundle[] bundles = resolvBundles(urls);
+
+ // detect bundles entries
+
+ I18nBundleEntry[] entries =
+ I18nBundleUtil.getBundleEntries(bundles);
+
+ // copy all bundle entries resource
+
+ for (I18nBundleEntry e : entries) {
+
+ URL url = e.getPath();
+
+ if (log.isInfoEnabled()) {
+ log.info("I18n file to load : " + url);
+ }
+
+ String path = url.getPath();
+
+ String name = path.substring(path.lastIndexOf("/"));
+
+ File dst = new File(directory, name);
+
+ if (log.isInfoEnabled()) {
+ log.info("Create user file to create : " + dst);
+ }
+
+ // recopie du fichier
+
+ outStream = new FileOutputStream(dst);
+ try {
+ IOUtils.copy(url.openStream(), outStream);
+ } finally {
+ outStream.close();
+ }
+ }
+ }
+
+ /**
+ * Hook to update the user i18n structure.
+ * <p/>
+ * If you wants to do something specific, overrides this method.
+ *
+ * @param directory the user directory where are i18n resources
+ * @param bundles the user i18n bundles
+ */
+ public void updateUserI18nLayout(File directory, I18nBundle[] bundles) {
+ // by default nothing to do, change this if you wants something
+ }
+}
Property changes on: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/init/UserI18nInitializer.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Added: trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/package.html
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/package.html (rev 0)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/i18n/package.html 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,12 @@
+<html>
+<body>
+<h1>Nuiton i18n </h1>
+Ensemble de classes Java permettant de charger le système i18n basée
+sur la librairie nuiton-i18n-api.
+
+Pour initialiser le système utiliser les deux méthodes suivantes :
+
+<pre>org.nuiton.i18n.I18n.setBundleResolver(resolver);</pre>
+<pre>org.nuiton.i18n.I18n.init(locale);</pre>
+</body>
+</html>
Modified: trunk/nuiton-i18n-api/src/main/java/org/nuiton/util/LocaleConverter.java
===================================================================
--- trunk/nuiton-i18n-api/src/main/java/org/nuiton/util/LocaleConverter.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/main/java/org/nuiton/util/LocaleConverter.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -23,6 +23,8 @@
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.Converter;
import static org.apache.commons.logging.LogFactory.getLog;
+
+import org.apache.commons.logging.Log;
import org.nuiton.i18n.CountryEnum;
import org.nuiton.i18n.LanguageEnum;
@@ -31,22 +33,26 @@
import java.util.regex.Pattern;
/**
- * classe pour convertir une chaine en un objet {@link java.util.Locale}.
+ * classe pour convertir une chaine en un objet {@link Locale}.
*
- * @author chemit
+ * @author chemit <chemit(a)codelutin.com>
*/
public class LocaleConverter implements Converter {
- private static final Pattern FULL_SCOPE_PATTERN = Pattern.compile("([a-zA-Z]{2})_([a-zA-Z]{2})");
+ private static final Pattern FULL_SCOPE_PATTERN =
+ Pattern.compile("([a-zA-Z]{2})_([a-zA-Z]{2})");
- private static final Pattern MEDIUM_SCOPE_PATTERN = Pattern.compile("([a-zA-Z]{2})");
+ private static final Pattern MEDIUM_SCOPE_PATTERN =
+ Pattern.compile("([a-zA-Z]{2})");
/** to use log facility, just put in your code: log.info(\"...\"); */
- static org.apache.commons.logging.Log log = getLog(LocaleConverter.class);
+ static Log log = getLog(LocaleConverter.class);
+ @Override
public Object convert(Class aClass, Object value) {
if (value == null) {
- throw new ConversionException("can not convert null value in " + this + " convertor");
+ throw new ConversionException("can not convert null value in " +
+ this + " convertor");
}
if (isEnabled(aClass)) {
Object result;
@@ -59,10 +65,12 @@
return result;
}
}
- throw new ConversionException("could not find a convertor for type " + aClass.getName() + " and value : " + value);
+ throw new ConversionException(
+ "could not find a convertor for type " + aClass.getName() +
+ " and value : " + value);
}
- protected Locale valueOf(String value) {
+ public Locale valueOf(String value) {
try {
Locale result = convertFullScope(value);
@@ -71,12 +79,14 @@
}
if (result == null) {
- throw new ConversionException("could not convert locale " + value);
+ throw new ConversionException("could not convert locale " +
+ value);
}
return result;
} catch (Exception e) {
- throw new ConversionException("could not convert locale " + value + " for reason " + e.getMessage());
+ throw new ConversionException("could not convert locale " + value +
+ " for reason " + e.getMessage());
}
}
@@ -84,11 +94,13 @@
Matcher m = FULL_SCOPE_PATTERN.matcher(value);
if (m.matches()) {
// found a full scope pattern (language + country)
- LanguageEnum language = LanguageEnum.valueOf(m.group(1).toLowerCase());
+ LanguageEnum language =
+ LanguageEnum.valueOf(m.group(1).toLowerCase());
CountryEnum country = CountryEnum.valueOf(m.group(2).toUpperCase());
if (language == null || country == null) {
// not safe
- throw new ConversionException("could not convert locale " + value);
+ throw new ConversionException("could not convert locale " +
+ value);
}
return new Locale(language.name(), country.name());
}
@@ -99,11 +111,13 @@
Matcher m = MEDIUM_SCOPE_PATTERN.matcher(value);
if (m.matches()) {
// found a medium scope pattern (only language)
- LanguageEnum language = LanguageEnum.valueOf(m.group(1).toLowerCase());
+ LanguageEnum language =
+ LanguageEnum.valueOf(m.group(1).toLowerCase());
if (language == null) {
// not safe
- throw new ConversionException("could not convert locale " + value);
+ throw new ConversionException("could not convert locale " +
+ value);
}
return new Locale(language.name());
}
@@ -117,8 +131,8 @@
}
}
- protected boolean isEnabled(Class aClass) {
- return aClass == Locale.class;
+ protected boolean isEnabled(Class<?> aClass) {
+ return Locale.class.equals(aClass);
}
public Class<?> getType() {
Added: trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/I18nStoreTest.java
===================================================================
--- trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/I18nStoreTest.java (rev 0)
+++ trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/I18nStoreTest.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,135 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.nuiton.i18n.init.DefaultI18nInitializer;
+
+import java.net.MalformedURLException;
+import java.util.Locale;
+
+/**
+ * Tests {@link I18nStore}.
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ * @since 1.1
+ */
+public class I18nStoreTest {
+
+ String encoding;
+
+ Locale locale;
+
+ I18nLanguage language;
+
+ I18nStore store;
+
+ @BeforeClass
+ public static void beforeClass() throws MalformedURLException {
+ I18n.setInitializer(
+ new DefaultI18nInitializer(I18nStoreTest.class.getSimpleName())
+ );
+ }
+
+ @AfterClass
+ public static void afterClass() throws Exception {
+ I18n.setInitializer(null);
+ I18n.close();
+ }
+
+ @Test
+ public void testGetLoader() throws Exception {
+
+ Assert.assertNull(I18n.store);
+
+ store = I18n.getStore();
+ Assert.assertNotNull(store);
+ //assertEquals(I18n.DEFAULT_ENCODING, store.getEncoding());
+ Assert.assertNull(store.getLanguage());
+ }
+
+ @Test
+ public void testChangeLocale() throws Exception {
+
+ locale = I18nUtil.newLocale("fr_FR");
+ encoding = I18n.ISO_8859_1_ENCONDING;
+ updateLanguage();
+ assertNbLanguages(1);
+ updateLanguage();
+ assertNbLanguages(1);
+
+ locale = I18nUtil.newLocale("en_GB");
+ updateLanguage();
+ assertLanguageChanged();
+ assertNbLanguages(2);
+
+ locale = I18nUtil.newLocale("en_US");
+ updateLanguage();
+ assertLanguageChanged();
+ assertNbLanguages(3);
+
+ locale = I18nUtil.newLocale("en");
+ updateLanguage();
+ assertLanguageChanged();
+ assertNbLanguages(4);
+ }
+
+ protected void assertLanguageChanged() {
+ Assert.assertNotSame(language, store.getLanguage());
+ }
+
+ /*public void testChangeEncoding() throws Exception {
+ locale = I18n.newLocale("fr_FR");
+ encoding = I18n.ISO_8859_1_ENCONDING;
+ updateLanguage();
+
+ locale = I18n.newLocale("en_GB");
+ updateLanguage();
+
+ // language change (from his encoding)
+ assertLanguageChanged();
+ // 2 language in cache
+ assertNbLanguages(2);
+
+ encoding = I18n.UTF_8_ENCONDING;
+ updateLanguage();
+ // language change (from his encoding)
+ assertLanguageChanged();
+ // one language in cache
+ assertNbLanguages(1);
+
+ }*/
+
+ protected void assertNbLanguages(int i) {
+ Assert.assertEquals(i, store.getLanguages().size());
+ }
+
+ protected void updateLanguage() {
+ language = store == null ? null : store.getLanguage();
+ store = I18n.getStore();
+ store.setLanguage(locale);
+ }
+
+}
+
Property changes on: trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/I18nStoreTest.java
___________________________________________________________________
Added: svn:keywords
+ "Author Date Id Revision HeadURL
Copied: trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBundleScopeTest.java (from rev 1700, trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java)
===================================================================
--- trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBundleScopeTest.java (rev 0)
+++ trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBundleScopeTest.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,70 @@
+/*
+ * *##%
+ * I18n :: Api
+ * Copyright (C) 2004 - 2010 CodeLutin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Lesser Public License for more details.
+ *
+ * You should have received a copy of the GNU General Lesser Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * ##%*
+ */
+package org.nuiton.i18n.bundle;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Locale;
+
+/**
+ * Tests {@link I18nBundleScope}
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ */
+public class I18nBundleScopeTest {
+
+ Locale locale;
+
+ I18nBundleScope excepted;
+
+ @Test
+ public void testFullScope() {
+ excepted = I18nBundleScope.FULL;
+
+ locale = new Locale("fr", "FR");
+ Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
+ }
+
+ @Test
+ public void testLanguageScope() {
+ excepted = I18nBundleScope.LANGUAGE;
+
+ locale = new Locale("fr");
+ Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
+
+ locale = new Locale("fr", "");
+ Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
+ }
+
+ @Test
+ public void testGeneralScope() {
+
+ excepted = I18nBundleScope.GENERAL;
+
+ locale = null;
+ Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
+
+ locale = new Locale("");
+ Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
+ }
+
+}
Property changes on: trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBundleScopeTest.java
___________________________________________________________________
Added: svn:keywords
+ Author Date Id Revision
Added: svn:eol-style
+ native
Deleted: trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java
===================================================================
--- trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/test/java/org/nuiton/i18n/bundle/I18nBunsleScopeTest.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,65 +0,0 @@
-/**
- * *##%
- * I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program 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 Lesser Public License for more details.
- *
- * You should have received a copy of the GNU General Lesser Public
- * License along with this program. If not, see
- * <http://www.gnu.org/licenses/lgpl-3.0.html>.
- * ##%*
- */
-package org.nuiton.i18n.bundle;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.Locale;
-
-/** @author chemit */
-public class I18nBunsleScopeTest {
-
- Locale locale;
- I18nBundleScope excepted;
-
- @Test
- public void testFullScope() {
- excepted = I18nBundleScope.FULL;
-
- locale = new Locale("fr", "FR");
- Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
- }
-
- @Test
- public void testLanguageScope() {
- excepted = I18nBundleScope.LANGUAGE;
-
- locale = new Locale("fr");
- Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
-
- locale = new Locale("fr", "");
- Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
- }
-
- @Test
- public void testGeneralScope() {
-
- excepted = I18nBundleScope.GENERAL;
-
- locale = null;
- Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
-
- locale = new Locale("");
- Assert.assertEquals(excepted, I18nBundleScope.valueOf(locale));
- }
-
-}
Modified: trunk/nuiton-i18n-api/src/test/java/org/nuiton/util/LocaleConverterTest.java
===================================================================
--- trunk/nuiton-i18n-api/src/test/java/org/nuiton/util/LocaleConverterTest.java 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/test/java/org/nuiton/util/LocaleConverterTest.java 2010-03-07 16:02:33 UTC (rev 1702)
@@ -1,7 +1,7 @@
-/**
- * *##%
+/*
+ * *##%
* I18n :: Api
- * Copyright (C) 2004 - 2009 CodeLutin
+ * Copyright (C) 2004 - 2010 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -25,11 +25,17 @@
import java.util.Locale;
-/** @author chemit */
+/**
+ * Tests {@link LocaleConverter}.
+ *
+ * @author tchemit <chemit(a)codelutin.com>
+ */
public class LocaleConverterTest extends TestCase {
String toConvert;
+
Locale excepted;
+
Converter converter;
@Override
@@ -65,7 +71,7 @@
//TODO Arch, we must also check coherence !
toConvert = "fr_GB";
- excepted = new Locale("fr","GB");
+ excepted = new Locale("fr", "GB");
assertEquals(toConvert, excepted);
}
Added: trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-definition.properties
===================================================================
--- trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-definition.properties (rev 0)
+++ trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-definition.properties 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,4 @@
+#Sun Mar 07 00:23:58 CET 2010
+bundles.en_GB=bundleTest/I18nStoreTest-en_GB.properties
+bundles.fr_FR=bundleTest/I18nStoreTest-fr_FR.properties
+locales=fr_FR,en_GB
Added: trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-en_GB.properties
===================================================================
--- trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-en_GB.properties (rev 0)
+++ trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-en_GB.properties 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,2 @@
+key.one=First
+key.two=Second
Added: trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-fr_FR.properties
===================================================================
--- trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-fr_FR.properties (rev 0)
+++ trunk/nuiton-i18n-api/src/test/resources/META-INF/I18nStoreTest-fr_FR.properties 2010-03-07 16:02:33 UTC (rev 1702)
@@ -0,0 +1,2 @@
+key.one=Premier
+key.two=Seconde
Modified: trunk/nuiton-i18n-api/src/test/resources/log4j.properties
===================================================================
--- trunk/nuiton-i18n-api/src/test/resources/log4j.properties 2010-03-07 16:00:07 UTC (rev 1701)
+++ trunk/nuiton-i18n-api/src/test/resources/log4j.properties 2010-03-07 16:02:33 UTC (rev 1702)
@@ -6,6 +6,4 @@
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n
#log4j.appender.stdout.layout.ConversionPattern=%%c=%c %%C=%C %%d=%d %%F=%F %%l=%l %%L=%L %%m=%m %%M=%M %%p=%p %%r=%r %%t=%t %%x=%x %%X=%X
# package level
-log4j.logger.org.codelutin.i18n=INFO
-log4j.logger.org.codelutin.util=INFO
-log4j.logger.org.codelutin.option=INFO
+log4j.logger.org.nuiton.i18n=INFO
1
0
Author: tchemit
Date: 2010-03-07 17:00:07 +0100 (Sun, 07 Mar 2010)
New Revision: 1701
Log:
use commons-io + log4j (for tests)
Modified:
trunk/pom.xml
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-02-21 10:26:15 UTC (rev 1700)
+++ trunk/pom.xml 2010-03-07 16:00:07 UTC (rev 1701)
@@ -13,7 +13,7 @@
</parent>
<artifactId>i18n</artifactId>
- <version>1.0.2-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
<modules>
<module>nuiton-i18n-api</module>
@@ -37,6 +37,13 @@
</dependency>
<dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.4</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.nuiton.processor</groupId>
<artifactId>nuiton-processor</artifactId>
<version>${processor.version}</version>
@@ -181,6 +188,12 @@
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.14</version>
+ </dependency>
+
</dependencies>
</dependencyManagement>
1
0
Author: tchemit
Date: 2010-02-21 11:26:15 +0100 (Sun, 21 Feb 2010)
New Revision: 1700
Modified:
trunk/pom.xml
Log:
Utilisation de mavenpom4redmine 2.0.5
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-26 21:06:49 UTC (rev 1699)
+++ trunk/pom.xml 2010-02-21 10:26:15 UTC (rev 1700)
@@ -9,7 +9,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>mavenpom4redmine</artifactId>
- <version>2.0.4</version>
+ <version>2.0.5</version>
</parent>
<artifactId>i18n</artifactId>
1
0
Author: tchemit
Date: 2010-01-26 22:06:49 +0100 (Tue, 26 Jan 2010)
New Revision: 1699
Modified:
trunk/pom.xml
Log:
Utilisation de mavenpom4redmine 2.0.4
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-24 23:35:31 UTC (rev 1698)
+++ trunk/pom.xml 2010-01-26 21:06:49 UTC (rev 1699)
@@ -9,7 +9,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>mavenpom4redmine</artifactId>
- <version>2.0.3</version>
+ <version>2.0.4</version>
</parent>
<artifactId>i18n</artifactId>
1
0
Author: tchemit
Date: 2010-01-25 00:35:31 +0100 (Mon, 25 Jan 2010)
New Revision: 1698
Modified:
trunk/pom.xml
Log:
Utilisation de mavenpom4redmine 2.0.3
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-19 17:33:35 UTC (rev 1697)
+++ trunk/pom.xml 2010-01-24 23:35:31 UTC (rev 1698)
@@ -9,7 +9,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>mavenpom4redmine</artifactId>
- <version>2.0.2</version>
+ <version>2.0.3</version>
</parent>
<artifactId>i18n</artifactId>
1
0
Author: tchemit
Date: 2010-01-19 18:33:35 +0100 (Tue, 19 Jan 2010)
New Revision: 1697
Modified:
trunk/pom.xml
Log:
Utilisation de mavenpom4redmine 2.0.2
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-17 20:14:18 UTC (rev 1696)
+++ trunk/pom.xml 2010-01-19 17:33:35 UTC (rev 1697)
@@ -9,7 +9,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>mavenpom4redmine</artifactId>
- <version>2.0.1</version>
+ <version>2.0.2</version>
</parent>
<artifactId>i18n</artifactId>
1
0
Author: tchemit
Date: 2010-01-17 21:14:18 +0100 (Sun, 17 Jan 2010)
New Revision: 1696
Modified:
trunk/pom.xml
Log:
Utilisation de mavenpom4redmine 2.0.1
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-15 17:28:54 UTC (rev 1695)
+++ trunk/pom.xml 2010-01-17 20:14:18 UTC (rev 1696)
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -10,7 +9,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>mavenpom4redmine</artifactId>
- <version>2.0</version>
+ <version>2.0.1</version>
</parent>
<artifactId>i18n</artifactId>
@@ -227,4 +226,3 @@
</scm>
</project>
-
1
0
Author: fdesbois
Date: 2010-01-15 18:28:54 +0100 (Fri, 15 Jan 2010)
New Revision: 1695
Modified:
trunk/pom.xml
Log:
use 1.5 version of jdk and mavenpom 2.0
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-10 23:01:44 UTC (rev 1694)
+++ trunk/pom.xml 2010-01-15 17:28:54 UTC (rev 1695)
@@ -10,7 +10,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>mavenpom4redmine</artifactId>
- <version>2.0-SNAPSHOT</version>
+ <version>2.0</version>
</parent>
<artifactId>i18n</artifactId>
@@ -204,7 +204,9 @@
<projectId>i18n</projectId>
<processor.version>1.0.1</processor.version>
-
+
+ <maven.compile.source>1.5</maven.compile.source>
+ <maven.compile.target>1.5</maven.compile.target>
</properties>
<build>
1
0
Author: tchemit
Date: 2010-01-11 00:01:44 +0100 (Mon, 11 Jan 2010)
New Revision: 1694
Modified:
trunk/pom.xml
Log:
use mavenpom4redmine
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-07 03:26:47 UTC (rev 1693)
+++ trunk/pom.xml 2010-01-10 23:01:44 UTC (rev 1694)
@@ -9,8 +9,8 @@
<!-- ************************************************************* -->
<parent>
<groupId>org.nuiton</groupId>
- <artifactId>mavenpom</artifactId>
- <version>1.1.4</version>
+ <artifactId>mavenpom4redmine</artifactId>
+ <version>2.0-SNAPSHOT</version>
</parent>
<artifactId>i18n</artifactId>
1
0
Author: tchemit
Date: 2010-01-07 04:26:47 +0100 (Thu, 07 Jan 2010)
New Revision: 1693
Modified:
trunk/pom.xml
Log:
use mavenpom 1.1.4
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2010-01-01 17:20:49 UTC (rev 1692)
+++ trunk/pom.xml 2010-01-07 03:26:47 UTC (rev 1693)
@@ -10,7 +10,7 @@
<parent>
<groupId>org.nuiton</groupId>
<artifactId>mavenpom</artifactId>
- <version>1.1.4-SNAPSHOT</version>
+ <version>1.1.4</version>
</parent>
<artifactId>i18n</artifactId>
1
0