跳到主要内容
版本:2.10.x(Latest)

基本介绍

版本支持

该特性从 v2.10.0 版本开始支持。

在开发多语言应用时,错误消息的国际化是一个常见需求。GoFrame 框架的 gerror 组件从 v2.10.0 版本开始,提供了 ITextArgs 接口来支持错误信息的国际化场景。

该接口允许错误对象分别暴露错误消息模板格式化参数,使得开发者可以:

  1. 获取原始的错误消息模板(未格式化)
  2. 获取模板中的格式化参数
  3. 根据不同语言环境翻译模板
  4. 使用参数重新格式化翻译后的消息

这种设计避免了错误消息在创建时就被格式化,确保了国际化场景下的灵活性。

接口定义

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 组件结合

在实际项目中,可以结合 GoFramegi18n 国际化组件使用:

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)
}