docs: function compile(self, str, locale) {
    var nodes, buf, key, strict_exec, forms_exec, plurals_cache;
  
    // Quick check to avoid parse in most cases :)
    if (!CAN_HAVE_DIRECTIVES_RE.test(str)) { return str; }
  
    nodes = parser.parse(str);
  
    if (nodes.length === 1 && nodes[0].type === 'literal') {
      return nodes[0].text;
    }
  
    // init cache instance for plural parts, if not exists yet.
    if (!self._plurals_cache[locale]) {
      self._plurals_cache[locale] = new BabelFish(locale);
    }
    plurals_cache = self._plurals_cache[locale];
  
    buf = [];
    buf.push([ 'var str = "", strict, strict_exec, forms, forms_exec, plrl, cache, loc, loc_plzr, anchor;' ]);
    buf.push('params = flatten(params);');
  
    forEach(nodes, function (node) {
      if (node.type === 'literal') {
        buf.push(format('str += %j;', node.text));
        return;
      }
  
      if (node.type === 'variable') {
        key = node.anchor;
        buf.push(format(
          'str += ("undefined" === typeof (params[%j])) ? "[missed variable: %s]" : params[%j];',
          key, key, key
        ));
        return;
      }
  
      // should never happen
      /*istanbul ignore next*/
      if (node.type !== 'plural') { throw new Error('Unknown node type'); }
  
      //
      // Compile plural
      //
  
      key = node.anchor;
      // check if plural parts are plain strings or executable,
      // and add executable to "cache" instance of babelfish
      // plural part text will be used as translation key
      strict_exec = {};
      forEach(node.strict, function (text, k) {
        var parsed = parser.parse(text);
        if (parsed.length === 1 && parsed[0].type === 'literal') {
          strict_exec[k] = false;
          // patch with unescaped value for direct extract
          node.strict[k] = parsed[0].text;
          return;
        }
  
        strict_exec[k] = true;
        if (!plurals_cache.hasPhrase(locale, text, true)) {
          plurals_cache.addPhrase(locale, text, text);
        }
      });
  
      forms_exec = {};
      forEach(node.forms, function (text, idx) {
        var parsed = parser.parse(text), unescaped;
        if (parsed.length === 1 && parsed[0].type === 'literal') {
          // patch with unescaped value for direct extract
          unescaped = parsed[0].text;
          node.forms[idx] = unescaped;
          forms_exec[unescaped] = false;
          return;
        }
  
        forms_exec[text] = true;
        if (!plurals_cache.hasPhrase(locale, text, true)) {
          plurals_cache.addPhrase(locale, text, text);
        }
      });
      /*eslint-disable space-in-parens*/
      buf.push(format('loc = %j;', locale));
      buf.push(format('loc_plzr = %j;', locale.split(/[-_]/)[0]));
      buf.push(format('anchor = params[%j];', key));
      buf.push(format('cache = this._plurals_cache[loc];'));
      buf.push(format('strict = %j;', node.strict));
      buf.push(format('strict_exec = %j;', strict_exec));
      buf.push(format('forms = %j;', node.forms));
      buf.push(format('forms_exec = %j;', forms_exec));
      buf.push(       'if (+(anchor) != anchor) {');
      buf.push(format('  str += "[invalid plurals amount: %s(" + anchor + ")]";', key));
      buf.push(       '} else {');
      buf.push(       '  if (strict[anchor] !== undefined) {');
      buf.push(       '    plrl = strict[anchor];');
      buf.push(       '    str += strict_exec[anchor] ? cache.t(loc, plrl, params) : plrl;');
      buf.push(       '  } else {');
      buf.push(       '    plrl = pluralizer(loc_plzr, +anchor, forms);');
      buf.push(       '    str += forms_exec[plrl] ? cache.t(loc, plrl, params) : plrl;');
      buf.push(       '  }');
      buf.push(       '}');
      return;
    });
  
    buf.push('return str;');
  
    /*eslint-disable no-new-func*/
    return new Function('params', 'flatten', 'pluralizer', buf.join('\n'));
  }