How to manage Translations in Golang Projects

Golang Translation i18n

Recently the author has been building a fully internationalized (i18n) and localized (l10n) web application for the first time with Go’s golang.org/x/text packages. I’ve found that the packages and tools that live under golang.org/x/text are really effective and well designed, although it’s been a bit of a challenge to figure out how to put it all together in a real application.

In this tutorial I want to explain how you can use golang.org/x/text packages to manage translations in your application. Specifically:

  • How to use the golang.org/x/text/language and golang.org/x/text/message packages to print translated messages from your Go code.
  • How to use the gotext tool to automatically extract messages for translation from your code into JSON files.
  • How to use gotext to parse translated JSON files and create a catalog containing translated messages.
  • How to manage variables in messages and provided pluralized versions of translations.

Note: Just in case you’re not already aware, the packages that live under golang.org/x are part of the official Go Project but outside the main Go standard library tree. They are held to looser standards that the standard library packages, which means they aren’t subject to the Go compatibility promise (i.e. their APIs might change), and documentation may not always be complete.

What we’ll be build with Golang

To help put this into context, we’re going to create a simple pre-launch website for an imaginary online bookstore. We’ll start off slowly and build up the code step-by-step.

Our application will have just a single home page, and we’ll localize the page content based on a locale identifier at the start of the URL path. We’ll set up our application to support three different locales: the United Kingdom, Germany, and the French-speaking part of Switzerland.

URLLocalized for
localhost:4018/en-gbUnited Kingdom
localhost:4018/de-deGermany
localhost:4018/fr-chSwitzerland (French-speaking)

We will follow a standard convention and use BCP 47 language tags as the locale identifier in our URLs. Simplifying things hugely for the sake of this tutorial, BCP 47 language tags typically take the format {language}-{region}. The language part is a ISO 639-1 code and the region is a two-letter country code from ISO_3166-1. It’s conventional to uppercase the region (like en-GB), but BCP 47 tags are technically case-insensitive and it’s OK for us to use all-lowercase versions in our URLs.

Scaffolding a web application

If you’d like to follow along with the application build, go ahead and run the following commands to setup a new project directory.$ mkdir bookstore $ cd bookstore $ go mod init bookstore.example.com go: creating new go.mod: module bookstore.example.com

At this point, you should have a go.mod file in the root of the project directory with the module path bookstore.example.com.

Next create a new cmd/www directory to hold the code for the bookstore web application, and add main.go and handlers.go files like so:$ mkdir -p cmd/www $ touch cmd/www/main.go cmd/www/handlers.go

Your project directory should now look like this:. ├── cmd │ └── www │ ├── handlers.go │ └── main.go └── go.mod

Let’s begin in the cmd/www/main.go file and add the code to declare our application routes and start a HTTP server.

Because our application URL paths will always use a (dynamic) locale as a prefix — like /en-gb/bestsellers or /fr-ch/bestsellers — it’s simplest if our application uses a third-party router which supports dynamic values in URL path segments. I’m going to use pat, but feel free to use an alternative like chi or gorilla/mux if you prefer.Note: If you’re not sure which router to use in your project, you might like to take a look at my comparison of Go routers blog post.

OK, open up the main.go file and add the following code: