在上一篇文章 在 Golang 中如何做国际化? 中我提到了 “Go 目前没有一个好的检测运行环境语言的库”,go-locale 就是为了解决这个问题而诞生的。

一些事实

现实的情况是,我们没有一个标准的方法可以获取到当前代码运行环境的语言情况。

对于大部分 Linux 发行版而言,我们可以检查 LANG 或者 LANGUAGE 环境变量,但是不同发行版有不同的习惯,而每个人还有自己的配置,比如我在 Archlinux 上就只有 LANG=en_US.UTF-8 ,其他的语言相关环境变量都没有。macOS 上更加复杂,传统上我们将它看作类 Unix 操作系统,沿用环境变量或者直接调用 locale 来检查,但是这也是不一定的。Windows 更不必说了,环境变量这一套不好使,需要检查注册表或者调用 Win32 API。

所以想要解决这些问题,我们需要为不同的平台实现不同的语言检测逻辑,但是现在这些任务可以交给 go-locale 来做了。

介绍

go-locale 的目标是跨平台的语言检测库,它会使用各个平台的不同方式来尽力探测出当前使用的语言,目前已经支持了 LinuxmacOSWindows 三大主流平台。

import (
    "github.com/Xuanwo/go-locale"
)

func main() {
	tag, err := locale.Detect()
    if err != nil {
        log.Fatal(err)
    }
    // Have fun with language.Tag!
}

内部实现

go-locale 使用条件编译机制,对外暴露一个统一的 Detect ,内部按照平台不同实现不一样的 detect 方法,在 detect 内会尝试该平台下已知可用的多种不同方式,所有方式都检测不到的情况下才会返回 ErrNotDetected

Linux

在 Linux 平台下首先会检查环境变量,检查顺序如下:

  • LANGUAGE 兼容 gettext 的行为
  • LC_ALL 总是会覆盖其他的所有 LC_* 配置
  • LC_MESSAGES 是所有消息的 locale 配置
  • LANG 是所有 LC_* 配置的 fallback 值

如果环境变量全部检查失败,会尝试调用 locale 命令来获取语言。

macOS

在 macOS 下首先会进行全部的 Linux 系统兼容的检查,如果没有获取到,则会使用系统提供的 User Defaults System ,通过 defaults read NSGlobalDomain AppleLanguages 来获取语言。

Windows

Windows 下则会使用 Win32 OLE API 来获取。

已知问题

虚高的 Code Coverage

目前项目显示的代码测试覆盖率为 100%,但是这只是在 Linux 平台下测试的结果。目前 Codecov 不支持汇总多个平台下的整体结果,所以选择了只提交最好看的一个平台。

此外,在 Linux 的单元测试中大量的进行了 mock 和 monkey patch 来模拟行为,但是由于我本人 Windows 和 macOS 使用经验较少,并不确定他们的行为是否真的是这样,所以也没有写类似的单元测试。

根据在 Travis CI 上测试的结果,Windows 和 macOS 上的实现是 work 的,但是我不排除在特定版本和配置下会出问题的可能,如果遇到的话欢迎提交 Issue 反馈。

只返回一个语言

设计时的目标是返回最有可能的那个语言,但是很多人会设置多种可选语言,比如 zh_CN:zh_TW:en_US,如果软件不支持中文的话使用英文也可以。所以这个计划在之后会返回成按照 fallback 顺序来排列的语言数组。