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

org.nuiton.i18n.bundle.I18nBundleUtil Maven / Gradle / Ivy

There is a newer version: 4.2
Show newest version
/*
 * #%L
 * I18n :: Api
 * *
 * $Id: I18nBundleUtil.java 1928 2011-05-13 09:59:47Z tchemit $
 * $HeadURL: https://nuiton.org/svn/i18n/tags/i18n-3.0/nuiton-i18n/src/main/java/org/nuiton/i18n/bundle/I18nBundleUtil.java $
 * %%
 * 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
 * .
 * #L%
 */

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.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * Usefull methods on bundles.
 * 

* Note: Replace the previous class {@code org.nuiton.i18n.bundle.I18nBundleFactory}. * * @author tchemit * @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"; 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 result = new HashSet(); 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 result = new ArrayList(); 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 result = new ArrayList(); 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 result = new ArrayList(); for (I18nBundle i18nBundle : bundles) { List 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 result = new ArrayList(); 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 urlToSeek = new ArrayList(); 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 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 listURLs = new ArrayList(); for (URL url : urlToSeek) { // on recherche tous les fichiers de traduction pour cet url List 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 (log.isDebugEnabled()) { log.debug("url to search " + file); log.debug("Is a exsting file or directory ? " + file.exists()); } 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) { if (log.isWarnEnabled()) { 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} si aucune entree trouvee, {@code false} * 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. *

* 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 detectBundles(URL... urls) { List bundleNames = new ArrayList(); List bundles = new ArrayList(); 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 bundleNames, List 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 bundleNames, List 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. *

* Note: Calling this method implies there is no entry matched by the * common method {@link #getBundleEntries(Locale, Locale, I18nBundle...)} * returns 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... if (log.isWarnEnabled()) { 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); if (log.isWarnEnabled()) { log.warn("PROMUTE" + l + " to " + entry.getLocale() + " [" + bundle.getBundlePrefix() + ']'); } return new I18nBundleEntry[]{entry}; } List result = new ArrayList(); 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 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())) { if (log.isWarnEnabled()) { 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 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); if (log.isWarnEnabled()) { 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 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); if (log.isWarnEnabled()) { 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 if (log.isWarnEnabled()) { 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); if (log.isWarnEnabled()) { 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 getURLsFromJar(URL incomingURL, File jarfile) { String pattern = getSearchBundlePattern(); try { ZipInputStream zis = new ZipInputStream(new FileInputStream(jarfile)); try { List result = new ArrayList(); 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; } finally { zis.close(); } } catch (Exception eee) { throw new RuntimeException( "n'a pas pu trouve la resource dans le jar " + jarfile.getAbsolutePath(), eee); } } /** * Compute the search pattern according to the {@link File#separator} on * the underlying os. *

* Under linux this is {@code .*i18n/.+\.properties}, and under windows this * is {@code .*i18n\\\\.+\.properties}. * * @return the correct pattern * @since 2.0 */ protected static String getSearchBundlePattern() { String result = ".*i18n"; String path = File.separator; result += "\\".equals(path) ? path + path : path; return result + ".+\\.properties"; } protected static List getURLsFromDirectory(URL incomingURL, File repository) { String pattern = getSearchBundlePattern(); try { if (log.isDebugEnabled()) { log.debug("search '" + pattern + "' in " + repository); } List urlList = new ArrayList(); 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); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy