完成了博客引擎后,咱们来考虑额外的一件事:Web应用的国际化和语言的本地化。虽然咱们能够一开始就作这件事,可是最好仍是先完成该应用的单一语言版本,而后再添加其余语言的支持。html
咱们将分两步讨论,先是国际化,再是本地化。这两样都会被重点说起。java
国际化,在编程领域中,指的是移除本地特定的代码。在Web应用中,基本上是修改模板中特定于语言的交互文本。它还包括修改非文本的数据类型:日期,货币和其余数字。编程
本地化,是打造应用的本地版本。若是应用已经国际化了,就意味着存在一个或多个可选的本地版本。在Web应用中,本地化主要是把交互文本翻译成特定的语言。这里的“特定语言”,取决于浏览器和应用本身的设置。segmentfault
实际上,二者是同步进行的:你在国际化的同时,每每也是在本地化。api
咱们这次的起点,是在Play安装包的
samples-and-tests/yabe
中的已完成版本。目标是完全国际化应用,而后添加简体中文的本地版本。(我擅自修改了教程的内容 :D )浏览器
如今开始吧。首先打开conf/application.conf
,去除注释或添加一行来支持两种语言:oracle
# 为英文,简体中文本地化 application.langs=en,zh_CN
若是你如今启动应用,Play命令行会显示两个Warning,由于你还没准备好本地信息文件:app
16:19:04,728 WARN ~ Messages file missing for locale en 16:19:04,729 WARN ~ Messages file missing for locale zh_CN
前面的警告提醒咱们须要把现有的conf/messages
文件替换成每一个语言对应一个信息文件:框架
messages.en messages.zh_CN
在这里,咱们遇到跟通常在Java中的作法不同的地方。这些文件用的语法跟Java properties文件同样,但它们不是properties文件,由于它们须要使用UTF-8编码。而Java properties文件使用的是Latin-1编码。post
使用UTF-8进行编码可以给本地化带来不少的好处。这使得你能够用纯文本记录下本地化信息。好比,这意味着对于希腊本地化文本,本来须要:
hello.morning = \u0152\u222b\u0152\u00b1\u0152\u00aa\u0152\u2211\u0152\u00ba\u0152\u2260\u0153\u00c5\u0152\u00b1 hello.informal = \u0152\u2265\u0152\u00b5\u0152\u03c0\u0152\u00b1 \u0153\u00c9\u0152\u00f8\u0153\u00d6
如今只需直接使用希腊字母:
hello.morning = καλημέρα hello.informal = γεια σου
在本教程剩余部分,咱们会在上述文件中定义信息,或在HTML模板中使用国际化标记。
最简单的状况是HTML模板中被包含起来的一条文本字符串。举个例子,在yabe/app/views/main.html
模板内的tools
列表中:
<ul id="tools"> <li> <a href="@{Admin.index()}">Log in to write something</a> </li> </ul>
仅需使用&{'key'}
语法,把该文本替换成待查找的信息,就能实现国际化:
<ul id="tools"> <li> <a href="@{Admin.index()}">&{'views.main.tools.login'}</a> </li> </ul>
添加对应的行到每一个信息文件,就能实现本地化。在conf/messages.en
:
views.main.tools.login = Log in to write something
在conf/messages.zh_CN
:
views.main.tools.login = 写点什么
具体的内容由你来定;在这个例子中,我使用了一个键来标记位置views/main.html#tools
一旦保存了更改,刷新一下,应该能够看到本来的英文文本变成中文了。这是由于请求中设定了Accept-Language
为zh-CN
的缘故。
若是登陆进博客的'admin'页面,你就能看到文章,标签,评论和用户的列表。这些页面是CRUD模块的功劳。对于每一个页面,标题和列表头是与应用的模型,好比JavaBean类和属性名,联系在一块儿的。
咱们可使用这些模型的名字做为信息键,来国际化CRUD模块。就跟前面作的同样:
在conf/messages.zh_CN
post = 文章 Post = 文章 posts = 文章列表 Posts = 文章列表 comment = 评论 Comment = 评论 comments = 评论列表 Comments = 评论列表 user = 用户 User = 用户 users = 用户列表 Users = 用户列表
你将注意到那些圆角的紫色导航连接没有改变:
它们是在views/admin.html
中定义的,经过用&{'...'}
把文本包围起来,你就能把它们国际化:
<a href="@{Posts.list()}">&{'Posts'}</a> … <a href="@{Tags.list()}">&{'Tags'}</a> … <a href="@{Comments.list()}">&{'Comments'}</a> … <a href="@{Users.list()}">&{'Users'}</a>
除了字面量字符串,咱们的应用还包括带有变量的信息,好比posts tagged with Play
。
对于带单个参数的字符串,用Java格式化字符串来插入参数:
views.Application.listTagged.title = Posts tagged with %s
接着在模板中,添加这样的参数:
&{'views.Application.listTagged.title', tag}
当一个信息包括多个参数时,在格式化字符串中添加索引来指定参数顺序:
views.Admin.index.welcome = Welcome %1$s, <span>you have written %2$s posts so far</span>
……而后就是这样:
&{'views.Admin.index.welcome', user, posts.size()}
在这个例子中,咱们想要使用“post”的正确的复数形式,因此也把这个词当作一个参数:
views.Admin.index.welcome = Welcome %1$s, <span>you have written %2$s %3$s so far</span>
而后在模板中使用pluralize
拓展:
&{'views.Admin.index.welcome', user, posts.size(), posts.pluralize(messages.get('post'), messages.get('posts'))}
注意咱们须要使用messages.get
来查找对应的单数和复数形式。
对Play模型的本地化就跟对其余地方的本地化同样。这个应用使用到了CRUD和Secure模块,意味着咱们须要本地化play/modules/crud/conf/messages
和play/modules/secure/conf/messages
中的用到的信息。
在conf/messages.zh_CN
:
# play/modules/crud (administration) crud.title = 管理面板 crud.home = 主页 crud.blank = 新增功能 crud.index.title = 选择编辑对象 crud.index.objectType = 输入对象 crud.index.action = crud.index.add = 添加 crud.add = &{%s} 添加 crud.list.title = &{%s} crud.list.size = %d &{%s} crud.list.totalSize = %d 总计 crud.pagination.previous = « 上一页 crud.pagination.next = 下一页 » crud.pagination.last = 末页 »» crud.pagination.first = «« 首页 crud.show.title = &{%s} 编辑 crud.save = 保存 crud.saveAndContinue = 保存并继续编辑 crud.cancel = 取消 crud.hasErrors = 请更正错误 crud.blank.title = &{%s} 添加 crud.saveAndAddAnother = 保存并新增 crud.delete = &{%s} 删除 crud.created = &{%s} 已建立 crud.saved = &{%s} 已保存 crud.deleted = &{%s} 已删除 crud.delete.error = 此对象没法删除 crud.search = 搜索 crud.none = (无) crud.help.required = 必填 crud.help.minlength = 至少要有 %d. crud.help.maxlength = 最多只能是 %d. crud.help.email = 须要有效邮箱 crud.help.dateformat = 时间格式 YYYY-MM-DD crud.help.numeric = 须要数值类型 crud.help.min = 至少须要 %d crud.help.future = 在未来 crud.help.past = 在以前 crud.help.after = 之上 %s. crud.help.before = 之下 %s. crud.help.range = 从 %d 到 %d # play/modules/secure secure.username = 您的邮箱: secure.password = 您的密码: secure.signin = 立刻登陆
当你在本地化一个Web应用时,假如你正在使用一个基于组件的Web应用框架,好比JavaServer Faces,不免会遇到一些难如下手的地方:
在Play里,这三点都不是问题。
第一种状况,你在模板的属性的值里用到了参数信息,好比:
<a href="@{Application.show(_post.id)}" title="By Bob">
这是JSF的一个问题,由于一般须要使用XML标签来完成参数替换,而不能直接在属性值里完成。在Play中,你能够直接这么写:
<a href="@{Application.show(_post.id)}" title="&{'views.tags.display.author', _post.author.fullname}">
第二种状况是在想要使用格式化字符串来排版一个值,好比用参数拼出By Bob on 2009-06-14
这样的日期格式。这又是因为使用XML标签来格式化所致使的问题。原本若是能使用一个XML属性的值来排版,就能解决这个问题。在Play中,因为传递信息参数的语法不同,你不会遇到这样的问题。你能够:
<span>&{'views.tags.display.author', _post.author.fullname, comment.postedAt.format('yyyy-MM-dd')}"}</span>
你也能够这样写:
<span>&{'views.tags.display.author', _post.author.fullname, comment.postedAt.format(messages.get('views.dateFormat'))}"}</span>
第三种状况发生在你想把一个特定信息做为超连接的时候。在JSF,这是一个问题。由于超连接是一个JSF组件,意味着它的标记不能放在信息文件中。Play,正好相反,容许你在模板中使用原生HTML,因此你能够把用于URL的参数直接放入带信息的标记中:
logIn = <a href="%s">Log in</a> to write something &{'logIn', '/admin'}
咱们应用就曾用<a href="@{Admin.index()}">
这样的语法会让框架生成基于路由文件的URL。要想在本地化的过程当中处理它,使用:
&{'logIn'}, actionBridge.Admin.index()}
来看下咱们给“Yet Another Blog Engine”进行本地化(汉化)的结果。
其实没有汉化彻底,对吧……(╯▽╰)