2011-06-23 154 views
8

我已经接受了使用2.3 servlet规范将i18n引入到J2EE Web应用程序的(相当艰巨的)任务。该应用程序非常庞大,已经积极开发超过8年。因此,我希望第一次把事情做好,这样我就可以限制我需要通过JSP,JavaScript文件,servlet和其他任何地方乱写乱七八糟的字符串,并用消息包中的值替换硬编码的字符串。Java web应用程序i18n

这里没有使用框架。我该如何处理支持国际化的问题。请注意,我希望每个视图都有一个JSP,用于从(a)属性文件加载文本,而不是针对每个受支持的区域设置使用不同的JSP。

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

任何想法,我可以如何处理这个?

+0

[How to internationalize a java web applications。](http://stackoverflow.com/questions/4276061/how-to-internationalize-a-java-web-application) – BalusC

回答

18

有很多问题需要采取,而国际化应用的护理事情:

区域设置检测

您需要考虑的第一件事是检测终端用户的语言环境。根据你想要支持的内容,它可能很容易或者有点复杂。

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

JSTL的做法可能是足够的,如果你只是想支持第一种方法,或者如果你不打算添加任何额外的依赖(如Spring框架)。

虽然我们在Spring框架有不少,你可以同时使用来检测地点(如CookieLocaleResolverAcceptHeaderLocaleResolverSessionLocaleResolverLocaleChangeInterceptor)和外在的字符串和格式化的消息(见spring:message tab)几个不错的功能。
Spring Framework将允许您轻松实现上述所有场景,这就是为什么我更喜欢它。

字符串外部

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

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

串连

我讨厌这样说,但级联从本地化的角度真的很痛苦。它们很常见,大多数人都没有意识到。

那么什么是串接呢?

从原理上讲,每个英语句子都需要翻译成目标语言。问题是,正确翻译的信息使用与其英文对应的不同顺序(因此英语“安全策略”被翻译为波兰语“Politykabezpieczeństwa” - “策略”是“polityka” - 顺序不同),它发生了很多次。

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

在Web应用程序,你可以连接字符串是这样的:

String securityPolicy = "Security " + "policy"; 

或像这样:

<p><span style="font-weight:bold">Security</span> policy</p> 

两者会产生问题。在第一种情况下,您需要使用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" /> 

这是隐蔽的日期和时间,重要的是有效的(最终用户)的时区。此外,将其转换为易于理解的格式非常重要 - 这就是为什么我建议使用默认格式样式。
顺便说一句。时区检测并不容易,因为网页浏览器不太好发送任何东西。相反,你可以首选时区字段添加到用户的偏好(如果有的话),或者获取当前时区通过客户端脚本从Web浏览器偏移(见Date object's methods

数字和货币

号以及货币应该转换为本地格式。它是在类似的方式来完成的日期格式(解析也同样进行):

<fmt:formatNumber value="1.21" type="currency"/> 

复合的消息

你已经被警告不要连接字符串。相反,你可能会使用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ę. 

字符编码

如果你打算本地化为不支持ISO 8859-1代码页的语言,你将需要支持Unicode - 最好的方法是将页面编码设置为UTF-8。我看到有人在做这样的:

<%@ page contentType="text/html; charset=UTF-8" %> 

我必须提醒你:这是不够。实际上,你需要这样的声明:

<%@page pageEncoding="UTF-8" %> 

此外,你还需要在页眉申报编码,只是为了安全起见:

<META http-equiv="Content-Type" content="text/html;charset=UTF-8"> 

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

+0

Thanks for优秀的答案。不幸的是,我使用的应用程序是庞大的(这是一个非常老的项目)。 Spring被引入到项目中(这是约10年的iirc),我在使用字符串的应用程序的一部分中使用了Spring的i18n支持。不幸的是,似乎没有人真的想到i18n,所以似乎我陷入了一个相当漫长而乏味的任务。 – NRaf

1

您可以使用带有标签的JSTL标准标签库完成此操作。抓住JSTL规范的副本,阅读讨论一般文本+日期,时间,货币的i8N章节。写得非常清楚,并向您展示如何使用标签完成所有操作。你也可以以编程方式设置Locale之类的东西

1

你不应该(并且不应该)每个语言环境都需要一个单独的JSP文件。困难的任务是找出没有编辑好的密钥,并将它们移动到每个语言环境的文件中,比如messages_en.properties,messages_fr.properties等等。

区域设置计算可能发生在多个地方,具体取决于您的逻辑。我们支持存储在数据库中的用户语言环境以及浏览器语言环境。每一个进入你的应用程序的请求都会有一个“Accept-Language”标题,它表示你的浏览器配置了哪些语言,以及首选项,即日语和英语。如果是这种情况,应用程序应该读取messages_ja.properties以及不在该文件中的键,回退到messages_en.properties。对于存储在数据库内的用户区域设置也是如此。请注意,标准只是在浏览器中切换语言,并期望内容能够被浏览。 (我们最初开始在数据库中存储语言环境,然后移动到支持来自浏览器的语言环境)。无论如何,翻译人员会错过将英语(主要语言文件)中的键和值复制到其他语言,因此您将需要默认值,因此对于不在其他文件中的值,您需要默认为英语。

我还发现mygengo将翻译工作转给其他懂得特定语言的人时非常有用,它为我们节省了大量时间。