Java Web应用程序i18n

NRa*_*Raf 8 java jsp internationalization

我已经获得了使用2.3 servlet规范将i18n引入J2EE Web应用程序的(相当艰巨的)任务.该应用程序非常庞大,已经积极开发了8年多.

因此,我希望第一次就把事情做好,这样我就可以限制我需要在JSP,JavaScript文件,servlet和其他任何地方乱写的时间,用消息包中的值替换硬编码的字符串.

这里没有使用框架.我怎样才能支持i18n.请注意,我希望每个视图都有一个JSP,它可以从(a)属性文件加载文本,而不是为每个支持的语言环境加载不同的JSP.

我想我的主要问题是我是否可以在'后端'中的某处设置区域设置(即在登录时从用户配置文件中读取区域设置并在会话中存储值),然后期望JSP页面能够正确加载指定的字符串.正确的属性文件(即,当语言环境为法语时,来自messages_fr.properties),而不是添加逻辑以在每个JSP中查找正确的语言环境.

我有什么想法可以解决这个问题吗?

Paw*_*yda 19

在国际化应用程序时需要注意很多事情:

区域设置检测

您需要考虑的第一件事是检测最终用户的区域设置.根据您想要支持的内容,它可能很容易或有点复杂.

  1. 您肯定知道,W​​eb浏览器倾向于通过HTTP Accept-Language标头发送最终用户的首选语言.在Servlet中访问此信息可能就像调用一样简单request.getLocale().如果您不打算支持任何花哨的Locale Detection工作流程,您可能会坚持使用此方法.
  2. 如果您的应用程序中有用户配置文件,则可能需要向其添加"首选语言"和"首选格式设置区域设置".在这种情况下,您需要在用户登录后切换Locale.
  3. 您可能希望支持基于URL的语言切换(例如:http://deutsch.example.com/http://example.com?lang=de).您需要根据URL信息设置有效的Locale - 这可以通过各种方式完成(即URL过滤器).
  4. 您可能希望支持语言切换(从下拉菜单中选择它,或者某些东西),但是我不推荐它(除非它与第3点结合使用).

如果您只想支持第一种方法,或者您不打算添加任何其他依赖项(如Spring Framework),那么 JSTL方法就足够了.

当我们使用Spring Framework时,它有很多不错的功能,您可以使用它们来检测Locale(如CookieLocaleResolver,AcceptHeaderLocaleResolver,SessionLocaleResolverLocaleChangeInterceptor)以及外部化字符串和格式化消息(请参阅spring:message选项卡).
Spring Framework允许您轻松实现上面的所有场景,这就是我喜欢它的原因.

字符串外化

这应该是容易的,对吧?好吧,主要是 - 只需使用适当的标签.您可能面临的唯一问题是外部化客户端(JavaScript)文本.有几种可能的方法,但我要提到这两种方法:

  1. 让每个JSP编写已翻译字符串的数组(带有消息标记),并在客户端代码中简单地访问该数组.这种方法更简单但可维护性更低 - 您需要实际从有效页面(实际引用客户端脚本的页面)中编写有效字符串.我之前已经做过并相信我,这不是你想要在大型应用程序中做的事情(但它可能是小型应用程序的最佳解决方案).
  2. 另一种方法原则上可能听起来很难,但实际上将来更容易处理.我们的想法是在客户端集中字符串(将它们移动到一些常见的JavaScript文件).之后,您需要实现自己的Servlet,它将根据请求返回此脚本 - 内容应该被翻译.您将无法在此处使用JSTL,您需要直接从Resource Bundles获取字符串.
    它更容易维护,因为你有一个中心点来添加可翻译的字符串.

级联

我讨厌这样说,但从Localizability的角度来看,连接真的很痛苦.它们很常见,大多数人都没有意识到这一点.

那么什么是串联呢?

原则上,每个英语句子都需要翻译成目标语言.问题是,正确翻译的消息使用不同的单词顺序发生了很多次,因为英语"安全政策"被翻译成波兰语"Politykabezpieczeństwa" - "政策"是"polityka" - 顺序不同).

好的,但它与软件有什么关系?

在Web应用程序中,您可以像这样连接字符串:

String securityPolicy = "Security " + "policy";
Run Code Online (Sandbox Code Playgroud)

或者像这样:

<p><span style="font-weight:bold">Security</span> policy</p>
Run Code Online (Sandbox Code Playgroud)

两者都有问题.在第一种情况下,你将需要使用MessageFormat.format()方法和外部化字符串作为(例如)"Security {0}",并"policy"在后者,你会外部化全段(p标签)的内容,包括 span标签.我知道这对翻译来说是痛苦的,但实际上并没有更好的方法.
有时你必须在你的段落中使用动态内容 - JSTL fmt:format标签也会在这里帮助你(它在后端方面使用石灰MessageFormat).

布局

在本地化应用程序中,经常发生翻译的字符串比英语字符串更长.结果可能看起来很丑陋.不知何故,你需要修复样式.还有两种方法:

  1. 通过调整常见样式来解决问题(并祈祷它不会破坏其他语言).这是非常痛苦的维护.
  2. 实现CSS本地化机制.我正在谈论的机制应该提供默认的,与语言无关的CSS文件和每种语言的覆盖.我们的想法是为每种语言覆盖CSS文件,以便您可以按需调整布局(仅适用于一种语言).为此,默认CSS文件以及JSP页面不得包含!important任何样式定义旁边的关键字.如果你真的必须使用它,将它们移动到基于语言的en.css - 这将允许其他语言修改它们.

文化特定问题

避免使用可能特定于西方文化的图形,颜色和声音.如果您真的需要它,请提供本地化方法.避免使用方向敏感图形(因为当您尝试本地化以说阿拉伯语或希伯来语时,这将是一个问题).此外,不要假设整个世界使用相同的数字(即阿拉伯语不是这样).

日期和时区

在Java中处理日期的时间至少不容易.如果您不打算支持除格里高利历之外的其他任何内容,您可以坚持使用内置的日期和日历类.您可以使用JSTL fmt:timeZone,fmt:formatDate和fmt:parseDate在JSP中正确设置时区,格式和解析日期.

我强烈建议像这样使用fmt:formatDate:

<fmt:formatDate value="${someController.somedate}" 
    timeZone="${someController.detectedTimeZone}"
    dateStyle="default" 
    timeStyle="default" />
Run Code Online (Sandbox Code Playgroud)

将日期和时间转换为有效(最终用户)的时区非常重要.将它转换为易于理解的格式非常重要 - 这就是我推荐默认格式化风格的原因.
BTW.时区检测并不容易,因为Web浏览器发送任何东西都不是那么好.相反,您可以将首选时区字段添加到用户首选项(如果有的话)或通过客户端脚本从Web浏览器获取当前时区偏移(请参阅Date对象的方法)

数字和货币

数字和货币应转换为本地格式.它以与格式化日期类似的方式完成(解析也类似地完成):

<fmt:formatNumber value="1.21" type="currency"/> 
Run Code Online (Sandbox Code Playgroud)

复合消息

您已经被警告不要连接字符串.相反,你可能会使用MessgageFormat.但是,我必须声明您应该尽量减少使用复合邮件.这只是因为目标语法规则通常是不同的,因此翻译者可能不仅需要重新排序句子(这可以通过使用占位符来解决MessageFormat.format()),而是根据将被替换的内容以不同的方式翻译整个句子.我举几个例子:

// Multiple plural forms
English: 4 viruses found.
Polish: Znaleziono 4 wirusy. **OR** Znaleziono 5 wirusów.

// Conjugation
English: Program encountered incorrect character | Application encountered incorrect character.
Polish: Program napotka? nieznan? liter? | Aplikacja napotka?a nieznan? liter?.
Run Code Online (Sandbox Code Playgroud)

字符编码

如果您计划将本地化为不支持ISO 8859-1代码页的语言,则需要支持Unicode - 最好的方法是将页面编码设置为UTF-8.我见过人们这样做:

<%@ page contentType="text/html; charset=UTF-8" %>
Run Code Online (Sandbox Code Playgroud)

我必须警告你:这还不够.你真的需要这个声明:

<%@page pageEncoding="UTF-8" %>
Run Code Online (Sandbox Code Playgroud)

此外,您仍然需要在页眉中声明编码,只是为了安全起见:

<META http-equiv="Content-Type" content="text/html;charset=UTF-8"> 
Run Code Online (Sandbox Code Playgroud)

我给你的清单并不详尽,但这是一个很好的起点.祝好运 :)