Labels"

From Documentation
 
(37 intermediate revisions by 7 users not shown)
Line 5: Line 5:
 
For a multilingual application, it is common to display the content in the language that the end user prefers. Here we discuss the built-in support called ''internationalization labels''.  
 
For a multilingual application, it is common to display the content in the language that the end user prefers. Here we discuss the built-in support called ''internationalization labels''.  
  
However, if you prefer to use other approach, please refer to [[#Use Other Implementation|the Use Other Implementation section]].
+
However, if you prefer to use other approaches, please refer to [[#Use Other Implementation|the Use Other Implementation section]].
  
=Internationalization Labels=
+
=Creating Internationalization Labels=
  
The internationalization labels of an application are loaded from properties files based on the current locale<ref>It is the value returned by <javadoc method="getCurrent()">org.zkoss.util.Locales</javadoc>. For more information, please refer to [[ZK Developer's Reference/Internationalization/Locale|the Locale section]].</ref>. A properties file is a simple text file encoded in UTF-8<ref>If you prefer a different charset, please refer to [[#Encoding character set|the Encoding Character Set section]].</ref>. The file contains a list of <tt>key=value</tt> pairs, such as<ref>Please refer to [[ZK Developer's Reference/Internationalization/Labels/The Format of Properties Files|here]] for more details about the format of a properties file, such as the use of multiple lines and EL expressions.</ref>
+
The internationalization labels of an application are loaded from properties files based on the current locale<ref>It is the value returned by <javadoc method="getCurrent()">org.zkoss.util.Locales</javadoc>. For more information, please refer to [[ZK Developer's Reference/Internationalization/Locale|the Locale section]].</ref>. A properties file is a simple text file encoded in UTF-8<ref>If you prefer a different charset, please refer to [[#Encoding character set|the Encoding Character Set section]].</ref>. The file contains a list of <code>key=value</code> pairs, such as<ref>Please refer to [[ZK Developer's Reference/Internationalization/Labels/The Format of Properties Files|here]] for more details about the format of a properties file, such as the use of multiple lines and EL expressions.</ref>
  
 
<source lang="text">
 
<source lang="text">
Line 19: Line 19:
 
</source>
 
</source>
  
By default the property file must be placed under the <tt>WEB-INF</tt> directory and named as <tt>i3-label_''lang''_''CNTY''.properties</tt>.<ref>Notice the directory and filename is configurable. For more information, please refer [[ZK Configuration Reference/zk.xml/The Library Properties/org.zkoss.util.label.web.location|ZK Configuration Reference: org.zkoss.util.label.web.location]]</ref>, where ''lang'' is the language such as en and fr, and ''CNTY'' is the country, such as US and FR.
+
By default the property file must be placed under the <code>WEB-INF</code> directory and named as <code>zk-label_''lang''_''CNTY''.properties</code>.<ref>Notice the directory and filename are configurable. For more information, please refer to [[ZK Configuration Reference/zk.xml/The Library Properties/org.zkoss.util.label.web.location|ZK Configuration Reference: org.zkoss.util.label.web.location]]</ref>, where ''lang'' is the language such as en and fr, and ''CNTY'' is the country, such as US and FR.
  
If you want to use one file to represent a language regardless the country, you could name it <code>i3-label_''lang''.properties</code>, such as <tt>i3-label_ja.properties</tt>. Furthermore, <code>i3-label.properties</code> is the default file if the user's preferred locale doesn't match any other file.
+
If you want to use one file to represent a language regardless of the country, you could name it <code>zk-label_''lang''.properties</code>, such as <code>zk-label_ja.properties</code>. Furthermore, <code>zk-label.properties</code> is the default file if the user's preferred locale doesn't match any other file.
  
When an user accesses a page, ZK will load the properties files for the user's locale. For example, assume the locale is <tt>de_DE</tt>, then it will search the following files and load them if found:
+
When a user accesses a page, ZK will load the properties files for the user's locale. For example, assume the locale is <code>de_DE</code>, then it will search the following files and load them if found:
  
#i3-label_de_DE.properties
+
#zk-label_de_DE.properties
#i3-label_de.properties
+
#zk-label_de.properties
#i3-label.properties
+
#zk-label.properties
  
By default, one properties file is used to contain all labels of a given locale. If you prefer to split it to multiple properties files (such as one file per module), please refer to [[#Loading Labels from Multiple Resources|the Loading Labels from Multiple Resources section]].
+
By default, one properties file is used to contain all labels of a given locale. If you prefer to split it into multiple properties files (such as one file per module), please refer to [[#Loading Labels from Multiple Resources|the Loading Labels from Multiple Resources section]].
  
Also notice that all files match the given locale will be loaded and merged, and the property specified in, say, <tt>i3-label_de_DE.properties</tt> will override what are defined in <tt>i3-label_de.properties</tt> if replicated. It also means if a label is the same in both <tt>de_DE</tt> and <tt>de</tt>, then you need only to specify in <tt>i3-label_de.properties</tt> (and then it will be ''inherited'' when de_DE is used). Of course, you could specify it in both files.
+
Also, notice that all files that match the given locale will be loaded and merged, and the property specified in, say, <code>zk-label_de_DE.properties</code> will override what is defined in <code>zk-label_de.properties</code> if replicated. It also means if a label is the same in both <code>de_DE</code> and <code>de</code>, then you need only to specify in <code>zk-label_de.properties</code> (and then it will be ''inherited'' when de_DE is used). Of course, you could specify it in both files.
  
 
<blockquote>
 
<blockquote>
Line 38: Line 38:
 
</blockquote>
 
</blockquote>
  
{{ZKDevelopersReferenceHeadingToc}}
 
  
== Access Internationalization Labels In ZUML ==
 
=== Use <tt>labels</tt> ===
 
  
Since 5.0.7 and later, an implicit object called [[ZUML Reference/EL Expressions/Implicit Objects/labels|labels]] was introduced, such that you could access the internationalization labels (so-called internationalization labels) directly. For example, assume you have a label called <tt>app.title</tt>, and then you could:
+
== Encoding character set ==
 +
 
 +
By default, the encoding of properties files is assumed to be <code>UTF-8</code>. If you prefer another encoding, please specify it in a library property called <code>org.zkoss.util.label.web.charset</code>.  It also means all properties files must be encoded in the same character set.
 +
 
 +
For more information, please refer to [[ZK Configuration Reference/zk.xml/The Library Properties/org.zkoss.util.label.web.charset|ZK Configuration Reference]].
 +
 
 +
 
 +
= Access Internationalization Labels In ZUML =
 +
== Use <code>labels</code> ==
 +
{{versionSince | 5.0.7}}
 +
 
 +
Since 5.0.7 and later, an implicit object called [[ZUML Reference/EL Expressions/Implicit Objects/labels|labels]] was introduced, such that you could access the internationalization labels (so-called internationalization labels) directly. For example, assume you have a label called <code>app.title</code>, and then you could:
  
 
<source lang="xml">
 
<source lang="xml">
Line 51: Line 59:
 
</source>
 
</source>
  
The [[ZUML Reference/EL Expressions/Implicit Objects/labels|labels]] object is a map (<tt>java.util.Map</tt>), so you could access the label directly by use of <tt>labels.''whatever''</tt> in an EL expression. Moreover, as shown above, you could access the label even if a key is named as ''aa''.''bb''.''cc'' (a string containing dot), such as <code>app.title</code> in the above example.
+
The [[ZUML Reference/EL Expressions/Implicit Objects/labels|labels]] object is a map (<code>java.util.Map</code>), so you could access the label directly by the use of <code>labels.''whatever''</code> in an EL expression. Moreover, as shown above, you could access the label even if a key is named as ''aa''.''bb''.''cc'' (a string containing dots), such as <code>app.title</code> in the above example.
 +
 
 +
If the key is not a legal name, you could use <code>labels['key']</code> to access it, such as <code>labels['foo-yet']</code>.
 +
 
 +
When an internationalization label is about to be retrieved, one of zk-label_''lang''_''CNTY''.properties will be loaded. For example, if the Locale is <code>de_DE</code>, then <code>WEB-INF/zk-label_de_DE.properties</code> will be loaded. If no such file, ZK will try to load <code>WEB-INF/zk-label_de.properties</code> and <code>WEB-INF/zk-label.properties</code> in turn.
 +
 
 +
Notice that ZK groups the segmented labels as maps. For example, <code>${labels.app}</code> was resolved as a map containing two entries (<code>title</code> and <code>description</code>).
 +
 
 +
<source lang="xml">
 +
app.title=Foo
 +
app.description=A super application
 +
</source>
 +
 
 +
If you have a key named as the prefix of the other keys, you have to use <code>$</code> to access it. For example, if the labels consist of keys <code>a, a.b</code>, etc., <code>${labels.a.$}</code> is required to resolve the label with the key named <code>a</code>.
  
If the key is not a legal name, you could use <tt>labels['key']</tt> to access, such as <tt>labels['foo-yet']</tt>.
+
For example, in properties file:
 +
<source lang="xml">
 +
app=Application
 +
app.title=Foo
 +
app.description=A super application
 +
</source>
  
When an internationalization label is about to be retrieved, one of i3-label_''lang''_''CNTY''.properties will be loaded. For example, if the Locale is <tt>de_DE</tt>, then <tt>WEB-INF/i3-label_de_DE.properties</tt> will be loaded. If no such file, ZK will try to load <tt>WEB-INF/i3-label_de.properties</tt> and <tt>WEB-INF/i3-label.properties</tt> in turn.
+
In ZUL:
  
=== Use <tt>c:l('key')</tt> ===
+
<source lang="xml">
With 5.0.6 or prior, you could, to get an internationalization label, use <tt>${c:l('key')}</tt> in EL expression. For example,
+
<window title="${labels.app.$}"><!-- shows "Application" -->
 +
...
 +
</window>
 +
<window title="${labels.app}"><!-- WRONG! -->
 +
...
 +
</window>
 +
</source>
 +
 
 +
== Use <code>c:l('key')</code> ==
 +
With 5.0.6 or prior, you could get an internationalization label using <code>${c:l('key')}</code> in EL expression. For example,
  
 
<source lang="xml" >
 
<source lang="xml" >
Line 70: Line 105:
 
Notice that [[ZUML Reference/EL Expressions/Core Methods/l|the l function]] belongs to the TLD file called [[ZUML Reference/EL Expressions/Core Methods|http://www.zkoss.org/dsp/web/core]], so we have to specify it with the [[ZUML Reference/ZUML/Processing Instructions/taglib|taglib]] directive as shown above.
 
Notice that [[ZUML Reference/EL Expressions/Core Methods/l|the l function]] belongs to the TLD file called [[ZUML Reference/EL Expressions/Core Methods|http://www.zkoss.org/dsp/web/core]], so we have to specify it with the [[ZUML Reference/ZUML/Processing Instructions/taglib|taglib]] directive as shown above.
  
=== Use <tt>c:l2('key')</tt> to format the message ===
 
  
If you'd like to use the label as a pattern to generate concatenated message with additional arguments (like <code>[http://download.oracle.com/javase/6/docs/api/java/text/MessageFormat.html java.text.MessageFormat]</code> does), you could use [[ZUML Reference/EL Expressions/Core Methods/l2|the l2 function]]
+
== Use <code>c:l2('key')</code> to format the message ==
 +
 
 +
If you'd like to use the label as a pattern to generate concatenated message with additional arguments (like <code>[https://docs.oracle.com/javase/8/docs/api/java/text/MessageFormat.html java.text.MessageFormat]</code> does), you could use [[ZUML Reference/EL Expressions/Core Methods/l2|the l2 function]].
 +
 
 +
For example, let us assume we want to generate a full name based on the current Locale, then we could use <code>${c:l2('key',args)}</code> to generate concatenated messages as follows.
  
For example, let us assume we want to generate a full name based on the current Locale, then we could use <tt>${c:l2('key',args)}</tt> to generate concatenated messages as follows.
+
<syntaxhighlight lang='text'>
 +
fullname.format=full name is {0}
 +
</syntaxhighlight>
  
 
<source lang="xml" >
 
<source lang="xml" >
Line 81: Line 121:
 
</source>
 
</source>
  
where we assume fullname is a string array (such as <code>new String[] {"Jimmy", "Shiau"}</code>).
+
* We assume <code>fullname</code> is a string array (such as <code>new String[] {"Jimmy", "Shiau"}</code>).
  
 
<javadoc method="getLabel(java.lang.String, java.lang.Object[])">org.zkoss.util.resource.Labels</javadoc> assumes the content is a valid pattern accepted by [http://download.oracle.com/javase/6/docs/api/java/text/MessageFormat.html MessageFormat], such as <code>"{1}, {0}"</code>.
 
<javadoc method="getLabel(java.lang.String, java.lang.Object[])">org.zkoss.util.resource.Labels</javadoc> assumes the content is a valid pattern accepted by [http://download.oracle.com/javase/6/docs/api/java/text/MessageFormat.html MessageFormat], such as <code>"{1}, {0}"</code>.
  
== Access Internationalization Labels In Java ==
+
Please notice that "a single quote itself must be represented by doubled single quotes" according to [https://docs.oracle.com/javase/8/docs/api/java/text/MessageFormat.html java.text.MessageFormat].
 +
 
 +
= Access Internationalization Labels In Java =
 
To access labels in Java code (including zscript), you could use <javadoc method="getLabel(java.lang.String)">org.zkoss.util.resource.Labels</javadoc>, <javadoc method="getLabel(java.lang.String, java.lang.Object[])">org.zkoss.util.resource.Labels</javadoc> and others.
 
To access labels in Java code (including zscript), you could use <javadoc method="getLabel(java.lang.String)">org.zkoss.util.resource.Labels</javadoc>, <javadoc method="getLabel(java.lang.String, java.lang.Object[])">org.zkoss.util.resource.Labels</javadoc> and others.
  
Line 101: Line 143:
  
 
<javadoc method="getLabel(java.lang.String, java.lang.Object[])">org.zkoss.util.resource.Labels</javadoc> assumes the content is a valid pattern accepted by [http://download.oracle.com/javase/6/docs/api/java/text/MessageFormat.html MessageFormat], such as <code>"{1}, {0}"</code>.
 
<javadoc method="getLabel(java.lang.String, java.lang.Object[])">org.zkoss.util.resource.Labels</javadoc> assumes the content is a valid pattern accepted by [http://download.oracle.com/javase/6/docs/api/java/text/MessageFormat.html MessageFormat], such as <code>"{1}, {0}"</code>.
 
== Encoding character set ==
 
 
By default, the encoding of properties files are assumed to be <code>UTF-8</code>. If you prefer another encoding, please specify it in a library property called <code>org.zkoss.util.label.web.charset</code>.  It also means all properties files must be encoded in the same character set.
 
 
For more information, please refer to [[ZK Configuration Reference/zk.xml/The Library Properties/org.zkoss.util.label.web.charset|ZK Configuration Reference]].
 
  
 
= Loading Labels from Multiple Resources =
 
= Loading Labels from Multiple Resources =
 +
{{versionSince | 5.0.7}}
  
 
It is typical to partition the properties file into several modules for easy maintenance. Since 5.0.7 and later, you could specify the location for each of these properties file with [[ZK Configuration Reference/zk.xml/The system-config Element/The label-location Element|the label-location element]]. For example,
 
It is typical to partition the properties file into several modules for easy maintenance. Since 5.0.7 and later, you could specify the location for each of these properties file with [[ZK Configuration Reference/zk.xml/The system-config Element/The label-location Element|the label-location element]]. For example,
Line 119: Line 156:
 
</source>
 
</source>
  
Notice that, once you specify <tt>label-location</tt>, the default loading of <tt>WEB-INF/i3-labels.properties</tt> is ignored. In other words, ZK loads only the files specified in the label-location elements. Thus, if you'd like to load <tt>WEB-INF/i3-labels.properties</tt> too, you have to add it to <tt>label-location</tt> with others.
+
Notice that, once you specify <code>label-location</code>, the default loading of <code>/WEB-INF/zk-labels.properties</code> won't take place. In other words, only the properties files specified in the label-location elements are loaded. Thus, if you'd like to load <code>/WEB-INF/zk-labels.properties</code> too, you have to add it to <code>label-location</code> with others.
 +
 
 +
Also notice that you don't have to and shall not specify the language, such as <code>de_DE</code>, in the path. ZK will try to locate the most matched one as described in the previous section.
 +
 
 +
In addition to the servlet path, you could specify a file path by starting with <code>file://</code><ref>For more information about the URI of a file, please refer to [http://en.wikipedia.org/wiki/File_URI_scheme File URI scheme].</ref>. For example, <code>file:///foo/labels.properties</code>. If the target environment is Windows, you could specify the drive too, such as <code>file:///C:/myapp/foo.properties</code>. The advantage is that additional properties files could be added after the project has been built into a WAR file.
 +
 
 +
<source lang="xml">
 +
<system-config>
 +
    <label-location>file:///labels/order.properties</label-location>
 +
    <label-location>file:///labels/invoice.properties</label-location>
 +
</system-config>
 +
</source>
 +
 
 +
Notice that the configuration with a path related to the file system is better not to be part of <code>WEB-INF/zk.xml</code>, since it is easy to cause errors when deploying the application. Rather, it is better to be specified in the additional configuration file. The additional configuration file is also specified at the run time and could be located in the file system (rather than the WAR file). It can be done by specifying the path of the configuration file in a library property called [[ZK Configuration Reference/zk.xml/The Library Properties/org.zkoss.zk.config.path|org.zkoss.zk.config.path]].
  
 
For 5.0.6 and older, you could use the approach described in the following section to load multiple properties files.
 
For 5.0.6 and older, you could use the approach described in the following section to load multiple properties files.
  
== Loading from Database or Other Resources ==
+
<blockquote>
 +
----
 +
<references/>
 +
</blockquote>
  
If you prefer to put the internationalization labels in, say, database, you could extend the label loader to load labels from other locations, say database. It can be done by registering a locator, which must implement either <javadoc type="interface">org.zkoss.util.resource.LabelLocator</javadoc> or <javadoc type="interface">org.zkoss.util.resource.LabelLocator2</javadoc>. Then, invoke <javadoc method="register(org.zkoss.util.resource.LabelLocator)">org.zkoss.util.resource.Labels</javadoc> or <javadoc method="register(org.zkoss.util.resource.LabelLocator2)">org.zkoss.util.resource.Labels</javadoc> to register it.
+
= Loading Labels from Jar=
 +
If your application is built using multiple Jars as custom components or as a modular project, you can load internationalization labels by putting the .properties files in the resource folder of your add-on project.
  
If you can represent your resource in URL, you could use <javadoc type="interface">org.zkoss.util.resource.LabelLocator</javadoc> (as show below). If you have to load it by yourself, you could use <javadoc type="interface">org.zkoss.util.resource.LabelLocator2</javadoc> and return an input stream (java.io.InputStream).
+
Required Steps:
 +
# put properties files under <code>[classpath]/metainfo</code>
 +
# the properties files name should be <code>zk-label.properties</code> or <code>zk-label_[LOCALE].properties</code>
 +
#: e.g. <code>zk-label_en.properties</code>
  
For example, with ZK 5.0.6 and older, you could load labels from multiple properties files by use of the following code<ref>For 5.0.7 and later, you could use [[ZK Configuration Reference/zk.xml/The system-config Element/The label-location Element|the label-location element]] instead.</ref>:
+
For example, if you are building with maven, the files can be placed into <code>/src/main/resources/metainfo/</code> in your project.
 +
You can define the default labels using <code>zk-label.properties</code> as well as language specific labels using the same file name convention as in the default case.
 +
 
 +
When the jars generated this way are added as libraries to the main ZK project, the properties files located in these libraries will be used to locate labels as well as the properties files declared in the main application.
 +
The properties files must follow the same syntax used in the default case.
 +
 
 +
= Loading from Database or Other Resources =
 +
 
 +
If you prefer to put the internationalization labels in, say, database, you could extend the label loader to load labels from other locations, say database. It can be done by registering a locator, which must implement either <javadoc type="interface">org.zkoss.util.resource.LabelLocator</javadoc> or <javadoc type="interface">org.zkoss.util.resource.LabelLocator2</javadoc>. Then, invoking <javadoc method="register(org.zkoss.util.resource.LabelLocator)">org.zkoss.util.resource.Labels</javadoc> or <javadoc method="register(org.zkoss.util.resource.LabelLocator2)">org.zkoss.util.resource.Labels</javadoc> to register it<ref>For 5.0.7 and later, you could use [[ZK Configuration Reference/zk.xml/The system-config Element/The label-location Element|the label-location element]] if the properties file is located in the file system or in the Web application as described in the previous section.</ref>.
 +
 
 +
If you can represent your resource in URL, you could use <javadoc type="interface">org.zkoss.util.resource.LabelLocator</javadoc> (as shown below). If you have to load it by yourself, you could use <javadoc type="interface">org.zkoss.util.resource.LabelLocator2</javadoc> and return an input stream (java.io.InputStream).
 +
 
 +
'''Alernative 1: load as an input stream:'''
 +
<source lang="java">
 +
public class FooDBLocator implements org.zkoss.util.resource.LabelLocator2 {
 +
    private String _field;
 +
    public FooDBLocator(String field) {
 +
        _field = field;
 +
    }
 +
    public InputStream locate(Locale locale) {
 +
        InputStream is = ... //load the properties from, say, database
 +
        return is;
 +
    }
 +
    public String getCharset() {
 +
        return "UTF-8"; //depending the encoding you use
 +
    }
 +
}
 +
</source>
 +
 
 +
'''Alernative 2: load as an URL:'''
  
 
<source lang="java">
 
<source lang="java">
public class FooLocator extends org.zkoss.zk.ui.util.LabelLocator {
+
public class FooServletLocator implements org.zkoss.util.resource.LabelLocator {
 
     private ServletContext _svlctx;
 
     private ServletContext _svlctx;
 
     private String _name;
 
     private String _name;
     public FootLocator(SevletContext svlctx, String name) {
+
     public FooServletLocator(ServletContext svlctx, String name) {
 
         _svlctx = svlctx;
 
         _svlctx = svlctx;
 
         _name = name;
 
         _name = name;
Line 150: Line 236:
 
public class MyAppInit implements org.zkoss.zk.ui.util.WebAppInit {
 
public class MyAppInit implements org.zkoss.zk.ui.util.WebAppInit {
 
     public void init(WebApp wapp) throws Exception {
 
     public void init(WebApp wapp) throws Exception {
         Labels.register(new FooLocator((ServletContext)wapp.getNativeContext(), "module-1");
+
         Labels.register(new FooDBLocator(("moduleX");
         Labels.register(new FooLocator((ServletContext)wapp.getNativeContext(), "module-2");
+
        Labels.register(new FooDBLocator(("moduleY");
 +
        Labels.register(new FooServletLocator((ServletContext)wapp.getNativeContext(), "module-1");
 +
         Labels.register(new FooServletLocator((ServletContext)wapp.getNativeContext(), "module-2");
 
     }
 
     }
 
}
 
}
 
</source>
 
</source>
  
where we assume <tt>module-1.properties</tt> and <tt>module-2.properties</tt> are two modules of messages you provide. Then, you configure it in <tt>WEB-INF/zk.xml</tt> as described in [[ZK_Configuration_Reference/zk.xml/The_listener_Element/The_org.zkoss.zk.ui.util.WebAppInit_interface|ZK Configuration Reference]].
+
where we assume <code>moduleX</code> and <code>moduleY</code> are the database tables to load the properties, and <code>module-1.properties</code> and <code>module-2.properties</code> are two modules of messages you provide. Then, you configure it in <code>WEB-INF/zk.xml</code> as described in [[ZK_Configuration_Reference/zk.xml/The_listener_Element/The_org.zkoss.zk.ui.util.WebAppInit_interface|ZK Configuration Reference]].
  
 
<blockquote>
 
<blockquote>
Line 164: Line 252:
  
 
=Reload Labels Dynamically=
 
=Reload Labels Dynamically=
The internationalization labels are loaded when a locale is used at the first time. It won't be reloaded automatically if the file is modified. However, it is easy to force ZK to reload by use of <javadoc method="reset()">org.zkoss.util.resource.Labels</javadoc>.
+
The internationalization labels are loaded when a locale is used for the first time. It won't be reloaded automatically if the file is modified. However, it is easy to force ZK to reload by the use of <javadoc method="reset()">org.zkoss.util.resource.Labels</javadoc>.
  
 
For example, you could prepare a test paging for reloading as follows.
 
For example, you could prepare a test paging for reloading as follows.
Line 187: Line 275:
  
 
=Version History=
 
=Version History=
{{LastUpdated}}
+
 
{| border='1px' | width="100%"
+
{| class='wikitable' | width="100%"
 
! Version !! Date !! Content
 
! Version !! Date !! Content
 
|-
 
|-
Line 197: Line 285:
 
| 5.0.7
 
| 5.0.7
 
| March 2011
 
| March 2011
| The <tt>labels</tt> object was introduced.
+
| The <code>labels</code> object was introduced.
 
|}
 
|}
  
 +
 +
{{ZKDevelopersReferenceHeadingToc}}
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 06:30, 6 February 2024

Overview

For a multilingual application, it is common to display the content in the language that the end user prefers. Here we discuss the built-in support called internationalization labels.

However, if you prefer to use other approaches, please refer to the Use Other Implementation section.

Creating Internationalization Labels

The internationalization labels of an application are loaded from properties files based on the current locale[1]. A properties file is a simple text file encoded in UTF-8[2]. The file contains a list of key=value pairs, such as[3]

# This is the default LabelsBundle.properties file
s1=computer
s2=disk
s3=monitor
s4=keyboard

By default the property file must be placed under the WEB-INF directory and named as zk-label_lang_CNTY.properties.[4], where lang is the language such as en and fr, and CNTY is the country, such as US and FR.

If you want to use one file to represent a language regardless of the country, you could name it zk-label_lang.properties, such as zk-label_ja.properties. Furthermore, zk-label.properties is the default file if the user's preferred locale doesn't match any other file.

When a user accesses a page, ZK will load the properties files for the user's locale. For example, assume the locale is de_DE, then it will search the following files and load them if found:

  1. zk-label_de_DE.properties
  2. zk-label_de.properties
  3. zk-label.properties

By default, one properties file is used to contain all labels of a given locale. If you prefer to split it into multiple properties files (such as one file per module), please refer to the Loading Labels from Multiple Resources section.

Also, notice that all files that match the given locale will be loaded and merged, and the property specified in, say, zk-label_de_DE.properties will override what is defined in zk-label_de.properties if replicated. It also means if a label is the same in both de_DE and de, then you need only to specify in zk-label_de.properties (and then it will be inherited when de_DE is used). Of course, you could specify it in both files.


  1. It is the value returned by Locales.getCurrent(). For more information, please refer to the Locale section.
  2. If you prefer a different charset, please refer to the Encoding Character Set section.
  3. Please refer to here for more details about the format of a properties file, such as the use of multiple lines and EL expressions.
  4. Notice the directory and filename are configurable. For more information, please refer to ZK Configuration Reference: org.zkoss.util.label.web.location


Encoding character set

By default, the encoding of properties files is assumed to be UTF-8. If you prefer another encoding, please specify it in a library property called org.zkoss.util.label.web.charset. It also means all properties files must be encoded in the same character set.

For more information, please refer to ZK Configuration Reference.


Access Internationalization Labels In ZUML

Use labels

Since 5.0.7

Since 5.0.7 and later, an implicit object called labels was introduced, such that you could access the internationalization labels (so-called internationalization labels) directly. For example, assume you have a label called app.title, and then you could:

<window title="${labels.app.title}">
...
</window>

The labels object is a map (java.util.Map), so you could access the label directly by the use of labels.whatever in an EL expression. Moreover, as shown above, you could access the label even if a key is named as aa.bb.cc (a string containing dots), such as app.title in the above example.

If the key is not a legal name, you could use labels['key'] to access it, such as labels['foo-yet'].

When an internationalization label is about to be retrieved, one of zk-label_lang_CNTY.properties will be loaded. For example, if the Locale is de_DE, then WEB-INF/zk-label_de_DE.properties will be loaded. If no such file, ZK will try to load WEB-INF/zk-label_de.properties and WEB-INF/zk-label.properties in turn.

Notice that ZK groups the segmented labels as maps. For example, ${labels.app} was resolved as a map containing two entries (title and description).

app.title=Foo
app.description=A super application

If you have a key named as the prefix of the other keys, you have to use $ to access it. For example, if the labels consist of keys a, a.b, etc., ${labels.a.$} is required to resolve the label with the key named a.

For example, in properties file:

app=Application
app.title=Foo
app.description=A super application

In ZUL:

<window title="${labels.app.$}"><!-- shows "Application" -->
...
</window>
<window title="${labels.app}"><!-- WRONG! -->
...
</window>

Use c:l('key')

With 5.0.6 or prior, you could get an internationalization label using ${c:l('key')} in EL expression. For example,

<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>

<window title="${c:l('app.title')}">
 ...
</window>

Notice that the l function belongs to the TLD file called http://www.zkoss.org/dsp/web/core, so we have to specify it with the taglib directive as shown above.


Use c:l2('key') to format the message

If you'd like to use the label as a pattern to generate concatenated message with additional arguments (like java.text.MessageFormat does), you could use the l2 function.

For example, let us assume we want to generate a full name based on the current Locale, then we could use ${c:l2('key',args)} to generate concatenated messages as follows.

fullname.format=full name is {0}
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<label value="${c:l2('fullname.format', fullname)}">
  • We assume fullname is a string array (such as new String[] {"Jimmy", "Shiau"}).

Labels.getLabel(String, Object[]) assumes the content is a valid pattern accepted by MessageFormat, such as "{1}, {0}".

Please notice that "a single quote itself must be represented by doubled single quotes" according to java.text.MessageFormat.

Access Internationalization Labels In Java

To access labels in Java code (including zscript), you could use Labels.getLabel(String), Labels.getLabel(String, Object[]) and others.

String username = Labels.getLabel("username");

Here is a more complex example. Let us assume we want to generate a full name based on the Locale, then we could use Labels.getLabel(String, Object[]) to generate concatenated messages as follows.

public String getFullName(String firstName, String lastName) {
   return Labels.getLabel("fullname.format", new java.lang.Object[] {firstName, lastName});
}

Labels.getLabel(String, Object[]) assumes the content is a valid pattern accepted by MessageFormat, such as "{1}, {0}".

Loading Labels from Multiple Resources

Since 5.0.7

It is typical to partition the properties file into several modules for easy maintenance. Since 5.0.7 and later, you could specify the location for each of these properties file with the label-location element. For example,

<system-config>
    <label-location>/WEB-INF/labels/order.properties</label-location>
    <label-location>/WEB-INF/labels/invoice.properties</label-location>
</system-config>

Notice that, once you specify label-location, the default loading of /WEB-INF/zk-labels.properties won't take place. In other words, only the properties files specified in the label-location elements are loaded. Thus, if you'd like to load /WEB-INF/zk-labels.properties too, you have to add it to label-location with others.

Also notice that you don't have to and shall not specify the language, such as de_DE, in the path. ZK will try to locate the most matched one as described in the previous section.

In addition to the servlet path, you could specify a file path by starting with file://[1]. For example, file:///foo/labels.properties. If the target environment is Windows, you could specify the drive too, such as file:///C:/myapp/foo.properties. The advantage is that additional properties files could be added after the project has been built into a WAR file.

<system-config>
    <label-location>file:///labels/order.properties</label-location>
    <label-location>file:///labels/invoice.properties</label-location>
</system-config>

Notice that the configuration with a path related to the file system is better not to be part of WEB-INF/zk.xml, since it is easy to cause errors when deploying the application. Rather, it is better to be specified in the additional configuration file. The additional configuration file is also specified at the run time and could be located in the file system (rather than the WAR file). It can be done by specifying the path of the configuration file in a library property called org.zkoss.zk.config.path.

For 5.0.6 and older, you could use the approach described in the following section to load multiple properties files.


  1. For more information about the URI of a file, please refer to File URI scheme.

Loading Labels from Jar

If your application is built using multiple Jars as custom components or as a modular project, you can load internationalization labels by putting the .properties files in the resource folder of your add-on project.

Required Steps:

  1. put properties files under [classpath]/metainfo
  2. the properties files name should be zk-label.properties or zk-label_[LOCALE].properties
    e.g. zk-label_en.properties

For example, if you are building with maven, the files can be placed into /src/main/resources/metainfo/ in your project. You can define the default labels using zk-label.properties as well as language specific labels using the same file name convention as in the default case.

When the jars generated this way are added as libraries to the main ZK project, the properties files located in these libraries will be used to locate labels as well as the properties files declared in the main application. The properties files must follow the same syntax used in the default case.

Loading from Database or Other Resources

If you prefer to put the internationalization labels in, say, database, you could extend the label loader to load labels from other locations, say database. It can be done by registering a locator, which must implement either LabelLocator or LabelLocator2. Then, invoking Labels.register(LabelLocator) or Labels.register(LabelLocator2) to register it[1].

If you can represent your resource in URL, you could use LabelLocator (as shown below). If you have to load it by yourself, you could use LabelLocator2 and return an input stream (java.io.InputStream).

Alernative 1: load as an input stream:

public class FooDBLocator implements org.zkoss.util.resource.LabelLocator2 {
    private String _field;
    public FooDBLocator(String field) {
        _field = field;
    }
    public InputStream locate(Locale locale) {
        InputStream is = ... //load the properties from, say, database
        return is;
    }
    public String getCharset() {
        return "UTF-8"; //depending the encoding you use
    }
}

Alernative 2: load as an URL:

public class FooServletLocator implements org.zkoss.util.resource.LabelLocator {
    private ServletContext _svlctx;
    private String _name;
    public FooServletLocator(ServletContext svlctx, String name) {
        _svlctx = svlctx;
        _name = name;
    }
    public URL locate(Locale locale) {
        return _svlctx.getResource("/WEB-INF/labels/" + name + "_" + locale + ".properties");
    }
}

Then, we could register label locators when the application starts by use of WebAppInit as follows.

public class MyAppInit implements org.zkoss.zk.ui.util.WebAppInit {
    public void init(WebApp wapp) throws Exception {
        Labels.register(new FooDBLocator(("moduleX");
        Labels.register(new FooDBLocator(("moduleY");
        Labels.register(new FooServletLocator((ServletContext)wapp.getNativeContext(), "module-1");
        Labels.register(new FooServletLocator((ServletContext)wapp.getNativeContext(), "module-2");
    }
}

where we assume moduleX and moduleY are the database tables to load the properties, and module-1.properties and module-2.properties are two modules of messages you provide. Then, you configure it in WEB-INF/zk.xml as described in ZK Configuration Reference.


  1. For 5.0.7 and later, you could use the label-location element if the properties file is located in the file system or in the Web application as described in the previous section.

Reload Labels Dynamically

The internationalization labels are loaded when a locale is used for the first time. It won't be reloaded automatically if the file is modified. However, it is easy to force ZK to reload by the use of Labels.reset().

For example, you could prepare a test paging for reloading as follows.

<zk>
<button label="Reload Labels" onClick="org.zkoss.util.resource.Labels.reset();execution.sendRedirect(null);"/>
Test result: ${foo} ${another.whatever}
</zk>

Use Other Implementation

If you prefer to use other implementation (such as property bundle), you could implement a static method and map it with xel-method. Then, you could reference it in EL expressions. For example,

<?xel-method prefix="c" name="label" class="foo.MyI18Ns" 
  signature="java.lang.String label(java.lang.String)"?>
<window title="${c:label('app.title')}">
....
${c:label('another.key')}
</window>

Version History

Version Date Content
5.0.5 October 2010 LabelLocator2 was introduced.
5.0.7 March 2011 The labels object was introduced.




Last Update : 2024/02/06

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.