您的位置:晶晶的博客>GoLang>分享一个golang国际化多语言方案:i18n-stringer

分享一个golang国际化多语言方案:i18n-stringer

i18n是英文单词internationalization的首末字符i和n,18为中间的字符数,是“国际化”的简称,类似规则产生的简称还有k8s

开门见山,这篇文章主要是灌水,仓库里有详细的使用说明和介绍。仓库地址:https://github.com/jjonline/i18n-stringer

一、go实用哲学

下面罗列一些go语言的伪哲学思想,山非山水非水,最终山还是山水还是水,颇有一股一本正经胡说八道的意味。

一切皆接口

长得像鸭子像鸭子一样呱呱叫的就是鸭子,接口interface就是鸭子规则在go语言里的体现。

当然一切皆接口真要较真理解也不是完全准确的,这个抽象的核心是理解鸭子类型(duck typing)的深层次原理和由此而带来的收益。是一种取舍折中,go官方早先提的少即是多(Less is more)或者翻译后的:大道至简,仁者见仁智者见智咯。

这里要提一笔的是:go1.18引入了any关键字,后续版本any或等价于interface或是一个超集,拭目以待。

只要error不要exception

go语言本身没有exception这种说法,当然panic你也可以理解为一种异常,但是go语言本身又并不提供try {} catch {}语法,当然你也可以配合recover实现一种并不推荐使用的伪try-catch的机制。按照语言本身的机制发生panic就是一种流程行进不下去的恐慌是需要修复的,而exception机制更多的是一种中断机制以实现流程转向的目的。

这种机制也就导致了代码段的方法、函数流程中出现了非预期的结果就需要使用error来标识。go语言内置了error类型(https://github.com/golang/go/blob/master/src/builtin/builtin.go#L270),是一个interface,只要实现了Error() string方法的就是一个error,所以可以自定义错误。

枚举常量用数值类型

这里数值类型是多个类型的集合称呼,罗列出来就是int、uint、int8、uint8等等这些。

Go语言中的常量使用关键字const定义,用于存储不会改变的数据,常量是在编译时被创建的,即使定义在函数内部也是如此,常量允许布尔型、数值型(整数型、浮点型和复数)和字符串型。当然这3种类型的派生自定义类型也是阔以的。

作为业务开发中常见的枚举值一般是推荐使用数值型,当然简单的非是即否的用布尔型也没问题,超过2个待选的枚举集合你要用字符串是可以的。

二、i18n-stringer

这个工具是因工作中的项目需要,单独定义的翻译文件可以直接丢给PM去填,是一个甩锅卷KPI的开源项目----这段是吹水。该工具参考了go官方的tool工具stringer:https://github.com/golang/tools/tree/master/cmd/stringer

基于上述3个伪哲学:我们的接口一般会使用错误码来做一些场景辨识,那错误码就是自定义数值类型的枚举值;而go没有异常只有error,一切皆接口我们的错误码同时也实现buitin.error接口也可当做错误在方法体代码段传递返回。

i18n-stringer是一个使用TOML文件配置多语言文案支持国际化多语言的自动生成代码的工具,用于自动为自定义数值类型的枚举常量的类型实现fmt.Stringerbuitin.error接口,即为自定义数值类型添加Error() stringString() string等方法,不需要手动实现这些代码,当枚举值集合被修改时也无需同时修改多处代码,一切用工具自动生成即可。go语言本身提供了工具链go generate配合源码里的//go:generate打头的注释指令,即可go generate一键执行,参考这里https://blog.jjonline.cn/golang/257.html的介绍不再赘述。

i18n-stringer不是被你的代码所依赖的包,而是要被编译安装到GOBIN目录的命令行指令程序。因使用了1.16新增的一些文件操作方法编译安装仅支持1.16及其以上的go环境,生成的代码因使用了context上下文,可用于1.7及其以上的环境。go语言对go1的兼容性保证,历史代码一般均可无缝使用新版本,建议跟随官方永远使用最新版本。

自动生成的代码方法概要

// 自动生成的代码导出方法列表
String() string
Error() string
Wrap(err error, locale string, args ..._T_) *I18nError_T_Wrap
WrapWithContext(ctx context.Context, err error, args ..._T_) *I18n_T_ErrorWrap
IsLocaleSupport(locale string) bool
Lang(ctx context.Context, args ..._T_) string
Trans(locale string, args ..._T_) string

// 自动生成的代码里还会提供一个错误包装结构体
// 结构体名称形如:I18nError_T_Wrap
// 也会提供一些导出方法列表

Translate() string
String() string
Error() string
Format() string
Value() _T_
Unwrap() error

// 以上 _T_ 指代你的数值类型的自定义类型名称

因为项目本身的README文件介绍的已经很详细,具体使用方法请参考仓库README.md介绍,下面罗列一些要点:

  1. 使用TOML文件定义多语言,多语言类型使用文件名或目录名自动生成不需要额外指定;
  2. 自动为你的自定义类型实现fmt.Stringerbuitin.error两个接口,并自动生成一个包装类型便于包装其他错误;
  3. 支持通过context.Context自动取需要语言类型标识符;
  4. 自带检查TOML文件中缺失或无效键值对功能,通过指令参数自取;

TOML语言文件取语言类型标识规则

因为定义多语言的翻译文本通过toml格式文件单独配置,识别需要支持的多语言通过目录名或文件名实现。具体而言语言包目录下的toml文件的文件名被作为语言标识符,语言包目录下的文件夹名称也会被当做语言标识符,这种惯例约定可以减少很多不必要的配置麻烦,按惯例定义toml后缀的翻译文件即可,举个栗子:

.
├── i18n
│     └── en.toml
│     ├── zh_cn.toml
│     └── zh_hk
│     │     ├── user.toml
│     │     └── merchant.toml
└── pill.go

上述目录结构中i18n目录即为定义多语言TOML文件的根目录,其下有两个toml格式的直接文件en.tomlzh_cn.toml,所以两个文件的文件名将被提取为两个语言类型标记:enzh_cn,然后还有一个文件夹zh_hk,文件夹内还有toml文件,所以该文件夹名也被提取一个语言标记:zh_hk ,所以上述目录结构定义了3种类型的语言翻译。

有些项目语言定义文件需要分模块按功能分多个toml文件来定义,所以上述zh_hk文件夹下全部用于定义zh_hk的翻译文本,该目录下的toml后缀的文件的文件名可以任意定义且文件数量不限制,该目录下也还可以有子目录且子目录名可以任意定义。关于重复:上述例子中如果还有个叫en的子目录且子目录有toml后缀格式的文件会怎样?当然是自动合并均作为en类型的翻译文本啦。

关于编译后二进制文件是否要将TOML文件包含?

先给答案:不需要!

因为定义的TOML文件仅在自动生成代码时使用,属于编译前依赖,代码生成时将这些TOML文件中定义的键值对硬编码至了代码中,业务代码调用时不需要再去读取TOML文件,所以你的业务代码编译成的二进制可执行程序不需要包含这些TOML文件。

----

注意:go语言里并没有buitin包,文中buitin.error是为了便于说明而这么写的。

----

使用示例参考:https://github.com/jjonline/i18n-stringer/tree/master/example

转载请注明本文标题和链接:《分享一个golang国际化多语言方案:i18n-stringer
分享到:

相关推荐

哟嚯,本文评论功能关闭啦~

  1. #1

    感谢分享 赞一个

    防水涂料加盟 2年前 (2022-01-25) 回复