@lokascript/i18n 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 (96) hide show
  1. package/README.md +286 -0
  2. package/dist/browser.cjs +7669 -0
  3. package/dist/browser.cjs.map +1 -0
  4. package/dist/browser.d.cts +50 -0
  5. package/dist/browser.d.ts +50 -0
  6. package/dist/browser.js +7592 -0
  7. package/dist/browser.js.map +1 -0
  8. package/dist/hyperfixi-i18n.min.js +2 -0
  9. package/dist/hyperfixi-i18n.min.js.map +1 -0
  10. package/dist/hyperfixi-i18n.mjs +8558 -0
  11. package/dist/hyperfixi-i18n.mjs.map +1 -0
  12. package/dist/index.cjs +14205 -0
  13. package/dist/index.cjs.map +1 -0
  14. package/dist/index.d.cts +947 -0
  15. package/dist/index.d.ts +947 -0
  16. package/dist/index.js +14095 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/transformer-Ckask-yw.d.cts +1041 -0
  19. package/dist/transformer-Ckask-yw.d.ts +1041 -0
  20. package/package.json +84 -0
  21. package/src/browser.ts +122 -0
  22. package/src/compatibility/browser-tests/grammar-demo.spec.ts +169 -0
  23. package/src/constants.ts +366 -0
  24. package/src/dictionaries/ar.ts +233 -0
  25. package/src/dictionaries/bn.ts +156 -0
  26. package/src/dictionaries/de.ts +233 -0
  27. package/src/dictionaries/derive.ts +515 -0
  28. package/src/dictionaries/en.ts +237 -0
  29. package/src/dictionaries/es.ts +233 -0
  30. package/src/dictionaries/fr.ts +233 -0
  31. package/src/dictionaries/hi.ts +270 -0
  32. package/src/dictionaries/id.ts +233 -0
  33. package/src/dictionaries/index.ts +238 -0
  34. package/src/dictionaries/it.ts +233 -0
  35. package/src/dictionaries/ja.ts +233 -0
  36. package/src/dictionaries/ko.ts +233 -0
  37. package/src/dictionaries/ms.ts +276 -0
  38. package/src/dictionaries/pl.ts +239 -0
  39. package/src/dictionaries/pt.ts +237 -0
  40. package/src/dictionaries/qu.ts +233 -0
  41. package/src/dictionaries/ru.ts +270 -0
  42. package/src/dictionaries/sw.ts +233 -0
  43. package/src/dictionaries/th.ts +156 -0
  44. package/src/dictionaries/tl.ts +276 -0
  45. package/src/dictionaries/tr.ts +233 -0
  46. package/src/dictionaries/uk.ts +270 -0
  47. package/src/dictionaries/vi.ts +210 -0
  48. package/src/dictionaries/zh.ts +233 -0
  49. package/src/enhanced-i18n.test.ts +454 -0
  50. package/src/enhanced-i18n.ts +713 -0
  51. package/src/examples/new-languages.ts +326 -0
  52. package/src/formatting.test.ts +213 -0
  53. package/src/formatting.ts +416 -0
  54. package/src/grammar/direct-mappings.ts +353 -0
  55. package/src/grammar/grammar.test.ts +1053 -0
  56. package/src/grammar/index.ts +59 -0
  57. package/src/grammar/profiles/index.ts +860 -0
  58. package/src/grammar/transformer.ts +1318 -0
  59. package/src/grammar/types.ts +630 -0
  60. package/src/index.ts +202 -0
  61. package/src/new-languages.test.ts +389 -0
  62. package/src/parser/analyze-conflicts.test.ts +229 -0
  63. package/src/parser/ar.ts +40 -0
  64. package/src/parser/create-provider.ts +309 -0
  65. package/src/parser/de.ts +36 -0
  66. package/src/parser/es.ts +31 -0
  67. package/src/parser/fr.ts +31 -0
  68. package/src/parser/id.ts +34 -0
  69. package/src/parser/index.ts +50 -0
  70. package/src/parser/ja.ts +36 -0
  71. package/src/parser/ko.ts +37 -0
  72. package/src/parser/locale-manager.test.ts +198 -0
  73. package/src/parser/locale-manager.ts +197 -0
  74. package/src/parser/parser-integration.test.ts +439 -0
  75. package/src/parser/pt.ts +37 -0
  76. package/src/parser/qu.ts +37 -0
  77. package/src/parser/sw.ts +37 -0
  78. package/src/parser/tr.ts +38 -0
  79. package/src/parser/types.ts +113 -0
  80. package/src/parser/zh.ts +38 -0
  81. package/src/plugins/vite.ts +224 -0
  82. package/src/plugins/webpack.ts +124 -0
  83. package/src/pluralization.test.ts +197 -0
  84. package/src/pluralization.ts +393 -0
  85. package/src/runtime.ts +441 -0
  86. package/src/ssr-integration.ts +225 -0
  87. package/src/test-setup.ts +195 -0
  88. package/src/translation-validation.test.ts +171 -0
  89. package/src/translator.test.ts +252 -0
  90. package/src/translator.ts +297 -0
  91. package/src/types.ts +209 -0
  92. package/src/utils/locale.ts +190 -0
  93. package/src/utils/tokenizer-adapter.ts +469 -0
  94. package/src/utils/tokenizer.ts +19 -0
  95. package/src/validators/index.ts +174 -0
  96. package/src/validators/schema.ts +129 -0
@@ -0,0 +1,34 @@
1
+ // packages/i18n/src/parser/id.ts
2
+
3
+ import { id } from '../dictionaries/id';
4
+ import { createKeywordProvider } from './create-provider';
5
+ import type { KeywordProvider } from './types';
6
+
7
+ /**
8
+ * Indonesian (Bahasa Indonesia) keyword provider for the hyperscript parser.
9
+ *
10
+ * Enables parsing hyperscript written in Indonesian:
11
+ * - `pada klik ganti .active` → parses as `on click toggle .active`
12
+ * - `jika benar lalu log "Halo"` → parses as `if true then log "Hello"`
13
+ *
14
+ * Indonesian is an SVO language with:
15
+ * - Agglutinative morphology (prefixes/suffixes)
16
+ * - No grammatical gender
17
+ * - Prepositions (like English)
18
+ * - Relatively simple syntax
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * import { idKeywords } from '@lokascript/i18n/parser/id';
23
+ * import { Parser } from '@lokascript/core';
24
+ *
25
+ * const parser = new Parser({ keywords: idKeywords });
26
+ * parser.parse('pada klik ganti .active');
27
+ * ```
28
+ */
29
+ export const idKeywords: KeywordProvider = createKeywordProvider(id, 'id', {
30
+ allowEnglishFallback: true,
31
+ });
32
+
33
+ // Re-export for convenience
34
+ export { id as idDictionary } from '../dictionaries/id';
@@ -0,0 +1,50 @@
1
+ // packages/i18n/src/parser/index.ts
2
+
3
+ /**
4
+ * Parser integration for multilingual hyperscript.
5
+ *
6
+ * This module provides KeywordProvider implementations that enable
7
+ * the hyperscript parser to understand non-English keywords.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * // Spanish
12
+ * import { esKeywords } from '@lokascript/i18n/parser';
13
+ * const parser = new Parser({ keywords: esKeywords });
14
+ * parser.parse('en clic alternar .active');
15
+ *
16
+ * // Japanese
17
+ * import { jaKeywords } from '@lokascript/i18n/parser';
18
+ * const parser = new Parser({ keywords: jaKeywords });
19
+ * parser.parse('クリック で 切り替え .active');
20
+ *
21
+ * // Custom locale
22
+ * import { createKeywordProvider } from '@lokascript/i18n/parser';
23
+ * import { fr } from '@lokascript/i18n/dictionaries';
24
+ * const frKeywords = createKeywordProvider(fr, 'fr');
25
+ * ```
26
+ */
27
+
28
+ // Types
29
+ export type { KeywordProvider, KeywordProviderOptions } from './types';
30
+
31
+ // Factory
32
+ export { createKeywordProvider, createEnglishProvider } from './create-provider';
33
+ export { ENGLISH_COMMANDS, ENGLISH_KEYWORDS, UNIVERSAL_ENGLISH_KEYWORDS } from './create-provider';
34
+
35
+ // Locale packs
36
+ export { esKeywords, esDictionary } from './es';
37
+ export { jaKeywords, jaDictionary } from './ja';
38
+ export { frKeywords, frDictionary } from './fr';
39
+ export { deKeywords, deDictionary } from './de';
40
+ export { arKeywords, arDictionary } from './ar';
41
+ export { koKeywords, koDictionary } from './ko';
42
+ export { zhKeywords, zhDictionary } from './zh';
43
+ export { trKeywords, trDictionary } from './tr';
44
+ export { idKeywords, idDictionary } from './id';
45
+ export { quKeywords, quDictionary } from './qu';
46
+ export { swKeywords, swDictionary } from './sw';
47
+ export { ptKeywords, ptDictionary } from './pt';
48
+
49
+ // Locale management
50
+ export { LocaleManager, detectBrowserLocale } from './locale-manager';
@@ -0,0 +1,36 @@
1
+ // packages/i18n/src/parser/ja.ts
2
+
3
+ import { ja } from '../dictionaries/ja';
4
+ import { createKeywordProvider } from './create-provider';
5
+ import type { KeywordProvider } from './types';
6
+
7
+ /**
8
+ * Japanese keyword provider for the hyperscript parser.
9
+ *
10
+ * Enables parsing hyperscript written in Japanese:
11
+ * - `クリック で 切り替え .active` → parses as `on click toggle .active`
12
+ * - `もし 真 それから 記録 "こんにちは"` → parses as `if true then log "hello"`
13
+ *
14
+ * English keywords are also accepted (mixed mode), so:
15
+ * - `click で 切り替え .active` also works (English `click` + Japanese `で`, `切り替え`)
16
+ *
17
+ * Japanese is a useful test case because:
18
+ * - SOV word order (subject-object-verb) vs English SVO
19
+ * - No articles (a, an, the)
20
+ * - Unicode characters (tests tokenizer robustness)
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { jaKeywords } from '@lokascript/i18n/parser/ja';
25
+ * import { Parser } from '@lokascript/core';
26
+ *
27
+ * const parser = new Parser({ keywords: jaKeywords });
28
+ * parser.parse('クリック で 切り替え .active');
29
+ * ```
30
+ */
31
+ export const jaKeywords: KeywordProvider = createKeywordProvider(ja, 'ja', {
32
+ allowEnglishFallback: true,
33
+ });
34
+
35
+ // Re-export for convenience
36
+ export { ja as jaDictionary } from '../dictionaries/ja';
@@ -0,0 +1,37 @@
1
+ // packages/i18n/src/parser/ko.ts
2
+
3
+ import { ko } from '../dictionaries/ko';
4
+ import { createKeywordProvider } from './create-provider';
5
+ import type { KeywordProvider } from './types';
6
+
7
+ /**
8
+ * Korean keyword provider for the hyperscript parser.
9
+ *
10
+ * Enables parsing hyperscript written in Korean:
11
+ * - `에 클릭 토글 .active` → parses as `on click toggle .active`
12
+ * - `만약 참 그러면 로그 "안녕하세요"` → parses as `if true then log "hello"`
13
+ *
14
+ * English keywords are also accepted (mixed mode), so:
15
+ * - `에 click 토글 .active` also works (Korean `에` + English `click`)
16
+ *
17
+ * Korean is a useful test case because:
18
+ * - SOV word order (Subject-Object-Verb)
19
+ * - Hangul script (syllabic blocks)
20
+ * - Agglutinative morphology with particles
21
+ * - Tests parser's Unicode handling with Korean characters
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { koKeywords } from '@lokascript/i18n/parser/ko';
26
+ * import { Parser } from '@lokascript/core';
27
+ *
28
+ * const parser = new Parser({ keywords: koKeywords });
29
+ * parser.parse('에 클릭 토글 .active');
30
+ * ```
31
+ */
32
+ export const koKeywords: KeywordProvider = createKeywordProvider(ko, 'ko', {
33
+ allowEnglishFallback: true,
34
+ });
35
+
36
+ // Re-export for convenience
37
+ export { ko as koDictionary } from '../dictionaries/ko';
@@ -0,0 +1,198 @@
1
+ // packages/i18n/src/parser/locale-manager.test.ts
2
+
3
+ import { describe, it, expect, beforeEach } from 'vitest';
4
+ import { LocaleManager, detectBrowserLocale } from './locale-manager';
5
+ import { esKeywords } from './es';
6
+ import { jaKeywords } from './ja';
7
+ import { frKeywords } from './fr';
8
+ import { deKeywords } from './de';
9
+ import { arKeywords } from './ar';
10
+
11
+ describe('LocaleManager', () => {
12
+ beforeEach(() => {
13
+ LocaleManager.reset();
14
+ });
15
+
16
+ describe('register / unregister', () => {
17
+ it('should register a locale provider', () => {
18
+ LocaleManager.register('es', esKeywords);
19
+ expect(LocaleManager.has('es')).toBe(true);
20
+ });
21
+
22
+ it('should unregister a locale provider', () => {
23
+ LocaleManager.register('es', esKeywords);
24
+ LocaleManager.unregister('es');
25
+ expect(LocaleManager.has('es')).toBe(false);
26
+ });
27
+
28
+ it('should handle case-insensitive locale codes', () => {
29
+ LocaleManager.register('ES', esKeywords);
30
+ expect(LocaleManager.has('es')).toBe(true);
31
+ expect(LocaleManager.has('ES')).toBe(true);
32
+ });
33
+ });
34
+
35
+ describe('setDefault / getDefault', () => {
36
+ it('should have en as default initially', () => {
37
+ expect(LocaleManager.getDefault()).toBe('en');
38
+ });
39
+
40
+ it('should set default locale', () => {
41
+ LocaleManager.register('es', esKeywords);
42
+ LocaleManager.setDefault('es');
43
+ expect(LocaleManager.getDefault()).toBe('es');
44
+ });
45
+
46
+ it('should throw when setting unregistered locale as default', () => {
47
+ expect(() => LocaleManager.setDefault('xyz')).toThrow("Locale 'xyz' is not registered");
48
+ });
49
+
50
+ it('should allow setting en as default without registration', () => {
51
+ LocaleManager.setDefault('en');
52
+ expect(LocaleManager.getDefault()).toBe('en');
53
+ });
54
+ });
55
+
56
+ describe('get', () => {
57
+ it('should get English provider by default', () => {
58
+ const provider = LocaleManager.get();
59
+ expect(provider.locale).toBe('en');
60
+ });
61
+
62
+ it('should get registered locale provider', () => {
63
+ LocaleManager.register('es', esKeywords);
64
+ const provider = LocaleManager.get('es');
65
+ expect(provider.locale).toBe('es');
66
+ });
67
+
68
+ it('should get default locale when no argument provided', () => {
69
+ LocaleManager.register('ja', jaKeywords);
70
+ LocaleManager.setDefault('ja');
71
+ const provider = LocaleManager.get();
72
+ expect(provider.locale).toBe('ja');
73
+ });
74
+
75
+ it('should throw for unregistered locale', () => {
76
+ expect(() => LocaleManager.get('xyz')).toThrow("Locale 'xyz' is not registered");
77
+ });
78
+ });
79
+
80
+ describe('has', () => {
81
+ it('should always have en', () => {
82
+ expect(LocaleManager.has('en')).toBe(true);
83
+ });
84
+
85
+ it('should return false for unregistered locale', () => {
86
+ expect(LocaleManager.has('xyz')).toBe(false);
87
+ });
88
+
89
+ it('should return true for registered locale', () => {
90
+ LocaleManager.register('fr', frKeywords);
91
+ expect(LocaleManager.has('fr')).toBe(true);
92
+ });
93
+ });
94
+
95
+ describe('getAvailable', () => {
96
+ it('should include en by default', () => {
97
+ expect(LocaleManager.getAvailable()).toContain('en');
98
+ });
99
+
100
+ it('should include registered locales', () => {
101
+ LocaleManager.register('es', esKeywords);
102
+ LocaleManager.register('ja', jaKeywords);
103
+ const available = LocaleManager.getAvailable();
104
+ expect(available).toContain('en');
105
+ expect(available).toContain('es');
106
+ expect(available).toContain('ja');
107
+ });
108
+ });
109
+
110
+ describe('registerAll', () => {
111
+ it('should register multiple locales at once', () => {
112
+ LocaleManager.registerAll({
113
+ es: esKeywords,
114
+ ja: jaKeywords,
115
+ fr: frKeywords,
116
+ de: deKeywords,
117
+ ar: arKeywords,
118
+ });
119
+
120
+ expect(LocaleManager.has('es')).toBe(true);
121
+ expect(LocaleManager.has('ja')).toBe(true);
122
+ expect(LocaleManager.has('fr')).toBe(true);
123
+ expect(LocaleManager.has('de')).toBe(true);
124
+ expect(LocaleManager.has('ar')).toBe(true);
125
+ });
126
+ });
127
+
128
+ describe('reset', () => {
129
+ it('should clear all registered locales and reset default', () => {
130
+ LocaleManager.register('es', esKeywords);
131
+ LocaleManager.setDefault('es');
132
+
133
+ LocaleManager.reset();
134
+
135
+ expect(LocaleManager.has('es')).toBe(false);
136
+ expect(LocaleManager.getDefault()).toBe('en');
137
+ });
138
+ });
139
+
140
+ describe('Integration: keyword resolution', () => {
141
+ beforeEach(() => {
142
+ LocaleManager.registerAll({
143
+ es: esKeywords,
144
+ ja: jaKeywords,
145
+ fr: frKeywords,
146
+ });
147
+ });
148
+
149
+ it('should resolve keywords for different locales', () => {
150
+ // Spanish
151
+ const es = LocaleManager.get('es');
152
+ expect(es.resolve('alternar')).toBe('toggle');
153
+
154
+ // Japanese
155
+ const ja = LocaleManager.get('ja');
156
+ expect(ja.resolve('切り替え')).toBe('toggle');
157
+
158
+ // French
159
+ const fr = LocaleManager.get('fr');
160
+ expect(fr.resolve('basculer')).toBe('toggle');
161
+
162
+ // English
163
+ const en = LocaleManager.get('en');
164
+ expect(en.resolve('toggle')).toBe('toggle');
165
+ });
166
+
167
+ it('should support switching default locale dynamically', () => {
168
+ // Start with English
169
+ expect(LocaleManager.get().resolve('toggle')).toBe('toggle');
170
+
171
+ // Switch to Spanish
172
+ LocaleManager.setDefault('es');
173
+ expect(LocaleManager.get().resolve('alternar')).toBe('toggle');
174
+
175
+ // Switch to Japanese
176
+ LocaleManager.setDefault('ja');
177
+ expect(LocaleManager.get().resolve('切り替え')).toBe('toggle');
178
+ });
179
+ });
180
+ });
181
+
182
+ describe('detectBrowserLocale', () => {
183
+ beforeEach(() => {
184
+ LocaleManager.reset();
185
+ });
186
+
187
+ it('should return English provider when navigator is undefined', () => {
188
+ // In Node.js environment, navigator is undefined
189
+ const provider = detectBrowserLocale();
190
+ expect(provider.locale).toBe('en');
191
+ });
192
+
193
+ it('should return English when no matching locale is registered', () => {
194
+ // No locales registered, should fall back to English
195
+ const provider = detectBrowserLocale();
196
+ expect(provider.locale).toBe('en');
197
+ });
198
+ });
@@ -0,0 +1,197 @@
1
+ // packages/i18n/src/parser/locale-manager.ts
2
+
3
+ import type { KeywordProvider } from './types';
4
+ import { createEnglishProvider } from './create-provider';
5
+
6
+ /**
7
+ * LocaleManager - Centralized locale configuration for hyperscript parsing.
8
+ *
9
+ * Provides utilities for:
10
+ * - Setting/getting the default locale
11
+ * - Creating locale-aware parsers
12
+ * - Runtime locale switching
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { LocaleManager } from '@lokascript/i18n/parser';
17
+ * import { esKeywords, frKeywords } from '@lokascript/i18n/parser';
18
+ *
19
+ * // Register available locales
20
+ * LocaleManager.register('es', esKeywords);
21
+ * LocaleManager.register('fr', frKeywords);
22
+ *
23
+ * // Set default locale
24
+ * LocaleManager.setDefault('es');
25
+ *
26
+ * // Get keyword provider for parsing
27
+ * const provider = LocaleManager.get(); // Returns esKeywords
28
+ * const frProvider = LocaleManager.get('fr'); // Returns frKeywords
29
+ *
30
+ * // Use with parser
31
+ * const parser = new Parser({ keywords: LocaleManager.get() });
32
+ * ```
33
+ */
34
+ export class LocaleManager {
35
+ private static providers = new Map<string, KeywordProvider>();
36
+ private static defaultLocale: string = 'en';
37
+ private static englishProvider: KeywordProvider = createEnglishProvider();
38
+
39
+ /**
40
+ * Register a locale provider.
41
+ *
42
+ * @param locale - The locale code (e.g., 'es', 'ja', 'fr')
43
+ * @param provider - The KeywordProvider for this locale
44
+ */
45
+ static register(locale: string, provider: KeywordProvider): void {
46
+ LocaleManager.providers.set(locale.toLowerCase(), provider);
47
+ }
48
+
49
+ /**
50
+ * Unregister a locale provider.
51
+ *
52
+ * @param locale - The locale code to remove
53
+ */
54
+ static unregister(locale: string): void {
55
+ LocaleManager.providers.delete(locale.toLowerCase());
56
+ }
57
+
58
+ /**
59
+ * Set the default locale.
60
+ *
61
+ * @param locale - The locale code to use as default
62
+ * @throws Error if the locale is not registered
63
+ */
64
+ static setDefault(locale: string): void {
65
+ const normalizedLocale = locale.toLowerCase();
66
+ if (normalizedLocale !== 'en' && !LocaleManager.providers.has(normalizedLocale)) {
67
+ throw new Error(
68
+ `Locale '${locale}' is not registered. ` +
69
+ `Available locales: ${LocaleManager.getAvailable().join(', ')}`
70
+ );
71
+ }
72
+ LocaleManager.defaultLocale = normalizedLocale;
73
+ }
74
+
75
+ /**
76
+ * Get the current default locale.
77
+ *
78
+ * @returns The default locale code
79
+ */
80
+ static getDefault(): string {
81
+ return LocaleManager.defaultLocale;
82
+ }
83
+
84
+ /**
85
+ * Get a keyword provider for a locale.
86
+ *
87
+ * @param locale - The locale code (optional, defaults to the default locale)
88
+ * @returns The KeywordProvider for the locale
89
+ * @throws Error if the locale is not registered
90
+ */
91
+ static get(locale?: string): KeywordProvider {
92
+ const targetLocale = (locale || LocaleManager.defaultLocale).toLowerCase();
93
+
94
+ // English is always available
95
+ if (targetLocale === 'en') {
96
+ return LocaleManager.englishProvider;
97
+ }
98
+
99
+ const provider = LocaleManager.providers.get(targetLocale);
100
+ if (!provider) {
101
+ throw new Error(
102
+ `Locale '${targetLocale}' is not registered. ` +
103
+ `Available locales: ${LocaleManager.getAvailable().join(', ')}`
104
+ );
105
+ }
106
+
107
+ return provider;
108
+ }
109
+
110
+ /**
111
+ * Check if a locale is registered.
112
+ *
113
+ * @param locale - The locale code to check
114
+ * @returns True if the locale is available
115
+ */
116
+ static has(locale: string): boolean {
117
+ const normalizedLocale = locale.toLowerCase();
118
+ return normalizedLocale === 'en' || LocaleManager.providers.has(normalizedLocale);
119
+ }
120
+
121
+ /**
122
+ * Get all available locale codes.
123
+ *
124
+ * @returns Array of locale codes
125
+ */
126
+ static getAvailable(): string[] {
127
+ return ['en', ...Array.from(LocaleManager.providers.keys())];
128
+ }
129
+
130
+ /**
131
+ * Reset the LocaleManager to its initial state.
132
+ * Useful for testing.
133
+ */
134
+ static reset(): void {
135
+ LocaleManager.providers.clear();
136
+ LocaleManager.defaultLocale = 'en';
137
+ }
138
+
139
+ /**
140
+ * Register all built-in locales at once.
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * import { LocaleManager } from '@lokascript/i18n/parser';
145
+ * import { esKeywords, jaKeywords, frKeywords, deKeywords, arKeywords } from '@lokascript/i18n/parser';
146
+ *
147
+ * LocaleManager.registerAll({
148
+ * es: esKeywords,
149
+ * ja: jaKeywords,
150
+ * fr: frKeywords,
151
+ * de: deKeywords,
152
+ * ar: arKeywords,
153
+ * });
154
+ * ```
155
+ */
156
+ static registerAll(providers: Record<string, KeywordProvider>): void {
157
+ for (const [locale, provider] of Object.entries(providers)) {
158
+ LocaleManager.register(locale, provider);
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Quick utility to detect the browser's preferred locale and return
165
+ * a matching provider, falling back to English.
166
+ *
167
+ * @returns The KeywordProvider for the browser's locale or English
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * import { detectBrowserLocale } from '@lokascript/i18n/parser';
172
+ * import { Parser } from '@lokascript/core';
173
+ *
174
+ * // Auto-detect and use browser locale
175
+ * const parser = new Parser({ keywords: detectBrowserLocale() });
176
+ * ```
177
+ */
178
+ export function detectBrowserLocale(): KeywordProvider {
179
+ if (typeof navigator === 'undefined') {
180
+ return LocaleManager.get('en');
181
+ }
182
+
183
+ // Get browser languages in order of preference
184
+ const languages = navigator.languages || [navigator.language];
185
+
186
+ for (const lang of languages) {
187
+ // Extract the base language code (e.g., 'es-MX' -> 'es')
188
+ const baseLocale = lang.split('-')[0].toLowerCase();
189
+
190
+ if (LocaleManager.has(baseLocale)) {
191
+ return LocaleManager.get(baseLocale);
192
+ }
193
+ }
194
+
195
+ // Fallback to English
196
+ return LocaleManager.get('en');
197
+ }