Translating
There are three ways to translate phrases:
- Manually translate generated JSON file by
php artisan fbt:generate-translationsand then use thephp artisan fbt:translatecommand. - Manually add translations to the database and then use the
php artisan fbt:translatecommand. - Use the app editor Swiftyper Translations.
Command to generate missing translation hashes from collected source strings:
⚠️ NOTE: This command is required only if you are using json driver.
php artisan fbt:generate-translations --translation-input=./storage/fbt/translation_input.json
# or
php artisan fbt:generate-translations --translations=./storage/fbt/translations/*.json
Options:
| name | default | description |
|---|---|---|
| --src | FbtConfig::get('path')/.source_strings.json | Path to collected source strings file |
| --translation-input | FbtConfig::get('path')/translation_input.json | Path to translation input file |
--translations=[path] | none | The translation files containing translations. E.g. --translations=./storage/fbt/translations/*.json |
| --sort-by-hash | no | Sort 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 fbt: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 fbt: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 artisan fbt:translate
# or
php artisan fbt:translate --stdin < translation_input.json
# or
php artisan fbt:translate --translations=./storage/fbt/translations/*.json
The translations are written to translatedFbts.json in the path of /config/fbt.php.
Options:
| name | default | description |
|---|---|---|
| --pretty | no | Pretty print the translation output |
--translations=[path] | none | The translation files containing translations. If not specified, the translations are retrieved from the database. E.g. --translations=/path/to/translations/*.json |
| --stdin < translation_input.json | none | Instead of reading translation files and source file separately, read monolithic JSON file from STDIN |
Writing to the standard output
With the CLI of fbt (./vendor/bin/fbt), the translations can be 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
| name | default | description |
|---|---|---|
--source-strings=[path] | .source_strings.json | The file containing source strings |
--translations=[paths] | none | The translation files (comma separated or globs), a translation group per file |
| --stdin | no | Read a monolithic JSON payload ({phrases, translationGroups}) from STDIN |
| --jenkins | no | Output 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] | none | Like --jenkins, with the hashes computed by a PHP file returning a callable |
--output-dir=[dir], -o=[dir] | none | Write one <locale>.json file per locale into the directory instead of the standard output |
--output-file=[file] | none | Write the combined output of all locales into a single file instead of the standard output |
| --strict | no | Stop 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 |
| --pretty | no | Pretty 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