001: /*
002: * Copyright 2003 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package velosurf.web.l10n;
018:
019: import java.util.Locale;
020: import java.util.HashMap;
021: import java.util.Map;
022: import java.util.Iterator;
023:
024: import javax.servlet.ServletContext;
025: import javax.servlet.http.HttpSession;
026:
027: import org.apache.velocity.tools.view.context.ViewContext;
028:
029: import velosurf.util.Logger;
030: import velosurf.context.Instance;
031: import velosurf.context.DBReference;
032: import velosurf.context.EntityReference;
033: import velosurf.web.VelosurfTool;
034:
035: /**
036: * <p>A basic database based Localizer implementation.</p>
037: * <p>The following <code>toolbox.xml</code> parameters are available:</p>
038: * <ul>
039: * <li><code>localized-table</code>: the name of the table containing localized strings (default: "<code>localized</code>").</li>
040: * <li><code>id-field</code>: the name of the field containing the string id (default: "<code>id</code>").</li>
041: * <li><code>locale-field</code>: the name of the field containing the ISO locale code (default: "<code>locale</code>").</li>
042: * <li><code>string-field</code>: the name of the field containing the lcoalized string (default: "<code>string</code>").</li>
043: * </ul>
044: *
045: * <p>You can find on the web the list of
046: * <a href="http://www.loc.gov/standards/iso639-2/englangn.html">ISO Language Codes</a>
047: * and the list of
048: * <a href="http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">ISO Country Codes</a>.
049: *
050: */
051:
052: public class SimpleDBLocalizer extends HTTPLocalizerTool {
053:
054: /** localized table name parameter key. */
055: private static final String LOCALIZED_TABLE_KEY = "localized-table";
056: /** id field name parameter key. */
057: private static final String ID_FIELD_KEY = "id-field";
058: /** locale field name parameter key. */
059: private static final String LOCALE_FIELD_KEY = "locale-field";
060: /** localized string field name parameter key. */
061: private static final String STRING_FIELD_KEY = "string-field";
062:
063: /** default localized table name. */
064: private static final String LOCALIZED_TABLE_DEFAULT = "localized";
065: /** default id fied name. */
066: private static final String ID_FIELD_DEFAULT = "id";
067: /** default locale field name. */
068: private static final String LOCALE_FIELD_DEFAULT = "locale";
069: /** default localized string field name. */
070: private static final String STRING_FIELD_DEFAULT = "string";
071:
072: /** localized table name. */
073: private static String localizedTable = LOCALIZED_TABLE_DEFAULT;
074: /** id field name. */
075: private static String idField = ID_FIELD_DEFAULT;
076: /** locale field name. */
077: private static String localeField = LOCALE_FIELD_DEFAULT;
078: /** localized string field name. */
079: private static String stringField = STRING_FIELD_DEFAULT;
080:
081: /** initialization status. */
082: private static boolean initialized = false;
083:
084: /** map locale -> (id -> localized). */
085: private static Map<Locale, Map<Object, String>> localeStrings = null;
086:
087: /** map (id -> localized) for the current locale. */
088: private Map<Object, String> currentStrings = null;
089:
090: /** tool configuration. */
091: private Map config;
092:
093: /**
094: * Constructor.
095: */
096: public SimpleDBLocalizer() {
097: }
098:
099: /**
100: * Configure this tool.
101: * @param config tool configuration
102: */
103: public void configure(Map config) {
104: this .config = config;
105: }
106:
107: /**
108: * Initialize this tool.
109: * @param initData a view context
110: */
111: public void init(Object initData) {
112: if (!initialized) {
113: if (config != null) {
114: String value;
115: value = (String) config.get(LOCALIZED_TABLE_KEY);
116: if (value != null) {
117: localizedTable = value;
118: }
119: value = (String) config.get(ID_FIELD_KEY);
120: if (value != null) {
121: idField = value;
122: }
123: value = (String) config.get(LOCALE_FIELD_KEY);
124: if (value != null) {
125: localeField = value;
126: }
127: value = (String) config.get(STRING_FIELD_KEY);
128: if (value != null) {
129: stringField = value;
130: }
131: }
132: ServletContext ctx = initData instanceof ServletContext ? (ServletContext) initData
133: : ((ViewContext) initData).getServletContext();
134: readLocales(ctx);
135:
136: if (initData instanceof ViewContext) {
137: HttpSession session = ((ViewContext) initData)
138: .getRequest().getSession(true);
139: session.setAttribute(Localizer.class.getName(), this );
140: }
141: }
142: super .init(initData);
143: }
144:
145: /**
146: * read localized messages into memory.
147: * @param ctx servlet context
148: */
149: private static synchronized void readLocales(ServletContext ctx) {
150: if (initialized)
151: return;
152: try {
153: DBReference db = VelosurfTool.getDefaultInstance(ctx);
154: if (db == null) {
155: throw new Exception("Cannot find database!");
156: }
157: EntityReference entity = (EntityReference) db
158: .get(localizedTable);
159: if (entity == null) {
160: throw new Exception(
161: "Cannot find 'localized' database entity!");
162: }
163: localeStrings = new HashMap<Locale, Map<Object, String>>();
164: entity.setOrder(localeField);
165: Iterator locales = entity.iterator(); // sorted by locale
166: Map<Object, String> map = null;
167: String current = null;
168: Locale loc = null;
169: while (locales.hasNext()) {
170: Instance row = (Instance) locales.next();
171: String key = (String) row.get(idField);
172: String locale = (String) row.get(localeField);
173: String string = (String) row.get(stringField);
174: if (!locale.equals(current)) {
175: current = locale;
176: Logger.trace("Found new locale in db: " + locale);
177: map = new HashMap<Object, String>();
178: /* for now, take language and country into account... TODO: take variant into account */
179: int sep = locale.indexOf('_');
180: loc = (sep == -1 ? new Locale(locale) : new Locale(
181: locale.substring(0, sep), locale
182: .substring(sep + 1)));
183: localeStrings.put(loc, map);
184: }
185: map.put(key, string);
186: }
187: initialized = true;
188: } catch (Exception e) {
189: // Logger.log(e);
190: Logger.error(e.getMessage());
191: }
192: }
193:
194: /**
195: * Check for the presence of a locale.
196: * @param locale locale
197: * @return true if present
198: */
199: public boolean hasLocale(Locale locale) {
200: return localeStrings.containsKey(locale);
201: }
202:
203: /**
204: * Locale setter.
205: * @param locale locale
206: */
207: public void setLocale(Locale locale) {
208: if (locale == null && this .locale == null || locale != null
209: && locale.equals(this .locale)) {
210: /* no change */
211: return;
212: }
213: super .setLocale(locale);
214: if (localeStrings != null) {
215: currentStrings = localeStrings.get(getLocale());
216: }
217: if (currentStrings == null) {
218: Logger.warn("l10n: no strings found for locale " + locale);
219: }
220: }
221:
222: /**
223: * Localized message getter.
224: * @param id message id
225: * @return localized message or id itself if not found
226: */
227: public String get(Object id) {
228: if (currentStrings == null) {
229: Logger
230: .warn("l10n: no current locale! (was getting string id '"
231: + id + "')");
232: return id.toString();
233: }
234: String message = currentStrings.get(id);
235: //Logger.trace("l10n: "+id+" -> "+message);
236: return message == null ? id.toString() : message;
237: }
238: }
|