文档章节

Go语言的国际化支持(基于gettext-go)

chai2010
 chai2010
发布于 2014/01/07 07:54
字数 1330
阅读 3986
收藏 79
点赞 11
评论 17

本文在 Golang中国博客 的地址: http://blog.go-china.org/07-gettext

hello, world!

假设有以下的程序, 输出: "Hello, world!".

package main

import (
	"fmt"
)

func main() {
	fmt.Println("Hello, world!")
}

现在要让改程序支持不同语言的用户, 然后以本地语言输出相同意思的信息. 这就是很多程序面临的国际化问题.

Go语言的国际化思路

程序的国际化一般涉及到编码和翻译两个概念. 其中编码一般采用UTF8编码标准, Go语言已经完美支持. 而目前常见翻译技术是Qt的tr函数和GNU gettext提供的gettext函数, 另外微软的MFC也有自己的多国语言支持方式.

Go语言目前还没有标准的多国语言翻译方式. 不过笔者已经初步将gettext的运行时环境移植到了Go语言(采用纯Go实现, 无其他依赖).

Go语言版的gettext名字为gettext-go, 项目地址在: http://code.google.com/p/gettext-go.

gettext-go 同时也借鉴了 Qt 的翻译上下文特性. 在 GNU gettext 的 pomo 翻译文件中都是含有 msgctxt 上下文信息的, 但是 C/C++ 的翻译接口函数并没有上下文的参数, 因此 传统的 gettext 函数没有设置上下文的参数.

可以去 godoc.orggowalker.org 查看 gettext-go 的文档.

Go语言的多国语言支持

基于 gettext-go , 我们可以很容易给Go程序增加多国语言的支持:

package main

import (
	"fmt"

	"code.google.com/p/gettext-go/gettext"
)

func main() {
	gettext.BindTextdomain("hello", "local", nil)
	gettext.Textdomain("hello")

	fmt.Println(gettext.Gettext("Hello, world!"))
}

其中 gettext.BindTextdomain 是绑定翻译的空间, 其中 "hello" 是对应翻译一类信息的翻译, "local" 为翻译文件的所在路径(这里当前目录下的"local")子目录.

按照 GNU gettext 的习惯, 简体中文对应的翻译文件为 "local/zh_CN/LC_MESSAGES/hello.mo". 不同语言的命名有一个国际规范, 比如繁体中文对应"zh_TW", 美国英文对应"en_US"等等. 但是 gettext-go 对名字并没有特殊的要求.

gettext.BindTextdomain 可以绑定多个翻译空间, 但是同一个时刻只能使用一个翻译空间.

这里我们使用 gettext.Textdomain 指定当前的翻译空间为 "hello" .

运行新的程序程序, 发现输出还是: "Hello, world!".

这是因为缺少翻译文件...

生成翻译文件

未来, gettext-go 会开发一个 GNU gettext 工具集 中 的 xgettext 类似工具, 用于从程序中提取要翻译的字符串.

不过目前, 我们只能手工支持翻译文件了(还好这个例子只有一个字符串需要翻译).

创建 "local/zh_CN/LC_MESSAGES/hello.po" 文件, 内容如下:

msgid ""
msgstr ""

msgctxt "main.main"
msgid "Hello, world!"
msgstr "你好, 世界!"

保存为UTF8编码格式.

然后用 GNU gettext 工具集中的 msgfmt 命令将 hello.po 文件编译为 hello.mo 文件(注:如果缺少mo文件的话,也尝试用同名的po文件代替.):

msgfmt -o hello.mo hello.po

如果是Windows用户, 可以下载 poedit 翻译工具. 然后用 poedit 打开 hello.po 文件, 点击保存后会自动生成 hello.mo 文件(也是poedit的bin目录下自带的msgfmt 命令生成的).

重新运行新的程序程序, 还是输出: "Hello, world!" ?

本地的语言环境

在上一节, 我们已经制作了简体中文的翻译文件 "local/zh_CN/LC_MESSAGES/hello.mo", 然后输出依然是英文.

这是因为 gettext-go 翻译时不仅要依赖对应语言的翻译文件, 还需要知道要范围为哪种语言(和网上翻译类似, 需要知道翻译的目标语言).

如果没有指定翻译语言, gettext-go 会尝试获取本地的默认语言环境, 主要是通过检查 $(LC_MESSAGES)$(LANG) 两个环境变量. 如果两个环境变量都没有设置, 那么默认是不进行翻译的.

我们设置环境变量后重新运行程序(Windows):

set LANG=zh_CN
go run hello.go

这里时候应该可以输出中文了.

动态切换语言

如果不想使用默认的本地语言环境, 也可以用 gettext.SetLocale 接口设置本地语言环境.

func main() {
	gettext.BindTextdomain("hello", "local", nil)
	gettext.Textdomain("hello")

	// 切换到简体中文
	gettext.SetLocale("zh_CN")
	fmt.Println(gettext.Gettext("Hello, world!"))
	// 切换到繁体中文
	gettext.SetLocale("zh_TW")
	fmt.Println(gettext.Gettext("Hello, world!"))
}

这样可以根据需要采用合适的语言翻译文件.

翻译的上下文

Go语言版的 gettext-go 的每个 gettext.Gettext 调用都有一个隐含的上下文信息(如果想自己指定上下文可以使用gettext.PGettext).

默认的上下文为包含gettext.Gettext调用的函数名称, 比如:

  • 如果是main包的全局函数初始化调用, 则为 main.init
  • 如果是main包的init函数调用, 则为 main.init
  • 如果是main包的main函数调用, 则为 main.main
  • 如果是main包中的闭包调用, 则为 main.func
  • 如果是非main包的函数, 则还需要包含包的完全路径名

上下文对应Go的运行时调用者名称, 具体实现在这里: caller.go .

练习题

  1. 给前面的程序增加 繁体/日文/韩文/克林贡语 等语言的支持
  2. 增加一个 -local 参数, 用于设置本地语言
  3. 提交改进建议或其他反馈意见

https://chai2010.cn/

© 著作权归作者所有

共有 人打赏支持
chai2010

chai2010

粉丝 414
博文 99
码字总数 81410
作品 10
武汉
程序员
加载中

评论(17)

O
Orzogc

引用来自“chai2010”的评论

引用来自“Orzogc”的评论

引用来自“chai2010”的评论

引用来自“Orzogc”的评论

上下文信息不能支持指定文件行数这种方式吗?这样的话好像可以直接用xgettext生成pot吧

不支持. 文件行数容易变化, 而且不同的源文件的行数很容易重名.
我觉得采用pkg路径和函数名组合作为上下文是最靠谱的方式.
(在Qt中, 是采用的类的名字作为上下文)

我的意思是指定文件及其行数,然后gettext会搜索指定位置附近范围的字符串。
或者上下文干脆只用指定文件,然后gettext搜索整个文件。
这样的话就可以用xgettext生成pot。

对于po文件, 注释部分是支持文件名和行号的:
http://gowalker.org/code.google.com/p/gettext-go/gettext/po#Comment

但是我觉得用作Go的上下文就不太适合了.
因为Go的pkg路径组合函数名就基本保证了唯一性了(init和闭包除外).
而且按照Go的习惯, pkg内文件名的调整和行号的变化都是不应该影响运行结果的
(因为语法树结构并没有发送变化).

当然, 目前缺少xgettext工具的支持是一个问题.
我计划以后做一个针对Go语言的xgettext类似的工具.
因为Go语言除了运行时翻译需求外, godoc的文档提取也是一个需求(估计xgettext不会支持这个特性的).
我希望提前工具能够支持Go语言的文档提取,
这样也便于以后中文文档的翻译.

感谢你的反馈, 新年快乐 :)

新年快乐0
chai2010
chai2010

引用来自“Orzogc”的评论

引用来自“chai2010”的评论

引用来自“Orzogc”的评论

上下文信息不能支持指定文件行数这种方式吗?这样的话好像可以直接用xgettext生成pot吧

不支持. 文件行数容易变化, 而且不同的源文件的行数很容易重名.
我觉得采用pkg路径和函数名组合作为上下文是最靠谱的方式.
(在Qt中, 是采用的类的名字作为上下文)

我的意思是指定文件及其行数,然后gettext会搜索指定位置附近范围的字符串。
或者上下文干脆只用指定文件,然后gettext搜索整个文件。
这样的话就可以用xgettext生成pot。

对于po文件, 注释部分是支持文件名和行号的:
http://gowalker.org/code.google.com/p/gettext-go/gettext/po#Comment

但是我觉得用作Go的上下文就不太适合了.
因为Go的pkg路径组合函数名就基本保证了唯一性了(init和闭包除外).
而且按照Go的习惯, pkg内文件名的调整和行号的变化都是不应该影响运行结果的
(因为语法树结构并没有发送变化).

当然, 目前缺少xgettext工具的支持是一个问题.
我计划以后做一个针对Go语言的xgettext类似的工具.
因为Go语言除了运行时翻译需求外, godoc的文档提取也是一个需求(估计xgettext不会支持这个特性的).
我希望提前工具能够支持Go语言的文档提取,
这样也便于以后中文文档的翻译.

感谢你的反馈, 新年快乐 :)
O
Orzogc

引用来自“chai2010”的评论

引用来自“Orzogc”的评论

上下文信息不能支持指定文件行数这种方式吗?这样的话好像可以直接用xgettext生成pot吧

不支持. 文件行数容易变化, 而且不同的源文件的行数很容易重名.
我觉得采用pkg路径和函数名组合作为上下文是最靠谱的方式.
(在Qt中, 是采用的类的名字作为上下文)

我的意思是指定文件及其行数,然后gettext会搜索指定位置附近范围的字符串。
或者上下文干脆只用指定文件,然后gettext搜索整个文件。
这样的话就可以用xgettext生成pot。
chai2010
chai2010

引用来自“Orzogc”的评论

上下文信息不能支持指定文件行数这种方式吗?这样的话好像可以直接用xgettext生成pot吧

不支持. 文件行数容易变化, 而且不同的源文件的行数很容易重名.
我觉得采用pkg路径和函数名组合作为上下文是最靠谱的方式.
(在Qt中, 是采用的类的名字作为上下文)
O
Orzogc
上下文信息不能支持指定文件行数这种方式吗?这样的话好像可以直接用xgettext生成pot吧
chai2010
chai2010
已经支持资源文件的翻译和zip数据(可内嵌到程序中), 具体可参考这个文件:
http://code.google.com/p/gettext-go/source/browse/gettext/gettext_test.go

@羊半仙
狗头666
狗头666

引用来自“chai2010”的评论

引用来自“羊半仙”的评论

程序的国际化可能还要考虑资源文件的翻译,根据不同语言环境加载不同的图片、语音等等

计划增加一个 Getdata 函数, 用于支持资源文件的翻译.
以后domain还计划增加对zip文件和内置的zip数据的支持.
主要涉及 BindTextdomain 和 Getdata 两个函数:
http://gowalker.org/code.google.com/p/gettext-go/gettext#BindTextdomain
http://gowalker.org/code.google.com/p/gettext-go/gettext#Getdata

Good news,这个项目真的很超前.....
chai2010
chai2010

引用来自“羊半仙”的评论

程序的国际化可能还要考虑资源文件的翻译,根据不同语言环境加载不同的图片、语音等等

计划增加一个 Getdata 函数, 用于支持资源文件的翻译.
以后domain还计划增加对zip文件和内置的zip数据的支持.
主要涉及 BindTextdomain 和 Getdata 两个函数:
http://gowalker.org/code.google.com/p/gettext-go/gettext#BindTextdomain
http://gowalker.org/code.google.com/p/gettext-go/gettext#Getdata

chai2010
chai2010

引用来自“苗哥”的评论

为什么这里的资源文件不能使用Java中的properties文件那种key-value格式呢,这样移植或者维护起来不是更加简单,用mo格式的资源文件,每次还要多壹個步骤来生成新的资源文件。

采用的文件格式没有统一的标准. 用mo只是因为格式比较简单, 而且poedit也足够用(比Qt的翻译工具轻量很多).
如果只是key-value, 信息量还不如mo文件, 因为mo文件支持上下文和复数.
如果只是为了维护简单, 用po文件就可以了(以后版本会在mo缺少时用对应的po代替).
苗哥
苗哥
为什么这里的资源文件不能使用Java中的properties文件那种key-value格式呢,这样移植或者维护起来不是更加简单,用mo格式的资源文件,每次还要多壹個步骤来生成新的资源文件。
Struts2的国际化,中英文切换

几年之前,应用程序开发者能够考虑到仅仅支持他们本国的只使用一种语言(或者有时候是两种)和通常只有一种数量表现方式(例如日期、数字、货币值)的应用。然而,基于web技术的应用程序的爆...

长平狐
2013/01/06
5.4K
1
gettext-go

Gettext 用于系统的国际化(I18N)和本地化(L10N),可以在编译程序的时候使用本国语言支持(Native Language Support(NLS)),其可以使程序的输出使用用户设置. 而 gettext-go 是完全采用 Go 语言实...

chai2010
2014/01/01
468
0
Gettext 0.19.7 发布下载,国际化语言支持库

Gettext 0.19.7 发布下载:http://ftp.gnu.org/pub/gnu/gettext/gettext-0.19.7.tar.xz Gettext 用于系统的国际化(I18N)和本地化(L10N),可以在编译程序的时候使用本国语言支持(Native Langu...

oschina
2015/12/28
1K
0
Django 基于 gettext 的国际化

Django 框架具有很好的 I18N 和 L10N 的支持,其实现是基于 GNU 的 gettext。 设置 在项目的 文件中进行设置: 创建及编译语言文件 Django 为我们提供了工具, 用来自动创建和维护这些文件。...

与蟒唯舞
06/07
0
0
Go语言的国际化支持(资源文件翻译)

在之前的 Go语言的国际化支持(基于gettext-go) 中, 讲到了如何翻译源代码中的字符串. 项目地址在: http://code.google.com/p/gettext-go. 文档在 godoc.org 或 gowalker.org . 根据评论的反馈...

chai2010
2014/01/27
0
0
gettext 0.18.2 发布,国际化语言支持库

gettext 0.18.2 支持 Guile 2.0 块注释,支持更新 automake 的 autopoint,移植性方面的问题修复。 Gettext 用于系统的国际化(I18N)和本地化(L10N),可以在编译程序的时候使用本国语言支持(Na...

oschina
2012/12/27
1K
1
Django 国际化实例及原理分析

当 Web 服务搭建好以后,可以接收来自全球不同国家用户访问。这样就要求开发人员调整软件,使之能适用于不同的语言,即国际化和本地化。国际化 -- Internationalization,i 和 n 之间有 18 个...

红薯
2011/02/03
5.3K
9
Java程序员从笨鸟到菜鸟之(六十六)细谈struts2(十二)struts2国际化底层大揭秘

Struts2的博客在前不久已经停止了,但是里面还有很多内容我们都还没接触到,所以现在我们在补充一下struts2的内容。这篇博客我们主要是一块来看一下struts2内对国际化的支持。在了解struts2...

长平狐
2012/11/12
95
0
浅析 Linux 的国际化与本地化机制

Linux 是一个国际化的操作系统,它的工具集和设备驱动程序均支持多语言操作。本文通过分析 glibc 中实现国际化和本地化机制的函数和命令工具集以及从程序开发者、翻译者和用户等多角度浅析国...

红薯
2009/12/24
1K
0
国际化语言支持库--gettext

Gettext 用于系统的国际化(I18N)和本地化(L10N),可以在编译程序的时候使用本国语言支持(Native Language Support(NLS)),其可以使程序的输出使用用户设置的语言而不是英文....

匿名
2008/09/19
4.3K
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring基础

Spring是什么? Spring是一个开源框架,最早由Rod Johnson创建,它解决的是业务逻辑层和其他各层的松耦合问题。 经过十几年的发展,Spring正在扩展其他的领域,如:移动开发、社交API集成、N...

这很耳东先生
2分钟前
0
0
面试系列-40个Java多线程问题总结

前言 这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题。 这些多线程的问题,有些来源于各大网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也...

Ryan-瑞恩
15分钟前
0
0
微信分享的细节

分享的缩略图要求: 一、图片大小小于32k 二、图片的尺寸为 宽度 :128px 高度:128px 分享title 和 description 出现金额等 以上情况存在会导致触发分享按钮 但是页面没有反应...

Js_Mei
21分钟前
0
0
【2018.07.23学习笔记】【linux高级知识 Shell脚本编程练习】

1、编写shell脚本,计算1-100的和; #!/bin/bashsum=0for i in `seq 1 100`do sum=$[$sum+$i]doneecho $sum 2、编写shell脚本,要求输入一个数字,然后计算出从1到输入数字的和,要求...

lgsxp
24分钟前
0
0
xss攻防浅谈

导读 XSS (Cross-Site Script) 攻击又叫跨站脚本攻击, 本质是一种注入攻击. 其原理, 简单的说就是利用各种手段把恶意代码添加到网页中, 并让受害者执行这段脚本. XSS能做用户使用浏览器能做的...

吴伟祥
24分钟前
0
0
js回调的一次应用

function hideBtn(option) { if (option == 1) { $("#addBtn").hide(); $("#addSonBtn").hide(); }}$("body").on("click", "#selectBtn", function () {......

晨猫
30分钟前
0
0
C++_读写ini配置文件

1.WritePrivateProfileString:

一个小妞
30分钟前
0
0
通往阿里,BAT的50+经典Java面试题及答案解析(上)

Java是一个支持并发、基于类和面向对象的计算机编程语言。下面列出了面向对象软件开发的优点: 代码开发模块化,更易维护和修改。 代码复用。 增强代码的可靠性和灵活性。 增加代码的可理解性...

Java大蜗牛
30分钟前
1
0
数据库两大神器【索引和锁】

前言 只有光头才能变强 索引和锁在数据库中可以说是非常重要的知识点了,在面试中也会经常会被问到的。 本文力求简单讲清每个知识点,希望大家看完能有所收获 声明:如果没有说明具体的数据库...

Java3y
34分钟前
0
0
Application Express安装

Application Express安装文档 数据库选择和安装 数据库选择 Oracle建议直接12.2.0.1.0及以上的版本,12.1存在20618595bug(具体可参见官方文档) Oracle 12c 中安装oracle application expr...

youfen
46分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部