This document describes how to write Sakai tools that are localized and internationalized.
Contents
Best Practices
- Follow the standards described in this guide
- Always use properties files for user interface text; never include displayable text in java, jsp, or templates (see Localizing Tools below)
- Use properties files only for user interface text and config files for configuration settings (see Config Properties below).
- Use a proper naming schema for keys in your resource bundles. The name of the keys should provide some information about the context of the displayed text. This helps the translators during the translation process. A naming schema could be <view>.<type>.<name>. Examples: edit.button.save, edit.label.title or list.action.addnew
- Group keys by view, ie. edit.title, edit.instructions, list.title, list.instructions, create.title, etc
- Never use displayable text when executing comparisons within the logic of the tool (separate codified values from displayable text)
- Always use the ResourceLoader (not ResourceBundle) class for retrieving properties values, and invoke ResourceLoader methods dynamically, to accomodate dynamic user preferences (see Resource Loader below).
- All dynamically constructed phrases must be sensitive to locale specific subject/verb/object ordering by using the Sakai ResourceLoader class, currently: org.sakaiproject.util.ResourceLoader.getFormattedMessage() (see Structure messages appropriately below)
- Test tools in more than one language
Localizing Tools
All language based text should be localized into a properties file (e.g. Messages.properties), specific to each tool. This includes static text rendered from JSF/RSF/Velocity/etc. templates, and dynamic text inserted from the tool classes.
For example:
page.message.key = This is a message which the user will see
Standard Java formatting classes, such as MessageFormat, ChoiceFormat, DateFormat, NumberFormat and DecimalFormat are preferred over tool-specific formatting, which may not be sensitive to international formats.
There is an excellent tutorial on internationalization at http://java.sun.com/docs/books/tutorial/i18n/index.html as well as a checklist at http://java.sun.com/docs/books/tutorial/i18n/intro/checklist.html.
Structure messages appropriately
Don't split a simple statement across multiple messages in order to place variables in the message. Different languages use different subject/verb/object sentence constructs. The Java MessageFormat and ChoiceFormat classes can be used insert variable text into static text.
Example Final Statement:
Welcome to the awesome view, Mr. User.
Wrong Way:
page.statement.1 = Welcome to the page.statement.2 = view,
Right Way:
# Sample: Welcome to the (page title) view, (user display name).
page.statement = Welcome to the {0} view, {1}
Config Properties
Properties files (<filename>.properties) should only be used for user interface text that should be translated. All other configuration information (e.g. configuration constants, class names, filenames, etc.) should be in a separate <filename>.config file and referenced as follows:
import java.util.Properties; ... Properties p = new Properties(); p.load(this.getClass().getResourceAsStream("filename.config"));
ResourceLoader Class
The org.sakaiproject.util.java.ResourceLoader class is a wrapper class for the ResourceBundle class. It provides dynamic language/locale support for individual users, and is recommended for all Sakai tools.
The sakai-util.jar file (or sakai-util-java.jar file prior to Sakai 2.2) needs to be included with every tool during the maven build process, by including the following dependency in the tool's project.xml file:
<dependency>
<groupId>org.sakaiproject</groupId>
<artifactId>sakai-util</artifactId>
<version>${sakai.version}</version>
<properties>
<war.bundle>true</war.bundle>
</properties>
</dependency>
Strings in the tool's properties file can be retrieved in the Java code using the ResourceLoader class:
ResourceLoader rb = new ResourceLoader("_org.sakaiproject.tool.foobar.bundle.Messages_"); String foo = rb.getString("foo");
JSF based tools
Each tool's properties file should be loaded as a managed-bean in it's faces-config.xml file:
<managed-bean>
<description>
Dynamic Resource Bundle Loader
</description>
<managed-bean-name>msgs</managed-bean-name>
<managed-bean-class>org.sakaiproject.util.java.ResourceLoader</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<description>Bundle baseName</description>
<property-name>baseName</property-name>
<value>_org.sakaiproject.tool.foobar.bundle.Messages_</value>
</managed-property>
</managed-bean>
An alternative to modifying the faces-config.xml file is to insert the following into the jsf file:
<jsp:useBean id="msgs" class="org.sakaiproject.util.ResourceLoader" scope="session"> <jsp:setProperty name="msgs" property="baseName" value="_org.sakaiproject.tool.foobar.bundle.Messages_"/> </jsp:useBean>
Any reference to <f:loadBundle> should be removed from all JSF template files.
Localized strings in properties files are referenced using standard JSF variable syntax:
<h:outputText value="#\{msgs.foo\}"/>
Velocity based tools
Reference to the ResourceLoader object class can be passed to a Velocity template in it's context:
ResourceLoader rb = new ResourceLoader("_org.sakaiproject.tool.foobar.bundle.Messages_"); context.put("tlang", rb );
Then any strings can be referenced as standard velocity variables:
$tlang.getString("foo");
RSF based tools
RSF has a few ways to handle internationalized messages. The simplest way is to place the message key right in the RSF template. The default location for the Messages.properties file in RSF tools is tool/src/webapp/WEB-INF/messages but this is configurable.
Here is an example of placing the message key in the template:
<span rsf:id="msg=page.user.message.key">This is an internationalized message.</span>
Here is an example of using the UIMessage class:
(From the RSF template)
<span rsf:id="my-rsf-id">This will be an internationalized message.</span>
(From the RSF producer)
UIMessage.make(tofill, "my-rsf-id", "page.user.message.key");
There is more information at the RSF Wiki I18n page
Suggested reading
Introducing inheritance to PropertyResourceBundles
"Creating a fully internationalized Java application using PropertyResourceBundles can present some interesting design and implementation problems, including concern over how to modularize the bundles to be used in different areas of the application. In this article, we will explore a solution based on PropertyResourceBundles, which should simplify the design and implementation problems, while promoting reuse of existing bundles."
http://www-128.ibm.com/developerworks/java/library/j-bundles/
Comments (1)
Jul 15
Roland Groen says:
Spring MVC integration Hi, We ported a part of the ResourceLoader code into the ...Spring MVC integration
Hi,
We ported a part of the ResourceLoader code into the SakaiLocaleResolver that integrates the same functionality into the Spring MVC framework. This class is part of edia-sakai-utilsin contrib, and can be installed into your spring configuration with the following xml fragment:
Regards,
Roland.