21

我想不一定特定于 GAE,但我很好奇人们使用什么来翻译或本地化他们的 Web 应用程序。

恐怕我自己的方法太天真了,实际上只是通过根据用户配置文件中记录的语言环境值从数据存储中为每个包加载一个实体来解决这个问题。至少这允许提供一些字符串的翻译:

package foo

...

type Messages struct {
    Locale string
    ErrorDatastore string
    LoginSuccessful string
    ...
}

使用对应于语言环境的字符串 id 存储,然后加载到 Gorilla 上下文或类似内容:

const Messages ContextKey = iota

...

k := datastore.NewKey(c, "Messages", "en_US", 0, nil)
m := new(Messages)
if err := datastore.Get(c, k, m); err != nil {
    ...
} else {
    context.Set(r, Messages, m)
}

这显然是非常有限的,但至少可以通过 context.Get(r, foo.Messages) 从调用代码中获取字符串。谁能指出我更有用的实现,或提出更好的方法?

编辑(相关但不完全有用):

4

3 回答 3

9

Jonathan Chan指出Samuel Stauffer 的 go-gettext似乎可以解决问题。给定目录:

~appname/
 |~app/
 | `-app.go
 |+github.com/
 `-app.yaml

从(假设 *nix)开始:

$ cd appname
$ git clone git://github.com/samuel/go-gettext.git github.com/samuel/go-gettext

由于下划线在 Go 中的特殊特性,源准备不能使用 _("String to be translate") 短格式。您可以使用 -k 标志告诉 xgettext 查找驼峰式函数名称“GetText”。

最小的工作示例:

package app

import (
    "fmt"
    "log"
    "net/http"

    "github.com/samuel/go-gettext"
)

func init () {
    http.HandleFunc("/", home)
}

func home(w http.ResponseWriter, r *http.Request) {
    d, err := gettext.NewDomain("appname", "locale")
    if err != nil {
        log.Fatal("Failed at NewDomain.")
    }

    cat := d.GetCatalog("fr_FR")
    if cat == gettext.NullCatalog {
        log.Fatal("Failed at GetCatalog.")
    }

    fmt.Fprintf(w, cat.GetText("Yes."))
}

使用以下命令创建模板:

$ xgettext -d appname -kGetText -s -o appname.pot app/app.go

注意 -k,没有它就没有输出,因为 xgettext 不会识别对 GetText 的调用。在 appname.pot 中编辑相关字符串、电子邮件等。假设我们正在本地化法语:

$ mkdir -p locale/fr_FR/LC_MESSAGES
$ msginit -l fr_FR -o french.po -i appname.pot

编辑法语.po:

# Appname l10n
# Copyright (C) 2013 Wombat Inc
# This file is distributed under the same license as the appname package.
# Wombat <wombat@example.com>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: appname v0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-13 11:03+1300\n"
"PO-Revision-Date: 2013-01-13 11:10+1300\n"
"Last-Translator: Rich <rich@example.com>\n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: app/app.go:15
msgid "Yes."
msgstr "Oui."

生成二进制文件(将实际与应用程序一起部署的文件):

$ msgfmt -c -v -o locale/fr_FR/LC_MESSAGES/appname.mo french.po

最终目录结构:

~appname/
 |~app/
 | `-app.go
 |~github.com/
 | `~samuel/
 |   `~go-gettext/
 |     +locale/
 |     |-catalog.go
 |     |-domain.go
 |     `-mo.go
 |~locale/
 | `~fr_FR/
 |   `LC_MESSAGES/
 |    `-appname.mo 
 `-app.yaml

(go-gettext 下的 locale 目录包含测试数据,可以删除以进行部署。)

如果一切顺利,访问 appname 应该会显示“Oui”。

于 2013-01-12T23:11:16.513 回答
4

go-i18n是一个具有一些不错功能的替代包:

于 2014-01-30T06:37:30.410 回答
1

GNU Gettext 作为 i18n 解决方案的事实标准被广泛采用。

要直接从您的 Go 项目中使用 .po 文件并将所有翻译加载到内存中以获得更好的性能,您可以使用我的包:https ://github.com/leonelquinteros/gotext

这相当简单直接。

因此,给定一个 default.po 文件(在 GNU gettext 之后格式化:https ://www.gnu.org/software/gettext/manual/html_node/PO-Files.html ),/path/to/locales/es_ES/default.po您可以使用这个包加载它并开始使用立即翻译:

import "github.com/leonelquinteros/gotext"

func main() {
    // Configure package
    gotext.SetLibrary("/path/to/locales")
    gotext.SetLanguage("es_ES")

    // Translate text from default domain
    println(gotext.Get("Translate this text"))
}

如果您希望在字符串中定义翻译以便更“集中”使用,您可以使用 Po 对象解析 PO 格式的字符串:

import "github.com/leonelquinteros/gotext"

func main() {
    // Set PO content
    str := `
msgid "One apple"
msgstr "Una manzana"

msgid "One orange"
msgstr "Una naranja"

msgid "My name is %s"
msgstr "Mi nombre es %s"
`

    // Create Po object
    po := new(Po)
    po.Parse(str)

    // Get a translated string
    println(po.Get("One orange"))

    // Get a translated string using variables inside the translation
    name := "Tom"
    println(po.Get("My name is %s", name))
}

正如您在最后一个示例中看到的,也可以在翻译字符串中使用变量。

虽然大多数解决方案都非常相似,包括您的解决方案,但使用通用格式作为 gettext 可以带来一些额外的好处。

此外,您的解决方案似乎对于并发使用并不安全(当从多个 goroutine 使用时)。这个包为你处理所有这些。包也有单元测试,欢迎贡献。

于 2016-06-19T22:49:41.757 回答