i18nsmith 0.1.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 (213) hide show
  1. package/dist/commands/audit.d.ts +3 -0
  2. package/dist/commands/audit.d.ts.map +1 -0
  3. package/dist/commands/audit.js +180 -0
  4. package/dist/commands/audit.js.map +1 -0
  5. package/dist/commands/backup.d.ts +6 -0
  6. package/dist/commands/backup.d.ts.map +1 -0
  7. package/dist/commands/backup.js +85 -0
  8. package/dist/commands/backup.js.map +1 -0
  9. package/dist/commands/check.d.ts +3 -0
  10. package/dist/commands/check.d.ts.map +1 -0
  11. package/dist/commands/check.js +151 -0
  12. package/dist/commands/check.js.map +1 -0
  13. package/dist/commands/config.d.ts +3 -0
  14. package/dist/commands/config.d.ts.map +1 -0
  15. package/dist/commands/config.js +235 -0
  16. package/dist/commands/config.js.map +1 -0
  17. package/dist/commands/debug-patterns.d.ts +3 -0
  18. package/dist/commands/debug-patterns.d.ts.map +1 -0
  19. package/dist/commands/debug-patterns.js +192 -0
  20. package/dist/commands/debug-patterns.js.map +1 -0
  21. package/dist/commands/debug-patterns.test.d.ts +2 -0
  22. package/dist/commands/debug-patterns.test.d.ts.map +1 -0
  23. package/dist/commands/debug-patterns.test.js +109 -0
  24. package/dist/commands/debug-patterns.test.js.map +1 -0
  25. package/dist/commands/diagnose.d.ts +3 -0
  26. package/dist/commands/diagnose.d.ts.map +1 -0
  27. package/dist/commands/diagnose.js +117 -0
  28. package/dist/commands/diagnose.js.map +1 -0
  29. package/dist/commands/init.d.ts +8 -0
  30. package/dist/commands/init.d.ts.map +1 -0
  31. package/dist/commands/init.js +450 -0
  32. package/dist/commands/init.js.map +1 -0
  33. package/dist/commands/init.test.d.ts +2 -0
  34. package/dist/commands/init.test.d.ts.map +1 -0
  35. package/dist/commands/init.test.js +74 -0
  36. package/dist/commands/init.test.js.map +1 -0
  37. package/dist/commands/install-hooks.d.ts +3 -0
  38. package/dist/commands/install-hooks.d.ts.map +1 -0
  39. package/dist/commands/install-hooks.js +52 -0
  40. package/dist/commands/install-hooks.js.map +1 -0
  41. package/dist/commands/preflight.d.ts +7 -0
  42. package/dist/commands/preflight.d.ts.map +1 -0
  43. package/dist/commands/preflight.js +417 -0
  44. package/dist/commands/preflight.js.map +1 -0
  45. package/dist/commands/preflight.test.d.ts +5 -0
  46. package/dist/commands/preflight.test.d.ts.map +1 -0
  47. package/dist/commands/preflight.test.js +108 -0
  48. package/dist/commands/preflight.test.js.map +1 -0
  49. package/dist/commands/rename.d.ts +6 -0
  50. package/dist/commands/rename.d.ts.map +1 -0
  51. package/dist/commands/rename.js +204 -0
  52. package/dist/commands/rename.js.map +1 -0
  53. package/dist/commands/scaffold-adapter.d.ts +3 -0
  54. package/dist/commands/scaffold-adapter.d.ts.map +1 -0
  55. package/dist/commands/scaffold-adapter.js +204 -0
  56. package/dist/commands/scaffold-adapter.js.map +1 -0
  57. package/dist/commands/scaffold-adapter.test.d.ts +2 -0
  58. package/dist/commands/scaffold-adapter.test.d.ts.map +1 -0
  59. package/dist/commands/scaffold-adapter.test.js +102 -0
  60. package/dist/commands/scaffold-adapter.test.js.map +1 -0
  61. package/dist/commands/scan.d.ts +3 -0
  62. package/dist/commands/scan.d.ts.map +1 -0
  63. package/dist/commands/scan.js +93 -0
  64. package/dist/commands/scan.js.map +1 -0
  65. package/dist/commands/sync-seed.test.d.ts +2 -0
  66. package/dist/commands/sync-seed.test.d.ts.map +1 -0
  67. package/dist/commands/sync-seed.test.js +86 -0
  68. package/dist/commands/sync-seed.test.js.map +1 -0
  69. package/dist/commands/sync.d.ts +3 -0
  70. package/dist/commands/sync.d.ts.map +1 -0
  71. package/dist/commands/sync.js +590 -0
  72. package/dist/commands/sync.js.map +1 -0
  73. package/dist/commands/transform.d.ts +3 -0
  74. package/dist/commands/transform.d.ts.map +1 -0
  75. package/dist/commands/transform.js +114 -0
  76. package/dist/commands/transform.js.map +1 -0
  77. package/dist/commands/translate/csv-handler.d.ts +21 -0
  78. package/dist/commands/translate/csv-handler.d.ts.map +1 -0
  79. package/dist/commands/translate/csv-handler.js +270 -0
  80. package/dist/commands/translate/csv-handler.js.map +1 -0
  81. package/dist/commands/translate/executor.d.ts +31 -0
  82. package/dist/commands/translate/executor.d.ts.map +1 -0
  83. package/dist/commands/translate/executor.js +117 -0
  84. package/dist/commands/translate/executor.js.map +1 -0
  85. package/dist/commands/translate/index.d.ts +10 -0
  86. package/dist/commands/translate/index.d.ts.map +1 -0
  87. package/dist/commands/translate/index.js +170 -0
  88. package/dist/commands/translate/index.js.map +1 -0
  89. package/dist/commands/translate/reporter.d.ts +29 -0
  90. package/dist/commands/translate/reporter.d.ts.map +1 -0
  91. package/dist/commands/translate/reporter.js +103 -0
  92. package/dist/commands/translate/reporter.js.map +1 -0
  93. package/dist/commands/translate/types.d.ts +50 -0
  94. package/dist/commands/translate/types.d.ts.map +1 -0
  95. package/dist/commands/translate/types.js +5 -0
  96. package/dist/commands/translate/types.js.map +1 -0
  97. package/dist/commands/translate.d.ts +7 -0
  98. package/dist/commands/translate.d.ts.map +1 -0
  99. package/dist/commands/translate.js +7 -0
  100. package/dist/commands/translate.js.map +1 -0
  101. package/dist/commands/translate.test.d.ts +2 -0
  102. package/dist/commands/translate.test.d.ts.map +1 -0
  103. package/dist/commands/translate.test.js +118 -0
  104. package/dist/commands/translate.test.js.map +1 -0
  105. package/dist/e2e.test.d.ts +6 -0
  106. package/dist/e2e.test.d.ts.map +1 -0
  107. package/dist/e2e.test.js +376 -0
  108. package/dist/e2e.test.js.map +1 -0
  109. package/dist/index.d.ts +4 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +39 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/integration.test.d.ts +6 -0
  114. package/dist/integration.test.d.ts.map +1 -0
  115. package/dist/integration.test.js +320 -0
  116. package/dist/integration.test.js.map +1 -0
  117. package/dist/utils/diagnostics-exit.d.ts +12 -0
  118. package/dist/utils/diagnostics-exit.d.ts.map +1 -0
  119. package/dist/utils/diagnostics-exit.js +49 -0
  120. package/dist/utils/diagnostics-exit.js.map +1 -0
  121. package/dist/utils/diagnostics-exit.test.d.ts +2 -0
  122. package/dist/utils/diagnostics-exit.test.d.ts.map +1 -0
  123. package/dist/utils/diagnostics-exit.test.js +40 -0
  124. package/dist/utils/diagnostics-exit.test.js.map +1 -0
  125. package/dist/utils/diff-utils.d.ts +4 -0
  126. package/dist/utils/diff-utils.d.ts.map +1 -0
  127. package/dist/utils/diff-utils.js +30 -0
  128. package/dist/utils/diff-utils.js.map +1 -0
  129. package/dist/utils/diff-utils.test.d.ts +2 -0
  130. package/dist/utils/diff-utils.test.d.ts.map +1 -0
  131. package/dist/utils/diff-utils.test.js +30 -0
  132. package/dist/utils/diff-utils.test.js.map +1 -0
  133. package/dist/utils/exit-codes.d.ts +142 -0
  134. package/dist/utils/exit-codes.d.ts.map +1 -0
  135. package/dist/utils/exit-codes.js +168 -0
  136. package/dist/utils/exit-codes.js.map +1 -0
  137. package/dist/utils/package-manager.d.ts +4 -0
  138. package/dist/utils/package-manager.d.ts.map +1 -0
  139. package/dist/utils/package-manager.js +40 -0
  140. package/dist/utils/package-manager.js.map +1 -0
  141. package/dist/utils/pkg.d.ts +3 -0
  142. package/dist/utils/pkg.d.ts.map +1 -0
  143. package/dist/utils/pkg.js +24 -0
  144. package/dist/utils/pkg.js.map +1 -0
  145. package/dist/utils/provider-injector.d.ts +36 -0
  146. package/dist/utils/provider-injector.d.ts.map +1 -0
  147. package/dist/utils/provider-injector.js +223 -0
  148. package/dist/utils/provider-injector.js.map +1 -0
  149. package/dist/utils/provider-injector.test.d.ts +2 -0
  150. package/dist/utils/provider-injector.test.d.ts.map +1 -0
  151. package/dist/utils/provider-injector.test.js +67 -0
  152. package/dist/utils/provider-injector.test.js.map +1 -0
  153. package/dist/utils/scaffold.d.ts +20 -0
  154. package/dist/utils/scaffold.d.ts.map +1 -0
  155. package/dist/utils/scaffold.js +197 -0
  156. package/dist/utils/scaffold.js.map +1 -0
  157. package/package.json +35 -0
  158. package/src/commands/audit.ts +234 -0
  159. package/src/commands/backup.ts +96 -0
  160. package/src/commands/check.ts +191 -0
  161. package/src/commands/config.ts +263 -0
  162. package/src/commands/debug-patterns.test.ts +134 -0
  163. package/src/commands/debug-patterns.ts +257 -0
  164. package/src/commands/diagnose.ts +136 -0
  165. package/src/commands/init.test.ts +82 -0
  166. package/src/commands/init.ts +536 -0
  167. package/src/commands/install-hooks.ts +66 -0
  168. package/src/commands/preflight.test.ts +139 -0
  169. package/src/commands/preflight.ts +488 -0
  170. package/src/commands/rename.ts +264 -0
  171. package/src/commands/scaffold-adapter.test.ts +110 -0
  172. package/src/commands/scaffold-adapter.ts +250 -0
  173. package/src/commands/scan.ts +125 -0
  174. package/src/commands/sync-seed.test.ts +116 -0
  175. package/src/commands/sync.ts +736 -0
  176. package/src/commands/transform.ts +151 -0
  177. package/src/commands/translate/README.md +75 -0
  178. package/src/commands/translate/csv-handler.ts +301 -0
  179. package/src/commands/translate/executor.ts +188 -0
  180. package/src/commands/translate/index.ts +220 -0
  181. package/src/commands/translate/reporter.ts +138 -0
  182. package/src/commands/translate/types.ts +56 -0
  183. package/src/commands/translate.test.ts +173 -0
  184. package/src/commands/translate.ts +6 -0
  185. package/src/e2e.test.ts +479 -0
  186. package/src/fixtures/README.md +61 -0
  187. package/src/fixtures/basic-react/i18n.config.json +15 -0
  188. package/src/fixtures/basic-react/locales/de.json +8 -0
  189. package/src/fixtures/basic-react/locales/en.json +8 -0
  190. package/src/fixtures/basic-react/locales/fr.json +8 -0
  191. package/src/fixtures/basic-react/src/App.tsx +15 -0
  192. package/src/fixtures/basic-react/src/Messages.tsx +12 -0
  193. package/src/fixtures/nested-locales/i18n.config.json +9 -0
  194. package/src/fixtures/nested-locales/locales/en.json +23 -0
  195. package/src/fixtures/nested-locales/locales/fr.json +23 -0
  196. package/src/fixtures/nested-locales/src/HomePage.tsx +13 -0
  197. package/src/fixtures/suspicious-keys/i18n.config.json +9 -0
  198. package/src/fixtures/suspicious-keys/locales/en.json +11 -0
  199. package/src/fixtures/suspicious-keys/locales/fr.json +11 -0
  200. package/src/fixtures/suspicious-keys/src/BadKeys.tsx +19 -0
  201. package/src/index.ts +43 -0
  202. package/src/integration.test.ts +438 -0
  203. package/src/utils/diagnostics-exit.test.ts +47 -0
  204. package/src/utils/diagnostics-exit.ts +63 -0
  205. package/src/utils/diff-utils.test.ts +36 -0
  206. package/src/utils/diff-utils.ts +42 -0
  207. package/src/utils/exit-codes.ts +201 -0
  208. package/src/utils/package-manager.ts +44 -0
  209. package/src/utils/pkg.ts +23 -0
  210. package/src/utils/provider-injector.test.ts +79 -0
  211. package/src/utils/provider-injector.ts +315 -0
  212. package/src/utils/scaffold.ts +240 -0
  213. package/tsconfig.json +17 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-injector.js","sourceRoot":"","sources":["../../src/utils/provider-injector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider-injector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-injector.test.d.ts","sourceRoot":"","sources":["../../src/utils/provider-injector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { promises as fs } from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
5
+ import { maybeInjectProvider } from './provider-injector.js';
6
+ const providerComponentPath = 'src/components/i18n-provider.tsx';
7
+ const providerTemplate = `'use client';
8
+
9
+ export default function Providers({ children }: { children: React.ReactNode }) {
10
+ return <ThemeProvider>{children}</ThemeProvider>;
11
+ }
12
+ `;
13
+ describe('provider injector', () => {
14
+ let workspaceRoot;
15
+ let cwdSpy;
16
+ beforeEach(async () => {
17
+ workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'i18nsmith-inject-'));
18
+ cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(workspaceRoot);
19
+ await fs.mkdir(path.join(workspaceRoot, 'app'), { recursive: true });
20
+ });
21
+ afterEach(async () => {
22
+ cwdSpy.mockRestore();
23
+ await fs.rm(workspaceRoot, { recursive: true, force: true });
24
+ });
25
+ it('injects the provider when children expression exists', async () => {
26
+ const providerFile = path.join(workspaceRoot, 'app/providers.tsx');
27
+ await fs.writeFile(providerFile, providerTemplate, 'utf8');
28
+ const result = await maybeInjectProvider({ providerComponentPath });
29
+ expect(result.status).toBe('injected');
30
+ if (result.status !== 'injected')
31
+ return;
32
+ const contents = await fs.readFile(providerFile, 'utf8');
33
+ expect(contents).toMatch(/import \{ I18nProvider \} from ['"].+i18n-provider['"];?/);
34
+ expect(contents).toContain('<I18nProvider>{children}</I18nProvider>');
35
+ });
36
+ it('previews changes without touching files when dryRun is true', async () => {
37
+ const providerFile = path.join(workspaceRoot, 'app/providers.tsx');
38
+ await fs.writeFile(providerFile, providerTemplate, 'utf8');
39
+ const result = await maybeInjectProvider({ providerComponentPath, dryRun: true });
40
+ expect(result.status).toBe('preview');
41
+ if (result.status !== 'preview')
42
+ return;
43
+ expect(result.diff).toContain('I18nProvider');
44
+ const contents = await fs.readFile(providerFile, 'utf8');
45
+ expect(contents).toBe(providerTemplate);
46
+ });
47
+ it('fails gracefully when multiple children expressions exist', async () => {
48
+ const providerFile = path.join(workspaceRoot, 'app/providers.tsx');
49
+ await fs.writeFile(providerFile, `'use client';
50
+
51
+ export default function Providers({ children }: { children: React.ReactNode }) {
52
+ return (
53
+ <ThemeProvider>
54
+ {children}
55
+ <div>{children}</div>
56
+ </ThemeProvider>
57
+ );
58
+ }
59
+ `, 'utf8');
60
+ const result = await maybeInjectProvider({ providerComponentPath });
61
+ expect(result.status).toBe('failed');
62
+ if (result.status !== 'failed')
63
+ return;
64
+ expect(result.reason).toMatch(/Multiple/);
65
+ });
66
+ });
67
+ //# sourceMappingURL=provider-injector.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-injector.test.js","sourceRoot":"","sources":["../../src/utils/provider-injector.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ interface ScaffoldOptions {
2
+ localesDir?: string;
3
+ workspaceRoot?: string;
4
+ force?: boolean;
5
+ dryRun?: boolean;
6
+ }
7
+ export interface ScaffoldResult {
8
+ path: string;
9
+ content: string;
10
+ written: boolean;
11
+ }
12
+ export declare function scaffoldTranslationContext(filePath: string, sourceLanguage: string, options?: ScaffoldOptions): Promise<ScaffoldResult>;
13
+ export declare function scaffoldI18next(i18nPath: string, providerPath: string, sourceLanguage: string, localesDir?: string, options?: ScaffoldOptions): Promise<{
14
+ i18nPath: string;
15
+ providerPath: string;
16
+ i18nResult: ScaffoldResult;
17
+ providerResult: ScaffoldResult;
18
+ }>;
19
+ export {};
20
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/utils/scaffold.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AA2CD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CA0DzB;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,UAAU,SAAY,EACtB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,cAAc,CAAC;IAAC,cAAc,EAAE,cAAc,CAAA;CAAE,CAAC,CAgHjH"}
@@ -0,0 +1,197 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ function resolvePath(targetPath, workspaceRoot) {
4
+ return path.isAbsolute(targetPath) ? targetPath : path.join(workspaceRoot, targetPath);
5
+ }
6
+ async function ensureDir(dirPath) {
7
+ await fs.mkdir(dirPath, { recursive: true });
8
+ }
9
+ async function writeFileSafely(filePath, content, force, dryRun) {
10
+ try {
11
+ await fs.stat(filePath);
12
+ if (!force) {
13
+ throw new Error(`File already exists at ${filePath}. Re-run with --force to overwrite.`);
14
+ }
15
+ }
16
+ catch (error) {
17
+ if (error && typeof error === 'object' && 'code' in error && error.code !== 'ENOENT') {
18
+ throw error;
19
+ }
20
+ }
21
+ if (dryRun) {
22
+ return false;
23
+ }
24
+ await fs.writeFile(filePath, content, 'utf8');
25
+ return true;
26
+ }
27
+ function relativeImportPath(fromDir, targetPath, dropExtension = true) {
28
+ let relative = path.relative(fromDir, targetPath).replace(/\\/g, '/');
29
+ if (!relative.startsWith('.')) {
30
+ relative = `./${relative}`;
31
+ }
32
+ if (dropExtension) {
33
+ relative = relative.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, '');
34
+ }
35
+ return relative;
36
+ }
37
+ export async function scaffoldTranslationContext(filePath, sourceLanguage, options = {}) {
38
+ const workspaceRoot = options.workspaceRoot ?? process.cwd();
39
+ const localesDir = options.localesDir ?? 'locales';
40
+ const absolutePath = resolvePath(filePath, workspaceRoot);
41
+ const dir = path.dirname(absolutePath);
42
+ if (!options.dryRun) {
43
+ await ensureDir(dir);
44
+ }
45
+ const localeFile = path.join(workspaceRoot, localesDir, `${sourceLanguage}.json`);
46
+ let localeImportPath = path.relative(dir, localeFile).replace(/\\/g, '/');
47
+ if (!localeImportPath.startsWith('.')) {
48
+ localeImportPath = `./${localeImportPath}`;
49
+ }
50
+ const content = `'use client';
51
+
52
+ import { createContext, useContext, useState } from 'react';
53
+ import ${sourceLanguage}Messages from '${localeImportPath}';
54
+
55
+ type Language = '${sourceLanguage}';
56
+
57
+ const messages = {
58
+ ${sourceLanguage}: ${sourceLanguage}Messages,
59
+ };
60
+
61
+ interface TranslationContextType {
62
+ language: Language;
63
+ setLanguage: (lang: Language) => void;
64
+ t: (key: string) => string;
65
+ }
66
+
67
+ const TranslationContext = createContext<TranslationContextType>({
68
+ language: '${sourceLanguage}',
69
+ setLanguage: () => {},
70
+ t: (key) => key,
71
+ });
72
+
73
+ export function TranslationProvider({ children }: { children: React.ReactNode }) {
74
+ const [language, setLanguage] = useState<Language>('${sourceLanguage}');
75
+
76
+ const t = (key: string) => messages[language]?.[key] ?? key;
77
+
78
+ return (
79
+ <TranslationContext.Provider value={{ language, setLanguage, t }}>
80
+ {children}
81
+ </TranslationContext.Provider>
82
+ );
83
+ }
84
+
85
+ export function useTranslation() {
86
+ return useContext(TranslationContext);
87
+ }
88
+ `;
89
+ const written = await writeFileSafely(absolutePath, content, options.force, options.dryRun);
90
+ return { path: absolutePath, content, written };
91
+ }
92
+ export async function scaffoldI18next(i18nPath, providerPath, sourceLanguage, localesDir = 'locales', options = {}) {
93
+ const workspaceRoot = options.workspaceRoot ?? process.cwd();
94
+ const absoluteI18nPath = resolvePath(i18nPath, workspaceRoot);
95
+ const absoluteProviderPath = resolvePath(providerPath, workspaceRoot);
96
+ if (!options.dryRun) {
97
+ await ensureDir(path.dirname(absoluteI18nPath));
98
+ await ensureDir(path.dirname(absoluteProviderPath));
99
+ }
100
+ const localeFile = path.join(workspaceRoot, localesDir, `${sourceLanguage}.json`);
101
+ let localeImportPath = path.relative(path.dirname(absoluteI18nPath), localeFile).replace(/\\/g, '/');
102
+ if (!localeImportPath.startsWith('.')) {
103
+ localeImportPath = `./${localeImportPath}`;
104
+ }
105
+ const i18nContent = `import i18next from 'i18next';
106
+ import { initReactI18next } from 'react-i18next';
107
+ import ${sourceLanguage}Messages from '${localeImportPath}';
108
+
109
+ const resources = {
110
+ ${sourceLanguage}: {
111
+ translation: ${sourceLanguage}Messages,
112
+ },
113
+ };
114
+
115
+ function doInit() {
116
+ const initialized = (i18next as unknown as { isInitialized?: boolean }).isInitialized;
117
+ if (initialized) {
118
+ return Promise.resolve();
119
+ }
120
+
121
+ return i18next
122
+ .use(initReactI18next)
123
+ .init({
124
+ lng: '${sourceLanguage}',
125
+ fallbackLng: '${sourceLanguage}',
126
+ resources,
127
+ interpolation: {
128
+ escapeValue: false,
129
+ },
130
+ react: {
131
+ useSuspense: false,
132
+ },
133
+ })
134
+ .catch((error) => {
135
+ console.error('i18next init failed', error);
136
+ throw error;
137
+ });
138
+ }
139
+
140
+ if (typeof window !== 'undefined') {
141
+ void doInit();
142
+ }
143
+
144
+ export function initI18next() {
145
+ return doInit();
146
+ }
147
+
148
+ export default i18next;
149
+ `;
150
+ const i18nWritten = await writeFileSafely(absoluteI18nPath, i18nContent, options.force, options.dryRun);
151
+ const providerImport = relativeImportPath(path.dirname(absoluteProviderPath), absoluteI18nPath, true);
152
+ const providerContent = `'use client';
153
+
154
+ import { ReactNode, useEffect, useState } from 'react';
155
+ import { initI18next } from '${providerImport}';
156
+
157
+ interface I18nProviderProps {
158
+ children: ReactNode;
159
+ }
160
+
161
+ export function I18nProvider({ children }: I18nProviderProps) {
162
+ const [ready, setReady] = useState(false);
163
+
164
+ useEffect(() => {
165
+ let mounted = true;
166
+
167
+ Promise.resolve(initI18next())
168
+ .catch((error) => {
169
+ console.error('i18next initialization failed', error);
170
+ })
171
+ .finally(() => {
172
+ if (mounted) {
173
+ setReady(true);
174
+ }
175
+ });
176
+
177
+ return () => {
178
+ mounted = false;
179
+ };
180
+ }, []);
181
+
182
+ if (!ready) {
183
+ return null;
184
+ }
185
+
186
+ return <>{children}</>;
187
+ }
188
+ `;
189
+ const providerWritten = await writeFileSafely(absoluteProviderPath, providerContent, options.force, options.dryRun);
190
+ return {
191
+ i18nPath: absoluteI18nPath,
192
+ providerPath: absoluteProviderPath,
193
+ i18nResult: { path: absoluteI18nPath, content: i18nContent, written: i18nWritten },
194
+ providerResult: { path: absoluteProviderPath, content: providerContent, written: providerWritten },
195
+ };
196
+ }
197
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/utils/scaffold.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "i18nsmith",
3
+ "version": "0.1.0",
4
+ "description": "CLI for i18nsmith",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "i18nsmith": "dist/index.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "build": "tsc -b",
19
+ "test": "vitest run"
20
+ },
21
+ "dependencies": {
22
+ "@i18nsmith/core": "workspace:*",
23
+ "@i18nsmith/transformer": "workspace:*",
24
+ "@i18nsmith/translation": "workspace:*",
25
+ "@i18nsmith/translator-mock": "workspace:*",
26
+ "chalk": "^5.0.0",
27
+ "commander": "^11.0.0",
28
+ "diff": "^8.0.2",
29
+ "fast-glob": "^3.3.3",
30
+ "inquirer": "^9.0.0",
31
+ "p-limit": "^7.2.0",
32
+ "p-retry": "^7.1.0",
33
+ "ts-morph": "^21.0.0"
34
+ }
35
+ }
@@ -0,0 +1,234 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import type { Command } from 'commander';
5
+ import {
6
+ loadConfig,
7
+ LocaleStore,
8
+ KeyValidator,
9
+ LocaleValidator,
10
+ SUSPICIOUS_KEY_REASON_DESCRIPTIONS,
11
+ } from '@i18nsmith/core';
12
+
13
+ interface AuditCommandOptions {
14
+ config?: string;
15
+ locale?: string[];
16
+ json?: boolean;
17
+ report?: string;
18
+ strict?: boolean;
19
+ duplicates?: boolean;
20
+ inconsistent?: boolean;
21
+ orphaned?: boolean;
22
+ }
23
+
24
+ interface AuditIssue {
25
+ key: string;
26
+ value: string;
27
+ reason: string;
28
+ description: string;
29
+ suggestion?: string;
30
+ }
31
+
32
+ interface QualityIssue {
33
+ type: 'duplicate-value' | 'inconsistent-key' | 'orphaned-namespace';
34
+ description: string;
35
+ keys?: string[];
36
+ suggestion?: string;
37
+ }
38
+
39
+ interface LocaleAuditResult {
40
+ locale: string;
41
+ totalKeys: number;
42
+ issues: AuditIssue[];
43
+ qualityIssues: QualityIssue[];
44
+ }
45
+
46
+ export function registerAudit(program: Command) {
47
+ program
48
+ .command('audit')
49
+ .description('Audit locale files for suspicious keys, key=value patterns, and quality issues')
50
+ .option('-c, --config <path>', 'Path to i18nsmith config file', 'i18n.config.json')
51
+ .option('-l, --locale <locales...>', 'Specific locale(s) to audit (defaults to all)')
52
+ .option('--json', 'Print raw JSON results', false)
53
+ .option('--report <path>', 'Write JSON report to a file')
54
+ .option('--strict', 'Exit with error code if any issues found', false)
55
+ .option('--duplicates', 'Check for duplicate values (consolidation opportunities)', false)
56
+ .option('--inconsistent', 'Check for inconsistent key naming patterns', false)
57
+ .option('--orphaned', 'Check for orphaned namespaces with few keys', false)
58
+ .action(async (options: AuditCommandOptions) => {
59
+ console.log(chalk.blue('Auditing locale files...'));
60
+ try {
61
+ const config = await loadConfig(options.config);
62
+ const localesDir = path.resolve(process.cwd(), config.localesDir ?? 'locales');
63
+ const localeStore = new LocaleStore(localesDir, {
64
+ sortKeys: config.locales?.sortKeys ?? 'alphabetical',
65
+ });
66
+ const keyValidator = new KeyValidator(config.sync?.suspiciousKeyPolicy ?? 'skip');
67
+ const localeValidator = new LocaleValidator({
68
+ delimiter: config.locales?.delimiter ?? '.',
69
+ });
70
+
71
+ // Determine which locales to audit
72
+ let localesToAudit = options.locale ?? [];
73
+ if (localesToAudit.length === 0) {
74
+ localesToAudit = [config.sourceLanguage ?? 'en', ...(config.targetLanguages ?? [])];
75
+ }
76
+
77
+ // Enable all quality checks if none specified
78
+ const runQualityChecks = options.duplicates || options.inconsistent || options.orphaned;
79
+ const checkDuplicates = options.duplicates || !runQualityChecks;
80
+ const checkInconsistent = options.inconsistent;
81
+ const checkOrphaned = options.orphaned;
82
+
83
+ const results: LocaleAuditResult[] = [];
84
+ const allKeys = new Set<string>();
85
+
86
+ // First pass: collect all keys
87
+ for (const locale of localesToAudit) {
88
+ const data = await localeStore.get(locale);
89
+ for (const key of Object.keys(data)) {
90
+ allKeys.add(key);
91
+ }
92
+ }
93
+
94
+ // Second pass: run audits
95
+ for (const locale of localesToAudit) {
96
+ const data = await localeStore.get(locale);
97
+ const keys = Object.keys(data);
98
+ const issues: AuditIssue[] = [];
99
+ const qualityIssues: QualityIssue[] = [];
100
+
101
+ // Suspicious key detection
102
+ for (const key of keys) {
103
+ const value = data[key];
104
+ const analysis = keyValidator.analyzeWithValue(key, value);
105
+
106
+ if (analysis.suspicious && analysis.reason) {
107
+ issues.push({
108
+ key,
109
+ value: value.length > 50 ? `${value.slice(0, 47)}...` : value,
110
+ reason: analysis.reason,
111
+ description: SUSPICIOUS_KEY_REASON_DESCRIPTIONS[analysis.reason] ?? 'Unknown issue',
112
+ suggestion: keyValidator.suggestFix(key, analysis.reason),
113
+ });
114
+ }
115
+ }
116
+
117
+ // Quality checks
118
+ if (checkDuplicates) {
119
+ const duplicates = localeValidator.detectDuplicateValues(locale, data);
120
+ for (const dup of duplicates) {
121
+ qualityIssues.push({
122
+ type: 'duplicate-value',
123
+ description: `Value "${dup.value.slice(0, 40)}${dup.value.length > 40 ? '...' : ''}" used by ${dup.keys.length} keys`,
124
+ keys: dup.keys,
125
+ suggestion: 'Consider consolidating to a single key',
126
+ });
127
+ }
128
+ }
129
+
130
+ if (checkInconsistent && locale === localesToAudit[0]) {
131
+ // Only check inconsistent keys once (using all keys)
132
+ const inconsistent = localeValidator.detectInconsistentKeys(Array.from(allKeys));
133
+ for (const inc of inconsistent) {
134
+ qualityIssues.push({
135
+ type: 'inconsistent-key',
136
+ description: inc.pattern,
137
+ keys: inc.variants,
138
+ suggestion: inc.suggestion,
139
+ });
140
+ }
141
+ }
142
+
143
+ if (checkOrphaned && locale === localesToAudit[0]) {
144
+ // Only check orphaned namespaces once (using all keys)
145
+ const orphaned = localeValidator.detectOrphanedNamespaces(Array.from(allKeys));
146
+ for (const orph of orphaned) {
147
+ qualityIssues.push({
148
+ type: 'orphaned-namespace',
149
+ description: `Namespace "${orph.namespace}" has only ${orph.keyCount} key(s)`,
150
+ keys: orph.keys,
151
+ suggestion: 'Consider merging into a related namespace',
152
+ });
153
+ }
154
+ }
155
+
156
+ results.push({
157
+ locale,
158
+ totalKeys: keys.length,
159
+ issues,
160
+ qualityIssues,
161
+ });
162
+ }
163
+
164
+ const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0);
165
+ const totalQualityIssues = results.reduce((sum, r) => sum + r.qualityIssues.length, 0);
166
+
167
+ if (options.report) {
168
+ const outputPath = path.resolve(process.cwd(), options.report);
169
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
170
+ await fs.writeFile(outputPath, JSON.stringify({ results, totalIssues, totalQualityIssues }, null, 2));
171
+ console.log(chalk.green(`Audit report written to ${outputPath}`));
172
+ }
173
+
174
+ if (options.json) {
175
+ console.log(JSON.stringify({ results, totalIssues, totalQualityIssues }, null, 2));
176
+ } else {
177
+ // Pretty print results
178
+ for (const result of results) {
179
+ const hasIssues = result.issues.length > 0 || result.qualityIssues.length > 0;
180
+ if (!hasIssues) {
181
+ console.log(chalk.green(`✓ ${result.locale}.json: ${result.totalKeys} keys, no issues`));
182
+ } else {
183
+ console.log(chalk.yellow(`⚠ ${result.locale}.json: ${result.totalKeys} keys`));
184
+
185
+ // Suspicious keys
186
+ if (result.issues.length > 0) {
187
+ console.log(chalk.yellow(` Suspicious keys: ${result.issues.length}`));
188
+ for (const issue of result.issues) {
189
+ console.log(chalk.dim(` - "${issue.key}"`));
190
+ console.log(chalk.dim(` ${issue.description}`));
191
+ if (issue.suggestion) {
192
+ console.log(chalk.dim(` Suggestion: ${issue.suggestion}`));
193
+ }
194
+ }
195
+ }
196
+
197
+ // Quality issues
198
+ if (result.qualityIssues.length > 0) {
199
+ console.log(chalk.cyan(` Quality checks: ${result.qualityIssues.length}`));
200
+ for (const issue of result.qualityIssues) {
201
+ const typeLabel = issue.type === 'duplicate-value' ? '📋' :
202
+ issue.type === 'inconsistent-key' ? '🔀' : '📦';
203
+ console.log(chalk.dim(` ${typeLabel} ${issue.description}`));
204
+ if (issue.suggestion) {
205
+ console.log(chalk.dim(` ${issue.suggestion}`));
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ console.log();
213
+ if (totalIssues === 0 && totalQualityIssues === 0) {
214
+ console.log(chalk.green('✓ No issues found in locale files'));
215
+ } else {
216
+ if (totalIssues > 0) {
217
+ console.log(chalk.yellow(`Found ${totalIssues} suspicious key(s)`));
218
+ }
219
+ if (totalQualityIssues > 0) {
220
+ console.log(chalk.cyan(`Found ${totalQualityIssues} quality issue(s)`));
221
+ }
222
+ }
223
+ }
224
+
225
+ if (options.strict && totalIssues > 0) {
226
+ console.error(chalk.red(`\nAudit failed with ${totalIssues} issue(s). Use --strict=false to allow.`));
227
+ process.exitCode = 1;
228
+ }
229
+ } catch (error) {
230
+ console.error(chalk.red('Audit failed:'), (error as Error).message);
231
+ process.exitCode = 1;
232
+ }
233
+ });
234
+ }