/* * Copyright (c) JForum Team * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * 1) Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * 2) Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or * other materials provided with the distribution. * 3) Neither the name of "Rafael Steil" nor * the names of its contributors may be used to endorse * or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE * * This file creation date: Mar 10, 2003 / 9:28:40 PM * The JForum Project * http://www.jforum.net */ package net.jforum.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import net.jforum.SessionFacade; import net.jforum.entities.UserSession; import net.jforum.exceptions.ForumException; import net.jforum.util.preferences.ConfigKeys; import net.jforum.util.preferences.SystemGlobals; import org.apache.log4j.Logger; import freemarker.template.SimpleSequence; /** * I18n (Internationalization) class implementation. Does nothing of special, just loads the * messages into memory and provides a static method to acess them. * * @author Rafael Steil * @author James Yong * @version $Id: I18n.java,v 1.36 2007/07/08 14:11:43 rafaelsteil Exp $ */ public class I18n { private static final Logger logger = Logger.getLogger(I18n.class); private static I18n classInstance = new I18n(); private static Map messagesMap = new HashMap(); private static Properties localeNames = new Properties(); private static String defaultName; private static String baseDir; private static List watching = new ArrayList(); public static final String CANNOT_DELETE_GROUP = "CannotDeleteGroup"; public static final String CANNOT_DELETE_CATEGORY = "CannotDeleteCategory"; public static final String CANNOT_DELETE_BANNER = "CannotDeleteBanner"; private I18n() { } /** * Gets the singleton * * @return Instance of I18n class */ public static I18n getInstance() { return classInstance; } /** * Load the default I18n file * */ public static synchronized void load() { baseDir = SystemGlobals.getApplicationResourceDir() + "/" + SystemGlobals.getValue(ConfigKeys.LOCALES_DIR); loadLocales(); defaultName = SystemGlobals.getValue(ConfigKeys.I18N_DEFAULT_ADMIN); load(defaultName, null); String custom = SystemGlobals.getValue(ConfigKeys.I18N_DEFAULT); if (!custom.equals(defaultName)) { load(custom, defaultName); defaultName = custom; } } public static void changeBoardDefault(String newDefaultLanguage) { load(newDefaultLanguage, SystemGlobals.getValue(ConfigKeys.I18N_DEFAULT_ADMIN)); defaultName = newDefaultLanguage; } private static void loadLocales() { FileInputStream fis = null; try { fis = new FileInputStream(baseDir + SystemGlobals.getValue(ConfigKeys.LOCALES_NAMES)); localeNames.load(fis); } catch (IOException e) { throw new ForumException(e); } finally { if (fis != null) { try { fis.close(); } catch (Exception e) {} } } } static void load(String localeName, String mergeWith) { load(localeName, mergeWith, false); } static void load(String localeName, String mergeWith, boolean force) { if (!force && (localeName == null || localeName.trim().equals("") || I18n.contains(localeName))) { return; } if (localeNames.size() == 0) { loadLocales(); } Properties p = new Properties(); if (mergeWith != null) { if (!I18n.contains(mergeWith)) { load(mergeWith, null); } p.putAll((Properties) messagesMap.get(mergeWith)); } FileInputStream fis = null; try { String filename = baseDir + localeNames.getProperty(localeName); // If the requested locale does not exist, use the default if (!new File(filename).exists()) { filename = baseDir + localeNames.getProperty(SystemGlobals.getValue(ConfigKeys.I18N_DEFAULT_ADMIN)); } fis = new FileInputStream(filename); p.load(fis); } catch (IOException e) { throw new ForumException(e); } finally { if (fis != null) { try { fis.close(); } catch (Exception e) {} } } messagesMap.put(localeName, p); watchForChanges(localeName); } /** * Loads a new locale. If <code>localeName</code> is either null or empty, or if the locale is * already loaded, the method will return without executing any code. * * @param localeName * The locale name to load */ public static void load(String localeName) { load(localeName, SystemGlobals.getValue(ConfigKeys.I18N_DEFAULT)); } public static void reset() { messagesMap = new HashMap(); localeNames = new Properties(); defaultName = null; } private static void watchForChanges(final String localeName) { if (!watching.contains(localeName)) { watching.add(localeName); int fileChangesDelay = SystemGlobals.getIntValue(ConfigKeys.FILECHANGES_DELAY); if (fileChangesDelay > 0) { FileMonitor.getInstance().addFileChangeListener(new FileChangeListener() { /** * @see net.jforum.util.FileChangeListener#fileChanged(java.lang.String) */ public void fileChanged(String filename) { if (logger.isDebugEnabled()) { logger.info("Reloading i18n for " + localeName); } I18n.load(localeName, SystemGlobals.getValue(ConfigKeys.I18N_DEFAULT), true); } }, baseDir + localeNames.getProperty(localeName), fileChangesDelay); } } } /** * Gets a I18N (internationalized) message. * * @param localeName * The locale name to retrieve the messages from * @param messageName * The message name to retrieve. Must be a valid entry into the file specified by * <code>i18n.file</code> property. * @param params * Parameters needed by some messages. The messages with extra parameters are * formated according to {@link java.text.MessageFormat}specification * @return String With the message */ public static String getMessage(String localeName, String messageName, Object params[]) { return MessageFormat.format(((Properties) messagesMap.get(localeName)).getProperty(messageName), params); } /** * @see #getMessage(String, String, Object[]) * @param messageName String * @param params Object * @return String */ public static String getMessage(String messageName, Object params[]) { String lang = ""; UserSession us = SessionFacade.getUserSession(); if (us != null && us.getLang() != null) { lang = us.getLang(); } if ("".equals(lang)) { return getMessage(defaultName, messageName, params); } return getMessage(lang, messageName, params); } public static String getMessage(String messageName, SimpleSequence params) { try { return getMessage(messageName, params.toList().toArray()); } catch (Exception e) { throw new ForumException(e); } } /** * Gets an I18N (internationalization) message. * * @param m * The message name to retrieve. Must be a valid entry into the file specified by * <code>i18n.file</code> property. * @return String With the message * @param localeName * String */ public static String getMessage(String localeName, String m) { if (!messagesMap.containsKey(localeName)) { load(localeName); } return (((Properties) messagesMap.get(localeName)).getProperty(m)); } public static String getMessage(String m) { return getMessage(getUserLanguage(), m); } public static String getMessage(String m, UserSession us) { if (us == null || us.getLang() == null || us.getLang().equals("")) { return getMessage(defaultName, m); } return getMessage(us.getLang(), m); } /** * Gets the language name for the current request. The method will first look at * {@link UserSession#getLang()} and use it if any value is found. Otherwise, the default board * language will be used * * @return String */ public static String getUserLanguage() { UserSession us = SessionFacade.getUserSession(); if (us == null || us.getLang() == null || us.getLang().trim().equals("")) { return defaultName; } return us.getLang(); } /** * Check whether the language is loaded in i18n. * * @param language * String * @return boolean */ public static boolean contains(String language) { return messagesMap.containsKey(language); } /** * Check if the given language exist. * * @param language * The language to check * @return <code>true</code> if the language is a valid and registered translation. */ public static boolean languageExists(String language) { return (localeNames.getProperty(language) != null); } }