@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
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@lokascript/i18n",
3
+ "version": "1.0.0",
4
+ "description": "Grammar transformation and keyword translation for hyperscript",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ },
15
+ "./browser": {
16
+ "types": "./dist/browser.d.ts",
17
+ "import": "./dist/browser.mjs",
18
+ "require": "./dist/browser.js"
19
+ },
20
+ "./dictionaries": {
21
+ "types": "./dist/dictionaries/index.d.ts",
22
+ "import": "./dist/dictionaries/index.mjs",
23
+ "require": "./dist/dictionaries/index.js"
24
+ },
25
+ "./plugins/vite": {
26
+ "types": "./dist/plugins/vite.d.ts",
27
+ "import": "./dist/plugins/vite.mjs",
28
+ "require": "./dist/plugins/vite.js"
29
+ },
30
+ "./plugins/webpack": {
31
+ "types": "./dist/plugins/webpack.d.ts",
32
+ "import": "./dist/plugins/webpack.mjs",
33
+ "require": "./dist/plugins/webpack.js"
34
+ }
35
+ },
36
+ "scripts": {
37
+ "build": "tsup && npm run build:browser",
38
+ "build:browser": "rollup -c rollup.config.mjs",
39
+ "dev": "tsup --watch",
40
+ "test": "vitest",
41
+ "test:coverage": "vitest run --coverage",
42
+ "typecheck": "tsc --noEmit",
43
+ "lint": "eslint src --ext ts,tsx",
44
+ "validate-dictionaries": "node scripts/validate-dictionaries.js"
45
+ },
46
+ "browser": "dist/hyperfixi-i18n.min.js",
47
+ "sideEffects": false,
48
+ "keywords": [
49
+ "hyperscript",
50
+ "i18n",
51
+ "internationalization",
52
+ "translation",
53
+ "localization"
54
+ ],
55
+ "author": "Hyperfixi Team",
56
+ "license": "MIT",
57
+ "dependencies": {
58
+ "@lokascript/ast-toolkit": "*",
59
+ "@lokascript/core": "*",
60
+ "@lokascript/semantic": "*",
61
+ "esbuild": "0.25.11",
62
+ "happy-dom": "20.0.6",
63
+ "vite": "^7.1.12"
64
+ },
65
+ "devDependencies": {
66
+ "@rollup/plugin-node-resolve": "^16.0.1",
67
+ "@rollup/plugin-terser": "^0.4.4",
68
+ "@rollup/plugin-typescript": "^12.1.2",
69
+ "@types/node": "^20.0.0",
70
+ "@vitest/coverage-v8": "^4.0.0",
71
+ "eslint": "^8.0.0",
72
+ "rollup": "^4.41.1",
73
+ "tsup": "^8.0.0",
74
+ "typescript": "^5.0.0",
75
+ "vitest": "^4.0.0"
76
+ },
77
+ "files": [
78
+ "dist",
79
+ "src"
80
+ ],
81
+ "publishConfig": {
82
+ "access": "public"
83
+ }
84
+ }
package/src/browser.ts ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Browser entry point for @lokascript/i18n
3
+ *
4
+ * This module exports only what's needed for browser usage:
5
+ * - Keyword providers for each locale
6
+ * - LocaleManager for runtime locale switching
7
+ * - Factory functions for custom providers
8
+ *
9
+ * Usage with UMD bundle:
10
+ * ```html
11
+ * <script src="hyperfixi-core.min.js"></script>
12
+ * <script src="hyperfixi-i18n.min.js"></script>
13
+ * <script>
14
+ * // Configure HyperFixi with Spanish keywords
15
+ * HyperFixi.configure({ keywords: HyperFixiI18n.esKeywords });
16
+ * </script>
17
+ * ```
18
+ *
19
+ * Usage with ES modules:
20
+ * ```typescript
21
+ * import { esKeywords } from '@lokascript/i18n';
22
+ * import { Parser } from '@lokascript/core';
23
+ *
24
+ * const parser = new Parser(tokens, { keywords: esKeywords });
25
+ * ```
26
+ */
27
+
28
+ // Types
29
+ export type { KeywordProvider, KeywordProviderOptions } from './parser/types';
30
+
31
+ // Factory functions
32
+ export { createKeywordProvider, createEnglishProvider } from './parser/create-provider';
33
+ export {
34
+ ENGLISH_COMMANDS,
35
+ ENGLISH_KEYWORDS,
36
+ UNIVERSAL_ENGLISH_KEYWORDS,
37
+ } from './parser/create-provider';
38
+
39
+ // Locale keyword providers
40
+ export { esKeywords, esDictionary } from './parser/es';
41
+ export { jaKeywords, jaDictionary } from './parser/ja';
42
+ export { frKeywords, frDictionary } from './parser/fr';
43
+ export { deKeywords, deDictionary } from './parser/de';
44
+ export { arKeywords, arDictionary } from './parser/ar';
45
+ export { koKeywords, koDictionary } from './parser/ko';
46
+ export { zhKeywords, zhDictionary } from './parser/zh';
47
+ export { trKeywords, trDictionary } from './parser/tr';
48
+ export { idKeywords, idDictionary } from './parser/id';
49
+ export { quKeywords, quDictionary } from './parser/qu';
50
+ export { swKeywords, swDictionary } from './parser/sw';
51
+ export { ptKeywords, ptDictionary } from './parser/pt';
52
+
53
+ // Locale management
54
+ export { LocaleManager, detectBrowserLocale } from './parser/locale-manager';
55
+
56
+ // Re-export dictionaries for custom provider creation
57
+ export { es } from './dictionaries/es';
58
+ export { ja } from './dictionaries/ja';
59
+ export { fr } from './dictionaries/fr';
60
+ export { de } from './dictionaries/de';
61
+ export { ar } from './dictionaries/ar';
62
+ export { ko } from './dictionaries/ko';
63
+ export { zh } from './dictionaries/zh';
64
+ export { tr } from './dictionaries/tr';
65
+ export { id } from './dictionaries/id';
66
+ export { qu } from './dictionaries/qu';
67
+ export { sw } from './dictionaries/sw';
68
+ export { pt } from './dictionaries/pt';
69
+
70
+ // Grammar-aware transformation system (Phase 2)
71
+ export {
72
+ // Types (re-exported for browser usage)
73
+ type SemanticRole,
74
+ type WordOrder,
75
+ type AdpositionType,
76
+ type MorphologyType,
77
+ type GrammaticalMarker,
78
+ type LanguageProfile,
79
+ type GrammarRule,
80
+ type PatternMatcher,
81
+ type PatternTransform,
82
+ type ParsedStatement,
83
+ type ParsedElement,
84
+ // Universal patterns and language family defaults
85
+ UNIVERSAL_PATTERNS,
86
+ LANGUAGE_FAMILY_DEFAULTS,
87
+ // Transformation utilities
88
+ reorderRoles,
89
+ insertMarkers,
90
+ joinTokens,
91
+ transformStatement,
92
+ // Profiles
93
+ profiles,
94
+ getProfile,
95
+ getSupportedLocales,
96
+ englishProfile,
97
+ japaneseProfile,
98
+ koreanProfile,
99
+ chineseProfile,
100
+ arabicProfile,
101
+ turkishProfile,
102
+ spanishProfile,
103
+ germanProfile,
104
+ frenchProfile,
105
+ portugueseProfile,
106
+ indonesianProfile,
107
+ quechuaProfile,
108
+ swahiliProfile,
109
+ // Direct language-pair translation
110
+ directMappings,
111
+ hasDirectMapping,
112
+ getDirectMapping,
113
+ translateWordDirect,
114
+ getSupportedDirectPairs,
115
+ // Transformer
116
+ GrammarTransformer,
117
+ parseStatement,
118
+ toLocale,
119
+ toEnglish,
120
+ translate,
121
+ examples as grammarExamples,
122
+ } from './grammar';
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Browser test for Grammar Transformation Demo
3
+ *
4
+ * Verifies that the GrammarTransformer class works correctly in the browser
5
+ * and the multilingual demo page properly demonstrates live transformations.
6
+ */
7
+ import { test, expect } from '@playwright/test';
8
+
9
+ test.describe('Grammar Transformation Demo', () => {
10
+ test.beforeEach(async ({ page }) => {
11
+ await page.goto('http://127.0.0.1:3000/examples/multilingual/index.html');
12
+ // Wait for i18n bundle to load
13
+ await page.waitForFunction(() => window.LokaScriptI18n !== undefined, { timeout: 5000 });
14
+ });
15
+
16
+ test('should load LokaScriptI18n bundle with GrammarTransformer', async ({ page }) => {
17
+ const hasI18n = await page.evaluate(() => !!window.LokaScriptI18n);
18
+ expect(hasI18n).toBe(true);
19
+
20
+ const hasTransformer = await page.evaluate(() => !!window.LokaScriptI18n.GrammarTransformer);
21
+ expect(hasTransformer).toBe(true);
22
+
23
+ const hasTranslate = await page.evaluate(
24
+ () => typeof window.LokaScriptI18n.translate === 'function'
25
+ );
26
+ expect(hasTranslate).toBe(true);
27
+ });
28
+
29
+ test('should have profile functions available', async ({ page }) => {
30
+ const locales = await page.evaluate(() => window.LokaScriptI18n.getSupportedLocales?.() || []);
31
+ expect(locales).toContain('en');
32
+ expect(locales).toContain('ja');
33
+ expect(locales).toContain('ar');
34
+ expect(locales).toContain('zh');
35
+ });
36
+
37
+ test('should transform English to Japanese (SOV)', async ({ page }) => {
38
+ const result = await page.evaluate(() => {
39
+ const { translate } = window.LokaScriptI18n;
40
+ return translate('on click increment #count', 'en', 'ja');
41
+ });
42
+
43
+ // Japanese SOV should have #count before the action
44
+ expect(result).toContain('#count');
45
+ expect(result).toContain('を'); // Object particle
46
+ // Patient should come before action in SOV
47
+ const countIndex = result.indexOf('#count');
48
+ const actionIndex = result.indexOf('増加');
49
+ expect(countIndex).toBeLessThan(actionIndex);
50
+ });
51
+
52
+ test('should transform English to Arabic (VSO)', async ({ page }) => {
53
+ const result = await page.evaluate(() => {
54
+ const { translate } = window.LokaScriptI18n;
55
+ return translate('on click increment #count', 'en', 'ar');
56
+ });
57
+
58
+ // Arabic VSO should have action first
59
+ expect(result).toContain('#count');
60
+ expect(result).toContain('زِد'); // Action should be present
61
+ // Action should come first in VSO
62
+ const actionIndex = result.indexOf('زِد');
63
+ const countIndex = result.indexOf('#count');
64
+ expect(actionIndex).toBeLessThan(countIndex);
65
+ });
66
+
67
+ test('should transform English to Chinese with circumfix', async ({ page }) => {
68
+ const result = await page.evaluate(() => {
69
+ const { translate } = window.LokaScriptI18n;
70
+ return translate('on click increment #count', 'en', 'zh');
71
+ });
72
+
73
+ // Chinese should have 当...时 circumfix pattern
74
+ expect(result).toContain('当');
75
+ expect(result).toContain('时');
76
+ expect(result).toContain('#count');
77
+ });
78
+
79
+ test('should preserve CSS selectors during transformation', async ({ page }) => {
80
+ const selectors = ['.active', '#count', '.menu-item[data-active="true"]'];
81
+
82
+ for (const selector of selectors) {
83
+ const result = await page.evaluate(
84
+ ({ sel }) => {
85
+ const { translate } = window.LokaScriptI18n;
86
+ return translate(`on click toggle ${sel}`, 'en', 'ja');
87
+ },
88
+ { sel: selector }
89
+ );
90
+
91
+ expect(result).toContain(selector);
92
+ }
93
+ });
94
+
95
+ test('should have grammar demo section with live input', async ({ page }) => {
96
+ // Check for grammar demo input
97
+ const input = await page.locator('#grammar-input');
98
+ await expect(input).toBeVisible();
99
+
100
+ // Check for results container
101
+ const results = await page.locator('#grammar-results');
102
+ await expect(results).toBeVisible();
103
+ });
104
+
105
+ test('should update transformations when input changes', async ({ page }) => {
106
+ const input = await page.locator('#grammar-input');
107
+ const results = await page.locator('#grammar-results');
108
+
109
+ // Clear and enter new code
110
+ await input.fill('on click toggle .active');
111
+
112
+ // Wait for debounce and transformation
113
+ await page.waitForTimeout(200);
114
+
115
+ // Results should contain transformations
116
+ const resultsText = await results.textContent();
117
+ expect(resultsText).toContain('.active');
118
+ });
119
+
120
+ test('should show all 10 language transformations', async ({ page }) => {
121
+ const results = await page.locator('#grammar-results');
122
+
123
+ // Wait for initial render
124
+ await page.waitForTimeout(200);
125
+
126
+ // Count language cards (each has a flag emoji)
127
+ const langCards = await results.locator('div[style*="border-radius: 8px"]').count();
128
+ expect(langCards).toBeGreaterThanOrEqual(10);
129
+ });
130
+
131
+ test('should handle example buttons', async ({ page }) => {
132
+ const input = await page.locator('#grammar-input');
133
+
134
+ // Click toggle example button
135
+ await page.click('button:has-text("toggle")');
136
+
137
+ // Input should be updated
138
+ const value = await input.inputValue();
139
+ expect(value).toBe('on click toggle .active');
140
+ });
141
+
142
+ test('should have utility functions exported', async ({ page }) => {
143
+ const utilities = await page.evaluate(() => ({
144
+ hasJoinTokens: typeof window.LokaScriptI18n.joinTokens === 'function',
145
+ hasReorderRoles: typeof window.LokaScriptI18n.reorderRoles === 'function',
146
+ hasInsertMarkers: typeof window.LokaScriptI18n.insertMarkers === 'function',
147
+ hasTransformStatement: typeof window.LokaScriptI18n.transformStatement === 'function',
148
+ hasUniversalPatterns: !!window.LokaScriptI18n.UNIVERSAL_PATTERNS,
149
+ hasLanguageFamilyDefaults: !!window.LokaScriptI18n.LANGUAGE_FAMILY_DEFAULTS,
150
+ }));
151
+
152
+ expect(utilities.hasJoinTokens).toBe(true);
153
+ expect(utilities.hasReorderRoles).toBe(true);
154
+ expect(utilities.hasInsertMarkers).toBe(true);
155
+ expect(utilities.hasTransformStatement).toBe(true);
156
+ expect(utilities.hasUniversalPatterns).toBe(true);
157
+ expect(utilities.hasLanguageFamilyDefaults).toBe(true);
158
+ });
159
+
160
+ test('should handle joinTokens for agglutinative suffixes', async ({ page }) => {
161
+ const result = await page.evaluate(() => {
162
+ const { joinTokens } = window.LokaScriptI18n;
163
+ return joinTokens(['#count', '-ta', 'increment']); // Quechua-style suffix
164
+ });
165
+
166
+ // Suffix should attach without space
167
+ expect(result).toBe('#countta increment');
168
+ });
169
+ });