【Wubade】go-i18n/v2的操作示例(2021年03月版)【国际化支持】
国际化简称 i18n
我想要一个能够运行的 go-i18n v2.x 示例。而且需要在 Go v1.15 或以上版本上经过测试。
在 GitHub 上,可以找到 github.com/nicksnyder/go-i18n
由于我对 TOML 没有太大兴趣,所以更喜欢使用 JSON。
2021年3月7日,目前的 go-i18n 版本是 v2.1.1,但即使看官方的示例,我认为它似乎更适合“熟悉旧版本(v1.x)的人”,因为前提条件有些混乱不清。
为了展示 “Golang go-i18n v2 动作示例”,即使在搜索 “Golang go-i18n v2 动作示例” 时出现了很多旧版本的 go-i18n v1.x 或无法在 Go v1.15 以上版本中运行或由于拼写错误而无法工作的示例,这是作为自己搜索的一个参考样本。
实际上,通过阅读 golang.org/x/text 的文档中的 Translation 部分,我们可以使用这个本地化功能和 catalog.Builder 类来创建自己的 catalog(对应字典),并且通过 plural.Selectf() 函数还能应对复数形式。但不过,这个文档也不是很清楚如何使用ʕ◔ϖ◔ʔ
总之,本文主要关注 go-i18n,一旦我们掌握了利用 golang.org/x/text 进行国际化的方法,则会在另一篇文章中进行介绍。如果收到 10 个以上的”赞”,我们会督促自己加快进度。
太长不看(今北产百业)
Bundle が辞書を保持するもの。
Localizer が辞書を引くもの。
入力(メッセージ ID や設定)から、対応するメッセージを辞書から引っ張ってくる関数が Localize() です。MustLocalize() のように Must* が付く関数は、エラー発生時にエラーを返さずパニクるタイプです。
go-i18n のバージョンによって Bundle やLocalizer に渡す言語指定方法が異なる。
この記事は go-i18n v2.1 のサンプル。
とりま、動くサンプルをオンラインでみる @ Go Playground
在指定语言时,使用的 en-US 和 ja-JP 被称为标签(语言标签),通常指的是 “IETF 语言标签”。
IETF 语言标签是由互联网标准化组织 IETF 在 RFC 5646 和 RFC 4647 中制定的语言标签。但习惯上也使用了 BCP47 等说法。BCP 是指在制定前被认为是“当前最佳”的 BCP(最佳现行实践)中的第47号“语言标签指定最佳实践”,因此只是保留了当时的习惯。如果要参考,请使用 RFC。
另外,IETF 语言标签采用的是类似 “en-US” 和 “ja-JP” 这样的连字符(横线)结构,所以在使用操作系统的区域设置时,需要注意与下划线(underscore)的记法如 “ja_JP” 或 “ja_JP.UTF-8” 的差异。 ←(我
老师,请给我一个在 Go v1.15 及以上版本可运行的东西。
嗯?国际化?
我认为当被告知“需要进行国际化适应”时,尽管我使用英语开发了应用程序并吸引了日本人的需求,但我会感到一种不愉快的味道。
但是即使说“国际化应对”,
-
- 为每种语言准备术语字典。
-
- 通过操作系统的Locale设置,在运行环境中选择使用的语言字典。
-
- 指定术语的识别符(消息ID),将返回相应术语的指定语言字典。
- 然后将其打印出来。
只不过如此罢了。
总的来说,可以将其类比为类似于关联数组的东西。可以这样考虑:为每种语言准备一个数组(映射),当指定相应语言数组的键时,可以获得相应的值。
以下是用map(Go的关联数组)简单实现的。
以下是只需一种选择的原生中文释义:这里以map(Go的关联数组)简单实现为例。
en := map[string]string{
"helloworld": "hello, world",
}
ja := map[string]string{
"helloworld": "こんにちは, 世界",
}
fmt.Println(en["helloworld"])
fmt.Println(ja["helloworld"])
// Output:
// hello, world
// こんにちは, 世界
オンラインで動作をみる @ Go Playground
国际化和本地化的区别
当谈到将应用程序进行多语言支持时,涉及到的术语通常是国际化和本地化。
国际化的日语翻译是“Internationalization”,本土化的日语翻译是“Localization”。在应用中,这两个都是“可用其他语言显示的功能”。他们有什么不同呢?
如果将”多言语表示支持”改为”国际化支持”,基本上是相同的意思。在这种情况下,Internationalization表示准备国际化支持,而Localization表示应用国际化支持。
换句话说,Internationalization 是指“准备(实现功能)以在其他语言环境下显示”,而 Localization 是指“将英文显示适配到日文显示等地区特定优化(语言实现)”。
由于国际化工作更具通用性,因此也被称为“全球化”。将“国际化”称为“全球化”,只是为了更好地表达“能够更全面(通用)地适应全球”的意思而已。
举个例子,在重构中,国际化的意思是“尽管显示方式保持原样,但只要准备好词典,就能随时切换显示语言”,而创建特定语言的词典和确认显示方式则称为本地化。
在不需要IME的ASCII码领域(尤其是英语区域)的代码库中,可能会发生国际化问题的骚动。在这种情况下,使其支持UTF-8等就是国际化,而对日语输入法进行支持则称为本地化。
要推动国际化和地域化,我们可以使用关联数组来实现”我国际化对应”,就像之前的例子一样。但我们也可以利用专用的库来节省时间,将空闲时间用于创建词典。
Go语言的国际化套件
Linux上有一个老牌的C语言库叫做GNU Gettext。对于PHP(尤其是WordPress)用户来说,我想你可能见过翻译函数”_()”,那就是它的起源。
在 Go 语言中,有一个著名的 go-i18n 包,用于实现国际化(internationalization)功能。
go-i18n パッケージ
github.com/nicksnyder/go-i18n @ GitHub
只需要将它作为模块嵌入,并将文档输出的部分替换为翻译函数,就可以简单地通过准备词典来准备其他语言的支持。
然而,这个包装上的官方示范很难理解。
因此,我認為直接看到一個實際運作的簡單示例可能更為快速,所以,請先參考以下的源代碼。
使用 JSON 格式定义并使用固定消息,例如帮助信息。
package main
import (
"encoding/json"
"fmt"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
func main() {
// デフォルト言語を英語に設定
bundle := i18n.NewBundle(language.English)
// パーサーエンジンを JSON にセット
bundle.RegisterUnmarshalFunc("json", json.Unmarshal)
// JSON の辞書を登録
bundle.MustParseMessageFileBytes([]byte(`{"MSG001": "Hello World!"}`), "en.json")
bundle.MustParseMessageFileBytes([]byte(`{"MSG001": "Hola Mundo!"}`), "es.json")
bundle.MustParseMessageFileBytes([]byte(`{"MSG001": "ようこそ、世界!"}`), "ja.json")
// 辞書から引きたい内容(MSG001 を引きたい)
referTo := &i18n.LocalizeConfig{MessageID: "MSG001"}
// 辞書を引く
{
// 英語の辞書を使う
localizer := i18n.NewLocalizer(bundle, "en-US")
fmt.Println(localizer.MustLocalize(referTo))
}
{
// スペイン語の辞書を使う
localizer := i18n.NewLocalizer(bundle, "es-ES")
fmt.Println(localizer.MustLocalize(referTo))
}
{
// 日本語の辞書を使う
localizer := i18n.NewLocalizer(bundle, "ja-JP")
fmt.Println(localizer.MustLocalize(referTo))
}
{
// 未定義の辞書を使う(デフォルトの英語を使う)
localizer := i18n.NewLocalizer(bundle, "ko-KR")
fmt.Println(localizer.MustLocalize(referTo))
}
}
// Output:
// Hello World!
// Hola Mundo!
// ようこそ、世界!
// Hello World!
オンラインで動作をみる @ Go Playground
复数形 和 单数形 的变化选项
通常情况下,像上述的使用 JSON 的例子一样,我们通常认为“标题、帮助信息等简单替换就足够了”是比较常见的情况。
然而,如果说「有●条消息」,根据语言的不同,有些语言可能会不考虑单数或复数形式而显得不自然。
以下是一个根据 PluralCount 值在翻译(本土化)时使用单数形或复数形的示例。
说实话,只有一个注册术语,并且使用了结构体而不是JSON,所以你可能会觉得用”if”语句分支会更快。
然而,请注意在“仅通过消息ID进行简单替换”的限制达到时,可以给消息(术语)引入变化的可能性。
package main
import (
"fmt"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
func main() {
// 辞書セットを作成しデフォルトを英語に設定
bundle := i18n.NewBundle(language.English)
// 使う辞書の言語設定
loc := i18n.NewLocalizer(bundle, language.English.String())
// 辞書用語を設定(単数形・複数形対応)
// i18n.Message 型は Localize から呼び出された際の個数情報(PluralCount)
// によって単数(One:)、複数(Other:)でメッセージ内容が変わる。
// "One" と "Other" にある "{{.Name}}" と "{{.Count}}" は、
// Localize() から呼び出された際に渡される map のキー名。
message := &i18n.Message{
// メッセージ ID(この設定を呼び出すキー)
ID: "Emails",
// メモ
Description: "The number of unread emails a user has",
// 単数形用の構文定義(いわるゆテンプレート)
One: "{{.Name}} has {{.Count}} email.",
// 複数形用の構文定義(いわるゆテンプレート)
Other: "{{.Name}} has {{.Count}} emails.",
}
// 個数を指定(単数・複数でメッセージ内容が変わるかの確認)
emailCount := 2
// 辞書翻訳の実行
// MustLocalize と Localize は同じですが、MustLocalize はエラー時に
// パニックになります。
translation := loc.MustLocalize(&i18n.LocalizeConfig{
// デフォルトで使う辞書用語を定義
DefaultMessage: message,
// 辞書の用語(のテンプレート)に流し込むデータ。
// "Name", "Count" を持つ辞書登録(用語)すべてに "KEINOS" と個数
// をテンプレートとしてマッピング(割り当てを)する。
TemplateData: map[string]interface{}{
"Name": "KEINOS",
"Count": emailCount,
},
// データの個数情報
PluralCount: emailCount,
})
fmt.Println(translation)
}
// Output: KEINOS has 2 emails.
オンラインで動作をみる @ Go Playground(Go version: 1.16)
相關文獻
【Golang】JSON などの外部ファイルをバイナリに埋め込むサンプル【Go 1.16+】 @ Qiita