基本介绍
版本支持
该特性从 v2.10.0 版本开始支持。
在开发多语言应用时,错误消息的国际化是一个常见需求。GoFrame 框架的 gerror 组件从 v2.10.0 版本开始,提供了 ITextArgs 接口来支持错误信息的国际化场景。
该接口允许错误对象分别暴露错误消息模板和格式化参数,使得开发者可以:
- 获取原始的错误消息模板(未格式化)
- 获取模板中的格式化参数
- 根据不同语言环境翻译模板
- 使用参数重新格式化翻译后的消息
这种设计避免了错误消息在创建时就被格式化,确保了国际化场景下的灵活性。
接口定义
ITextArgs 接口
// ITextArgs is the interface for Text and Args features.
// This interface is mainly used for i18n features, that needs text and args separately.
type ITextArgs interface {
error
Text() string
Args() []any
}
核心方法
gerror.Error 对象实现了 ITextArgs 接口,提供以下方法:
| 方法 | 返回类型 | 说明 |
|---|---|---|
Text() | string | 返回错误消息的格式化模板字符串(未进行参数替换)。例如:"user %s not found with id: %d" |
Args() | []any | 返回用于格式化的参数列表。例如:["john", 123] |
TextWithArgs() | string | 返回完整的格式化后的错误消息,等同于 Error() 方法。例如:"user john not found with id: 123" |
使用场景
1. 国际化(i18n)
在多语言应用中,可以获取错误消息模板,在不同语言环境下翻译后再进行格式化:
package main
import (
"context"
"errors"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/i18n/gi18n"
)
func main() {
// 创建一个带参数的错误
err := gerror.Newf("user %s not found with id: %d", "john", 123)
// 获取错误消息模板和参数
var e gerror.ITextArgs
if errors.As(err, &e) {
var (
format = e.Text() // "user %s not found with id: %d"
args = e.Args() // ["john", 123]
ctx = context.Background()
i18n = gi18n.New(gi18n.Options{
Language: "zh-CN",
})
)
// 在国际化场景中,可以先翻译模板,再进行格式化
translated := i18n.Tf(ctx, format, args...)
fmt.Println(translated)
}
}
2. 结构化错误处理
在日志系统或监控系统中,可以单独记录错误模板和参数,便于后续分析:
package main
import (
"context"
"errors"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
func main() {
var (
ctx = context.Background()
err = gerror.Newf("failed to connect to database %s:%d", "localhost", 3306)
e gerror.ITextArgs
)
if errors.As(err, &e) {
// 记录结构化日志
g.Log().Error(ctx, g.Map{
"error_template": e.Text(),
"error_args": e.Args(),
"error_message": e.Error(),
})
}
}
3. 错误分析与聚合
在错误收集系统中,可以按错误模板进行聚合统计,而不是按完整错误消息:
package main
import (
"github.com/gogf/gf/v2/errors/gerror"
)
// 错误统计器
type ErrorStats struct {
counts map[string]int
}
func (s *ErrorStats) Record(err error) {
if e, ok := err.(gerror.ITextArgs); ok {
// 按模板统计,而不是按完整消息
template := e.Text()
s.counts[template]++
}
}
完整示例
基本使用
package main
import (
"errors"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
)
func main() {
// 创建一个带参数的错误
err := gerror.Newf("user %s not found with id: %d", "john", 123)
// 获取完整的错误消息
fmt.Println("Error:", err.Error())
// 输出: Error: user john not found with id: 123
// 获取错误消息模板和参数
var e gerror.ITextArgs
if errors.As(err, &e) {
fmt.Println("Template:", e.Text())
// 输出: Template: user %s not found with id: %d
fmt.Println("Args:", e.Args())
// 输出: Args: [john 123]
}
}
国际化翻译示例
以下示例展示了如何结合ITextArgs接口实现错误消息的多语言翻译:
package main
import (
"errors"
"fmt"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
// 简单的翻译映射
var translations = map[string]map[string]string{
"en": {
"user %s not found with id: %d": "user %s not found with id: %d",
"failed to connect to database %s:%d": "failed to connect to database %s:%d",
"invalid parameter: %s must be %s": "invalid parameter: %s must be %s",
"operation timeout after %d seconds": "operation timeout after %d seconds",
},
"zh": {
"user %s not found with id: %d": "用户 %s 未找到,ID: %d",
"failed to connect to database %s:%d": "无法连接到数据库 %s:%d",
"invalid parameter: %s must be %s": "无效参数:%s 必须是 %s",
"operation timeout after %d seconds": "操作超时,已等待 %d 秒",
},
"ja": {
"user %s not found with id: %d": "ユーザー %s が見つかりません、ID: %d",
"failed to connect to database %s:%d": "データベース %s:%d への接続に失敗しました",
"invalid parameter: %s must be %s": "無効なパラメータ: %s は %s である必要があります",
"operation timeout after %d seconds": "%d 秒後に操作がタイムアウトしました",
},
}
// 翻译函数
func translate(template string, lang string) string {
if langMap, ok := translations[lang]; ok {
if trans, ok := langMap[template]; ok {
return trans
}
}
// 如果没有找到翻译,返回原模板
return template
}
// TranslateError 将错误翻译为指定语言
func TranslateError(err error, lang string) string {
var e gerror.ITextArgs
if errors.As(err, &e) {
// 获取模板和参数
template := e.Text()
args := e.Args()
// 翻译模板
translatedTemplate := translate(template, lang)
// 使用参数格式化翻译后的模板
return fmt.Sprintf(translatedTemplate, args...)
}
// 如果不支持 ITextArgs 接口,直接返回错误消息
return err.Error()
}
func main() {
// 创建不同类型的错误
allErrors := []error{
gerror.Newf("user %s not found with id: %d", "john", 123),
gerror.NewCodef(gcode.CodeInvalidParameter, "invalid parameter: %s must be %s", "age", "positive"),
gerror.Newf("operation timeout after %d seconds", 30),
}
// 测试不同语言的翻译
languages := []string{"en", "zh", "ja"}
for _, err := range allErrors {
fmt.Println("\n原始错误:", err.Error())
for _, lang := range languages {
translated := TranslateError(err, lang)
fmt.Printf(" %s: %s\n", lang, translated)
}
}
}
输出示例:
原始错误: user john not found with id: 123
en: user john not found with id: 123
zh: 用户 john 未找到,ID: 123
ja: ユーザー john が見つかりません、ID: 123
原始错误: invalid parameter: age must be positive
en: invalid parameter: age must be positive
zh: 无效参数:age 必须是 positive
ja: 無効なパラメータ: age は positive である必要があります
原始错误: operation timeout after 30 seconds
en: operation timeout after 30 seconds
zh: 操作超时,已等待 30 秒
ja: 30 秒後に操作がタイムアウトしました
与 gi18n 组件结合
在实际项目中,可以结合 GoFrame 的 gi18n 国际化组件使用:
package main
import (
"context"
"errors"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/i18n/gi18n"
"github.com/gogf/gf/v2/os/gctx"
)
// TranslateErrorWithI18n 使用 gi18n 翻译错误
func TranslateErrorWithI18n(ctx context.Context, err error) string {
var e gerror.ITextArgs
if errors.As(err, &e) {
// 获取模板和参数
var (
format = e.Text()
args = e.Args()
)
// 使用参数格式化翻译后的模板
return gi18n.Tf(ctx, format, args...)
}
return err.Error()
}
func main() {
var ctx = gctx.New()
// 设置语言
ctx = gi18n.WithLanguage(ctx, "zh-CN")
// 创建错误
err := gerror.Newf("user %s not found with id: %d", "john", 123)
// 翻译错误
translated := TranslateErrorWithI18n(ctx, err)
fmt.Println(translated)
}