i18next 20.5.0 → 21.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npx lint-staged
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ### 21.0.1
2
+
3
+ - fix typescript typo for skipOnVariables
4
+
5
+ ### 21.0.0
6
+
7
+ **This is a major breaking release:**
8
+
9
+ - introduce new v4 json format (intl plural forms with ordinal support), simplifyPluralSuffix not used anymore
10
+ - `skipOnVariables` by default now is true
11
+ - automatically detect natural language keys (no need to set nsSeparator or keySeparator to false)
12
+ - remove deprecated whitelist features
13
+ - introduce new i18next.resolvedLanguage property
14
+
15
+ ### 20.6.1
16
+
17
+ - fix deepFind if looking for nesting array item
18
+
19
+ ### 20.6.0
20
+
21
+ - feature: key prefix option [1644](https://github.com/i18next/i18next/pull/1644)
22
+
1
23
  ### 20.5.0
2
24
 
3
25
  - introduce missingKeyNoValueFallbackToKey option
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 i18next
3
+ Copyright (c) 2021 i18next
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -298,6 +298,19 @@ function escape(data) {
298
298
  return data;
299
299
  }
300
300
  var isIE10 = typeof window !== 'undefined' && window.navigator && window.navigator.userAgent && window.navigator.userAgent.indexOf('MSIE') > -1;
301
+ var chars = [' ', ',', '?', '!', ';'];
302
+ function looksLikeObjectPath(key, nsSeparator, keySeparator) {
303
+ nsSeparator = nsSeparator || '';
304
+ keySeparator = keySeparator || '';
305
+ var possibleChars = chars.filter(function (c) {
306
+ return nsSeparator.indexOf(c) < 0 || keySeparator.indexOf(c) < 0;
307
+ });
308
+ if (possibleChars.length === 0) return true;
309
+ var r = new RegExp("(".concat(possibleChars.map(function (c) {
310
+ return c === '?' ? '\\?' : c;
311
+ }).join('|'), ")"));
312
+ return !r.test(key);
313
+ }
301
314
 
302
315
  function deepFind(obj, path) {
303
316
  var keySeparator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '.';
@@ -307,6 +320,8 @@ function deepFind(obj, path) {
307
320
  var current = obj;
308
321
 
309
322
  for (var i = 0; i < paths.length; ++i) {
323
+ if (!current) return undefined;
324
+
310
325
  if (typeof current[paths[i]] === 'string' && i + 1 < paths.length) {
311
326
  return undefined;
312
327
  }
@@ -493,6 +508,15 @@ var ResourceStore = function (_EventEmitter) {
493
508
  value: function getDataByLanguage(lng) {
494
509
  return this.data[lng];
495
510
  }
511
+ }, {
512
+ key: "hasLanguageSomeTranslations",
513
+ value: function hasLanguageSomeTranslations(lng) {
514
+ var data = this.getDataByLanguage(lng);
515
+ var n = data && Object.keys(data) || [];
516
+ return !!n.find(function (v) {
517
+ return data[v] && Object.keys(data[v]).length > 0;
518
+ });
519
+ }
496
520
  }, {
497
521
  key: "toJSON",
498
522
  value: function toJSON() {
@@ -573,8 +597,10 @@ var Translator = function (_EventEmitter) {
573
597
  if (nsSeparator === undefined) nsSeparator = ':';
574
598
  var keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
575
599
  var namespaces = options.ns || this.options.defaultNS;
600
+ var wouldCheckForNsInKey = nsSeparator && key.indexOf(nsSeparator) > -1;
601
+ var seemsNaturalLanguage = !looksLikeObjectPath(key, nsSeparator, keySeparator);
576
602
 
577
- if (nsSeparator && key.indexOf(nsSeparator) > -1) {
603
+ if (wouldCheckForNsInKey && !seemsNaturalLanguage) {
578
604
  var m = key.match(this.interpolator.nestingRegexp);
579
605
 
580
606
  if (m && m.length > 0) {
@@ -673,7 +699,7 @@ var Translator = function (_EventEmitter) {
673
699
  var usedKey = false;
674
700
  var needsPluralHandling = options.count !== undefined && typeof options.count !== 'string';
675
701
  var hasDefaultValue = Translator.hasDefaultValue(options);
676
- var defaultValueSuffix = needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count) : '';
702
+ var defaultValueSuffix = needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count, options) : '';
677
703
  var defaultValue = options["defaultValue".concat(defaultValueSuffix)] || options.defaultValue;
678
704
 
679
705
  if (!this.isValidLookup(res) && hasDefaultValue) {
@@ -845,7 +871,7 @@ var Translator = function (_EventEmitter) {
845
871
  _this4.i18nFormat.addLookupKeys(finalKeys, key, code, ns, options);
846
872
  } else {
847
873
  var pluralSuffix;
848
- if (needsPluralHandling) pluralSuffix = _this4.pluralResolver.getSuffix(code, options.count);
874
+ if (needsPluralHandling) pluralSuffix = _this4.pluralResolver.getSuffix(code, options.count, options);
849
875
  if (needsPluralHandling && needsContextHandling) finalKeys.push(finalKey + pluralSuffix);
850
876
  if (needsContextHandling) finalKeys.push(finalKey += "".concat(_this4.options.contextSeparator).concat(options.context));
851
877
  if (needsPluralHandling) finalKeys.push(finalKey += pluralSuffix);
@@ -909,7 +935,6 @@ var LanguageUtil = function () {
909
935
  _classCallCheck__default['default'](this, LanguageUtil);
910
936
 
911
937
  this.options = options;
912
- this.whitelist = this.options.supportedLngs || false;
913
938
  this.supportedLngs = this.options.supportedLngs || false;
914
939
  this.logger = baseLogger.create('languageUtils');
915
940
  }
@@ -959,12 +984,6 @@ var LanguageUtil = function () {
959
984
 
960
985
  return this.options.cleanCode || this.options.lowerCaseLng ? code.toLowerCase() : code;
961
986
  }
962
- }, {
963
- key: "isWhitelisted",
964
- value: function isWhitelisted(code) {
965
- this.logger.deprecate('languageUtils.isWhitelisted', 'function "isWhitelisted" will be renamed to "isSupportedCode" in the next major - please make sure to rename it\'s usage asap.');
966
- return this.isSupportedCode(code);
967
- }
968
987
  }, {
969
988
  key: "isSupportedCode",
970
989
  value: function isSupportedCode(code) {
@@ -1217,6 +1236,15 @@ var _rulesPluralsTypes = {
1217
1236
  return Number(n == 1 ? 0 : n == 2 ? 1 : (n < 0 || n > 10) && n % 10 == 0 ? 2 : 3);
1218
1237
  }
1219
1238
  };
1239
+ var deprecatedJsonVersions = ['v1', 'v2', 'v3'];
1240
+ var suffixesOrder = {
1241
+ zero: 0,
1242
+ one: 1,
1243
+ two: 2,
1244
+ few: 3,
1245
+ many: 4,
1246
+ other: 5
1247
+ };
1220
1248
 
1221
1249
  function createRules() {
1222
1250
  var rules = {};
@@ -1251,19 +1279,38 @@ var PluralResolver = function () {
1251
1279
  }, {
1252
1280
  key: "getRule",
1253
1281
  value: function getRule(code) {
1282
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1283
+
1284
+ if (this.shouldUseIntlApi()) {
1285
+ try {
1286
+ return new Intl.PluralRules(code, {
1287
+ type: options.ordinal ? 'ordinal' : 'cardinal'
1288
+ });
1289
+ } catch (_unused) {
1290
+ return;
1291
+ }
1292
+ }
1293
+
1254
1294
  return this.rules[code] || this.rules[this.languageUtils.getLanguagePartFromCode(code)];
1255
1295
  }
1256
1296
  }, {
1257
1297
  key: "needsPlural",
1258
1298
  value: function needsPlural(code) {
1259
- var rule = this.getRule(code);
1299
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1300
+ var rule = this.getRule(code, options);
1301
+
1302
+ if (this.shouldUseIntlApi()) {
1303
+ return rule && rule.resolvedOptions().pluralCategories.length > 1;
1304
+ }
1305
+
1260
1306
  return rule && rule.numbers.length > 1;
1261
1307
  }
1262
1308
  }, {
1263
1309
  key: "getPluralFormsOfKey",
1264
1310
  value: function getPluralFormsOfKey(code, key) {
1265
- return this.getSuffixes(code).map(function (suffix) {
1266
- return key + suffix;
1311
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1312
+ return this.getSuffixes(code, options).map(function (suffix) {
1313
+ return "".concat(key).concat(suffix);
1267
1314
  });
1268
1315
  }
1269
1316
  }, {
@@ -1271,54 +1318,78 @@ var PluralResolver = function () {
1271
1318
  value: function getSuffixes(code) {
1272
1319
  var _this = this;
1273
1320
 
1274
- var rule = this.getRule(code);
1321
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1322
+ var rule = this.getRule(code, options);
1275
1323
 
1276
1324
  if (!rule) {
1277
1325
  return [];
1278
1326
  }
1279
1327
 
1328
+ if (this.shouldUseIntlApi()) {
1329
+ return rule.resolvedOptions().pluralCategories.sort(function (pluralCategory1, pluralCategory2) {
1330
+ return suffixesOrder[pluralCategory1] - suffixesOrder[pluralCategory2];
1331
+ }).map(function (pluralCategory) {
1332
+ return "".concat(_this.options.prepend).concat(pluralCategory);
1333
+ });
1334
+ }
1335
+
1280
1336
  return rule.numbers.map(function (number) {
1281
- return _this.getSuffix(code, number);
1337
+ return _this.getSuffix(code, number, options);
1282
1338
  });
1283
1339
  }
1284
1340
  }, {
1285
1341
  key: "getSuffix",
1286
1342
  value: function getSuffix(code, count) {
1287
- var _this2 = this;
1288
-
1289
- var rule = this.getRule(code);
1343
+ var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1344
+ var rule = this.getRule(code, options);
1290
1345
 
1291
1346
  if (rule) {
1292
- var idx = rule.noAbs ? rule.plurals(count) : rule.plurals(Math.abs(count));
1293
- var suffix = rule.numbers[idx];
1294
-
1295
- if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
1296
- if (suffix === 2) {
1297
- suffix = 'plural';
1298
- } else if (suffix === 1) {
1299
- suffix = '';
1300
- }
1347
+ if (this.shouldUseIntlApi()) {
1348
+ return "".concat(this.options.prepend).concat(rule.select(count));
1301
1349
  }
1302
1350
 
1303
- var returnSuffix = function returnSuffix() {
1304
- return _this2.options.prepend && suffix.toString() ? _this2.options.prepend + suffix.toString() : suffix.toString();
1305
- };
1351
+ return this.getSuffixRetroCompatible(rule, count);
1352
+ }
1306
1353
 
1307
- if (this.options.compatibilityJSON === 'v1') {
1308
- if (suffix === 1) return '';
1309
- if (typeof suffix === 'number') return "_plural_".concat(suffix.toString());
1310
- return returnSuffix();
1311
- } else if (this.options.compatibilityJSON === 'v2') {
1312
- return returnSuffix();
1313
- } else if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
1314
- return returnSuffix();
1354
+ this.logger.warn("no plural rule found for: ".concat(code));
1355
+ return '';
1356
+ }
1357
+ }, {
1358
+ key: "getSuffixRetroCompatible",
1359
+ value: function getSuffixRetroCompatible(rule, count) {
1360
+ var _this2 = this;
1361
+
1362
+ var idx = rule.noAbs ? rule.plurals(count) : rule.plurals(Math.abs(count));
1363
+ var suffix = rule.numbers[idx];
1364
+
1365
+ if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
1366
+ if (suffix === 2) {
1367
+ suffix = 'plural';
1368
+ } else if (suffix === 1) {
1369
+ suffix = '';
1315
1370
  }
1371
+ }
1316
1372
 
1317
- return this.options.prepend && idx.toString() ? this.options.prepend + idx.toString() : idx.toString();
1373
+ var returnSuffix = function returnSuffix() {
1374
+ return _this2.options.prepend && suffix.toString() ? _this2.options.prepend + suffix.toString() : suffix.toString();
1375
+ };
1376
+
1377
+ if (this.options.compatibilityJSON === 'v1') {
1378
+ if (suffix === 1) return '';
1379
+ if (typeof suffix === 'number') return "_plural_".concat(suffix.toString());
1380
+ return returnSuffix();
1381
+ } else if (this.options.compatibilityJSON === 'v2') {
1382
+ return returnSuffix();
1383
+ } else if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
1384
+ return returnSuffix();
1318
1385
  }
1319
1386
 
1320
- this.logger.warn("no plural rule found for: ".concat(code));
1321
- return '';
1387
+ return this.options.prepend && idx.toString() ? this.options.prepend + idx.toString() : idx.toString();
1388
+ }
1389
+ }, {
1390
+ key: "shouldUseIntlApi",
1391
+ value: function shouldUseIntlApi() {
1392
+ return !deprecatedJsonVersions.includes(this.options.compatibilityJSON);
1322
1393
  }
1323
1394
  }]);
1324
1395
 
@@ -1777,8 +1848,6 @@ function get() {
1777
1848
  defaultNS: ['translation'],
1778
1849
  fallbackLng: ['dev'],
1779
1850
  fallbackNS: false,
1780
- whitelist: false,
1781
- nonExplicitWhitelist: false,
1782
1851
  supportedLngs: false,
1783
1852
  nonExplicitSupportedLngs: false,
1784
1853
  load: 'all',
@@ -1833,7 +1902,7 @@ function get() {
1833
1902
  nestingSuffix: ')',
1834
1903
  nestingOptionsSeparator: ',',
1835
1904
  maxReplaces: 1000,
1836
- skipOnVariables: false
1905
+ skipOnVariables: true
1837
1906
  }
1838
1907
  };
1839
1908
  }
@@ -1842,18 +1911,6 @@ function transformOptions(options) {
1842
1911
  if (typeof options.fallbackLng === 'string') options.fallbackLng = [options.fallbackLng];
1843
1912
  if (typeof options.fallbackNS === 'string') options.fallbackNS = [options.fallbackNS];
1844
1913
 
1845
- if (options.whitelist) {
1846
- if (options.whitelist && options.whitelist.indexOf('cimode') < 0) {
1847
- options.whitelist = options.whitelist.concat(['cimode']);
1848
- }
1849
-
1850
- options.supportedLngs = options.whitelist;
1851
- }
1852
-
1853
- if (options.nonExplicitWhitelist) {
1854
- options.nonExplicitSupportedLngs = options.nonExplicitWhitelist;
1855
- }
1856
-
1857
1914
  if (options.supportedLngs && options.supportedLngs.indexOf('cimode') < 0) {
1858
1915
  options.supportedLngs = options.supportedLngs.concat(['cimode']);
1859
1916
  }
@@ -1915,12 +1972,12 @@ var I18n = function (_EventEmitter) {
1915
1972
  options = {};
1916
1973
  }
1917
1974
 
1918
- if (options.whitelist && !options.supportedLngs) {
1919
- this.logger.deprecate('whitelist', 'option "whitelist" will be renamed to "supportedLngs" in the next major - please make sure to rename this option asap.');
1920
- }
1921
-
1922
- if (options.nonExplicitWhitelist && !options.nonExplicitSupportedLngs) {
1923
- this.logger.deprecate('whitelist', 'options "nonExplicitWhitelist" will be renamed to "nonExplicitSupportedLngs" in the next major - please make sure to rename this option asap.');
1975
+ if (!options.defaultNS && options.ns) {
1976
+ if (typeof options.ns === 'string') {
1977
+ options.defaultNS = options.ns;
1978
+ } else {
1979
+ options.defaultNS = options.ns[0];
1980
+ }
1924
1981
  }
1925
1982
 
1926
1983
  this.options = _objectSpread__default['default']({}, get(), this.options, transformOptions(options));
@@ -2139,10 +2196,26 @@ var I18n = function (_EventEmitter) {
2139
2196
  var deferred = defer();
2140
2197
  this.emit('languageChanging', lng);
2141
2198
 
2199
+ var setLngProps = function setLngProps(l) {
2200
+ _this4.language = l;
2201
+ _this4.languages = _this4.services.languageUtils.toResolveHierarchy(l);
2202
+ _this4.resolvedLanguage = undefined;
2203
+ if (['cimode', 'dev'].indexOf(l) > -1) return;
2204
+
2205
+ for (var li = 0; li < _this4.languages.length; li++) {
2206
+ var lngInLngs = _this4.languages[li];
2207
+ if (['cimode', 'dev'].indexOf(lngInLngs) > -1) continue;
2208
+
2209
+ if (_this4.store.hasLanguageSomeTranslations(lngInLngs)) {
2210
+ _this4.resolvedLanguage = lngInLngs;
2211
+ break;
2212
+ }
2213
+ }
2214
+ };
2215
+
2142
2216
  var done = function done(err, l) {
2143
2217
  if (l) {
2144
- _this4.language = l;
2145
- _this4.languages = _this4.services.languageUtils.toResolveHierarchy(l);
2218
+ setLngProps(l);
2146
2219
 
2147
2220
  _this4.translator.changeLanguage(l);
2148
2221
 
@@ -2169,8 +2242,7 @@ var I18n = function (_EventEmitter) {
2169
2242
 
2170
2243
  if (l) {
2171
2244
  if (!_this4.language) {
2172
- _this4.language = l;
2173
- _this4.languages = _this4.services.languageUtils.toResolveHierarchy(l);
2245
+ setLngProps(l);
2174
2246
  }
2175
2247
 
2176
2248
  if (!_this4.translator.language) _this4.translator.changeLanguage(l);
@@ -2194,7 +2266,7 @@ var I18n = function (_EventEmitter) {
2194
2266
  }
2195
2267
  }, {
2196
2268
  key: "getFixedT",
2197
- value: function getFixedT(lng, ns) {
2269
+ value: function getFixedT(lng, ns, keyPrefix) {
2198
2270
  var _this5 = this;
2199
2271
 
2200
2272
  var fixedT = function fixedT(key, opts) {
@@ -2213,7 +2285,9 @@ var I18n = function (_EventEmitter) {
2213
2285
  options.lng = options.lng || fixedT.lng;
2214
2286
  options.lngs = options.lngs || fixedT.lngs;
2215
2287
  options.ns = options.ns || fixedT.ns;
2216
- return _this5.t(key, options);
2288
+ var keySeparator = _this5.options.keySeparator || '.';
2289
+ var resultKey = keyPrefix ? "".concat(keyPrefix).concat(keySeparator).concat(key) : key;
2290
+ return _this5.t(resultKey, options);
2217
2291
  };
2218
2292
 
2219
2293
  if (typeof lng === 'string') {
@@ -2223,6 +2297,7 @@ var I18n = function (_EventEmitter) {
2223
2297
  }
2224
2298
 
2225
2299
  fixedT.ns = ns;
2300
+ fixedT.keyPrefix = keyPrefix;
2226
2301
  return fixedT;
2227
2302
  }
2228
2303
  }, {
@@ -2261,7 +2336,7 @@ var I18n = function (_EventEmitter) {
2261
2336
  return false;
2262
2337
  }
2263
2338
 
2264
- var lng = this.languages[0];
2339
+ var lng = this.resolvedLanguage || this.languages[0];
2265
2340
  var fallbackLng = this.options ? this.options.fallbackLng : false;
2266
2341
  var lastLng = this.languages[this.languages.length - 1];
2267
2342
  if (lng.toLowerCase() === 'cimode') return true;
@@ -2329,7 +2404,7 @@ var I18n = function (_EventEmitter) {
2329
2404
  }, {
2330
2405
  key: "dir",
2331
2406
  value: function dir(lng) {
2332
- if (!lng) lng = this.languages && this.languages.length > 0 ? this.languages[0] : this.language;
2407
+ if (!lng) lng = this.resolvedLanguage || (this.languages && this.languages.length > 0 ? this.languages[0] : this.language);
2333
2408
  if (!lng) return 'rtl';
2334
2409
  var rtlLngs = ['ar', 'shu', 'sqr', 'ssh', 'xaa', 'yhd', 'yud', 'aao', 'abh', 'abv', 'acm', 'acq', 'acw', 'acx', 'acy', 'adf', 'ads', 'aeb', 'aec', 'afb', 'ajp', 'apc', 'apd', 'arb', 'arq', 'ars', 'ary', 'arz', 'auz', 'avl', 'ayh', 'ayl', 'ayn', 'ayp', 'bbz', 'pga', 'he', 'iw', 'ps', 'pbt', 'pbu', 'pst', 'prp', 'prd', 'ug', 'ur', 'ydd', 'yds', 'yih', 'ji', 'yi', 'hbo', 'men', 'xmn', 'fa', 'jpr', 'peo', 'pes', 'prs', 'dv', 'sam'];
2335
2410
  return rtlLngs.indexOf(this.services.languageUtils.getLanguagePartFromCode(lng)) >= 0 ? 'rtl' : 'ltr';
@@ -2384,7 +2459,8 @@ var I18n = function (_EventEmitter) {
2384
2459
  options: this.options,
2385
2460
  store: this.store,
2386
2461
  language: this.language,
2387
- languages: this.languages
2462
+ languages: this.languages,
2463
+ resolvedLanguage: this.resolvedLanguage
2388
2464
  };
2389
2465
  }
2390
2466
  }]);