next-i18next 10.2.0 → 10.5.0

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.
package/README.md CHANGED
@@ -223,16 +223,18 @@ This option will reload your translations whenever `serverSideTranslations` is c
223
223
 
224
224
  #### Options
225
225
 
226
- | Key | Default value |
227
- | ------------------- | -------------------- |
228
- | `defaultNS` | `'common'` |
229
- | `localeExtension` | `'json'` |
230
- | `localePath` | `'./public/locales'` |
231
- | `localeStructure` | `'{{lng}}/{{ns}}'` |
232
- | `reloadOnPrerender` | `false` |
233
- | `serializeConfig` | `true` |
234
- | `strictMode` | `true` |
235
- | `use` (for plugins) | `[]` |
226
+ | Key | Default value | Note |
227
+ | ------------------- | -------------------- | -------------------------------------- |
228
+ | `defaultNS` | `'common'` | |
229
+ | `localePath` | `'./public/locales'` | Can be a function, see note below. |
230
+ | `localeExtension` | `'json'` | Ignored if `localePath` is a function. |
231
+ | `localeStructure` | `'{{lng}}/{{ns}}'` | Ignored if `localePath` is a function. |
232
+ | `reloadOnPrerender` | `false` | |
233
+ | `serializeConfig` | `true` | |
234
+ | `strictMode` | `true` | |
235
+ | `use` (for plugins) | `[]` | |
236
+
237
+ `localePath` as a function is of the form `(locale: string, namespace: string, missing: boolean) => string` returning the entire path including filename and extension. When `missing` is true, return the path for the `addPath` option of `i18next-fs-backend`, when false, return the path for the `loadPath` option. [More info at the `i18next-fs-backend` repo.](https://github.com/i18next/i18next-fs-backend/tree/master#backend-options)
236
238
 
237
239
  All other [i18next options](https://www.i18next.com/overview/configuration-options) can be passed in as well.
238
240
 
@@ -254,6 +256,27 @@ const Component = () => {
254
256
  }
255
257
  ```
256
258
 
259
+ #### Custom interpolation prefix/suffix
260
+
261
+ By default, i18next uses `{{` as prefix and `}}` as suffix for [interpolation](https://www.i18next.com/translation-function/interpolation).
262
+ If you want/need to override these interpolation settings, you **must** also specify an alternative `localeStructure` setting that matches your custom prefix and suffix.
263
+
264
+ For example, if you want to use `{` and `}` the config would look like this:
265
+
266
+ ```js
267
+ {
268
+ i18n: {
269
+ defaultLocale: 'en',
270
+ locales: ['en', 'nl'],
271
+ },
272
+ interpolation: {
273
+ prefix: '{',
274
+ suffix: '}',
275
+ },
276
+ localeStructure: '{lng}/{ns}',
277
+ }
278
+ ```
279
+
257
280
  ## Migration to v8
258
281
 
259
282
  To migrate from previous versions to the version 8, check out the [v8-migration guide](https://github.com/isaachinman/next-i18next/tree/master/docs/v8-migration.md)
@@ -86,9 +86,11 @@ var appWithTranslation = function appWithTranslation(WrappedComponent) {
86
86
  var configOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
87
87
 
88
88
  var AppWithTranslation = function AppWithTranslation(props) {
89
+ var _nextI18Next$initialL;
90
+
89
91
  var _ref = props.pageProps,
90
92
  _nextI18Next = _ref._nextI18Next;
91
- var locale = null; // Memoize the instance and only re-initialize when either:
93
+ var locale = (_nextI18Next$initialL = _nextI18Next === null || _nextI18Next === void 0 ? void 0 : _nextI18Next.initialLocale) !== null && _nextI18Next$initialL !== void 0 ? _nextI18Next$initialL : null; // Memoize the instance and only re-initialize when either:
92
94
  // 1. The route changes (non-shallowly)
93
95
  // 2. Router locale changes
94
96
 
@@ -97,9 +99,8 @@ var appWithTranslation = function appWithTranslation(WrappedComponent) {
97
99
 
98
100
  if (!_nextI18Next) return null;
99
101
  var userConfig = _nextI18Next.userConfig;
100
- var initialI18nStore = _nextI18Next.initialI18nStore,
101
- initialLocale = _nextI18Next.initialLocale;
102
- locale = initialLocale;
102
+ var initialI18nStore = _nextI18Next.initialI18nStore;
103
+ var resources = configOverride !== null && configOverride !== void 0 && configOverride.resources ? configOverride.resources : initialI18nStore;
103
104
 
104
105
  if (userConfig === null && configOverride === null) {
105
106
  throw new Error('appWithTranslation was called without a next-i18next config');
@@ -117,7 +118,7 @@ var appWithTranslation = function appWithTranslation(WrappedComponent) {
117
118
  lng: locale
118
119
  }))), {}, {
119
120
  lng: locale,
120
- resources: initialI18nStore
121
+ resources: resources
121
122
  })).i18n;
122
123
  exports.globalI18n = globalI18n = instance;
123
124
  return instance;
@@ -61,20 +61,21 @@ require("core-js/modules/es.array.reduce.js");
61
61
 
62
62
  require("core-js/modules/es.object.values.js");
63
63
 
64
- require("core-js/modules/es.string.match.js");
65
-
66
64
  require("core-js/modules/es.array.for-each.js");
67
65
 
68
- var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
69
-
70
66
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
71
67
 
68
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
69
+
72
70
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
73
71
 
74
72
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
75
73
 
76
74
  var _defaultConfig = require("./defaultConfig");
77
75
 
76
+ var _excluded = ["i18n"],
77
+ _excluded2 = ["i18n"];
78
+
78
79
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
79
80
 
80
81
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -98,9 +99,9 @@ var createConfig = function createConfig(userConfig) {
98
99
 
99
100
 
100
101
  var userI18n = userConfig.i18n,
101
- userConfigStripped = (0, _objectWithoutProperties2["default"])(userConfig, ["i18n"]);
102
+ userConfigStripped = (0, _objectWithoutProperties2["default"])(userConfig, _excluded);
102
103
  var defaultI18n = _defaultConfig.defaultConfig.i18n,
103
- defaultConfigStripped = (0, _objectWithoutProperties2["default"])(_defaultConfig.defaultConfig, ["i18n"]);
104
+ defaultConfigStripped = (0, _objectWithoutProperties2["default"])(_defaultConfig.defaultConfig, _excluded2);
104
105
 
105
106
  var combinedConfig = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, defaultConfigStripped), userConfigStripped), defaultI18n), userI18n);
106
107
 
@@ -133,39 +134,66 @@ var createConfig = function createConfig(userConfig) {
133
134
  if (!hasCustomBackend) {
134
135
  var fs = require('fs');
135
136
 
136
- var path = require('path');
137
-
138
- var serverLocalePath = localePath; //
137
+ var path = require('path'); //
139
138
  // Validate defaultNS
140
139
  // https://github.com/isaachinman/next-i18next/issues/358
141
140
  //
142
141
 
142
+
143
143
  if (typeof defaultNS === 'string' && typeof lng !== 'undefined') {
144
- var _userConfig$interpola, _userConfig$interpola2, _userConfig$interpola3, _userConfig$interpola4;
144
+ if (typeof localePath === 'string') {
145
+ var _userConfig$interpola, _userConfig$interpola2, _userConfig$interpola3, _userConfig$interpola4;
146
+
147
+ var prefix = (_userConfig$interpola = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola2 = userConfig.interpolation) === null || _userConfig$interpola2 === void 0 ? void 0 : _userConfig$interpola2.prefix) !== null && _userConfig$interpola !== void 0 ? _userConfig$interpola : '{{';
148
+ var suffix = (_userConfig$interpola3 = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola4 = userConfig.interpolation) === null || _userConfig$interpola4 === void 0 ? void 0 : _userConfig$interpola4.suffix) !== null && _userConfig$interpola3 !== void 0 ? _userConfig$interpola3 : '}}';
149
+ var defaultLocaleStructure = localeStructure.replace("".concat(prefix, "lng").concat(suffix), lng).replace("".concat(prefix, "ns").concat(suffix), defaultNS);
150
+ var defaultFile = "/".concat(defaultLocaleStructure, ".").concat(localeExtension);
151
+ var defaultNSPath = path.join(localePath, defaultFile);
152
+ var defaultNSExists = fs.existsSync(defaultNSPath);
153
+
154
+ if (!defaultNSExists && process.env.NODE_ENV !== 'production') {
155
+ throw new Error("Default namespace not found at ".concat(defaultNSPath));
156
+ }
157
+ } else if (typeof localePath === 'function') {
158
+ var _defaultNSPath = localePath(lng, defaultNS, false);
145
159
 
146
- var prefix = (_userConfig$interpola = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola2 = userConfig.interpolation) === null || _userConfig$interpola2 === void 0 ? void 0 : _userConfig$interpola2.prefix) !== null && _userConfig$interpola !== void 0 ? _userConfig$interpola : '{{';
147
- var suffix = (_userConfig$interpola3 = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola4 = userConfig.interpolation) === null || _userConfig$interpola4 === void 0 ? void 0 : _userConfig$interpola4.suffix) !== null && _userConfig$interpola3 !== void 0 ? _userConfig$interpola3 : '}}';
148
- var defaultLocaleStructure = localeStructure.replace("".concat(prefix, "lng").concat(suffix), lng).replace("".concat(prefix, "ns").concat(suffix), defaultNS);
149
- var defaultFile = "/".concat(defaultLocaleStructure, ".").concat(localeExtension);
150
- var defaultNSPath = path.join(localePath, defaultFile);
151
- var defaultNSExists = fs.existsSync(defaultNSPath);
160
+ var _defaultNSExists = fs.existsSync(_defaultNSPath);
152
161
 
153
- if (!defaultNSExists && process.env.NODE_ENV !== 'production') {
154
- throw new Error("Default namespace not found at ".concat(defaultNSPath));
162
+ if (!_defaultNSExists && process.env.NODE_ENV !== 'production') {
163
+ throw new Error("Default namespace not found at ".concat(_defaultNSPath));
164
+ }
155
165
  }
156
166
  } //
157
167
  // Set server side backend
158
168
  //
159
169
 
160
170
 
161
- combinedConfig.backend = {
162
- addPath: path.resolve(process.cwd(), "".concat(serverLocalePath, "/").concat(localeStructure, ".missing.").concat(localeExtension)),
163
- loadPath: path.resolve(process.cwd(), "".concat(serverLocalePath, "/").concat(localeStructure, ".").concat(localeExtension))
164
- }; //
171
+ if (typeof localePath === 'string') {
172
+ combinedConfig.backend = {
173
+ addPath: path.resolve(process.cwd(), "".concat(localePath, "/").concat(localeStructure, ".missing.").concat(localeExtension)),
174
+ loadPath: path.resolve(process.cwd(), "".concat(localePath, "/").concat(localeStructure, ".").concat(localeExtension))
175
+ };
176
+ } else if (typeof localePath === 'function') {
177
+ combinedConfig.backend = {
178
+ addPath: function addPath(locale, namespace) {
179
+ return localePath(locale, namespace, true);
180
+ },
181
+ loadPath: function loadPath(locale, namespace) {
182
+ return localePath(locale, namespace, false);
183
+ }
184
+ };
185
+ } else {
186
+ throw new Error("Unsupported localePath type: ".concat((0, _typeof2["default"])(localePath)));
187
+ } //
165
188
  // Set server side preload (namespaces)
166
189
  //
167
190
 
191
+
168
192
  if (!combinedConfig.ns && typeof lng !== 'undefined') {
193
+ if (typeof localePath === 'function') {
194
+ throw new Error('Must provide all namespaces in ns option if using a function as localePath');
195
+ }
196
+
169
197
  var unique = function unique(list) {
170
198
  return Array.from(new Set(list));
171
199
  };
@@ -178,7 +206,7 @@ var createConfig = function createConfig(userConfig) {
178
206
  };
179
207
 
180
208
  var namespacesByLocale = locales.map(function (locale) {
181
- return getLocaleNamespaces(path.resolve(process.cwd(), "".concat(serverLocalePath, "/").concat(locale)));
209
+ return getLocaleNamespaces(path.resolve(process.cwd(), "".concat(localePath, "/").concat(locale)));
182
210
  });
183
211
  var allNamespaces = [];
184
212
 
@@ -222,22 +250,25 @@ var createConfig = function createConfig(userConfig) {
222
250
  }
223
251
  }
224
252
  } else {
225
- var clientLocalePath = localePath; //
226
- // Remove public prefix from client site config
227
253
  //
228
-
229
- if (localePath.match(/^\.?\/public\//)) {
230
- clientLocalePath = localePath.replace(/^\.?\/public/, '');
231
- } //
232
254
  // Set client side backend, if there is no custom backend
233
255
  //
234
-
235
-
236
256
  if (!hasCustomBackend) {
237
- combinedConfig.backend = {
238
- addPath: "".concat(clientLocalePath, "/").concat(localeStructure, ".missing.").concat(localeExtension),
239
- loadPath: "".concat(clientLocalePath, "/").concat(localeStructure, ".").concat(localeExtension)
240
- };
257
+ if (typeof localePath === 'string') {
258
+ combinedConfig.backend = {
259
+ addPath: "".concat(localePath, "/").concat(localeStructure, ".missing.").concat(localeExtension),
260
+ loadPath: "".concat(localePath, "/").concat(localeStructure, ".").concat(localeExtension)
261
+ };
262
+ } else if (typeof localePath === 'function') {
263
+ combinedConfig.backend = {
264
+ addPath: function addPath(locale, namespace) {
265
+ return localePath(locale, namespace, true);
266
+ },
267
+ loadPath: function loadPath(locale, namespace) {
268
+ return localePath(locale, namespace, false);
269
+ }
270
+ };
271
+ }
241
272
  }
242
273
 
243
274
  if (typeof combinedConfig.ns !== 'string' && !Array.isArray(combinedConfig.ns)) {
@@ -222,19 +222,31 @@ var serverSideTranslations = /*#__PURE__*/function () {
222
222
  initialI18nStore[lng] = {};
223
223
  });
224
224
 
225
- if (!Array.isArray(namespacesRequired)) {
226
- getLocaleNamespaces = function getLocaleNamespaces(path) {
227
- return _fs["default"].readdirSync(path).map(function (file) {
228
- return file.replace(".".concat(localeExtension), '');
229
- });
230
- };
231
-
232
- namespacesByLocale = Object.keys(initialI18nStore).map(function (locale) {
233
- return getLocaleNamespaces(_path["default"].resolve(process.cwd(), "".concat(localePath, "/").concat(locale)));
234
- });
235
- namespacesRequired = flatNamespaces(namespacesByLocale);
225
+ if (Array.isArray(namespacesRequired)) {
226
+ _context.next = 27;
227
+ break;
228
+ }
229
+
230
+ if (!(typeof localePath === 'function')) {
231
+ _context.next = 24;
232
+ break;
236
233
  }
237
234
 
235
+ throw new Error('Must provide namespacesRequired to serverSideTranslations when using a function as localePath');
236
+
237
+ case 24:
238
+ getLocaleNamespaces = function getLocaleNamespaces(path) {
239
+ return _fs["default"].readdirSync(path).map(function (file) {
240
+ return file.replace(".".concat(localeExtension), '');
241
+ });
242
+ };
243
+
244
+ namespacesByLocale = Object.keys(initialI18nStore).map(function (locale) {
245
+ return getLocaleNamespaces(_path["default"].resolve(process.cwd(), "".concat(localePath, "/").concat(locale)));
246
+ });
247
+ namespacesRequired = flatNamespaces(namespacesByLocale);
248
+
249
+ case 27:
238
250
  namespacesRequired.forEach(function (ns) {
239
251
  for (var locale in initialI18nStore) {
240
252
  initialI18nStore[locale][ns] = (i18n.services.resourceStore.data[locale] || {})[ns] || {};
@@ -248,7 +260,7 @@ var serverSideTranslations = /*#__PURE__*/function () {
248
260
  }
249
261
  });
250
262
 
251
- case 24:
263
+ case 29:
252
264
  case "end":
253
265
  return _context.stop();
254
266
  }
@@ -12,20 +12,21 @@ export const appWithTranslation = (WrappedComponent, configOverride = null) => {
12
12
  const {
13
13
  _nextI18Next
14
14
  } = props.pageProps;
15
- let locale = null; // Memoize the instance and only re-initialize when either:
15
+ const locale = (_nextI18Next === null || _nextI18Next === void 0 ? void 0 : _nextI18Next.initialLocale) ?? null; // Memoize the instance and only re-initialize when either:
16
16
  // 1. The route changes (non-shallowly)
17
17
  // 2. Router locale changes
18
18
 
19
19
  const i18n = useMemo(() => {
20
+ var _userConfig;
21
+
20
22
  if (!_nextI18Next) return null;
21
23
  let {
22
24
  userConfig
23
25
  } = _nextI18Next;
24
26
  const {
25
- initialI18nStore,
26
- initialLocale
27
+ initialI18nStore
27
28
  } = _nextI18Next;
28
- locale = initialLocale;
29
+ const resources = configOverride !== null && configOverride !== void 0 && configOverride.resources ? configOverride.resources : initialI18nStore;
29
30
 
30
31
  if (userConfig === null && configOverride === null) {
31
32
  throw new Error('appWithTranslation was called without a next-i18next config');
@@ -35,7 +36,7 @@ export const appWithTranslation = (WrappedComponent, configOverride = null) => {
35
36
  userConfig = configOverride;
36
37
  }
37
38
 
38
- if (!userConfig?.i18n) {
39
+ if (!((_userConfig = userConfig) !== null && _userConfig !== void 0 && _userConfig.i18n)) {
39
40
  throw new Error('appWithTranslation was called without config.i18n');
40
41
  }
41
42
 
@@ -43,7 +44,7 @@ export const appWithTranslation = (WrappedComponent, configOverride = null) => {
43
44
  lng: locale
44
45
  }),
45
46
  lng: locale,
46
- resources: initialI18nStore
47
+ resources
47
48
  }).i18n;
48
49
  globalI18n = instance;
49
50
  return instance;
@@ -1,7 +1,9 @@
1
1
  import { defaultConfig } from './defaultConfig';
2
2
  const deepMergeObjects = ['backend', 'detection'];
3
3
  export const createConfig = userConfig => {
4
- if (typeof userConfig?.lng !== 'string') {
4
+ var _userConfig$use;
5
+
6
+ if (typeof (userConfig === null || userConfig === void 0 ? void 0 : userConfig.lng) !== 'string') {
5
7
  throw new Error('config.lng was not passed into createConfig');
6
8
  } //
7
9
  // Initial merge of default and user-provided config
@@ -42,7 +44,7 @@ export const createConfig = userConfig => {
42
44
  combinedConfig.fallbackLng = combinedConfig.defaultLocale;
43
45
  }
44
46
 
45
- const hasCustomBackend = userConfig?.use?.some(b => b.type === 'backend');
47
+ const hasCustomBackend = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$use = userConfig.use) === null || _userConfig$use === void 0 ? void 0 : _userConfig$use.some(b => b.type === 'backend');
46
48
 
47
49
  if (!process.browser && typeof window === 'undefined') {
48
50
  combinedConfig.preload = locales;
@@ -50,43 +52,67 @@ export const createConfig = userConfig => {
50
52
  if (!hasCustomBackend) {
51
53
  const fs = require('fs');
52
54
 
53
- const path = require('path');
54
-
55
- const serverLocalePath = localePath; //
55
+ const path = require('path'); //
56
56
  // Validate defaultNS
57
57
  // https://github.com/isaachinman/next-i18next/issues/358
58
58
  //
59
59
 
60
+
60
61
  if (typeof defaultNS === 'string' && typeof lng !== 'undefined') {
61
- const prefix = userConfig?.interpolation?.prefix ?? '{{';
62
- const suffix = userConfig?.interpolation?.suffix ?? '}}';
63
- const defaultLocaleStructure = localeStructure.replace(`${prefix}lng${suffix}`, lng).replace(`${prefix}ns${suffix}`, defaultNS);
64
- const defaultFile = `/${defaultLocaleStructure}.${localeExtension}`;
65
- const defaultNSPath = path.join(localePath, defaultFile);
66
- const defaultNSExists = fs.existsSync(defaultNSPath);
67
-
68
- if (!defaultNSExists && process.env.NODE_ENV !== 'production') {
69
- throw new Error(`Default namespace not found at ${defaultNSPath}`);
62
+ if (typeof localePath === 'string') {
63
+ var _userConfig$interpola, _userConfig$interpola2;
64
+
65
+ const prefix = (userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola = userConfig.interpolation) === null || _userConfig$interpola === void 0 ? void 0 : _userConfig$interpola.prefix) ?? '{{';
66
+ const suffix = (userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola2 = userConfig.interpolation) === null || _userConfig$interpola2 === void 0 ? void 0 : _userConfig$interpola2.suffix) ?? '}}';
67
+ const defaultLocaleStructure = localeStructure.replace(`${prefix}lng${suffix}`, lng).replace(`${prefix}ns${suffix}`, defaultNS);
68
+ const defaultFile = `/${defaultLocaleStructure}.${localeExtension}`;
69
+ const defaultNSPath = path.join(localePath, defaultFile);
70
+ const defaultNSExists = fs.existsSync(defaultNSPath);
71
+
72
+ if (!defaultNSExists && process.env.NODE_ENV !== 'production') {
73
+ throw new Error(`Default namespace not found at ${defaultNSPath}`);
74
+ }
75
+ } else if (typeof localePath === 'function') {
76
+ const defaultNSPath = localePath(lng, defaultNS, false);
77
+ const defaultNSExists = fs.existsSync(defaultNSPath);
78
+
79
+ if (!defaultNSExists && process.env.NODE_ENV !== 'production') {
80
+ throw new Error(`Default namespace not found at ${defaultNSPath}`);
81
+ }
70
82
  }
71
83
  } //
72
84
  // Set server side backend
73
85
  //
74
86
 
75
87
 
76
- combinedConfig.backend = {
77
- addPath: path.resolve(process.cwd(), `${serverLocalePath}/${localeStructure}.missing.${localeExtension}`),
78
- loadPath: path.resolve(process.cwd(), `${serverLocalePath}/${localeStructure}.${localeExtension}`)
79
- }; //
88
+ if (typeof localePath === 'string') {
89
+ combinedConfig.backend = {
90
+ addPath: path.resolve(process.cwd(), `${localePath}/${localeStructure}.missing.${localeExtension}`),
91
+ loadPath: path.resolve(process.cwd(), `${localePath}/${localeStructure}.${localeExtension}`)
92
+ };
93
+ } else if (typeof localePath === 'function') {
94
+ combinedConfig.backend = {
95
+ addPath: (locale, namespace) => localePath(locale, namespace, true),
96
+ loadPath: (locale, namespace) => localePath(locale, namespace, false)
97
+ };
98
+ } else {
99
+ throw new Error(`Unsupported localePath type: ${typeof localePath}`);
100
+ } //
80
101
  // Set server side preload (namespaces)
81
102
  //
82
103
 
104
+
83
105
  if (!combinedConfig.ns && typeof lng !== 'undefined') {
106
+ if (typeof localePath === 'function') {
107
+ throw new Error('Must provide all namespaces in ns option if using a function as localePath');
108
+ }
109
+
84
110
  const unique = list => Array.from(new Set(list));
85
111
 
86
112
  const getNamespaces = locales => {
87
113
  const getLocaleNamespaces = p => fs.readdirSync(p).map(file => file.replace(`.${localeExtension}`, ''));
88
114
 
89
- const namespacesByLocale = locales.map(locale => getLocaleNamespaces(path.resolve(process.cwd(), `${serverLocalePath}/${locale}`)));
115
+ const namespacesByLocale = locales.map(locale => getLocaleNamespaces(path.resolve(process.cwd(), `${localePath}/${locale}`)));
90
116
  const allNamespaces = [];
91
117
 
92
118
  for (const localNamespaces of namespacesByLocale) {
@@ -117,22 +143,21 @@ export const createConfig = userConfig => {
117
143
  }
118
144
  }
119
145
  } else {
120
- let clientLocalePath = localePath; //
121
- // Remove public prefix from client site config
122
146
  //
123
-
124
- if (localePath.match(/^\.?\/public\//)) {
125
- clientLocalePath = localePath.replace(/^\.?\/public/, '');
126
- } //
127
147
  // Set client side backend, if there is no custom backend
128
148
  //
129
-
130
-
131
149
  if (!hasCustomBackend) {
132
- combinedConfig.backend = {
133
- addPath: `${clientLocalePath}/${localeStructure}.missing.${localeExtension}`,
134
- loadPath: `${clientLocalePath}/${localeStructure}.${localeExtension}`
135
- };
150
+ if (typeof localePath === 'string') {
151
+ combinedConfig.backend = {
152
+ addPath: `${localePath}/${localeStructure}.missing.${localeExtension}`,
153
+ loadPath: `${localePath}/${localeStructure}.${localeExtension}`
154
+ };
155
+ } else if (typeof localePath === 'function') {
156
+ combinedConfig.backend = {
157
+ addPath: (locale, namespace) => localePath(locale, namespace, true),
158
+ loadPath: (locale, namespace) => localePath(locale, namespace, false)
159
+ };
160
+ }
136
161
  }
137
162
 
138
163
  if (typeof combinedConfig.ns !== 'string' && !Array.isArray(combinedConfig.ns)) {
@@ -4,7 +4,9 @@ export default (config => {
4
4
  let initPromise;
5
5
 
6
6
  if (!instance.isInitialized) {
7
- config?.use?.forEach(x => instance.use(x));
7
+ var _config$use;
8
+
9
+ config === null || config === void 0 ? void 0 : (_config$use = config.use) === null || _config$use === void 0 ? void 0 : _config$use.forEach(x => instance.use(x));
8
10
  initPromise = instance.init(config);
9
11
  } else {
10
12
  initPromise = Promise.resolve(i18n.t);
@@ -16,13 +16,15 @@ export default (config => {
16
16
  let initPromise;
17
17
 
18
18
  if (!instance.isInitialized) {
19
- const hasCustomBackend = config?.use?.some(b => b.type === 'backend');
19
+ var _config$use, _config$use2;
20
+
21
+ const hasCustomBackend = config === null || config === void 0 ? void 0 : (_config$use = config.use) === null || _config$use === void 0 ? void 0 : _config$use.some(b => b.type === 'backend');
20
22
 
21
23
  if (!hasCustomBackend) {
22
24
  instance.use(i18nextFSBackend);
23
25
  }
24
26
 
25
- config?.use?.forEach(x => instance.use(x));
27
+ config === null || config === void 0 ? void 0 : (_config$use2 = config.use) === null || _config$use2 === void 0 ? void 0 : _config$use2.forEach(x => instance.use(x));
26
28
  initPromise = instance.init(config);
27
29
  } else {
28
30
  initPromise = Promise.resolve(i18n.t);
@@ -57,7 +57,7 @@ export const serverSideTranslations = async (initialLocale, namespacesRequired =
57
57
  } = config;
58
58
 
59
59
  if (reloadOnPrerender) {
60
- await globalI18n?.reloadResources();
60
+ await (globalI18n === null || globalI18n === void 0 ? void 0 : globalI18n.reloadResources());
61
61
  }
62
62
 
63
63
  const {
@@ -75,6 +75,10 @@ export const serverSideTranslations = async (initialLocale, namespacesRequired =
75
75
  });
76
76
 
77
77
  if (!Array.isArray(namespacesRequired)) {
78
+ if (typeof localePath === 'function') {
79
+ throw new Error('Must provide namespacesRequired to serverSideTranslations when using a function as localePath');
80
+ }
81
+
78
82
  const getLocaleNamespaces = path => fs.readdirSync(path).map(file => file.replace(`.${localeExtension}`, ''));
79
83
 
80
84
  const namespacesByLocale = Object.keys(initialI18nStore).map(locale => getLocaleNamespaces(path.resolve(process.cwd(), `${localePath}/${locale}`)));
@@ -17,9 +17,11 @@ export var appWithTranslation = function appWithTranslation(WrappedComponent) {
17
17
  var configOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
18
18
 
19
19
  var AppWithTranslation = function AppWithTranslation(props) {
20
+ var _nextI18Next$initialL;
21
+
20
22
  var _ref = props.pageProps,
21
23
  _nextI18Next = _ref._nextI18Next;
22
- var locale = null; // Memoize the instance and only re-initialize when either:
24
+ var locale = (_nextI18Next$initialL = _nextI18Next === null || _nextI18Next === void 0 ? void 0 : _nextI18Next.initialLocale) !== null && _nextI18Next$initialL !== void 0 ? _nextI18Next$initialL : null; // Memoize the instance and only re-initialize when either:
23
25
  // 1. The route changes (non-shallowly)
24
26
  // 2. Router locale changes
25
27
 
@@ -28,9 +30,8 @@ export var appWithTranslation = function appWithTranslation(WrappedComponent) {
28
30
 
29
31
  if (!_nextI18Next) return null;
30
32
  var userConfig = _nextI18Next.userConfig;
31
- var initialI18nStore = _nextI18Next.initialI18nStore,
32
- initialLocale = _nextI18Next.initialLocale;
33
- locale = initialLocale;
33
+ var initialI18nStore = _nextI18Next.initialI18nStore;
34
+ var resources = configOverride !== null && configOverride !== void 0 && configOverride.resources ? configOverride.resources : initialI18nStore;
34
35
 
35
36
  if (userConfig === null && configOverride === null) {
36
37
  throw new Error('appWithTranslation was called without a next-i18next config');
@@ -48,7 +49,7 @@ export var appWithTranslation = function appWithTranslation(WrappedComponent) {
48
49
  lng: locale
49
50
  }))), {}, {
50
51
  lng: locale,
51
- resources: initialI18nStore
52
+ resources: resources
52
53
  })).i18n;
53
54
  globalI18n = instance;
54
55
  return instance;
@@ -1,7 +1,9 @@
1
- import _typeof from "@babel/runtime/helpers/typeof";
2
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
+ import _typeof from "@babel/runtime/helpers/typeof";
3
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
5
+ var _excluded = ["i18n"],
6
+ _excluded2 = ["i18n"];
5
7
 
6
8
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
7
9
 
@@ -26,10 +28,10 @@ export var createConfig = function createConfig(userConfig) {
26
28
 
27
29
 
28
30
  var userI18n = userConfig.i18n,
29
- userConfigStripped = _objectWithoutProperties(userConfig, ["i18n"]);
31
+ userConfigStripped = _objectWithoutProperties(userConfig, _excluded);
30
32
 
31
33
  var defaultI18n = defaultConfig.i18n,
32
- defaultConfigStripped = _objectWithoutProperties(defaultConfig, ["i18n"]);
34
+ defaultConfigStripped = _objectWithoutProperties(defaultConfig, _excluded2);
33
35
 
34
36
  var combinedConfig = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, defaultConfigStripped), userConfigStripped), defaultI18n), userI18n);
35
37
 
@@ -62,39 +64,66 @@ export var createConfig = function createConfig(userConfig) {
62
64
  if (!hasCustomBackend) {
63
65
  var fs = require('fs');
64
66
 
65
- var path = require('path');
66
-
67
- var serverLocalePath = localePath; //
67
+ var path = require('path'); //
68
68
  // Validate defaultNS
69
69
  // https://github.com/isaachinman/next-i18next/issues/358
70
70
  //
71
71
 
72
+
72
73
  if (typeof defaultNS === 'string' && typeof lng !== 'undefined') {
73
- var _userConfig$interpola, _userConfig$interpola2, _userConfig$interpola3, _userConfig$interpola4;
74
+ if (typeof localePath === 'string') {
75
+ var _userConfig$interpola, _userConfig$interpola2, _userConfig$interpola3, _userConfig$interpola4;
76
+
77
+ var prefix = (_userConfig$interpola = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola2 = userConfig.interpolation) === null || _userConfig$interpola2 === void 0 ? void 0 : _userConfig$interpola2.prefix) !== null && _userConfig$interpola !== void 0 ? _userConfig$interpola : '{{';
78
+ var suffix = (_userConfig$interpola3 = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola4 = userConfig.interpolation) === null || _userConfig$interpola4 === void 0 ? void 0 : _userConfig$interpola4.suffix) !== null && _userConfig$interpola3 !== void 0 ? _userConfig$interpola3 : '}}';
79
+ var defaultLocaleStructure = localeStructure.replace("".concat(prefix, "lng").concat(suffix), lng).replace("".concat(prefix, "ns").concat(suffix), defaultNS);
80
+ var defaultFile = "/".concat(defaultLocaleStructure, ".").concat(localeExtension);
81
+ var defaultNSPath = path.join(localePath, defaultFile);
82
+ var defaultNSExists = fs.existsSync(defaultNSPath);
83
+
84
+ if (!defaultNSExists && process.env.NODE_ENV !== 'production') {
85
+ throw new Error("Default namespace not found at ".concat(defaultNSPath));
86
+ }
87
+ } else if (typeof localePath === 'function') {
88
+ var _defaultNSPath = localePath(lng, defaultNS, false);
74
89
 
75
- var prefix = (_userConfig$interpola = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola2 = userConfig.interpolation) === null || _userConfig$interpola2 === void 0 ? void 0 : _userConfig$interpola2.prefix) !== null && _userConfig$interpola !== void 0 ? _userConfig$interpola : '{{';
76
- var suffix = (_userConfig$interpola3 = userConfig === null || userConfig === void 0 ? void 0 : (_userConfig$interpola4 = userConfig.interpolation) === null || _userConfig$interpola4 === void 0 ? void 0 : _userConfig$interpola4.suffix) !== null && _userConfig$interpola3 !== void 0 ? _userConfig$interpola3 : '}}';
77
- var defaultLocaleStructure = localeStructure.replace("".concat(prefix, "lng").concat(suffix), lng).replace("".concat(prefix, "ns").concat(suffix), defaultNS);
78
- var defaultFile = "/".concat(defaultLocaleStructure, ".").concat(localeExtension);
79
- var defaultNSPath = path.join(localePath, defaultFile);
80
- var defaultNSExists = fs.existsSync(defaultNSPath);
90
+ var _defaultNSExists = fs.existsSync(_defaultNSPath);
81
91
 
82
- if (!defaultNSExists && process.env.NODE_ENV !== 'production') {
83
- throw new Error("Default namespace not found at ".concat(defaultNSPath));
92
+ if (!_defaultNSExists && process.env.NODE_ENV !== 'production') {
93
+ throw new Error("Default namespace not found at ".concat(_defaultNSPath));
94
+ }
84
95
  }
85
96
  } //
86
97
  // Set server side backend
87
98
  //
88
99
 
89
100
 
90
- combinedConfig.backend = {
91
- addPath: path.resolve(process.cwd(), "".concat(serverLocalePath, "/").concat(localeStructure, ".missing.").concat(localeExtension)),
92
- loadPath: path.resolve(process.cwd(), "".concat(serverLocalePath, "/").concat(localeStructure, ".").concat(localeExtension))
93
- }; //
101
+ if (typeof localePath === 'string') {
102
+ combinedConfig.backend = {
103
+ addPath: path.resolve(process.cwd(), "".concat(localePath, "/").concat(localeStructure, ".missing.").concat(localeExtension)),
104
+ loadPath: path.resolve(process.cwd(), "".concat(localePath, "/").concat(localeStructure, ".").concat(localeExtension))
105
+ };
106
+ } else if (typeof localePath === 'function') {
107
+ combinedConfig.backend = {
108
+ addPath: function addPath(locale, namespace) {
109
+ return localePath(locale, namespace, true);
110
+ },
111
+ loadPath: function loadPath(locale, namespace) {
112
+ return localePath(locale, namespace, false);
113
+ }
114
+ };
115
+ } else {
116
+ throw new Error("Unsupported localePath type: ".concat(_typeof(localePath)));
117
+ } //
94
118
  // Set server side preload (namespaces)
95
119
  //
96
120
 
121
+
97
122
  if (!combinedConfig.ns && typeof lng !== 'undefined') {
123
+ if (typeof localePath === 'function') {
124
+ throw new Error('Must provide all namespaces in ns option if using a function as localePath');
125
+ }
126
+
98
127
  var unique = function unique(list) {
99
128
  return Array.from(new Set(list));
100
129
  };
@@ -107,7 +136,7 @@ export var createConfig = function createConfig(userConfig) {
107
136
  };
108
137
 
109
138
  var namespacesByLocale = locales.map(function (locale) {
110
- return getLocaleNamespaces(path.resolve(process.cwd(), "".concat(serverLocalePath, "/").concat(locale)));
139
+ return getLocaleNamespaces(path.resolve(process.cwd(), "".concat(localePath, "/").concat(locale)));
111
140
  });
112
141
  var allNamespaces = [];
113
142
 
@@ -151,22 +180,25 @@ export var createConfig = function createConfig(userConfig) {
151
180
  }
152
181
  }
153
182
  } else {
154
- var clientLocalePath = localePath; //
155
- // Remove public prefix from client site config
156
183
  //
157
-
158
- if (localePath.match(/^\.?\/public\//)) {
159
- clientLocalePath = localePath.replace(/^\.?\/public/, '');
160
- } //
161
184
  // Set client side backend, if there is no custom backend
162
185
  //
163
-
164
-
165
186
  if (!hasCustomBackend) {
166
- combinedConfig.backend = {
167
- addPath: "".concat(clientLocalePath, "/").concat(localeStructure, ".missing.").concat(localeExtension),
168
- loadPath: "".concat(clientLocalePath, "/").concat(localeStructure, ".").concat(localeExtension)
169
- };
187
+ if (typeof localePath === 'string') {
188
+ combinedConfig.backend = {
189
+ addPath: "".concat(localePath, "/").concat(localeStructure, ".missing.").concat(localeExtension),
190
+ loadPath: "".concat(localePath, "/").concat(localeStructure, ".").concat(localeExtension)
191
+ };
192
+ } else if (typeof localePath === 'function') {
193
+ combinedConfig.backend = {
194
+ addPath: function addPath(locale, namespace) {
195
+ return localePath(locale, namespace, true);
196
+ },
197
+ loadPath: function loadPath(locale, namespace) {
198
+ return localePath(locale, namespace, false);
199
+ }
200
+ };
201
+ }
170
202
  }
171
203
 
172
204
  if (typeof combinedConfig.ns !== 'string' && !Array.isArray(combinedConfig.ns)) {
@@ -1,8 +1,8 @@
1
- import _regeneratorRuntime from "@babel/runtime/regenerator";
2
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
2
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
4
3
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
5
4
  import _typeof from "@babel/runtime/helpers/typeof";
5
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
6
6
 
7
7
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
8
8
 
@@ -140,19 +140,31 @@ export var serverSideTranslations = /*#__PURE__*/function () {
140
140
  initialI18nStore[lng] = {};
141
141
  });
142
142
 
143
- if (!Array.isArray(namespacesRequired)) {
144
- getLocaleNamespaces = function getLocaleNamespaces(path) {
145
- return fs.readdirSync(path).map(function (file) {
146
- return file.replace(".".concat(localeExtension), '');
147
- });
148
- };
143
+ if (Array.isArray(namespacesRequired)) {
144
+ _context.next = 27;
145
+ break;
146
+ }
149
147
 
150
- namespacesByLocale = Object.keys(initialI18nStore).map(function (locale) {
151
- return getLocaleNamespaces(path.resolve(process.cwd(), "".concat(localePath, "/").concat(locale)));
152
- });
153
- namespacesRequired = flatNamespaces(namespacesByLocale);
148
+ if (!(typeof localePath === 'function')) {
149
+ _context.next = 24;
150
+ break;
154
151
  }
155
152
 
153
+ throw new Error('Must provide namespacesRequired to serverSideTranslations when using a function as localePath');
154
+
155
+ case 24:
156
+ getLocaleNamespaces = function getLocaleNamespaces(path) {
157
+ return fs.readdirSync(path).map(function (file) {
158
+ return file.replace(".".concat(localeExtension), '');
159
+ });
160
+ };
161
+
162
+ namespacesByLocale = Object.keys(initialI18nStore).map(function (locale) {
163
+ return getLocaleNamespaces(path.resolve(process.cwd(), "".concat(localePath, "/").concat(locale)));
164
+ });
165
+ namespacesRequired = flatNamespaces(namespacesByLocale);
166
+
167
+ case 27:
156
168
  namespacesRequired.forEach(function (ns) {
157
169
  for (var locale in initialI18nStore) {
158
170
  initialI18nStore[locale][ns] = (i18n.services.resourceStore.data[locale] || {})[ns] || {};
@@ -166,7 +178,7 @@ export var serverSideTranslations = /*#__PURE__*/function () {
166
178
  }
167
179
  });
168
180
 
169
- case 24:
181
+ case 29:
170
182
  case "end":
171
183
  return _context.stop();
172
184
  }
@@ -8,7 +8,7 @@ declare type NextJsI18NConfig = {
8
8
  export declare type UserConfig = {
9
9
  i18n: NextJsI18NConfig;
10
10
  localeExtension?: string;
11
- localePath?: string;
11
+ localePath?: string | ((locale: string, namespace: string, missing: boolean) => string);
12
12
  localeStructure?: string;
13
13
  reloadOnPrerender?: boolean;
14
14
  serializeConfig?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-i18next",
3
- "version": "10.2.0",
3
+ "version": "10.5.0",
4
4
  "repository": "git@github.com:isaachinman/next-i18next.git",
5
5
  "author": "Isaac Hinman <isaac@isaachinman.com>",
6
6
  "funding": {
@@ -72,7 +72,7 @@
72
72
  "@babel/preset-env": "^7.10.4",
73
73
  "@babel/preset-react": "^7.10.4",
74
74
  "@babel/preset-typescript": "^7.10.4",
75
- "@testing-library/react": "^11.2.5",
75
+ "@testing-library/react": "^12.1.3",
76
76
  "@types/i18next-fs-backend": "^1.1.2",
77
77
  "@types/jest": "^27.0.1",
78
78
  "@types/node": "^16.7.1",
@@ -96,7 +96,7 @@
96
96
  "eslint-plugin-typescript-sort-keys": "^2.1.0",
97
97
  "husky": "^3.0.0",
98
98
  "jest": "^26.6.3",
99
- "next": "^10.0.4",
99
+ "next": "^12.1.0",
100
100
  "react": "^17.0.1",
101
101
  "react-dom": "^17.0.1",
102
102
  "start-server-and-test": "^1.12.0",
@@ -107,9 +107,9 @@
107
107
  "@types/hoist-non-react-statics": "^3.3.1",
108
108
  "core-js": "^3",
109
109
  "hoist-non-react-statics": "^3.2.0",
110
- "i18next": "^21.5.3",
110
+ "i18next": "^21.6.12",
111
111
  "i18next-fs-backend": "^1.0.7",
112
- "react-i18next": "^11.8.13"
112
+ "react-i18next": "^11.15.5"
113
113
  },
114
114
  "peerDependencies": {
115
115
  "next": ">= 10.0.0",
package/vercel.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "github": {
3
+ "silent": true
4
+ }
5
+ }