cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Piroumian, Konstantin" <KPiroum...@flagship.ru>
Subject Re: future plans? Made it partly now!
Date Wed, 01 Aug 2001 08:16:18 GMT
> Hi Konstantin, hi Marcus,
> thank you for your response.
> I have added some lines of code to I18nTransformer.java
> and tested: ok. It is attached to this mail.
> May be you (and others) find it useful and it will find the way
> into the release. That is the first time I contribute somthing,
> so I don't know how to checkin. That's why I post it here.
> I post the whole file because I don't know if somebody has changed
> this file since Cocoon0730041508.

The procedure of contributing is like this:
    - update your CVS snapshot
    - make your changes (including documentation in xdocs/)
    - run 'cvs diff -u' command from the xml-cocoon2 root directory
(redirect the output to a file)
    - send generated 'diff' file to cocoon-dev mail list and somebody from
the committers will apply your patch.

Btw, development issues are better be discussed in cocoon-dev.

>
> !!! For people who are reading this and want's to play with
>     the attached file but have not the latest Dev-Snapshot:
>     The i18n configuration has changed.
>     sitemap.xmap and dictionary are different to C2b2!!!
>     But it is well documented in the source code.
>
> 1) !!! Now we are incompatible with earlier versions !!!

I think that the namespace URI must be changed to:
http://apache.org/cocoon/i18n/2.1

>    I changed <i18n:date/> so we get now only the date.
>    I introduced <i18n:time/> for time.
>    I didn't introduce <i18n:datetime/> because it is
>    the same as <i18n:date/> <i18n:time/>

I think that <i18n:datetime /> or <i18n:date-time /> also needed to be the
full reflection of DateFormat class. (Also, it easier to use <i18n:datetime
format="dd/MM/yy hh:mm:ss" /> than two separate tags and two formats).

> 2) I added a style attribute for date and time:
>    <i18n:date style="short"/>
>    available values for style: "short","medium","long","full"
>    all other will throw an exception.

What will happen if someone will specify both 'style' and 'format'
attributes? Maybe it's better to use the same attribute name and recognise
the value to perform the right formatting?

> 3) I added a little bit of documentation in the source.

That's fine! If you have time, could you update the documentation to
xdocs/i18n.xml too (you can build the documentation by  'build.bat docs'
command line)?

>
> One question about my third topic: different locales for e.g.
> currencies: You wrote that it could be better to use xsl to
> format currencies. But than I have to provide for each locale
> a different xsl file because the locale, currency sign and the
> separators are hard wired:
> > <xsl:decimal-format name="us"> decimal-separator="."
grouping-separator=","/>
> > <xsl:value-of select="format-number(price, '$#,##0.00', 'us')"/>

Yes, that's true. I'm not sure, but maybe the format name argument ('us' in
format-number) can be dynamic? So you can define some formats and choose the
appropriate one:

<xsl:decimal-format name="us"> decimal-separator="."
grouping-separator=","/>
<xsl:decimal-format name="ru"> decimal-separator="," grouping-separator="
"/>

<xsl:param name="locale">ru</xsl:param>
...
<xsl:value-of select="format-number(price, '$#,##0.00', $locale)"/>

Anyway, this is a XSL question and is off-topic for Cocoon mail list.

> So what would you say about changing the
> <i18n:number sub-type="currency" value="1234"/> to
> <i18n:number sub-type="currency">
>   1234 <!-- this value could now be derived from a database -->
> </i18n:number>

As I remember, this is already supported. The same is behavior is in
sustitution params (value and text() are interchangeable).

> If I can do this in this way and add an attribute 'locale',
> where can I effectively insert it: In
> public void setup(SourceResolver resolver, Map objectModel, String
> source,
>                   Parameters parameters)
> or in the formatXXX for every element???

In setup you can define only the default locale (as it is now).
To add a locale attribute processing for every element it's better to insert
it somewhere in the startI18NElement function. You can test if element has
'locale' attribute then call setLocale(). Note, that you'll need to save in
a stack the old locale and restore it in the endI18nElement(). (This is
because in some cases - <i18n:translate /> - i18n elements can be nested and
they can have different locales).

>
> Another question: actually the value for sub-type currency must be
> written
> with a comma. <i18n:number sub-type="currency" value="1.23"/> (with a
> dot)
> doesn't work correctly (only 1 is in the output).
> Was that wilful?

It's strange... In  i18n samples from C2 distro there is a value with a dot
(the 5th atricle) and it works fine. I'll check this.

Konstantin.

>
> Best regards,
> Michael
>
> "Piroumian, Konstantin" wrote:
> >
> > Hi!
> >
> > > Hi,
> > > does anybody knows if i18n will be extended in the future
> > > or has some undocumented features?
> >
> > Currently, I am busy with my work project and don't have much time for
> > i18nTransformer. It's open source and you are free to extend it. I hope
to
> > be a little more free in a week or two then I can do it myself.
> >
> > > I have some needs and I wonder that I'm the first one (?)
> > > who think and ask about this:
> >
> > No, I thought of all this features when I was implementing the
transformer,
> > but it seems that not many people is interested in it and you are the
first
> > or the second one who asked about new features.
> >
> > > 1) I need more control over <i18n:date/> e.g. I need a parameter
> > >    to specify DateFormat.SHORT, MEDIUM, LONG or FULL.
> >
> > Ok.
> >
> > > 2) I would like to have more control over date and time: Make it
> > >    separate, for instance as date the actual date
> > >    but the time hard wired.
> > >    So it should give a parameter for selecting analog to java:
> > >    getDateInstance(), getTimeInstance() or getDateTimeInstance()
> >
> > Yes, a pair of tags can be added for that:
> >
> > <i18n:datetime />
> > <i18n:time />
> >
> > > 2) In the same page I need more than one currency.
> > >    I think about a parameter to specify a different then the default
> > >    locale.
> >
> > Yes, I thought of this too. I think that every i18n element must have a
> > possibility to specify its own locale:
> >
> > <i18n:text locale="ru" />
> > <i18n:currency locale="en_US" />
> >
> > > I know that I can do this inside a xsp but ...
> >
> > You can do it also in XSLT and in some cases it would be better. E.g.
for
> > currency format you can use  <xsl:decimal-format name="us"
> > decimal-separator="." grouping-separator=","/> declaratation, then use
it
> > like this:
> >
> > <xsl:value-of select="format-number(price, '$#,##0.00', 'us')"/>
> >
> > The problem with i18n tranformer is that you cannot use objects as
> > arguments. This is not possible now with a transformer:
> >
> > <i18n:currency value="java:new Date()" /> or <i18n:currency
> > value="java:dateVariable" />
> >
> > Anyway, thank you for your suggestions and feel free to contact me for
any
> > questions about i18n transformer.
> >
> > P.S. Marcus Crafter <crafterm@fztig938.bank.dresdner.net> is now working
on
> > the i18n transformer, i18n dictionaries and locale selection, so I think
> > that you can ask him about this too.
> >
> > >
> > > Regards,
> > > Michael
> > >
> >
> > Best regards,
> >
> > Konstantin Piroumian
> > Software engineer
> >
> > Protek Flagship LLC
> > Phone: + 7 095 795 0520 (add. 1288)
> > Fax: + 7 095 795 0525
> > E-mail: kpiroumian@flagship.ru
> > http://www.protek.com
>
>
>


----------------------------------------------------------------------------
----


> /**
>
****************************************************************************
>  * 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.cocoon.transformation;
>
> import org.apache.cocoon.ProcessingException;
> 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;
> import org.apache.avalon.framework.component.Composable;
> import org.apache.avalon.framework.component.Component;
> 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;
> import org.xml.sax.helpers.AttributesImpl;
> import org.xml.sax.helpers.DefaultHandler;
>
> import java.io.IOException;
>
> import java.util.Map;
> import java.util.HashMap;
> import java.util.StringTokenizer;
> import java.util.ArrayList;
> import java.util.Locale;
> import java.util.Date;
> import java.util.MissingResourceException;
>
> import java.text.Format;
> import java.text.MessageFormat;
> import java.text.DateFormat;
> import java.text.SimpleDateFormat;
> import java.text.NumberFormat;
> import java.text.DecimalFormat;
> import java.text.ParseException;
>
> import java.net.URL;
> import java.net.MalformedURLException;
>
> /**
>  * Internationalisation transformer. Used to transform i18n markup into
text
>  * based on a particular locale.
>  *
>  * <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>
>  *
>  * 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>
>  *
>  * Catalogues are of the following format:
>  *
>  * <pre>
>  * &lt;?xml version="1.0"?&gt;
>  * &lt;!-- message catalogue file for locale ... --&gt;
>  *
>  * &lt;catalogue xml:lang=&quot;locale&quot;&gt;
>  *        &lt;message key="key"&gt;text&lt;/message&gt;
>  *        ....
>  * &lt;/catalogue&gt;
>  * </pre>
>  *
>  * Where <strong>key</strong> specifies a particular message for that
>  * language.
>  *
>  * <p>
>  *
>  * Files to be translated contain the following markup:
>  *
>  * <pre>
>  * &lt;?xml version="1.0"?&gt;
>  *
>  * ... some text, translate &lt;i18n:text&gt;key&lt;/i18n:text&gt;
>  * </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>&lt;i18n:text&gt;</code> markup, using the value between the tags
as
>  * the lookup key.
>  *
>  * <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>
>  *
>  * Sitemap configuration:
>  *
>  * <pre>
>  * &lt;map:transformer name="i18n"
src="org.apache.cocoon.transformation.I18nTransformer"&gt;
>  *  &lt;catalogue-name&gt;messages&lt;/catalogue-name&gt;
>  *  &lt;catalogue-location&gt;translations&lt;/catalogue-location&gt;
>  *  &lt;untranslated-text&gt;untranslated&lt;/untranslated-text&gt;
>  *  &lt;cache-at-startup&gt;true&lt;/cache-at-startup&gt;
>  * &lt;/map:transformer&gt;
>  * </pre>
>  *
>  * <ul>
>  *  <li>&lt;strong&gt;catalogue-name&lt;/strong&gt;: base name of the
message
>  *      catalogue (<i>mandatory</i>).
>  *  <li>&lt;strong&gt;catalogue-location&lt;/strong&gt;: location of the
>  *      message catalogues (<i>mandatory</i>).
>  *  <li>&lt;strong&gt;untranslated-text&lt;/strong&gt;: default text used
for
>  *      untranslated keys (default is 'untranslated-text').
>  *  <li>&lt;strong&gt;cache-at-startup&lt;/strong&gt;: 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:
>  *
>  * <pre>
>  * &lt;map:match pattern="file"&gt;
>  *  &lt;map:generate src="file.xml"/&gt;
>  *  &lt;map:transform type="i18n"/&gt;
>  *  &lt;map:serialize/&gt;
>  * &lt;/map:match&gt;
>  * </pre>
>  *
>  * <p/>
>  * <ul>
>  *  <li><strong><i18n:date/></strong> gives now only the date.</li>
>  *  <li><strong><i18n:time/></strong> gives the time.</li>
>  *  <li>For date and time an attribute <strong>style</strong> is
>  *      available which can have values of: "short", "medium",
>  *      "long" or "full".</li>
>  * </ul>
>  * 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>
>  * @author <a href="mailto:Michael.Enke@wincor-nixdorf.com">Michael
Enke</a>
>  */
> public class I18nTransformer extends AbstractTransformer
>     implements Composable, Poolable, Configurable {
>
>     protected ComponentManager manager;
>
>     /**
>      * The namespace for i18n is "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 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 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_TIME_ELEMENT = "time";
>     public static final String I18N_NUMBER_ELEMENT = "number";
>
>     // number and date formatting attributes
>     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";
>     public static final String I18N_STYLE_ATTRIBUTE = "style";
>
>     // 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>.
>      */
>     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>.
>      * If <code>type</code> is <code>number</code> then a
<code>sub-type</code>
>      * can be used.
>      */
>     public static final String I18N_TYPE_ATTRIBUTE = "type";
>
>     // States of the transformer
>     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;
>     private static final int STATE_INSIDE_TIME = 9;
>
>     /**
>      * Current state of the transformer.
>      * The value is STATE_OUTSIDE by default.
>      */
>     private int current_state = STATE_OUTSIDE;
>
>     /**
>      * Previous state.
>      * Used to translate text inside params and translate elements.
>      */
>     private int prev_state = STATE_OUTSIDE;
>
>     /**
>      * The i18n:key attribute is stored for the current element.
>      * If no translation found for the key then the character
>      * data of element is used as default value.
>      */
>     private String current_key = null;
>
>     /**
>      * Translated text inside the i18n:text element.
>      */
>     private String translated_text = null;
>
>     /**
>      * Translated text, ready for param substitution.
>      */
>     private String substitute_text = null;
>
>     /**
>      * Current parameter value (translated or not)
>      */
>     private String param_value = null;
>
>     /**
>      * @todo Named parameter substitution.
>      * i18n:params are stored in a HashMap for named substitutions, <br>
>      * name attribute is used as a key.<br>
>      * <i>MessageFormat class does not support named params.
>      * Some kind of mapping (name to index) must be used.</i>
>      */
>     private HashMap namedParams = null;
>
>     /**
>      * i18n:params are stored for index substitutions.
>      */
>     private ArrayList indexedParams = new ArrayList();
>
>     /**
>      * Message formatter for param substitution.
>      */
>     private MessageFormat formatter = new MessageFormat("");
>
>     /**
>      * Current language id.
>      */
>     private String lang;
>
>     /**
>      * Locale setting.
>      */
>     private Locale locale;
>
>     /**
>      * Date element attributes and their values.
>      */
>     private HashMap formattingParams;
>
>     /**
>      * Dictionary data.
>      */
>     private XMLResourceBundle dictionary;
>     private XMLResourceBundleFactory factory = new
XMLResourceBundleFactory();
>
>     /*
>      * 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"
>             );
>
>             // activate resource bundle logging
>             factory.setLogger(getLogger());
>         }
>     }
>
>     /**
>      *  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 {
>
>         try {
>
>             // Set current language and locale
>             String lc = LocaleAction.getLocale(objectModel);
>
>             // configure the factory
>             _setup(resolver);
>
>             // 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 {
>
>         if (I18N_NAMESPACE_URI.equals(uri)) {
>             debug("Starting i18n element: " + name);
>             startI18NElement(name, attr);
>             return;
>         }
>
>         super.startElement(uri, name, raw, translateAttributes(name,
attr));
>     }
>
>
>     public void endElement(String uri, String name, String raw)
>     throws SAXException {
>
>         if (I18N_NAMESPACE_URI.equals(uri)) {
>             endI18NElement(name);
>             return;
>         }
>
>         super.endElement(uri, name, raw);
>     }
>
>     public void characters(char[] ch, int start, int len) throws
SAXException {
>
>         if (current_state != STATE_OUTSIDE) {
>             i18nCharacters(ch, start, len);
>             return;
>         }
>
>         super.characters(ch, start, len);
>
>     }
>
>     // My own content handlers
>
>     private void startI18NElement(String name, Attributes attr)
>     throws SAXException {
>         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) {
>                     throw new SAXException(this.getClass().getName()
>                                            + ": 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)) {
>                 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);
>                 }
>                 current_state = STATE_INSIDE_TRANSLATE;
>             } 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);
>                 }
>                 setFormattingParams(attr);
>                 current_state = STATE_INSIDE_PARAM;
>             } 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.");
>                 }
>
>                 setFormattingParams(attr);
>                 current_state = STATE_INSIDE_DATE;
>             } else if (I18N_TIME_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.");
>                 }
>
>                 setFormattingParams(attr);
>                 current_state = STATE_INSIDE_TIME;
>             } 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.");
>                 }
>
>                 setFormattingParams(attr);
>                 current_state = STATE_INSIDE_NUMBER;
>             }
>         } catch (Exception e) {
>             // 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);
>         }
>     }
>
>     /**
>      * Get src-pattern, pattern and value attribute values and store in a
Map
>      */
>     private void setFormattingParams(Attributes attr) throws SAXException
{
>         formattingParams = new HashMap(3);
>
>         String attr_value = attr.getValue(I18N_SRC_PATTERN_ATTRIBUTE);
>         if (attr_value != null) {
>             formattingParams.put(I18N_SRC_PATTERN_ATTRIBUTE, attr_value);
>         }
>
>         attr_value = attr.getValue(I18N_PATTERN_ATTRIBUTE);
>         if (attr_value != null) {
>             formattingParams.put(I18N_PATTERN_ATTRIBUTE, attr_value);
>         }
>
>         attr_value = attr.getValue(I18N_VALUE_ATTRIBUTE);
>         if (attr_value != null) {
>             formattingParams.put(I18N_VALUE_ATTRIBUTE, attr_value);
>         }
>
>         attr_value = attr.getValue(I18N_TYPE_ATTRIBUTE);
>         if (attr_value != null) {
>             formattingParams.put(I18N_TYPE_ATTRIBUTE, attr_value);
>         }
>
>         attr_value = attr.getValue(I18N_SUB_TYPE_ATTRIBUTE);
>         if (attr_value != null) {
>             formattingParams.put(I18N_SUB_TYPE_ATTRIBUTE, attr_value);
>         }
>
>         attr_value = attr.getValue(I18N_STYLE_ATTRIBUTE);
>         if (attr_value != null) {
>             formattingParams.put(I18N_STYLE_ATTRIBUTE, attr_value);
>         }
>
>     }
>
>     private void endI18NElement(String name) throws SAXException {
>         debug("End i18n element: " + name);
>         try {
>             switch (current_state) {
>             case STATE_INSIDE_TEXT:
>                 {
>                     endTextElement();
>                     break;
>                 }
>             case STATE_INSIDE_TRANSLATE:
>                 {
>                     endTranslateElement();
>                     break;
>                 }
>             case STATE_INSIDE_PARAM:
>                 {
>                     endParamElement();
>                     break;
>                 }
>             case STATE_INSIDE_DATE:
>             case STATE_INSIDE_TIME:
>                 {
>                     endDate_TimeElement();
>                     break;
>                 }
>             case STATE_INSIDE_NUMBER:
>                 {
>                     endNumberElement();
>                     break;
>                 }
>             }
>         } catch (Exception e) {
>             // 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);
>         }
>     }
>
>
>     /*
>      */
>     private String stripWhitespace(String s) {
>         // FIXME (KP) Must be a better way to determine whitespace-only
nodes.
>         // trim() function does not remove spaces if string does not
contain
>         // anything else.
>         if (s == null) {
>             return null;
>         }
>         String result = (s + "!").trim();
>         return result.substring(0, result.length() - 1);
>     }
>
>     private void i18nCharacters(char[] ch, int start, int len)
>     throws SAXException {
>
>         String key = new String(ch, start, len);
>         key = stripWhitespace(key);
>         if (key == null || key.length() == 0) {
>             return;
>         }
>
>         debug("i18n message key = '" + key + "'");
>
>         switch (current_state) {
>         case STATE_INSIDE_TEXT:
>             {
>                 if (current_key != null) {
>                     try {
>                         translated_text = getString(current_key);
>                     } catch (MissingResourceException e) {
>                         translated_text = untranslated;
>                     }
>                     if (translated_text == null) {
>                         translated_text = key;
>                     }
>                     current_key = null;
>                 } else {
>                     try {
>                         translated_text = getString(key);
>                     } catch (MissingResourceException e) {
>                         translated_text = untranslated;
>                     }
>                 }
>
>                 break;
>             }
>         case STATE_INSIDE_TRANSLATE:
>             {
>                 // Store text for param substitution (do not translate)
>                 if (substitute_text == null) {
>                     substitute_text = key;
>                 }
>                 break;
>             }
>         case STATE_INSIDE_PARAM:
>             {
>                 // Store translation for param substitution
>                 if (param_value == null) {
>                     param_value = key;
>                 }
>                 break;
>             }
>         case STATE_INSIDE_DATE:
>         case STATE_INSIDE_TIME:
>             {
>                 if (formattingParams != null) {
>                     if (formattingParams.get(I18N_VALUE_ATTRIBUTE) ==
null) {
>                         formattingParams.put(I18N_VALUE_ATTRIBUTE, key);
>                     } else {
>                         // how to use the text inside of date element?
>                     }
>
>                 }
>                 break;
>             }
>         case STATE_INSIDE_NUMBER:
>             {
>                 if (formattingParams != null) {
>                     if (formattingParams.get(I18N_PATTERN_ATTRIBUTE) ==
null) {
>                         formattingParams.put(I18N_PATTERN_ATTRIBUTE, key);
>                     } else {
>                         // how to use the text inside of number element?
>                     }
>
>                 }
>                 break;
>             }
>         default:
>             {
>                 throw new SAXException(this.getClass().getName()
>                                        + "Something's really wrong!!!");
>             }
>         }
>     }
>
>     private Attributes translateAttributes(String name, Attributes attr)
>     throws SAXException {
>         if (attr == null) {
>             return attr;
>         }
>
>         AttributesImpl temp_attr = new AttributesImpl(attr);
>
>         // Translate all attributes from i18n:attr="name1 name2 ..."
>         // using their values as keys
>         int i18n_attr_index =
>             temp_attr.getIndex(I18N_NAMESPACE_URI, I18N_ATTR_ATTRIBUTE);
>
>         if (i18n_attr_index != -1) {
>
>             StringTokenizer st =
>                 new StringTokenizer(temp_attr.getValue(i18n_attr_index));
>             // remove the i18n:attr attribute - we don't need it
>             temp_attr.removeAttribute(i18n_attr_index);
>             while (st.hasMoreElements()) {
>                 // 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;
>
>                     try {
>                         result = getString(text2translate);
>                     } catch (MissingResourceException e) {
>                         result = untranslated;
>                     }
>                     // set the translated value
>                     if (result != null) {
>                         temp_attr.setValue(attr_index, result);
>                     } else {
>                         getLogger().warn("translation not found for
attribute "
>                                          + attr_name + " in element: " +
name);
>                     }
>                 } else {
>                     getLogger().warn("i18n attribute '" + attr_name
>                                      + "' not found in element: " + name);
>                 }
>             }
>             return temp_attr;
>         }
>
>         return attr;
>     }
>
>     private void endTextElement() throws SAXException {
>         debug("End text element, translated_text: " + translated_text);
>         switch (prev_state) {
>         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
>                     debug("--- Translation not found! ---");
>                 }
>                 break;
>             }
>         case STATE_INSIDE_TRANSLATE:
>             {
>                 substitute_text = translated_text;
>                 break;
>             }
>         case STATE_INSIDE_PARAM:
>             {
>                 param_value = translated_text;
>                 break;
>             }
>         }
>         translated_text = null;
>         current_state = prev_state;
>         prev_state = STATE_OUTSIDE;
>     }
>
>     private void endParamElement() throws SAXException {
>         debug("Substitution param: " + param_value);
>         if (formattingParams != null) {
>             String paramType =
(String)formattingParams.get(I18N_TYPE_ATTRIBUTE);
>             if (paramType != null) {
>                 debug("Param type: " + paramType);
>                 if (formattingParams.get(I18N_VALUE_ATTRIBUTE) == null
>                         && param_value != null) {
>                     debug("Put param value: " + param_value);
>                     formattingParams.put(I18N_VALUE_ATTRIBUTE,
param_value);
>                 }
>                 if ("date".equals(paramType) ||
>     "time".equals(paramType)) {
>                     debug("Formatting date_time param: " +
formattingParams);
>                     param_value = formatDate_Time(formattingParams);
>                 } else if ("number".equals(paramType)) {
>                     debug("Formatting number param: " + formattingParams);
>                     param_value = formatNumber(formattingParams);
>                 }
>             }
>         }
>         debug("Added substitution param: " + param_value);
>         indexedParams.add(param_value);
>         param_value = null;
>         current_state = STATE_INSIDE_TRANSLATE;
>     }
>
>     private void endTranslateElement() throws SAXException {
>
>         if (substitute_text == null) {
>             return;
>         }
>
>         String result;
>         if (indexedParams.size() > 0 && substitute_text.length() > 0) {
>             debug("Text for susbtitution: " + substitute_text);
>             result = formatter.format(substitute_text,
indexedParams.toArray());
>             debug("Result of susbtitution: " + result);
>         } else {
>             result = substitute_text;
>         }
>
>         super.contentHandler.characters(result.toCharArray(), 0,
result.length());
>         indexedParams.clear();
>         substitute_text = null;
>         current_state = STATE_OUTSIDE;
>     }
>
>     private void endDate_TimeElement() throws SAXException {
>         String result = formatDate_Time(formattingParams);
>         super.contentHandler.characters(result.toCharArray(), 0,
result.length());
>         current_state = STATE_OUTSIDE;
>     }
>
>     private String formatDate_Time(Map params) throws SAXException {
>         // result pattern is localized
>         SimpleDateFormat to_fmt = null;
> String element = null;
>
>         if (params == null) {
>             throw new SAXException(this.getClass().getName()
>                                    + ": i18n:"+element+" - error in
element attributes.");
>         }
>
> int style;
> String styleAttribute = (String)params.get(I18N_STYLE_ATTRIBUTE);
> if(styleAttribute == null) {
>     style = DateFormat.DEFAULT;
> }
> else {
>     if(styleAttribute.equalsIgnoreCase("short"))
> style = DateFormat.SHORT;
>     else if(styleAttribute.equalsIgnoreCase("medium"))
> style = DateFormat.MEDIUM;
>     else if(styleAttribute.equalsIgnoreCase("long"))
> style = DateFormat.LONG;
>     else if(styleAttribute.equalsIgnoreCase("full"))
> style = DateFormat.FULL;
>     else
> throw new SAXException(this.getClass().getName()
>        + ": i18n:"+element+
>        " - error in element attribute: style.");
> }
>
> if (current_state == STATE_INSIDE_DATE) {
>     element = I18N_DATE_ELEMENT;
>     to_fmt = (SimpleDateFormat)DateFormat.
> getDateInstance(style, locale);
> }
> else { /* STATE_INSIDE_TIME */
>     element = I18N_TIME_ELEMENT;
>     to_fmt = (SimpleDateFormat)DateFormat.
> getTimeInstance(style, locale);
> }
>         // from pattern
>         String srcPattern =
(String)params.get(I18N_SRC_PATTERN_ATTRIBUTE);
>         // to pattern
>         String pattern = (String)params.get(I18N_PATTERN_ATTRIBUTE);
>         // the date value
>         String value = (String)params.get(I18N_VALUE_ATTRIBUTE);
>
>         // parsed date object
>         Date dateValue = null;
>
>         // src format
>         SimpleDateFormat from_fmt =
(SimpleDateFormat)DateFormat.getInstance();
>         if (srcPattern != null) {
>             from_fmt.applyPattern(srcPattern);
>         }
>
>         if (pattern != null) {
>             to_fmt.applyPattern(pattern);
>         }
>
>         // get current date and time by default
>         if (value == null) {
>             dateValue = new Date();
>         } else {
>             try {
>                 dateValue = from_fmt.parse(value);
>             } catch (ParseException pe) {
>                 throw new SAXException(this.getClass().getName()
>                                        + "i18n:date - parsing error.",
pe);
>             }
>         }
>
>         // we have all necessary data here: do formatting.
>         String result = to_fmt.format(dateValue);
>         debug("i18n:"+element+" result: " + result);
>         return result;
>     }
>
>     private void endNumberElement() throws SAXException {
>         String result = formatNumber(formattingParams);
>         super.contentHandler.characters(result.toCharArray(), 0,
result.length());
>         current_state = STATE_OUTSIDE;
>     }
>
>     private String formatNumber(Map params) throws SAXException {
>         if (params == null) {
>             throw new SAXException(this.getClass().getName()
>                                    + ": i18n:number - error in element
attributes.");
>         }
>         // from pattern
>         String srcPattern =
(String)params.get(I18N_SRC_PATTERN_ATTRIBUTE);
>         // to pattern
>         String pattern = (String)params.get(I18N_PATTERN_ATTRIBUTE);
>         // the number value
>         String value = (String)params.get(I18N_VALUE_ATTRIBUTE);
>         // sub-type
>         String subType = (String)params.get(I18N_SUB_TYPE_ATTRIBUTE);
>
>         // parsed number
>         Number numberValue = null;
>
>         // src format
>         DecimalFormat from_fmt =
(DecimalFormat)NumberFormat.getInstance();
>         if (srcPattern != null) {
>             from_fmt.applyPattern(srcPattern);
>         }
>
>         // result pattern is localized
>         DecimalFormat to_fmt = null;
>         if (subType == null) {
>             to_fmt = (DecimalFormat)NumberFormat.getInstance(locale);
>         } else if (subType.equals("currency")) {
>             to_fmt =
(DecimalFormat)NumberFormat.getCurrencyInstance(locale);
>         } else if (subType.equals("percent")) {
>             to_fmt =
(DecimalFormat)NumberFormat.getPercentInstance(locale);
>         }
>         if (pattern != null) {
>             to_fmt.applyPattern(pattern);
>         }
>
>         // get current date and time by default
>         if (value == null) {
>             numberValue = new Long(0);
>         } else {
>             try {
>                 numberValue = from_fmt.parse(value);
>             } catch (ParseException pe) {
>                 throw new SAXException(this.getClass().getName()
>                                        + "i18n:number - parsing error.",
pe);
>             }
>         }
>
>         // we have all necessary data here: do formatting.
>         String result = to_fmt.format(numberValue);
>         debug("i18n:number result: " + result);
>         return result;
>     }
>
>     /**
>      * Helper method to retrieve a message from the dictionary
>      */
>     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);
>     }
>
>     /**
>      * Helper method to debug messages
>      */
>     private void debug(String msg) {
>     getLogger().debug("I18nTransformer: " + msg);
>     }
>
>     /**
>      *
>     static public void main(String[] args) {
>
>         Locale locale = null;
>
>         Locale[] locales = Locale.getAvailableLocales();
>         for (int i = 0; i < locales.length; i++) {
>             locale = locales[i];
>             SimpleDateFormat fmt =
(SimpleDateFormat)DateFormat.getDateTimeInstance(
>                                        DateFormat.DEFAULT,
DateFormat.DEFAULT, locale
>                                    );
>
>             String localized = fmt.format(new Date());
>
>             NumberFormat n_fmt = NumberFormat.getCurrencyInstance(locale);
>             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);
>         }
>     }
>      */
>
> }
>
>


----------------------------------------------------------------------------
----


> ---------------------------------------------------------------------
> Please check that your question has not already been answered in the
> FAQ before posting. <http://xml.apache.org/cocoon/faqs.html>
>
> To unsubscribe, e-mail: <cocoon-users-unsubscribe@xml.apache.org>
> For additional commands, e-mail: <cocoon-users-help@xml.apache.org>

---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Mime
View raw message