001: /*
002: * Copyright (c) JForum Team
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms,
006: * with or without modification, are permitted provided
007: * that the following conditions are met:
008: *
009: * 1) Redistributions of source code must retain the above
010: * copyright notice, this list of conditions and the
011: * following disclaimer.
012: * 2) Redistributions in binary form must reproduce the
013: * above copyright notice, this list of conditions and
014: * the following disclaimer in the documentation and/or
015: * other materials provided with the distribution.
016: * 3) Neither the name of "Rafael Steil" nor
017: * the names of its contributors may be used to endorse
018: * or promote products derived from this software without
019: * specific prior written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
022: * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
023: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
024: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
025: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR
026: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
027: * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
028: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
029: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES
030: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
031: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
032: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
033: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
034: * IN CONTRACT, STRICT LIABILITY, OR TORT
035: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
036: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
037: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
038: *
039: * This file creation date: Mar 10, 2003 / 9:28:40 PM
040: * The JForum Project
041: * http://www.jforum.net
042: */
043: package net.jforum.util;
044:
045: import java.io.File;
046: import java.io.FileInputStream;
047: import java.io.IOException;
048: import java.text.MessageFormat;
049: import java.util.ArrayList;
050: import java.util.HashMap;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.Properties;
054:
055: import net.jforum.SessionFacade;
056: import net.jforum.entities.UserSession;
057: import net.jforum.exceptions.ForumException;
058: import net.jforum.util.preferences.ConfigKeys;
059: import net.jforum.util.preferences.SystemGlobals;
060:
061: import org.apache.log4j.Logger;
062:
063: import freemarker.template.SimpleSequence;
064:
065: /**
066: * I18n (Internationalization) class implementation. Does nothing of special, just loads the
067: * messages into memory and provides a static method to acess them.
068: *
069: * @author Rafael Steil
070: * @author James Yong
071: * @version $Id: I18n.java,v 1.36 2007/07/08 14:11:43 rafaelsteil Exp $
072: */
073: public class I18n {
074: private static final Logger logger = Logger.getLogger(I18n.class);
075:
076: private static I18n classInstance = new I18n();
077:
078: private static Map messagesMap = new HashMap();
079:
080: private static Properties localeNames = new Properties();
081:
082: private static String defaultName;
083:
084: private static String baseDir;
085:
086: private static List watching = new ArrayList();
087:
088: public static final String CANNOT_DELETE_GROUP = "CannotDeleteGroup";
089:
090: public static final String CANNOT_DELETE_CATEGORY = "CannotDeleteCategory";
091:
092: public static final String CANNOT_DELETE_BANNER = "CannotDeleteBanner";
093:
094: private I18n() {
095: }
096:
097: /**
098: * Gets the singleton
099: *
100: * @return Instance of I18n class
101: */
102: public static I18n getInstance() {
103: return classInstance;
104: }
105:
106: /**
107: * Load the default I18n file
108: *
109: */
110: public static synchronized void load() {
111: baseDir = SystemGlobals.getApplicationResourceDir() + "/"
112: + SystemGlobals.getValue(ConfigKeys.LOCALES_DIR);
113:
114: loadLocales();
115:
116: defaultName = SystemGlobals
117: .getValue(ConfigKeys.I18N_DEFAULT_ADMIN);
118: load(defaultName, null);
119:
120: String custom = SystemGlobals.getValue(ConfigKeys.I18N_DEFAULT);
121: if (!custom.equals(defaultName)) {
122: load(custom, defaultName);
123: defaultName = custom;
124: }
125: }
126:
127: public static void changeBoardDefault(String newDefaultLanguage) {
128: load(newDefaultLanguage, SystemGlobals
129: .getValue(ConfigKeys.I18N_DEFAULT_ADMIN));
130: defaultName = newDefaultLanguage;
131: }
132:
133: private static void loadLocales() {
134: FileInputStream fis = null;
135:
136: try {
137: fis = new FileInputStream(baseDir
138: + SystemGlobals.getValue(ConfigKeys.LOCALES_NAMES));
139: localeNames.load(fis);
140: } catch (IOException e) {
141: throw new ForumException(e);
142: } finally {
143: if (fis != null) {
144: try {
145: fis.close();
146: } catch (Exception e) {
147: }
148: }
149: }
150: }
151:
152: static void load(String localeName, String mergeWith) {
153: load(localeName, mergeWith, false);
154: }
155:
156: static void load(String localeName, String mergeWith, boolean force) {
157: if (!force
158: && (localeName == null || localeName.trim().equals("") || I18n
159: .contains(localeName))) {
160: return;
161: }
162:
163: if (localeNames.size() == 0) {
164: loadLocales();
165: }
166:
167: Properties p = new Properties();
168:
169: if (mergeWith != null) {
170: if (!I18n.contains(mergeWith)) {
171: load(mergeWith, null);
172: }
173:
174: p.putAll((Properties) messagesMap.get(mergeWith));
175: }
176:
177: FileInputStream fis = null;
178:
179: try {
180: String filename = baseDir
181: + localeNames.getProperty(localeName);
182:
183: // If the requested locale does not exist, use the default
184: if (!new File(filename).exists()) {
185: filename = baseDir
186: + localeNames
187: .getProperty(SystemGlobals
188: .getValue(ConfigKeys.I18N_DEFAULT_ADMIN));
189: }
190:
191: fis = new FileInputStream(filename);
192: p.load(fis);
193: } catch (IOException e) {
194: throw new ForumException(e);
195: } finally {
196: if (fis != null) {
197: try {
198: fis.close();
199: } catch (Exception e) {
200: }
201: }
202: }
203:
204: messagesMap.put(localeName, p);
205:
206: watchForChanges(localeName);
207: }
208:
209: /**
210: * Loads a new locale. If <code>localeName</code> is either null or empty, or if the locale is
211: * already loaded, the method will return without executing any code.
212: *
213: * @param localeName
214: * The locale name to load
215: */
216: public static void load(String localeName) {
217: load(localeName, SystemGlobals
218: .getValue(ConfigKeys.I18N_DEFAULT));
219: }
220:
221: public static void reset() {
222: messagesMap = new HashMap();
223: localeNames = new Properties();
224: defaultName = null;
225: }
226:
227: private static void watchForChanges(final String localeName) {
228: if (!watching.contains(localeName)) {
229: watching.add(localeName);
230:
231: int fileChangesDelay = SystemGlobals
232: .getIntValue(ConfigKeys.FILECHANGES_DELAY);
233:
234: if (fileChangesDelay > 0) {
235: FileMonitor.getInstance().addFileChangeListener(
236: new FileChangeListener() {
237: /**
238: * @see net.jforum.util.FileChangeListener#fileChanged(java.lang.String)
239: */
240: public void fileChanged(String filename) {
241: if (logger.isDebugEnabled()) {
242: logger.info("Reloading i18n for "
243: + localeName);
244: }
245:
246: I18n
247: .load(
248: localeName,
249: SystemGlobals
250: .getValue(ConfigKeys.I18N_DEFAULT),
251: true);
252: }
253: },
254: baseDir + localeNames.getProperty(localeName),
255: fileChangesDelay);
256: }
257: }
258: }
259:
260: /**
261: * Gets a I18N (internationalized) message.
262: *
263: * @param localeName
264: * The locale name to retrieve the messages from
265: * @param messageName
266: * The message name to retrieve. Must be a valid entry into the file specified by
267: * <code>i18n.file</code> property.
268: * @param params
269: * Parameters needed by some messages. The messages with extra parameters are
270: * formated according to {@link java.text.MessageFormat}specification
271: * @return String With the message
272: */
273: public static String getMessage(String localeName,
274: String messageName, Object params[]) {
275: return MessageFormat.format(((Properties) messagesMap
276: .get(localeName)).getProperty(messageName), params);
277: }
278:
279: /**
280: * @see #getMessage(String, String, Object[])
281: * @param messageName String
282: * @param params Object
283: * @return String
284: */
285: public static String getMessage(String messageName, Object params[]) {
286: String lang = "";
287: UserSession us = SessionFacade.getUserSession();
288:
289: if (us != null && us.getLang() != null) {
290: lang = us.getLang();
291: }
292:
293: if ("".equals(lang)) {
294: return getMessage(defaultName, messageName, params);
295: }
296:
297: return getMessage(lang, messageName, params);
298: }
299:
300: public static String getMessage(String messageName,
301: SimpleSequence params) {
302: try {
303: return getMessage(messageName, params.toList().toArray());
304: } catch (Exception e) {
305: throw new ForumException(e);
306: }
307: }
308:
309: /**
310: * Gets an I18N (internationalization) message.
311: *
312: * @param m
313: * The message name to retrieve. Must be a valid entry into the file specified by
314: * <code>i18n.file</code> property.
315: * @return String With the message
316: * @param localeName
317: * String
318: */
319: public static String getMessage(String localeName, String m) {
320: if (!messagesMap.containsKey(localeName)) {
321: load(localeName);
322: }
323:
324: return (((Properties) messagesMap.get(localeName))
325: .getProperty(m));
326: }
327:
328: public static String getMessage(String m) {
329: return getMessage(getUserLanguage(), m);
330: }
331:
332: public static String getMessage(String m, UserSession us) {
333: if (us == null || us.getLang() == null
334: || us.getLang().equals("")) {
335: return getMessage(defaultName, m);
336: }
337:
338: return getMessage(us.getLang(), m);
339: }
340:
341: /**
342: * Gets the language name for the current request. The method will first look at
343: * {@link UserSession#getLang()} and use it if any value is found. Otherwise, the default board
344: * language will be used
345: *
346: * @return String
347: */
348: public static String getUserLanguage() {
349: UserSession us = SessionFacade.getUserSession();
350:
351: if (us == null || us.getLang() == null
352: || us.getLang().trim().equals("")) {
353: return defaultName;
354: }
355:
356: return us.getLang();
357: }
358:
359: /**
360: * Check whether the language is loaded in i18n.
361: *
362: * @param language
363: * String
364: * @return boolean
365: */
366: public static boolean contains(String language) {
367: return messagesMap.containsKey(language);
368: }
369:
370: /**
371: * Check if the given language exist.
372: *
373: * @param language
374: * The language to check
375: * @return <code>true</code> if the language is a valid and registered translation.
376: */
377: public static boolean languageExists(String language) {
378: return (localeNames.getProperty(language) != null);
379: }
380: }
|