Revel使用文本文件提供国际化翻译支持。Revel 支持语言翻译文件化, 自动区域查询, cookie重写、嵌套的消息与参数。
#### 词汇表
* Locale(语言环境): 包含 *语言* 和 *区域*两个部分,指示用户的语言偏好,例如 `en-US`。
* Language(语言): locale 的语言部分, 例如 `en`。 语言标识符请参考 [ISO 639-1 codes](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)。
* Region(区域): locale 的区域部分, 例如 `US`。 区域标识符请参考 [ISO 3166-1 alpha-2 codes](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)。
## 示例程序
Revel 处理消息文件和国际化的方法与其他框架类似。如果你想立马上手, 可以参考示例程序 `revel/samples/i18n`,里面包含所有的基础知识。
## 消息文件
国际化消息在消息文件中定义。这些消息在渲染视图模板(或程序的其他地方)时使用。当创建新的消息文件时,需要遵循几个规则:
* 所有的消息文件应存放在应用程序根目录的`messages`目录中。
* 文件扩展名与 *language* 一致,并且应为 [ISO 639-1 code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)。
* 消息文件应该是 UTF-8 编码。虽然不是强制规定, 最好还是遵循这个约定。
* 消息文件必须是 [goconfig 文件](https://github.com/revel/config) 支持多有的 goconfig 特性
### 组织消息文件
消息文件的文件名没有任何限制; 只要扩展名合法就行。每种语言的文件数量也没有限制。当应用程序启动时,Revel会解析`messages`语言目录中所有的扩展名合法的消息文件,并根据语言将它们合并。这意味着你可以自由组织你的消息文件。
例如,您可能希望采取传统的方法,在一个文件中定义所有的:
~~~
/app
/messages
messages.en
messages.fr
...
~~~
或者是为每种语言创建多个文件,并根据分类组织他们:
~~~
/app
/messages
labels.en
warnings.en
labels.fr
warnings.fr
...
~~~
**重要注意事项:** 在同一语言的多个文件中,虽然技术上可以定义相同的消息key,但是这将导致不可预知的行为。当每种语言定义了多个文件时,请注意保持您的key唯一,这样文件合并后,key将不会被覆盖!
### 消息 key 和 值
消息文件是一个 [goconfig 文件](https://github.com/revel/goconfig)。这意味着,消息应按照key-value格式定义:
~~~
key=value
~~~
举个栗子:
~~~
greeting=Hello
greeting.name=Rob
greeting.suffix=, welcome to Revel!
~~~
### 段
goconfig 文件被分成多个 *sections*. *默认的段* 总是存在, 并且包含未指定段时定义的消息。例如:
~~~
key=value
[SECTION]
key2=value2
~~~
`key=value` 消息消息被隐式放置在默认段中,因为它没有定义在一个指定的段中。
消息文件的所有消息应在定义默认的段中,除非它们是用于某个特定区域(见[Regions](http://gorevel.cn/docs/manual/i18n-messages.html#regions)获取更多消息)。
**注意:** 段是 *goconfig* 功能.
### 区域
特定区域的消息应当在相同的段中定义。Region-specific messages should be defined in sections with the same name. 例如,假设我们要对所有讲英语的用户显示 `"Hello"`, 对英国用户显示 `"Hey"` ,对讲美国用户显示 `"Howdy"`。 为了做到这一点,我们可以定义下面的消息文件 `greeting.en`:
~~~
greeting=Hello
[GB]
greeting=Hey
[US]
greeting=Howdy
~~~
对于定义了英语 (`en`) 作为他们的首选语言时,Revel 会将`greeting` 解析成 `Hello`。只有在用户的区域被明确定义的情况下,比如`en-GB` 或 `en-US` ,Revel 才会用对应段中的消息去解析 `greeting`。
**重要提示:** 如果定义了一个不合法的区域,技术上是允许的,但是它们永远不会被解析。
### 引用 和 参数
#### 引用
文件中的消息也可以引用其他消息。这使得用户可以从一个或多个其它的消息组成一个单一的消息。引用消息的语法是 `%(key)s`. 例如:
~~~
greeting=Hello
greeting.name=Rob
greeting.suffix=, welcome to Revel!
greeting.full=%(greeting)s %(greeting.name)s%(greeting.suffix)s
~~~
**注意:**
* 引用是 *goconfig* 的功能.
* 因为消息文件可以合并,多以只要它们被定义为同一种语言,就可以引用其他文件中的消息。
#### 参数
消息支持一个或多个参数。在消息中参数使用与 `fmt` 包相同的规则去解析。 例如:
~~~
greeting.name_arg=Hello %s!
~~~
参数按照给定的顺序进行解析,参考 [解析消息](http://gorevel.cn/docs/manual/i18n-messages.html#resolving_messages).
## 解析客户端语言环境(locale)
为了找出用户喜欢的语言环境,Revel会在以下地方查找一个可用的语言环境:
1. 语言 cookie
对于每次请求,框架会查找应用程序配置 (`i18n.cookie`)。如果找到了,它的值被假定为当前的语言环境。当一个cookie已经发现,所有其他解析方法将被跳过。
2. `Accept-Language` HTTP 头
对于每次请求,Revel 会自动解析 HTTP 头信息的 `Accept-Language`。其中的每个语言环境根据 [HTTP 规范](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)在当前`Request`实例中获取和存储。此信息用来为稍后的消息解析函数确定当前语言环境。
更多信息请参考 [Parsed Accept-Language HTTP header](http://gorevel.cn/docs/manual/i18n-messages.html#parsed_acceptlanguage_http_header).
3. 默认语言
当以上所有方法都没有找到可用的客户端语言环境时,框架将使用配置文件中定义的默认语言`i18n.default_language`。
如果有的消息解析失败,则返回一个包含原始消息名的特殊格式的字符串。
**注意:** `Accept-Language` 头信息 **总是** 被解析并保存到当前 `Request`中, 即使它已经存在。在这种情况下,头信息中的值即使从未被消息解析功能使用,但是仍可以在需要他们的时候使用。
### 检查当前语言环境
应用程序可以从`Request`内部使用 `Request.Locale` 属性访问当前语言环境。例如:
~~~
func (c App) Index() revel.Result {
currentLocale := c.Request.Locale
c.Render(currentLocale)
}
~~~
在模板中, 就可以从 `renderArgs` 中的 `currentLocale` 变量中访问当前语言环境。 例如:
~~~
<p>Current preferred locale: {{.currentLocale}}</p>
~~~
### 解析 Accept-Language HTTP 头信息
如果程序需要访问 `Accept-Language` HTTP 头信息, 可以从 `Controller` 实例的 `Request`中获取。 `AcceptLanguages` 字段(`AcceptLanguage`实例的切片)包含所有从各自的头信息中解析的值, 最合适的值是切片中的第一个。例如:
~~~
func (c App) Index() revel.Result {
// 获取所有解析到的AcceptLanguage的字符串表示形式
c.RenderArgs["acceptLanguageHeaderParsed"] = c.Request.AcceptLanguages.String()
// 返回最合适AcceptLanguage实例
c.RenderArgs["acceptLanguageHeaderMostQualified"] = c.Request.AcceptLanguages[0]
c.Render()
}
~~~
更多信息请参考 [HTTP 规范](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4).
## 解析消息
消息可以在 *控制器* 或 *模板*中解析。
### 控制器
控制器中使用 `Message(message string, args ...interface{})` 函数使用当前语言环境解析消息:
~~~
func (c App) Index() revel.Result {
c.RenderArgs["controllerGreeting"] = c.Message("greeting")
c.Render()
}
~~~
### 模板
在模板中使用 *模板函数* `msg` 解析当前语言环境的消息。例如:
~~~
<p>Greetings without arguments: {{msg . "greeting"}}</p>
<p>Greetings: {{msg . "greeting.full.name" "Tommy Lee Jones"}}</p>
~~~
**注意:** `msg` 函数的签名是 `msg . "message name" "argument" "argument"`. 如果没有参数, 可以忽略。
## 配置
| 文件 | 选项 | 描述 |
| --- | --- | --- |
| `app.conf` | `i18n.cookie` | 语言cookie的名称。应使用Revel cookie的前缀,以避免cookie名称冲突。 |
| `app.conf` | `i18n.default_language` | 默认的语言环境,在没有首选区域的情况下使用。 |