@prosopo/locale 3.9.0 → 3.10.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.
Files changed (409) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +267 -14
  2. package/.turbo/turbo-build$colon$tsc.log +5 -5
  3. package/.turbo/turbo-build.log +266 -13
  4. package/CHANGELOG.md +43 -0
  5. package/README.md +203 -0
  6. package/dist/browserUiLanguages.d.ts +10 -0
  7. package/dist/browserUiLanguages.d.ts.map +1 -0
  8. package/dist/browserUiLanguages.js +768 -0
  9. package/dist/browserUiLanguages.js.map +1 -0
  10. package/dist/cjs/browserUiLanguages.cjs +768 -0
  11. package/dist/cjs/direction.cjs +30 -15
  12. package/dist/cjs/errorMessage.cjs +3 -2
  13. package/dist/cjs/i18SharedOptions.cjs +0 -1
  14. package/dist/cjs/i18nBackend.cjs +32 -20
  15. package/dist/cjs/i18nFrontend.cjs +23 -73
  16. package/dist/cjs/index.cjs +23 -2
  17. package/dist/cjs/languagePreference.cjs +142 -0
  18. package/dist/cjs/loadI18next.cjs +4 -4
  19. package/dist/cjs/locales/en/target.cjs +304 -0
  20. package/dist/cjs/locales/en/translation.cjs +1 -459
  21. package/dist/cjs/locales/en/widget.cjs +162 -0
  22. package/dist/cjs/requestLanguageDetector.cjs +24 -0
  23. package/dist/cjs/serverTranslationKeys.cjs +38 -0
  24. package/dist/cjs/translationKey.cjs +29 -7
  25. package/dist/cjs/translations.cjs +63 -2
  26. package/dist/cjs/translator.cjs +2 -1
  27. package/dist/direction.d.ts.map +1 -1
  28. package/dist/direction.js +30 -15
  29. package/dist/direction.js.map +1 -1
  30. package/dist/errorMessage.d.ts.map +1 -1
  31. package/dist/errorMessage.js +4 -3
  32. package/dist/errorMessage.js.map +1 -1
  33. package/dist/i18SharedOptions.d.ts +0 -1
  34. package/dist/i18SharedOptions.d.ts.map +1 -1
  35. package/dist/i18SharedOptions.js +0 -1
  36. package/dist/i18SharedOptions.js.map +1 -1
  37. package/dist/i18nBackend.d.ts +3 -1
  38. package/dist/i18nBackend.d.ts.map +1 -1
  39. package/dist/i18nBackend.js +32 -22
  40. package/dist/i18nBackend.js.map +1 -1
  41. package/dist/i18nFrontend.d.ts.map +1 -1
  42. package/dist/i18nFrontend.js +23 -73
  43. package/dist/i18nFrontend.js.map +1 -1
  44. package/dist/index.d.ts +8 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +5 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/languagePreference.d.ts +17 -0
  49. package/dist/languagePreference.d.ts.map +1 -0
  50. package/dist/languagePreference.js +132 -0
  51. package/dist/languagePreference.js.map +1 -0
  52. package/dist/loadI18next.js +4 -4
  53. package/dist/locales/af/target.json +301 -0
  54. package/dist/locales/af/translation.json +71 -0
  55. package/dist/locales/af/widget.json +149 -0
  56. package/dist/locales/am/target.json +301 -0
  57. package/dist/locales/am/translation.json +71 -0
  58. package/dist/locales/am/widget.json +149 -0
  59. package/dist/locales/an/target.json +301 -0
  60. package/dist/locales/an/translation.json +71 -0
  61. package/dist/locales/an/widget.json +149 -0
  62. package/dist/locales/ar/target.json +301 -0
  63. package/dist/locales/ar/translation.json +0 -446
  64. package/dist/locales/ar/widget.json +149 -0
  65. package/dist/locales/as/target.json +301 -0
  66. package/dist/locales/as/translation.json +71 -0
  67. package/dist/locales/as/widget.json +149 -0
  68. package/dist/locales/ast/target.json +301 -0
  69. package/dist/locales/ast/translation.json +71 -0
  70. package/dist/locales/ast/widget.json +149 -0
  71. package/dist/locales/az/target.json +301 -0
  72. package/dist/locales/az/translation.json +0 -446
  73. package/dist/locales/az/widget.json +149 -0
  74. package/dist/locales/be/target.json +301 -0
  75. package/dist/locales/be/translation.json +71 -0
  76. package/dist/locales/be/widget.json +149 -0
  77. package/dist/locales/bg/target.json +301 -0
  78. package/dist/locales/bg/translation.json +71 -0
  79. package/dist/locales/bg/widget.json +149 -0
  80. package/dist/locales/bn/target.json +301 -0
  81. package/dist/locales/bn/translation.json +71 -0
  82. package/dist/locales/bn/widget.json +149 -0
  83. package/dist/locales/bs/target.json +301 -0
  84. package/dist/locales/bs/translation.json +71 -0
  85. package/dist/locales/bs/widget.json +149 -0
  86. package/dist/locales/ca/target.json +301 -0
  87. package/dist/locales/ca/translation.json +71 -0
  88. package/dist/locales/ca/widget.json +149 -0
  89. package/dist/locales/cs/target.json +301 -0
  90. package/dist/locales/cs/translation.json +0 -446
  91. package/dist/locales/cs/widget.json +149 -0
  92. package/dist/locales/cy/target.json +301 -0
  93. package/dist/locales/cy/translation.json +71 -0
  94. package/dist/locales/cy/widget.json +149 -0
  95. package/dist/locales/da/target.json +301 -0
  96. package/dist/locales/da/translation.json +71 -0
  97. package/dist/locales/da/widget.json +149 -0
  98. package/dist/locales/de/target.json +301 -0
  99. package/dist/locales/de/translation.json +0 -446
  100. package/dist/locales/de/widget.json +149 -0
  101. package/dist/locales/el/target.json +301 -0
  102. package/dist/locales/el/translation.json +0 -446
  103. package/dist/locales/el/widget.json +149 -0
  104. package/dist/locales/en/target.js +303 -0
  105. package/dist/locales/en/target.json +301 -0
  106. package/dist/locales/en/translation.js +2 -453
  107. package/dist/locales/en/translation.json +0 -446
  108. package/dist/locales/en/widget.js +156 -0
  109. package/dist/locales/en/widget.json +149 -0
  110. package/dist/locales/eo/target.json +301 -0
  111. package/dist/locales/eo/translation.json +71 -0
  112. package/dist/locales/eo/widget.json +149 -0
  113. package/dist/locales/es/target.json +301 -0
  114. package/dist/locales/es/translation.json +0 -446
  115. package/dist/locales/es/widget.json +149 -0
  116. package/dist/locales/et/target.json +301 -0
  117. package/dist/locales/et/translation.json +71 -0
  118. package/dist/locales/et/widget.json +149 -0
  119. package/dist/locales/eu/target.json +301 -0
  120. package/dist/locales/eu/translation.json +71 -0
  121. package/dist/locales/eu/widget.json +149 -0
  122. package/dist/locales/fa/target.json +301 -0
  123. package/dist/locales/fa/translation.json +71 -0
  124. package/dist/locales/fa/widget.json +149 -0
  125. package/dist/locales/fi/target.json +301 -0
  126. package/dist/locales/fi/translation.json +0 -446
  127. package/dist/locales/fi/widget.json +149 -0
  128. package/dist/locales/fil/target.json +301 -0
  129. package/dist/locales/fil/translation.json +71 -0
  130. package/dist/locales/fil/widget.json +149 -0
  131. package/dist/locales/fr/target.json +301 -0
  132. package/dist/locales/fr/translation.json +0 -446
  133. package/dist/locales/fr/widget.json +149 -0
  134. package/dist/locales/fy/target.json +301 -0
  135. package/dist/locales/fy/translation.json +71 -0
  136. package/dist/locales/fy/widget.json +149 -0
  137. package/dist/locales/ga/target.json +301 -0
  138. package/dist/locales/ga/translation.json +71 -0
  139. package/dist/locales/ga/widget.json +149 -0
  140. package/dist/locales/gd/target.json +301 -0
  141. package/dist/locales/gd/translation.json +71 -0
  142. package/dist/locales/gd/widget.json +149 -0
  143. package/dist/locales/gl/target.json +301 -0
  144. package/dist/locales/gl/translation.json +71 -0
  145. package/dist/locales/gl/widget.json +149 -0
  146. package/dist/locales/gu/target.json +301 -0
  147. package/dist/locales/gu/translation.json +71 -0
  148. package/dist/locales/gu/widget.json +149 -0
  149. package/dist/locales/he/target.json +301 -0
  150. package/dist/locales/he/translation.json +71 -0
  151. package/dist/locales/he/widget.json +149 -0
  152. package/dist/locales/hi/target.json +301 -0
  153. package/dist/locales/hi/translation.json +0 -446
  154. package/dist/locales/hi/widget.json +149 -0
  155. package/dist/locales/hr/target.json +301 -0
  156. package/dist/locales/hr/translation.json +71 -0
  157. package/dist/locales/hr/widget.json +149 -0
  158. package/dist/locales/hu/target.json +301 -0
  159. package/dist/locales/hu/translation.json +0 -446
  160. package/dist/locales/hu/widget.json +149 -0
  161. package/dist/locales/hy/target.json +301 -0
  162. package/dist/locales/hy/translation.json +71 -0
  163. package/dist/locales/hy/widget.json +149 -0
  164. package/dist/locales/ia/target.json +301 -0
  165. package/dist/locales/ia/translation.json +71 -0
  166. package/dist/locales/ia/widget.json +149 -0
  167. package/dist/locales/id/target.json +301 -0
  168. package/dist/locales/id/translation.json +10 -456
  169. package/dist/locales/id/widget.json +149 -0
  170. package/dist/locales/is/target.json +301 -0
  171. package/dist/locales/is/translation.json +71 -0
  172. package/dist/locales/is/widget.json +149 -0
  173. package/dist/locales/it/target.json +301 -0
  174. package/dist/locales/it/translation.json +0 -446
  175. package/dist/locales/it/widget.json +149 -0
  176. package/dist/locales/ja/target.json +301 -0
  177. package/dist/locales/ja/translation.json +0 -446
  178. package/dist/locales/ja/widget.json +149 -0
  179. package/dist/locales/jv/target.json +301 -0
  180. package/dist/locales/jv/translation.json +47 -493
  181. package/dist/locales/jv/widget.json +149 -0
  182. package/dist/locales/ka/target.json +301 -0
  183. package/dist/locales/ka/translation.json +71 -0
  184. package/dist/locales/ka/widget.json +149 -0
  185. package/dist/locales/kk/target.json +301 -0
  186. package/dist/locales/kk/translation.json +71 -0
  187. package/dist/locales/kk/widget.json +149 -0
  188. package/dist/locales/km/target.json +301 -0
  189. package/dist/locales/km/translation.json +71 -0
  190. package/dist/locales/km/widget.json +149 -0
  191. package/dist/locales/kn/target.json +301 -0
  192. package/dist/locales/kn/translation.json +71 -0
  193. package/dist/locales/kn/widget.json +149 -0
  194. package/dist/locales/ko/target.json +301 -0
  195. package/dist/locales/ko/translation.json +0 -446
  196. package/dist/locales/ko/widget.json +149 -0
  197. package/dist/locales/ky/target.json +301 -0
  198. package/dist/locales/ky/translation.json +71 -0
  199. package/dist/locales/ky/widget.json +149 -0
  200. package/dist/locales/lo/target.json +301 -0
  201. package/dist/locales/lo/translation.json +71 -0
  202. package/dist/locales/lo/widget.json +149 -0
  203. package/dist/locales/lt/target.json +301 -0
  204. package/dist/locales/lt/translation.json +71 -0
  205. package/dist/locales/lt/widget.json +149 -0
  206. package/dist/locales/lv/target.json +301 -0
  207. package/dist/locales/lv/translation.json +71 -0
  208. package/dist/locales/lv/widget.json +149 -0
  209. package/dist/locales/mk/target.json +301 -0
  210. package/dist/locales/mk/translation.json +71 -0
  211. package/dist/locales/mk/widget.json +149 -0
  212. package/dist/locales/ml/target.json +301 -0
  213. package/dist/locales/ml/translation.json +47 -493
  214. package/dist/locales/ml/widget.json +149 -0
  215. package/dist/locales/mn/target.json +301 -0
  216. package/dist/locales/mn/translation.json +71 -0
  217. package/dist/locales/mn/widget.json +149 -0
  218. package/dist/locales/mr/target.json +301 -0
  219. package/dist/locales/mr/translation.json +71 -0
  220. package/dist/locales/mr/widget.json +149 -0
  221. package/dist/locales/ms/target.json +301 -0
  222. package/dist/locales/ms/translation.json +47 -493
  223. package/dist/locales/ms/widget.json +149 -0
  224. package/dist/locales/my/target.json +301 -0
  225. package/dist/locales/my/translation.json +71 -0
  226. package/dist/locales/my/widget.json +149 -0
  227. package/dist/locales/ne/target.json +301 -0
  228. package/dist/locales/ne/translation.json +71 -0
  229. package/dist/locales/ne/widget.json +149 -0
  230. package/dist/locales/nl/target.json +301 -0
  231. package/dist/locales/nl/translation.json +0 -446
  232. package/dist/locales/nl/widget.json +149 -0
  233. package/dist/locales/nn/target.json +301 -0
  234. package/dist/locales/nn/translation.json +71 -0
  235. package/dist/locales/nn/widget.json +149 -0
  236. package/dist/locales/no/target.json +301 -0
  237. package/dist/locales/no/translation.json +1 -447
  238. package/dist/locales/no/widget.json +149 -0
  239. package/dist/locales/oc/target.json +301 -0
  240. package/dist/locales/oc/translation.json +71 -0
  241. package/dist/locales/oc/widget.json +149 -0
  242. package/dist/locales/or/target.json +301 -0
  243. package/dist/locales/or/translation.json +71 -0
  244. package/dist/locales/or/widget.json +149 -0
  245. package/dist/locales/pa/target.json +301 -0
  246. package/dist/locales/pa/translation.json +71 -0
  247. package/dist/locales/pa/widget.json +149 -0
  248. package/dist/locales/pl/target.json +301 -0
  249. package/dist/locales/pl/translation.json +0 -446
  250. package/dist/locales/pl/widget.json +149 -0
  251. package/dist/locales/pt/target.json +301 -0
  252. package/dist/locales/pt/translation.json +0 -446
  253. package/dist/locales/pt/widget.json +149 -0
  254. package/dist/locales/pt-BR/target.json +301 -0
  255. package/dist/locales/pt-BR/translation.json +0 -446
  256. package/dist/locales/pt-BR/widget.json +149 -0
  257. package/dist/locales/rm/target.json +301 -0
  258. package/dist/locales/rm/translation.json +71 -0
  259. package/dist/locales/rm/widget.json +149 -0
  260. package/dist/locales/ro/target.json +301 -0
  261. package/dist/locales/ro/translation.json +0 -446
  262. package/dist/locales/ro/widget.json +149 -0
  263. package/dist/locales/ru/target.json +301 -0
  264. package/dist/locales/ru/translation.json +0 -446
  265. package/dist/locales/ru/widget.json +149 -0
  266. package/dist/locales/sco/target.json +301 -0
  267. package/dist/locales/sco/translation.json +71 -0
  268. package/dist/locales/sco/widget.json +149 -0
  269. package/dist/locales/si/target.json +301 -0
  270. package/dist/locales/si/translation.json +71 -0
  271. package/dist/locales/si/widget.json +149 -0
  272. package/dist/locales/sk/target.json +301 -0
  273. package/dist/locales/sk/translation.json +71 -0
  274. package/dist/locales/sk/widget.json +149 -0
  275. package/dist/locales/sl/target.json +301 -0
  276. package/dist/locales/sl/translation.json +71 -0
  277. package/dist/locales/sl/widget.json +149 -0
  278. package/dist/locales/sq/target.json +301 -0
  279. package/dist/locales/sq/translation.json +71 -0
  280. package/dist/locales/sq/widget.json +149 -0
  281. package/dist/locales/sr/target.json +301 -0
  282. package/dist/locales/sr/translation.json +0 -446
  283. package/dist/locales/sr/widget.json +149 -0
  284. package/dist/locales/sv/target.json +301 -0
  285. package/dist/locales/sv/translation.json +0 -446
  286. package/dist/locales/sv/widget.json +149 -0
  287. package/dist/locales/sw/target.json +301 -0
  288. package/dist/locales/sw/translation.json +71 -0
  289. package/dist/locales/sw/widget.json +149 -0
  290. package/dist/locales/ta/target.json +301 -0
  291. package/dist/locales/ta/translation.json +71 -0
  292. package/dist/locales/ta/widget.json +149 -0
  293. package/dist/locales/te/target.json +301 -0
  294. package/dist/locales/te/translation.json +71 -0
  295. package/dist/locales/te/widget.json +149 -0
  296. package/dist/locales/tg/target.json +301 -0
  297. package/dist/locales/tg/translation.json +71 -0
  298. package/dist/locales/tg/widget.json +149 -0
  299. package/dist/locales/th/target.json +301 -0
  300. package/dist/locales/th/translation.json +0 -446
  301. package/dist/locales/th/widget.json +149 -0
  302. package/dist/locales/tr/target.json +301 -0
  303. package/dist/locales/tr/translation.json +6 -452
  304. package/dist/locales/tr/widget.json +149 -0
  305. package/dist/locales/uk/target.json +301 -0
  306. package/dist/locales/uk/translation.json +47 -493
  307. package/dist/locales/uk/widget.json +149 -0
  308. package/dist/locales/ur/target.json +301 -0
  309. package/dist/locales/ur/translation.json +71 -0
  310. package/dist/locales/ur/widget.json +149 -0
  311. package/dist/locales/uz/target.json +301 -0
  312. package/dist/locales/uz/translation.json +71 -0
  313. package/dist/locales/uz/widget.json +149 -0
  314. package/dist/locales/vi/target.json +301 -0
  315. package/dist/locales/vi/translation.json +0 -446
  316. package/dist/locales/vi/widget.json +149 -0
  317. package/dist/locales/xh/target.json +301 -0
  318. package/dist/locales/xh/translation.json +71 -0
  319. package/dist/locales/xh/widget.json +149 -0
  320. package/dist/locales/zh/target.json +301 -0
  321. package/dist/locales/zh/translation.json +71 -0
  322. package/dist/locales/zh/widget.json +149 -0
  323. package/dist/locales/zh-CN/target.json +301 -0
  324. package/dist/locales/zh-CN/translation.json +0 -446
  325. package/dist/locales/zh-CN/widget.json +149 -0
  326. package/dist/locales/zh-HK/target.json +301 -0
  327. package/dist/locales/zh-HK/translation.json +71 -0
  328. package/dist/locales/zh-HK/widget.json +149 -0
  329. package/dist/locales/zh-TW/target.json +301 -0
  330. package/dist/locales/zh-TW/translation.json +71 -0
  331. package/dist/locales/zh-TW/widget.json +149 -0
  332. package/dist/locales/zu/target.json +301 -0
  333. package/dist/locales/zu/translation.json +71 -0
  334. package/dist/locales/zu/widget.json +149 -0
  335. package/dist/requestLanguageDetector.d.ts +3 -0
  336. package/dist/requestLanguageDetector.d.ts.map +1 -0
  337. package/dist/requestLanguageDetector.js +24 -0
  338. package/dist/requestLanguageDetector.js.map +1 -0
  339. package/dist/serverTranslationKeys.d.ts +7 -0
  340. package/dist/serverTranslationKeys.d.ts.map +1 -0
  341. package/dist/serverTranslationKeys.js +34 -0
  342. package/dist/serverTranslationKeys.js.map +1 -0
  343. package/dist/tests/browserBundle.unit.test.d.ts +2 -0
  344. package/dist/tests/browserBundle.unit.test.d.ts.map +1 -0
  345. package/dist/tests/browserBundle.unit.test.js +26 -0
  346. package/dist/tests/browserBundle.unit.test.js.map +1 -0
  347. package/dist/tests/browserUiLanguages.unit.test.d.ts +2 -0
  348. package/dist/tests/browserUiLanguages.unit.test.d.ts.map +1 -0
  349. package/dist/tests/browserUiLanguages.unit.test.js +55 -0
  350. package/dist/tests/browserUiLanguages.unit.test.js.map +1 -0
  351. package/dist/tests/catalogues.test-d.d.ts +2 -0
  352. package/dist/tests/catalogues.test-d.d.ts.map +1 -0
  353. package/dist/tests/catalogues.test-d.js +766 -0
  354. package/dist/tests/catalogues.test-d.js.map +1 -0
  355. package/dist/tests/direction.unit.test.js +216 -5
  356. package/dist/tests/direction.unit.test.js.map +1 -1
  357. package/dist/tests/errorMessage.unit.test.js +9 -1
  358. package/dist/tests/errorMessage.unit.test.js.map +1 -1
  359. package/dist/tests/i18SharedOptions.unit.test.js +0 -4
  360. package/dist/tests/i18SharedOptions.unit.test.js.map +1 -1
  361. package/dist/tests/i18nBackend.unit.test.js +100 -21
  362. package/dist/tests/i18nBackend.unit.test.js.map +1 -1
  363. package/dist/tests/i18nBackendFiles.unit.test.d.ts +2 -0
  364. package/dist/tests/i18nBackendFiles.unit.test.d.ts.map +1 -0
  365. package/dist/tests/i18nBackendFiles.unit.test.js +22 -0
  366. package/dist/tests/i18nBackendFiles.unit.test.js.map +1 -0
  367. package/dist/tests/i18nFrontend.unit.test.js +90 -54
  368. package/dist/tests/i18nFrontend.unit.test.js.map +1 -1
  369. package/dist/tests/keptAsIs.d.ts +4 -0
  370. package/dist/tests/keptAsIs.d.ts.map +1 -0
  371. package/dist/tests/keptAsIs.js +1236 -0
  372. package/dist/tests/keptAsIs.js.map +1 -0
  373. package/dist/tests/languagePreference.unit.test.d.ts +2 -0
  374. package/dist/tests/languagePreference.unit.test.d.ts.map +1 -0
  375. package/dist/tests/languagePreference.unit.test.js +204 -0
  376. package/dist/tests/languagePreference.unit.test.js.map +1 -0
  377. package/dist/tests/locale.test-d.js +20 -1
  378. package/dist/tests/locale.test-d.js.map +1 -1
  379. package/dist/tests/localePlaceholders.unit.test.js +5 -2
  380. package/dist/tests/localePlaceholders.unit.test.js.map +1 -1
  381. package/dist/tests/locales.unit.test.js +136 -43
  382. package/dist/tests/locales.unit.test.js.map +1 -1
  383. package/dist/tests/requestLanguageDetector.unit.test.d.ts +2 -0
  384. package/dist/tests/requestLanguageDetector.unit.test.d.ts.map +1 -0
  385. package/dist/tests/requestLanguageDetector.unit.test.js +106 -0
  386. package/dist/tests/requestLanguageDetector.unit.test.js.map +1 -0
  387. package/dist/tests/thrownErrorKeys.unit.test.js +2 -10
  388. package/dist/tests/thrownErrorKeys.unit.test.js.map +1 -1
  389. package/dist/tests/translationKey.unit.test.js +110 -5
  390. package/dist/tests/translationKey.unit.test.js.map +1 -1
  391. package/dist/tests/translations.unit.test.js +9 -5
  392. package/dist/tests/translations.unit.test.js.map +1 -1
  393. package/dist/tests/translator.unit.test.js +8 -2
  394. package/dist/tests/translator.unit.test.js.map +1 -1
  395. package/dist/translationKey.d.ts +16 -4
  396. package/dist/translationKey.d.ts.map +1 -1
  397. package/dist/translationKey.js +27 -6
  398. package/dist/translationKey.js.map +1 -1
  399. package/dist/translations.d.ts +63 -2
  400. package/dist/translations.d.ts.map +1 -1
  401. package/dist/translations.js +63 -2
  402. package/dist/translations.js.map +1 -1
  403. package/dist/translator.d.ts +2 -2
  404. package/dist/translator.d.ts.map +1 -1
  405. package/dist/translator.js +2 -1
  406. package/dist/translator.js.map +1 -1
  407. package/dist/types.d.ts +2 -0
  408. package/dist/types.d.ts.map +1 -1
  409. package/package.json +13 -3
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"}