This document aims to augment that which appears in the goi18n project and provide information that can help the client use the i18n functionality defined there and its integration into this template project. The translations process is quite laborious, so this project tries alleviate this process by providing helper tasks which will be documented here also.
💥 Warning: when using goi18n and defining content that needs to be extracted, it is vitally important that any code files with extractable content imports directly from go-i18n ie
import ("github.com/nicksnyder/go-i18n/v2/i18n")
Now this may seem obvious, but you should not fall into the trap of writing your own layer on top of go-i18n; do not do something like this ...:
type Message = i18n.Message... thinking that the new type defined in another package of your own making then defining messages with this new type. This will not work, because now presumably, you would be importing the new type, eg:
import (
"github.com/snivilised/extendio/i18n"
)
func (td PavementGraffitiReportTemplData) Message() *i18n.Message {
return &i18n.Message{
ID: "pavement-graffiti-report.graffico.unit-test",
Description: "Report of graffiti found on a pavement",
Other: "Found graffiti on pavement; primary colour: '{{.Primary}}'",
}
}In this example, the i18n.Message type refers to the alias not the original type defined in go-i18n, but when you go to extract the content using the go-i18n extract command, this will silently extract nothing. This is because the extract command is specifically looking for "github.com/nicksnyder/go-i18n/v2/i18n" in the import statement.
This does also have other implications. When using go-i18n, do not define your own local package of the name i18n because that will cause a conflict with go-i18n which is defined using this package name. I have taken to using an alternative name of locale so that no conflicts occur. However, this is not mandatory because you can just use a different import alias, but this is just adding friction that is best avoided, particularly when authoring a library package to be consumed by third parties.
li18ngo contains a helper program, lingo which provides facilities to assist and improve aspects of i18n curation. Currently, the command contains a sort sub-command; other sub commands may be added in the future.
When extracting content, the resulting default active file (the one representing the native language en-GB) contains i18n.Message entries indexed by the message ID. However, they may not be in any particular order, probably just the order in which they are discovered by the go-i18n extract command. When working with large translation files, it really is unhelpful if the order of the messages is non deterministic. This is the raison d'etre of the lingo sort sub command, it will sort the json file by message ID.
When the extract is run, the sort is invoked on the native active file ./locale/out/l10n/active.en-GB.json. This means that before running the extract, the user needs to ensure that the lingo command is installed.
From within li18ngo, the installation can be performed locally from the root directory using:
go install ./...
From a foreign repo, when using li18ngo to develop translations for other packages, the installation can be completed by running
go install github.com/snivilised/li18ngo/cmd/lingo@latest
When running the merge task, the sorted order will be maintained when creating the translation files, eg translate.en-US.
The local directory structure is as follows:
-
default: contains the translation file created by the newt (new translation task). Actually, this task creates an active file (
active.en-GB.json) in thelocale/outfolder, the result of which needs to be manually copied into the active file in thedefaultfolder. -
deploy: contains all the translations files that are intended to be deployed with the application. There will be one per supported language and by default this template project includes a translation file for en-US (
li18ngo.active.en-US.json)
goi18n instructs the user to manually create an empty translation message file that they want to add (eg translate.en-US.json). This is taken care of by the newt task. Then the requirement is to run the goi18n merge <active> command (goi18n merge li18ngo.active.en-US.json li18ngo.translate.en-US.json). This has been wrapped up into the merge task and the result is that the translation file li18ngo.translation.en-US.json is populated with the messages to be translated. So the sequence goes:
- run newt task: (generates default language file
./locale/out/l10n/active.en-GB.jsonand empty./locale/out/l10n/li18ngo.translation.en-US.jsonfile). This task can be run from the root folder, goi18n will recursively search the directory tree for files with translate-able content, ie files with template definitions (i18n.Message) - run merge task: derives a translation file for the requested language en-US using 2 files as inputs: source active file (
./locale/out/active.en-GB.json) and the empty en-US translate file (./locale/out/l10n/li18ngo.translation.en-US.json), both of which were generated in the previous step. - hand the translate file (
translate.en-US.json) to your translator for them to translate - rename the translate file to the active equivalent (
li18ngo.translation.en-US.json); ie renameli18ngo.translation.en-US.jsontoli18ngo.active.en-US.json. Ifli18ngo.translation.en-US.jsonexists as an empty file, then delete it as its not required. Save this into the deploy folder. This file will be deployed with the application.
goi18n instructs the user to run the following steps:
- Run
goi18n extractto updateactive.en.tomlwith the new messages. - Run
goi18n merge active.*.tomlto generate updatedtranslate.*.tomlfiles. - Translate all the messages in the
translate.*.tomlfiles. - Run
goi18n merge active.*.toml translate.*.tomlto merge the translated messages into the active message files.
The above description is way too vague and ambiguous. It is for this reason that this process has not been finalised. The intention will be to upgrade the instructions as time goes by and experience is gained.
However, in this template, the user can execute the following steps:
- run task update: this will re-extract messages into
active.en-GB.jsonand then runs merge to create an updated translate file. - hand the translate file to your translator for them to translate
- as before, this translated file should be used to update the active file
li18ngo.active.en-US.jsoninside the deploy folder.
❗This is a work in progress ...
Scans the code base for messages and extracts them into out/active.en-GB.json (The name of the file can't be controlled, it is derived from the language specified). No need to call this task directly.
Invokes the extract task to extract messages from code. After running this task, the translate.en-US.json file is empty ready to be used as one of the inputs to the merge task (why do we need an empty input file for the merge? Perhaps the input file instructs the language tag to goi18n).
Inputs:
- source code
Outputs:
- ./locale/out/active.en-GB.json (messages extracted from code, without hashes)
- ./locale/out/en-US/translate.en-US.json (empty)
Inputs:
- ./locale/out/active.en-GB.json
- ./locale/out/en-US/li18ngo.translate-en-US.json
Outputs:
- ./locale/out/active.en-US.json
- ./locale/out/translate.en-US.json