You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by di...@apache.org on 2001/07/19 13:22:22 UTC
cvs commit: xml-cocoon2/webapp/i18n/translations convert.xsl messages.xml messages_de.xml messages_en.xml messages_es.xml messages_hy.xml messages_pl.xml messages_ru.xml
dims 01/07/19 04:22:22
Modified: src/org/apache/cocoon/transformation I18nTransformer.java
webapp sitemap.xmap
webapp/i18n simple.xml simple.xsp sitemap.xmap
Added: src/org/apache/cocoon/i18n XMLResourceBundle.java
XMLResourceBundleFactory.java
webapp/i18n/translations convert.xsl messages.xml
messages_de.xml messages_en.xml messages_es.xml
messages_hy.xml messages_pl.xml messages_ru.xml
Log:
Patches from Marcus Crafter <cr...@fztig938.bank.dresdner.net> for i18n
Revision Changes Path
1.1 xml-cocoon2/src/org/apache/cocoon/i18n/XMLResourceBundle.java
Index: XMLResourceBundle.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
//package org.apache.avalon.excalibur.i18n;
package org.apache.cocoon.i18n;
/** JDK classes **/
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Enumeration;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
/** W3C DOM classes **/
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;
/** SAX classes **/
import org.xml.sax.SAXException;
/** Parser classes **/
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
/** XPath classes **/
import org.apache.xpath.XPathAPI;
/** TRaX classes **/
import javax.xml.transform.TransformerException;
/** Avalon classes **/
import org.apache.avalon.framework.logger.Loggable;
import org.apache.avalon.framework.component.Component;
import org.apache.log.Logger;
/**
* @author <a href="mailto:mengelhart@earthtrip.com">Mike Engelhart</a>
* @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
* @author <a href="mailto:oleg@one.lv">Oleg Podolsky</a>
* @version $Id: XMLResourceBundle.java,v 1.1 2001/07/19 11:22:21 dims Exp $
*/
public class XMLResourceBundle
extends ResourceBundle
implements Loggable, Component
{
/** DOM factory */
protected static DocumentBuilderFactory docfactory =
DocumentBuilderFactory.newInstance();
/** Cache for storing string values for existing XPaths */
private Hashtable cache = new Hashtable();
/** Cache for storing non-existing XPaths */
private Map cacheNotFound = new HashMap();
/** Bundle name */
private String name = "";
/** DOM-tree containing the bundle content */
private Document doc;
/** Locale of the bundle */
private Locale locale;
/** Parent of the current bundle */
protected XMLResourceBundle parent = null;
/** Logger */
protected Logger logger;
/**
* Initalize the bundle
*
* @param name name of the bundle
* @param fileName name of the XML source file
* @param locale locale
* @param parent parent bundle of this bundle
* @param cacheAtStartup cache all the keys when constructing?
* @exception IOException if an IO error occurs while reading the file
* @exception ParserConfigurationException if no parser is configured
* @exception SAXException if an error occurs while parsing the file
*/
public void init(String name, String fileName, Locale locale, XMLResourceBundle parent, boolean cacheAtStartup)
throws IOException, ParserConfigurationException, SAXException
{
if (logger.isInfoEnabled()) logger.info("Constructing XMLResourceBundle: " + name + ", locale: " + locale);
this.name = name;
this.doc = loadResourceBundle(fileName);
this.locale = locale;
this.parent = parent;
if (cacheAtStartup)
cacheAll(doc.getDocumentElement(), "");
}
/**
* Load the DOM tree, based on the file name.
*
* @param fileName name of the XML source file
* @return the DOM tree
* @exception IOException if an IO error occurs while reading the file
* @exception ParserConfigurationException if no parser is configured
* @exception SAXException if an error occurs while parsing the file
*/
protected static Document loadResourceBundle(String fileName)
throws IOException, ParserConfigurationException, SAXException
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(fileName);
}
/**
* Gets the name of the bundle.
*
* @return the name
*/
public String getName()
{
return this.name;
}
/**
* Gets the source DOM tree of the bundle.
*
* @return the DOM tree
*/
public Document getDocument()
{
return this.doc;
}
/**
* Gets the locale of the bundle.
*
* @return the locale
*/
public Locale getLocale()
{
return locale;
}
/**
* Does the "key-cache" contain the value with such key?
*
* @param key the key to the value to be returned
* @return true if contains, false otherwise
*/
private boolean cacheContains(String key)
{
if (logger.isDebugEnabled()) logger.debug(name + ": cache contains: " + key);
return cache.containsKey(key);
}
/**
* Does the "key-not-found-cache" contain such key?
*
* @param key the key to the value to be returned
* @return true if contains, false otherwise
*/
private boolean cacheNotFoundContains(String key)
{
if (logger.isDebugEnabled()) logger.debug(name + ": cache_not_found contains: " + key);
return cacheNotFound.containsKey(key);
}
/**
* Cache the key and value in "key-cache".
*
* @param key the key
* @param value the value
*/
private void cacheKey(String key, String value)
{
if (logger.isDebugEnabled()) logger.debug(name + ": caching: " + key + " = " + value);
cache.put(key, value);
}
/**
* Cache the key in "key-not-found-cache".
*
* @param key the key
*/
private void cacheNotFoundKey(String key)
{
if (logger.isDebugEnabled()) logger.debug(name + ": caching not_found: " + key);
cacheNotFound.put(key, "");
}
/**
* Gets the value by the key from the "key-cache".
*
* @param key the key
* @return the value
*/
private String getFromCache(String key)
{
if (logger.isDebugEnabled()) logger.debug(name + ": returning from cache: " + key);
return (String) cache.get(key);
}
/**
* Steps through the bundle tree and stores all text element values
* in bundle's cache. Also stores attributes for all element nodes.
*
* @param parent parent node, must be an element
* @param pathToParent XPath to the parent node
*/
private void cacheAll(Node parent, String pathToParent)
{
NodeList children = parent.getChildNodes();
int childnum = children.getLength();
for(int i = 0; i < childnum; i++)
{
Node child = children.item(i);
if(child.getNodeType() == Node.ELEMENT_NODE)
{
String pathToChild = pathToParent + '/' + child.getNodeName();
NamedNodeMap attrs = child.getAttributes();
if(attrs != null)
{
Node temp = null;
String pathToAttr = null;
int attrnum = attrs.getLength();
for(int j = 0; j < attrnum; j++)
{
temp = attrs.item(j);
if (!temp.getNodeName().equalsIgnoreCase("xml:lang"))
pathToChild += "[@" + temp.getNodeName() + "='" + temp.getNodeValue() + "']";
}
}
String childValue = getTextValue(child);
if(childValue != null)
cacheKey(pathToChild, childValue);
else
cacheAll(child, pathToChild);
}
}
}
/**
* Get value by key and substitute variables.
*
* @param key key
* @param dictionary map with variable values
* @return value with variable values substituted
* @exception MissingResourceException if resource was not found
*/
public String getString(String key, Map dictionary)
throws MissingResourceException
{
String value = _getString(key);
if (value == null)
throw new MissingResourceException(
"Unable to locate resource: " + key,
XMLResourceBundle.class.getName(),
key);
else
return substitute(value, dictionary);
}
/**
* Get value by key.
*
* @param key the key
* @return the value
*/
private String _getString(String key)
{
if (key == null) return null;
String value = getFromCache(key);
if (value == null && !cacheNotFoundContains(key))
{
if (doc != null)
value = _getString(this.doc.getDocumentElement(), key);
if (value == null)
{
if (this.parent != null)
value = this.parent._getString(key);
}
if (value != null)
cacheKey(key, value);
else
cacheNotFoundKey(key);
}
return value;
}
/**
* Get value by key from a concrete node.
*
* @param node the node
* @param key the key
* @return the value
*/
private String _getString(Node node, String key)
{
String value = null;
try
{
value = getTextValue(_getNode(node, key));
}
catch (Exception e)
{
logger.error(name + ": error while locating resource: " + key, e);
}
return value;
}
/**
* Get the text value of the node.
*
* @param node the node
* @return the value
*/
private static String getTextValue(Node element)
{
if (element == null) return null;
NodeList list = element.getChildNodes();
int listsize = list.getLength();
Node item = null;
String itemValue = null;
for(int i = 0; i < listsize; i++)
{
item = list.item(i);
if(item.getNodeType() != Node.TEXT_NODE)
return null;
itemValue = item.getNodeValue();
if(itemValue == null)
return null;
itemValue = itemValue.trim();
if(itemValue.length() == 0)
return null;
return itemValue;
}
return null;
}
/**
* Get the node with the supplied XPath key.
*
* @param key the key
* @return the node
*/
private Node _getNode(String key)
{
return _getNode(this.doc.getDocumentElement(), key);
}
/**
* Get the node with the supplied XPath key, starting from concrete
* root node.
*
* @param rootNode the root node
* @param key the key
* @return the node
*/
private Node _getNode(Node rootNode, String key)
{
Node node = null;
try
{
node = XPathAPI.selectSingleNode(rootNode, key);
}
catch (Exception e)
{
logger.error("Error while locating resource with key: " + key, e);
}
return node;
}
/**
* Substitute the "variables" in the string with the values
* provided in the map.
*
* @param value value where to search for variables
* @param dictionary map with variable values
* @return value with variable values substituted
*/
public String substitute(String value, Map dictionary)
{
if (value == null || dictionary == null) return value;
StringBuffer result = new StringBuffer(value.length());
int startPos = 0;
int endPos = 0;
int lastPos = value.length();
Object varValue = "";
String varKey = "", oldKey = "";
while (endPos < lastPos)
{
startPos = endPos;
endPos = value.indexOf('{', startPos);
if (endPos == -1)
endPos = lastPos;
result.append(value.substring(startPos, endPos));
if (endPos < lastPos)
endPos++;
if (endPos < lastPos)
{
startPos = endPos;
endPos = value.indexOf('}', startPos);
if (endPos == -1)
endPos = lastPos;
oldKey = varKey;
varKey = value.substring(startPos, endPos);
if (!oldKey.equals(varKey))
varValue = dictionary.get(varKey);
if (varValue != null)
{
if (logger.isDebugEnabled()) logger.debug("Substituting var: " + varKey + " --> " + varValue);
result.append(varValue);
}
else
{
if (logger.isWarnEnabled()) logger.warn(name + ": var not found: " + varKey);
result.append('{').append(varKey).append('}');
}
if (endPos < lastPos)
endPos++;
}
}
return result.toString();
}
/**
* Set the logger.
*
* @param logger the logger
*/
public void setLogger( final Logger logger )
{
this.logger = logger;
}
/**
* Return an Object by key.
* Implementation of the ResourceBundle abstract method.
*
* @param key the key
* @return the object
*/
protected Object handleGetObject(String key)
throws MissingResourceException
{
return (Object) getString(key, null);
}
/**
* Return an enumeration of the keys.
* Implementation of the ResourceBundle abstract method.
*
* @return the enumeration of keys
*/
public Enumeration getKeys()
{
return cache.keys();
}
}
1.1 xml-cocoon2/src/org/apache/cocoon/i18n/XMLResourceBundleFactory.java
Index: XMLResourceBundleFactory.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
//package org.apache.avalon.excalibur.i18n;
package org.apache.cocoon.i18n;
import java.util.Map;
import java.util.HashMap;
import java.util.Locale;
import org.xml.sax.SAXParseException;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.logger.Loggable;
import org.apache.log.Logger;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.DefaultComponentSelector;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.thread.ThreadSafe;
/**
* This is the XMLResourceBundleFactory, the method for getting and
* creating XMLResourceBundles.
*
* @author <a href="mailto:mengelhart@earthtrip.com">Mike Engelhart</a>
* @author <a href="mailto:neeme@one.lv">Neeme Praks</a>
* @author <a href="mailto:oleg@one.lv">Oleg Podolsky</a>
* @version $Id: XMLResourceBundleFactory.java,v 1.1 2001/07/19 11:22:21 dims Exp $
*/
public class XMLResourceBundleFactory
extends DefaultComponentSelector
implements Configurable, Loggable, ThreadSafe
{
/** Should we load bundles to cache on startup or not? */
protected boolean cacheAtStartup = false;
/** Root directory to all bundle names */
protected String directory;
/** Cache for the names of the bundles that were not found */
protected Map cacheNotFound = new HashMap();
/** The logger */
protected Logger logger;
/** Constants for configuration keys */
public static class ConfigurationKeys
{
public static final String CACHE_AT_STARTUP = "cache-at-startup";
public static final String ROOT_DIRECTORY = "catalogue-location";
}
/**
* Default constructor.
*/
public XMLResourceBundleFactory()
{
}
/**
* Set the logger.
*
* @param logger the logger
*/
public void setLogger( final Logger logger )
{
this.logger = logger;
}
/**
* Configure the component.
*
* @param configuration the configuration
*/
public void configure( Configuration configuration ) throws ConfigurationException
{
this.cacheAtStartup = configuration.getChild(ConfigurationKeys.CACHE_AT_STARTUP).getValueAsBoolean(false);
try
{
this.directory = configuration.getChild(ConfigurationKeys.ROOT_DIRECTORY, true).getValue();
}
catch (ConfigurationException e)
{
if (logger.isWarnEnabled()) logger.warn("Root directory not provided in configuration, using default (root).");
this.directory = "";
}
if (logger.isDebugEnabled())
{
logger.debug(
"XMLResourceBundleFactory configured with: cacheAtStartup = " +
cacheAtStartup + ", directory = '" + directory + "'"
);
}
}
/**
* Select a bundle based on bundle name and locale.
*
* @param name bundle name
* @param locale locale
* @return the bundle
* @exception ComponentException if a bundle is not found
*/
public Component select(String name, Locale locale)
throws ComponentException
{
return select(name, locale, cacheAtStartup);
}
/**
* Select a bundle based on bundle name and locale.
*
* @param name bundle name
* @param locale locale
* @param cacheAtStartup cache all the keys when constructing?
* @return the bundle
* @exception ComponentException if a bundle is not found
*/
public Component select(String name, Locale loc, boolean cacheAtStartup)
throws ComponentException
{
Component bundle = _select(name, loc, cacheAtStartup);
if (bundle == null)
throw new ComponentException("Unable to locate resource: " + name);
return bundle;
}
/**
* Select a bundle based on bundle name and locale.
*
* @param name bundle name
* @param locale locale
* @return the bundle
*/
private Component _select(String name, Locale loc)
{
return _select(name, loc, cacheAtStartup);
}
/**
* Select the parent bundle of the current bundle, based on
* bundle name and locale.
*
* @param name bundle name
* @param locale locale
* @return the bundle
*/
protected Component selectParent(String name, Locale loc)
{
return selectParent(name, loc, cacheAtStartup);
}
/**
* Select the parent bundle of the current bundle, based on
* bundle name and locale.
*
* @param name bundle name
* @param locale locale
* @param cacheAtStartup cache all the keys when constructing?
* @return the bundle
*/
protected Component selectParent(String name, Locale loc, boolean cacheAtStartup)
{
return _select(name, getParentLocale(loc), cacheAtStartup);
}
/**
* Select a bundle based on bundle name and locale name.
*
* @param name bundle name
* @param localeName locale name
* @return the bundle
* @exception ComponentException if a bundle is not found
*/
public Component select(String name, String localeName)
throws ComponentException
{
return select(name, new Locale(localeName, localeName) );
}
/**
* Select a bundle based on source XML file name.
*
* @param fileName file name
* @return the bundle
* @exception ComponentException if a bundle is not found
*/
public Component selectFromFilename(String fileName)
throws ComponentException
{
return selectFromFilename(fileName, cacheAtStartup);
}
/**
* Select a bundle based on source XML file name.
*
* @param fileName file name
* @param cacheAtStartup cache all the keys when constructing?
* @return the bundle
* @exception ComponentException if a bundle is not found
*/
public Component selectFromFilename(String fileName, boolean cacheAtStartup)
throws ComponentException
{
Component bundle = _select(fileName, null, cacheAtStartup);
if (bundle == null)
throw new ComponentException("Unable to locate resource: " + fileName);
return bundle;
}
/**
* Select a bundle based on bundle name and locale.
*
* @param name bundle name
* @param locale locale
* @param cacheAtStartup cache all the keys when constructing?
* @return the bundle
* @exception ComponentException if a bundle is not found
*/
private Component _select(String name, Locale loc, boolean cacheAtStartup)
{
if (logger.isDebugEnabled()) logger.debug("_getBundle: " + name + ", locale " + loc);
String fileName = getFileName(name, loc);
XMLResourceBundle bundle = (XMLResourceBundle) selectCached(fileName);
if (bundle == null && !isNotFoundBundle(fileName))
{
if (logger.isDebugEnabled()) logger.debug("not found in cache, loading: " + fileName);
synchronized(this)
{
bundle = (XMLResourceBundle) selectCached(fileName);
if (bundle == null && !isNotFoundBundle(fileName))
{
if (logger.isDebugEnabled()) logger.debug("synchronized: not found in cache, loading: " + fileName);
bundle = _loadBundle(name, fileName, loc, cacheAtStartup);
Locale parentLoc = loc;
String parentBundleName;
while (bundle == null && parentLoc != null && !parentLoc.getLanguage().equals(""))
{
if (logger.isDebugEnabled()) logger.debug("synchronized: still not found, trying parent: " + fileName);
parentLoc = getParentLocale(parentLoc);
parentBundleName = getFileName(name, parentLoc);
bundle = _loadBundle(name, parentBundleName, parentLoc, cacheAtStartup);
updateCache(parentBundleName, bundle);
}
updateCache(fileName, bundle);
}
}
}
return (Component) bundle;
}
/**
* Construct a bundle based on bundle name, file name and locale.
*
* @param name bundle name
* @param fileName full path to source XML file
* @param locale locale
* @param cacheAtStartup cache all the keys when constructing?
* @return the bundle, null if loading failed
*/
private XMLResourceBundle _loadBundle(String name, String fileName, Locale loc, boolean cacheAtStartup)
{
if (logger.isDebugEnabled()) logger.debug("Trying to load bundle: " + name + ", locale " + loc + ", filename " + fileName);
XMLResourceBundle bundle = null;
XMLResourceBundle parentBundle = null;
try
{
if (loc != null && !loc.getLanguage().equals(""))
parentBundle = (XMLResourceBundle) selectParent(name, loc);
bundle = new XMLResourceBundle();
bundle.setLogger(logger);
bundle.init(name, fileName, loc, parentBundle, cacheAtStartup);
}
catch (SAXParseException e)
{
if (logger.isInfoEnabled()) logger.info("Resource loading failed: " + e.getMessage());
bundle = null;
}
catch (Exception e)
{
logger.error("Error while loading resource: " + name + ", locale " + loc + ", bundleName " + fileName, e);
bundle = null;
}
return bundle;
}
/**
* Returns the next locale up the parent hierarchy.
* E.g. the parent of new Locale("en","us","mac") would be
* new Locale("en", "us", "").
*
* @param locale the locale
* @return the parent locale
*/
protected Locale getParentLocale(Locale loc)
{
Locale newloc;
if (loc.getVariant().equals(""))
{
if (loc.getCountry().equals(""))
newloc = new Locale("","","");
else
newloc = new Locale(loc.getLanguage(), "", "");
}
else
newloc = new Locale(loc.getLanguage(), loc.getCountry(), "");
return newloc;
}
/**
* Maps a bundle name and locale to a full path in the filesystem.
* If you need a different mapping, then just override this method.
*
* @param locale the locale
* @return the parent locale
*/
protected String getFileName(String name, Locale loc)
{
StringBuffer sb = new StringBuffer(getDirectory());
sb.append('/').append(name);
if (loc != null)
{
if (! loc.getLanguage().equals(""))
{
sb.append("_");
sb.append(loc.getLanguage());
}
if (! loc.getCountry().equals(""))
{
sb.append("_");
sb.append(loc.getCountry());
}
if (! loc.getVariant().equals(""))
{
sb.append("_");
sb.append(loc.getVariant());
}
}
sb.append(".xml");
String result = sb.toString();
if (logger.isDebugEnabled()) logger.debug("Resolving bundle name to file name: " + name + ", locale " + loc + " --> " + result);
return result;
}
/**
* Selects a bundle from the cache.
*
* @param fileName file name of the bundle
* @return the cached bundle; null, if not found
*/
protected Component selectCached(String fileName)
{
Component bundle = null;
try
{
bundle = super.select(fileName);
if (logger.isDebugEnabled()) logger.debug("Returning from cache: " + fileName);
}
catch (ComponentException e)
{
if (logger.isDebugEnabled()) logger.debug("Not found in cache: " + fileName);
}
return bundle;
}
/**
* Checks if the bundle is in the "not-found" cache.
*
* @param fileName file name of the bundle
* @return true, if the bundle wasn't found already before;
* otherwise, false.
*/
protected boolean isNotFoundBundle(String fileName)
{
String result = (String)(cacheNotFound.get(fileName));
if (result != null)
{
if (logger.isDebugEnabled()) logger.debug("Returning from not_found_cache: " + fileName);
}
else
{
if (logger.isDebugEnabled()) logger.debug("Not found in not_found_cache: " + fileName);
}
return result != null;
}
/**
* Checks if the bundle is in the "not-found" cache.
*
* @param fileName file name of the bundle
* @return true, if the bundle wasn't found already before;
* otherwise, false.
*/
protected void updateCache(String fileName, XMLResourceBundle bundle)
{
if (bundle == null)
{
if (logger.isDebugEnabled()) logger.debug("Updating not_found_cache: " + fileName);
cacheNotFound.put(fileName, fileName);
}
else
{
if (logger.isDebugEnabled()) logger.debug("Updating cache: " + fileName);
super.put((Object) fileName, (Component) bundle);
}
}
/**
* Returns the root directory to all bundles.
*
* @return the directory path
*/
public String getDirectory()
{
return directory;
}
/**
* Should we load bundles to cache on startup or not?
*
* @return true if pre-loading all resources; false otherwise
*/
public boolean cacheAtStartup()
{
return cacheAtStartup;
}
}
1.14 +428 -319 xml-cocoon2/src/org/apache/cocoon/transformation/I18nTransformer.java
Index: I18nTransformer.java
===================================================================
RCS file: /home/cvs/xml-cocoon2/src/org/apache/cocoon/transformation/I18nTransformer.java,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- I18nTransformer.java 2001/07/07 11:43:35 1.13
+++ I18nTransformer.java 2001/07/19 11:22:21 1.14
@@ -10,10 +10,14 @@
package org.apache.cocoon.transformation;
import org.apache.cocoon.ProcessingException;
-import org.apache.cocoon.acting.LangSelect;
+import org.apache.cocoon.acting.LocaleAction;
+import org.apache.cocoon.components.parser.Parser;
import org.apache.cocoon.environment.Source;
import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.i18n.XMLResourceBundle;
+import org.apache.cocoon.i18n.XMLResourceBundleFactory;
+
import org.apache.avalon.excalibur.pool.Poolable;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentException;
@@ -22,6 +26,13 @@
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.logger.Loggable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+
+import org.apache.regexp.RE;
+
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -36,6 +47,7 @@
import java.util.ArrayList;
import java.util.Locale;
import java.util.Date;
+import java.util.MissingResourceException;
import java.text.Format;
import java.text.MessageFormat;
@@ -49,121 +61,199 @@
import java.net.MalformedURLException;
/**
- * I18nTransformer. Cocoon2 port of Infozone groups I18nProcessor.
+ * Internationalisation transformer. Used to transform i18n markup into text
+ * based on a particular locale.
+ *
* <p>
- * Sitemap configuration:
- * </p>
+ *
+ * The i18n transformer works by obtaining the users locale via
+ * <code>getLocale()</code> in the LocaleAction.
+ * (@see org.apache.cocoon.acting.LocaleAction). It them attempts to find a
+ * message catalogue that satisifies the particular locale, and use it for
+ * for text replacement within i18n markup.
+ *
* <p>
- * <map:transformer<br>
- * name="translate"<br>
- * src="org.apache.cocoon.transformation.I18nTransformer"/><br>
- * </p>
+ *
+ * Catalogues are maintained in separate files, with a naming convention
+ * similar to that of ResourceBundle (@see java.util.ResourceBundle). ie.
+ * <strong>basename</strong>_<strong>locale</strong>, where <i>basename</i>
+ * can be any name, and <i>locale</i> can be any locale specified using
+ * ISO 639/3166 characters (eg. en_AU, de_AT, es).
+ *
* <p>
- * <map:match pattern="file"><br>
- * <map:generate src="file.xml"/><br>
- * <map:transform type="translate"><br>
- * <parameter name="default_lang" value="fi"/><br>
- * <parameter name="available_lang_1" value="fi"/><br>
- * <parameter name="available_lang_2" value="en"/><br>
- * <parameter name="available_lang_3" value="sv"/><br>
- * <parameter name="src"<br>
- * value="translations/file_trans.xml"/><br>
- * </map:transform><br>
- * </p>
+ *
+ * Catalogues are of the following format:
+ *
+ * <pre>
+ * <?xml version="1.0"?>
+ * <!-- message catalogue file for locale ... -->
+ *
+ * <catalogue xml:lang="locale">
+ * <message key="key">text</message>
+ * ....
+ * </catalogue>
+ * </pre>
+ *
+ * Where <strong>key</strong> specifies a particular message for that
+ * language.
+ *
* <p>
- * When user requests .../file?lang=fi<br>
- * transformer substitutes text surrounded <i18n:text> with
- * translations from file_trans.xml.<br>
- * Attributes listed in <i18n:attr> attribute are also translated
- * </p>
+ *
+ * Files to be translated contain the following markup:
+ *
+ * <pre>
+ * <?xml version="1.0"?>
+ *
+ * ... some text, translate <i18n:text>key</i18n:text>
+ * </pre>
+ *
+ * At runtime, the i18n transformer will find a message catalogue for the
+ * user's locale, and will appropriately replace the text between the
+ * <code><i18n:text></code> markup, using the value between the tags as
+ * the lookup key.
+ *
* <p>
- * file.xml:<br>
- * <root xmlns:i18n="http://apache.org/cocoon/i18n/2.0"><br>
- * <elem i18n:attr="title" title="translate_me">Text</elem><br>
- * <elem><i18n:text>Translate me</i18n:text></elem><br>
- * </root>
- * </p>
+ *
+ * If the i18n transformer cannot find an appropriate message catalogue for
+ * the user's given locale, it will recursively try to locate a <i>parent</i>
+ * message catalogue, until a valid catalogue can be found.
+ *
+ * ie:
+ *
+ * <ul>
+ * <li><strong>catalogue</strong>_<i>language</i>_<i>country</i>_<i>variant</i>.xml
+ * <li><strong>catalogue</strong>_<i>language</i>_<i>country</i>.xml
+ * <li><strong>catalogue</strong>_<i>language</i>.xml
+ * <li><strong>catalogue</strong>.xml
+ * </ul>
+ *
+ * eg: Assuming a basename of <i>messages</i> and a locale of <i>en_AU</i>
+ * (no variant), the following search will occur:
+ *
+ * <ul>
+ * <li><strong>messages</strong>_<i>en</i>_<i>AU</i>.xml
+ * <li><strong>messages</strong>_<i>en</i>.xml
+ * <li><strong>messages</strong>.xml
+ * </ul>
+ *
+ * This allows the developer to write a hierarchy of message catalogues,
+ * at each defining messages with increasing depth of variation.
+ *
* <p>
- * file_trans.xml:<br>
- * <translations><br>
- * <entry><key>Translate me</key><br>
- * <translation lang="sv">�vers�tta mej</translation><br>
- * <translation lang="fi">K��nn� minut</translation><br>
- * </entry><br>
- * </translations><br>
- * </p>
+ *
+ * Sitemap configuration:
+ *
+ * <pre>
+ * <map:transformer name="i18n" src="org.apache.cocoon.transformation.I18nTransformer">
+ * <catalogue-name>messages</catalogue-name>
+ * <catalogue-location>translations</catalogue-location>
+ * <untranslated-text>untranslated</untranslated-text>
+ * <cache-at-startup>true</cache-at-startup>
+ * </map:transformer>
+ * </pre>
+ *
+ * <ul>
+ * <li><strong>catalogue-name</strong>: base name of the message
+ * catalogue (<i>mandatory</i>).
+ * <li><strong>catalogue-location</strong>: location of the
+ * message catalogues (<i>mandatory</i>).
+ * <li><strong>untranslated-text</strong>: default text used for
+ * untranslated keys (default is 'untranslated-text').
+ * <li><strong>cache-at-startup</strong>: flag whether to cache
+ * messages at startup (false by default).
+ * </ul>
+ *
* <p>
+ *
+ * To use the transformer in a pipeline, simply specify it in a particular
+ * transform. eg:
*
- * @todo Caching dictionaries in memory.<br>
- * @todo Date and Number i18n.<br>
- * @todo Multiple dictionary support. <br>
-*
+ * <pre>
+ * <map:match pattern="file">
+ * <map:generate src="file.xml"/>
+ * <map:transform type="i18n"/>
+ * <map:serialize/>
+ * </map:match>
+ * </pre>
+ *
+ * Future work coming:
+ *
+ * <ul>
+ * <li>Ability to override definition parameters in the pipeline
+ * <li>Many clean ups :-)
+ * </ul>
+ *
+ * @author <a href="mailto:Marcus.Crafter@osa.de">Marcus Crafter</a>
* @author <a href="mailto:kpiroumian@flagship.ru">Konstantin Piroumian</a>
* @author <a href="mailto:lassi.immonen@valkeus.com">Lassi Immonen</a>
*/
public class I18nTransformer extends AbstractTransformer
-implements Composable, Poolable {
+ implements Composable, Poolable, Configurable {
protected ComponentManager manager;
/**
- * The parsed dictionary data.
- */
- public Map dictionary;
-
- /**
* The namespace for i18n is "http://apache.org/cocoon/i18n/2.0"
*/
- public final static String I18N_NAMESPACE_URI =
- "http://apache.org/cocoon/i18n/2.0";
+ public static final String I18N_NAMESPACE_URI =
+ "http://apache.org/cocoon/i18n/2.0";
//
// Dictionary elements and attributes
//
- public final static String I18N_DICTIONARY_ELEMENT = "dictionary";
- public final static String I18N_ENTRY_ELEMENT = "entry";
- public final static String I18N_KEY_ELEMENT = "key";
- public final static String I18N_TRANSLATION_ELEMENT = "translation";
+ public static final String I18N_DICTIONARY_ELEMENT = "dictionary";
+ public static final String I18N_ENTRY_ELEMENT = "entry";
+ public static final String I18N_KEY_ELEMENT = "key";
+ public static final String I18N_TRANSLATION_ELEMENT = "translation";
//
// Text elements and attributes
//
- public final static String I18N_LANG = "lang";
- public final static String I18N_KEY_ATTRIBUTE = "key";
- public final static String I18N_ATTR_ATTRIBUTE = "attr";
- public final static String I18N_TEXT_ELEMENT = "text";
- public final static String I18N_TRANSLATE_ELEMENT = "translate";
- public final static String I18N_PARAM_ELEMENT = "param";
- public final static String I18N_DATE_ELEMENT = "date";
- public final static String I18N_NUMBER_ELEMENT = "number";
+ public static final String I18N_LANG = "lang";
+ public static final String I18N_KEY_ATTRIBUTE = "key";
+ public static final String I18N_ATTR_ATTRIBUTE = "attr";
+ public static final String I18N_TEXT_ELEMENT = "text";
+ public static final String I18N_TRANSLATE_ELEMENT = "translate";
+ public static final String I18N_PARAM_ELEMENT = "param";
+ public static final String I18N_DATE_ELEMENT = "date";
+ public static final String I18N_NUMBER_ELEMENT = "number";
// number and date formatting attributes
- public final static String I18N_SRC_PATTERN_ATTRIBUTE = "src-pattern";
- public final static String I18N_PATTERN_ATTRIBUTE = "pattern";
- public final static String I18N_VALUE_ATTRIBUTE = "value";
+ public static final String I18N_SRC_PATTERN_ATTRIBUTE = "src-pattern";
+ public static final String I18N_PATTERN_ATTRIBUTE = "pattern";
+ public static final String I18N_VALUE_ATTRIBUTE = "value";
+
+ // configuration parameters
+ public static final String I18N_CATALOGUE_NAME = "catalogue-name";
+ public static final String I18N_CATALOGUE_LOCATION = "catalogue-location";
+ public static final String I18N_CATALOGUE_PREFIX = "/catalogue/message";
+ public static final String I18N_UNTRANSLATED = "untranslated-text";
+ public static final String I18N_CACHE_STARTUP = "cache-at-startup";
+
/**
- * <code>sub-type</code> attribute is used with <code>i18:number</code> to indicate
- * a sub-type: <code>currency</code> or <code>percent</code>.
+ * <code>sub-type</code> attribute is used with <code>i18:number</code> to
+ * indicate a sub-type: <code>currency</code> or <code>percent</code>.
*/
- public final static String I18N_SUB_TYPE_ATTRIBUTE = "sub-type";
+ public static final String I18N_SUB_TYPE_ATTRIBUTE = "sub-type";
+
/**
- * <code>type</code> attribute is used with <code>i18:param</code> to indicate
- * the parameter type: <code>date</code> or <code>number</code>.
+ * <code>type</code> attribute is used with <code>i18:param</code> to
+ * indicate the parameter type: <code>date</code> or <code>number</code>.
* If <code>type</code> is <code>number</code> then a <code>sub-type</code>
* can be used.
*/
- public final static String I18N_TYPE_ATTRIBUTE = "type";
+ public static final String I18N_TYPE_ATTRIBUTE = "type";
// States of the transformer
- private final static int STATE_OUTSIDE = 0;
- private final static int STATE_INSIDE_TEXT = 1;
- private final static int STATE_INSIDE_PARAM = 2;
- private final static int STATE_INSIDE_TRANSLATE = 3;
- private final static int STATE_INSIDE_TRANSLATE_TEXT = 4;
- private final static int STATE_TRANSLATE_KEY = 5;
- private final static int STATE_TRANSLATE_TEXT_KEY = 6;
- private final static int STATE_INSIDE_DATE = 7;
- private final static int STATE_INSIDE_NUMBER = 8;
+ private static final int STATE_OUTSIDE = 0;
+ private static final int STATE_INSIDE_TEXT = 1;
+ private static final int STATE_INSIDE_PARAM = 2;
+ private static final int STATE_INSIDE_TRANSLATE = 3;
+ private static final int STATE_INSIDE_TRANSLATE_TEXT = 4;
+ private static final int STATE_TRANSLATE_KEY = 5;
+ private static final int STATE_TRANSLATE_TEXT_KEY = 6;
+ private static final int STATE_INSIDE_DATE = 7;
+ private static final int STATE_INSIDE_NUMBER = 8;
/**
* Current state of the transformer.
@@ -175,7 +265,7 @@
* Previous state.
* Used to translate text inside params and translate elements.
*/
- private int prev_state = STATE_OUTSIDE;
+ private int prev_state = STATE_OUTSIDE;
/**
* The i18n:key attribute is stored for the current element.
@@ -224,10 +314,7 @@
private String lang;
/**
- * @todo Locale full support.
- * Do we really need it? We can use a combination of
- * language code and country code (en_US) instead. <br>
- * Also, different encodings can be specified: ru_RU_koi8
+ * Locale setting.
*/
private Locale locale;
@@ -235,70 +322,148 @@
* Date element attributes and their values.
*/
private HashMap formattingParams;
-
- public static Locale parseLocale(String locale) {
- StringTokenizer st = new StringTokenizer(locale, "_");
- String lang = null;
- String country = null;
- String variant = null;
- if (!st.hasMoreTokens()) {
- return Locale.ENGLISH;
- }
- else {
- lang = st.nextToken();
- }
- country = st.hasMoreTokens() ? st.nextToken() : "";
- variant = st.hasMoreTokens() ? st.nextToken() : "";
+ /**
+ * Dictionary data.
+ */
+ private XMLResourceBundle dictionary;
+ private XMLResourceBundleFactory factory = new XMLResourceBundleFactory();
- return new Locale(lang, country, variant);
- }
+ /*
+ * i18n configuration variables
+ */
+ private String catalogueName;
+ private String catalogueLocation;
+ private String untranslated;
+ private boolean cacheAtStartup;
+
+ /**
+ * Configure this transformer.
+ */
+ public void configure(Configuration conf)
+ throws ConfigurationException {
+ if (conf != null) {
+
+ // read in the config options from the transformer definition
+
+ // obtain the base name of the message catalogue
+ Configuration child = conf.getChild(I18N_CATALOGUE_NAME);
+ catalogueName = child.getValue(null);
+ debug("Default catalogue name is " + catalogueName);
+
+ // obtain the directory location of message catalogues
+ child = conf.getChild(I18N_CATALOGUE_LOCATION);
+ catalogueLocation = child.getValue(null);
+ debug("Default catalogue location is " + catalogueLocation);
+
+ // check our mandatory parameters
+ if (catalogueName == null || catalogueLocation == null)
+ throw new ConfigurationException(
+ "I18nTransformer requires the name and location of " +
+ "the message catalogues"
+ );
+
+ // obtain default text to use for untranslated messages
+ child = conf.getChild(I18N_UNTRANSLATED);
+ untranslated = child.getValue(I18N_UNTRANSLATED);
+ debug("Default untranslated text is '" + untranslated + "'");
+
+ // obtain config option, whether to cache messages at startup time
+ child = conf.getChild(I18N_CACHE_STARTUP);
+ cacheAtStartup = child.getValueAsBoolean(false);
+ debug((cacheAtStartup ? "will" : "won't") +
+ " cache messages during startup, by default"
+ );
- public void setLocale(Locale locale) {
- this.locale = locale;
- this.lang = locale.getLanguage();
+ // activate resource bundle logging
+ factory.setLogger(getLogger());
+ }
}
/**
- * Uses <code>org.apache.cocoon.acting.LangSelect.getLang()</code>
- * to get language user has selected. First it checks is lang set in
- * objectModel.
+ * Uses <code>org.apache.cocoon.acting.LocaleAction.getLocale()</code>
+ * to get language user has selected.
*/
public void setup(SourceResolver resolver, Map objectModel, String source,
- Parameters parameters)
- throws ProcessingException, SAXException, IOException {
+ Parameters parameters)
+ throws ProcessingException, SAXException, IOException {
- // Set current language and locale
- String lang = (String)(objectModel.get("lang"));
- if (lang == null) {
- lang = LangSelect.getLang(objectModel, parameters);
- }
+ try {
- setLocale(parseLocale(lang));
- formatter.setLocale(locale);
+ // Set current language and locale
+ String lc = LocaleAction.getLocale(objectModel);
+
+ // configure the factory
+ _setup(resolver);
- // FIXME (KP)
- // We need another way of specifying dictionaries, e.g.
- // <parameter name="dictionary.en" src="dict.xml" />
- // <parameter name="dictionary.de" src="dict_de.xml" />
- // etc.
- String translations_file = parameters.getParameter("src", null);
-
- // FIXME (KP)
- // Add multiple dictionary support!
- initialiseDictionary(resolver.resolve(translations_file));
+ // setup everything for the current locale
+ String[] matches = new RE("_").split(lc);
+
+ String l = matches.length > 0
+ ? matches[0] : Locale.getDefault().getLanguage();
+ String c = matches.length > 1 ? matches[1] : "";
+ String v = matches.length > 2 ? matches[2] : "";
+ Locale locale = new Locale(l, c, v);
+
+ debug("using locale " + locale.toString());
+
+ dictionary =
+ (XMLResourceBundle) factory.select(catalogueName, locale);
+
+ debug("selected dictionary " + dictionary);
+
+ setLocale(locale);
+
+ } catch(Exception e) {
+ debug("exception generated, leaving unconfigured");
+ throw new ProcessingException(e.getMessage(), e);
+ }
}
+ /**
+ * Internal setup.
+ *
+ * REVISIT: when we can get the resolver anywhere, we can pass the
+ * configuration object directly to XMLResourceBundle.
+ */
+ private void _setup(SourceResolver resolver) throws Exception {
+
+ // configure the factory to log correctly and cache catalogues
+ DefaultConfiguration configuration =
+ new DefaultConfiguration("name", "location");
+ DefaultConfiguration cacheConf =
+ new DefaultConfiguration(
+ XMLResourceBundleFactory.ConfigurationKeys.CACHE_AT_STARTUP,
+ "location"
+ );
+ cacheConf.setValue(new Boolean(cacheAtStartup).toString());
+ configuration.addChild(cacheConf);
+
+ // set the root location for message catalogues
+ DefaultConfiguration dirConf =
+ new DefaultConfiguration(
+ XMLResourceBundleFactory.ConfigurationKeys.ROOT_DIRECTORY,
+ "location"
+ );
+ String cR =
+ resolver.resolve(catalogueLocation).getFile().getCanonicalPath();
+ dirConf.setValue(cR);
+
+ configuration.addChild(dirConf);
+ factory.configure(configuration);
+
+ debug("configured");
+ }
public void compose(ComponentManager manager) {
this.manager = manager;
}
public void startElement(String uri, String name, String raw,
- Attributes attr) throws SAXException {
+ Attributes attr) throws SAXException {
if (I18N_NAMESPACE_URI.equals(uri)) {
- this.getLogger().debug("Starting i18n element: " + name);
+ debug("Starting i18n element: " + name);
startI18NElement(name, attr);
return;
}
@@ -308,7 +473,7 @@
public void endElement(String uri, String name, String raw)
- throws SAXException {
+ throws SAXException {
if (I18N_NAMESPACE_URI.equals(uri)) {
endI18NElement(name);
@@ -333,51 +498,47 @@
private void startI18NElement(String name, Attributes attr)
throws SAXException {
- this.getLogger().debug("Start i18n element: " + name);
+ debug("Start i18n element: " + name);
try {
if (I18N_TEXT_ELEMENT.equals(name)) {
if (current_state != STATE_OUTSIDE
- && current_state != STATE_INSIDE_PARAM
- && current_state != STATE_INSIDE_TRANSLATE) {
+ && current_state != STATE_INSIDE_PARAM
+ && current_state != STATE_INSIDE_TRANSLATE) {
throw new SAXException(this.getClass().getName()
- + ": nested i18n:text elements are not allowed. Current state: " + current_state);
+ + ": nested i18n:text elements are not allowed. Current state: " + current_state);
}
prev_state = current_state;
current_state = STATE_INSIDE_TEXT;
current_key = attr.getValue(I18N_NAMESPACE_URI, I18N_KEY_ATTRIBUTE);
- }
- else if (I18N_TRANSLATE_ELEMENT.equals(name)) {
+ } else if (I18N_TRANSLATE_ELEMENT.equals(name)) {
if (current_state != STATE_OUTSIDE) {
throw new SAXException(this.getClass().getName()
- + ": i18n:translate element must be used "
- + "outside of other i18n elements. Current state: " + current_state);
+ + ": i18n:translate element must be used "
+ + "outside of other i18n elements. Current state: " + current_state);
}
current_state = STATE_INSIDE_TRANSLATE;
- }
- else if (I18N_PARAM_ELEMENT.equals(name)) {
+ } else if (I18N_PARAM_ELEMENT.equals(name)) {
if (current_state != STATE_INSIDE_TRANSLATE) {
throw new SAXException(this.getClass().getName()
- + ": i18n:param element can be used only inside "
- + "i18n:translate element. Current state: " + current_state);
+ + ": i18n:param element can be used only inside "
+ + "i18n:translate element. Current state: " + current_state);
}
setFormattingParams(attr);
current_state = STATE_INSIDE_PARAM;
- }
- else if (I18N_DATE_ELEMENT.equals(name)) {
+ } else if (I18N_DATE_ELEMENT.equals(name)) {
if (current_state != STATE_OUTSIDE) {
throw new SAXException(this.getClass().getName()
- + ": i18n:date elements are not allowed "
- + "inside of other i18n elements.");
+ + ": i18n:date elements are not allowed "
+ + "inside of other i18n elements.");
}
setFormattingParams(attr);
current_state = STATE_INSIDE_DATE;
- }
- else if (I18N_NUMBER_ELEMENT.equals(name)) {
+ } else if (I18N_NUMBER_ELEMENT.equals(name)) {
if (current_state != STATE_OUTSIDE) {
throw new SAXException(this.getClass().getName()
- + ": i18n:number elements are not allowed "
- + "inside of other i18n elements.");
+ + ": i18n:number elements are not allowed "
+ + "inside of other i18n elements.");
}
setFormattingParams(attr);
@@ -387,7 +548,7 @@
// we need it to avoid further errors if an exception occurs
current_state = STATE_OUTSIDE;
throw new SAXException(this.getClass().getName()
- + ": error in format", e);
+ + ": error in format", e);
}
}
@@ -425,26 +586,31 @@
}
private void endI18NElement(String name) throws SAXException {
- this.getLogger().debug("End i18n element: " + name);
+ debug("End i18n element: " + name);
try {
switch (current_state) {
- case STATE_INSIDE_TEXT: {
+ case STATE_INSIDE_TEXT:
+ {
endTextElement();
break;
}
- case STATE_INSIDE_TRANSLATE: {
+ case STATE_INSIDE_TRANSLATE:
+ {
endTranslateElement();
break;
}
- case STATE_INSIDE_PARAM: {
+ case STATE_INSIDE_PARAM:
+ {
endParamElement();
break;
}
- case STATE_INSIDE_DATE: {
+ case STATE_INSIDE_DATE:
+ {
endDateElement();
break;
}
- case STATE_INSIDE_NUMBER: {
+ case STATE_INSIDE_NUMBER:
+ {
endNumberElement();
break;
}
@@ -453,7 +619,7 @@
// we need it to avoid further errors if an exception occurs
current_state = STATE_OUTSIDE;
throw new SAXException(this.getClass().getName()
- + ": error in format", e);
+ + ": error in format", e);
}
}
@@ -472,70 +638,83 @@
}
private void i18nCharacters(char[] ch, int start, int len)
- throws SAXException{
+ throws SAXException {
- String text2translate = new String(ch, start, len);
- text2translate = stripWhitespace(text2translate);
- if (text2translate == null || text2translate.length() == 0) {
+ String key = new String(ch, start, len);
+ key = stripWhitespace(key);
+ if (key == null || key.length() == 0) {
return;
}
- this.getLogger().debug("Text 2 translate: '" + text2translate + "'");
+ debug("i18n message key = '" + key + "'");
switch (current_state) {
- case STATE_INSIDE_TEXT: {
+ case STATE_INSIDE_TEXT:
+ {
if (current_key != null) {
- translated_text = (String)(dictionary.get(current_key));
+ try {
+ translated_text = getString(current_key);
+ } catch (MissingResourceException e) {
+ translated_text = untranslated;
+ }
if (translated_text == null) {
- translated_text = text2translate;
+ translated_text = key;
}
current_key = null;
- }
- else {
- translated_text = (String)(dictionary.get(text2translate));
+ } else {
+ try {
+ translated_text = getString(key);
+ } catch (MissingResourceException e) {
+ translated_text = untranslated;
+ }
}
break;
}
- case STATE_INSIDE_TRANSLATE: {
+ case STATE_INSIDE_TRANSLATE:
+ {
// Store text for param substitution (do not translate)
if (substitute_text == null) {
- substitute_text = text2translate;
+ substitute_text = key;
}
break;
}
- case STATE_INSIDE_PARAM: {
+ case STATE_INSIDE_PARAM:
+ {
// Store translation for param substitution
if (param_value == null) {
- param_value = text2translate;
+ param_value = key;
}
break;
}
- case STATE_INSIDE_DATE: {
+ case STATE_INSIDE_DATE:
+ {
if (formattingParams != null) {
if (formattingParams.get(I18N_VALUE_ATTRIBUTE) == null) {
- formattingParams.put(I18N_VALUE_ATTRIBUTE, text2translate);
- }
- else {
+ formattingParams.put(I18N_VALUE_ATTRIBUTE, key);
+ } else {
// how to use the text inside of date element?
}
+
}
break;
}
- case STATE_INSIDE_NUMBER: {
+ case STATE_INSIDE_NUMBER:
+ {
if (formattingParams != null) {
if (formattingParams.get(I18N_PATTERN_ATTRIBUTE) == null) {
- formattingParams.put(I18N_PATTERN_ATTRIBUTE, text2translate);
- }
- else {
+ formattingParams.put(I18N_PATTERN_ATTRIBUTE, key);
+ } else {
// how to use the text inside of number element?
}
+
}
break;
}
- default: {
+ default:
+ {
throw new SAXException(this.getClass().getName()
- + "Something's really wrong!!!");
+ + "Something's really wrong!!!");
}
}
}
@@ -560,25 +739,29 @@
// remove the i18n:attr attribute - we don't need it
temp_attr.removeAttribute(i18n_attr_index);
while (st.hasMoreElements()) {
- // translate all listed attributes
+ // translate all listed attributes
String attr_name = st.nextToken();
int attr_index = temp_attr.getIndex(attr_name);
if (attr_index != -1) {
String text2translate = temp_attr.getValue(attr_index);
- String result = (String)(dictionary.get(text2translate));
+ String result;
+
+ try {
+ result = getString(text2translate);
+ } catch (MissingResourceException e) {
+ result = untranslated;
+ }
// set the translated value
if (result != null) {
temp_attr.setValue(attr_index, result);
- }
- else {
+ } else {
getLogger().warn("translation not found for attribute "
- + attr_name + " in element: " + name);
+ + attr_name + " in element: " + name);
}
- }
- else {
+ } else {
getLogger().warn("i18n attribute '" + attr_name
- + "' not found in element: " + name);
+ + "' not found in element: " + name);
}
}
return temp_attr;
@@ -588,25 +771,27 @@
}
private void endTextElement() throws SAXException {
- this.getLogger().debug("End text element, translated_text: " + translated_text);
+ debug("End text element, translated_text: " + translated_text);
switch (prev_state) {
- case STATE_OUTSIDE: {
+ case STATE_OUTSIDE:
+ {
// simply translate text (key translation already performed)
if (translated_text != null) {
super.contentHandler.characters(translated_text.toCharArray(),
- 0, translated_text.length());
- }
- else {
- // else - translation not found
- this.getLogger().debug("--- Translation not found! ---");
+ 0, translated_text.length());
+ } else {
+ // else - translation not found
+ debug("--- Translation not found! ---");
}
break;
}
- case STATE_INSIDE_TRANSLATE: {
+ case STATE_INSIDE_TRANSLATE:
+ {
substitute_text = translated_text;
break;
}
- case STATE_INSIDE_PARAM: {
+ case STATE_INSIDE_PARAM:
+ {
param_value = translated_text;
break;
}
@@ -617,27 +802,26 @@
}
private void endParamElement() throws SAXException {
- this.getLogger().debug("Substitution param: " + param_value);
+ debug("Substitution param: " + param_value);
if (formattingParams != null) {
String paramType = (String)formattingParams.get(I18N_TYPE_ATTRIBUTE);
if (paramType != null) {
- this.getLogger().debug("Param type: " + paramType);
+ debug("Param type: " + paramType);
if (formattingParams.get(I18N_VALUE_ATTRIBUTE) == null
- && param_value != null) {
- this.getLogger().debug("Put param value: " + param_value);
+ && param_value != null) {
+ debug("Put param value: " + param_value);
formattingParams.put(I18N_VALUE_ATTRIBUTE, param_value);
}
if ("date".equals(paramType)) {
- this.getLogger().debug("Formatting date param: " + formattingParams);
+ debug("Formatting date param: " + formattingParams);
param_value = formatDate(formattingParams);
- }
- else if ("number".equals(paramType)) {
- this.getLogger().debug("Formatting number param: " + formattingParams);
+ } else if ("number".equals(paramType)) {
+ debug("Formatting number param: " + formattingParams);
param_value = formatNumber(formattingParams);
}
}
}
- this.getLogger().debug("Added substitution param: " + param_value);
+ debug("Added substitution param: " + param_value);
indexedParams.add(param_value);
param_value = null;
current_state = STATE_INSIDE_TRANSLATE;
@@ -651,11 +835,10 @@
String result;
if (indexedParams.size() > 0 && substitute_text.length() > 0) {
- this.getLogger().debug("Text for susbtitution: " + substitute_text);
+ debug("Text for susbtitution: " + substitute_text);
result = formatter.format(substitute_text, indexedParams.toArray());
- this.getLogger().debug("Result of susbtitution: " + result);
- }
- else {
+ debug("Result of susbtitution: " + result);
+ } else {
result = substitute_text;
}
@@ -674,7 +857,7 @@
private String formatDate(Map params) throws SAXException {
if (params == null) {
throw new SAXException(this.getClass().getName()
- + ": i18n:date - error in element attributes.");
+ + ": i18n:date - error in element attributes.");
}
// from pattern
String srcPattern = (String)params.get(I18N_SRC_PATTERN_ATTRIBUTE);
@@ -694,7 +877,7 @@
// result pattern is localized
SimpleDateFormat to_fmt = (SimpleDateFormat)DateFormat.getDateTimeInstance(
- DateFormat.DEFAULT, DateFormat.DEFAULT, locale);
+ DateFormat.DEFAULT, DateFormat.DEFAULT, locale);
if (pattern != null) {
to_fmt.applyPattern(pattern);
}
@@ -702,19 +885,18 @@
// get current date and time by default
if (value == null) {
dateValue = new Date();
- }
- else {
+ } else {
try {
dateValue = from_fmt.parse(value);
} catch (ParseException pe) {
throw new SAXException(this.getClass().getName()
- + "i18n:date - parsing error.", pe);
+ + "i18n:date - parsing error.", pe);
}
}
// we have all necessary data here: do formatting.
String result = to_fmt.format(dateValue);
- this.getLogger().debug("i18n:date result: " + result);
+ debug("i18n:date result: " + result);
return result;
}
@@ -727,7 +909,7 @@
private String formatNumber(Map params) throws SAXException {
if (params == null) {
throw new SAXException(this.getClass().getName()
- + ": i18n:number - error in element attributes.");
+ + ": i18n:number - error in element attributes.");
}
// from pattern
String srcPattern = (String)params.get(I18N_SRC_PATTERN_ATTRIBUTE);
@@ -751,11 +933,9 @@
DecimalFormat to_fmt = null;
if (subType == null) {
to_fmt = (DecimalFormat)NumberFormat.getInstance(locale);
- }
- else if (subType.equals("currency")) {
+ } else if (subType.equals("currency")) {
to_fmt = (DecimalFormat)NumberFormat.getCurrencyInstance(locale);
- }
- else if (subType.equals("percent")) {
+ } else if (subType.equals("percent")) {
to_fmt = (DecimalFormat)NumberFormat.getPercentInstance(locale);
}
if (pattern != null) {
@@ -765,118 +945,46 @@
// get current date and time by default
if (value == null) {
numberValue = new Long(0);
- }
- else {
+ } else {
try {
numberValue = from_fmt.parse(value);
} catch (ParseException pe) {
throw new SAXException(this.getClass().getName()
- + "i18n:number - parsing error.", pe);
+ + "i18n:number - parsing error.", pe);
}
}
// we have all necessary data here: do formatting.
String result = to_fmt.format(numberValue);
- this.getLogger().debug("i18n:number result: " + result);
+ debug("i18n:number result: " + result);
return result;
}
/**
- * Gets translations from xml file to dictionary.
+ * Helper method to retrieve a message from the dictionary
*/
- class I18nContentHandler extends DefaultHandler {
- boolean in_entry = false;
- boolean in_key = false;
- boolean in_translation = false;
-
- String key = null;
- String translation = null;
-
-
- public void startElement(String namespace, String name, String raw,
- Attributes attr) throws SAXException {
-
- if (name.equals(I18N_ENTRY_ELEMENT)) {
- in_entry = true;
- } else {
- if (in_entry) {
- if (name.equals(I18N_KEY_ELEMENT)) {
- in_key = true;
- } else {
- if (name.equals(I18N_TRANSLATION_ELEMENT)
- && attr.getValue(I18N_LANG).equals(lang)) {
- in_translation = true;
- }
- }
- }
- }
- }
-
-
- public void endElement(String namespace, String name, String raw)
- throws SAXException {
-
- if (name.equals(I18N_ENTRY_ELEMENT)) {
- if (key != null && translation != null) {
- dictionary.put(key, translation);
- key = null;
- translation = null;
- }
- in_entry = false;
- } else if (name.equals(I18N_KEY_ELEMENT)) {
- in_key = false;
- } else {
- if (name.equals(I18N_TRANSLATION_ELEMENT)) {
- in_translation = false;
- }
- }
-
- }
-
-
- public void characters(char[] ary, int start, int length)
- throws SAXException {
- if (in_key) {
- key = new String(ary, start, length);
-
- } else {
- if (in_translation) {
- translation = new String(ary, start, length);
- }
- }
- }
+ private String getString(String key) {
+ return dictionary.getString(
+ I18N_CATALOGUE_PREFIX + "[@key='" + key + "']"
+ );
}
+ private void setLocale(Locale locale) {
+ this.locale = locale;
+ lang = locale.getLanguage();
+ formatter.setLocale(locale);
+ }
/**
- *Loads translations from given URL
+ * Helper method to debug messages
*/
- private void initialiseDictionary(Source inputSource)
- throws ProcessingException, SAXException, MalformedURLException, IOException {
-
-
- try
- {
- // How this could be cached?
- this.dictionary = new HashMap();
- I18nContentHandler i18n_handler = new I18nContentHandler();
- inputSource.stream(i18n_handler);
- } catch(SAXException e) {
- getLogger().error("Error in initialiseDictionary", e);
- throw e;
- } catch(MalformedURLException e) {
- getLogger().error("Error in initialiseDictionary", e);
- throw e;
- } catch(IOException e) {
- getLogger().error("Error in initialiseDictionary", e);
- throw e;
- }
+ private void debug(String msg) {
+ getLogger().debug("I18nTransformer: " + msg);
}
/**
*
- */
static public void main(String[] args) {
Locale locale = null;
@@ -885,8 +993,8 @@
for (int i = 0; i < locales.length; i++) {
locale = locales[i];
SimpleDateFormat fmt = (SimpleDateFormat)DateFormat.getDateTimeInstance(
- DateFormat.DEFAULT, DateFormat.DEFAULT, locale
- );
+ DateFormat.DEFAULT, DateFormat.DEFAULT, locale
+ );
String localized = fmt.format(new Date());
@@ -894,13 +1002,14 @@
String money = n_fmt.format(1210.5);
System.out.println("Locale ["
- + locale.getLanguage() + ", "
- + locale.getCountry() + ", "
- + locale.getVariant() + "] : "
- + locale.getDisplayName()
- + " \t Date: " + localized
- + " \t Money: " + money);
+ + locale.getLanguage() + ", "
+ + locale.getCountry() + ", "
+ + locale.getVariant() + "] : "
+ + locale.getDisplayName()
+ + " \t Date: " + localized
+ + " \t Money: " + money);
}
}
+ */
}
1.37 +4 -1 xml-cocoon2/webapp/sitemap.xmap
Index: sitemap.xmap
===================================================================
RCS file: /home/cvs/xml-cocoon2/webapp/sitemap.xmap,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -r1.36 -r1.37
--- sitemap.xmap 2001/07/18 12:14:18 1.36
+++ sitemap.xmap 2001/07/19 11:22:21 1.37
@@ -29,7 +29,10 @@
<map:transformer name="log" src="org.apache.cocoon.transformation.LogTransformer"/>
<map:transformer name="sql" src="org.apache.cocoon.transformation.SQLTransformer"/>
<map:transformer name="extractor" src="org.apache.cocoon.transformation.FragmentExtractorTransformer"/>
- <map:transformer name="i18n" src="org.apache.cocoon.transformation.I18nTransformer"/>
+ <map:transformer name="i18n" src="org.apache.cocoon.transformation.I18nTransformer">
+ <catalogue-name>messages</catalogue-name>
+ <catalogue-location>translations</catalogue-location>
+ </map:transformer>
<map:transformer name="xinclude" src="org.apache.cocoon.transformation.XIncludeTransformer"/>
<map:transformer name="cinclude" src="org.apache.cocoon.transformation.CIncludeTransformer"/>
<map:transformer name="filter" src="org.apache.cocoon.transformation.FilterTransformer"/>
1.5 +12 -12 xml-cocoon2/webapp/i18n/simple.xml
Index: simple.xml
===================================================================
RCS file: /home/cvs/xml-cocoon2/webapp/i18n/simple.xml,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- simple.xml 2001/06/15 15:01:09 1.4
+++ simple.xml 2001/07/19 11:22:21 1.5
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:i18n="http://apache.org/cocoon/i18n/2.0">
<title>
- <i18n:text>Hello, internationalization!</i18n:text>
+ <i18n:text>titletext</i18n:text>
</title>
<sub-title>
<i18n:date pattern="EEE, MMMM dd, yyyy zzz" />
</sub-title>
<annotation>
- <i18n:text>Documentation link:</i18n:text>
+ <i18n:text>doclink</i18n:text>
<link>
<href>http://xml.apache.org/cocoon2/i18n.html</href>
<title>Cocoon 2 Web Site</title>
@@ -19,7 +19,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id1</i18n:text></href>
+ <href>simple.xml?locale=<i18n:text>lang_id1</i18n:text></href>
<title>
<i18n:text>language1</i18n:text>
</title>
@@ -27,7 +27,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id2</i18n:text></href>
+ <href>simple.xml?locale=<i18n:text>lang_id2</i18n:text></href>
<title>
<i18n:text>language2</i18n:text>
</title>
@@ -35,7 +35,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id3</i18n:text></href>
+ <href>simple.xml?locale=<i18n:text>lang_id3</i18n:text></href>
<title>
<i18n:text>language3</i18n:text>
</title>
@@ -43,7 +43,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id4</i18n:text></href>
+ <href>simple.xml?locale=<i18n:text>lang_id4</i18n:text></href>
<title>
<i18n:text>language4</i18n:text>
</title>
@@ -51,7 +51,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id5</i18n:text></href>
+ <href>simple.xml?locale=<i18n:text>lang_id5</i18n:text></href>
<title>
<i18n:text>language5</i18n:text>
</title>
@@ -61,25 +61,25 @@
<menu>
<item>
<link>
- <href>?lang=en_US</href>
+ <href>simple.xml?locale=en_US</href>
<title>English (US)</title>
</link>
</item>
<item>
<link>
- <href>?lang=en_GB</href>
+ <href>simple.xml?locale=en_GB</href>
<title>English (GB)</title>
</link>
</item>
<item>
<link>
- <href>?lang=ru_RU</href>
+ <href>simple.xml?locale=ru_RU</href>
<title>Russian (Russia)</title>
</link>
</item>
<item>
<link>
- <href>?lang=de_AT_EURO</href>
+ <href>simple.xml?locale=de_AT_EURO</href>
<title>German (Austria, Euro)</title>
</link>
</item>
@@ -93,7 +93,7 @@
</para>
<para title="third" name="article" i18n:attr="title name">
<i18n:translate>
- <i18n:text>Hello, {0}! Glad to see you!</i18n:text>
+ <i18n:text>Hello</i18n:text>
<i18n:param name="username">
<i18n:text>Kot</i18n:text>
</i18n:param>
1.7 +13 -17 xml-cocoon2/webapp/i18n/simple.xsp
Index: simple.xsp
===================================================================
RCS file: /home/cvs/xml-cocoon2/webapp/i18n/simple.xsp,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- simple.xsp 2001/06/15 18:08:04 1.6
+++ simple.xsp 2001/07/19 11:22:21 1.7
@@ -14,19 +14,15 @@
count++;
}
- Locale loc = null;
- String lang = <xsp-request:get-attribute name="lang"/>;
- if (lang != null) {
- loc = org.apache.cocoon.transformation.I18nTransformer.parseLocale(lang);
- }
-
+ Locale loc = request.getLocale();
+
SimpleDateFormat df = new SimpleDateFormat("EEEE, MMMM dd, yyyy H:mm:ss", loc);
</xsp:logic>
<title>
- <i18n:text>Hello, internationalization!</i18n:text>
+ <i18n:text>titletext</i18n:text>
</title>
<annotation>
- <i18n:text>Documentation link:</i18n:text>
+ <i18n:text>doclink</i18n:text>
<link>
<href>http://xml.apache.org/cocoon2/i18n.html</href>
<title>Cocoon 2 Web Site</title>
@@ -51,7 +47,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id1</i18n:text></href>
+ <href>simple.xsp?locale=<i18n:text>lang_id1</i18n:text></href>
<title>
<i18n:text>language1</i18n:text>
</title>
@@ -59,7 +55,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id2</i18n:text></href>
+ <href>simple.xsp?locale=<i18n:text>lang_id2</i18n:text></href>
<title>
<i18n:text>language2</i18n:text>
</title>
@@ -67,7 +63,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id3</i18n:text></href>
+ <href>simple.xsp?locale=<i18n:text>lang_id3</i18n:text></href>
<title>
<i18n:text>language3</i18n:text>
</title>
@@ -75,7 +71,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id4</i18n:text></href>
+ <href>simple.xsp?locale=<i18n:text>lang_id4</i18n:text></href>
<title>
<i18n:text>language4</i18n:text>
</title>
@@ -83,7 +79,7 @@
</item>
<item>
<link>
- <href>?lang=<i18n:text>lang_id5</i18n:text></href>
+ <href>simple.xsp?locale=<i18n:text>lang_id5</i18n:text></href>
<title>
<i18n:text>language5</i18n:text>
</title>
@@ -93,25 +89,25 @@
<menu>
<item>
<link>
- <href>?lang=en_US</href>
+ <href>simple.xsp?locale=en_US</href>
<title>English (US)</title>
</link>
</item>
<item>
<link>
- <href>?lang=en_GB</href>
+ <href>simple.xsp?locale=en_GB</href>
<title>English (GB)</title>
</link>
</item>
<item>
<link>
- <href>?lang=ru_RU</href>
+ <href>simple.xsp?locale=ru_RU</href>
<title>Russian (Russia)</title>
</link>
</item>
<item>
<link>
- <href>?lang=de_AT_EURO</href>
+ <href>simple.xsp?locale=de_AT_EURO</href>
<title>German (Austria, Euro)</title>
</link>
</item>
1.7 +7 -32 xml-cocoon2/webapp/i18n/sitemap.xmap
Index: sitemap.xmap
===================================================================
RCS file: /home/cvs/xml-cocoon2/webapp/i18n/sitemap.xmap,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- sitemap.xmap 2001/06/15 18:08:05 1.6
+++ sitemap.xmap 2001/07/19 11:22:21 1.7
@@ -3,52 +3,27 @@
<!-- =========================== Components ================================ -->
<map:components>
<map:generators default="file"/>
- <map:transformers default="xslt">
- <map:transformer name="i18n" src="org.apache.cocoon.transformation.I18nTransformer">
- <map:parameter name="available_lang_1" value="en"/>
- <map:parameter name="available_lang_2" value="ru"/>
- <map:parameter name="available_lang_3" value="de"/>
- <map:parameter name="available_lang_4" value="pl"/>
- <map:parameter name="available_lang_5" value="sp"/>
- <map:parameter name="available_lang_6" value="hy"/>
- </map:transformer>
- </map:transformers>
+ <map:transformers default="xslt"/>
<map:readers default="resource"/>
<map:serializers default="html"/>
<map:selectors default="browser"/>
<map:matchers default="wildcard">
<map:matcher name="wildcard" src="org.apache.cocoon.matching.WildcardURIMatcherFactory"/>
</map:matchers>
- <map:actions>
- <map:action name="lang-select" src="org.apache.cocoon.acting.LangSelect">
- <store-in-session>true</store-in-session>
- <create-session>true</create-session>
- <store-in-cookie>true</store-in-cookie>
- <store-in-request>true</store-in-request>
- </map:action>
- </map:actions>
</map:components>
<!-- =========================== Pipelines ================================= -->
<map:pipelines>
<map:pipeline>
- <map:match pattern="**.xml">
- <map:act type="lang-select">
- <map:generate src="{../1}.xml"/>
- <map:transform type="i18n">
- <map:parameter name="src" value="translations/{../1}_dict.xml"/>
- </map:transform>
- </map:act>
+ <map:match pattern="*.xml">
+ <map:generate src="{1}.xml"/>
+ <map:transform type="i18n"/>
<map:transform src="simple.xsl"/>
<map:serialize/>
</map:match>
- <map:match pattern="**.xsp">
- <map:act type="lang-select">
- <map:generate type="serverpages" src="{../1}.xsp"/>
- <map:transform type="i18n">
- <map:parameter name="src" value="translations/{../1}_dict.xml"/>
- </map:transform>
- </map:act>
+ <map:match pattern="*.xsp">
+ <map:generate type="serverpages" src="{1}.xsp"/>
+ <map:transform type="i18n"/>
<map:transform src="simple.xsl"/>
<map:serialize/>
</map:match>
1.1 xml-cocoon2/webapp/i18n/translations/convert.xsl
Index: convert.xsl
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<!-- specify here the language of the catalogue you would like to create -->
<xsl:param name="lang">hy</xsl:param>
<xsl:template match="translations">
<catalogue xml:lang="{$lang}">
<xsl:apply-templates select="entry"/>
</catalogue>
</xsl:template>
<xsl:template match="entry">
<message key="{key}">
<xsl:value-of select="translation[@lang=$lang]"/>
</message>
</xsl:template>
</xsl:stylesheet>
1.1 xml-cocoon2/webapp/i18n/translations/messages.xml
Index: messages.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- CVS $Id: messages.xml,v 1.1 2001/07/19 11:22:22 dims Exp $ -->
<!-- Default English message catalogue file for cocoon2 sample webapp -->
<catalogue xml:lang="en">
<message key="a_key">This is a key value.</message>
<message key="lang_id1">ru</message>
<message key="lang_id2">de</message>
<message key="lang_id3">pl</message>
<message key="lang_id4">es</message>
<message key="lang_id5">hy</message>
<message key="language">English</message>
<message key="language1">Russian</message>
<message key="language2">German</message>
<message key="language3">Polish</message>
<message key="language4">Spanish</message>
<message key="language5">Armenian</message>
<message key="titletext">Hello, internationalization!</message>
<message key="doclink">See i18n documentation for details:</message>
<message key="first">First</message>
<message key="second">Second</message>
<message key="third">Third</message>
<message key="forth">Forth</message>
<message key="article">Article</message>
<message key="article_text1">This is a i18n paragraph.</message>
<message key="article_text2">This is another i18n paragraph and is also a cool one.</message>
<message key="copyright">Copyright © 2001 Konstantin Piroumian. No rights are reserved.</message>
<message key="Hello">Hello, {0}! Glad to see you!</message>
<message key="Kot">Tomcat</message>
<message key="none">None</message>
<message key="one">one</message>
<message key="two">two</message>
<message key="count_title">This page was accessed {0} times. Last at: {1}.</message>
</catalogue>
1.1 xml-cocoon2/webapp/i18n/translations/messages_de.xml
Index: messages_de.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- CVS $Id: messages_de.xml,v 1.1 2001/07/19 11:22:22 dims Exp $ -->
<!-- German message catalogue file for cocoon2 sample webapp -->
<catalogue xml:lang="de">
<message key="count_title">Diese Seite wurde {0}mal aufgerufen. Letzter Aufruf: {1}.</message>
<message key="a_key">Dies ist der Wert eines Schlüssels.</message>
<message key="lang_id1">en</message>
<message key="lang_id2">ru</message>
<message key="lang_id3">pl</message>
<message key="lang_id4">es</message>
<message key="lang_id5">hy</message>
<message key="language">Deutsch</message>
<message key="language1">Englische</message>
<message key="language2">Russe</message>
<message key="language3">Polnisch</message>
<message key="language4">Spanisch</message>
<message key="language5">Armenier</message>
<message key="titletext">Herzlich willkommen, Internationalisierung!</message>
<message key="doclink">Näheres unter der i18n Dokumentation:</message>
<message key="first">Erstens</message>
<message key="second">Zweitens</message>
<message key="third">Drittens</message>
<message key="forth">Viertens</message>
<message key="article">Artikel</message>
<message key="article_text1">Dies ist ein Absatz nach i18n.</message>
<message key="article_text2">Dies ist ein weiterer Absatz nach i18n und auch noch ein ziemlich cooler dazu.</message>
<message key="copyright">Copyright © 2001 Konstantin Piroumian. Deutsche Übersetzung von Jörg Prante.</message>
<message key="Hello, {0}! Glad to see you!">Hallo {0}! Schön, dich zu sehen!</message>
<message key="Kot">Tomcat</message>
<message key="none">nichts</message>
<message key="one">eins</message>
<message key="two">zwei</message>
</catalogue>
1.1 xml-cocoon2/webapp/i18n/translations/messages_en.xml
Index: messages_en.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- CVS $Id: messages_en.xml,v 1.1 2001/07/19 11:22:22 dims Exp $ -->
<!-- English message catalogue file for cocoon2 sample webapp -->
<catalogue xml:lang="en">
<message key="a_key">This is a key value.</message>
<message key="lang_id1">ru</message>
<message key="lang_id2">de</message>
<message key="lang_id3">pl</message>
<message key="lang_id4">es</message>
<message key="lang_id5">hy</message>
<message key="language">English</message>
<message key="language1">Russian</message>
<message key="language2">German</message>
<message key="language3">Polish</message>
<message key="language4">Spanish</message>
<message key="language5">Armenian</message>
<message key="titletext">Hello, internationalization!</message>
<message key="doclink">See i18n documentation for details:</message>
<message key="first">First</message>
<message key="second">Second</message>
<message key="third">Third</message>
<message key="forth">Forth</message>
<message key="article">Article</message>
<message key="article_text1">This is a i18n paragraph.</message>
<message key="article_text2">This is another i18n paragraph and is also a cool one.</message>
<message key="copyright">Copyright © 2001 Konstantin Piroumian. No rights are reserved.</message>
<message key="Hello">Hello, {0}! Glad to see you!</message>
<message key="Kot">Tomcat</message>
<message key="none">None</message>
<message key="one">one</message>
<message key="two">two</message>
<message key="count_title">This page was accessed {0} times. Last at: {1}.</message>
</catalogue>
1.1 xml-cocoon2/webapp/i18n/translations/messages_es.xml
Index: messages_es.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- CVS $Id: messages_es.xml,v 1.1 2001/07/19 11:22:22 dims Exp $ -->
<!-- Spanish message catalogue file for cocoon2 sample webapp -->
<catalogue xml:lang="es">
<message key="count_title">Esta página fue tenida acceso {0} veces. Pasado en: {1}.</message>
<message key="a_key">Esto es un valor clave.</message>
<message key="lang_id1">en</message>
<message key="lang_id2">ru</message>
<message key="lang_id3">de</message>
<message key="lang_id4">pl</message>
<message key="lang_id5">hy</message>
<message key="language">Español</message>
<message key="language1">Inglés</message>
<message key="language2">Ruso</message>
<message key="language3">Alemán</message>
<message key="language4">Polaco</message>
<message key="language5">Armenio</message>
<message key="titletext">¡¡Hola!, internacionalización!</message>
<message key="doclink">Visto la documentación i18n para detalles:</message>
<message key="first">Primero</message>
<message key="second">Segundo</message>
<message key="third">Tercio</message>
<message key="forth">En adelante</message>
<message key="article">Artículo</message>
<message key="article_text1">Esto es un párrafo i18n.</message>
<message key="article_text2">Esto es otro párrafo i18n y es también uno 'cool'.</message>
<message key="copyright">Copyright © 2001 Konstantin Piroumian. Ningunos derechos son reservados.</message>
<message key="Hello, {0}! Glad to see you!">¡¡Hola!, {0}! ¡Alegre de verle!</message>
<message key="Kot">Gato</message>
<message key="none">Ninguno</message>
<message key="one">un</message>
<message key="two">dos</message>
</catalogue>
1.1 xml-cocoon2/webapp/i18n/translations/messages_hy.xml
Index: messages_hy.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- CVS $Id: messages_hy.xml,v 1.1 2001/07/19 11:22:22 dims Exp $ -->
<!-- Armenian message catalogue file for cocoon2 sample webapp -->
<catalogue xml:lang="hy">
<message key="count_title">²Ûë ¿çÁ ³Ûó»É»É »Ý {0} ³Ý·³Ù. ì»ñçÇÝÁ {1}.</message>
<message key="a_key">ê³ µ³Ý³ÉÇÇ ³éÅ»ùÝ ¿£</message>
<message key="lang_id1">en</message>
<message key="lang_id2">ru</message>
<message key="lang_id3">de</message>
<message key="lang_id4">pl</message>
<message key="lang_id5">es</message>
<message key="language">гۻñ»Ý</message>
<message key="language1">²Ý·É»ñ»Ý</message>
<message key="language2">èáõë»ñ»Ý</message>
<message key="language3">¶»ñٳݻñ»Ý</message>
<message key="language4">Ȼѻñ»Ý</message>
<message key="language5">Æëå³Ý»ñ»Ý</message>
<message key="titletext">´³ñ¢°, ÇÝï»ñݳóÛáݳÉáõÃÛáõÝ£</message>
<message key="doclink">سÝñ³Ù³ë µ³ó³ïñáõÃÛ³Ý Ñ³Ù³ñ ݳÇñª</message>
<message key="first">²é³çÇÝ</message>
<message key="second">ºñÏñáñ¹</message>
<message key="third">ºññáñ¹</message>
<message key="forth">¼áñáñ¹</message>
<message key="article">Ðá¹í³Í</message>
<message key="article_text1">ê³ ÇÝï»ñݳóÛáÝ³É å³ñ³·ñ³ý ¿£</message>
<message key="article_text2">ê³ ÙÇ áõñÇß ÇÝï»ñݳóÛáÝ³É å³ñ³·ñ³ý ¿, ¢ ÝáõÛÝ å»ë ó»Ýïñ£</message>
<message key="copyright">Copyright © 2001 Konstantin Piroumian. àãÇÝã ãÇ å³Ñå³Ýí³Í£</message>
<message key="Hello, {0}! Glad to see you!">´³ñ¢¯ {0}: àõñ³Ë »Ù ù»½ ï»ëݻɣ</message>
<message key="Kot">γïáõ</message>
<message key="none">àã áù</message>
<message key="one">Ù»Ï</message>
<message key="two">»ñÏáõë</message>
</catalogue>
1.1 xml-cocoon2/webapp/i18n/translations/messages_pl.xml
Index: messages_pl.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- CVS $Id: messages_pl.xml,v 1.1 2001/07/19 11:22:22 dims Exp $ -->
<!-- Polish message catalogue file for cocoon2 sample webapp -->
<catalogue xml:lang="pl">
<message key="count_title">Ta strona była pobierana {0} razy. Ostatnio {1}</message>
<message key="a_key">To jest klucz.</message>
<message key="lang_id1">en</message>
<message key="lang_id2">ru</message>
<message key="lang_id3">de</message>
<message key="lang_id4">es</message>
<message key="lang_id5">hy</message>
<message key="language">Polski</message>
<message key="language1">Angielski</message>
<message key="language2">Rosyjski</message>
<message key="language3">Niemiecki</message>
<message key="language4">Hiszpañski</message>
<message key="language5">Armeñski</message>
<message key="titletext">Witam, oto przykład wielojęzycznej strony!</message>
<message key="doclink">Widzą i18n dokumentacja dla szczegółów:</message>
<message key="first">Pierwszy</message>
<message key="second">Drugi</message>
<message key="third">Trzeci</message>
<message key="forth">Czwarty</message>
<message key="article">Artykuł</message>
<message key="article_text1">To jest paragraf w i18n.</message>
<message key="article_text2">To jest następny paragraf w i18n i jest takze fajny.</message>
<message key="copyright">Copyright © 2001 Konstantin Piroumian i Krzysztof Zieliński. Żadne prawa nie są zastrzeżone:)</message>
<message key="Hello, {0}! Glad to see you!">Witam, {0}! Miło Cię widzieć!</message>
<message key="Kot">Tomcat</message>
<message key="none">Nic</message>
<message key="one">raz</message>
<message key="two">dwa</message>
</catalogue>
1.1 xml-cocoon2/webapp/i18n/translations/messages_ru.xml
Index: messages_ru.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- CVS $Id: messages_ru.xml,v 1.1 2001/07/19 11:22:22 dims Exp $ -->
<!-- Russian message catalogue file for cocoon2 sample webapp -->
<catalogue xml:lang="ru">
<message key="count_title">На эту страницу заходили {0} раз(а). В последний раз {1}.</message>
<message key="a_key">Это значение по ключу.</message>
<message key="lang_id1">en</message>
<message key="lang_id2">de</message>
<message key="lang_id3">pl</message>
<message key="lang_id4">es</message>
<message key="lang_id5">hy</message>
<message key="language">Русский</message>
<message key="language1">Английский</message>
<message key="language2">Немецкий</message>
<message key="language3">Польский</message>
<message key="language4">Испанский</message>
<message key="language5">Армянский</message>
<message key="titletext">Привет, многоязычность!</message>
<message key="doclink">Для дополнительной информации по i18n смотри:</message>
<message key="first">Первый</message>
<message key="second">Второй</message>
<message key="third">Третий</message>
<message key="forth">Четвертый</message>
<message key="article">Статья</message>
<message key="article_text1">Это интернационализированный абзац.</message>
<message key="article_text2">Это тоже интернационализированный абзац и такой же классный.</message>
<message key="copyright">Авторские права © 2001 Константин Пирумян. Ничто не защищено.</message>
<message key="Hello, {0}! Glad to see you!">Привет, {0}! Рад тебя видеть!</message>
<message key="Kot">Кот</message>
<message key="none">Никто</message>
<message key="one">раз</message>
<message key="two">два</message>
</catalogue>
----------------------------------------------------------------------
In case of troubles, e-mail: webmaster@xml.apache.org
To unsubscribe, e-mail: cocoon-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: cocoon-cvs-help@xml.apache.org