react-localization 2.0.6 → 2.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.1.0] - 2026-10-04
9
+
10
+ Behaviour of `formatString` is now aligned with the
11
+ [`localized-strings`](https://github.com/stefalda/localized-strings) package,
12
+ which this library builds upon. The override still exists only to add React
13
+ (JSX) interpolation, but it no longer drops the base package features.
14
+
15
+ ### Added
16
+
17
+ - `formatString` now expands `$ref{key}` references, e.g.
18
+ `strings.formatString(strings.questionWithReferences)`.
19
+ - `formatString` accepts a string key directly, with dot-notation support, e.g.
20
+ `strings.formatString("fridge.bread")`.
21
+ - `getContent()` added to the public TypeScript types (aligned with
22
+ `localized-strings` 2.1.x).
23
+
24
+ ### Fixed
25
+
26
+ - `$ref{key}` placeholders are no longer silently dropped. Previously the inner
27
+ `{key}` was treated as an unknown named placeholder and removed from the
28
+ output.
29
+ - `require("react-localization")` now returns the library instead of an empty
30
+ object. The UMD bundle is emitted as `.cjs` and `main`/`exports.require` point
31
+ to it; previously the `require` entry resolved to an ES module that could not
32
+ be loaded as CommonJS.
33
+ - `getString()` is now correctly typed as returning `string | null`.
34
+ - The placeholder regular expression no longer contains a literal `|`
35
+ (`/(\{[\d|\w]+\})/` → `/(\{\w+\})/`).
36
+ - Tests work again with jsdom 30, where `navigator` is a getter-only property
37
+ (`vi.stubGlobal` is used instead of direct assignment).
38
+
39
+ ### Changed
40
+
41
+ - React element interpolation now uses `React.cloneElement` to assign keys
42
+ instead of spreading the element object, which is discouraged in React 19.
43
+ - The test suite runs against `src/` through a Vitest alias instead of the
44
+ built `lib/` artifact, so a stale build can no longer make the suite pass or
45
+ fail incorrectly.
46
+ - `npm test` runs the suite once (`vitest run`); `npm run test:watch` is
47
+ available for watch mode.
48
+ - The library instance's `formatString` is now a regular method (previously an
49
+ arrow function) so it can access the instance for `$ref`/key resolution.
50
+
51
+ ### Dependencies
52
+
53
+ - `localized-strings`: `^2.0.3` → `^2.1.2`
54
+ - `typescript`: `^5.7.3` → `^6.0.3`
55
+ - `react` / `react-dom`: `^19.0.0` → `^19.3.0`
56
+ - `@types/react` / `@types/react-dom`: `^19.0.4` / `^19.0.2` → `^19.3.0`
57
+ - `@types/node`: `^22.10.5` → `^26.6.4`
58
+ - `vite`: `^6.0.7` → `^8.3.2`
59
+ - `vitest`: `^2.1.8` → `^5.0.3`
60
+ - `@vitejs/plugin-react`: `^4.3.4` → `^6.1.1`
61
+ - `jsdom`: `^26.0.0` → `^30.1.1`
62
+ - `@testing-library/react`: `^16.1.0` → `^16.3.3`
63
+
64
+ ### Notes
65
+
66
+ - TypeScript 6 requires an explicit `rootDir` when `declarationDir` is used.
67
+ - The Vite config now uses `import.meta.dirname` instead of `__dirname`.
68
+
69
+ [2.1.0]: https://github.com/stefalda/react-localization/compare/v2.0.6...v2.1.0
package/README.md CHANGED
@@ -126,6 +126,13 @@ strings.setContent({
126
126
  day: 12,
127
127
  year: 2018
128
128
  })
129
+
130
+ // A key can also be passed directly (dot-notation is supported)
131
+ strings.formatString('fridge.bread')
132
+
133
+ // Other localized strings can be referenced with $ref{key}
134
+ // en: { bread: 'bread', questionWithReferences: 'I\'d like $ref{bread}' }
135
+ strings.formatString(strings.questionWithReferences)
129
136
  ```
130
137
 
131
138
  Typical usage is to render it in a JSX with `formatString` calls inlined:
@@ -149,6 +156,9 @@ Typical usage is to render it in a JSX with `formatString` calls inlined:
149
156
  **Beware: do not define a string key as formatString!**
150
157
 
151
158
  - setContent(props) - to dynamically load another set of strings
159
+ - getContent() - to get the whole content object (all languages/strings)
160
+ - getString(key, language?, omitWarning?) - to get a single string, dot-notation
161
+ supported
152
162
  - getAvailableLanguages() - to get an array of the languages passed in the
153
163
  constructor
154
164
 
@@ -39,12 +39,16 @@ declare module 'react-localization' {
39
39
  * @param key
40
40
  * @param language
41
41
  */
42
- getString(key: string, language?: string, omitWarning?: boolean): string;
42
+ getString(key: string, language?: string | null, omitWarning?: boolean): string | null;
43
43
  /**
44
44
  * Replace the NamedLocalization object without reinstantiating the object
45
45
  * @param props
46
46
  */
47
47
  setContent(props: any): void;
48
+ /**
49
+ * Return the whole content object (all the languages/strings)
50
+ */
51
+ getContent(): GlobalStrings<any>;
48
52
  }
49
53
  export type LocalizedStrings<T> = LocalizedStringsMethods & T;
50
54
  type GetInterfaceLanguageCallback = () => string;
@@ -1,188 +1,155 @@
1
- import c from "react";
2
- var p = Object.defineProperty, _ = (n, e, t) => e in n ? p(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t, o = (n, e, t) => _(n, typeof e != "symbol" ? e + "" : e, t);
3
- function d() {
4
- const n = "en-US";
5
- if (typeof navigator > "u")
6
- return n;
7
- const e = navigator;
8
- if (e) {
9
- if (e.language)
10
- return e.language;
11
- if (e.languages && e.languages[0])
12
- return e.languages[0];
13
- if ("userLanguage" in e)
14
- return e.userLanguage;
15
- if ("browserLanguage" in e)
16
- return e.browserLanguage;
17
- }
18
- return n;
1
+ import e from "react";
2
+ //#region node_modules/localized-strings/lib/LocalizedStrings.es.js
3
+ function t() {
4
+ let e = "en-US";
5
+ if (typeof navigator > "u") return e;
6
+ let t = navigator;
7
+ if (t) {
8
+ if (t.language) return t.language;
9
+ if (t.languages && t.languages[0]) return t.languages[0];
10
+ if ("userLanguage" in t) return t.userLanguage;
11
+ if ("browserLanguage" in t) return t.browserLanguage;
12
+ }
13
+ return e;
19
14
  }
20
- function L(n, e) {
21
- if (e[n]) return n;
22
- const t = n.indexOf("-"), a = t >= 0 ? n.substring(0, t) : n;
23
- return e[a] ? a : Object.keys(e)[0];
15
+ function n(e, t) {
16
+ if (t[e]) return e;
17
+ let n = e.indexOf("-"), r = n >= 0 ? e.substring(0, n) : e;
18
+ return t[r] ? r : Object.keys(t)[0];
24
19
  }
25
- function b(n) {
26
- const e = [
27
- "_interfaceLanguage",
28
- "_language",
29
- "_defaultLanguage",
30
- "_defaultLanguageFirstLevelKeys",
31
- "_props"
32
- ];
33
- n.forEach((t) => {
34
- if (e.indexOf(t) !== -1)
35
- throw new Error(`${t} cannot be used as a key. It is a reserved word.`);
36
- });
20
+ function r(e) {
21
+ let t = [
22
+ "_interfaceLanguage",
23
+ "_language",
24
+ "_defaultLanguage",
25
+ "_defaultLanguageFirstLevelKeys",
26
+ "_props"
27
+ ];
28
+ e.forEach((e) => {
29
+ if (t.indexOf(e) !== -1) throw Error(`${e} cannot be used as a key. It is a reserved word.`);
30
+ });
37
31
  }
38
- function y(n) {
39
- let e = "";
40
- const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
41
- for (let a = 0; a < n; a += 1)
42
- e += t.charAt(Math.floor(Math.random() * t.length));
43
- return e;
32
+ function i(e) {
33
+ let t = "";
34
+ for (let n = 0; n < e; n += 1) t += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(Math.random() * 62));
35
+ return t;
44
36
  }
45
- const l = /(\{[\d|\w]+\})/, f = /(\$ref\{[\w|.]+\})/;
46
- class m {
47
- /**
48
- * Constructor used to provide the strings objects in various language and the optional callback to get
49
- * the interface language
50
- * @param props - the strings object
51
- * @param options - configuration options
52
- */
53
- constructor(e, t) {
54
- o(this, "_opts"), o(this, "_interfaceLanguage"), o(this, "_language"), o(this, "_defaultLanguage"), o(this, "_defaultLanguageFirstLevelKeys"), o(this, "_props"), o(this, "_availableLanguages"), typeof t == "function" && (t = { customLanguageInterface: t }), this._opts = {
55
- customLanguageInterface: d,
56
- pseudo: !1,
57
- pseudoMultipleLanguages: !1,
58
- logsEnabled: !0,
59
- ...t
60
- }, this._interfaceLanguage = this._opts.customLanguageInterface(), this._language = this._interfaceLanguage, this.setContent(e);
61
- }
62
- /**
63
- * Set the strings objects based on the parameter passed in the constructor
64
- */
65
- setContent(e) {
66
- const [t] = Object.keys(e);
67
- this._defaultLanguage = t, this._defaultLanguageFirstLevelKeys = [], this._props = e, b(Object.keys(e[this._defaultLanguage])), Object.keys(this._props[this._defaultLanguage]).forEach((a) => {
68
- typeof this._props[this._defaultLanguage][a] == "string" && this._defaultLanguageFirstLevelKeys.push(a);
69
- }), this.setLanguage(this._interfaceLanguage), this._opts.pseudo && this._pseudoAllValues(this._props);
70
- }
71
- /**
72
- * Replace all strings to pseudo value
73
- */
74
- _pseudoAllValues(e) {
75
- Object.keys(e).forEach((t) => {
76
- if (typeof e[t] == "object")
77
- this._pseudoAllValues(e[t]);
78
- else if (typeof e[t] == "string") {
79
- if (e[t].indexOf("[") === 0 && e[t].lastIndexOf("]") === e[t].length - 1)
80
- return;
81
- const a = e[t].split(" ");
82
- for (let s = 0; s < a.length; s += 1) {
83
- if (a[s].match(l) || a[s].match(f))
84
- continue;
85
- let r = a[s].length;
86
- this._opts.pseudoMultipleLanguages && (r = Math.floor(r * 1.4)), a[s] = y(r);
87
- }
88
- e[t] = `[${a.join(" ")}]`;
89
- }
90
- });
91
- }
92
- /**
93
- * Can be used from outside the class to force a particular language
94
- * independently from the interface one
95
- */
96
- setLanguage(e) {
97
- const t = L(e, this._props), a = Object.keys(this._props)[0];
98
- if (this._language = t, this._props[t]) {
99
- for (const r of this._defaultLanguageFirstLevelKeys)
100
- delete this[r];
101
- let s = { ...this._props[this._language] };
102
- Object.keys(s).forEach((r) => {
103
- this[r] = s[r];
104
- }), a !== this._language && (s = this._props[a], this._fallbackValues(s, this));
105
- }
106
- }
107
- /**
108
- * Load fallback values for missing translations
109
- */
110
- _fallbackValues(e, t) {
111
- Object.keys(e).forEach((a) => {
112
- Object.prototype.hasOwnProperty.call(e, a) && !t[a] && t[a] !== "" ? (t[a] = e[a], this._opts.logsEnabled && console.log(
113
- `🚧 👷 key '${a}' not found in localizedStrings for language ${this._language} 🚧`
114
- )) : typeof t[a] != "string" && this._fallbackValues(e[a], t[a]);
115
- });
116
- }
117
- getLanguage() {
118
- return this._language;
119
- }
120
- getInterfaceLanguage() {
121
- return this._interfaceLanguage;
122
- }
123
- getAvailableLanguages() {
124
- return this._availableLanguages || (this._availableLanguages = Object.keys(this._props)), this._availableLanguages;
125
- }
126
- formatString(e, ...t) {
127
- let a = e || "";
128
- return typeof a == "string" && (a = this.getString(e, null, !0) || a), a.split(f).filter(Boolean).map((s) => {
129
- if (s.match(f)) {
130
- const r = s.slice(5, -1);
131
- return this.getString(r) || (this._opts.logsEnabled && console.log(
132
- `No Localization ref found for '${s}' in string '${e}'`
133
- ), `$ref(id:${r})`);
134
- }
135
- return s;
136
- }).join("").split(l).filter(Boolean).map((s) => {
137
- if (s.match(l)) {
138
- const r = s.slice(1, -1);
139
- let i = t[r];
140
- return i === void 0 && t[0] && (i = t[0][r]), i;
141
- }
142
- return s;
143
- }).join("");
144
- }
145
- getString(e, t, a = !1) {
146
- try {
147
- let s = this._props[t || this._language];
148
- const r = e.split(".");
149
- for (const i of r) {
150
- if (s[i] === void 0)
151
- throw new Error(i);
152
- s = s[i];
153
- }
154
- return s;
155
- } catch (s) {
156
- !a && this._opts.logsEnabled && console.log(
157
- `No localization found for key '${e}' and language '${t}', failed on ${s.message}`
158
- );
159
- }
160
- return null;
161
- }
162
- getContent() {
163
- return this._props;
164
- }
165
- }
166
- const h = /(\{[\d|\w]+\})/;
167
- m.prototype.formatString = (n, ...e) => {
168
- let t = !1;
169
- const a = (n || "").split(h).filter((s) => !!s).map((s, r) => {
170
- if (s.match(h)) {
171
- const i = s.slice(1, -1);
172
- let u = e[i];
173
- if (u == null) {
174
- const g = e[0] ? e[0][i] : void 0;
175
- if (g !== void 0)
176
- u = g;
177
- else
178
- return u;
179
- }
180
- return c.isValidElement(u) ? (t = !0, c.Children.toArray(u).map((g) => ({ ...g, key: r.toString() }))) : u;
181
- }
182
- return s;
183
- });
184
- return t ? a : a.join("");
185
- };
186
- export {
187
- m as default
37
+ var a = /(\{[\d|\w]+\})/, o = /(\$ref\{[\w|.]+\})/, s = class {
38
+ _opts;
39
+ _interfaceLanguage;
40
+ _language;
41
+ _defaultLanguage;
42
+ _defaultLanguageFirstLevelKeys;
43
+ _props;
44
+ _availableLanguages;
45
+ constructor(e, n) {
46
+ typeof n == "function" && (n = { customLanguageInterface: n }), this._opts = {
47
+ customLanguageInterface: t,
48
+ pseudo: !1,
49
+ pseudoMultipleLanguages: !1,
50
+ logsEnabled: !0,
51
+ ...n
52
+ }, this._interfaceLanguage = this._opts.customLanguageInterface(), this._language = this._interfaceLanguage, this.setContent(e);
53
+ }
54
+ setContent(e) {
55
+ let [t] = Object.keys(e);
56
+ this._defaultLanguage = t, this._defaultLanguageFirstLevelKeys = [], this._props = e, r(Object.keys(e[this._defaultLanguage])), Object.keys(this._props[this._defaultLanguage]).forEach((e) => {
57
+ typeof this._props[this._defaultLanguage][e] == "string" && this._defaultLanguageFirstLevelKeys.push(e);
58
+ }), this.setLanguage(this._interfaceLanguage), this._opts.pseudo && this._pseudoAllValues(this._props);
59
+ }
60
+ _pseudoAllValues(e) {
61
+ Object.keys(e).forEach((t) => {
62
+ if (typeof e[t] == "object") this._pseudoAllValues(e[t]);
63
+ else if (typeof e[t] == "string") {
64
+ if (e[t].indexOf("[") === 0 && e[t].lastIndexOf("]") === e[t].length - 1) return;
65
+ let n = e[t].split(" ");
66
+ for (let e = 0; e < n.length; e += 1) {
67
+ if (n[e].match(a) || n[e].match(o)) continue;
68
+ let t = n[e].length;
69
+ this._opts.pseudoMultipleLanguages && (t = Math.floor(t * 1.4)), n[e] = i(t);
70
+ }
71
+ e[t] = `[${n.join(" ")}]`;
72
+ }
73
+ });
74
+ }
75
+ setLanguage(e) {
76
+ let t = n(e, this._props), r = Object.keys(this._props)[0];
77
+ if (this._language = t, this._props[t]) {
78
+ for (let e of this._defaultLanguageFirstLevelKeys) delete this[e];
79
+ let e = { ...this._props[this._language] };
80
+ Object.keys(e).forEach((t) => {
81
+ this[t] = e[t];
82
+ }), r !== this._language && (e = this._props[r], this._fallbackValues(e, this));
83
+ }
84
+ }
85
+ _fallbackValues(e, t) {
86
+ Object.keys(e).forEach((n) => {
87
+ Object.prototype.hasOwnProperty.call(e, n) && !t[n] && t[n] !== "" ? (t[n] = e[n], this._opts.logsEnabled && console.log(`🚧 👷 key '${n}' not found in localizedStrings for language ${this._language} 🚧`)) : typeof t[n] != "string" && this._fallbackValues(e[n], t[n]);
88
+ });
89
+ }
90
+ getLanguage() {
91
+ return this._language;
92
+ }
93
+ getInterfaceLanguage() {
94
+ return this._interfaceLanguage;
95
+ }
96
+ getAvailableLanguages() {
97
+ return this._availableLanguages ||= Object.keys(this._props), this._availableLanguages;
98
+ }
99
+ formatString(e, ...t) {
100
+ let n = e || "";
101
+ return typeof n == "string" && (n = this.getString(e, null, !0) || n), n.split(o).filter(Boolean).map((t) => {
102
+ if (t.match(o)) {
103
+ let n = t.slice(5, -1);
104
+ return this.getString(n) || (this._opts.logsEnabled && console.log(`No Localization ref found for '${t}' in string '${e}'`), `$ref(id:${n})`);
105
+ }
106
+ return t;
107
+ }).join("").split(a).filter(Boolean).map((e) => {
108
+ if (e.match(a)) {
109
+ let n = e.slice(1, -1), r = t[n];
110
+ return r === void 0 && t[0] && (r = t[0][n]), r;
111
+ }
112
+ return e;
113
+ }).join("");
114
+ }
115
+ getString(e, t, n = !1) {
116
+ try {
117
+ let n = this._props[t || this._language], r = e.split(".");
118
+ for (let e of r) {
119
+ if (n[e] === void 0) throw Error(e);
120
+ n = n[e];
121
+ }
122
+ return n;
123
+ } catch (r) {
124
+ !n && this._opts.logsEnabled && console.log(`No localization found for key '${e}' and language '${t}', failed on ${r.message}`);
125
+ }
126
+ return null;
127
+ }
128
+ getContent() {
129
+ return this._props;
130
+ }
131
+ }, c = /(\{\w+\})/, l = /(\$ref\{[\w|.]+\})/;
132
+ s.prototype.formatString = function(t, ...n) {
133
+ let r = (t ? this.getString(t, null, !0) || t : "").split(l).filter((e) => !!e).map((e) => {
134
+ if (e.match(l)) {
135
+ let t = e.slice(5, -1);
136
+ return this.getString(t) || `$ref(id:${t})`;
137
+ }
138
+ return e;
139
+ }).join(""), i = !1, a = r.split(c).filter((e) => !!e).map((t, r) => {
140
+ if (t.match(c)) {
141
+ let a = t.slice(1, -1), o = n[a];
142
+ if (o == null) {
143
+ let e = n[0] ? n[0][a] : void 0;
144
+ if (e !== void 0) o = e;
145
+ else return o;
146
+ }
147
+ return e.isValidElement(o) ? (i = !0, e.Children.toArray(o).map((t) => e.cloneElement(t, { key: r.toString() }))) : o;
148
+ }
149
+ return t;
150
+ });
151
+ return i ? a : a.join("");
188
152
  };
153
+ var u = s;
154
+ //#endregion
155
+ export { u as default };
@@ -0,0 +1 @@
1
+ "use strict";(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t(require("react")):typeof define==`function`&&define.amd?define([`react`],t):(e=typeof globalThis<`u`?globalThis:e||self,e.ReactLocalization=t(e.React))})(this,function(e){var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;l<u;l++)d=c[l],!o.call(e,d)&&d!==a&&n(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e};e=((e,r,i)=>(i=e==null?{}:t(a(e)),s(r||!e||!e.__esModule||!o.call(e,`default`)?n(i,`default`,{value:e,enumerable:!0}):i,e)))(e,1);function c(){let e=`en-US`;if(typeof navigator>`u`)return e;let t=navigator;if(t){if(t.language)return t.language;if(t.languages&&t.languages[0])return t.languages[0];if(`userLanguage`in t)return t.userLanguage;if(`browserLanguage`in t)return t.browserLanguage}return e}function l(e,t){if(t[e])return e;let n=e.indexOf(`-`),r=n>=0?e.substring(0,n):e;return t[r]?r:Object.keys(t)[0]}function u(e){let t=[`_interfaceLanguage`,`_language`,`_defaultLanguage`,`_defaultLanguageFirstLevelKeys`,`_props`];e.forEach(e=>{if(t.indexOf(e)!==-1)throw Error(`${e} cannot be used as a key. It is a reserved word.`)})}function d(e){let t=``;for(let n=0;n<e;n+=1)t+=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`.charAt(Math.floor(Math.random()*62));return t}var f=/(\{[\d|\w]+\})/,p=/(\$ref\{[\w|.]+\})/,m=class{_opts;_interfaceLanguage;_language;_defaultLanguage;_defaultLanguageFirstLevelKeys;_props;_availableLanguages;constructor(e,t){typeof t==`function`&&(t={customLanguageInterface:t}),this._opts={customLanguageInterface:c,pseudo:!1,pseudoMultipleLanguages:!1,logsEnabled:!0,...t},this._interfaceLanguage=this._opts.customLanguageInterface(),this._language=this._interfaceLanguage,this.setContent(e)}setContent(e){let[t]=Object.keys(e);this._defaultLanguage=t,this._defaultLanguageFirstLevelKeys=[],this._props=e,u(Object.keys(e[this._defaultLanguage])),Object.keys(this._props[this._defaultLanguage]).forEach(e=>{typeof this._props[this._defaultLanguage][e]==`string`&&this._defaultLanguageFirstLevelKeys.push(e)}),this.setLanguage(this._interfaceLanguage),this._opts.pseudo&&this._pseudoAllValues(this._props)}_pseudoAllValues(e){Object.keys(e).forEach(t=>{if(typeof e[t]==`object`)this._pseudoAllValues(e[t]);else if(typeof e[t]==`string`){if(e[t].indexOf(`[`)===0&&e[t].lastIndexOf(`]`)===e[t].length-1)return;let n=e[t].split(` `);for(let e=0;e<n.length;e+=1){if(n[e].match(f)||n[e].match(p))continue;let t=n[e].length;this._opts.pseudoMultipleLanguages&&(t=Math.floor(t*1.4)),n[e]=d(t)}e[t]=`[${n.join(` `)}]`}})}setLanguage(e){let t=l(e,this._props),n=Object.keys(this._props)[0];if(this._language=t,this._props[t]){for(let e of this._defaultLanguageFirstLevelKeys)delete this[e];let e={...this._props[this._language]};Object.keys(e).forEach(t=>{this[t]=e[t]}),n!==this._language&&(e=this._props[n],this._fallbackValues(e,this))}}_fallbackValues(e,t){Object.keys(e).forEach(n=>{Object.prototype.hasOwnProperty.call(e,n)&&!t[n]&&t[n]!==``?(t[n]=e[n],this._opts.logsEnabled&&console.log(`🚧 👷 key '${n}' not found in localizedStrings for language ${this._language} 🚧`)):typeof t[n]!=`string`&&this._fallbackValues(e[n],t[n])})}getLanguage(){return this._language}getInterfaceLanguage(){return this._interfaceLanguage}getAvailableLanguages(){return this._availableLanguages||=Object.keys(this._props),this._availableLanguages}formatString(e,...t){let n=e||``;return typeof n==`string`&&(n=this.getString(e,null,!0)||n),n.split(p).filter(Boolean).map(t=>{if(t.match(p)){let n=t.slice(5,-1);return this.getString(n)||(this._opts.logsEnabled&&console.log(`No Localization ref found for '${t}' in string '${e}'`),`$ref(id:${n})`)}return t}).join(``).split(f).filter(Boolean).map(e=>{if(e.match(f)){let n=e.slice(1,-1),r=t[n];return r===void 0&&t[0]&&(r=t[0][n]),r}return e}).join(``)}getString(e,t,n=!1){try{let n=this._props[t||this._language],r=e.split(`.`);for(let e of r){if(n[e]===void 0)throw Error(e);n=n[e]}return n}catch(r){!n&&this._opts.logsEnabled&&console.log(`No localization found for key '${e}' and language '${t}', failed on ${r.message}`)}return null}getContent(){return this._props}},h=/(\{\w+\})/,g=/(\$ref\{[\w|.]+\})/;return m.prototype.formatString=function(t,...n){let r=(t?this.getString(t,null,!0)||t:``).split(g).filter(e=>!!e).map(e=>{if(e.match(g)){let t=e.slice(5,-1);return this.getString(t)||`$ref(id:${t})`}return e}).join(``),i=!1,a=r.split(h).filter(e=>!!e).map((t,r)=>{if(t.match(h)){let a=t.slice(1,-1),o=n[a];if(o==null){let e=n[0]?n[0][a]:void 0;if(e!==void 0)o=e;else return o}return e.default.isValidElement(o)?(i=!0,e.default.Children.toArray(o).map(t=>e.default.cloneElement(t,{key:r.toString()}))):o}return t});return i?a:a.join(``)},m});
package/package.json CHANGED
@@ -1,24 +1,26 @@
1
1
  {
2
2
  "name": "react-localization",
3
- "version": "2.0.6",
3
+ "version": "2.1.0",
4
4
  "description": "Simple module to localize the React interface using the same syntax used in the ReactNativeLocalization module",
5
5
  "type": "module",
6
- "main": "./lib/react-localization.umd.js",
6
+ "main": "./lib/react-localization.umd.cjs",
7
7
  "module": "./lib/react-localization.es.js",
8
8
  "types": "./lib/react-localization.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "require": "./lib/react-localization.es.js",
12
11
  "types": "./lib/react-localization.d.ts",
13
- "import": "./lib/react-localization.es.js"
12
+ "import": "./lib/react-localization.es.js",
13
+ "require": "./lib/react-localization.umd.cjs"
14
14
  }
15
15
  },
16
16
  "files": [
17
- "lib"
17
+ "lib",
18
+ "CHANGELOG.md"
18
19
  ],
19
20
  "scripts": {
20
21
  "dev": "vite",
21
- "test": "vitest",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
22
24
  "prepare": "npm run build",
23
25
  "build:vite": "vite build",
24
26
  "build:types": "tsc --emitDeclarationOnly",
@@ -47,20 +49,20 @@
47
49
  "react-dom": ">=19.0.0"
48
50
  },
49
51
  "devDependencies": {
50
- "@testing-library/react": "^16.1.0",
51
- "@types/node": "^22.10.5",
52
- "@types/react": "^19.0.4",
53
- "@types/react-dom": "^19.0.2",
54
- "@vitejs/plugin-react": "^4.3.4",
55
- "react": "^19.0.0",
56
- "react-dom": "^19.0.0",
57
- "typescript": "^5.7.3",
58
- "vite": "^6.0.7",
59
- "vitest": "^2.1.8",
60
- "jsdom": "^26.0.0"
52
+ "@testing-library/react": "^16.3.3",
53
+ "@types/node": "^26.6.4",
54
+ "@types/react": "^19.3.0",
55
+ "@types/react-dom": "^19.3.0",
56
+ "@vitejs/plugin-react": "^6.1.1",
57
+ "react": "^19.3.0",
58
+ "react-dom": "^19.3.0",
59
+ "typescript": "^6.0.3",
60
+ "vite": "^8.3.2",
61
+ "vitest": "^5.0.3",
62
+ "jsdom": "^30.1.1"
61
63
  },
62
64
  "dependencies": {
63
- "localized-strings": "^2.0.3"
65
+ "localized-strings": "^2.1.2"
64
66
  },
65
67
  "peerDependenciesMeta": {
66
68
  "react": {
@@ -1 +0,0 @@
1
- (function(o,g){typeof exports=="object"&&typeof module<"u"?module.exports=g(require("react")):typeof define=="function"&&define.amd?define(["react"],g):(o=typeof globalThis<"u"?globalThis:o||self,o.ReactLocalization=g(o.React))})(this,function(o){"use strict";var g=Object.defineProperty,_=(s,e,t)=>e in s?g(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t,u=(s,e,t)=>_(s,typeof e!="symbol"?e+"":e,t);function L(){const s="en-US";if(typeof navigator>"u")return s;const e=navigator;if(e){if(e.language)return e.language;if(e.languages&&e.languages[0])return e.languages[0];if("userLanguage"in e)return e.userLanguage;if("browserLanguage"in e)return e.browserLanguage}return s}function y(s,e){if(e[s])return s;const t=s.indexOf("-"),a=t>=0?s.substring(0,t):s;return e[a]?a:Object.keys(e)[0]}function b(s){const e=["_interfaceLanguage","_language","_defaultLanguage","_defaultLanguageFirstLevelKeys","_props"];s.forEach(t=>{if(e.indexOf(t)!==-1)throw new Error(`${t} cannot be used as a key. It is a reserved word.`)})}function m(s){let e="";const t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for(let a=0;a<s;a+=1)e+=t.charAt(Math.floor(Math.random()*t.length));return e}const c=/(\{[\d|\w]+\})/,h=/(\$ref\{[\w|.]+\})/;class p{constructor(e,t){u(this,"_opts"),u(this,"_interfaceLanguage"),u(this,"_language"),u(this,"_defaultLanguage"),u(this,"_defaultLanguageFirstLevelKeys"),u(this,"_props"),u(this,"_availableLanguages"),typeof t=="function"&&(t={customLanguageInterface:t}),this._opts={customLanguageInterface:L,pseudo:!1,pseudoMultipleLanguages:!1,logsEnabled:!0,...t},this._interfaceLanguage=this._opts.customLanguageInterface(),this._language=this._interfaceLanguage,this.setContent(e)}setContent(e){const[t]=Object.keys(e);this._defaultLanguage=t,this._defaultLanguageFirstLevelKeys=[],this._props=e,b(Object.keys(e[this._defaultLanguage])),Object.keys(this._props[this._defaultLanguage]).forEach(a=>{typeof this._props[this._defaultLanguage][a]=="string"&&this._defaultLanguageFirstLevelKeys.push(a)}),this.setLanguage(this._interfaceLanguage),this._opts.pseudo&&this._pseudoAllValues(this._props)}_pseudoAllValues(e){Object.keys(e).forEach(t=>{if(typeof e[t]=="object")this._pseudoAllValues(e[t]);else if(typeof e[t]=="string"){if(e[t].indexOf("[")===0&&e[t].lastIndexOf("]")===e[t].length-1)return;const a=e[t].split(" ");for(let n=0;n<a.length;n+=1){if(a[n].match(c)||a[n].match(h))continue;let i=a[n].length;this._opts.pseudoMultipleLanguages&&(i=Math.floor(i*1.4)),a[n]=m(i)}e[t]=`[${a.join(" ")}]`}})}setLanguage(e){const t=y(e,this._props),a=Object.keys(this._props)[0];if(this._language=t,this._props[t]){for(const i of this._defaultLanguageFirstLevelKeys)delete this[i];let n={...this._props[this._language]};Object.keys(n).forEach(i=>{this[i]=n[i]}),a!==this._language&&(n=this._props[a],this._fallbackValues(n,this))}}_fallbackValues(e,t){Object.keys(e).forEach(a=>{Object.prototype.hasOwnProperty.call(e,a)&&!t[a]&&t[a]!==""?(t[a]=e[a],this._opts.logsEnabled&&console.log(`🚧 👷 key '${a}' not found in localizedStrings for language ${this._language} 🚧`)):typeof t[a]!="string"&&this._fallbackValues(e[a],t[a])})}getLanguage(){return this._language}getInterfaceLanguage(){return this._interfaceLanguage}getAvailableLanguages(){return this._availableLanguages||(this._availableLanguages=Object.keys(this._props)),this._availableLanguages}formatString(e,...t){let a=e||"";return typeof a=="string"&&(a=this.getString(e,null,!0)||a),a.split(h).filter(Boolean).map(n=>{if(n.match(h)){const i=n.slice(5,-1);return this.getString(i)||(this._opts.logsEnabled&&console.log(`No Localization ref found for '${n}' in string '${e}'`),`$ref(id:${i})`)}return n}).join("").split(c).filter(Boolean).map(n=>{if(n.match(c)){const i=n.slice(1,-1);let r=t[i];return r===void 0&&t[0]&&(r=t[0][i]),r}return n}).join("")}getString(e,t,a=!1){try{let n=this._props[t||this._language];const i=e.split(".");for(const r of i){if(n[r]===void 0)throw new Error(r);n=n[r]}return n}catch(n){!a&&this._opts.logsEnabled&&console.log(`No localization found for key '${e}' and language '${t}', failed on ${n.message}`)}return null}getContent(){return this._props}}const d=/(\{[\d|\w]+\})/;return p.prototype.formatString=(s,...e)=>{let t=!1;const a=(s||"").split(d).filter(n=>!!n).map((n,i)=>{if(n.match(d)){const r=n.slice(1,-1);let l=e[r];if(l==null){const f=e[0]?e[0][r]:void 0;if(f!==void 0)l=f;else return l}return o.isValidElement(l)?(t=!0,o.Children.toArray(l).map(f=>({...f,key:i.toString()}))):l}return n});return t?a:a.join("")},p});