_buildRecursive(
    hashOrTable: FbtSiteHashifiedTableJSFBTTree,
    tokenConstraints: TokenToConstraint = {},
    levelIdx: number = 0,
  ): TranslationTree {
    if (typeof hashOrTable === 'string') {
      return this._getLeafTranslation(hashOrTable, tokenConstraints);
    }

    const table: TranslationTree = {};
    for (const key in hashOrTable) {
      const branchOrLeaf = hashOrTable[key];
      let trans: TranslationTree = this._buildRecursive(
        branchOrLeaf,
        tokenConstraints,
        levelIdx + 1,
      );
      if (_shouldStore(trans)) {
        // $FlowFixMe[incompatible-use]
        table[key] = trans;
      }

      // This level will have metadata if it could potentially have variations.
      // Below, we fill the table with those variation entries.
      //
      // NOTE: A key of '_1' (EXACTLY_ONE) will be processed by the
      // buildRecursive call above, as its corresponding token constraint is
      // defaulted to '*'.  See _getConstraintMap for more details
      const metadata = this._metadata[levelIdx];
      if (
        metadata != null &&
        metadata.hasVariationMask() &&
        key !== EXACTLY_ONE
      ) {
        const mask = nullthrows(
          metadata.getVariationMask(),
          'Expect mask not to be null because metadata.hasVariationMask() returns true.',
        );
        invariant(
          mask === Mask.NUMBER || mask === Mask.GENDER,
          'Unknown variation mask: %s (%s)',
          varDump(mask),
          typeof mask,
        );
        invariant(
          isValidValue(key),
          'Expect variation keys to be coercible to IntlVariationsEnum: current key=%s (%s)',
          varDump(key),
          typeof key,
        );
        const token = nullthrows(
          metadata.getToken(),
          'Expect `token` to not be falsy when the metadata has a variation mask.',
        );
        const variationCandidates = _getTypesFromMask(mask);
        variationCandidates.forEach(variationKey => {
          tokenConstraints[token] = variationKey;
          trans = this._buildRecursive(
            branchOrLeaf,
            tokenConstraints,
            levelIdx + 1,
          );
          if (_shouldStore(trans)) {
            // $FlowFixMe[incompatible-use]
            table[String(variationKey)] = trans;
          }
        });
        delete tokenConstraints[token];
      }
    }
    return table;
  }
Given a hash (or hash-table), return the translated text (or table of
texts).  If the hash (or hashes) do not have a translation, then the
original text will be used as the translation.

If we should include the string hash then the method returns a vector with
[string, hash] so that the hash is available to the run-time logging code.