Golang多语言编程实践与技巧

Golang多语言编程实践与技巧 我已经为多语言网站的问题困扰了一段时间。据我了解,实现这个功能有很多种方法。我发现了一种使用 GitHub - leonelquinteros/gotext: Go (Golang) GNU gettext utilities package 的方法。在我看来,这似乎比 golang.org/x/text 更简单。

如果有人能审阅我的代码并告诉我是否走对了方向,我将不胜感激。

Go Playground - The Go Programming Language

实时预览

goLang

在 Go 模板内部翻译文本


更多关于Golang多语言编程实践与技巧的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

整体看起来不错,我有几点建议:移除 init() 函数,或许可以考虑使用 GitHub - nicksnyder/go-i18n: Translate your Go program into multiple languages.,但这很大程度上是个人偏好。过去我处理国际化(i18n)时只使用过 Hugo(它是最简单的选择)。

我看到一个可以改进的地方:你可以使用 go:embed 指令配合 embed.FS 来嵌入你的公共静态资源/模板文件。这样,最终生成的二进制文件就可以独立运行,并包含 locale/ 目录下的文件。

另外,如果你只是从映射(map)中读取数据,那么就不需要互斥锁(mutex)。我不会建议在 HTTP 处理器中使用非线程安全的内存映射并进行读写操作,因为数据量最终可能会增长。你需要的可能是一个类似线程安全的 LRU(最近最少使用)缓存的东西(如果你继续采用这个库和方法,在某个时间点你将需要对映射或缓存进行数据淘汰)。可以参考这个库:GitHub - hashicorp/golang-lru: Golang LRU cache

// 示例代码:使用 embed.FS
package main

import "embed"

//go:embed locale/*
var localeFS embed.FS

更多关于Golang多语言编程实践与技巧的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的建议。

MrWormHole: 我会做的是移除 init(),或许可以考虑使用 [GitHub - nicksnyder/go-i18n。

init{} 函数会在内存中创建一个映射变量,除此之外还有其他作用。所以我真的不知道该如何替换它。从一开始我就对使用哪种解决方案感到困惑。有几种方法可以选择。golang/x 看起来更复杂,而 go-i18n 使用 .toml 文件,这看起来和 .po 文件很接近,但是因为 .po 文件可以编译成 .mo 文件,所以可能更快?与 leonelquinteros/gotext 相比,go-i18n 有什么优势?

我能看到的一个可以改进的方面是,你可以使用 go:embed 配合 embed.FS 来处理你的公共静态资源/模板,这样你的最终二进制文件就可以独立包含 locale/ 文件。

我可能会使用 go:embed 来处理本地化翻译文件夹。但是对于 HTML 内容,我仍然犹豫不决。主要是因为将来它可能包含大量文件,并且将无法再在服务器上即时对错误进行细微编辑。

如果你只是从映射中读取数据,那么你也不需要互斥锁。

您的意思是使用映射时不需要加锁/解锁吗?

我不会在内存中使用不安全的映射并在 HTTP 处理程序中读写,因为它最终会增长,你可能需要的是类似线程安全的 LRU(最近最少使用)缓存。

那么 var translations map[string]*gotext.Po 是一个内存中的不安全映射吗?

使用 gotext 包处理多语言翻译是常见且有效的方案。以下是基于你代码的优化示例,展示如何正确配置和使用:

package main

import (
	"fmt"
	"html/template"
	"net/http"
	
	"github.com/leonelquinteros/gotext"
)

func init() {
	// 配置本地化目录结构
	gotext.Configure("locales", "en_US", "default")
}

func main() {
	http.HandleFunc("/", homeHandler)
	http.ListenAndServe(":8080", nil)
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	// 从请求中获取语言参数
	lang := r.URL.Query().Get("lang")
	if lang == "" {
		lang = "en_US"
	}
	
	// 创建指定语言的locale对象
	locale := gotext.NewLocale("locales", lang)
	locale.AddDomain("default")
	
	// 模板函数映射
	funcMap := template.FuncMap{
		"translate": func(s string) string {
			return locale.Get(s)
		},
	}
	
	// 解析模板
	tmpl := template.Must(template.New("home").Funcs(funcMap).Parse(`
		<!DOCTYPE html>
		<html>
		<body>
			<h1>{{"Welcome" | translate}}</h1>
			<p>{{"Hello, world!" | translate}}</p>
			<p>{{translate "Current language"}}: {{.Language}}</p>
		</body>
		</html>
	`))
	
	// 执行模板
	data := struct {
		Language string
	}{
		Language: lang,
	}
	
	tmpl.Execute(w, data)
}

目录结构示例:

project/
├── main.go
└── locales/
    ├── en_US/
    │   └── default.po
    ├── es_ES/
    │   └── default.po
    └── zh_CN/
        └── default.po

PO文件内容示例(locales/en_US/default.po):

msgid "Welcome"
msgstr "Welcome"

msgid "Hello, world!"
msgstr "Hello, world!"

msgid "Current language"
msgstr "Current language"

关键改进点:

  1. 使用 gotext.NewLocale 动态创建locale实例
  2. 通过模板函数 translate 在模板内直接调用翻译
  3. 支持通过URL参数动态切换语言
  4. 符合标准的gettext目录结构

访问示例:

  • 英文:http://localhost:8080/?lang=en_US
  • 中文:http://localhost:8080/?lang=zh_CN

这种方法比 golang.org/x/text 更轻量,特别适合Web应用的多语言需求。

回到顶部