@prosopo/locale 3.5.0 → 3.6.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.
- package/.turbo/turbo-build$colon$cjs.log +14 -12
- package/.turbo/turbo-build$colon$tsc.log +8 -5
- package/.turbo/turbo-build.log +16 -14
- package/CHANGELOG.md +54 -0
- package/dist/cjs/i18SharedOptions.cjs +2 -2
- package/dist/cjs/i18nBackend.cjs +1 -1
- package/dist/cjs/i18nFrontend.cjs +169 -51
- package/dist/cjs/i18nMiddleware.cjs +5 -2
- package/dist/cjs/index.cjs +3 -2
- package/dist/cjs/translationKey.cjs +2 -3
- package/dist/cjs/translations.cjs +4 -5
- package/dist/cjs/translator.cjs +2 -3
- package/dist/i18SharedOptions.d.ts +2 -2
- package/dist/i18SharedOptions.js +3 -3
- package/dist/i18SharedOptions.js.map +1 -1
- package/dist/i18nBackend.js +1 -1
- package/dist/i18nFrontend.d.ts +2 -2
- package/dist/i18nFrontend.d.ts.map +1 -1
- package/dist/i18nFrontend.js +169 -45
- package/dist/i18nFrontend.js.map +1 -1
- package/dist/i18nMiddleware.d.ts.map +1 -1
- package/dist/i18nMiddleware.js +3 -1
- package/dist/i18nMiddleware.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/loadI18next.d.ts +2 -3
- package/dist/loadI18next.d.ts.map +1 -1
- package/dist/loadI18next.js.map +1 -1
- package/dist/locales/fi/translation.json +1 -1
- package/dist/locales/hi/translation.json +406 -406
- package/dist/locales/nl/translation.json +1 -1
- package/dist/tests/i18nFrontend.unit.test.js +218 -170
- package/dist/tests/i18nFrontend.unit.test.js.map +1 -1
- package/dist/tests/i18nMiddleware.unit.test.js +11 -2
- package/dist/tests/i18nMiddleware.unit.test.js.map +1 -1
- package/dist/tests/loadI18next.unit.test.js.map +1 -1
- package/dist/tests/locale.test-d.js +15 -11
- package/dist/tests/locale.test-d.js.map +1 -1
- package/dist/tests/localePlaceholders.unit.test.d.ts +2 -0
- package/dist/tests/localePlaceholders.unit.test.d.ts.map +1 -0
- package/dist/tests/localePlaceholders.unit.test.js +35 -0
- package/dist/tests/localePlaceholders.unit.test.js.map +1 -0
- package/dist/tests/translationKey.unit.test.js +11 -33
- package/dist/tests/translationKey.unit.test.js.map +1 -1
- package/dist/tests/translations.unit.test.js +12 -22
- package/dist/tests/translations.unit.test.js.map +1 -1
- package/dist/translationKey.d.ts +2 -3
- package/dist/translationKey.d.ts.map +1 -1
- package/dist/translationKey.js +2 -3
- package/dist/translationKey.js.map +1 -1
- package/dist/translations.d.ts +3 -2
- package/dist/translations.d.ts.map +1 -1
- package/dist/translations.js +3 -3
- package/dist/translations.js.map +1 -1
- package/dist/translator.d.ts +2 -3
- package/dist/translator.d.ts.map +1 -1
- package/dist/translator.js +2 -3
- package/dist/translator.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +4 -8
- package/src/i18SharedOptions.ts +3 -3
- package/src/i18nFrontend.ts +240 -73
- package/src/i18nMiddleware.ts +6 -1
- package/src/index.ts +9 -3
- package/src/loadI18next.ts +7 -8
- package/src/locales/fi/translation.json +1 -1
- package/src/locales/hi/translation.json +406 -406
- package/src/locales/nl/translation.json +1 -1
- package/src/tests/i18nFrontend.unit.test.ts +310 -216
- package/src/tests/i18nMiddleware.unit.test.ts +21 -2
- package/src/tests/loadI18next.unit.test.ts +11 -11
- package/src/tests/locale.test-d.ts +24 -23
- package/src/tests/localePlaceholders.unit.test.ts +81 -0
- package/src/tests/translationKey.unit.test.ts +12 -38
- package/src/tests/translations.unit.test.ts +18 -31
- package/src/translationKey.ts +10 -9
- package/src/translations.ts +6 -5
- package/src/translator.ts +6 -7
- package/src/types.ts +42 -0
- package/tsconfig.cjs.json +3 -0
- package/tsconfig.json +3 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/locale@3.
|
|
2
|
+
> @prosopo/locale@3.6.1 build:cjs
|
|
3
3
|
> NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
|
|
4
4
|
|
|
5
5
|
ViteCommonJSConfig: .
|
|
6
|
-
{
|
|
7
|
-
|
|
6
|
+
{
|
|
7
|
+
tsConfigPaths: [ '/home/runner/work/captcha/captcha/packages/util/tsconfig.json' ]
|
|
8
|
+
}
|
|
9
|
+
{ externals: [ '@prosopo/util' ] }
|
|
8
10
|
[33mThe plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the [1mresolve.tsconfigPaths[22m option. You can remove the plugin and set [1mresolve.tsconfigPaths: true[22m in your Vite config instead.[39m
|
|
9
11
|
[36mvite v8.1.5 [32mbuilding ssr environment for production...[36m[39m
|
|
10
12
|
Bundle build started
|
|
@@ -13,20 +15,20 @@ Bundle build started
|
|
|
13
15
|
rendering chunks...
|
|
14
16
|
computing gzip size...
|
|
15
17
|
dist/cjs/util.cjs 0.28 kB │ gzip: 0.17 kB
|
|
16
|
-
dist/cjs/
|
|
17
|
-
dist/cjs/
|
|
18
|
-
dist/cjs/translationKey.cjs 0.
|
|
18
|
+
dist/cjs/i18SharedOptions.cjs 0.39 kB │ gzip: 0.24 kB
|
|
19
|
+
dist/cjs/i18nMiddleware.cjs 0.51 kB │ gzip: 0.26 kB
|
|
20
|
+
dist/cjs/translationKey.cjs 0.68 kB │ gzip: 0.35 kB
|
|
19
21
|
dist/cjs/process.cjs 0.78 kB │ gzip: 0.46 kB
|
|
20
|
-
dist/cjs/
|
|
21
|
-
dist/cjs/
|
|
22
|
-
dist/cjs/
|
|
22
|
+
dist/cjs/translations.cjs 0.80 kB │ gzip: 0.40 kB
|
|
23
|
+
dist/cjs/translator.cjs 0.83 kB │ gzip: 0.45 kB
|
|
24
|
+
dist/cjs/index.cjs 0.87 kB │ gzip: 0.27 kB
|
|
23
25
|
dist/cjs/_virtual/_rolldown/runtime.cjs 1.00 kB │ gzip: 0.50 kB
|
|
24
26
|
dist/cjs/i18nBackend.cjs 1.47 kB │ gzip: 0.62 kB
|
|
25
27
|
dist/cjs/loadI18next.cjs 2.03 kB │ gzip: 0.84 kB
|
|
26
|
-
dist/cjs/i18nFrontend.cjs
|
|
28
|
+
dist/cjs/i18nFrontend.cjs 6.07 kB │ gzip: 2.44 kB
|
|
27
29
|
dist/cjs/locales/en/translation.cjs 16.64 kB │ gzip: 6.01 kB
|
|
28
30
|
|
|
29
|
-
[32m✓ built in
|
|
31
|
+
[32m✓ built in 85ms[39m
|
|
30
32
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/ar/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/ar/translation.json
|
|
31
33
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/az/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/az/translation.json
|
|
32
34
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/cs/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/cs/translation.json
|
|
@@ -34,9 +36,9 @@ dist/cjs/locales/en/translation.cjs 16.64 kB │ gzip: 6.01 kB
|
|
|
34
36
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/el/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/el/translation.json
|
|
35
37
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/en/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/en/translation.json
|
|
36
38
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/es/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/es/translation.json
|
|
39
|
+
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/fi/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/fi/translation.json
|
|
37
40
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/fr/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/fr/translation.json
|
|
38
41
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/hi/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/hi/translation.json
|
|
39
|
-
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/fi/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/fi/translation.json
|
|
40
42
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/hu/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/hu/translation.json
|
|
41
43
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/id/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/id/translation.json
|
|
42
44
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/it/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/it/translation.json
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/locale@3.
|
|
2
|
+
> @prosopo/locale@3.6.1 build:tsc
|
|
3
3
|
> tsc --build --verbose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
9:56:01 AM - Projects in this build:
|
|
6
6
|
* ../../dev/config/tsconfig.json
|
|
7
|
+
* ../util/tsconfig.json
|
|
7
8
|
* tsconfig.json
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
9:56:01 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/vite/vite.frontend.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
9:56:01 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/verifyRecency.ts' is older than output '../util/tsconfig.tsbuildinfo'
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
9:56:01 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
|
|
15
|
+
|
|
16
|
+
9:56:01 AM - Building project '/home/runner/work/captcha/captcha/packages/locale/tsconfig.json'...
|
|
14
17
|
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/locale@3.
|
|
2
|
+
> @prosopo/locale@3.6.1 build
|
|
3
3
|
> npm run build:cross-env -- --mode ${NODE_ENV:-development}
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @prosopo/locale@3.
|
|
6
|
+
> @prosopo/locale@3.6.1 build:cross-env
|
|
7
7
|
> vite build --config vite.esm.config.ts --mode production
|
|
8
8
|
|
|
9
9
|
ViteEsmConfig: .
|
|
10
|
-
{
|
|
11
|
-
|
|
10
|
+
{
|
|
11
|
+
tsConfigPaths: [ '/home/runner/work/captcha/captcha/packages/util/tsconfig.json' ]
|
|
12
|
+
}
|
|
13
|
+
{ externals: [ '@prosopo/util' ] }
|
|
12
14
|
[33mThe plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the [1mresolve.tsconfigPaths[22m option. You can remove the plugin and set [1mresolve.tsconfigPaths: true[22m in your Vite config instead.[39m
|
|
13
15
|
[36mvite v8.1.5 [32mbuilding ssr environment for production...[36m[39m
|
|
14
16
|
Bundle build started
|
|
@@ -18,28 +20,28 @@ rendering chunks...
|
|
|
18
20
|
computing gzip size...
|
|
19
21
|
dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
|
|
20
22
|
dist/util.js 0.25 kB │ gzip: 0.16 kB
|
|
21
|
-
dist/i18nMiddleware.js 0.
|
|
22
|
-
dist/i18SharedOptions.js 0.
|
|
23
|
-
dist/index.js 0.
|
|
24
|
-
dist/translationKey.js 0.
|
|
25
|
-
dist/translations.js 0.
|
|
23
|
+
dist/i18nMiddleware.js 0.31 kB │ gzip: 0.19 kB
|
|
24
|
+
dist/i18SharedOptions.js 0.35 kB │ gzip: 0.24 kB
|
|
25
|
+
dist/index.js 0.48 kB │ gzip: 0.20 kB
|
|
26
|
+
dist/translationKey.js 0.63 kB │ gzip: 0.34 kB
|
|
27
|
+
dist/translations.js 0.74 kB │ gzip: 0.40 kB
|
|
26
28
|
dist/process.js 0.77 kB │ gzip: 0.46 kB
|
|
27
|
-
dist/translator.js 0.
|
|
29
|
+
dist/translator.js 0.79 kB │ gzip: 0.44 kB
|
|
28
30
|
dist/i18nBackend.js 1.01 kB │ gzip: 0.53 kB
|
|
29
|
-
dist/i18nFrontend.js 1.64 kB │ gzip: 0.74 kB
|
|
30
31
|
dist/loadI18next.js 1.93 kB │ gzip: 0.83 kB
|
|
32
|
+
dist/i18nFrontend.js 5.98 kB │ gzip: 2.42 kB
|
|
31
33
|
dist/locales/en/translation.js 16.41 kB │ gzip: 5.97 kB
|
|
32
34
|
|
|
33
|
-
[32m✓ built in
|
|
34
|
-
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/ar/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/ar/translation.json
|
|
35
|
+
[32m✓ built in 42ms[39m
|
|
35
36
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/az/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/az/translation.json
|
|
37
|
+
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/ar/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/ar/translation.json
|
|
36
38
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/cs/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/cs/translation.json
|
|
37
39
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/de/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/de/translation.json
|
|
38
40
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/el/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/el/translation.json
|
|
39
41
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/en/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/en/translation.json
|
|
40
42
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/es/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/es/translation.json
|
|
41
|
-
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/fi/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/fi/translation.json
|
|
42
43
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/fr/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/fr/translation.json
|
|
44
|
+
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/fi/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/fi/translation.json
|
|
43
45
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/hi/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/hi/translation.json
|
|
44
46
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/hu/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/hu/translation.json
|
|
45
47
|
[copy-plugin] copying /home/runner/work/captcha/captcha/packages/locale/src/locales/id/translation.json to /home/runner/work/captcha/captcha/packages/locale/dist/locales/id/translation.json
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @prosopo/locale
|
|
2
2
|
|
|
3
|
+
## 3.6.1
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- 294b480: Hindi text is readable again. A bulk edit in July 2025 had stripped every vowel sign from 410 of the 444 Hindi strings, so the widget said "म मनषय ह" instead of "मैं मनुष्य हूँ". The originals are restored, and the 14 strings added since then are translated properly. The Finnish, Hindi and Dutch "can't find keyring pair" messages had also translated their `{{address}}` placeholder, so the address was never filled in; that is fixed too. New tests check that every locale keeps all of the English placeholders, and that Hindi strings keep their vowel signs.
|
|
7
|
+
- Updated dependencies [0d29dde]
|
|
8
|
+
- @prosopo/util@3.3.12
|
|
9
|
+
|
|
10
|
+
## 3.6.0
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- a9141c3: Stop shipping i18next to the browser. Saves about 22KB gzipped off the widget.
|
|
14
|
+
|
|
15
|
+
The widget was pulling in i18next and four of its plugins — a language detector, an HTTP backend, a chained backend and a resources-to-backend adapter, which between them also dragged in the `cross-fetch` polyfill — to look up 444 short strings with no plurals, no nesting and two interpolated values. That machinery is about 23KB gzipped; the replacement is 1.25KB.
|
|
16
|
+
|
|
17
|
+
`i18nFrontend.ts` now does the job directly: pick a language, fetch the matching `locales/<lang>/translation.json` next to the bundle, and look keys up in it. Behaviour is unchanged in the ways a visitor can see:
|
|
18
|
+
|
|
19
|
+
- language is chosen from the widget's own setting first, then a cookie, then localStorage, then the browser — the same order as before, and the choice is still remembered in both cookie and localStorage
|
|
20
|
+
- a regional tag like `de-AT` still resolves to `de`
|
|
21
|
+
- English is still fetched alongside the chosen language, so a key a translation is missing still renders English rather than its key
|
|
22
|
+
- `{{name}}` placeholders are still filled in, including in a caller's `defaultValue`
|
|
23
|
+
- if the catalogue cannot be fetched the widget still renders, in English, rather than waiting
|
|
24
|
+
|
|
25
|
+
i18next stays for the server, where `i18next-http-middleware` needs the real instance to read the `Accept-Language` header. `Ti18n` is now a small interface describing the handful of methods this repository actually calls, which both implementations satisfy, so `@prosopo/common` no longer needs i18next for a type either.
|
|
26
|
+
|
|
27
|
+
Covered by 189 tests in `@prosopo/locale`, and the built bundle was checked in a real browser: French picked up from the browser, a switch to German, interpolation, and both fallbacks.
|
|
28
|
+
- a9141c3: Stop loading zod before the widget can draw itself. Takes another 13KB gzipped off the critical path.
|
|
29
|
+
|
|
30
|
+
zod is 14KB gzipped and it was being downloaded and parsed before the checkbox appeared, because six small things on the startup path happened to use it:
|
|
31
|
+
|
|
32
|
+
- two lists of strings in `@prosopo/logger` (log levels, output format)
|
|
33
|
+
- two lists of strings in `@prosopo/locale` (language codes, translation keys)
|
|
34
|
+
- two lists of two strings in `@prosopo/types` (start mode, challenge placement)
|
|
35
|
+
- one four-field object in `@prosopo/load-balancer` (a provider entry)
|
|
36
|
+
- an `instanceof ZodError` check in `@prosopo/common`
|
|
37
|
+
- `INPUT_LIMITS`, a plain table of numbers, that happened to live in the same file as zod-based string builders
|
|
38
|
+
|
|
39
|
+
None of these need a validation library. They are now plain TypeScript: a list, a type, and where input is untrusted, a one-line guard. `INPUT_LIMITS` moved to its own file so reading it no longer drags the builders along.
|
|
40
|
+
|
|
41
|
+
zod has not gone anywhere — the real request and response schemas in `@prosopo/types` still use it, and still validate exactly as before. It now arrives with the code that needs it, after the widget is on screen, rather than in front of it.
|
|
42
|
+
|
|
43
|
+
Two API changes for anyone importing these directly:
|
|
44
|
+
|
|
45
|
+
- `LanguageSchema`, `TranslationKeysSchema`, `StartModeSchema` and `Placement` are no longer exported as zod schemas. Use `isLanguage()`, `isStartMode()`, `isPlacement()` to check a value, and `LanguageCodes`, `translationKeys`, `StartModes`, `Placements` for the lists.
|
|
46
|
+
- `isZodError()` now recognises a zod error by its name rather than `instanceof`. That is strictly more tolerant: the name still matches when an error crosses a realm boundary or comes from a second copy of zod, which `instanceof` misses — it was already the fallback arm of the same check.
|
|
47
|
+
|
|
48
|
+
Two behaviour notes: a malformed entry in the fetched provider list now throws a plain `Error` naming the entry, where it used to throw an untranslated zod error; and the language codes accepted are unchanged.
|
|
49
|
+
|
|
50
|
+
Covered by the existing suites for every package touched (types, types-database, locale, logger, common, load-balancer, all five procaptcha packages, api, cli, api-express-router, server, and the provider's 1322 unit tests), all passing. The built bundle was also loaded in a real browser: the widget renders from the first eight chunks, zod arrives in the second wave, and the provider's error came back translated into German.
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- Updated dependencies [a9141c3]
|
|
55
|
+
- @prosopo/util@3.3.11
|
|
56
|
+
|
|
3
57
|
## 3.5.0
|
|
4
58
|
### Minor Changes
|
|
5
59
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const require_translations = require("./translations.cjs");
|
|
2
2
|
var i18nSharedOptions = {
|
|
3
3
|
debug: require("./process.cjs").getProcess()?.env?.PROSOPO_LOG_LEVEL === "debug",
|
|
4
|
-
fallbackLng: require_translations.
|
|
4
|
+
fallbackLng: require_translations.Languages.english,
|
|
5
5
|
namespace: "translation",
|
|
6
|
-
supportedLngs:
|
|
6
|
+
supportedLngs: require_translations.LanguageCodes,
|
|
7
7
|
nonExplicitSupportedLngs: false
|
|
8
8
|
};
|
|
9
9
|
//#endregion
|
package/dist/cjs/i18nBackend.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
const require_util = require("./util.cjs");
|
|
3
3
|
const require_i18SharedOptions = require("./i18SharedOptions.cjs");
|
|
4
|
-
let i18next_http_middleware = require("i18next-http-middleware");
|
|
5
4
|
let i18next = require("i18next");
|
|
6
5
|
i18next = require_runtime.__toESM(i18next, 1);
|
|
6
|
+
let i18next_http_middleware = require("i18next-http-middleware");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
node_path = require_runtime.__toESM(node_path, 1);
|
|
9
9
|
let i18next_fs_backend_cjs = require("i18next-fs-backend/cjs");
|
|
@@ -1,58 +1,176 @@
|
|
|
1
|
-
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
2
1
|
const require_i18SharedOptions = require("./i18SharedOptions.cjs");
|
|
3
|
-
let
|
|
4
|
-
i18next = require_runtime.__toESM(i18next, 1);
|
|
5
|
-
let i18next_browser_languagedetector = require("i18next-browser-languagedetector");
|
|
6
|
-
i18next_browser_languagedetector = require_runtime.__toESM(i18next_browser_languagedetector, 1);
|
|
7
|
-
let i18next_chained_backend = require("i18next-chained-backend");
|
|
8
|
-
i18next_chained_backend = require_runtime.__toESM(i18next_chained_backend, 1);
|
|
9
|
-
let i18next_http_backend = require("i18next-http-backend");
|
|
10
|
-
i18next_http_backend = require_runtime.__toESM(i18next_http_backend, 1);
|
|
11
|
-
let i18next_resources_to_backend = require("i18next-resources-to-backend");
|
|
12
|
-
i18next_resources_to_backend = require_runtime.__toESM(i18next_resources_to_backend, 1);
|
|
2
|
+
let _prosopo_util = require("@prosopo/util");
|
|
13
3
|
//#region src/i18nFrontend.ts
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
4
|
+
var NAMESPACE = "translation";
|
|
5
|
+
var FALLBACK_LANGUAGE = require_i18SharedOptions.i18nSharedOptions.fallbackLng;
|
|
6
|
+
var STORAGE_KEY = "i18nextLng";
|
|
7
|
+
var COOKIE_KEY = "i18next";
|
|
8
|
+
var COOKIE_MAX_AGE_SECONDS = 10080 * 60;
|
|
9
|
+
/**
|
|
10
|
+
* How long to wait for a catalogue before rendering untranslated.
|
|
11
|
+
*
|
|
12
|
+
* A widget with no text is worse than a widget in English: every caller
|
|
13
|
+
* supplies a `defaultValue`, and `t()` falls back to the key otherwise, so a
|
|
14
|
+
* catalogue that never arrives degrades rather than blocks.
|
|
15
|
+
*/
|
|
16
|
+
var CATALOGUE_FETCH_TIMEOUT_MS = 1e4;
|
|
17
|
+
var SUPPORTED_LANGUAGES = require_i18SharedOptions.i18nSharedOptions.supportedLngs;
|
|
18
|
+
/**
|
|
19
|
+
* Locale JSON sits next to the bundle that asked for it. The widget is embedded
|
|
20
|
+
* by third parties and cannot know a base URL, but it does know where its own
|
|
21
|
+
* module was served from.
|
|
22
|
+
*/
|
|
23
|
+
var catalogueUrl = (language) => {
|
|
24
|
+
const moduleUrl = require("url").pathToFileURL(__filename).href;
|
|
25
|
+
return `${moduleUrl.slice(0, moduleUrl.lastIndexOf("/"))}/locales/${language}/${NAMESPACE}.json`;
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* `en-GB` is not in the catalogue list but `en` is, so a regional tag resolves
|
|
29
|
+
* to its base language rather than falling all the way back to English.
|
|
30
|
+
*/
|
|
31
|
+
var resolveLanguage = (candidate) => {
|
|
32
|
+
if (!candidate) return;
|
|
33
|
+
if (SUPPORTED_LANGUAGES.includes(candidate)) return candidate;
|
|
34
|
+
const base = candidate.split("-")[0];
|
|
35
|
+
return base && SUPPORTED_LANGUAGES.includes(base) ? base : void 0;
|
|
36
|
+
};
|
|
37
|
+
var readCookie = () => {
|
|
38
|
+
try {
|
|
39
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${COOKIE_KEY}=([^;]*)`));
|
|
40
|
+
return match?.[1] ? decodeURIComponent(match[1]) : void 0;
|
|
41
|
+
} catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var readStorage = () => {
|
|
46
|
+
try {
|
|
47
|
+
return localStorage.getItem(STORAGE_KEY) ?? void 0;
|
|
48
|
+
} catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var navigatorLanguages = () => {
|
|
53
|
+
try {
|
|
54
|
+
return navigator.languages?.length ? navigator.languages : [navigator.language];
|
|
55
|
+
} catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Cookie and localStorage are both written, matching what the widget did when
|
|
61
|
+
* i18next-browser-languagedetector handled this, so a visitor's choice survives
|
|
62
|
+
* across pages that embed the widget. Either store can throw in a sandboxed
|
|
63
|
+
* iframe or with third-party storage blocked; neither is load-bearing.
|
|
64
|
+
*/
|
|
65
|
+
var cacheLanguage = (language) => {
|
|
66
|
+
try {
|
|
67
|
+
localStorage.setItem(STORAGE_KEY, language);
|
|
68
|
+
} catch {}
|
|
69
|
+
try {
|
|
70
|
+
document.cookie = `${COOKIE_KEY}=${encodeURIComponent(language)};max-age=${COOKIE_MAX_AGE_SECONDS};path=/`;
|
|
71
|
+
} catch {}
|
|
72
|
+
};
|
|
73
|
+
var detectLanguage = () => {
|
|
74
|
+
const candidates = [
|
|
75
|
+
readCookie(),
|
|
76
|
+
readStorage(),
|
|
77
|
+
...navigatorLanguages()
|
|
78
|
+
];
|
|
79
|
+
for (const candidate of candidates) {
|
|
80
|
+
const resolved = resolveLanguage(candidate);
|
|
81
|
+
if (resolved) return resolved;
|
|
82
|
+
}
|
|
83
|
+
return FALLBACK_LANGUAGE;
|
|
84
|
+
};
|
|
85
|
+
var fetchCatalogue = async (language) => {
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
const timer = setTimeout(() => controller.abort(), CATALOGUE_FETCH_TIMEOUT_MS);
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(catalogueUrl(language), { signal: controller.signal });
|
|
90
|
+
if (!response.ok) return {};
|
|
91
|
+
const body = await response.json();
|
|
92
|
+
return typeof body === "object" && body !== null ? (0, _prosopo_util.flatten)(body) : {};
|
|
93
|
+
} catch {
|
|
94
|
+
return {};
|
|
95
|
+
} finally {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var interpolate = (template, options) => options ? template.replace(/\{\{(\w+)\}\}/g, (placeholder, name) => name in options ? String(options[name]) : placeholder) : template;
|
|
100
|
+
var createBrowserI18n = (requested) => {
|
|
101
|
+
const catalogues = /* @__PURE__ */ new Map();
|
|
102
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
103
|
+
let language = resolveLanguage(requested) ?? detectLanguage();
|
|
104
|
+
let initialised = false;
|
|
105
|
+
const emit = (event) => {
|
|
106
|
+
for (const listener of listeners.get(event) ?? []) listener();
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* The fallback catalogue is fetched alongside the active one so a key
|
|
110
|
+
* missing from a translation still renders English rather than its key —
|
|
111
|
+
* the same key-level fallback i18next gave us via `fallbackLng`.
|
|
112
|
+
*/
|
|
113
|
+
const loadCatalogues = async (target) => {
|
|
114
|
+
const wanted = [.../* @__PURE__ */ new Set([target, FALLBACK_LANGUAGE])].filter((candidate) => !catalogues.has(candidate));
|
|
115
|
+
await Promise.all(wanted.map(async (candidate) => {
|
|
116
|
+
catalogues.set(candidate, await fetchCatalogue(candidate));
|
|
117
|
+
}));
|
|
118
|
+
};
|
|
119
|
+
const lookup = (candidate, key) => catalogues.get(candidate)?.[key];
|
|
120
|
+
cacheLanguage(language);
|
|
121
|
+
const loading = loadCatalogues(language).then(() => {
|
|
122
|
+
initialised = true;
|
|
123
|
+
emit("initialized");
|
|
124
|
+
emit("loaded");
|
|
125
|
+
});
|
|
126
|
+
const instance = {
|
|
127
|
+
get language() {
|
|
128
|
+
return language;
|
|
129
|
+
},
|
|
130
|
+
get isInitialized() {
|
|
131
|
+
return initialised;
|
|
132
|
+
},
|
|
133
|
+
t: (key, options) => interpolate(lookup(language, key) ?? lookup(FALLBACK_LANGUAGE, key) ?? options?.defaultValue ?? key, options),
|
|
134
|
+
hasLoadedNamespace: (namespace) => namespace === NAMESPACE && catalogues.has(language),
|
|
135
|
+
changeLanguage: async (next) => {
|
|
136
|
+
const resolved = resolveLanguage(next) ?? FALLBACK_LANGUAGE;
|
|
137
|
+
if (resolved !== language) {
|
|
138
|
+
language = resolved;
|
|
139
|
+
cacheLanguage(resolved);
|
|
140
|
+
await loadCatalogues(resolved);
|
|
141
|
+
emit("loaded");
|
|
142
|
+
emit("languageChanged");
|
|
143
|
+
}
|
|
144
|
+
return instance;
|
|
145
|
+
},
|
|
146
|
+
on: (event, listener) => {
|
|
147
|
+
const existing = listeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
148
|
+
existing.add(listener);
|
|
149
|
+
listeners.set(event, existing);
|
|
150
|
+
},
|
|
151
|
+
off: (event, listener) => {
|
|
152
|
+
listeners.get(event)?.delete(listener);
|
|
153
|
+
},
|
|
154
|
+
ready: async () => {
|
|
155
|
+
await loading;
|
|
156
|
+
return instance;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return instance;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* The widget owns this instance. A host page running its own i18n must not be
|
|
163
|
+
* able to capture the widget's translations, and every widget on the page
|
|
164
|
+
* shares one catalogue fetch.
|
|
165
|
+
*/
|
|
166
|
+
var instance;
|
|
27
167
|
function initializeI18n(i18nLoadedCallback, lng) {
|
|
28
|
-
if (!
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
],
|
|
35
|
-
lookupQuerystring: "lng",
|
|
36
|
-
lookupCookie: "i18next",
|
|
37
|
-
lookupLocalStorage: "i18nextLng",
|
|
38
|
-
caches: ["localStorage", "cookie"]
|
|
39
|
-
})).init({
|
|
40
|
-
...require_i18SharedOptions.i18nSharedOptions,
|
|
41
|
-
...lng ? { lng } : {},
|
|
42
|
-
backend: {
|
|
43
|
-
backends: [i18next_http_backend.default, (0, i18next_resources_to_backend.default)((language, namespace) => import(getLoadPath(language, namespace)))],
|
|
44
|
-
backendOptions: [{ loadPath: getLoadPath("{{lng}}", "{{ns}}") }]
|
|
45
|
-
},
|
|
46
|
-
...detectionOptions
|
|
47
|
-
});
|
|
48
|
-
let notified = false;
|
|
49
|
-
i18next.default.on("loaded", () => {
|
|
50
|
-
if (notified) return;
|
|
51
|
-
notified = true;
|
|
52
|
-
i18nLoadedCallback?.(i18next.default);
|
|
53
|
-
});
|
|
54
|
-
} else i18nLoadedCallback?.(i18next.default);
|
|
55
|
-
return i18next.default;
|
|
168
|
+
if (!instance) {
|
|
169
|
+
const created = createBrowserI18n(lng);
|
|
170
|
+
instance = created;
|
|
171
|
+
created.ready().then(() => i18nLoadedCallback?.(created));
|
|
172
|
+
} else i18nLoadedCallback?.(instance);
|
|
173
|
+
return instance;
|
|
56
174
|
}
|
|
57
175
|
//#endregion
|
|
58
176
|
exports.default = initializeI18n;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
1
2
|
const require_loadI18next = require("./loadI18next.cjs");
|
|
3
|
+
let i18next = require("i18next");
|
|
4
|
+
i18next = require_runtime.__toESM(i18next, 1);
|
|
2
5
|
let i18next_http_middleware = require("i18next-http-middleware");
|
|
3
6
|
//#region src/i18nMiddleware.ts
|
|
4
7
|
async function i18nMiddleware(options) {
|
|
5
|
-
|
|
6
|
-
return (0, i18next_http_middleware.handle)(
|
|
8
|
+
await require_loadI18next.default(true);
|
|
9
|
+
return (0, i18next_http_middleware.handle)(i18next.default, { ...options });
|
|
7
10
|
}
|
|
8
11
|
//#endregion
|
|
9
12
|
exports.default = i18nMiddleware;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -5,10 +5,11 @@ const require_translations = require("./translations.cjs");
|
|
|
5
5
|
const require_util = require("./util.cjs");
|
|
6
6
|
const require_translationKey = require("./translationKey.cjs");
|
|
7
7
|
const require_translator = require("./translator.cjs");
|
|
8
|
-
exports.
|
|
8
|
+
exports.LanguageCodes = require_translations.LanguageCodes;
|
|
9
9
|
exports.Languages = require_translations.Languages;
|
|
10
|
-
exports.TranslationKeysSchema = require_translationKey.TranslationKeysSchema;
|
|
11
10
|
exports.createTranslator = require_translator.createTranslator;
|
|
12
11
|
exports.i18nMiddleware = require_i18nMiddleware.default;
|
|
13
12
|
exports.isClientSide = require_util.isClientSide;
|
|
13
|
+
exports.isLanguage = require_translations.isLanguage;
|
|
14
14
|
exports.loadI18next = require_loadI18next.default;
|
|
15
|
+
exports.translationKeys = require_translationKey.translationKeys;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const require_translation = require("./locales/en/translation.cjs");
|
|
2
|
-
let zod = require("zod");
|
|
3
2
|
//#region src/translationKey.ts
|
|
4
3
|
function getLeafFieldPath(obj) {
|
|
5
4
|
if (typeof obj === "string") return [];
|
|
@@ -13,7 +12,7 @@ function getLeafFieldPath(obj) {
|
|
|
13
12
|
}));
|
|
14
13
|
}, []);
|
|
15
14
|
}
|
|
16
|
-
var
|
|
15
|
+
var translationKeys = getLeafFieldPath(require_translation.default);
|
|
17
16
|
//#endregion
|
|
18
|
-
exports.TranslationKeysSchema = TranslationKeysSchema;
|
|
19
17
|
exports.getLeafFieldPath = getLeafFieldPath;
|
|
18
|
+
exports.translationKeys = translationKeys;
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
2
|
-
let zod = require("zod");
|
|
3
|
-
zod = require_runtime.__toESM(zod, 1);
|
|
4
1
|
//#region src/translations.ts
|
|
5
2
|
var Languages = {
|
|
6
3
|
arabic: "ar",
|
|
@@ -36,7 +33,9 @@ var Languages = {
|
|
|
36
33
|
vietnamese: "vi",
|
|
37
34
|
chinese: "zh-CN"
|
|
38
35
|
};
|
|
39
|
-
var
|
|
36
|
+
var LanguageCodes = Object.values(Languages);
|
|
37
|
+
var isLanguage = (value) => LanguageCodes.includes(value);
|
|
40
38
|
//#endregion
|
|
41
|
-
exports.
|
|
39
|
+
exports.LanguageCodes = LanguageCodes;
|
|
42
40
|
exports.Languages = Languages;
|
|
41
|
+
exports.isLanguage = isLanguage;
|
package/dist/cjs/translator.cjs
CHANGED
|
@@ -2,9 +2,8 @@ const require_i18nFrontend = require("./i18nFrontend.cjs");
|
|
|
2
2
|
//#region src/translator.ts
|
|
3
3
|
var NAMESPACE = "translation";
|
|
4
4
|
/**
|
|
5
|
-
* Vanilla replacement for the `useTranslation` hook
|
|
6
|
-
*
|
|
7
|
-
* events that used to trigger a React re-render.
|
|
5
|
+
* Vanilla replacement for the `useTranslation` hook: the instance's `t` plus
|
|
6
|
+
* the three events that used to trigger a React re-render.
|
|
8
7
|
*/
|
|
9
8
|
var createTranslator = (existing) => {
|
|
10
9
|
const i18n = existing ?? require_i18nFrontend.default();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare const i18nSharedOptions: {
|
|
2
2
|
debug: boolean;
|
|
3
|
-
fallbackLng:
|
|
3
|
+
fallbackLng: "en";
|
|
4
4
|
namespace: string;
|
|
5
|
-
supportedLngs:
|
|
5
|
+
supportedLngs: readonly string[];
|
|
6
6
|
nonExplicitSupportedLngs: boolean;
|
|
7
7
|
};
|
|
8
8
|
//# sourceMappingURL=i18SharedOptions.d.ts.map
|
package/dist/i18SharedOptions.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LanguageCodes, Languages } from "./translations.js";
|
|
2
2
|
import { getProcess } from "./process.js";
|
|
3
3
|
var i18nSharedOptions = {
|
|
4
4
|
debug: getProcess()?.env?.PROSOPO_LOG_LEVEL === "debug",
|
|
5
|
-
fallbackLng:
|
|
5
|
+
fallbackLng: Languages.english,
|
|
6
6
|
namespace: "translation",
|
|
7
|
-
supportedLngs:
|
|
7
|
+
supportedLngs: LanguageCodes,
|
|
8
8
|
nonExplicitSupportedLngs: false
|
|
9
9
|
};
|
|
10
10
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18SharedOptions.js","sourceRoot":"","sources":["../src/i18SharedOptions.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"i18SharedOptions.js","sourceRoot":"","sources":["../src/i18SharedOptions.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAM7D,MAAM,QAAQ,GAAG,UAAU,EAAE,EAAE,GAAG,EAAE,iBAAiB,CAAC;AAEtD,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,KAAK,EAAE,QAAQ,KAAK,OAAO;IAC3B,WAAW,EAAE,SAAS,CAAC,OAAO;IAC9B,SAAS,EAAE,aAAa;IACxB,aAAa,EAAE,aAAa;IAC5B,wBAAwB,EAAE,KAAK;CAC/B,CAAC"}
|
package/dist/i18nBackend.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "./_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { isServerSide } from "./util.js";
|
|
3
3
|
import { i18nSharedOptions } from "./i18SharedOptions.js";
|
|
4
|
-
import { LanguageDetector } from "i18next-http-middleware";
|
|
5
4
|
import i18n from "i18next";
|
|
5
|
+
import { LanguageDetector } from "i18next-http-middleware";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import FSBackend from "i18next-fs-backend/cjs";
|
|
8
8
|
//#region src/i18nBackend.ts
|
package/dist/i18nFrontend.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
export declare function initializeI18n(i18nLoadedCallback?: (value:
|
|
1
|
+
import type { Ti18n } from "./types.js";
|
|
2
|
+
export declare function initializeI18n(i18nLoadedCallback?: (value: Ti18n) => void, lng?: string): Ti18n;
|
|
3
3
|
export default initializeI18n;
|
|
4
4
|
//# sourceMappingURL=i18nFrontend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18nFrontend.d.ts","sourceRoot":"","sources":["../src/i18nFrontend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"i18nFrontend.d.ts","sourceRoot":"","sources":["../src/i18nFrontend.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAa,KAAK,EAAoB,MAAM,YAAY,CAAC;AAsOrE,wBAAgB,cAAc,CAC7B,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EAC3C,GAAG,CAAC,EAAE,MAAM,GACV,KAAK,CAeP;AAED,eAAe,cAAc,CAAC"}
|