对象创建

单例对象

大多数场景下,我们推荐使用g.I18n单例对象,并可自定义配置不同的单例对象,但是需要注意的是,单例对象的配置修改是全局有效的。例如:

g.I18n().T(context.TODO(), "{#hello} {#world}")

在所有的转译方法中,第一个参数都要求输入Context上下文变量参数,用于上下文变量的传递、翻译语言的指定、后续的可扩展能力。该参数虽然直接传递nil也是可以的,但是为保证程序的严谨性,我们建议您当不知道传递什么或者没有特殊要求的时候传递context.TODO()或者context.Background()来替代。

独立对象

其次,我们也可以模块化独立使用gi18n模块,通过gi18n.New()方法创建独立的i18n对象,然后开发者自行进行管理。例如:

i18n := gi18n.New() 
i18n.T(context.TODO(), "{#hello} {#world}")

语言设置

设置转译语言有两种方式,一种是通过SetLanguage方法设置当前I18N对象统一的转译语言,另一种是通过上下文设置当前执行转译的语言。

SetLanguage

例如,我们通过g.I18n().SetLanguage("zh-CN")即可设置当前转译对象的转译语言,随后使用该对象都将使用zh-CN进行转译。需要注意的是,组件的配置方法往往都不是并发安全的,该方法也同样如此,需要在程序初始化时进行设置,随后不能在运行时进行更改。

WithLanguage

WithLanguage方法可以创建一个新的上下文变量,并临时设置您当前转译的语言,由于该方法作用于Context上下文,因此是并发安全的,常用于运行时转译语言设置。我们来看一个例子:

ctx := gi18n.WithLanguage(context.TODO(), "zh-CN")
i18n.Translate(ctx, `hello`)

其中WithLanguage方法定义如下:

// WithLanguage append language setting to the context and returns a new context.
func WithLanguage(ctx context.Context, language string) context.Context

用于将转译语言设置到上下文变量中,并返回一个新的上下文变量,该变量可用于后续的转译方法。

常用方法

T方法

T方法为Translate方法的别名,也是大多数时候我们推荐使用的方法名称。T方法可以给定关键字名称,也可以直接给定模板内容,将会被自动转译并返回转译后的字符串内容。

此外,T方法可以通过第二个语言参数指定需要转译的目标语言名称,该名称需要和配置文件/路径中的名称一致,往往是标准化的国际化语言缩写名称例如:en/ja/ru/zh-CN/zh-TW等等。否则,将会自动使用Manager转译对象中设置的语言进行转译。

方法定义:

// T translates <content> with configured language and returns the translated content.
func T(ctx context.Context, content string)

关键字转译

关键字的转译直接给T方法传递关键字即可,例如:T(context.TODO(), "hello")T(context.TODO(), "world")I18N组件将会优先将给定的关键字进行转译,转译成后返回转译后的内容,否则直接展示原内容。

模板内容转译

T方法支持模板内容转换,模板中的关键字默认使用{#}标签进行包含,模板解析时将会自动替换该标签中的关键字内容。使用示例:

1)目录结构

├── main.go
└── i18n
    ├── en.toml
    ├── ja.toml
    ├── ru.toml
    └── zh-CN.toml

2)转译文件

ja.toml

hello = "こんにちは" 
world = "世界"

ru.toml

hello = "Привет" 
world = "мир"

zh-CN.toml 

hello = "你好" 
world = "世界"

3)示例代码 

package main

import (
	"fmt"
	"github.com/gogf/gf/v2/os/gctx"

	"github.com/gogf/gf/v2/i18n/gi18n"
)

func main() {
	var (
		ctx  = gctx.New()
		i18n = gi18n.New()
	)

	i18n.SetLanguage("en")
	fmt.Println(i18n.Translate(ctx, `hello`))
	fmt.Println(i18n.Translate(ctx, `GF says: {#hello}{#world}!`))

	i18n.SetLanguage("ja")
	fmt.Println(i18n.Translate(ctx, `hello`))
	fmt.Println(i18n.Translate(ctx, `GF says: {#hello}{#world}!`))

	i18n.SetLanguage("ru")
	fmt.Println(i18n.Translate(ctx, `hello`))
	fmt.Println(i18n.Translate(ctx, `GF says: {#hello}{#world}!`))

	ctx = gi18n.WithLanguage(ctx, "zh-CN")
	fmt.Println(i18n.Translate(ctx, `hello`))
	fmt.Println(i18n.Translate(ctx, `GF says: {#hello}{#world}!`))
}

执行后,终端输出为:

Hello
GF says: HelloWorld!
こんにちは
GF says: こんにちは世界!
Привет
GF says: Приветмир!
你好
GF says: 你好世界!

Tf方法

我们都知道,模板内容中也会存在一些变量,这些变量可以通过Tf方法进行转译。

TfTranslateFormat的别名,该方法支持格式化转译内容,字符串格式化语法参考标准库fmt包的Sprintf方法。

方法定义:

// Tf translates, formats and returns the <format> with configured language
// and given <values>.
func Tf(ctx context.Context, format string, values ...interface{}) string

我们来看一个简单的示例。

1)目录结构

├── main.go
└── i18n
    ├── en.toml
    └── zh-CN.toml

2)转译文件

en.toml 

OrderPaid = "You have successfully complete order #%d payment, paid amount: ¥%0.2f."

 zh-CN.toml 

OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"

3)示例代码

package main

import (
	"fmt"
	"github.com/gogf/gf/v2/i18n/gi18n"
	"github.com/gogf/gf/v2/os/gctx"
)

func main() {
	var (
		ctx         = gctx.New()
		orderId     = 865271654
		orderAmount = 99.8
	)

	i18n := gi18n.New()
	i18n.SetLanguage("en")
	fmt.Println(i18n.Tf(ctx, `{#OrderPaid}`, orderId, orderAmount))

	i18n.SetLanguage("zh-CN")
	fmt.Println(i18n.Tf(ctx, `{#OrderPaid}`, orderId, orderAmount))
}

执行后,终端输出为:

You have successfully complete order #865271654 payment, paid amount: ¥99.80.
您已成功完成订单号 #865271654 支付,支付金额¥99.80。

为方便演示,该示例中对支付金额的处理比较简单,在实际项目中往往需要在业务代码中对支付金额的单位按照区域做自动转换,再渲染i18n显示内容。

上下文设置转译语言

我们将上面的示例做些改动来演示。

1)目录结构

├── main.go
└── i18n
    ├── en.toml
    └── zh-CN.toml

2)转译文件

en.toml 

OrderPaid = "You have successfully complete order #%d payment, paid amount: ¥%0.2f."

zh-CN.toml

OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"

3)示例代码

package main

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/i18n/gi18n"
)

func main() {
	var (
		orderId     = 865271654
		orderAmount = 99.8
	)
	fmt.Println(g.I18n().Tf(
		gi18n.WithLanguage(context.TODO(), `en`),
		`{#OrderPaid}`, orderId, orderAmount,
	))
	fmt.Println(g.I18n().Tf(
		gi18n.WithLanguage(context.TODO(), `zh-CN`),
		`{#OrderPaid}`, orderId, orderAmount,
	))
}

执行后,终端输出为:

You have successfully complete order #865271654 payment, paid amount: ¥99.80.
您已成功完成订单号 #865271654 支付,支付金额¥99.80。

为方便演示,该示例中对支付金额的处理比较简单,在实际项目中往往需要在业务代码中对支付金额的单位按照区域做自动转换,再渲染i18n显示内容。

I18N与视图引擎

gi18n默认已经集成到了GoFrame框架的视图引擎中,直接在模板文件/内容中使用gi18n的关键字标签即可。我们同样可以通过上下文变量的形式来设置当前请求的转译语言。

此外,我们也可以通过设置模板变量I18nLanguage设置当前模板的解析语言,该变量可以控制不同的模板内容按照不同的国际化语言进行解析。

使用示例:

package main

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/i18n/gi18n"
	"github.com/gogf/gf/v2/net/ghttp"
)

func main() {
	s := g.Server()
	s.Group("/", func(group *ghttp.RouterGroup) {
		group.Middleware(func(r *ghttp.Request) {
			r.SetCtx(gi18n.WithLanguage(r.Context(), r.GetString("lang", "zh-CN")))
			r.Middleware.Next()
		})
		group.ALL("/", func(r *ghttp.Request) {
			r.Response.WriteTplContent(`{#hello}{#world}!`)
		})
	})
	s.SetPort(8199)
	s.Run()
}

执行后,访问以下页面,将会输出:

  1. http://127.0.0.1:8199
    你好世界!
  2. http://127.0.0.1:8199/?lang=ja
    こんにちは世界!






















Content Menu

  • No labels

10 Comments

  1. 不明白既然初始时设置了WithLanguage,为何转译时还要设置上下文context,感觉有点画蛇添足

    1. 一个是全局设置,一个是局部设置。比如不同的用户可能设置了不同的语言,仅靠一个i18n对象是不行的,可以使用ctx来实现。

      1. 感谢回复,我也是一个Phper转过来的,在php中,可以很方便的使用__CLASS__及__METHOD__定义存储翻译文件的位置(如 i18n/语言Locale代码/后台/控制器名.toml),不知道在go中,除了用反射,是否有还别的处理方法?

        1. Go里面可能没有,你可以看看这个模块 调试功能-gdebug

  2. 有一个问题,假如我分配了不同的模块给不同的人,让他们自己写自己的toml文件去进行翻译,而他们的toml中key值可能会有重复,即同一个句子获得了2个对应的翻译,那么i18n是怎么处理的呢?是会报err还是以遍历到的第一个映射为准呢?

  3. 郭强 i18n目录默认是和main一个级别时可以正常加载翻译,比如:

    ├── main.go
    └── i18n
        ├── en.toml
        └── zh-CN.toml

    但是按照gf2.0 建议的目录结构是如下这样的

    ├───manifest
        │   ├───config
        ├───resource
        │   ├───i18n
        │   ├───public

    ├───main.go

    i18n目录默认没有和main同一个级别。那么怎么设置才可以正确加载到i18n的翻译呢?

    1. 终于搞明白了。

      读取路径
      
      默认情况下gi18n会自动读取当前项目源码根目录(或者当前PWD运行目录下)下的i18n目录,默认将该目录作为国际化转译文件存储目录。开发者也可以通过SetPath方法自定义i18n文件的存储目录路径。


      所以可以设置SetPath,

      func main() {
      	s := g.Server()
      	g.I18n().SetPath("resource/i18n") // i18n目录默认是根目录下或者gres资源目录下
      	
      	s.Group("/", func(group *ghttp.RouterGroup) {
      		group.Middleware(
      			service.Middleware().I18NMiddleware,
      			service.Middleware().ResponseHandler,
      		)
      		group.Bind(
      			controller.User, // 用户
      		)
      	})
      	// 启动Http Server
      	s.Run()
      }
  4. 示例代码中的

    示例代码
    package main
    
    import (
    	"github.com/gogf/gf/v2/frame/g"
    	"github.com/gogf/gf/v2/i18n/gi18n"
    	"github.com/gogf/gf/v2/net/ghttp"
    )
    
    func main() {
    	s := g.Server()
    	s.Group("/", func(group *ghttp.RouterGroup) {
    		group.Middleware(func(r *ghttp.Request) {
    			r.SetCtx(gi18n.WithLanguage(r.Context(), r.GetString("lang", "zh-CN")))
    			r.Middleware.Next()
    		})
    		group.ALL("/", func(r *ghttp.Request) {
    			r.Response.WriteTplContent(`{#hello}{#world}!`)
    		})
    	})
    	s.SetPort(8199)
    	s.Run()
    }

    的第13行  r.SetCtx(gi18n.WithLanguage(r.Context(), r.GetString("lang", "zh-CN"))) 无法在2.0脚手架生成的代码中正常运行,r.GetString方法不存在

    1. 可以使用 `r.GetRequest`代替

  5. 求教一下,Tf方法goland会报format关于占位符的警告,这个有什么处理办法吗