blob: c43a0c04efc38396173070ba5d77b8f2ac661bb0 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="Date-Revision-yyyymmdd" content="20140918"/>
<meta http-equiv="Content-Language" content="en"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Localization</title>
<link href="//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,400italic,600italic,700italic" rel="stylesheet" type="text/css">
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link href="/css/main.css" rel="stylesheet">
<link href="/css/custom.css" rel="stylesheet">
<link href="/highlighter/github-theme.css" rel="stylesheet">
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="/bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="/js/community.js"></script>
</head>
<body>
<a href="http://github.com/apache/struts" class="github-ribbon">
<img style="position: absolute; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub">
</a>
<header>
<nav>
<div role="navigation" class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" data-toggle="collapse" data-target="#struts-menu" class="navbar-toggle">
Menu
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/index.html" class="navbar-brand logo"><img src="/img/struts-logo.svg"></a>
</div>
<div id="struts-menu" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Home<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/index.html">Welcome</a></li>
<li><a href="/download.cgi">Download</a></li>
<li><a href="/releases.html">Releases</a></li>
<li><a href="/announce-2021.html">Announcements</a></li>
<li><a href="http://www.apache.org/licenses/">License</a></li>
<li><a href="https://www.apache.org/foundation/thanks.html">Thanks!</a></li>
<li><a href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Support<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/mail.html">User Mailing List</a></li>
<li><a href="https://issues.apache.org/jira/browse/WW">Issue Tracker</a></li>
<li><a href="/security.html">Reporting Security Issues</a></li>
<li class="divider"></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Migration+Guide">Version Notes</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Security+Bulletins">Security Bulletins</a></li>
<li class="divider"></li>
<li><a href="/maven/project-info.html">Maven Project Info</a></li>
<li><a href="/maven/struts2-core/dependencies.html">Struts Core Dependencies</a></li>
<li><a href="/maven/struts2-plugins/modules.html">Plugin Dependencies</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Documentation<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/birdseye.html">Birds Eye</a></li>
<li><a href="/primer.html">Key Technologies</a></li>
<li><a href="/kickstart.html">Kickstart FAQ</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Home">Wiki</a></li>
<li class="divider"></li>
<li><a href="/getting-started/">Getting Started</a></li>
<li><a href="/security/">Security Guide</a></li>
<li><a href="/core-developers/">Core Developers Guide</a></li>
<li><a href="/tag-developers/">Tag Developers Guide</a></li>
<li><a href="/maven-archetypes/">Maven Archetypes</a></li>
<li><a href="/plugins/">Plugins</a></li>
<li><a href="/maven/struts2-core/apidocs/index.html">Struts Core API</a></li>
<li><a href="/tag-developers/tag-reference.html">Tag reference</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/FAQs">FAQs</a></li>
<li><a href="http://cwiki.apache.org/S2PLUGINS/home.html">Plugin registry</a></li>
</ul>
</li>
<li class="dropdown">
<a data-toggle="dropdown" href="#" class="dropdown-toggle">
Contributing<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="/youatstruts.html">You at Struts</a></li>
<li><a href="/helping.html">How to Help FAQ</a></li>
<li><a href="/dev-mail.html">Development Lists</a></li>
<li><a href="/contributors/">Contributors Guide</a></li>
<li class="divider"></li>
<li><a href="/submitting-patches.html">Submitting patches</a></li>
<li><a href="/builds.html">Source Code and Builds</a></li>
<li><a href="/coding-standards.html">Coding standards</a></li>
<li><a href="https://cwiki.apache.org/confluence/display/WW/Contributors+Guide">Contributors Guide</a></li>
<li class="divider"></li>
<li><a href="/release-guidelines.html">Release Guidelines</a></li>
<li><a href="/bylaws.html">PMC Charter</a></li>
<li><a href="/volunteers.html">Volunteers</a></li>
<li><a href="https://gitbox.apache.org/repos/asf?p=struts.git">Source Repository</a></li>
<li><a href="/updating-website.html">Updating the website</a></li>
</ul>
</li>
<li class="apache"><a href="http://www.apache.org/"><img src="/img/apache.png"></a></li>
</ul>
</div>
</div>
</div>
</nav>
</header>
<article class="container">
<section class="col-md-12">
<a href="index.html" title="back to Core Developers Guide"><< back to Core Developers Guide</a>
<a class="edit-on-gh" href="https://github.com/apache/struts-site/edit/master/source/core-developers/localization.md" title="Edit this page on GitHub">Edit on GitHub</a>
<h1 class="no_toc" id="localization">Localization</h1>
<ul id="markdown-toc">
<li><a href="#overview" id="markdown-toc-overview">Overview</a></li>
<li><a href="#resource-bundle-search-order" id="markdown-toc-resource-bundle-search-order">Resource Bundle Search Order</a></li>
<li><a href="#default-actions-class" id="markdown-toc-default-actions-class">Default action’s class</a></li>
<li><a href="#examples" id="markdown-toc-examples">Examples</a> <ul>
<li><a href="#using-gettext-from-a-tag" id="markdown-toc-using-gettext-from-a-tag">Using getText from a Tag</a></li>
<li><a href="#using-the-text-tag" id="markdown-toc-using-the-text-tag">Using the text tag</a></li>
<li><a href="#using-the-i18n-tag" id="markdown-toc-using-the-i18n-tag">Using the I18n tag</a></li>
<li><a href="#using-the-key-attribute-of-ui-tags" id="markdown-toc-using-the-key-attribute-of-ui-tags">Using the Key attribute of UI Tags</a></li>
</ul>
</li>
<li><a href="#i18n-interceptor" id="markdown-toc-i18n-interceptor">I18n Interceptor</a></li>
<li><a href="#global-resources-strutscustomi18nresources-in-strutsproperties" id="markdown-toc-global-resources-strutscustomi18nresources-in-strutsproperties">Global Resources (struts.custom.i18n.resources) in struts.properties</a></li>
<li><a href="#formatting-dates-and-numbers" id="markdown-toc-formatting-dates-and-numbers">Formatting Dates and Numbers</a></li>
<li><a href="#comparison-with-struts-1" id="markdown-toc-comparison-with-struts-1">Comparison with Struts 1</a></li>
<li><a href="#using-only-global-bundles" id="markdown-toc-using-only-global-bundles">Using only global bundles</a></li>
<li><a href="#custom-textprovider-and-textproviderfactory" id="markdown-toc-custom-textprovider-and-textproviderfactory">Custom TextProvider and TextProviderFactory</a></li>
</ul>
<h2 id="overview">Overview</h2>
<p>The framework supports internationalization (i18n) in the following places:</p>
<ol>
<li>the <em>UI Tags</em></li>
<li>Messages and Errors from the <a href="http://struts.apache.org/struts2-core/apidocs/index.html?com/opensymphony/xwork2/ValidationAware.html">ValidationAware</a></li>
<li>Within action classes that extend <a href="http://struts.apache.org/struts2-core/apidocs/index.html?com/opensymphony/xwork2/ActionSupport.html">ActionSupport</a> through the <code class="highlighter-rouge">getText()</code> method</li>
</ol>
<h2 id="resource-bundle-search-order">Resource Bundle Search Order</h2>
<p>Resource bundles are searched in the following order:</p>
<ol>
<li><code class="highlighter-rouge">ActionClass</code>.properties</li>
<li><code class="highlighter-rouge">Interface</code>.properties (every interface and sub-interface)</li>
<li><code class="highlighter-rouge">BaseClass</code>.properties (all the way to Object.properties)</li>
<li>ModelDriven’s model (if implements ModelDriven), for the model object repeat from 1</li>
<li>package.properties (of the directory where class is located and every parent directory all the way to the root directory)</li>
<li>search up the i18n message key hierarchy itself</li>
<li>global resource properties</li>
</ol>
<p>This is how it is implemented in a default implementation of the <code class="highlighter-rouge">LocalizedTextProvider</code> interface. You can provide your
own implementation using <code class="highlighter-rouge">TextProvider</code> and <code class="highlighter-rouge">TextProviderFactory</code> interfaces.</p>
<p>To clarify #5, while traversing the package hierarchy, Struts 2 will look for a file <code class="highlighter-rouge">package.properties</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>com/
acme/
package.properties
actions/
package.properties
FooAction.java
FooAction.properties
</code></pre></div></div>
<blockquote>
<p>If <code class="highlighter-rouge">FooAction.properties</code> does not exist, <code class="highlighter-rouge">com/acme/action/package.properties</code> will be searched for, if not found
<code class="highlighter-rouge">com/acme/package.properties</code>, if not found <code class="highlighter-rouge">com/package.properties</code>, etc.</p>
</blockquote>
<h2 id="default-actions-class">Default action’s class</h2>
<p>If you configure action as follow</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;action</span> <span class="na">name=</span><span class="s">"index"</span><span class="nt">&gt;</span>
<span class="nt">&lt;result&gt;</span>/index.jsp<span class="nt">&lt;/result&gt;</span>
<span class="nt">&lt;/action&gt;</span>
</code></pre></div></div>
<p>it will use a default class defined with <code class="highlighter-rouge">default-class-ref</code> in <code class="highlighter-rouge">struts-default.xml</code> which is
<code class="highlighter-rouge">com.opensymphony.xwork2.ActionSupport</code>. It means you have two options here to get I18N working in that case:</p>
<ul>
<li>define <code class="highlighter-rouge">com/opensymphony/xwork2/ActionSupport.properties</code> and put messages there</li>
<li>point <code class="highlighter-rouge">default-class-ref</code> to your base class and then defined appropriated <code class="highlighter-rouge">.properties</code> file (corresponding to
class’ name or package)</li>
</ul>
<h2 id="examples">Examples</h2>
<p>There are several ways to access the message resources, including <code class="highlighter-rouge">getText</code>, the <code class="highlighter-rouge">text</code> tag, and the <code class="highlighter-rouge">i18n</code> tag.</p>
<h3 id="using-gettext-from-a-tag">Using getText from a Tag</h3>
<p>To display i18n text, use a call to <code class="highlighter-rouge">getText</code> in the <code class="highlighter-rouge">property</code> tag, or any other tag, such as the UI tags. (The <code class="highlighter-rouge">getText</code>
technique is especially useful for labels of UI tags.)</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;s:property</span> <span class="na">value=</span><span class="s">"getText('some.key')"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<p>The default implementation of <code class="highlighter-rouge">TextProvider</code> which is used in <code class="highlighter-rouge">ActionSupport</code> perform evaluation of value read from
bundle base on the provided key, see [Localizing Output] for an example.</p>
<h3 id="using-the-text-tag">Using the text tag</h3>
<p>The <code class="highlighter-rouge">text</code> tag retrieves a message from the default resource bundle.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;s:text</span> <span class="na">name=</span><span class="s">"some.key"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;s:text</span> <span class="na">name=</span><span class="s">"some.invalid.key"</span><span class="nt">&gt;</span>
The Default Message That Will Be Displayed
<span class="nt">&lt;/s:text&gt;</span>
</code></pre></div></div>
<h3 id="using-the-i18n-tag">Using the I18n tag</h3>
<p>The <code class="highlighter-rouge">i18n</code> tag pushes an arbitrary resource bundle on to the value stack. Other tags within the scope of the i18n tag
can display messages from that resource bundle.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;s:i18n</span> <span class="na">name=</span><span class="s">"some.package.bundle"</span><span class="nt">&gt;</span>
<span class="nt">&lt;s:text</span> <span class="na">name=</span><span class="s">"some.key"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/s:i18n&gt;</span>
</code></pre></div></div>
<p>Internationalizing SiteMesh decorators is possible, but there are quirks. See <a href="../plugins/sitemesh/">SiteMesh Plugin</a>
for more.</p>
<h3 id="using-the-key-attribute-of-ui-tags">Using the Key attribute of UI Tags</h3>
<p>The key attribute of most UI tags can be used to retrieve a message from a resource bundle:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;s:textfield</span> <span class="na">key=</span><span class="s">"some.key"</span> <span class="na">name=</span><span class="s">"textfieldName"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<h2 id="i18n-interceptor">I18n Interceptor</h2>
<p>Essentially, the i18n Interceptor pushes a locale into the ActionContext map upon every request. The framework
components that support localization all utilize the ActionContext locale. See <a href="i18n-interceptor.html">I18n Interceptor</a> f
or details.</p>
<h2 id="global-resources-strutscustomi18nresources-in-strutsproperties">Global Resources (struts.custom.i18n.resources) in struts.properties</h2>
<p>A global resource bundle could be specified programmatically, as well as the locale.</p>
<h2 id="formatting-dates-and-numbers">Formatting Dates and Numbers</h2>
<p>See <a href="formatting-dates-and-numbers.html">Formatting Dates and Numbers</a> for more details and examples.</p>
<h2 id="comparison-with-struts-1">Comparison with Struts 1</h2>
<p>Struts 1 users should be familiar with the application.properties resource bundle, where you can put all the messages
in the application that are going to be translated. Struts 2, though, splits the resource bundles per action or model
class, and you may end up with duplicated messages in those resource bundles. A quick fix for that is to create a file
called ActionSupport.properties in com/opensymphony/xwork2 and put it on your classpath. This will only work well if
all your actions subclass XWork2’s ActionSupport.</p>
<h2 id="using-only-global-bundles">Using only global bundles</h2>
<p>If you don’t need to use the package-scan-functionality and only base on the global bundles (those provided by
the framework and via <code class="highlighter-rouge">struts.custom.i18n.resources</code>) you can use existing <code class="highlighter-rouge">GlobalLocalizedTextProvider</code>
implementation. To use this please define the following option in your <code class="highlighter-rouge">struts.xml</code>:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;constant</span> <span class="na">name=</span><span class="s">"struts.localizedTextProvider"</span> <span class="na">value=</span><span class="s">"global-only"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<h2 id="custom-textprovider-and-textproviderfactory">Custom TextProvider and TextProviderFactory</h2>
<p>If you want use a different logic to search for localized messages, or you want to use a database or just want to search
default bundles, you must implement both those interfaces (or subclass the existing implementations). You can check
a small <a href="https://github.com/apache/struts-examples/tree/master/text-provider">example app</a> how to use both.
Please remember that the <code class="highlighter-rouge">TextProvider</code> interface is implemented by the <code class="highlighter-rouge">ActionSupport</code> class, that’s why
an extra layer - <code class="highlighter-rouge">TextProviderFactory</code> - is needed.</p>
</section>
</article>
<footer class="container">
<div class="col-md-12">
Copyright &copy; 2000-2018 <a href="http://www.apache.org/">The Apache Software Foundation </a>.
All Rights Reserved.
</div>
<div class="col-md-12">
Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts project logos are
trademarks of The Apache Software Foundation.
</div>
<div class="col-md-12">Logo and website design donated by <a href="https://softwaremill.com/">SoftwareMill</a>.</div>
</footer>
<script>!function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = "//platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");</script>
<script src="https://apis.google.com/js/platform.js" async="async" defer="defer"></script>
<div id="fb-root"></div>
<script>(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_GB/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
</body>
</html>