本地化
概述
Goyave 框架提供了便捷的方式来支持应用程序中的多语言。开箱即用,Goyave 仅提供 en-US
语言,但您可以添加任意多种语言。
.
└── resources
└── lang
└── en-US (语言名称)
├── fields.json (可选)
├── locale.json (可选)
└── rules.json (可选)
每种语言都有自己的目录,应使用 ISO 639-1 语言代码命名。您还可以为语言添加变体:en-US
、en-UK
、fr-FR
、fr-CA
等。大小写很重要。
每个语言目录包含三个文件。每个文件都是可选的。
fields.json
: 字段名称翻译和字段特定的规则消息。locale.json
: 所有其他语言行。rules.json
: 验证规则消息。
INFO
resources/lang
目录中的所有目录在服务器创建时会自动加载。
语言文件
字段 (Fields)
fields.json
文件包含字段名称的翻译。翻译字段名称有助于生成更具表现力的消息,而不是向用户显示技术性的字段名称。
示例:
{
"email": "电子邮件地址"
}
这样,验证错误消息中的 :field
占位符将被填充为“电子邮件地址”,而不是原始字段名“email”。
INFO
有关验证消息占位符的更多信息,请参阅验证部分。
区域设置 (Locale)
locale.json
文件包含不与验证相关的所有语言行。这是您应该为用户界面或控制器返回的消息编写语言行的地方。
示例:
{
"product.created": "产品已成功创建。",
"product.deleted": "产品已成功删除。"
}
TIP
使用点分隔的名称作为语言行是一个好习惯,有助于使它们更清晰、更具表现力。
规则 (Rules)
rules.json
文件包含验证规则消息。这些消息可以包含**占位符**,验证器会自动用动态值替换它们。如果您编写自定义验证规则,它们的消息应写在此文件中。
示例:
{
"integer": ":field 必须是整数。",
"starts_with": ":field 必须以以下值之一开头::values。",
"same": ":field 和 :other 必须匹配。"
}
TIP
- 如果在应用程序中定义了
en-US
语言,则默认语言行将被语言文件中的行覆盖,所有未定义的行将保留。 - 验证规则的本地化在此处有更详细的说明。
手动加载
可以从其他位置手动加载语言目录。如果加载的语言已在应用程序中可用,新加载的语言将通过合并覆盖先前的语言。
import (
"embed"
"fmt"
"os"
"goyave.dev/goyave/v5"
"goyave.dev/goyave/v5/lang"
"goyave.dev/goyave/v5/util/errors"
"goyave.dev/goyave/v5/util/fsutil"
)
//go:embed resources
var resources embed.FS
func main() {
server, err := goyave.New(goyave.Options{})
if err != nil {
fmt.Fprintln(os.Stderr, err.(*errors.Error).String())
os.Exit(1)
}
resources := fsutil.NewEmbed(resources)
langFS, err := resources.Sub("resources/lang")
if err != nil {
server.Logger.Error(err)
os.Exit(1)
}
err = server.Lang.Load(langFS, "en-CA", "lang/en-CA")
err = server.Lang.LoadDirectory(langFS, "lang")
//...
}
TIP
任何实现 fsutil.FS
的文件系统都可以使用。如果不想使用嵌入式文件系统,可以使用 osfs.FS
。
使用本地化
有两个重要的结构需要了解:
*lang.Languages
: 所有已加载语言的容器,它还定义了默认语言。此容器对所有组件可访问。*lang.Language
: 单一语言,包含所有本地化的行。
当传入请求进入应用程序时,内置的语言中间件检查是否设置了 Accept-Language
头,并将 *goyave.Request.Lang
字段设置为识别出的 *lang.Language
或默认语言。
您可以使用语言的 Get()
方法获取本地化字符串。
func (ctrl *Controller) Handle(response *goyave.Response, request *goyave.Request) {
request.Lang.Get("customMessage")
// 或
ctrl.Lang().GetDefault().Get("customMessage")
ctrl.Lang().Get("en-US", "customMessage")
}
路径
- 在
locale.json
中定义的自定义行直接使用其键访问。 - 在
rules.json
中定义的验证规则消息使用validation.rules
前缀访问:validation.rules.<规则名称>
validation.rules.<规则名称>.element
validation.rules.<规则名称>.string
validation.rules.<规则名称>.numeric
validation.rules.<规则名称>.array
validation.rules.<规则名称>.file
validation.rules.<规则名称>.object
- 在
fields.json
中定义的字段名称使用validation.fields
前缀访问:validation.fields.<字段名称>
INFO
不存在的条目会按原样返回。例如,如果您 Get()
一个语言条目 validation.rules.nonExisting
,返回的字符串将是 validation.rules.nonExisting
。
占位符
语言行可以包含占位符。占位符由冒号直接后跟占位符名称标识:
{
"greetings": "问候,:username!"
}
Get()
方法的最后一个参数是占位符及其替换值的可变关联切片。在以下示例中,占位符 :username
将被用户结构中的 Name
字段替换。
language.Get("greetings", ":username", user.Name) // "问候,Taylor!"
您可以提供任意数量的占位符:
language.Get("greetings-with-date", ":username", user.Name, ":day", "星期一") // "问候,Taylor!今天是星期一"
TIP
当提供占位符时,所有出现的占位符都会被替换。
{
"popular": ":product 非常受欢迎。:product 的销售额上周超过了1000。"
}
language.Get("popular", ":product", "割草机")
// "割草机非常受欢迎。割草机的销售额上周超过了1000。"