Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I18n strategies for Go with App Engine

Tags:

Not necessarily specific to GAE I suppose, but I'm curious as to what people are using to translate or localise their web applications.

My own approach I'm afraid is hopelessly naive, really just a hand-wave at the issue by loading an entity from the datastore for each package based on a locale value recorded in the user's profile. At least this allows translations of a few strings to be provided:

package foo

...

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

Store with a string id corresponding to a locale, then load to Gorilla context or similar:

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

Which is obviously incredibly limited, but at least makes strings available from calling code via context.Get(r, foo.Messages). Can anyone point me at more useful implementations, or suggest a better approach?

Edit (relevant but not completely useful):

  • gettext: a MO file parser
  • go-18n
  • Internationalization plan for Go
  • Polyglot
like image 606
Rich Churcher Avatar asked Jan 02 '13 15:01

Rich Churcher


People also ask

What is i18n in Javascript?

Internationalization (I18N) is the process of designing and preparing software products (apps) to support multiple locales, languages, and regions. By internationalizing a codebase, developers and businesses can expand their user base and access a wider audience.

What is i18n localization?

Internationalization (sometimes shortened to "I18N , meaning "I - eighteen letters -N") is the process of planning and implementing products and services so that they can easily be adapted to specific local languages and cultures, a process called localization .

What is i18n in node JS?

Lightweight simple translation module with dynamic JSON storage. Supports plain vanilla Node. js apps and should work with any framework (like Express, restify and probably more) that exposes an app. use() method passing in res and req objects.


1 Answers

Jonathan Chan points out Samuel Stauffer's go-gettext which seems to do the trick. Given the directories:

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

Start with (assumes *nix):

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

Source preparation cannot use the _("String to be translated") short form, due to underscore's special characteristics in Go. You can tell xgettext to look for the camelcase function name "GetText" using the -k flag.

Minimal working example:

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

Create the template with:

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

Note -k, without it there'll be no output as xgettext won't recognise calls to GetText. Edit relevant strings, email etc in appname.pot. Let's assume we're localising for French:

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

Edit french.po:

# Appname l10n
# Copyright (C) 2013 Wombat Inc
# This file is distributed under the same license as the appname package.
# Wombat <[email protected]>, 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 <[email protected]>\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."

Generate the binary (the file that'll actually get deployed with the app):

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

Final directory structure:

~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

(locale directory under go-gettext holds test data, could be removed for deployment.)

If all goes well, a visit to appname should display "Oui."

like image 162
Rich Churcher Avatar answered Sep 28 '22 05:09

Rich Churcher