@prosopo/locale 3.9.1 → 3.10.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 +266 -13
- package/.turbo/turbo-build$colon$tsc.log +5 -5
- package/.turbo/turbo-build.log +266 -13
- package/CHANGELOG.md +42 -0
- package/README.md +203 -0
- package/dist/browserUiLanguages.d.ts +10 -0
- package/dist/browserUiLanguages.d.ts.map +1 -0
- package/dist/browserUiLanguages.js +768 -0
- package/dist/browserUiLanguages.js.map +1 -0
- package/dist/cjs/browserUiLanguages.cjs +768 -0
- package/dist/cjs/direction.cjs +30 -15
- package/dist/cjs/errorMessage.cjs +3 -2
- package/dist/cjs/i18SharedOptions.cjs +0 -1
- package/dist/cjs/i18nBackend.cjs +33 -20
- package/dist/cjs/i18nFrontend.cjs +23 -73
- package/dist/cjs/index.cjs +23 -2
- package/dist/cjs/languagePreference.cjs +142 -0
- package/dist/cjs/loadI18next.cjs +4 -4
- package/dist/cjs/locales/en/target.cjs +304 -0
- package/dist/cjs/locales/en/translation.cjs +1 -459
- package/dist/cjs/locales/en/widget.cjs +162 -0
- package/dist/cjs/requestLanguageDetector.cjs +24 -0
- package/dist/cjs/serverTranslationKeys.cjs +38 -0
- package/dist/cjs/translationKey.cjs +29 -7
- package/dist/cjs/translations.cjs +63 -2
- package/dist/cjs/translator.cjs +2 -1
- package/dist/direction.d.ts.map +1 -1
- package/dist/direction.js +30 -15
- package/dist/direction.js.map +1 -1
- package/dist/errorMessage.d.ts.map +1 -1
- package/dist/errorMessage.js +4 -3
- package/dist/errorMessage.js.map +1 -1
- package/dist/i18SharedOptions.d.ts +0 -1
- package/dist/i18SharedOptions.d.ts.map +1 -1
- package/dist/i18SharedOptions.js +0 -1
- package/dist/i18SharedOptions.js.map +1 -1
- package/dist/i18nBackend.d.ts +3 -1
- package/dist/i18nBackend.d.ts.map +1 -1
- package/dist/i18nBackend.js +32 -22
- package/dist/i18nBackend.js.map +1 -1
- package/dist/i18nFrontend.d.ts.map +1 -1
- package/dist/i18nFrontend.js +23 -73
- package/dist/i18nFrontend.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/languagePreference.d.ts +17 -0
- package/dist/languagePreference.d.ts.map +1 -0
- package/dist/languagePreference.js +132 -0
- package/dist/languagePreference.js.map +1 -0
- package/dist/loadI18next.js +4 -4
- package/dist/locales/af/target.json +301 -0
- package/dist/locales/af/translation.json +71 -0
- package/dist/locales/af/widget.json +149 -0
- package/dist/locales/am/target.json +301 -0
- package/dist/locales/am/translation.json +71 -0
- package/dist/locales/am/widget.json +149 -0
- package/dist/locales/an/target.json +301 -0
- package/dist/locales/an/translation.json +71 -0
- package/dist/locales/an/widget.json +149 -0
- package/dist/locales/ar/target.json +301 -0
- package/dist/locales/ar/translation.json +0 -446
- package/dist/locales/ar/widget.json +149 -0
- package/dist/locales/as/target.json +301 -0
- package/dist/locales/as/translation.json +71 -0
- package/dist/locales/as/widget.json +149 -0
- package/dist/locales/ast/target.json +301 -0
- package/dist/locales/ast/translation.json +71 -0
- package/dist/locales/ast/widget.json +149 -0
- package/dist/locales/az/target.json +301 -0
- package/dist/locales/az/translation.json +0 -446
- package/dist/locales/az/widget.json +149 -0
- package/dist/locales/be/target.json +301 -0
- package/dist/locales/be/translation.json +71 -0
- package/dist/locales/be/widget.json +149 -0
- package/dist/locales/bg/target.json +301 -0
- package/dist/locales/bg/translation.json +71 -0
- package/dist/locales/bg/widget.json +149 -0
- package/dist/locales/bn/target.json +301 -0
- package/dist/locales/bn/translation.json +71 -0
- package/dist/locales/bn/widget.json +149 -0
- package/dist/locales/bs/target.json +301 -0
- package/dist/locales/bs/translation.json +71 -0
- package/dist/locales/bs/widget.json +149 -0
- package/dist/locales/ca/target.json +301 -0
- package/dist/locales/ca/translation.json +71 -0
- package/dist/locales/ca/widget.json +149 -0
- package/dist/locales/cs/target.json +301 -0
- package/dist/locales/cs/translation.json +0 -446
- package/dist/locales/cs/widget.json +149 -0
- package/dist/locales/cy/target.json +301 -0
- package/dist/locales/cy/translation.json +71 -0
- package/dist/locales/cy/widget.json +149 -0
- package/dist/locales/da/target.json +301 -0
- package/dist/locales/da/translation.json +71 -0
- package/dist/locales/da/widget.json +149 -0
- package/dist/locales/de/target.json +301 -0
- package/dist/locales/de/translation.json +0 -446
- package/dist/locales/de/widget.json +149 -0
- package/dist/locales/el/target.json +301 -0
- package/dist/locales/el/translation.json +0 -446
- package/dist/locales/el/widget.json +149 -0
- package/dist/locales/en/target.js +303 -0
- package/dist/locales/en/target.json +301 -0
- package/dist/locales/en/translation.js +2 -453
- package/dist/locales/en/translation.json +0 -446
- package/dist/locales/en/widget.js +156 -0
- package/dist/locales/en/widget.json +149 -0
- package/dist/locales/eo/target.json +301 -0
- package/dist/locales/eo/translation.json +71 -0
- package/dist/locales/eo/widget.json +149 -0
- package/dist/locales/es/target.json +301 -0
- package/dist/locales/es/translation.json +0 -446
- package/dist/locales/es/widget.json +149 -0
- package/dist/locales/et/target.json +301 -0
- package/dist/locales/et/translation.json +71 -0
- package/dist/locales/et/widget.json +149 -0
- package/dist/locales/eu/target.json +301 -0
- package/dist/locales/eu/translation.json +71 -0
- package/dist/locales/eu/widget.json +149 -0
- package/dist/locales/fa/target.json +301 -0
- package/dist/locales/fa/translation.json +71 -0
- package/dist/locales/fa/widget.json +149 -0
- package/dist/locales/fi/target.json +301 -0
- package/dist/locales/fi/translation.json +0 -446
- package/dist/locales/fi/widget.json +149 -0
- package/dist/locales/fil/target.json +301 -0
- package/dist/locales/fil/translation.json +71 -0
- package/dist/locales/fil/widget.json +149 -0
- package/dist/locales/fr/target.json +301 -0
- package/dist/locales/fr/translation.json +0 -446
- package/dist/locales/fr/widget.json +149 -0
- package/dist/locales/fy/target.json +301 -0
- package/dist/locales/fy/translation.json +71 -0
- package/dist/locales/fy/widget.json +149 -0
- package/dist/locales/ga/target.json +301 -0
- package/dist/locales/ga/translation.json +71 -0
- package/dist/locales/ga/widget.json +149 -0
- package/dist/locales/gd/target.json +301 -0
- package/dist/locales/gd/translation.json +71 -0
- package/dist/locales/gd/widget.json +149 -0
- package/dist/locales/gl/target.json +301 -0
- package/dist/locales/gl/translation.json +71 -0
- package/dist/locales/gl/widget.json +149 -0
- package/dist/locales/gu/target.json +301 -0
- package/dist/locales/gu/translation.json +71 -0
- package/dist/locales/gu/widget.json +149 -0
- package/dist/locales/he/target.json +301 -0
- package/dist/locales/he/translation.json +71 -0
- package/dist/locales/he/widget.json +149 -0
- package/dist/locales/hi/target.json +301 -0
- package/dist/locales/hi/translation.json +0 -446
- package/dist/locales/hi/widget.json +149 -0
- package/dist/locales/hr/target.json +301 -0
- package/dist/locales/hr/translation.json +71 -0
- package/dist/locales/hr/widget.json +149 -0
- package/dist/locales/hu/target.json +301 -0
- package/dist/locales/hu/translation.json +0 -446
- package/dist/locales/hu/widget.json +149 -0
- package/dist/locales/hy/target.json +301 -0
- package/dist/locales/hy/translation.json +71 -0
- package/dist/locales/hy/widget.json +149 -0
- package/dist/locales/ia/target.json +301 -0
- package/dist/locales/ia/translation.json +71 -0
- package/dist/locales/ia/widget.json +149 -0
- package/dist/locales/id/target.json +301 -0
- package/dist/locales/id/translation.json +10 -456
- package/dist/locales/id/widget.json +149 -0
- package/dist/locales/is/target.json +301 -0
- package/dist/locales/is/translation.json +71 -0
- package/dist/locales/is/widget.json +149 -0
- package/dist/locales/it/target.json +301 -0
- package/dist/locales/it/translation.json +0 -446
- package/dist/locales/it/widget.json +149 -0
- package/dist/locales/ja/target.json +301 -0
- package/dist/locales/ja/translation.json +0 -446
- package/dist/locales/ja/widget.json +149 -0
- package/dist/locales/jv/target.json +301 -0
- package/dist/locales/jv/translation.json +47 -493
- package/dist/locales/jv/widget.json +149 -0
- package/dist/locales/ka/target.json +301 -0
- package/dist/locales/ka/translation.json +71 -0
- package/dist/locales/ka/widget.json +149 -0
- package/dist/locales/kk/target.json +301 -0
- package/dist/locales/kk/translation.json +71 -0
- package/dist/locales/kk/widget.json +149 -0
- package/dist/locales/km/target.json +301 -0
- package/dist/locales/km/translation.json +71 -0
- package/dist/locales/km/widget.json +149 -0
- package/dist/locales/kn/target.json +301 -0
- package/dist/locales/kn/translation.json +71 -0
- package/dist/locales/kn/widget.json +149 -0
- package/dist/locales/ko/target.json +301 -0
- package/dist/locales/ko/translation.json +0 -446
- package/dist/locales/ko/widget.json +149 -0
- package/dist/locales/ky/target.json +301 -0
- package/dist/locales/ky/translation.json +71 -0
- package/dist/locales/ky/widget.json +149 -0
- package/dist/locales/lo/target.json +301 -0
- package/dist/locales/lo/translation.json +71 -0
- package/dist/locales/lo/widget.json +149 -0
- package/dist/locales/lt/target.json +301 -0
- package/dist/locales/lt/translation.json +71 -0
- package/dist/locales/lt/widget.json +149 -0
- package/dist/locales/lv/target.json +301 -0
- package/dist/locales/lv/translation.json +71 -0
- package/dist/locales/lv/widget.json +149 -0
- package/dist/locales/mk/target.json +301 -0
- package/dist/locales/mk/translation.json +71 -0
- package/dist/locales/mk/widget.json +149 -0
- package/dist/locales/ml/target.json +301 -0
- package/dist/locales/ml/translation.json +47 -493
- package/dist/locales/ml/widget.json +149 -0
- package/dist/locales/mn/target.json +301 -0
- package/dist/locales/mn/translation.json +71 -0
- package/dist/locales/mn/widget.json +149 -0
- package/dist/locales/mr/target.json +301 -0
- package/dist/locales/mr/translation.json +71 -0
- package/dist/locales/mr/widget.json +149 -0
- package/dist/locales/ms/target.json +301 -0
- package/dist/locales/ms/translation.json +47 -493
- package/dist/locales/ms/widget.json +149 -0
- package/dist/locales/my/target.json +301 -0
- package/dist/locales/my/translation.json +71 -0
- package/dist/locales/my/widget.json +149 -0
- package/dist/locales/ne/target.json +301 -0
- package/dist/locales/ne/translation.json +71 -0
- package/dist/locales/ne/widget.json +149 -0
- package/dist/locales/nl/target.json +301 -0
- package/dist/locales/nl/translation.json +0 -446
- package/dist/locales/nl/widget.json +149 -0
- package/dist/locales/nn/target.json +301 -0
- package/dist/locales/nn/translation.json +71 -0
- package/dist/locales/nn/widget.json +149 -0
- package/dist/locales/no/target.json +301 -0
- package/dist/locales/no/translation.json +1 -447
- package/dist/locales/no/widget.json +149 -0
- package/dist/locales/oc/target.json +301 -0
- package/dist/locales/oc/translation.json +71 -0
- package/dist/locales/oc/widget.json +149 -0
- package/dist/locales/or/target.json +301 -0
- package/dist/locales/or/translation.json +71 -0
- package/dist/locales/or/widget.json +149 -0
- package/dist/locales/pa/target.json +301 -0
- package/dist/locales/pa/translation.json +71 -0
- package/dist/locales/pa/widget.json +149 -0
- package/dist/locales/pl/target.json +301 -0
- package/dist/locales/pl/translation.json +0 -446
- package/dist/locales/pl/widget.json +149 -0
- package/dist/locales/pt/target.json +301 -0
- package/dist/locales/pt/translation.json +0 -446
- package/dist/locales/pt/widget.json +149 -0
- package/dist/locales/pt-BR/target.json +301 -0
- package/dist/locales/pt-BR/translation.json +0 -446
- package/dist/locales/pt-BR/widget.json +149 -0
- package/dist/locales/rm/target.json +301 -0
- package/dist/locales/rm/translation.json +71 -0
- package/dist/locales/rm/widget.json +149 -0
- package/dist/locales/ro/target.json +301 -0
- package/dist/locales/ro/translation.json +0 -446
- package/dist/locales/ro/widget.json +149 -0
- package/dist/locales/ru/target.json +301 -0
- package/dist/locales/ru/translation.json +0 -446
- package/dist/locales/ru/widget.json +149 -0
- package/dist/locales/sco/target.json +301 -0
- package/dist/locales/sco/translation.json +71 -0
- package/dist/locales/sco/widget.json +149 -0
- package/dist/locales/si/target.json +301 -0
- package/dist/locales/si/translation.json +71 -0
- package/dist/locales/si/widget.json +149 -0
- package/dist/locales/sk/target.json +301 -0
- package/dist/locales/sk/translation.json +71 -0
- package/dist/locales/sk/widget.json +149 -0
- package/dist/locales/sl/target.json +301 -0
- package/dist/locales/sl/translation.json +71 -0
- package/dist/locales/sl/widget.json +149 -0
- package/dist/locales/sq/target.json +301 -0
- package/dist/locales/sq/translation.json +71 -0
- package/dist/locales/sq/widget.json +149 -0
- package/dist/locales/sr/target.json +301 -0
- package/dist/locales/sr/translation.json +0 -446
- package/dist/locales/sr/widget.json +149 -0
- package/dist/locales/sv/target.json +301 -0
- package/dist/locales/sv/translation.json +0 -446
- package/dist/locales/sv/widget.json +149 -0
- package/dist/locales/sw/target.json +301 -0
- package/dist/locales/sw/translation.json +71 -0
- package/dist/locales/sw/widget.json +149 -0
- package/dist/locales/ta/target.json +301 -0
- package/dist/locales/ta/translation.json +71 -0
- package/dist/locales/ta/widget.json +149 -0
- package/dist/locales/te/target.json +301 -0
- package/dist/locales/te/translation.json +71 -0
- package/dist/locales/te/widget.json +149 -0
- package/dist/locales/tg/target.json +301 -0
- package/dist/locales/tg/translation.json +71 -0
- package/dist/locales/tg/widget.json +149 -0
- package/dist/locales/th/target.json +301 -0
- package/dist/locales/th/translation.json +0 -446
- package/dist/locales/th/widget.json +149 -0
- package/dist/locales/tr/target.json +301 -0
- package/dist/locales/tr/translation.json +6 -452
- package/dist/locales/tr/widget.json +149 -0
- package/dist/locales/uk/target.json +301 -0
- package/dist/locales/uk/translation.json +47 -493
- package/dist/locales/uk/widget.json +149 -0
- package/dist/locales/ur/target.json +301 -0
- package/dist/locales/ur/translation.json +71 -0
- package/dist/locales/ur/widget.json +149 -0
- package/dist/locales/uz/target.json +301 -0
- package/dist/locales/uz/translation.json +71 -0
- package/dist/locales/uz/widget.json +149 -0
- package/dist/locales/vi/target.json +301 -0
- package/dist/locales/vi/translation.json +0 -446
- package/dist/locales/vi/widget.json +149 -0
- package/dist/locales/xh/target.json +301 -0
- package/dist/locales/xh/translation.json +71 -0
- package/dist/locales/xh/widget.json +149 -0
- package/dist/locales/zh/target.json +301 -0
- package/dist/locales/zh/translation.json +71 -0
- package/dist/locales/zh/widget.json +149 -0
- package/dist/locales/zh-CN/target.json +301 -0
- package/dist/locales/zh-CN/translation.json +0 -446
- package/dist/locales/zh-CN/widget.json +149 -0
- package/dist/locales/zh-HK/target.json +301 -0
- package/dist/locales/zh-HK/translation.json +71 -0
- package/dist/locales/zh-HK/widget.json +149 -0
- package/dist/locales/zh-TW/target.json +301 -0
- package/dist/locales/zh-TW/translation.json +71 -0
- package/dist/locales/zh-TW/widget.json +149 -0
- package/dist/locales/zu/target.json +301 -0
- package/dist/locales/zu/translation.json +71 -0
- package/dist/locales/zu/widget.json +149 -0
- package/dist/requestLanguageDetector.d.ts +3 -0
- package/dist/requestLanguageDetector.d.ts.map +1 -0
- package/dist/requestLanguageDetector.js +24 -0
- package/dist/requestLanguageDetector.js.map +1 -0
- package/dist/serverTranslationKeys.d.ts +7 -0
- package/dist/serverTranslationKeys.d.ts.map +1 -0
- package/dist/serverTranslationKeys.js +34 -0
- package/dist/serverTranslationKeys.js.map +1 -0
- package/dist/tests/browserBundle.unit.test.d.ts +2 -0
- package/dist/tests/browserBundle.unit.test.d.ts.map +1 -0
- package/dist/tests/browserBundle.unit.test.js +52 -0
- package/dist/tests/browserBundle.unit.test.js.map +1 -0
- package/dist/tests/browserUiLanguages.unit.test.d.ts +2 -0
- package/dist/tests/browserUiLanguages.unit.test.d.ts.map +1 -0
- package/dist/tests/browserUiLanguages.unit.test.js +55 -0
- package/dist/tests/browserUiLanguages.unit.test.js.map +1 -0
- package/dist/tests/catalogues.test-d.d.ts +2 -0
- package/dist/tests/catalogues.test-d.d.ts.map +1 -0
- package/dist/tests/catalogues.test-d.js +766 -0
- package/dist/tests/catalogues.test-d.js.map +1 -0
- package/dist/tests/direction.unit.test.js +216 -5
- package/dist/tests/direction.unit.test.js.map +1 -1
- package/dist/tests/errorMessage.unit.test.js +9 -1
- package/dist/tests/errorMessage.unit.test.js.map +1 -1
- package/dist/tests/i18SharedOptions.unit.test.js +0 -4
- package/dist/tests/i18SharedOptions.unit.test.js.map +1 -1
- package/dist/tests/i18nBackend.unit.test.js +100 -21
- package/dist/tests/i18nBackend.unit.test.js.map +1 -1
- package/dist/tests/i18nBackendFiles.unit.test.d.ts +2 -0
- package/dist/tests/i18nBackendFiles.unit.test.d.ts.map +1 -0
- package/dist/tests/i18nBackendFiles.unit.test.js +22 -0
- package/dist/tests/i18nBackendFiles.unit.test.js.map +1 -0
- package/dist/tests/i18nFrontend.unit.test.js +90 -54
- package/dist/tests/i18nFrontend.unit.test.js.map +1 -1
- package/dist/tests/keptAsIs.d.ts +4 -0
- package/dist/tests/keptAsIs.d.ts.map +1 -0
- package/dist/tests/keptAsIs.js +1236 -0
- package/dist/tests/keptAsIs.js.map +1 -0
- package/dist/tests/languagePreference.unit.test.d.ts +2 -0
- package/dist/tests/languagePreference.unit.test.d.ts.map +1 -0
- package/dist/tests/languagePreference.unit.test.js +204 -0
- package/dist/tests/languagePreference.unit.test.js.map +1 -0
- package/dist/tests/locale.test-d.js +20 -1
- package/dist/tests/locale.test-d.js.map +1 -1
- package/dist/tests/localePlaceholders.unit.test.js +5 -2
- package/dist/tests/localePlaceholders.unit.test.js.map +1 -1
- package/dist/tests/locales.unit.test.js +136 -43
- package/dist/tests/locales.unit.test.js.map +1 -1
- package/dist/tests/requestLanguageDetector.unit.test.d.ts +2 -0
- package/dist/tests/requestLanguageDetector.unit.test.d.ts.map +1 -0
- package/dist/tests/requestLanguageDetector.unit.test.js +106 -0
- package/dist/tests/requestLanguageDetector.unit.test.js.map +1 -0
- package/dist/tests/thrownErrorKeys.unit.test.js +2 -10
- package/dist/tests/thrownErrorKeys.unit.test.js.map +1 -1
- package/dist/tests/translationKey.unit.test.js +110 -5
- package/dist/tests/translationKey.unit.test.js.map +1 -1
- package/dist/tests/translations.unit.test.js +9 -5
- package/dist/tests/translations.unit.test.js.map +1 -1
- package/dist/tests/translator.unit.test.js +8 -2
- package/dist/tests/translator.unit.test.js.map +1 -1
- package/dist/translationKey.d.ts +16 -4
- package/dist/translationKey.d.ts.map +1 -1
- package/dist/translationKey.js +27 -6
- package/dist/translationKey.js.map +1 -1
- package/dist/translations.d.ts +63 -2
- package/dist/translations.d.ts.map +1 -1
- package/dist/translations.js +63 -2
- package/dist/translations.js.map +1 -1
- package/dist/translator.d.ts +2 -2
- package/dist/translator.d.ts.map +1 -1
- package/dist/translator.js +2 -1
- package/dist/translator.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -1 +1,204 @@
|
|
|
1
1
|
# i18next localization for Prosopo
|
|
2
|
+
|
|
3
|
+
Translation catalogues (`src/locales/<code>/<namespace>.json`) and the language
|
|
4
|
+
negotiation the widget and the provider share.
|
|
5
|
+
|
|
6
|
+
## Catalogue files
|
|
7
|
+
|
|
8
|
+
Each language has one file per i18next namespace (`NAMESPACES` in
|
|
9
|
+
`src/translationKey.ts`). The browser downloads only what it renders:
|
|
10
|
+
|
|
11
|
+
| File | Sections | Loaded by |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `widget.json` | WIDGET, API, CAPTCHA, GENERAL, ACCOUNT, DEVELOPER | widget and provider |
|
|
14
|
+
| `target.json` | TARGET (image labels) | provider |
|
|
15
|
+
| `translation.json` | DATABASE, DATASET, CONTRACT, CONFIG, PORTAL, CLI, FS | provider |
|
|
16
|
+
|
|
17
|
+
- Keys keep their section prefix, so `TARGET.owl` and `WIDGET.SELECT_ALL` read
|
|
18
|
+
the same whichever file holds them. The provider loads all three with
|
|
19
|
+
`translation` as the default namespace and the others as `fallbackNS`, so
|
|
20
|
+
`t("TARGET.owl")` needs no namespace.
|
|
21
|
+
- The widget bundles English `widget.json` and fetches only
|
|
22
|
+
`locales/<code>/widget.json` for another language. It always has English
|
|
23
|
+
text, for its own fallback and for logs and crash reports.
|
|
24
|
+
- The provider translates image labels itself, so the browser never needs
|
|
25
|
+
`target.json`. Each captcha in the image challenge carries the translated
|
|
26
|
+
`target`, its `targetKey` and its English `targetEn`, and the response names
|
|
27
|
+
its `language` (`packages/provider/src/api/captcha/localiseTarget.ts`).
|
|
28
|
+
- Provider errors carry their key. The widget re-translates keys it has
|
|
29
|
+
(`isWidgetTranslationKey`) and shows the provider's message for the rest. For
|
|
30
|
+
those, the provider also sends `messageEn` (`unwrapError` in
|
|
31
|
+
`packages/common/src/error.ts`), so the widget has English for every string
|
|
32
|
+
it shows. The bundle build copies `widget.json` whole, rather than stripping
|
|
33
|
+
keys its code never names (`packages/procaptcha-bundle/vite.config.ts`).
|
|
34
|
+
- `englishText(key)`, `isTranslationKey` and `isTargetKey`
|
|
35
|
+
(`src/serverTranslationKeys.ts`) import every English catalogue, so only
|
|
36
|
+
server code calls them. They read English whatever language i18next is in.
|
|
37
|
+
Code that also runs in the widget, such as `ProsopoError`'s message
|
|
38
|
+
translation, asks the i18n instance instead (`canTranslate`, which uses
|
|
39
|
+
`exists`). That keeps `target.json` and `translation.json` out of the widget
|
|
40
|
+
bundle.
|
|
41
|
+
- A section moves between files by moving it in every language. The tests in
|
|
42
|
+
`src/tests/locales.unit.test.ts` and `src/tests/catalogues.test-d.ts` fail on
|
|
43
|
+
a file whose keys differ from English's.
|
|
44
|
+
|
|
45
|
+
## Choosing the language
|
|
46
|
+
|
|
47
|
+
One preference list, resolved by one function (`matchLanguage` in
|
|
48
|
+
`src/languagePreference.ts`) on both sides:
|
|
49
|
+
|
|
50
|
+
1. The site's choice: `renderOptions.language`, else the `data-language`
|
|
51
|
+
attribute. Either may list several codes, as in Accept-Language
|
|
52
|
+
(`data-language="xx, tr"`). The first one we have a catalogue for wins. If
|
|
53
|
+
none match, the widget logs an error and falls through to step 2
|
|
54
|
+
(`packages/procaptcha-bundle/src/util/language.ts`).
|
|
55
|
+
2. The browser's `navigator.languages`.
|
|
56
|
+
3. English.
|
|
57
|
+
|
|
58
|
+
The widget does not read or write a cookie or local storage. It runs on other
|
|
59
|
+
people's sites, so an `i18next` cookie or storage key could clash with the
|
|
60
|
+
host's own. Its language comes only from the attribute or the browser.
|
|
61
|
+
|
|
62
|
+
### Matching a tag to a catalogue (`matchLanguageTag`)
|
|
63
|
+
|
|
64
|
+
- Case is ignored: `TR-tr` → `tr`.
|
|
65
|
+
- A variant falls back to its parent (RFC 4647 lookup): `zh-SG` → `zh`,
|
|
66
|
+
`pt-PT` → `pt`. A variant with its own catalogue keeps it: `pt-BR` →
|
|
67
|
+
`pt-BR`, `zh-CN` → `zh-CN`.
|
|
68
|
+
- Traditional Chinese (`zh-TW`, `zh-HK`, `zh-MO`, `zh-Hant-*`) never falls
|
|
69
|
+
back to `zh`, which is Simplified: Hong Kong and Macau tags get `zh-HK`,
|
|
70
|
+
the rest `zh-TW`. Both were converted from `zh-CN` with OpenCC and then
|
|
71
|
+
given local words (Taiwan 滑鼠, 機車; Hong Kong 巴士, 多士爐).
|
|
72
|
+
- Aliases for codes we have no catalogue for: `nb` → `no`; `tl` → `fil`,
|
|
73
|
+
which Firefox ships its Filipino interface under; the withdrawn codes
|
|
74
|
+
`in` → `id`, `iw` → `he` and `jw` → `jv`, which some browsers still send.
|
|
75
|
+
Nynorsk (`nn`) has its own catalogue.
|
|
76
|
+
- Each parent catalogue (`zh`, `pt`) starts as a copy of its variant. People
|
|
77
|
+
who read the variant generally read the parent too.
|
|
78
|
+
|
|
79
|
+
### Which languages have a catalogue
|
|
80
|
+
|
|
81
|
+
`BROWSER_UI_LANGUAGES` (`src/browserUiLanguages.ts`) lists every language
|
|
82
|
+
Chrome and Firefox ship their interface in, since a visitor's browser is most
|
|
83
|
+
likely set to one of them. `browserUiLanguages.unit.test.ts` fails if one of
|
|
84
|
+
them reaches no catalogue, apart from its `UNTRANSLATED` list: Firefox-only
|
|
85
|
+
languages with too few speakers for a confident draft (Acholi, Kaqchikel,
|
|
86
|
+
Triqui, Songhay, Santali, Saraiki, Fulah, Guarani and others). Those
|
|
87
|
+
visitors get English. The catalogues added from
|
|
88
|
+
this list were drafted without a native speaker and need review.
|
|
89
|
+
|
|
90
|
+
## Text direction
|
|
91
|
+
|
|
92
|
+
`getLanguageDirection` (`src/direction.ts`) says whether the chosen language is
|
|
93
|
+
written right to left. The bundle sets the answer as `dir` on the widget's host
|
|
94
|
+
(`packages/procaptcha-bundle/src/util/widgetFactory.ts`), and the widget's
|
|
95
|
+
styles mirror only under that `dir`, never under the page's.
|
|
96
|
+
|
|
97
|
+
- A tag is right to left if CLDR 48 says so or the browser's
|
|
98
|
+
`Intl.Locale#getTextInfo()` (older engines: `textInfo`) does. MDN listed
|
|
99
|
+
`getTextInfo` as Baseline 2026, newly available (checked 2026-10-16), so the
|
|
100
|
+
lists are what older browsers rely on.
|
|
101
|
+
- The lists hold every right-to-left script (37) and language (286, with
|
|
102
|
+
deprecated and three-letter aliases such as `iw` and `per`), and 16
|
|
103
|
+
language-region pairs whose usual script is Arabic (`pa-PK`, `uz-AF`). They
|
|
104
|
+
cover languages we have no catalogue for, so adding one needs no change to
|
|
105
|
+
them. They were generated from cldr-core 48.0.0 (`likelySubtags.json`,
|
|
106
|
+
`aliases.json`) and the scripts Node 24's ICU 78.3 reports as right to left.
|
|
107
|
+
- Cost: `direction.ts` is 1,187 bytes minified and gzipped, against 167 for
|
|
108
|
+
the ten-language version it replaced (esbuild `--minify`, gzip -9,
|
|
109
|
+
2026-10-16). Rejected: trimming the lists to languages we have catalogues
|
|
110
|
+
for, which would need a change here for every new catalogue.
|
|
111
|
+
- An explicit script wins: `pa-Arab` is right to left, `az-Latn-IR` left to
|
|
112
|
+
right. Subtags after an extension singleton (`-u-nu-arab`) are ignored.
|
|
113
|
+
- The direction follows the language the widget resolved, so a site asking
|
|
114
|
+
for a language we have no catalogue for gets English, left to right.
|
|
115
|
+
- `src/tests/direction.unit.test.ts` compares the lists with Node's ICU for
|
|
116
|
+
every two- and three-letter language code and the scripts they use. When
|
|
117
|
+
Node moves to a newer CLDR, it fails and names the codes to add or remove.
|
|
118
|
+
It also checks the lists cover i18next 26.4.2's right-to-left codes, except
|
|
119
|
+
the ones CLDR writes left to right.
|
|
120
|
+
- Rejected: i18next's `dir()`. It does the same Intl-then-list lookup, but
|
|
121
|
+
i18next is not in the widget (below).
|
|
122
|
+
|
|
123
|
+
## Sending it to the provider
|
|
124
|
+
|
|
125
|
+
The provider translates the challenge text (the image dataset's labels) itself.
|
|
126
|
+
The dataset and its languages then live only on the server. The widget sends
|
|
127
|
+
its preference list with every provider request in our own header,
|
|
128
|
+
`Prosopo-Lang: tr,en` (`widgetLanguageHeaders`, passed to `ProviderApi` as
|
|
129
|
+
default headers). The browser sends its own `Accept-Language` as usual.
|
|
130
|
+
|
|
131
|
+
The provider (`requestLanguage`, through `createRequestLanguageDetector` in
|
|
132
|
+
`src/requestLanguageDetector.ts`) takes the first of these that names a
|
|
133
|
+
language we support:
|
|
134
|
+
|
|
135
|
+
1. The `?lng=` query parameter, for clients that cannot set headers.
|
|
136
|
+
2. The `Prosopo-Lang` header.
|
|
137
|
+
3. The `Accept-Language` header.
|
|
138
|
+
4. Otherwise English.
|
|
139
|
+
|
|
140
|
+
```mermaid
|
|
141
|
+
sequenceDiagram
|
|
142
|
+
participant Site as Host page
|
|
143
|
+
participant Widget
|
|
144
|
+
participant Provider
|
|
145
|
+
Site->>Widget: data-language="xx, tr" (optional)
|
|
146
|
+
Widget->>Widget: matchLanguage(attribute) ?? matchLanguage(navigator.languages) ?? en
|
|
147
|
+
Widget->>Provider: Prosopo-Lang: tr (browser adds its own Accept-Language)
|
|
148
|
+
Provider->>Provider: ?lng= > Prosopo-Lang > Accept-Language > en
|
|
149
|
+
Provider-->>Widget: challenge text in tr (Content-Language: tr)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Why these headers
|
|
153
|
+
|
|
154
|
+
- **Prosopo-Lang** carries the site's choice. The widget cannot put it in
|
|
155
|
+
Accept-Language without hiding the browser's own value (below). It is a
|
|
156
|
+
custom header, so it
|
|
157
|
+
triggers a CORS preflight. It is listed in the provider's allowed headers
|
|
158
|
+
(`providerCorsOptions` in `packages/provider/src/api/startProviderApi.ts`).
|
|
159
|
+
- **Accept-Language is left to the browser.** The provider still reads it, as
|
|
160
|
+
the fallback when Prosopo-Lang is missing (a proxy dropped it, or a client
|
|
161
|
+
other than the widget). Rejected: setting it from script. The Fetch spec
|
|
162
|
+
allows it, and Chromium 153, Firefox 155 and WebKit 26.6 all sent a scripted
|
|
163
|
+
value unchanged (checked 2026-10-15, docker image
|
|
164
|
+
`mcr.microsoft.com/playwright:v1.63.0-noble`). But the provider's bot rules
|
|
165
|
+
read the raw header (`checkLangRules` in `packages/provider/src/rules/lang.ts`,
|
|
166
|
+
the stored `headers.accept-language`), and a value the widget wrote would
|
|
167
|
+
replace the browser's real one in every request, hiding it from them.
|
|
168
|
+
- **Rejected: a cookie.** The provider is a third-party origin, so its cookies
|
|
169
|
+
are often blocked. An `i18next` cookie set on the host site would also clash
|
|
170
|
+
with the site's own i18n.
|
|
171
|
+
- **Rejected: the query parameter alone.** It is kept only as a backup.
|
|
172
|
+
Headers keep URLs (and logs, and caches keyed on them) the same for every
|
|
173
|
+
language.
|
|
174
|
+
|
|
175
|
+
## Why the widget does not use i18next
|
|
176
|
+
|
|
177
|
+
The provider uses i18next. The widget uses `src/i18nFrontend.ts`, a small
|
|
178
|
+
look-up with the same `t` / `changeLanguage` / `on` surface. It fetches
|
|
179
|
+
`locales/<code>/widget.json` and fills `{{name}}` placeholders, and does nothing
|
|
180
|
+
else. The widget's strings need no plurals, context or nesting (the one count,
|
|
181
|
+
`{{count}}` audio digits, is never 1). Image labels are translated by the
|
|
182
|
+
provider.
|
|
183
|
+
|
|
184
|
+
i18next was dropped from the browser in
|
|
185
|
+
https://github.com/prosopo/captcha/pull/3310. With its language detector,
|
|
186
|
+
HTTP, chained and resources backends and the `cross-fetch` polyfill, it was
|
|
187
|
+
23.5 KB gzipped on the render path. That PR cut everything fetched before the
|
|
188
|
+
widget renders from 145 KB to 21 KB gzipped.
|
|
189
|
+
|
|
190
|
+
Measured again on 2026-10-15 (i18next 26.4.2). This was i18next core with a
|
|
191
|
+
ten-line fetch backend, no plugins, against `i18nFrontend.ts`. Both were
|
|
192
|
+
bundled by esbuild with `minify`, gzip level 9, with `@prosopo/*` and the JSON
|
|
193
|
+
catalogues external:
|
|
194
|
+
|
|
195
|
+
| | minified | gzipped |
|
|
196
|
+
| --- | --- | --- |
|
|
197
|
+
| i18next core + fetch backend | 43.7 KB | 14.1 KB |
|
|
198
|
+
| `i18nFrontend.ts` | 3.5 KB | 1.8 KB |
|
|
199
|
+
|
|
200
|
+
That is 12.2 KB more on a 21 KB render path for features the widget does not
|
|
201
|
+
use, so the widget keeps its own. The two sides still share the parts that
|
|
202
|
+
must agree: catalogues, key types (`WidgetTranslationKey`), language matching
|
|
203
|
+
(`matchLanguage`) and options (`src/i18SharedOptions.ts`). Revisit this if the
|
|
204
|
+
widget needs plurals or context.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TextDirection } from "./direction.js";
|
|
2
|
+
export type Browser = "chrome" | "firefox";
|
|
3
|
+
export interface BrowserUiLanguage {
|
|
4
|
+
readonly tag: string;
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly direction: TextDirection;
|
|
7
|
+
readonly browsers: readonly Browser[];
|
|
8
|
+
}
|
|
9
|
+
export declare const BROWSER_UI_LANGUAGES: readonly BrowserUiLanguage[];
|
|
10
|
+
//# sourceMappingURL=browserUiLanguages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browserUiLanguages.d.ts","sourceRoot":"","sources":["../src/browserUiLanguages.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3C,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CACtC;AAgBD,eAAO,MAAM,oBAAoB,EAAE,SAAS,iBAAiB,EAkO5D,CAAC"}
|