Mryqu's Notes


  • 首页

  • 搜索
close

[Golang] Go项目的国际化

时间: 2017-10-21   |   分类: Golang     |   阅读: 548 字 ~3分钟

因为我看的那些书里都没有提及Go项目的国际化实现,但在放狗搜索前,还是觉得跟Java/JS项目中的实践应该都差不多。搜了一下才发现自己Too young too simple! 在Internationalization plan for Go中提到了Golang曾经的I18N路线图:

  • 对国际化文本提供全面支持
  • 对国际化日期、时间等提供支持
  • 对多语言消息提供支持

这个2011年的讨论有人提及Golang已经支持UTF8/unicode,日期时间自己可以格式化,其他人纷纷表示不同意他的观点,然后就没有然后了。 Golang的标准库还有提供完整的I18N支持,所以还需要对众多的Golang I18N库进行技术选型。通过https://golanglibs.com/search?q=i18n可知go-i18n排名第一,而且使用者也比较多。i18n4go为大厂IBM的cloud CTO出品,从Cloud Foundary CLI中提取出来的,估计能遇到的坎都解决掉了,但是排名并不靠前。 最终决定开始我的go-18n学习之旅。

通过go-bindata嵌入i18n properties文件

具体细节见之前的博文Go程序内嵌I18N properties文件。

资源文件

i18n/resources/locale.properties

mryqu.hello=Welcome to Golang world!
mryqu.intro=This is a go-bindata-i18n example.
mryqu.verinfo={{.Cli}} Version {{.Version}} (Build: {{.Build}})

i18n/resources/locale_en.properties

mryqu.hello=Welcome to Golang world!
mryqu.intro=This is a go-bindata-i18n example.
mryqu.verinfo={{.Cli}} Version {{.Version}} (Build: {{.Build}})

i18n/resources/locale_zh-CN.properties

mryqu.hello=欢迎来到Golang世界!
mryqu.intro=这是一个go-bindata-i18n示例。
mryqu.verinfo={{.Cli}} 版本 {{.Version}} (构建:{{.Build}})

其他源码

i18n/i18n.go

与之前的博文Go程序内嵌I18N properties文件相比,这里添加了下面内容:

  1. 使用github.com/magiconair/properties包获取properties文件内的键值对;
  2. 通过github.com/nicksnyder/go-i18n/i18n包判断properties文件语言、为文件内所有键值对创建新的翻译。(如果不用properties文件,而是json或TOML格式文件,i18n.LoadTranslationFile就可以直接完成这些事情了,见https://github.com/nicksnyder/go-i18n/blob/master/i18n/bundle/bundle.go。)

go-i18n的优点:

  • 实现了CLDR plural rules。
  • 使用text/template处理带有变量的字符串。
  • 翻译文件可以是简单的JSON、TOML和YAML。
package i18n

//go:generate go-bindata -pkg i18n -o resources.go resources/

import (
  "fmt"
  "github.com/magiconair/properties"
  "github.com/nicksnyder/go-i18n/i18n"
  "github.com/nicksnyder/go-i18n/i18n/language"
  "github.com/nicksnyder/go-i18n/i18n/translation"
  "strings"
)

const (
  DefaultLocale = "en"
  AssetSuffix    = ".properties"
  FileNamePrefix = "locale_"
)

var locale = DefaultLocale
var tDefault i18n.TranslateFunc = i18n.IdentityTfunc()
var T i18n.TranslateFunc

func init() {
  assets, err := AssetDir("resources")
  if err != nil {
    fmt.Println("Unable to read required assets:"+err.Error())
  }
  for _, asset := range assets {
    flName := strings.TrimSuffix(asset, AssetSuffix)
    assetLocale := strings.TrimPrefix(flName, FileNamePrefix)
    if "" == assetLocale {
      assetLocale = "en"
    }
    transFile := string(MustAsset("resources/" + asset))

    p, err := properties.Load([]byte(transFile), properties.UTF8)
    if err != nil {
      fmt.Printf("ERROR: during properties processing - %+v\n", err)
    }

    tranDocuments := []map[string]interface{}{}
    for _, k := range p.Keys() {
      tranDocuments = append(tranDocuments,
        map[string]interface{}{
          "id":          k,
          "translation": p.GetString(k, k),
        })
    }

    languages := language.Parse(assetLocale)
    for _, lang:= range languages {
      for _, trans := range tranDocuments {
        // TODO error handling
        t, err := translation.NewTranslation(trans)
        if err != nil {
          fmt.Printf("ERROR: during translation processing - %+v\n", err)
        }
        i18n.AddTranslation(lang, t)
      }
    }

    tDefault, _ = i18n.Tfunc(DefaultLocale)
    T = tDefault

  }
}

func SetLocale(l string) {
  locale = l
  T = TfuncWithFallback(l)
}

func TfuncWithFallback(l string) i18n.TranslateFunc {
  t, err := i18n.Tfunc(l)
  if err != nil {
    fmt.Println("failed to set i18n.Tfunc with "+l)
  }
  return func(translationID string, args ...interface{}) string {
    if translated := t(translationID, args...); translated != translationID {
      return translated
    }

    return tDefault(translationID, args...)
  }
}

main.go

package main

import (    
  "flag"
  "fmt"  
  "os"
  "hello-bindata-i18n/i18n"
)

var (
  Cli     string = "hello-bindata-i18n"
  Version string
  Build   string
)

func main() {
  if len(os.Args) > 1 {
    version := flag.Bool("version", false,
      "print version information and exit")
    locale := flag.String("locale", "en",
      "locale info")
    flag.Parse()

    if locale != nil {
      i18n.SetLocale(*locale)
    }

    if *version {
      fmt.Println(i18n.T("mryqu.verinfo",
        map[string]interface{}{"Cli": Cli, "Version": Version, "Build": Build}))
      os.Exit(0)
    }
  }

  fmt.Println(i18n.T("mryqu.hello"))
}

Makefile

更多细节见之前的博文使用Makefile实现Go项目的跨平台构建 。这里在setup规则里增加了glide update,增加了generate规则。

BUILD_ENV := CGO_ENABLED=0
BUILD=`date +%FT%T%z`
LDFLAGS=-ldflags "-w -s -X main.Version=${VERSION} -X main.Build=${BUILD}"

TARGET_EXEC := helloi18n

.PHONY: all clean setup generate build-linux build-osx build-windows

all: clean setup generate build-linux build-osx build-windows

clean:
        rm -rf build

setup:
        mkdir -p build/linux
        mkdir -p build/osx
        mkdir -p build/windows
        - ${BUILD_ENV} glide update

generate:
        go generate -x ./i18n

build-linux: setup
        ${BUILD_ENV} GOARCH=amd64 GOOS=linux go build ${LDFLAGS} -o build/linux/${TARGET_EXEC}

build-osx: setup
        ${BUILD_ENV} GOARCH=amd64 GOOS=darwin go build ${LDFLAGS} -o build/osx/${TARGET_EXEC}

build-windows: setup
        ${BUILD_ENV} GOARCH=amd64 GOOS=windows go build ${LDFLAGS} -o build/windows/${TARGET_EXEC}.exe

测试

  $ build/windows/helloi18n.exe
  Welcome to Golang world!

  $ build/windows/helloi18n.exe -version
  hello-bindata-i18n Version 1.2.3 (Build: 2017-10-21T20:45:26+0800)

  $ build/windows/helloi18n.exe -locale zh-CN
  欢迎来到Golang世界!

  $ build/windows/helloi18n.exe -locale zh-CN -version
  hello-bindata-i18n 版本 1.2.3 (构建:2017-10-21T20:45:26+0800)

  $ build/windows/helloi18n.exe -locale zh
  欢迎来到Golang世界!

  $ build/windows/helloi18n.exe -locale zh -version
  hello-bindata-i18n 版本 1.2.3 (构建:2017-10-21T20:45:26+0800)

  $ build/windows/helloi18n.exe -locale fr
  failed to set i18n.Tfunc with fr
  Welcome to Golang world!

参考


GitHub:nicksnyder/go-i18n
GitHub:jteeuwen/go-bindata
Go 内嵌静态资源
The Go Blog - Generating code
Go Web 编程 - 10 国际化和本地化
I18n strategies for Go with App Engine?
Internationalization plan for Go
GNU gettext utilities

标题:[Golang] Go项目的国际化
作者:mryqu
声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!

#golang# #i18n# #go-bindata# #properties# #go-i18n#
[Golang] 使用dep
[Golang] GOPATH和包导入
  • 文章目录
  • 站点概览

Programmer & Architect

662 日志
27 分类
1472 标签
GitHub Twitter FB Page
  • 通过go-bindata嵌入i18n properties文件
    • 资源文件
  • 其他源码
    • i18n/i18n.go
    • main.go
    • Makefile
  • 测试
  • 参考
© 2009 - 2023 Mryqu's Notes
Powered by - Hugo v0.120.4
Theme by - NexT
0%