[Yanel-commits] rev 32613 - in public/yanel/trunk/src:
core/java/org/wyona/yanel/core core/java/org/wyona/yanel/core/i18n
core/java/org/wyona/yanel/core/map
core/java/org/wyona/yanel/core/transformation
impl/java/org/wyona/yanel/impl/resources
simon at wyona.com
simon at wyona.com
Thu Feb 28 09:52:29 CET 2008
Author: simon
Date: 2008-02-28 09:52:28 +0100 (Thu, 28 Feb 2008)
New Revision: 32613
Added:
public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/
public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/I18nUtils.java
public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageManager.java
public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageProvider.java
public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/ResourceBundleMessageProvider.java
public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/XMLMessageProvider.java
public/yanel/trunk/src/core/java/org/wyona/yanel/core/transformation/I18nTransformer3.java
Modified:
public/yanel/trunk/src/core/java/org/wyona/yanel/core/map/Realm.java
public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/BasicXMLResource.java
Log:
see: http://bugzilla.wyona.com/cgi-bin/bugzilla/show_bug.cgi?id=6035
- support xml files (to avoid encoding issues with plain text property files)
- should not be necessary to copy i18n files to WEB-INF/classes
- allow a default i18n catalogue per realm
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/I18nUtils.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/I18nUtils.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/I18nUtils.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wyona.yanel.core.i18n;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * This class holds utility methods useful when working with i18n.
+ * @author Mattias Jiderhamn
+ */
+public class I18nUtils {
+
+ private I18nUtils() {
+ }
+
+ public static Locale getParentLocale (Locale locale) {
+ if(locale.getVariant().length() != 0)
+ return new Locale(locale.getLanguage(), locale.getCountry());
+ else if(locale.getCountry().length() != 0)
+ return new Locale(locale.getLanguage());
+ else // Locale with only language have no parent
+ return null;
+ }
+}
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageManager.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageManager.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageManager.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.wyona.yanel.core.i18n;
+
+import java.text.MessageFormat;
+import java.util.*;
+
+/**
+ * The <code>MessageManager</code> provides methods for retrieving localized
+ * messages and adding custom message providers.
+ * This class should not be called directly for other purposes than registering a custom
+ * {@link MessageProvider} or retrieving information about available
+ * message entries.
+ * <p>
+ * To access localized messages a subclass of the {@link LocalizedBundle} class
+ * such as <code>LocalizedText </code> should be used:
+ *
+ * <pre>
+ * LocalizedText welcome = new LocalizedText("welcome");
+ * // Get the german translacion of the retrieved welcome text
+ * System.out.println(welcome.getText(Locale.GERMAN));
+ * </pre>
+ *
+ * <p>
+ * You can call {@link MessageManager#getText(String,String,Object[],Locale) getText} directly,
+ * but if you do so, you have to ensure that the given entry key really
+ * exists and to deal with the {@link MessageNotFoundException} exception that will
+ * be thrown if you try to access a not existing entry.</p>
+ *
+ */
+public class MessageManager {
+
+ private Map messageProviders = new LinkedHashMap();
+
+ private Locale defaultLocale;
+
+ public MessageManager(Locale defaultLocale) {
+ this.defaultLocale = defaultLocale;
+ }
+
+ /**
+ * Add a custom <code>{@link MessageProvider}</code> to the
+ * <code>MessageManager</code>. It will be incorporated in later calls of
+ * the {@link MessageManager#getText(String,String,Object[],Locale) getText}
+ * or {@link #getEntries(String,Locale) getEntries}methods.
+ *
+ * @param providerId Id of the provider used for uninstallation and
+ * qualified naming.
+ * @param messageProvider
+ * The <code>MessageProvider</code> to be added.
+ */
+ public void addMessageProvider(String providerId, MessageProvider messageProvider) {
+ messageProviders.put(providerId, messageProvider);
+ }
+
+ /**
+ * Remove all <code>{@link MessageProvider}</code>s from the
+ * <code>MessageManager</code>. Used for tearing down unit tests.
+ */
+ public void clearMessageProviders() {
+ messageProviders.clear();
+ }
+
+ /**
+ * Remove custom <code>{@link MessageProvider}</code> from the
+ * <code>MessageManager</code>. Used for tearing down unit tests.
+ *
+ * @param providerId The ID of the provider to remove.
+ */
+ public void removeMessageProvider(String providerId) {
+ messageProviders.remove(providerId);
+ }
+
+ /**
+ * Iterates over all registered message providers in order to find the given
+ * entry in the requested message bundle.
+ *
+ * @param key
+ * The identifier that will be used to retrieve the message
+ * bundle
+ * @param arguments
+ * The dynamic parts of the message that will be evaluated using
+ * the standard java text formatting abilities.
+ * @param locale
+ * The locale in which the message will be printed
+ * @return The localized message or null if no message is found
+ */
+ public String getText(String key, Object[] arguments, Locale locale) {
+ for (Iterator i = messageProviders.values().iterator(); i.hasNext();) {
+ String text = ((MessageProvider) i.next()).getText(key, locale, this.defaultLocale);
+ if(text != null) {
+ return (arguments != null && arguments.length > 0) ?
+ MessageFormat.format(text, arguments) : text;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Iterates over all registered message providers in order to find the given
+ * entry in the requested message bundle.
+ *
+ * @param key
+ * The identifier that will be used to retrieve the message
+ * bundle
+ * @param locale
+ * The locale in which the message will be printed
+ * @return The localized message or null if no message is found
+ */
+ public String getText(String key, Locale locale) {
+ Object[] arguments = new Object[0];
+ return getText(key, arguments, locale);
+ }
+
+ /**
+ * Iterates over all registered message providers in order to find the given
+ * entry in the requested message bundle.
+ *
+ * @param key
+ * The identifier that will be used to retrieve the message
+ * bundle
+ * @param arguments
+ * The dynamic parts of the message that will be evaluated using
+ * the standard java text formatting abilities.
+ * @param locale
+ * The locale in which the message will be printed
+ * @param defaultMessage
+ * If no message bundle or message entry could be found for the
+ * specified parameters, the default text will be returned.
+ * @return The localized text or the default text if the message could not
+ * be found
+ */
+ public String getText(String key, Object[] arguments, Locale locale, String defaultText) {
+ String text = getText(key, arguments, locale);
+ if (text != null) {
+ return text;
+ } else {
+ return defaultText;
+ }
+ }
+
+ /**
+ * Iterates over all registered message providers in order to find the given
+ * entry in the requested message bundle.
+ *
+ * @param key
+ * The identifier that will be used to retrieve the message
+ * bundle
+ * @param locale
+ * The locale in which the message will be printed
+ * @param defaultMessage
+ * If no message bundle or message entry could be found for the
+ * specified parameters, the default text will be returned.
+ * @return The localized text or the default text if the message could not
+ * be found
+ */
+ public String getText(String key, Locale locale, String defaultText) {
+ Object[] arguments = new Object[0];
+ return getText(key, arguments, locale, defaultText);
+ }
+
+}
\ No newline at end of file
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageProvider.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageProvider.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/MessageProvider.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.wyona.yanel.core.i18n;
+
+import java.util.Locale;
+
+/**
+ * The <code>MessageProvider</code> interface specifies the methods that
+ * must be implemented by each message provider in order to be pluggable
+ * into the <code>MessageManager</code>.
+ *
+ */
+public interface MessageProvider {
+ /**
+ * Gets the text for a given key
+ * @param key unique id that specifies a particular message
+ * @param locale the locale for which this message should be provided
+ * @param defaultLocale the default locale if the message is not found for the given locale
+ * @return returns the localized message entry matching the given message key and locale. If
+ * no match is found for the given locale, the parent locale is used, and finally the default.
+ * Returns null if no message is found.
+ */
+ public String getText(String key, Locale locale, Locale defaultLocale);
+
+}
\ No newline at end of file
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/ResourceBundleMessageProvider.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/ResourceBundleMessageProvider.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/ResourceBundleMessageProvider.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -0,0 +1,55 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+package org.wyona.yanel.core.i18n;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+
+/**
+ * The <code>ResourceBundleMessageProvider</code> deals with messages defined in
+ * resource bundles. Messages defined in resource bundles can be grouped together
+ * by adding the entry key at the end of the message key separated by a dot.
+ */
+public class ResourceBundleMessageProvider implements MessageProvider {
+ private static Logger log = Logger.getLogger(ResourceBundleMessageProvider.class.getName());
+
+ private final String baseName;
+
+ /**
+ *
+ */
+ public ResourceBundleMessageProvider(String baseName) {
+ this.baseName = baseName;
+ }
+
+ public String getText(String key, Locale locale, Locale defaultLocale) {
+ ResourceBundle resourceBundle;
+ String text = null;
+ try {
+ resourceBundle = ResourceBundle.getBundle(baseName, locale);
+ text = resourceBundle.getObject(key).toString();
+ } catch (MissingResourceException e) {
+ resourceBundle = ResourceBundle.getBundle(baseName, defaultLocale);
+ text = resourceBundle.getObject(key).toString();
+ }
+ return text;
+ }
+}
\ No newline at end of file
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/XMLMessageProvider.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/XMLMessageProvider.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/i18n/XMLMessageProvider.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -0,0 +1,164 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+package org.wyona.yanel.core.i18n;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.log4j.Logger;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * The <code>XMLMessageProvider</code> provides messages defined in an XML format.
+ *
+ */
+public class XMLMessageProvider implements MessageProvider {
+ private static final Logger log = Logger.getLogger(XMLMessageProvider.class.getName());
+
+ private static SAXParserFactory factory = SAXParserFactory.newInstance();
+
+ private Map messages = new HashMap();
+
+ public XMLMessageProvider(InputStream inputStream) {
+ try {
+ SAXParser parser = factory.newSAXParser();
+ ConfigurationHandler handler = new ConfigurationHandler();
+ parser.parse(new InputSource(inputStream), handler);
+ messages = handler.getMessages();
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public String getText(String key, Locale locale, Locale defaultLocale) {
+ String text = lookupText(key, locale);
+ if (text == null) {
+ text = lookupText(key, defaultLocale);
+ }
+ return text;
+ }
+
+ private String lookupText(String key, Locale locale) {
+ if (messages.containsKey(key)) {
+ Message message = (Message)messages.get(key);
+ while (locale != null) {
+ String localeStr = locale.toString();
+ String text = message.getText(localeStr);
+ if (text != null) {
+ return text;
+ }
+ locale = I18nUtils.getParentLocale(locale);
+ }
+ }
+ return null;
+ }
+
+ class ConfigurationHandler extends DefaultHandler {
+ private String key;
+ private String language, country, variant;
+ private Message message;
+ private HashMap parsedMessages;
+ private StringBuffer cData;
+ private boolean insideText = false;;
+
+ public ConfigurationHandler() {
+ parsedMessages = new HashMap();
+ }
+
+ public void startElement(String namespaceUri, String localeName, String qName, Attributes attributes) {
+ if (qName.matches("message")) {
+ key = attributes.getValue("key");
+ message = new Message(key);
+ } else if (qName.matches("text")) {
+ language = attributes.getValue("language");
+ country = attributes.getValue("country");
+ variant = attributes.getValue("variant");
+ cData = new StringBuffer();
+ insideText = true;
+ }
+ }
+ public void characters(char[] ch, int start, int length) {
+ if (insideText && length > 0 ) {
+ cData.append(ch, start, length);
+ }
+ }
+
+ public void endElement(String namespaceUri, String localeName, String qName) {
+ if (qName.matches("message")) {
+ parsedMessages.put(message.getKey(), message);
+ } else if (qName.matches("text")) {
+ String localeString = (language == null ? "" : language)
+ + (country == null ? "" : "_" + country)
+ + (country == null && variant != null ? "_" : "")
+ + (variant == null ? "" : "_" + variant);
+ message.setText(localeString, cData.toString());
+ insideText = false;
+ }
+ }
+
+ Map getMessages() {
+ return parsedMessages;
+ }
+ }
+
+ static class Message {
+ private final String key;
+ private HashMap values;
+
+ public Message(String key) {
+ this.key = key;
+ this.values = new HashMap();
+ }
+
+ public String getText(String locale) {
+ return (String)values.get(locale);
+ }
+
+ public void setText(String locale, String value) {
+ values.put(locale, value);
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer(key + "(");
+ Iterator iter = values.keySet().iterator();
+ while (iter.hasNext()) {
+ String locale = (String)iter.next();
+ String value = (String)values.get(locale);
+ sb.append(locale + ":" + value);
+ if (iter.hasNext()) {
+ sb.append(",");
+ }
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+ }
+}
\ No newline at end of file
Modified: public/yanel/trunk/src/core/java/org/wyona/yanel/core/map/Realm.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/map/Realm.java 2008-02-28 08:30:22 UTC (rev 32612)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/map/Realm.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -64,6 +64,7 @@
private File configFile;
private File rootDir;
private String[] languages;
+ private String i18nCatalogue;
private boolean proxySet = false;
private String proxyHostName;
@@ -247,6 +248,11 @@
Repository repo = extraRepoFactory.newRepository(id, repoConfig);
}
}
+
+ configElement = config.getChild("i18n-catalogue", false);
+ if (configElement != null) {
+ this.i18nCatalogue = configElement.getValue();
+ }
}
/**
@@ -496,4 +502,15 @@
String defaultWebAuthenticatorImplClassName = "org.wyona.yanel.servlet.security.impl.DefaultWebAuthenticatorImpl";
return (WebAuthenticator) Class.forName(defaultWebAuthenticatorImplClassName).newInstance();
}
+
+ /**
+ * Gets the value of the i18n-catalogue config element.
+ * This value normally is a URI pointing to an i18n message catalogue.
+ * @return i18n catalogue
+ */
+ public String getI18nCatalogue() {
+ return this.i18nCatalogue;
+ }
+
+
}
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/transformation/I18nTransformer3.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/transformation/I18nTransformer3.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/transformation/I18nTransformer3.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -0,0 +1,248 @@
+package org.wyona.yanel.core.transformation;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+
+import org.apache.log4j.Category;
+import org.wyona.yanel.core.i18n.MessageManager;
+import org.wyona.yanel.core.i18n.MessageProvider;
+import org.wyona.yanel.core.i18n.ResourceBundleMessageProvider;
+import org.wyona.yanel.core.i18n.XMLMessageProvider;
+import org.wyona.yanel.core.source.SourceException;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+
+/**
+ * Transformer to translate content to a certain language using a message catalogue.
+ * This i18n transformer supports two kinds of syntax:
+ * <ol>
+ * <li>old (deprecated):
+ * <pre>
+ * <i18n:message key="pageInfo">Page Info Default Text</i18n:message>
+ * <i18n:message>pageInfo</i18n:message>
+ * <input type="submit" value="i18n:attr key=mySubmit"/>
+ * </pre>
+ * </li>
+ * <li>new (cocoon-like):
+ * <pre>
+ * <i18n:text xmlns:i18n="http://www.wyona.org/yanel/i18n/1.0" key="pageInfo">Page Info Default Text</i18n:text>
+ * <i18n:text>pageInfo</i18n:text>
+ * <input type="submit" value="mySubmit" i18n:attr="value"/>
+ * </pre>
+ * </li>
+ * </ol>
+ * The namespace uri is http://www.wyona.org/yanel/i18n/1.0
+ * <br/>
+ * Version 3 of this transformer is able to use XML message catalogues and also
+ * ResourceBundle properties. (XML message catalogues are the recommended option,
+ * ResourceBundles are supported mainly for backwards compatibility).
+ * <br/>
+ * A ResourceBundle is a set of property files which will be found in the classpath.
+ * (E.g. myi18n_de.properties, myi18n_en.properties)
+ * In this case the catalogue string is the base name of the resource bundle.
+ *
+ * <br/>
+ * An XML message catalogue is an XML file which will be retrieved via SourceResolver.
+ * In this case the catalogue string is a URI (e.g. yanelrepo:/i18n/myi18n.xml)
+ * <br/>
+ * The format of the XML file is the following:
+ *
+ * <pre>
+ * <messages>
+ * <message key="user">
+ * <text language="de">Benutzer</text>
+ * <text language="en">User</text>
+ * </message>
+ * <message key="logout">
+ * <text language="de">Abmelden</text>
+ * <text language="en">Log out</text>
+ * </message>
+ * </messages>
+ * </pre>
+ */
+public class I18nTransformer3 extends AbstractTransformer {
+
+ private static Category log = Category.getInstance(I18nTransformer2.class);
+ private MessageManager messageManager;
+ private URIResolver resolver;
+ private boolean insideI18n;
+ private String key;
+ private StringBuffer textBuffer;
+ private Locale locale;
+
+ public static final String NS_URI = "http://www.wyona.org/yanel/i18n/1.0";
+
+ public I18nTransformer3(String catalogue, String language, String defaultLanguage, URIResolver resolver) {
+ this.resolver = resolver;
+ this.locale = new Locale(language);
+ Locale defaultLocale = new Locale(defaultLanguage);
+ this.messageManager = new MessageManager(defaultLocale);
+ MessageProvider messageProvider = getMessageProvider(catalogue);
+ this.messageManager.addMessageProvider("catalogue-0", messageProvider);
+ }
+
+ public I18nTransformer3(String[] catalogues, String language, String defaultLanguage, URIResolver resolver) {
+ this.resolver = resolver;
+ this.locale = new Locale(language);
+ Locale defaultLocale = new Locale(defaultLanguage);
+ this.messageManager = new MessageManager(defaultLocale);
+
+ for (int i = 0; i < catalogues.length; i++) {
+ MessageProvider messageProvider = getMessageProvider(catalogues[i]);
+ this.messageManager.addMessageProvider("catalogue-" + i, messageProvider);
+ }
+ }
+
+ protected MessageProvider getMessageProvider(String catalogue) {
+ if (catalogue.indexOf(":") == -1) {
+ return new ResourceBundleMessageProvider(catalogue);
+ } else {
+ try {
+ Source source = resolver.resolve(catalogue, null);
+ InputStream is = SAXSource.sourceToInputSource(source).getByteStream();
+ return new XMLMessageProvider(is);
+ } catch (SourceException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (TransformerException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+
+ protected String getMessage(String key) {
+ String value = this.messageManager.getText(key, this.locale);
+ if (value == null) {
+ log.error("cannot find message for key: " + key);
+ }
+ return value;
+ }
+
+ public void startElement(String namespaceURI, String localName, String qName, Attributes attrs) throws SAXException {
+
+ if (this.insideI18n) {
+ throw new SAXException("no elements allowed inside of i18n element");
+ }
+
+ if (isI18nElement(namespaceURI, localName, qName)) {
+ this.insideI18n = true;
+ this.textBuffer = new StringBuffer();
+ this.key = attrs.getValue("key");
+
+ } else {
+ // translate attributes:
+
+ int index = attrs.getIndex(NS_URI, "attr");
+
+ if (index != -1) {
+ List i18nAttrs = Arrays.asList(attrs.getValue(index).split(" "));
+ AttributesImpl newAttrs = new AttributesImpl();
+
+ for(int i = 0; i < attrs.getLength(); i++) {
+ String attrUri = attrs.getURI(i);
+ String attrLocalName = attrs.getLocalName(i);
+ String attrQName = attrs.getQName(i);
+ String attrValue = attrs.getValue(i);
+ String attrType = attrs.getType(i);
+
+ if (!attrLocalName.equals("attr") || !attrUri.equals(NS_URI)) {
+ if (i18nAttrs.contains(attrQName)) {
+ String i18nValue = getMessage(attrValue);
+ if (i18nValue == null) {
+ i18nValue = attrValue;
+ }
+ newAttrs.addAttribute(attrUri, attrLocalName, attrQName, attrType, i18nValue);
+ } else {
+ newAttrs.addAttribute(attrUri, attrLocalName, attrQName, attrType, attrValue);
+ }
+ }
+ }
+ super.startElement(namespaceURI, localName, qName, newAttrs);
+
+ } else {
+
+ // support old i18n attribute syntax for compatibility reasons:
+ AttributesImpl newAttrs = new AttributesImpl();
+ for(int i = 0; i < attrs.getLength(); i++) {
+ String attrUri = attrs.getURI(i);
+ String attrLocalName = attrs.getLocalName(i);
+ String attrQName = attrs.getQName(i);
+ String attrValue = attrs.getValue(i);
+ String attrType = attrs.getType(i);
+
+ if (attrValue.indexOf("i18n:attr key=") != -1) {
+ String key = attrValue.substring(14);
+
+ String i18nValue = getMessage(key);
+ if (i18nValue == null) {
+ i18nValue = key;
+ }
+ newAttrs.addAttribute(attrUri, attrLocalName, attrQName, attrType, i18nValue);
+ } else {
+ newAttrs.addAttribute(attrUri, attrLocalName, attrQName, attrType, attrValue);
+ }
+ }
+ super.startElement(namespaceURI, localName, qName, newAttrs);
+ }
+ }
+ }
+
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ if (isI18nElement(namespaceURI, localName, qName)) {
+ String defaultText = this.textBuffer.toString();
+ if (this.key == null) {
+ this.key = defaultText;
+ }
+ String i18nText = getMessage(key);
+ if (i18nText == null) {
+ i18nText = defaultText;
+ }
+ if (i18nText.length() == 0) {
+ i18nText = key;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("TAG [key] " + this.key + " [message]" + i18nText);
+ }
+ char[] i18nMessage = i18nText.toCharArray();
+ this.insideI18n = false;
+ characters(i18nMessage, 0, i18nMessage.length);
+ } else {
+ super.endElement(namespaceURI, localName, qName);
+ }
+ }
+
+ /**
+ * Decides whether a the given element is a i18n element.
+ * Suppports the <text> element and for backwards compatibility also
+ * the <message> element.
+ * @param namespaceURI
+ * @param localName
+ * @param qName
+ * @return true if the element is a i18n element
+ */
+ protected boolean isI18nElement(String namespaceURI, String localName, String qName) {
+ if (namespaceURI.equals(NS_URI) && (localName.equals("text") || localName.equals("message"))) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void characters(char[] buf, int offset, int len) throws SAXException {
+ if (this.insideI18n) {
+ this.textBuffer.append(buf, offset, len);
+ } else {
+ super.characters(buf, offset, len);
+ }
+ }
+
+}
Modified: public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/BasicXMLResource.java
===================================================================
--- public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/BasicXMLResource.java 2008-02-28 08:30:22 UTC (rev 32612)
+++ public/yanel/trunk/src/impl/java/org/wyona/yanel/impl/resources/BasicXMLResource.java 2008-02-28 08:52:28 UTC (rev 32613)
@@ -19,6 +19,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
@@ -47,6 +48,7 @@
import org.wyona.yanel.core.serialization.SerializerFactory;
import org.wyona.yanel.core.source.SourceResolver;
import org.wyona.yanel.core.transformation.I18nTransformer2;
+import org.wyona.yanel.core.transformation.I18nTransformer3;
import org.wyona.yanel.core.transformation.XIncludeTransformer;
import org.wyona.yanel.core.util.PathUtil;
import org.wyona.yanel.impl.resources.xml.ConfigurableViewDescriptor;
@@ -250,7 +252,7 @@
}
// create i18n transformer:
- I18nTransformer2 i18nTransformer = new I18nTransformer2(getI18NCatalogueNames(), getRequestedLanguage(), getRealm().getDefaultLanguage());
+ I18nTransformer3 i18nTransformer = new I18nTransformer3(getI18NCatalogueNames(), getRequestedLanguage(), getRealm().getDefaultLanguage(), uriResolver);
i18nTransformer.setEntityResolver(catalogResolver);
// create xinclude transformer:
@@ -335,16 +337,24 @@
/**
* Gets the names of the i18n message catalogues used for the i18n transformation.
- * Looks for an rc config property named 'i18n-catalogue'. Defaults to 'global'.
+ * Uses the following priorization:
+ * 1. rc config properties named 'i18n-catalogue'.
+ * 2. realm i18n-catalogue
+ * 3. 'global'
* @return i18n catalogue name
*/
protected String[] getI18NCatalogueNames() throws Exception {
- String[] catalogueNames = getResourceConfigProperties("i18n-catalogue");
- if (catalogueNames == null || catalogueNames.length == 0) {
- catalogueNames = new String[1];
- catalogueNames[0] = "global";
+ ArrayList catalogues = new ArrayList();
+ String[] rcCatalogues = getResourceConfigProperties("i18n-catalogue");
+ for (int i = 0; i < rcCatalogues.length; i++) {
+ catalogues.add(rcCatalogues[i]);
}
- return catalogueNames;
+ String realmCatalogue = getRealm().getI18nCatalogue();
+ if (realmCatalogue != null) {
+ catalogues.add(realmCatalogue);
+ }
+ catalogues.add("global");
+ return (String [])catalogues.toArray(new String[catalogues.size()]);
}
/**
More information about the Yanel-commits
mailing list