登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go go-i18n 怎样向消息模板传入命名字段

来源:17golang原创

时间:2026-10-30 23:11:51 262浏览 收藏

Go go-i18n 通过 i18n.LocalizeConfig.TemplateData 向消息模板传入命名字段。数据可以是 map,也可以是包含导出字段的结构体;模板用 {{.User}} 这样的表达式读取对应值。字段名必须与模板约定一致,不能把 JSON 标签名自动当成模板字段名。

官方地址:https://github.com/nicksnyder/go-i18n

选择模板数据而非字符串拼接

一个“文件已就绪”通知要包含用户、文件名和工作区。若在业务代码里先拼出前半句、再追加文件名,翻译人员就难以调整不同语言的语序。把整句作为一个消息模板,再传入命名字段,能让中文与英文各自安排字段位置。

这里的目标是稳定的消息数据契约。MessageID 标识消息,TemplateData 提供本次业务值,模板负责语言表达。命名字段适合需要多语言调整语序的句子;不应把普通数据值反过来拼入模板源码。

使用 TemplateData 传入命名字段

下面把两种语言的消息直接加入 Bundle,省去文件加载,让示例聚焦字段绑定。两条消息共享 UploadReady,字段均为 User、File、Workspace。Localizer 选择语言,Localize 执行对应模板。

package main

import (
    "fmt"

    "github.com/nicksnyder/go-i18n/v2/i18n"
    i18ntemplate "github.com/nicksnyder/go-i18n/v2/i18n/template"
    "golang.org/x/text/language"
)

func render() error {
    bundle := i18n.NewBundle(language.English)
    // 英文和简体中文使用相同消息 ID 与字段名,可分别调整字段顺序。
    if err := bundle.AddMessages(language.English, &i18n.Message{
        ID: "UploadReady",
        Other: "{{.User}}: {{.File}} is ready in {{.Workspace}}.",
    }); err != nil {
        return err
    }
    if err := bundle.AddMessages(language.SimplifiedChinese, &i18n.Message{
        ID: "UploadReady",
        Other: "{{.User}},{{.Workspace}}中的文件{{.File}}已就绪。",
    }); err != nil {
        return err
    }
    localizer := i18n.NewLocalizer(bundle, "zh-Hans")
    message, err := localizer.Localize(&i18n.LocalizeConfig{
        MessageID: "UploadReady",
        TemplateData: map[string]any{
            "User": "林岚", "File": "report.csv", "Workspace": "项目资料",
        },
        // 缺失 map 键时返回模板错误,而不是依赖默认缺键显示。
        TemplateParser: &i18ntemplate.TextParser{Option: "missingkey=error"},
    })
    if err != nil {
        return fmt.Errorf("生成文件就绪提示: %w", err)
    }
    fmt.Println(message)
    return nil
}

func main() {
    if err := render(); err != nil {
        panic(err) // 示例入口停止使用错误或不完整的消息。
    }
}

按这段消息文本与字段值,中文提示应包含“林岚”“项目资料”和“report.csv”,它们的位置由中文模板决定。本文未运行程序,不把这段预期描述当作真实输出截图。

go-i18n消息与命名字段的结构关系
静态结构图:消息分组包含 MessageID 与模板,数据分组包含 TemplateData 及三个命名字段;连线表达字段读取关系。

TemplateData 只负责执行时的数据,不负责把消息添加到 Bundle。消息不存在、语言发生回退或模板执行失败时,都应处理 Localize 返回的 error,不能只看是否得到非空字符串。接口定义见LocalizeConfig 官方文档。

比较 map 与结构体的数据契约

数据形式模板读取适合场景主要注意点
map[string]any{{.User}}少量灵活字段键名拼错通常只能在运行时发现
导出字段结构体{{.User}}消息字段稳定、多个调用点复用字段需导出,模板引用仍是运行时约定
字符串拼接不提供命名数据仅非常简单且无需翻译重排的文本跨语言语序容易被业务代码锁死

例如定义含 User、File、Workspace 三个导出字符串字段的结构体,作为 TemplateData 传入后可沿用同一模板。小写 user 属于未导出字段,不能按导出字段方式读取;json:"user" 不会把 {{.User}} 自动变成 {{.user}}。标准模板按自身字段和键访问规则执行,见text/template 文档。

结构体可以约束 Go 调用方的数据形状,但模板文本中的字段名并不由 Go 编译器检查。消息字段改名时,所有语言模板和数据构造点应一起更新。不要将一个语言中的 User 改为 Username,而其他翻译仍保留旧字段。

把 PluralCount 与显示字段分开

PluralCount 决定使用哪种复数形式,TemplateData 则提供要展示的字段。如果模板需要 {{.Count}},可以在 TemplateData 中明确放入 Count,同时把同一个业务数量传给 PluralCount。不要以为配置了 PluralCount 就一定会向你已经提供的 map 自动追加 Count。

复数选择与模板展示数据的职责关系
静态结构图:数量关联 PluralCount 与 Count,前者关联复数形式,后者属于 TemplateData 并供消息模板读取;图示不表示操作步骤。

当前实现只有在 TemplateData 为 nil 且 PluralCount 非 nil 时,才会构造包含键 PluralCount 的默认数据。因此此时模板使用的是 {{.PluralCount}};显式传入自己的数据后,需要自行保证模板所需字段齐全。相关行为见Localizer 实现。

这两项职责也不能互相替代:只给显示用的 Count 字段,不会自动触发正确的复数形式选择。数量用来选择语言规则时应保持原始数值语义;若需要特殊显示格式,可另提供展示字段。

处理模板错误及输出边界

默认模板解析器使用 text/template,缺少 map 键的默认选项是 missingkey=default。示例显式使用 TextParser{Option:"missingkey=error"},使 {{.User}} 这类直接字段访问缺键时报告错误。不要把默认缺键表现当作业务回退机制;结构体访问不存在字段也需要正确处理执行错误。

在正常请求处理中通常更适合 Localize 并显式处理错误;MustLocalize 遇错会 panic。业务可记录 MessageID 和缺失字段类别,再按既定策略提供提示,不应在日志中输出用户隐私字段的全部内容。

TemplateData 中的字符串会再次当模板执行吗?普通插入值不是新的模板源码。不要为了二次替换而把用户输入接回模板文本。

生成结果可以直接作为 HTML 吗?默认 text/template 不提供 html/template 的上下文自动转义。将结果放入网页时,应在展示层按上下文转义,不要随意转换为可信 HTML;也不要以为命名字段会自动过滤所有标签。

多个请求能共用一张可变 map 吗?不要让并发请求修改同一个模板数据 map。为每次调用准备独立数据或使用明确的同步与不可变约定,避免请求字段串用。

什么时候选结构体?当同一消息的数据由多个调用点构造、字段长期稳定时,可用小型专用结构体表达契约;字段少且变化较灵活时,map 更直接。无论哪种形式,字段名、错误处理和输出转义都要清楚。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>