目录

Go语言项目如何实现多语言切换

项目多语言现在越来越是趋势了,那么 Go 如何实现多语言切换呢?本文将记录分享使用第三方库go-i18n实现多语言切换功能,希望能够帮助开发者少走弯路。

一、技术选型

Go生态里多语言方案不少,比如自研配置解析、go-localize等,但综合下来go-i18n是最优解:

  • 工具链完整:提供命令行工具自动生成翻译模板、合并翻译文件,不用手动写XML/JSON结构;
  • 支持丰富:兼容复数、性别、占位符等复杂场景,比如英文中"1 apple"和"2 apples"的复数变化;
  • 性能优秀:翻译内容加载后缓存到内存,读取效率高,不会拖慢业务接口;
  • 生态适配好:能无缝对接Gin、Beego等主流Web框架,也支持CLI工具多语言。

本文将以go-i18n v2版本为核心,结合最常用的Gin框架演示,其他框架思路完全通用。

二、前期准备

这一块就不需要多说了,安装 go-i18n

# 安装go-i18n命令行工具
go install github.com/go-i18n/go-i18n/v2/goi18n@latest
go install github.com/go-i18n/go-i18n/v2/goi18n/cmd/goi18n@latest

# 验证安装成功出现版本号即正常
goi18n -v

初始化项目与依赖

新建一个测试项目,执行go mod初始化,然后安装核心库:

# 新建项目目录并进入
mkdir go-multilingual-demo && cd go-multilingual-demo

# 初始化go mod替换为自己的模块名
go mod init github.com/your-name/go-multilingual-demo

# 安装go-i18n核心库
go get github.com/go-i18n/go-i18n/v2/i18n
go get golang.org/x/text/language

接着创建翻译文件存放目录,约定俗成用locales文件夹,结构如下(后续会自动生成部分文件):

locales/
├── active.en.toml  # 英文翻译文件(自动生成)
├── active.zh.toml  # 中文翻译文件(自动生成)
├── active.ja.toml  # 日文翻译文件(自动生成)
└── templates/
    └── active.en.toml  # 翻译模板文件(手动创建或自动生成)

三、代码实战

这部分是重点,分四步完成:创建翻译模板、生成翻译文件、初始化i18n、业务代码中使用。

以支持中(zh)、英(en)、日(ja)三种语言为例。

(一)创建翻译模板

翻译模板是所有语言翻译的基础,用toml格式编写,放在locales/templates目录下,命名为active.en.toml(以英文为基准模板):

# locales/templates/active.en.toml
[hello]
descript = "问候语"
other = "Hello {{.Name}}"

[welcome]
descript = "欢迎语"
other = "Welcome to {{.Project}}! Your account has {{.Count}} messages."

[message_count]
descript = "消息数量提示(支持复数)"
one = "You have {{.Count}} message."
other = "You have {{.Count}} messages."

[error_login]
descript = "登录错误提示"
other = "Login failed: {{.Reason}}"

[btn_submit]
descript = "提交按钮文本"
other = "Submit"

[btn_cancel]
descript = "取消按钮文本"
other = "Cancel"

模板文件关键说明:

  • 每个翻译项用方括号[key]标识,比如hello、welcome,业务代码中通过key获取翻译;
  • descript是翻译项的说明,方便翻译人员理解场景,不影响程序运行;
  • other是默认翻译文本,若需要复数形式,可添加one(单数)、zero(零)等字段,比如message_count项;
  • {{.XXX}}是占位符,用于动态填充内容,比如用户名、消息数量。

(二)生成翻译文件

通过goi18n命令生成其他语言的翻译文件,步骤如下:

# 1. 生成语言模板文件(i18n.toml),首次执行需生成
goi18n init

# 2. 基于英文模板生成中文翻译的待翻译文件(active.zh.toml)
goi18n extract -outdir locales/templates
goi18n merge -outdir locales -lang zh locales/templates/active.en.toml

# 3. 同理生成日文翻译文件(active.ja.toml)
goi18n merge -outdir locales -lang ja locales/templates/active.en.toml

执行完成后,locales目录下会出现active.zh.toml和active.ja.toml,此时文件中的翻译内容是默认的英文,需要手动修改为对应语言的翻译。比如修改active.zh.toml:

# locales/active.zh.toml
[hello]
descript = "问候语"
other = "你好 {{.Name}}"

[welcome]
descript = "欢迎语"
other = "欢迎来到{{.Project}}!你的账户有{{.Count}}条消息。"

[message_count]
descript = "消息数量提示(支持复数)"
one = "你有{{.Count}}条消息。"
other = "你有{{.Count}}条消息。"

[error_login]
descript = "登录错误提示"
other = "登录失败:{{.Reason}}"

[btn_submit]
descript = "提交按钮文本"
other = "提交"

[btn_cancel]
descript = "取消按钮文本"
other = "取消"

日文翻译文件active.ja.toml同理修改,这里就不赘述了。

后续若新增翻译项,只需修改英文模板,然后执行goi18n merge命令更新其他语言文件即可。

(三)初始化i18n实例

在项目中编写初始化代码,加载翻译文件并创建i18n实例,方便业务代码调用。新建i18n/i18n.go文件:

package i18n

import (
    "embed"
    "github.com/go-i18n/go-i18n/v2/i18n"
    "golang.org/x/text/language"
    "gopkg.in/yaml.v3"
)

// 嵌入翻译文件,确保编译后能读取到(关键!避免部署时找不到文件)
//go:embed ../locales/active.*.toml
var localesFS embed.FS

// I18n 多语言实例
var I18n *i18n.Bundle
var localizers map[string]*i18n.Localizer

// Init 初始化多语言
func Init() error {
    // 1. 创建bundle,指定默认语言为英文
    I18n = i18n.NewBundle(language.English)
    // 注册toml文件解析器
    I18n.RegisterUnmarshalFunc("toml", yaml.Unmarshal)
    // 从嵌入的文件系统加载所有翻译文件
    files, err := localesFS.ReadDir("../locales")
    if err != nil {
        return err
    }
    for _, file := range files {
        if file.IsDir() {
            continue
        }
        _, err := I18n.LoadMessageFileFromFS(localesFS, "../locales/"+file.Name())
        if err != nil {
            return err
        }
    }

    // 2. 初始化各语言的localizer(用于实际翻译)
    localizers = make(map[string]*i18n.Localizer)
    // 支持的语言列表,与翻译文件对应
    supportedLangs := []string{"en", "zh", "ja"}
    for _, lang := range supportedLangs {
        localizers[lang] = i18n.NewLocalizer(I18n, lang)
    }

    return nil
}

// Translate 翻译方法
// lang: 目标语言(en/zh/ja)
// key: 翻译项key
// data: 占位符数据(可选)
func Translate(lang, key string, data map[string]interface{}) (string, error) {
    // 若指定语言不支持,默认用英文
    localizer, ok := localizers[lang]
    if !ok {
        localizer = localizers["en"]
    }

    // 执行翻译
    if data == nil {
        return localizer.Localize(&i18n.LocalizeConfig{
            MessageID: key,
        })
    }

    // 带占位符的翻译(支持复数)
    return localizer.Localize(&i18n.LocalizeConfig{
        MessageID:    key,
        TemplateData: data,
        PluralCount:  data["Count"], // 复数场景需传入Count字段
    })
}

这里有个关键技巧:用//go:embed嵌入翻译文件,这样编译后的二进制文件能直接包含翻译内容,部署时不用单独拷贝locales目录,避免文件丢失问题。

(四)结合Gin框架使用

先在main.go中初始化i18n,然后在接口中根据用户选择的语言返回翻译内容。新建main.go:

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/your-name/go-multilingual-demo/i18n"
    "log"
    "net/http"
)

func main() {
    // 1. 初始化多语言
    err := i18n.Init()
    if err != nil {
        log.Fatalf("多语言初始化失败:%v", err)
    }

    // 2. 初始化Gin
    r := gin.Default()

    // 3. 测试接口:根据lang参数返回不同语言的内容
    r.GET("/greet", func(c *gin.Context) {
        // 获取语言参数(优先级:URL参数 > Header > Cookie > 默认英文)
        lang := c.Query("lang")
        if lang == "" {
            lang = c.GetHeader("Accept-Language")
            // 处理Header中的语言格式,比如"zh-CN,zh;q=0.9"取前面的zh
            if len(lang) > 2 {
                lang = lang[:2]
            }
        }
        if lang == "" {
            lang, _ = c.Cookie("lang")
        }

        // 翻译内容(带占位符)
        hello, _ := i18n.Translate(lang, "hello", map[string]interface{}{
            "Name": "张三",
        })
        welcome, _ := i18n.Translate(lang, "welcome", map[string]interface{}{
            "Project": "Go多语言演示项目",
            "Count":   5,
        })
        messageCount, _ := i18n.Translate(lang, "message_count", map[string]interface{}{
            "Count": 1,
        })
        btnSubmit, _ := i18n.Translate(lang, "btn_submit", nil)

        c.JSON(http.StatusOK, gin.H{
            "hello":         hello,
            "welcome":       welcome,
            "message_count": messageCount,
            "btn_submit":    btnSubmit,
        })
    })

    // 4. 启动服务
    log.Fatal(r.Run(":8080"))
}

启动服务后,通过不同的lang参数测试多语言效果:

  • 英文:访问http://localhost:8080/greet?lang=en,返回"Hello 张三";
  • 中文:访问http://localhost:8080/greet?lang=zh,返回"你好 张三";
  • 日文:访问http://localhost:8080/greet?lang=ja,返回对应日文翻译。

但是这里只是实现接口的多语言,页面多语言又该怎么实现呢?

其实实现逻辑是一样,只需要将原先页面上写死的文字改为有 go 模板进行多语言渲染即可

常见问题

Q1、翻译文件加载失败,返回默认英文

问题现象:明明配置了中文翻译文件,但无论怎么传lang参数,返回的都是英文。

常见原因

  • 翻译文件路径错误,程序找不到文件,比如部署时漏传locales目录;
  • 没有用embed嵌入文件,编译后的二进制无法读取外部文件;
  • 翻译文件中的key与模板文件不一致,比如模板中是hello,翻译文件中写成了helloworld。

解决方案

  • 优先使用//go:embed嵌入翻译文件,避免路径问题;
  • 初始化时添加日志打印,确认翻译文件是否成功加载:
  • 检查翻译文件中的key是否与模板文件完全一致,建议用复制粘贴避免拼写错误。

Q2、占位符不生效,直接显示 .Name

问题现象:翻译后的内容中,占位符没有被替换,而是直接显示{{.Name}}。

常见原因

  • 调用Translate方法时,没有传入占位符对应的data参数;
  • data参数中的key与占位符名称不一致,比如占位符是{{.Name}},传入的是{“name”: “张三”}(大小写敏感);
  • 翻译文件中的占位符拼写错误,比如写成了{{.Names}}。

解决方案

  • 确保调用Translate时传入正确的data参数,且key与占位符完全一致(大小写敏感);
  • 检查翻译文件中的占位符是否与模板文件一致,建议直接从模板文件复制占位符到翻译文件。

Q3、新增翻译项后,无法获取到新的翻译内容

问题现象:在模板文件中新增了翻译项,但调用Translate时返回key值,无法获取到翻译内容。

常见原因

  • 新增翻译项后,没有重新执行goi18n命令生成对应的翻译文件;
  • 翻译文件中没有添加新增翻译项的翻译内容;
  • 缓存没有清理,旧的缓存中没有新增翻译项的内容。

总结

多语言项目切换重点是选对工具(go-i18n)+ 规范流程(模板-翻译-初始化-使用)+ 细节优化(缓存、错误处理、语言选择逻辑)

你还知道哪些更优的方案吗

如果在使用过程中遇到其他问题,👏欢迎大家评论区分享留言!!!

版权声明

未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!

本文原文链接: https://fiveyoboy.com/articles/go-language-i18n/

备用原文链接: https://blog.fiveyoboy.com/articles/go-language-i18n/