Skip to main content
Version: 5.x

Translating

There are two ways to translate phrases:

  1. Manually translate generated JSON file.
  2. Use the app editor Swiftyper Translations.

Command to generate missing translation hashes from collected source strings:​

php ./vendor/bin/fbt generate-translations --src=./path/to/fbt/.source_strings.json --translation-input=./path/to/translation_input.json
# or
php ./vendor/bin/fbt generate-translations --src=./path/to/fbt/.source_strings.json --translations=./path/to/translations/*.json

Options:​

namedefaultdescription
--src./.source_strings.jsonPath to collected source strings file
--translation-input./translation_input.jsonPath to translation input file
--translations=[path]noneThe translation files containing translations.
E.g. --translations=./path/to/translations/*.json
--sort-by-hashnoSort translation entries by hash key (both existing and new), for cleaner diffs of committed files

Translation files have to be named by their locale (e.g. de_DE.json), other files are skipped. Each file contains a translation group ({"fb-locale": "de_DE", "translations": {...}}), so it can be passed to translate --translations. Without --translations, the missing translations are written to the --translation-input file.

The translations are merged with the collected strings like prepare-translations of fbtee: new strings are added with their source text, their description and "status": "new", and translations of strings that are no longer collected are removed.

Entries marked as new are not translated yet, so translate skips them (remove the status once an entry is translated). The translations by hash (--jenkins, --fbt-hash-module and translatedFbts.json) omit the strings without a translation, so the source string is used.

Command to convert provided translations to jenkins:​

php ./vendor/bin/fbt translate --path=./path/to/fbt/ --stdin < translation_input.json
# or
php ./vendor/bin/fbt translate --path=./path/to/fbt/ --translations=./path/to/translations/*.json

Options:​

namedefaultdescription
--pathnonePath to export translation output (Cache storage path - must be same as in configuration)
--prettynoPretty print the translation output
--translations=[path]noneThe translation files containing translations.
E.g. --translations=/path/to/translations/*.json
--stdin < translation_input.jsonnoneInstead of reading translation files and source file separately, read monolithic JSON file from STDIN

Writing to the standard output​

Without --path, the translations are written as JSON to the standard output instead of translatedFbts.json:

php ./vendor/bin/fbt translate --source-strings=./path/to/fbt/.source_strings.json --translations=./path/to/translations/*.json --jenkins
php ./vendor/bin/fbt translate --stdin --jenkins -o=./path/to/output/ < translation_input.json
namedefaultdescription
--source-strings=[path].source_strings.jsonThe file containing source strings
--translations=[paths]noneThe translation files (comma separated or globs), a translation group per file
--stdinnoRead a monolithic JSON payload ({phrases, translationGroups}) from STDIN
--jenkinsnoOutput the translations by locale and callsite hash ({"cs_CZ": {"<hash>": <payload>}}), like translatedFbts.json. Without it, the output is a list of translation groups ([{"fb-locale": ..., "translatedPhrases": [...]}])
--fbt-hash-module=[path]noneLike --jenkins, with the hashes computed by a PHP file returning a callable
--output-dir=[dir], -o=[dir]noneWrite one <locale>.json file per locale into the directory instead of the standard output
--output-file=[file]noneWrite the combined output of all locales into a single file instead of the standard output
--strictnoStop on missing translations: every collected string needs a completed translation in each translation group (missing entries, entries marked as new and entries without translations fail). An explicit empty translation is valid
--prettynoPretty print the translation output

JSON schema:​

In addition to the below example, the translation_input.json provided in our tests is a good reference on the "schema" used for the translations.

{
"phrases": [{
"hashToLeaf": {
<text_hash>: {"text": <text>, "desc": <description>},
...
},
"jsfbt": {"t": <leaf or table>, "m": <metadata>}
}],
...
"translationGroups": [{
"fb-locale": "xx_XX",
"translations": {
<translation_hash>: {
"tokens": [<token1>, ..., <tokenN>],
"types": [<variationType1>, ..., <variationTypeN>]
"translations": [{
"translation": <translation1>,
"variations": [variationValue1,...,variationValueN]
},
...,
]
}
}
}]
}

The <text_hash> and <translation_hash> correspond in the above example. That is translations[<hash>] is the translation entry for phrases.hashToLeaf[<hash>].

Translations of strings with inner strings (e.g. {=Learn more}) keep these tokens as they are.

Here tokens, types and variations are all associative arrays. That is, in the above example, types[i] represents the variation type (or mask) of tokens[i] and variations[i] is the variation value of token[i] for the given translation entry.

Variation types​

Variation types can be one of

IntlVariations::BITMASK_NUMBER: 28
IntlVariations::BITMASK_GENDER: 3

This signifies what the given token can variate on. Token types of type GENDER can be:

IntlVariations::GENDER_MALE: 1
IntlVariations::GENDER_FEMALE: 2
IntlVariations::GENDER_UNKNOWN: 3

while token types of NUMBER can be:


IntlVariations::NUMBER_ONE: 4
IntlVariations::NUMBER_TWO: 8
IntlVariations::NUMBER_MANY: 12
IntlVariations::NUMBER_ZERO: 16
IntlVariations::NUMBER_FEW: 20
IntlVariations::NUMBER_OTHER: 24