Go 程序的国际化

时间:2024-03-16 10:06:53

Go 程序的国际化

概述

Go 是静态编译的编程语言,最近很受欢迎,因为它简单、性能好而且非常适合开发云端应用。它有强大的能够 处理国际化(i18n)和本地化(l10n)的库,比如处理字符编码、文本转换还有特定地区的文本,但这个包的 文档写得不够好。让我们来看看该怎么使用这个库,并且让我们的 Go 程序能适应不同区域。

上面说的包是 golang.org/x/text ,如果用得好,在你想让应用全球化时能帮上大忙。此包带有一系列 抽象,让你翻译消息(message)、格式化、处理单复数,还有 Unicode 等更简单。

本文包大致了解 golang.org/x/text 这个包,看它提供了什么用来格式化和本地化的工具。
本文主要内容包含两部分
1.硬编码方式
2.文件加载本地化

  • 消息(message)是某些想传达给用户的的内容。每条消息根据键(key)进行区分,这可以有很多形式。 比如可以这样创建一条消息:
p := message.NewPrinter(language.BritishEnglish)
p.Printf("There are %v flowers in our garden.", 1500)

当调用 NewPrinter 函数时,你要提供 语言标签 (language tag)。想指定语言时,就使用这些 语言标签 。有多种创建标签的方式,如下文内容。

语言简写

通过组合 Tag、Base、Script、Region、Variant, []Variant, Extension, []Extension 或 error。比如:

	fmt.Println("国家语言简写格式-------------------")

	//语言类型构建
	zh, _ := language.ParseBase("zh") // 语言
	CN, _ := language.ParseRegion("CN") // 地区
	zhLngTag, _ := language.Compose(zh, CN)
	fmt.Println(zhLngTag) // 打印 zh-CN
	fmt.Println(language.Chinese)// 打印中文缩写
	fmt.Println(language.SimplifiedChinese)// 打印中文缩写
	fmt.Println(language.TraditionalChinese)// 打印中文缩写
	fmt.Println(language.AmericanEnglish)// 打印英文缩写

如果你给了一个无效的字符串,会得到一个 Und,意思是未定义的语言标签。

fmt.Println(language.Compose(language.ParseRegion("AL"))) 
// 打印 Und-AL

更多语言类型资料,请参看
https://godoc.org/golang.org/x/text/language#Tag

中文简写的一些扩展知识

zh-CHS 是单纯的简体中文。
zh-CHT 是单纯的繁体中文。

zh-Hans和zh-CHS相同相对应。
zh-Hant和zh-CHT相同相对应。

以上时zh-CHS/zh-Hans 和 zh-CHT/zh-Hant的关系。

然后是
zh-CN 简体中文,*
zh-HK 繁体中文,香港特别行政区
zh-MO 繁体中文,澳门特别行政区
zh-SG 繁体中文,新加坡-
zh-SG 简体中文,新加坡
zh-TW 繁体中文,*

硬编码

这是使用解析包常用api的基础知识点,通过这里的样例我们可以知道大致的解析过程是怎么样的,方便后面更好的集成接口和服务。

package main
func init() {
	message.SetString(language.Chinese, "%s went to %s.", "%s去了%s。")
	message.SetString(language.AmericanEnglish, "%s went to %s.", "%s is in %s.")

	message.SetString(language.Chinese, "%s has been stolen.", "%s被偷走了。")
	message.SetString(language.AmericanEnglish, "%s has been stolen.", "%s has been stolen.")

	message.SetString(language.Chinese, "HOW_ARE_U", "%s 你好吗?")
	message.SetString(language.AmericanEnglish, "HOW_ARE_U", "%s How are you?")
}

func main() {
	// 中文版
	p := message.NewPrinter(language.Chinese)
	p.Printf("%s went to %s.", "彼得", "英格兰")
	fmt.Println()
	p.Printf("%s has been stolen.", "宝石")
	fmt.Println()
	p.Printf("HOW_ARE_U", "竹子")
	fmt.Println()


	// 英文版本
	p = message.NewPrinter(language.AmericanEnglish)
	p.Printf("%s went to %s.", "Peter", "England")
	fmt.Println()
	p.Printf("%s has been stolen.", "The Gem")
	fmt.Println()
	p.Printf("HOW_ARE_U", "bamboo")
	fmt.Println()
	}

预判断出来-处理单复数

在一些情况下,你需要根据词语的单、复数而添加多个翻译后的字符串,并且要在翻译集里 加上专门的调用才能管理好单复数的情况。子包 golang.org/x/text/feature/plural 里有个 叫 SelectF 的函数用来处理文本里语法的复数形式。

下面我给个典型的 SelectF 使用案例:

package main
func init() {
	
	//根据参数处理不同的返回语句
	message.Set(language.English, "APP_COUNT",
		plural.Selectf(1, "%d",
			"=1", "I have an apple",
			"=2", "I have two apples",
			"other", "I have %[1]d apples",
		))
}

func main() {
	fmt.Println("placehold中的条件判断-------------------")
	// 条件判断
	p.Printf("APP_COUNT", 1)
	fmt.Println()
	p.Printf("APP_COUNT", 2)
	p.Println()
}

(译者注:由于中文词语 “苹果”、“天” 既表示单数又能表示复数,而对应的英文单词是分单、复数的, 用 “汉译英” 的方式更易向读者说明这段代码的功能。因而将 原文 程序中的英文改成了中文, 希腊文换成了英文。下同。)

这里例子里,SelectF 能识别和支持几个量词,比如 zero 、 one 、 two 、 few 和 many , 此外还能匹配比较符如 >x 或 <x 。

插补字符串到消息中

还有一些情况下,你想进一步处理消息中的量词,你可以用占位符变量(placeholder variables)来 处理一些特定的语法特性。比如说,在前面我们使用复数的例子,可以改写成这样:

func init() {
    message.Set(language.English, "你迟了 %d 分钟。",
        catalog.Var("m", plural.Selectf(1, "%d",
            "one", "minute",
            "other", "minutes")),
        catalog.String("You are %[1]d ${m} late."))
}

func main() {
	p := message.NewPrinter(language.English)
	p.Printf("你迟了 %d 分钟。", 1)
        // 打印: You are 1 minute late.
	fmt.Println()
	p.Printf("你迟了 %d 分钟。", 10)
        // 打印: You are 10 minutes late.
	fmt.Println()
}

这里 catalog.Var 函数的第一个参数为字符串,即 m ,是一个特殊的标签,根据 %d 参数的值, 这个标签会被一个更准确的翻译内容替换。

格式化货币

golang.org/x/text/currency 处理货币格式化。

有几个有用的函数可以打印出指定地区货币的金额。下面的例子显示了几种格式化的方式:

package main

import (
	"fmt"

	"golang.org/x/text/currency"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
)

func main() {
	p := message.NewPrinter(language.English)
	p.Printf("%d", currency.Symbol(currency.USD.Amount(0.1)))
	fmt.Println()
	p.Printf("%d", currency.NarrowSymbol(currency.JPY.Amount(1.6)))
	fmt.Println()
	p.Printf("%d", currency.ISO.Kind(currency.Cash)(currency.EUR.Amount(12.255)))
	fmt.Println()
}

结果会是:

➜ go run main.go
$ 0.10
¥ 2
EUR 12.26

加载消息

当你处理本地化工作时,通常你需要事先准备好译文以便应用程序能用上它们。你可以把这些译文文件 当作静态资源。有几种部署程序和译文文件的方式:

手动设置译文字符串
最简单的方式是把译文构建到你的应用程序里。你必须手动创建一个数组,包含原文和译文的条目, 在初始化时,那些消息会被加载到默认翻译集中。在你的应用里,你只能用 NewPrinter 函数切换区域。

下面这个应用,示范了如何在初始化时加载译文:

package main
func init() {
// 以上代码和以下代码都是硬编码方式
	for _, e := range msaArry {
		tag := language.MustParse(e.tag)
		switch msg := e.msg.(type) {
		case string:
			message.SetString(tag, e.key, msg)
		case catalog.Message:
			message.Set(tag, e.key, msg)
		case []catalog.Message:
			message.Set(tag, e.key, msg...)
		}
	}
}

func main() {

//调用硬编码中的消息 国际化
	p = message.NewPrinter(language.English)
	p.Printf("HELLO_WORLD","bamboo")
	p.Println()
	p.Printf("TASK_REM", 2)
	p.Println()

}

//手工 硬编码方式
var msaArry = [...]langMsg{
	{"en", "HELLO_WORLD", "%s Hello World"},
	{"zh", "HELLO_WORLD", "%s 你好世界"},
	{"en", "TASK_REM", plural.Selectf(1, "%d",
		"=1", "One task remaining!",
		"=2", "Two tasks remaining!",
		"other", "[1]d tasks remaining!",
	)},
	{"zh", "TASK_REM", plural.Selectf(1, "%d",
		"=1", "剩余一项任务!",
		"=2", "剩余两项任务!",
		"other", "剩余 [1]d 项任务!",
	)},
}

文件加载本地化

一直以来,大多数本地化的框架都会把每个语言的译文分别存于文件里,这些文件会被动态加载。你可以把 这些文件交给翻译的人,在他们搞定后,你再把译文合并到你的应用中。
为了协助这个过程,Go 作者们开发了一个命令行小工具叫 gotext
安装:go get -u golang.org/x/text/cmd/gotext
但是本人参考该资料安装并没有成功,因此另想办法处理了,该内容部分属于个人学习创新的部分
思路如下:1.翻译的样本,2.整理成各个语言格式的json字符串文件,3.加载相关语言文件到系统中,4.根据当前语言解析出语句

本地化语言json文本文件如下

|locales
	|-el
		|--messages_en.json
		|--messages_zh.json

详细json内容请参考我的git地址,包含本文和相关学习的所有源码
https://github.com/BambooZhang/go-study/tree/master/go-base

源码如下


func main() {
	fmt.Println("从配置文件中读取配置json解析并加载到系统中-------------------")
	InitConfig("go-base/locales/el/messages_zh.json")//加载配置信息json
	InitConfig("go-base/locales/el/messages_en.json")//加载配置信息json
	p = message.NewPrinter(language.SimplifiedChinese)//设置语言类型
	p.Printf("HELLO_1", "Peter")
	fmt.Println()

	p.Printf("VISITOR", "Peter","用户管理系统接口")
	fmt.Println()

	p = message.NewPrinter(language.AmericanEnglish)//设置语言类型
	p.Printf("VISITOR", "Peter","UER MANAGE SYSTEM API")
	fmt.Println()


	msg := Message{"en", "HELLO_WORLD", "%s Hello World"}
	p.Printf("CONFIG", msg) //传一个对象值进去
	fmt.Println()


}

从配置文件中读取想关文件后,json解析出对应的message信息加载到系统中,需要使用时选择好语言,直接调用既返回对应的语言信息

学习资料地址

详细内容请参考我的git地址,包含本文和相关学习的所有源码
https://github.com/BambooZhang/go-study/tree/master/go-base

本文参考资料

[译] 手把手教你 Go 程序的国际化和本土化
https://www.colabug.com/3411106.html