2013-01-02 12 views
21

Non necessariamente specifico per GAE Suppongo, ma sono curioso di sapere che cosa le persone stanno usando per tradurre o localizzare le loro applicazioni web.Strategie I18n per Go with App Engine

Il mio approccio temo è irrimediabilmente ingenuo, in realtà solo una mano al problema caricando un'entità dall'archivio dati per ogni pacchetto in base a un valore di locale registrato nel profilo dell'utente. Almeno questo permette traduzioni di alcune stringhe che devono essere fornite:

package foo 

... 

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

Store con un ID stringa corrispondente ad un locale, quindi caricare al contesto Gorilla o simili:

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

che ovviamente è incredibilmente limitata , ma almeno rende le stringhe disponibili dal chiamare il codice tramite context.Get (r, foo.Messages). Qualcuno può indicarmi implementazioni più utili o suggerire un approccio migliore?

Edit (rilevanti, ma non del tutto utile):

+3

Per quanto riguarda la modifica suggerita: mi riservo il diritto di scrivere "localizzazione" [modo corretto] (http://en.wikipedia.org/wiki/American_and_British_English_spelling_differences#-ise.2C_-ize_.28-isation.2C_-ization .29). Ehi, almeno ho usato 'z' quando taggati! ;) –

+1

Che tipo di funzionalità ti aspetti da una "implementazione più utile"? Per la maggior parte delle applicazioni i18n, gli archivi a valore-chiave (con forse alcuni codici di formato) dovrebbero funzionare correttamente. –

+2

Beh, credo che qualcosa abbia permesso ai traduttori di fare il loro lavoro più facilmente. Ero curioso di sapere se c'erano porti da fare per altri approcci popolari (gettext, ecc.) Che avrei potuto perdere. In questo momento sto mettendo insieme un semplice modulo usando [il formato JSON di chrome.i18n] (http://developer.chrome.com/extensions/i18n.html), che posterò come risposta se nulla di più completo si materializza. –

risposta

9

Jonathan Chan punti su Samuel Stauffer'sgo-gettext che vedi ms per fare il trucco. Date le directory:

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

Inizia con (assume * nix):

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

preparazione Fonte non può utilizzare il _ ("Stringa da tradurre") forma abbreviata, due to underscore's special characteristics in Go. Puoi dire a xgettext di cercare il nome della funzione Camelcase "GetText" usando l'opzione -k.

minimo esempio di lavoro:

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

Creare il modello con:

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

Nota -k, senza di essa non ci sarà alcuna uscita come xgettext non riconoscerà chiamate a GetText. Modifica stringhe rilevanti, email ecc in appname.pot. Supponiamo che stiamo localizzando per il francese:

$ 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." 

Generare il binario (il file che verrà effettivamente ottenere distribuito con l'applicazione):

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

finale struttura di directory:

~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 

(la directory locale sotto go-gettext contiene i dati di test, potrebbe essere rimossa per la distribuzione.)

Se tutto va bene, una visita ad appname dovrebbe visualizzare "Oui".

+0

Le correzioni sono benvenute, questo è un nuovo territorio per me. –

3

go-i18n è un pacchetto alternativo con alcune caratteristiche:

+0

L'implementazione di go-i18n è sicura per l'uso simultaneo? Questo è importante qui perché l'OP vuole usarlo in un servizio web go. Presumo che ogni query debba utilizzare una nuova istanza di oggetto di go-i18n configurata dai cookie per la lingua dell'utente? – Frankenstein

+1

@Frankenstein non c'è sincronizzazione all'interno di go-i18n; tuttavia, la sincronizzazione non dovrebbe essere necessaria. Dovresti caricare le traduzioni in go-i18n durante la fase di inizializzazione del tuo webserver; durante init() o prima di aver iniziato a servire le richieste http. Dopodiché, lo stato viene letto solo in modo che non sia necessaria alcuna sincronizzazione. –

-1

GNU Gettext è ampiamente adottato come standard di fatto per le soluzioni i18n.

Per utilizzare .po file direttamente dal vostro progetto Go e caricare tutte le traduzioni in memoria per migliorare le prestazioni, è possibile utilizzare il mio pacchetto: https://github.com/leonelquinteros/gotext

E 'abbastanza semplice e direttamente al punto.

Quindi, dato un file di default.po (formattato dopo GNU gettext: https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) si trova nella /path/to/locales/es_ES/default.po è possibile caricare utilizzando questo pacchetto e iniziare a consumare le traduzioni subito:

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

Se si preferisce avere le traduzioni definiti in una stringa per una più "concentrati" l'uso, è possibile analizzare una stringa formattata PO con un oggetto 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)) 
} 

Come si può vedere l'ultimo esempio, è anche possibile utilizzare le variabili all'interno del translat corde di ioni.

Mentre la maggior parte delle soluzioni sono praticamente simili, incluso il vostro, l'utilizzo di un formato comune come gettext può apportare alcuni vantaggi aggiuntivi.

Inoltre, la soluzione non sembra essere sicura per l'uso concomitante (se consumata da diverse goroutine). Questo pacchetto gestisce tutto ciò che per te. Ci sono anche test unitari per il pacchetto e i contributi sono benvenuti.