Coder Social home page Coder Social logo

pogo's Introduction

pogo

...provides gettext-like internationalization for golang

Why?

...because most of the major, recurrent challenges in internationalizing software have been addressed by GNU almost twenty years ago and golang is badly lacking in this functionality. Gettext enjoys extremely wide adoption, established toolchains and great popularity among both translators and programmers. It's simple. It provides for better separation of localization from the rest of your architecture, allows non-programmers to translate your software, eliminates ugly, unmanageable JSON files and, when used properly, effectively solves the problems of context and grammatical differences in pluralization. Until something better comes along, it just doesn't make much sense to reinvent the square wheel. This project is meant to be a loose interpretation replicating the most useful functionality and making use of common po/mo standards.

Features

  • i18n in your templates, not crammed into them as an afterthought
  • Simple CLI package to extract strings in lieu of xgettext
  • Built-in support for pluralization rules, from Alcholi to Yoruba
  • Built-in support for context fields

Install

$ go get github.com/Sam-Izdat/pogo

Status

It's still very raw. A pre-release is available. Contributions and bug reports are most welcome.

License MIT GoDoc

Getting started

If your GOPATH and GOBIN environment variables were set correctly, you should now be able to run pogo from any directory. This little CLI package will scan your project for calls pogo's gettext-ish functions and compile string literals into po files. The first thing is to initialize it in the main project directory and set up a configuration file.

$ cd github.com/MyStuff/MyProject
$ pogo init

Now, edit the top few settings in the generated POGO.toml file, describing the translation project and providing a list of target locales. You can specify an arbitrary name and base filename for the set of catalogs (.pot/.po files) covered by this pogo project. For example:

targets             = ["en_US", "en_GB", "ja"]
project_name        = "My Application User Control Panel"
project_filename    = "my_app_user_cp"

or

targets             = ["en_US", "en_GB", "ja"]
project_name        = "My Application Administrator Control Panel"
project_filename    = "my_app_admin_cp"

That's it. There are no domains, as such. When pogo walks the directory it'll search until it bumps into another POGO.toml file somewhere; if it does, that subdirectory will be ignored and left to another configuration and collection of catalogs. If needed, an application's translation files can be compartmentalized by packages, or just by separate directories of views.

Putting it to use

Here's a basic webserver that can be found in the example folder.

package main
    
import (
    "fmt"
    "net/http"
    "html/template"
    "path/filepath"
    "github.com/Sam-Izdat/pogo/translate"
)

// Load your POGO.toml configuration file *before* processing the request
var POGO = translate.LoadCfg("github.com/MyStuff/MyProject")

func handler(w http.ResponseWriter, r *http.Request) {
    // Grab a translator in your request flow
    var T = POGO.New("ru") // "ru" is Russian

    // Set up some data for the template
    var bottles []int
    for i := 99; i >= 0; i-- { bottles = append(bottles, i) }

    data := struct {
        T translate.Translator  // Throw a translator at the template
        Title string
        Bottles []int
    } {
        T,                      // The Russian translator above
        T.G("Internationalization Example"), // Translate outside template
        bottles,
    }

    lp := filepath.Join("views", "layout.html")
    fp := filepath.Join("views", "index.html")
    templates := template.Must(template.ParseFiles(lp, fp))
    templates.ExecuteTemplate(w, "layout", data)
}

func main() {
    // Point to a handler and serve
    port := ":8383"
    fmt.Println("Serving on port", port)
    http.HandleFunc("/", handler)
    http.ListenAndServe(port, nil)
}

Translating strings

By default, a pogo "translator" exports four methods called:

  • G() - for basic translation (roughly equivalent to gettext() or _())
  • PG() - for translation with context (pgettext())
  • NG()- for translation with quantities (ngettext())
  • NPG() - for translation with both quantities and context (npgettext())

It will be expected by the CLI program that the above are reserved for pogo in .go files (as function and method names) and in templates (as function, method and variable names). If you would like something more verbose with less chance conflict/collision, just alias these methods and edit the POGO.toml file for the scanner, to reflect the new names.

String literals can be queued up for translation directly in your go files but, chances are, most of the content to be translated will reside in templates. Passing the translator to a template as above now lets you do this:

G - just translate

{{.T.G "A robot?!"}}

All of these functions are variadic and will take an arbitrary number of arguments. G() will pass along all subsequent arguments to fmt.Sprintf(), often eliminating the need to nest it in another function:

{{.T.G "I seem to have lost %s" "my shoes"}}

The words "my shoes" above will be translated if a translation is available.

PG - context

PG() allows you to provide a context for the translator, which is passed as the first argument:

{{.T.PG "childrens' game" "tag"}}
{{.T.PG "category or identifier" "tag"}}
{{.T.PG "the price on an item" "tag"}}

NG - plurals

NG() is for anything that may vary in quantity:

{{.T.NG "You have %d new message" "You have %d new messages" .ctMsg}}

The first argument must be the singular form of the string to be translated; the second must be the plural. The very last argument must be the quantity of the thing, which will be used to pull up a locale-specific pluralization rule. Anything in between will be interpolated.

{{.T.NG "I ate %[2]d muffin %[1]s" "I ate %[2]d muffins %[1]s" "yesterday" .ctMuffins}}

NPG - plurals & context

NPG() takes at least four arguments, combining the purposes of PG() and NG():

{{.T.NPG "sternly!" "Delete %d file?" "Delete %d files?" .ctFiles}}

The first argument is context, then singular, then plural, then zero or more other arguments, then the quantity.

Use a sigil ($) to access T while ranging over something or within conditional statements:

{{range .Bottles}}
<p>{{$.T.NG "%[2]d bottle of beer %[1]s!" "%[2]d bottles of beer %[1]s!" "on the wall" (.)}}</p>
{{end}}

There's really not much more to it.

Mos, pos and pots and other things

Now that there's some stuff to be translated you can compile the po files. One file with the extension "pot" will serve as the original template and every locale will have its own "po" file (catalog) containing the actual translations. Editors like Poedit can merge these catalogs with any new messages added to the template.

$ cd github.com/MyStuff/MyProject
$ mkdir locale
$ pogo build pot

...will compile the template...

$ pogo build po

...will produce individual po files for all your targets with some meta-data already in place. Whenever you have new strings to translate, just run pogo build -o pot again. It does roughly what xgettext does. Currently pogo does not compile mo files and leaves that up the fancy editors.

On the to-do list

  • Unit tests
  • Better documentation
  • General code cleanup
  • "pogo build mo" CLI command
  • "pogo status" CLI command
  • HTML UI stats, previews

Attributions

built upon:

License

MIT for pogo; see dependencies' docs for their respective licenses

pogo's People

Contributors

sam-izdat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pogo's Issues

More info on configuration

Hi, this is a very nice and useful project, many thanks.
I think the config file needs some clarification, without assuming too much knowledge of GNU Gettext.
For example the project_filename field needs some info.
It this used both from the pogo executable to fetch and generate the translations and from the lib when asking to correctly identify the translation domain and file... is this correct?

Default output if no correct language specified

Still experimenting with it, but il looks pogo will always get the first available translation if it is told a wrong language. As labels in my templates are in english I'm adding "en" to the languages and made sure to call it with a correct and existing language. I believe the correct way to handle this, reading gettext docs, should be to just output the string untranslated.

Performance

I am speculating on how much does it takes to obtain a new translator for each route handler, it they can be safely created once per language and then shared amongst handlers, and also if it's possible to pass a &T instead of T in the templates execution contexts. Could be nice to have the package assemble a map of translator for each language specified in POGO.toml. Anyway, I'll comment more on performance after some profiling.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.