engagelab-captcha-sdk 1.0.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 (73) hide show
  1. package/dist/captcha-sdk.esm.js +1 -0
  2. package/dist/captcha-sdk.umd.js +1 -0
  3. package/dist/types/api.d.ts +12 -0
  4. package/dist/types/challenges/dragsort.d.ts +24 -0
  5. package/dist/types/challenges/grid.d.ts +20 -0
  6. package/dist/types/challenges/icons.d.ts +18 -0
  7. package/dist/types/challenges/invisible.d.ts +8 -0
  8. package/dist/types/challenges/physics.d.ts +40 -0
  9. package/dist/types/challenges/rotate.d.ts +14 -0
  10. package/dist/types/challenges/slider.d.ts +17 -0
  11. package/dist/types/challenges/spatial.d.ts +12 -0
  12. package/dist/types/compat/hcaptcha.d.ts +1 -0
  13. package/dist/types/compat/recaptcha.d.ts +1 -0
  14. package/dist/types/crypto.d.ts +11 -0
  15. package/dist/types/deep_probes.d.ts +13 -0
  16. package/dist/types/degradation.d.ts +27 -0
  17. package/dist/types/device_attestation.d.ts +19 -0
  18. package/dist/types/fingerprint.d.ts +2 -0
  19. package/dist/types/headless.d.ts +2 -0
  20. package/dist/types/i18n.d.ts +7 -0
  21. package/dist/types/index.d.ts +36 -0
  22. package/dist/types/integrity.d.ts +3 -0
  23. package/dist/types/logger.d.ts +49 -0
  24. package/dist/types/native_monitor.d.ts +21 -0
  25. package/dist/types/passive_signals.d.ts +119 -0
  26. package/dist/types/pow.d.ts +2 -0
  27. package/dist/types/reporter.d.ts +8 -0
  28. package/dist/types/session_persist.d.ts +18 -0
  29. package/dist/types/theme.d.ts +11 -0
  30. package/dist/types/timing.d.ts +2 -0
  31. package/dist/types/trajectory.d.ts +18 -0
  32. package/dist/types/types.d.ts +182 -0
  33. package/dist/types/ui/container.d.ts +20 -0
  34. package/dist/types/ui/styles.d.ts +1 -0
  35. package/dist/types/wasm_core.d.ts +44 -0
  36. package/dist/types/web-component.d.ts +11 -0
  37. package/package.json +22 -0
  38. package/rollup.config.mjs +62 -0
  39. package/src/api.ts +180 -0
  40. package/src/challenges/dragsort.ts +168 -0
  41. package/src/challenges/grid.ts +147 -0
  42. package/src/challenges/icons.ts +106 -0
  43. package/src/challenges/invisible.ts +57 -0
  44. package/src/challenges/physics.ts +437 -0
  45. package/src/challenges/rotate.ts +81 -0
  46. package/src/challenges/slider.ts +168 -0
  47. package/src/challenges/spatial.ts +91 -0
  48. package/src/compat/hcaptcha.ts +112 -0
  49. package/src/compat/recaptcha.ts +108 -0
  50. package/src/crypto.ts +69 -0
  51. package/src/deep_probes.ts +690 -0
  52. package/src/degradation.ts +113 -0
  53. package/src/device_attestation.ts +109 -0
  54. package/src/fingerprint.ts +247 -0
  55. package/src/headless.ts +233 -0
  56. package/src/i18n.ts +455 -0
  57. package/src/index.ts +527 -0
  58. package/src/integrity.ts +100 -0
  59. package/src/logger.ts +170 -0
  60. package/src/native_monitor.ts +100 -0
  61. package/src/passive_signals.ts +544 -0
  62. package/src/pow.ts +120 -0
  63. package/src/reporter.ts +75 -0
  64. package/src/session_persist.ts +90 -0
  65. package/src/theme.ts +41 -0
  66. package/src/timing.ts +79 -0
  67. package/src/trajectory.ts +110 -0
  68. package/src/types.ts +199 -0
  69. package/src/ui/container.ts +161 -0
  70. package/src/ui/styles.ts +153 -0
  71. package/src/wasm_core.ts +189 -0
  72. package/src/web-component.ts +103 -0
  73. package/tsconfig.json +18 -0
package/src/i18n.ts ADDED
@@ -0,0 +1,455 @@
1
+ export type Locale = string;
2
+
3
+ const RTL_LOCALES = new Set(['ar-SA', 'he-IL', 'fa-IR']);
4
+
5
+ const messages: Record<string, Record<string, string>> = {
6
+ 'zh-CN': {
7
+ 'invisible.button': '我不是机器人',
8
+ 'invisible.verifying': '验证中...',
9
+ 'invisible.success': '验证成功',
10
+ 'invisible.fail': '验证失败',
11
+ 'slider.hint': '请拖动滑块到指定位置',
12
+ 'spatial.title': '空间推理验证',
13
+ 'rotate.hint': '请旋转图片到正确角度',
14
+ 'icons.confirm': '确认',
15
+ 'icons.reset': '重新选择',
16
+ 'dragsort.hint': '请拖拽排列到正确顺序',
17
+ 'dragsort.confirm': '确认排序',
18
+ 'dragsort.reset': '重置',
19
+ 'grid.confirm': '确认选择',
20
+ 'grid.reset': '重新选择',
21
+ 'loading': '正在验证中...',
22
+ 'success': '验证通过!',
23
+ 'fail': '验证失败,请重试',
24
+ 'close': '关闭',
25
+ 'poweredBy': 'Powered by EngageLab',
26
+ 'challenge.imageAlt': '验证码图片',
27
+ },
28
+ 'en-US': {
29
+ 'invisible.button': "I'm not a robot",
30
+ 'invisible.verifying': 'Verifying...',
31
+ 'invisible.success': 'Verified',
32
+ 'invisible.fail': 'Verification failed',
33
+ 'slider.hint': 'Drag the slider to the target position',
34
+ 'spatial.title': 'Spatial Reasoning',
35
+ 'rotate.hint': 'Rotate the image to the correct angle',
36
+ 'icons.confirm': 'Confirm',
37
+ 'icons.reset': 'Reset',
38
+ 'dragsort.hint': 'Drag items into the correct order',
39
+ 'dragsort.confirm': 'Confirm',
40
+ 'dragsort.reset': 'Reset',
41
+ 'grid.confirm': 'Confirm',
42
+ 'grid.reset': 'Reset',
43
+ 'loading': 'Verifying...',
44
+ 'success': 'Verification passed!',
45
+ 'fail': 'Verification failed, please retry',
46
+ 'close': 'Close',
47
+ 'poweredBy': 'Powered by EngageLab',
48
+ 'challenge.imageAlt': 'CAPTCHA image',
49
+ },
50
+ 'ja-JP': {
51
+ 'invisible.button': '私はロボットではありません',
52
+ 'invisible.verifying': '確認中...',
53
+ 'invisible.success': '確認完了',
54
+ 'invisible.fail': '確認失敗',
55
+ 'slider.hint': 'スライダーを指定位置までドラッグ',
56
+ 'spatial.title': '空間推論認証',
57
+ 'rotate.hint': '画像を正しい角度に回転',
58
+ 'icons.confirm': '確認',
59
+ 'icons.reset': 'リセット',
60
+ 'dragsort.hint': '正しい順序にドラッグ',
61
+ 'dragsort.confirm': '確認',
62
+ 'dragsort.reset': 'リセット',
63
+ 'grid.confirm': '確認',
64
+ 'grid.reset': 'リセット',
65
+ 'loading': '確認中...',
66
+ 'success': '認証完了!',
67
+ 'fail': '認証失敗、再試行してください',
68
+ 'close': '閉じる',
69
+ 'poweredBy': 'Powered by EngageLab',
70
+ 'challenge.imageAlt': 'CAPTCHA画像',
71
+ },
72
+ 'zh-TW': {
73
+ 'invisible.button': '我不是機器人',
74
+ 'invisible.verifying': '驗證中...',
75
+ 'invisible.success': '驗證成功',
76
+ 'invisible.fail': '驗證失敗',
77
+ 'slider.hint': '請拖動滑塊到指定位置',
78
+ 'spatial.title': '空間推理驗證',
79
+ 'rotate.hint': '請旋轉圖片到正確角度',
80
+ 'icons.confirm': '確認',
81
+ 'icons.reset': '重新選擇',
82
+ 'dragsort.hint': '請拖曳排列到正確順序',
83
+ 'dragsort.confirm': '確認排序',
84
+ 'dragsort.reset': '重置',
85
+ 'grid.confirm': '確認選擇',
86
+ 'grid.reset': '重新選擇',
87
+ 'loading': '正在驗證中...',
88
+ 'success': '驗證通過!',
89
+ 'fail': '驗證失敗,請重試',
90
+ 'close': '關閉',
91
+ 'poweredBy': 'Powered by EngageLab',
92
+ 'challenge.imageAlt': '驗證碼圖片',
93
+ },
94
+ 'ko-KR': {
95
+ 'invisible.button': '저는 로봇이 아닙니다',
96
+ 'invisible.verifying': '확인 중...',
97
+ 'invisible.success': '확인 완료',
98
+ 'invisible.fail': '확인 실패',
99
+ 'slider.hint': '슬라이더를 지정 위치로 드래그하세요',
100
+ 'spatial.title': '공간 추론 인증',
101
+ 'rotate.hint': '이미지를 올바른 각도로 회전하세요',
102
+ 'icons.confirm': '확인',
103
+ 'icons.reset': '재선택',
104
+ 'dragsort.hint': '올바른 순서로 드래그하세요',
105
+ 'dragsort.confirm': '확인',
106
+ 'dragsort.reset': '초기화',
107
+ 'grid.confirm': '확인',
108
+ 'grid.reset': '재선택',
109
+ 'loading': '확인 중...',
110
+ 'success': '인증 완료!',
111
+ 'fail': '인증 실패, 다시 시도해주세요',
112
+ 'close': '닫기',
113
+ 'poweredBy': 'Powered by EngageLab',
114
+ 'challenge.imageAlt': 'CAPTCHA 이미지',
115
+ },
116
+ 'de-DE': {
117
+ 'invisible.button': 'Ich bin kein Roboter',
118
+ 'invisible.verifying': 'Überprüfung...',
119
+ 'invisible.success': 'Verifiziert',
120
+ 'invisible.fail': 'Überprüfung fehlgeschlagen',
121
+ 'slider.hint': 'Ziehen Sie den Schieberegler zur Zielposition',
122
+ 'spatial.title': 'Räumliches Denken',
123
+ 'rotate.hint': 'Drehen Sie das Bild in den richtigen Winkel',
124
+ 'icons.confirm': 'Bestätigen',
125
+ 'icons.reset': 'Zurücksetzen',
126
+ 'dragsort.hint': 'Elemente in die richtige Reihenfolge ziehen',
127
+ 'dragsort.confirm': 'Bestätigen',
128
+ 'dragsort.reset': 'Zurücksetzen',
129
+ 'grid.confirm': 'Bestätigen',
130
+ 'grid.reset': 'Zurücksetzen',
131
+ 'loading': 'Überprüfung...',
132
+ 'success': 'Verifizierung bestanden!',
133
+ 'fail': 'Verifizierung fehlgeschlagen, bitte erneut versuchen',
134
+ 'close': 'Schließen',
135
+ 'poweredBy': 'Powered by EngageLab',
136
+ 'challenge.imageAlt': 'CAPTCHA-Bild',
137
+ },
138
+ 'fr-FR': {
139
+ 'invisible.button': 'Je ne suis pas un robot',
140
+ 'invisible.verifying': 'Vérification...',
141
+ 'invisible.success': 'Vérifié',
142
+ 'invisible.fail': 'Échec de la vérification',
143
+ 'slider.hint': 'Faites glisser le curseur vers la position cible',
144
+ 'spatial.title': 'Raisonnement spatial',
145
+ 'rotate.hint': "Tournez l'image au bon angle",
146
+ 'icons.confirm': 'Confirmer',
147
+ 'icons.reset': 'Réinitialiser',
148
+ 'dragsort.hint': "Glissez les éléments dans le bon ordre",
149
+ 'dragsort.confirm': 'Confirmer',
150
+ 'dragsort.reset': 'Réinitialiser',
151
+ 'grid.confirm': 'Confirmer',
152
+ 'grid.reset': 'Réinitialiser',
153
+ 'loading': 'Vérification en cours...',
154
+ 'success': 'Vérification réussie !',
155
+ 'fail': 'Échec de la vérification, veuillez réessayer',
156
+ 'close': 'Fermer',
157
+ 'poweredBy': 'Powered by EngageLab',
158
+ 'challenge.imageAlt': 'Image CAPTCHA',
159
+ },
160
+ 'es-ES': {
161
+ 'invisible.button': 'No soy un robot',
162
+ 'invisible.verifying': 'Verificando...',
163
+ 'invisible.success': 'Verificado',
164
+ 'invisible.fail': 'Verificación fallida',
165
+ 'slider.hint': 'Arrastra el deslizador a la posición objetivo',
166
+ 'spatial.title': 'Razonamiento espacial',
167
+ 'rotate.hint': 'Gira la imagen al ángulo correcto',
168
+ 'icons.confirm': 'Confirmar',
169
+ 'icons.reset': 'Reiniciar',
170
+ 'dragsort.hint': 'Arrastra los elementos en el orden correcto',
171
+ 'dragsort.confirm': 'Confirmar',
172
+ 'dragsort.reset': 'Reiniciar',
173
+ 'grid.confirm': 'Confirmar',
174
+ 'grid.reset': 'Reiniciar',
175
+ 'loading': 'Verificando...',
176
+ 'success': '¡Verificación exitosa!',
177
+ 'fail': 'Verificación fallida, por favor inténtalo de nuevo',
178
+ 'close': 'Cerrar',
179
+ 'poweredBy': 'Powered by EngageLab',
180
+ 'challenge.imageAlt': 'Imagen CAPTCHA',
181
+ },
182
+ 'pt-BR': {
183
+ 'invisible.button': 'Não sou um robô',
184
+ 'invisible.verifying': 'Verificando...',
185
+ 'invisible.success': 'Verificado',
186
+ 'invisible.fail': 'Falha na verificação',
187
+ 'slider.hint': 'Arraste o controle deslizante para a posição alvo',
188
+ 'spatial.title': 'Raciocínio espacial',
189
+ 'rotate.hint': 'Gire a imagem para o ângulo correto',
190
+ 'icons.confirm': 'Confirmar',
191
+ 'icons.reset': 'Redefinir',
192
+ 'dragsort.hint': 'Arraste os itens para a ordem correta',
193
+ 'dragsort.confirm': 'Confirmar',
194
+ 'dragsort.reset': 'Redefinir',
195
+ 'grid.confirm': 'Confirmar',
196
+ 'grid.reset': 'Redefinir',
197
+ 'loading': 'Verificando...',
198
+ 'success': 'Verificação aprovada!',
199
+ 'fail': 'Falha na verificação, tente novamente',
200
+ 'close': 'Fechar',
201
+ 'poweredBy': 'Powered by EngageLab',
202
+ 'challenge.imageAlt': 'Imagem CAPTCHA',
203
+ },
204
+ 'ru-RU': {
205
+ 'invisible.button': 'Я не робот',
206
+ 'invisible.verifying': 'Проверка...',
207
+ 'invisible.success': 'Подтверждено',
208
+ 'invisible.fail': 'Проверка не пройдена',
209
+ 'slider.hint': 'Перетащите ползунок в нужную позицию',
210
+ 'spatial.title': 'Пространственное мышление',
211
+ 'rotate.hint': 'Поверните изображение на правильный угол',
212
+ 'icons.confirm': 'Подтвердить',
213
+ 'icons.reset': 'Сбросить',
214
+ 'dragsort.hint': 'Перетащите элементы в правильном порядке',
215
+ 'dragsort.confirm': 'Подтвердить',
216
+ 'dragsort.reset': 'Сбросить',
217
+ 'grid.confirm': 'Подтвердить',
218
+ 'grid.reset': 'Сбросить',
219
+ 'loading': 'Проверка...',
220
+ 'success': 'Проверка пройдена!',
221
+ 'fail': 'Проверка не пройдена, попробуйте снова',
222
+ 'close': 'Закрыть',
223
+ 'poweredBy': 'Powered by EngageLab',
224
+ 'challenge.imageAlt': 'Изображение CAPTCHA',
225
+ },
226
+ 'ar-SA': {
227
+ 'invisible.button': 'أنا لست روبوتًا',
228
+ 'invisible.verifying': 'جارٍ التحقق...',
229
+ 'invisible.success': 'تم التحقق',
230
+ 'invisible.fail': 'فشل التحقق',
231
+ 'slider.hint': 'اسحب شريط التمرير إلى الموضع المستهدف',
232
+ 'spatial.title': 'التفكير المكاني',
233
+ 'rotate.hint': 'قم بتدوير الصورة إلى الزاوية الصحيحة',
234
+ 'icons.confirm': 'تأكيد',
235
+ 'icons.reset': 'إعادة تعيين',
236
+ 'dragsort.hint': 'اسحب العناصر بالترتيب الصحيح',
237
+ 'dragsort.confirm': 'تأكيد',
238
+ 'dragsort.reset': 'إعادة تعيين',
239
+ 'grid.confirm': 'تأكيد',
240
+ 'grid.reset': 'إعادة تعيين',
241
+ 'loading': 'جارٍ التحقق...',
242
+ 'success': 'تم التحقق بنجاح!',
243
+ 'fail': 'فشل التحقق، يرجى المحاولة مرة أخرى',
244
+ 'close': 'إغلاق',
245
+ 'poweredBy': 'Powered by EngageLab',
246
+ 'challenge.imageAlt': 'صورة التحقق',
247
+ },
248
+ 'hi-IN': {
249
+ 'invisible.button': 'मैं रोबोट नहीं हूँ',
250
+ 'invisible.verifying': 'सत्यापन हो रहा है...',
251
+ 'invisible.success': 'सत्यापित',
252
+ 'invisible.fail': 'सत्यापन विफल',
253
+ 'slider.hint': 'स्लाइडर को लक्ष्य स्थान पर खींचें',
254
+ 'spatial.title': 'स्थानिक तर्क',
255
+ 'rotate.hint': 'छवि को सही कोण पर घुमाएँ',
256
+ 'icons.confirm': 'पुष्टि करें',
257
+ 'icons.reset': 'रीसेट',
258
+ 'dragsort.hint': 'आइटम को सही क्रम में खींचें',
259
+ 'dragsort.confirm': 'पुष्टि करें',
260
+ 'dragsort.reset': 'रीसेट',
261
+ 'grid.confirm': 'पुष्टि करें',
262
+ 'grid.reset': 'रीसेट',
263
+ 'loading': 'सत्यापन हो रहा है...',
264
+ 'success': 'सत्यापन सफल!',
265
+ 'fail': 'सत्यापन विफल, कृपया पुनः प्रयास करें',
266
+ 'close': 'बंद करें',
267
+ 'poweredBy': 'Powered by EngageLab',
268
+ 'challenge.imageAlt': 'कैप्चा छवि',
269
+ },
270
+ 'th-TH': {
271
+ 'invisible.button': 'ฉันไม่ใช่หุ่นยนต์',
272
+ 'invisible.verifying': 'กำลังตรวจสอบ...',
273
+ 'invisible.success': 'ตรวจสอบแล้ว',
274
+ 'invisible.fail': 'การตรวจสอบล้มเหลว',
275
+ 'slider.hint': 'ลากแถบเลื่อนไปยังตำแหน่งเป้าหมาย',
276
+ 'spatial.title': 'การใช้เหตุผลเชิงพื้นที่',
277
+ 'rotate.hint': 'หมุนภาพไปยังมุมที่ถูกต้อง',
278
+ 'icons.confirm': 'ยืนยัน',
279
+ 'icons.reset': 'รีเซ็ต',
280
+ 'dragsort.hint': 'ลากรายการเรียงตามลำดับที่ถูกต้อง',
281
+ 'dragsort.confirm': 'ยืนยัน',
282
+ 'dragsort.reset': 'รีเซ็ต',
283
+ 'grid.confirm': 'ยืนยัน',
284
+ 'grid.reset': 'รีเซ็ต',
285
+ 'loading': 'กำลังตรวจสอบ...',
286
+ 'success': 'ผ่านการตรวจสอบ!',
287
+ 'fail': 'การตรวจสอบล้มเหลว กรุณาลองใหม่',
288
+ 'close': 'ปิด',
289
+ 'poweredBy': 'Powered by EngageLab',
290
+ 'challenge.imageAlt': 'ภาพ CAPTCHA',
291
+ },
292
+ 'vi-VN': {
293
+ 'invisible.button': 'Tôi không phải là robot',
294
+ 'invisible.verifying': 'Đang xác minh...',
295
+ 'invisible.success': 'Đã xác minh',
296
+ 'invisible.fail': 'Xác minh thất bại',
297
+ 'slider.hint': 'Kéo thanh trượt đến vị trí đích',
298
+ 'spatial.title': 'Suy luận không gian',
299
+ 'rotate.hint': 'Xoay hình ảnh đến góc chính xác',
300
+ 'icons.confirm': 'Xác nhận',
301
+ 'icons.reset': 'Đặt lại',
302
+ 'dragsort.hint': 'Kéo các mục vào đúng thứ tự',
303
+ 'dragsort.confirm': 'Xác nhận',
304
+ 'dragsort.reset': 'Đặt lại',
305
+ 'grid.confirm': 'Xác nhận',
306
+ 'grid.reset': 'Đặt lại',
307
+ 'loading': 'Đang xác minh...',
308
+ 'success': 'Xác minh thành công!',
309
+ 'fail': 'Xác minh thất bại, vui lòng thử lại',
310
+ 'close': 'Đóng',
311
+ 'poweredBy': 'Powered by EngageLab',
312
+ 'challenge.imageAlt': 'Hình ảnh CAPTCHA',
313
+ },
314
+ 'id-ID': {
315
+ 'invisible.button': 'Saya bukan robot',
316
+ 'invisible.verifying': 'Memverifikasi...',
317
+ 'invisible.success': 'Terverifikasi',
318
+ 'invisible.fail': 'Verifikasi gagal',
319
+ 'slider.hint': 'Seret penggeser ke posisi target',
320
+ 'spatial.title': 'Penalaran Spasial',
321
+ 'rotate.hint': 'Putar gambar ke sudut yang benar',
322
+ 'icons.confirm': 'Konfirmasi',
323
+ 'icons.reset': 'Atur ulang',
324
+ 'dragsort.hint': 'Seret item ke urutan yang benar',
325
+ 'dragsort.confirm': 'Konfirmasi',
326
+ 'dragsort.reset': 'Atur ulang',
327
+ 'grid.confirm': 'Konfirmasi',
328
+ 'grid.reset': 'Atur ulang',
329
+ 'loading': 'Memverifikasi...',
330
+ 'success': 'Verifikasi berhasil!',
331
+ 'fail': 'Verifikasi gagal, silakan coba lagi',
332
+ 'close': 'Tutup',
333
+ 'poweredBy': 'Powered by EngageLab',
334
+ 'challenge.imageAlt': 'Gambar CAPTCHA',
335
+ },
336
+ 'tr-TR': {
337
+ 'invisible.button': 'Ben robot değilim',
338
+ 'invisible.verifying': 'Doğrulanıyor...',
339
+ 'invisible.success': 'Doğrulandı',
340
+ 'invisible.fail': 'Doğrulama başarısız',
341
+ 'slider.hint': 'Kaydırıcıyı hedef konuma sürükleyin',
342
+ 'spatial.title': 'Uzamsal Akıl Yürütme',
343
+ 'rotate.hint': 'Resmi doğru açıya döndürün',
344
+ 'icons.confirm': 'Onayla',
345
+ 'icons.reset': 'Sıfırla',
346
+ 'dragsort.hint': 'Öğeleri doğru sıraya sürükleyin',
347
+ 'dragsort.confirm': 'Onayla',
348
+ 'dragsort.reset': 'Sıfırla',
349
+ 'grid.confirm': 'Onayla',
350
+ 'grid.reset': 'Sıfırla',
351
+ 'loading': 'Doğrulanıyor...',
352
+ 'success': 'Doğrulama başarılı!',
353
+ 'fail': 'Doğrulama başarısız, lütfen tekrar deneyin',
354
+ 'close': 'Kapat',
355
+ 'poweredBy': 'Powered by EngageLab',
356
+ 'challenge.imageAlt': 'CAPTCHA görüntüsü',
357
+ },
358
+ 'it-IT': {
359
+ 'invisible.button': 'Non sono un robot',
360
+ 'invisible.verifying': 'Verifica in corso...',
361
+ 'invisible.success': 'Verificato',
362
+ 'invisible.fail': 'Verifica fallita',
363
+ 'slider.hint': "Trascina il cursore nella posizione di destinazione",
364
+ 'spatial.title': 'Ragionamento spaziale',
365
+ 'rotate.hint': "Ruota l'immagine all'angolo corretto",
366
+ 'icons.confirm': 'Conferma',
367
+ 'icons.reset': 'Ripristina',
368
+ 'dragsort.hint': "Trascina gli elementi nell'ordine corretto",
369
+ 'dragsort.confirm': 'Conferma',
370
+ 'dragsort.reset': 'Ripristina',
371
+ 'grid.confirm': 'Conferma',
372
+ 'grid.reset': 'Ripristina',
373
+ 'loading': 'Verifica in corso...',
374
+ 'success': 'Verifica superata!',
375
+ 'fail': 'Verifica fallita, riprova',
376
+ 'close': 'Chiudi',
377
+ 'poweredBy': 'Powered by EngageLab',
378
+ 'challenge.imageAlt': 'Immagine CAPTCHA',
379
+ },
380
+ 'nl-NL': {
381
+ 'invisible.button': 'Ik ben geen robot',
382
+ 'invisible.verifying': 'Verifiëren...',
383
+ 'invisible.success': 'Geverifieerd',
384
+ 'invisible.fail': 'Verificatie mislukt',
385
+ 'slider.hint': 'Sleep de schuifregelaar naar de doelpositie',
386
+ 'spatial.title': 'Ruimtelijk redeneren',
387
+ 'rotate.hint': 'Draai de afbeelding naar de juiste hoek',
388
+ 'icons.confirm': 'Bevestigen',
389
+ 'icons.reset': 'Resetten',
390
+ 'dragsort.hint': 'Sleep items in de juiste volgorde',
391
+ 'dragsort.confirm': 'Bevestigen',
392
+ 'dragsort.reset': 'Resetten',
393
+ 'grid.confirm': 'Bevestigen',
394
+ 'grid.reset': 'Resetten',
395
+ 'loading': 'Verifiëren...',
396
+ 'success': 'Verificatie geslaagd!',
397
+ 'fail': 'Verificatie mislukt, probeer het opnieuw',
398
+ 'close': 'Sluiten',
399
+ 'poweredBy': 'Powered by EngageLab',
400
+ 'challenge.imageAlt': 'CAPTCHA-afbeelding',
401
+ },
402
+ };
403
+
404
+ const LANG_PREFIX_MAP: Record<string, string> = {
405
+ 'zh-Hant': 'zh-TW', 'zh-TW': 'zh-TW', 'zh-HK': 'zh-TW',
406
+ 'zh': 'zh-CN', 'en': 'en-US', 'ja': 'ja-JP', 'ko': 'ko-KR',
407
+ 'de': 'de-DE', 'fr': 'fr-FR', 'es': 'es-ES', 'pt': 'pt-BR',
408
+ 'ru': 'ru-RU', 'ar': 'ar-SA', 'hi': 'hi-IN', 'th': 'th-TH',
409
+ 'vi': 'vi-VN', 'id': 'id-ID', 'tr': 'tr-TR', 'it': 'it-IT',
410
+ 'nl': 'nl-NL',
411
+ };
412
+
413
+ let currentLocale: string = 'zh-CN';
414
+
415
+ export function setLocale(locale: string): void {
416
+ currentLocale = locale;
417
+ }
418
+
419
+ export function getLocale(): string {
420
+ return currentLocale;
421
+ }
422
+
423
+ export function isRtl(): boolean {
424
+ return RTL_LOCALES.has(currentLocale);
425
+ }
426
+
427
+ export function detectLocale(): string {
428
+ const htmlLang = typeof document !== 'undefined'
429
+ ? document.documentElement?.lang
430
+ : '';
431
+ const navLang = typeof navigator !== 'undefined'
432
+ ? navigator.language
433
+ : '';
434
+
435
+ const raw = htmlLang || navLang || 'zh-CN';
436
+
437
+ if (messages[raw]) return raw;
438
+
439
+ const mapped = LANG_PREFIX_MAP[raw];
440
+ if (mapped) return mapped;
441
+
442
+ const prefix = raw.split('-')[0];
443
+ const prefixMapped = LANG_PREFIX_MAP[prefix];
444
+ if (prefixMapped) return prefixMapped;
445
+
446
+ return 'zh-CN';
447
+ }
448
+
449
+ export function registerLocale(locale: string, msgs: Record<string, string>): void {
450
+ messages[locale] = msgs;
451
+ }
452
+
453
+ export function t(key: string): string {
454
+ return messages[currentLocale]?.[key] || messages['en-US']?.[key] || messages['zh-CN']?.[key] || key;
455
+ }