<commit_before>'use strict';

var C = require('./compose');
var plurals = require('./plurals');

//
// Locale
//

function Locale(options) {
  this.hash = {};
  this.options = C.call({
    tags: {
      open: '#{',
      close: '}'
    }
  }, options || {});
}

//
// add a `hash` of strings to the locale. `id` defines inflection type
// optional `compiler` specifies templating compiler function
//
Locale.prototype.add = function (id, hash, compiler) {

  var openTag = this.options.tags.open;
  var closeTag = this.options.tags.close;
  var compile = compiler || this.options.compiler || function (x) { return x; };

  // compose textual representation of inflection cases
  var pluralize = plurals(id).toString().replace(/^[\s\S]*return /, '(').replace(/;[\s\S]*$/, ')').replace(/ /g, '');

  //
  // JavaScript micro-templating, similar to <NAME>'s implementation
  //
  var re_interpolate1 = /#\{([\s\S]+?)\}/g;
  var re_interpolate2 = /%\{([\s\S]+?)\}:([\w.$]+)/g;

  var re_inflect = /^([\w.$]+?) ([\s\S]+)/;
  
  function expand(str) {
    var nonce = '~~~' + Math.random().toString().substring(2) + '~~~';
    var re_unescape = new RegExp(nonce, 'g');
    var tmpl = String(str || '')
      // }} are escaped to not interfere with closing }
      .replace(/\}\}/g, nonce)
      // #{var forms...}
      .replace(re_interpolate1, function (match, inner) {
        var m = re_inflect.exec(inner);
        if (m) {
          // unescape }} and parse word forms
          var forms = m[2].replace(re_unescape, '}').split('|');
          return openTag + JSON.stringify(forms) + '[' + pluralize.replace(/n/g, m[1]) + ']' + closeTag;
        } else {
          return openTag + inner + closeTag;
        }
      })
      // %{forms...}:var
      .replace(re_interpolate2, function (match, forms, variable) {
        // unescape }} and parse word forms
        forms = forms.replace(re_unescape, '}').split('|');
        return openTag + JSON.stringify(forms) + '[' + pluralize.replace(/n/g, variable) + ']' + closeTag;
      });
    return tmpl;
  }

  function clone(o, c, path) {
    var i, p;
    for (i in o) {
      if (o.hasOwnProperty(i)) {
        p = path ? path + '.' + i : i;
        if (o[i] && typeof o[i] === 'object') {
          c[i] = {};
          clone(o[i], c[i], p);
        } else if (typeof o[i] === 'string') {
          c[i] = compile(expand(o[i], id));
        } else {
          c[i] = o[i];
        }
        //c[p] = c[i];
      }
    }
  }
  var copy = {};
  clone(hash, copy);

  // augment current locale hash
  C.call(this.hash, copy);

  return this;
};

//
// get a subtree of locale.
// honors dotted notation: `get('foo.bar.baz') === get('foo').bar.baz`
//
Locale.prototype.get = function (key) {
  var path = (key || '').split('.');
  var o = this.hash;
  var p;
  while (o && (p = path.shift())) {
    o = o[p];
  }
  return o;
};

//
// helper
//
Locale.prototype.t = function (key, vars) {
  var fn = this.get(key);
  if (typeof fn !== 'function') {
    // TODO: if it's string, compile it and replace hash key. So called lazy caching
    return keys;
  }
  return fn(vars);
};

//
// module
//
module.exports = Locale;<commit_msg>compile to array, not particular template<commit_after>'use strict';

var C = require('./compose');
var plurals = require('./plurals');

//
// Locale
//

function Locale(options) {
  this.hash = {};
  this.options = C.call({
    tags: {
      open: '#{',
      close: '}'
    }
  }, options || {});
}

//
// get pluralize function
//
Locale.p = function (id) {
  // TODO: cache?
  var fn = plurals(id);
  return function(n, forms) {
    // TODO: consider guessing for languages with simple plural
    return forms[fn(n)] || forms[0] || '';
  };
};

//
// add a `hash` of strings to the locale. `id` defines inflection type
// optional `compiler` specifies templating compiler function
//
Locale.prototype.add = function (id, hash, compiler) {

  var openTag = this.options.tags.open;
  var closeTag = this.options.tags.close;
  var compile = compiler || this.options.compiler || function (x) { return x; };
  var expand = expand2array;

  // compose textual representation of inflection cases
  var pluralize = plurals(id).toString().replace(/^[\s\S]*return /, '(').replace(/;[\s\S]*$/, ')').replace(/ /g, '');

  //
  // JavaScript micro-templating, similar to <NAME>'s implementation
  //
  var re_interpolate1 = /#\{([\s\S]+?)\}/g;
  var re_interpolate2 = /%\{([\s\S]+?)\}:([\w.$]+)/g;

  var re_inflect = /^([\w.$]+?) +([\s\S]+)/;
  
  function expand2array(str) {
    var interpolated = false;
    var nonce = '~~~' + Math.random().toString().substring(2) + '~~~';
    var re_unescape = new RegExp(nonce, 'g');
    var tmpl = "var __p=Locale.p('" + id + "');with(obj||{}){return ['" + String(str || '')
      .replace(/\\/g, '\\\\')
      .replace(/'/g, "\\'")
      // }} are escaped to not interfere with closing }
      // FIXME: this is redundant for simple strings!
      .replace(/\}\}/g, nonce)
      // #{var forms...}
      .replace(re_interpolate1, function (match, inner) {
        inner = inner.replace(/\\'/, "'");
        var m = re_inflect.exec(inner);
        if (m) {
          // unescape }} and parse word forms
          var forms = m[2].replace(re_unescape, '}').split('|');
          return '\',__p(' + m[1] + ',' + JSON.stringify(forms) + '),\'';
        } else {
          return '\',' + inner + ',\'';
        }
      })
      // %{forms...}:var
      .replace(re_interpolate2, function (match, forms, variable) {
        forms = forms.replace(/\\'/, "'");
        variable = variable.replace(/\\'/, "'");
        // unescape }} and parse word forms
        forms = forms.replace(re_unescape, '}').split('|');
        return '\',__p(' + variable + ',' + JSON.stringify(forms) + '),\'';
      })
      // unescape }}
      .replace(re_unescape, '}')
      + "'].join('')}";
    return tmpl;
  }

  function clone(o, c, path) {
    var i, p;
    for (i in o) {
      if (o.hasOwnProperty(i)) {
        p = path ? path + '.' + i : i;
        if (o[i] && typeof o[i] === 'object') {
          c[i] = {};
          clone(o[i], c[i], p);
        } else if (typeof o[i] === 'string') {
          c[i] = compile(expand(o[i], id));
        } else {
          c[i] = o[i];
        }
        //c[p] = c[i];
      }
    }
  }
  var copy = {};
  clone(hash, copy);

  // augment current locale hash
  C.call(this.hash, copy);

  return this;
};

//
// get a subtree of locale.
// honors dotted notation: `get('foo.bar.baz') === get('foo').bar.baz`
//
Locale.prototype.get = function (key) {
  var path = (key || '').split('.');
  var o = this.hash;
  var p;
  while (o && (p = path.shift())) {
    o = o[p];
  }
  return o;
};

//
// helper
//
Locale.prototype.t = function (key, vars) {
  var fn = this.get(key);
  if (typeof fn !== 'function') {
    // TODO: if it's string, compile it and replace hash key. So called lazy caching
    return (new Function('obj, Locale', fn))(vars, Locale);
  }
  return fn(vars);
};

//
// module
//
module.exports = Locale;

var L = new Locale();
L.add('en', {foo:'You\'ve got #{wives} #{wives wife|wives} and #{children} %{child|childre n\'|kindern}:children'});
L.add('ru', {bar:'У тебя #{wives} #{wives   жена|жены|жён} и #{children} %{ребёнок}}|ребёнка}}}}|детей}}}}}}}:children'});
console.log(L.get('foo'));
console.log(L.get('bar'));
console.log((new Function('obj, Locale', L.get('foo')))({wives: 2, children: 5}, Locale));
console.log(L.t('bar', {wives: 2, children: 5}));
{'fraction_non_alphanumeric': 0.21831735889243875, 'fraction_numerical': 0.0027955271565495207, 'mean_word_length': 2.402626811594203, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '705958', 'n_tokens_mistral': 2693, 'n_tokens_neox': 2637, 'n_words': 639}