# go-locale 介绍

在上一篇文章 [在 Golang 中如何做国际化？](https://xuanwo.io/2019/12/11/golang-i18n/) 中我提到了 “Go 目前没有一个好的检测运行环境语言的库”，`go-locale` 就是为了解决这个问题而诞生的。

## 一些事实

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

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

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

## 介绍

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

```go
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 顺序来排列的语言数组。

