blob: 0d93a4d09b0b34ba3b0d0b4efa97413cfcc5d47d [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>
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
/* We explicitly disable cookie tracking to avoid privacy issues */
_paq.push(['disableCookies']);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//analytics.apache.org/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '41']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
</head>
<body>
<a href="https://github.com/apache/struts" class="github-ribbon">
<img decoding="async" loading="lazy" style="position: absolute; right: 0; border: 0;" width="149" height="149" src="https://github.blog/wp-content/uploads/2008/12/forkme_right_red_aa0000.png?resize=149%2C149" class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1">
</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-2024.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>
<li><a href="https://privacy.apache.org/policies/privacy-policy-public.html">Privacy Policy</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><a href="/commercial-support.html">Commercial Support</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 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="/contributors/">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="#search-in-default-bundles-first" id="markdown-toc-search-in-default-bundles-first">Search in default bundles first</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/maven/struts2-core/apidocs/index.html?com/opensymphony/xwork2/ValidationAware">ValidationAware</a></li>
<li>Within action classes that extend <a href="http://struts.apache.org/maven/struts2-core/apidocs/index.html?com/opensymphony/xwork2/ActionSupport">ActionSupport</a>
through the <code class="language-plaintext 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="language-plaintext highlighter-rouge">ActionClass</code>.properties</li>
<li><code class="language-plaintext highlighter-rouge">Interface</code>.properties (every interface and sub-interface)</li>
<li><code class="language-plaintext 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="language-plaintext highlighter-rouge">LocalizedTextProvider</code> interface. You can provide your
own implementation using <code class="language-plaintext highlighter-rouge">TextProvider</code> and <code class="language-plaintext highlighter-rouge">TextProviderFactory</code> interfaces.</p>
<p>To clarify #5, while traversing the package hierarchy, Struts 2 will look for a file <code class="language-plaintext highlighter-rouge">package.properties</code>:</p>
<div class="language-plaintext 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="language-plaintext highlighter-rouge">FooAction.properties</code> does not exist, <code class="language-plaintext highlighter-rouge">com/acme/action/package.properties</code> will be searched for, if not found
<code class="language-plaintext highlighter-rouge">com/acme/package.properties</code>, if not found <code class="language-plaintext 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="language-plaintext highlighter-rouge">default-class-ref</code> in <code class="language-plaintext highlighter-rouge">struts-default.xml</code> which is
<code class="language-plaintext 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="language-plaintext highlighter-rouge">com/opensymphony/xwork2/ActionSupport.properties</code> and put messages there</li>
<li>point <code class="language-plaintext highlighter-rouge">default-class-ref</code> to your base class and then defined appropriated <code class="language-plaintext 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="language-plaintext highlighter-rouge">getText</code>, the <code class="language-plaintext highlighter-rouge">text</code> tag, and the <code class="language-plaintext 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="language-plaintext highlighter-rouge">getText</code> in the <code class="language-plaintext highlighter-rouge">property</code> tag, or any other tag, such as the UI tags. (The <code class="language-plaintext 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="language-plaintext highlighter-rouge">TextProvider</code> which is used in <code class="language-plaintext 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="language-plaintext 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="language-plaintext 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">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">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="search-in-default-bundles-first">Search in default bundles first</h2>
<p>Since Struts 2.6 it is possible to enable searching in default bundles first instead of performing a full class hierarchy
scan and then default bundles.</p>
<p>By setting the below flag to <code class="language-plaintext highlighter-rouge">true</code> the default implementation of <a href="https://struts.apache.org/maven/struts2-core/apidocs/index.html?com/opensymphony/xwork2/LocalizedTextProvider.html">LocalizedTextProvider</a>
(which is <a href="https://struts.apache.org/maven/struts2-core/apidocs/index.html?com/opensymphony/xwork2/util/StrutsLocalizedTextProvider.html">StrutsLocalizedTextProvider</a>)
will search the default bundles first. In some cases this can improve overall application performance.</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.i18n.search.defaultbundles.first"</span> <span class="na">value=</span><span class="s">"true"</span><span class="nt">/&gt;</span>
</code></pre></div></div>
<blockquote>
<p>More details can be found in <a href="https://issues.apache.org/jira/browse/WW-5112">WW-5112</a> and the linked PR.</p>
</blockquote>
<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="language-plaintext highlighter-rouge">struts.custom.i18n.resources</code>) you can use existing <a href="https://struts.apache.org/maven/struts2-core/apidocs/index.html?com/opensymphony/xwork2/util/GlobalLocalizedTextProvider.html">GlobalLocalizedTextProvider</a>
implementation. To use this please define the following option in your <code class="language-plaintext 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 to 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="language-plaintext highlighter-rouge">TextProvider</code> interface is implemented by the <code class="language-plaintext highlighter-rouge">ActionSupport</code> class, that’s why
an extra layer - <a href="https://struts.apache.org/maven/struts2-core/apidocs/index.html?com/opensymphony/xwork2/TextProviderFactory.html">TextProviderFactory</a></p>
<ul>
<li>is needed.</li>
</ul>
</section>
</article>
<footer class="container">
<div class="col-md-12">
Copyright &copy; 2000-2022 <a href="https://www.apache.org/">The Apache Software Foundation</a>.
Apache Struts, Struts, Apache, the Apache feather logo, and the Apache Struts project logos are
trademarks of The Apache Software Foundation. All Rights Reserved.
</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>