Upgrading from 4.x to 5.0
FBT 5.0 is a port of the compiler of Facebook's fbt v1.0.0
(babel-plugin-fbt, rewritten in 2021 around "fbt nodes"). The collected source strings and the
generated translations use the formats of Facebook's fbt, so they must be regenerated.
Steps
-
Update the package:
composer require richarddobron/fbt:^5 -
Collect the source strings again (the old
.source_strings.jsoncan't be reused):php ./vendor/bin/fbt collect-fbts --path=./path/to/fbt/ --src=./path/to/project/ -
Migrate your translation files (translation groups keyed by the hashes of the source strings):
php ./vendor/bin/fbt migrate-v5 --translations="./path/to/translations/*.json" --src=./path/to/fbt/.source_strings.jsonThe md5 hash of a text doesn't change, but it's now encoded in
base64instead ofhexby default, so translation keys are converted. The command reports translations of strings that changed in v5 and have to be translated again (e.g. inner strings of HTML elements nested in an fbt, whose descriptions now contain the whole enclosing string).If you want to keep the
hexencoding, setmd5_digesttohexin the configuration and skip this step. -
Generate the translations again:
php ./vendor/bin/fbt translate --path=./path/to/fbt/ --translations=./path/to/translations/*.json
Changed formats
- Collected phrases:
hashToText({hash: text}),descandtypeare replaced byhashToLeaf({hash: {text, desc}}).jsfbtis always{t, m}, where the leaves oftare{desc, text, tokenAliases?}objects. Phrases also contain their location (filepath,line_beg,line_end). See Extracting FBTs. - Inner strings (HTML elements nested in an fbt) are collected after their enclosing string, and
childParentMappingsmaps child phrase indexes to their parent (it was reversed in v4). Enclosing strings refer to inner strings with tokens like{=Learn more}, which are replaced at runtime by aliases like{=m3}(tokenAliases). Translations keep the clear tokens. - Hash keys of translated payloads (
translatedFbts.json) are computed from the texts and token aliases of all leaves. They're unchanged for plain strings and for strings without inner strings.
Changed behaviors
-
String variations are no longer deduplicated by value. When a phrase contains several plurals, enums or pronouns, the translations are generated for all combinations of their variations. If some of them depend on the same value (e.g. two plurals with the same
$count), some combinations can never happen (There is {number} likes). v4 merged constructs whose values were equal, which also merged unrelated constructs with the same value (e.g. the123placeholder used bycollect-fbts). In v5, constructs are merged only when they have the samekeyoption:fbt('There ' . fbt::plural('is', $count, ['many' => 'are', 'key' => 'likes']) . ' ' .fbt::plural('a like', $count, ['showCount' => 'ifMany', 'many' => 'likes', 'key' => 'likes']),'likes');generates only
There is a likeandThere are {number} likes. Withoutkey, all 4 combinations are generated. Facebook's fbt compiles JavaScript and merges constructs that use the same expression (e.g.count). PHP code is evaluated before fbt sees it, so the expression isn't known andkeyreplaces it. -
Strings of implicit parameters are computed differently: an enum inside an HTML element produces variations (instead of an empty
{=}token), and descriptions of inner strings contain the whole enclosing string (In the phrase: "{name1} {=poked you}."instead of"{=} {=poked you}."). -
Empty HTML elements (e.g.
<br>,<i class="icon"></i>,<p></p>) are kept as a part of the text instead of being implicit parameters (an empty<p></p>threw an exception in v4). -
showCount="ifMany"withoutmanyis allowed (the plural form defaults tosingular + "s"). -
Duplicate token names throw an exception, also when
fbt:nameandfbt:paramuse the same name. -
Docblock options (
@fbt {"project": "..."}) only apply to the fbt calls of the file that declares them. -
The
fbt\fbt()andfbt\fbs()namespaced functions,fbtNamespace,fbtNode,fbtElementandFbtAutoWrapare removed (the globalfbt()/fbs()helpers are unchanged). -
fbt constructs return call expressions.
fbt::param(),fbt::plural(),fbt::enum(),fbt::name(),fbt::pronoun()andfbt::sameParam()return anFbtCallExpression(the equivalent of thefbt.param(...)call of Facebook's fbt) instead of<fbt:param>markup. They still work in arrays and when concatenated with strings, but code that relies on the returned markup (or on astringreturn type) has to be updated. The callsite is compiled once for all of its runtime values. -
Numeric strings are not formatted. Like in Facebook's fbt, the value of
fbt::param()(with thenumberoption) or of thevalueoption offbt::plural()is formatted (e.g.12,345) only when it's a number:fbt::param('zip', '12345', ['number' => true])renders12345. Cast the value to a number to keep the formatting. Numeric values of HTML constructs (<fbt:param number="true">12345</fbt:param>) are still formatted. -
<fbt desc>without a value throws<fbt> requires a "desc" attribute. -
Nested fbt constructs in the functional form (e.g.
fbt::param('a', 'x ' . fbt::param('b', $y))) throw an exception, like<fbt:param>nested in<fbt:param>. -
FbtTransform::transform()only accepts HTML documents (the$functionaland$valuesarguments are removed); fbt() callsites useFbtTransform::transformCallExpression().