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.
- package/dist/commands/audit.d.ts +3 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +180 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/backup.d.ts +6 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +85 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +151 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +235 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/debug-patterns.d.ts +3 -0
- package/dist/commands/debug-patterns.d.ts.map +1 -0
- package/dist/commands/debug-patterns.js +192 -0
- package/dist/commands/debug-patterns.js.map +1 -0
- package/dist/commands/debug-patterns.test.d.ts +2 -0
- package/dist/commands/debug-patterns.test.d.ts.map +1 -0
- package/dist/commands/debug-patterns.test.js +109 -0
- package/dist/commands/debug-patterns.test.js.map +1 -0
- package/dist/commands/diagnose.d.ts +3 -0
- package/dist/commands/diagnose.d.ts.map +1 -0
- package/dist/commands/diagnose.js +117 -0
- package/dist/commands/diagnose.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +450 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +74 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/install-hooks.d.ts +3 -0
- package/dist/commands/install-hooks.d.ts.map +1 -0
- package/dist/commands/install-hooks.js +52 -0
- package/dist/commands/install-hooks.js.map +1 -0
- package/dist/commands/preflight.d.ts +7 -0
- package/dist/commands/preflight.d.ts.map +1 -0
- package/dist/commands/preflight.js +417 -0
- package/dist/commands/preflight.js.map +1 -0
- package/dist/commands/preflight.test.d.ts +5 -0
- package/dist/commands/preflight.test.d.ts.map +1 -0
- package/dist/commands/preflight.test.js +108 -0
- package/dist/commands/preflight.test.js.map +1 -0
- package/dist/commands/rename.d.ts +6 -0
- package/dist/commands/rename.d.ts.map +1 -0
- package/dist/commands/rename.js +204 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/scaffold-adapter.d.ts +3 -0
- package/dist/commands/scaffold-adapter.d.ts.map +1 -0
- package/dist/commands/scaffold-adapter.js +204 -0
- package/dist/commands/scaffold-adapter.js.map +1 -0
- package/dist/commands/scaffold-adapter.test.d.ts +2 -0
- package/dist/commands/scaffold-adapter.test.d.ts.map +1 -0
- package/dist/commands/scaffold-adapter.test.js +102 -0
- package/dist/commands/scaffold-adapter.test.js.map +1 -0
- package/dist/commands/scan.d.ts +3 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +93 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/sync-seed.test.d.ts +2 -0
- package/dist/commands/sync-seed.test.d.ts.map +1 -0
- package/dist/commands/sync-seed.test.js +86 -0
- package/dist/commands/sync-seed.test.js.map +1 -0
- package/dist/commands/sync.d.ts +3 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +590 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/transform.d.ts +3 -0
- package/dist/commands/transform.d.ts.map +1 -0
- package/dist/commands/transform.js +114 -0
- package/dist/commands/transform.js.map +1 -0
- package/dist/commands/translate/csv-handler.d.ts +21 -0
- package/dist/commands/translate/csv-handler.d.ts.map +1 -0
- package/dist/commands/translate/csv-handler.js +270 -0
- package/dist/commands/translate/csv-handler.js.map +1 -0
- package/dist/commands/translate/executor.d.ts +31 -0
- package/dist/commands/translate/executor.d.ts.map +1 -0
- package/dist/commands/translate/executor.js +117 -0
- package/dist/commands/translate/executor.js.map +1 -0
- package/dist/commands/translate/index.d.ts +10 -0
- package/dist/commands/translate/index.d.ts.map +1 -0
- package/dist/commands/translate/index.js +170 -0
- package/dist/commands/translate/index.js.map +1 -0
- package/dist/commands/translate/reporter.d.ts +29 -0
- package/dist/commands/translate/reporter.d.ts.map +1 -0
- package/dist/commands/translate/reporter.js +103 -0
- package/dist/commands/translate/reporter.js.map +1 -0
- package/dist/commands/translate/types.d.ts +50 -0
- package/dist/commands/translate/types.d.ts.map +1 -0
- package/dist/commands/translate/types.js +5 -0
- package/dist/commands/translate/types.js.map +1 -0
- package/dist/commands/translate.d.ts +7 -0
- package/dist/commands/translate.d.ts.map +1 -0
- package/dist/commands/translate.js +7 -0
- package/dist/commands/translate.js.map +1 -0
- package/dist/commands/translate.test.d.ts +2 -0
- package/dist/commands/translate.test.d.ts.map +1 -0
- package/dist/commands/translate.test.js +118 -0
- package/dist/commands/translate.test.js.map +1 -0
- package/dist/e2e.test.d.ts +6 -0
- package/dist/e2e.test.d.ts.map +1 -0
- package/dist/e2e.test.js +376 -0
- package/dist/e2e.test.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.test.d.ts +6 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +320 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/utils/diagnostics-exit.d.ts +12 -0
- package/dist/utils/diagnostics-exit.d.ts.map +1 -0
- package/dist/utils/diagnostics-exit.js +49 -0
- package/dist/utils/diagnostics-exit.js.map +1 -0
- package/dist/utils/diagnostics-exit.test.d.ts +2 -0
- package/dist/utils/diagnostics-exit.test.d.ts.map +1 -0
- package/dist/utils/diagnostics-exit.test.js +40 -0
- package/dist/utils/diagnostics-exit.test.js.map +1 -0
- package/dist/utils/diff-utils.d.ts +4 -0
- package/dist/utils/diff-utils.d.ts.map +1 -0
- package/dist/utils/diff-utils.js +30 -0
- package/dist/utils/diff-utils.js.map +1 -0
- package/dist/utils/diff-utils.test.d.ts +2 -0
- package/dist/utils/diff-utils.test.d.ts.map +1 -0
- package/dist/utils/diff-utils.test.js +30 -0
- package/dist/utils/diff-utils.test.js.map +1 -0
- package/dist/utils/exit-codes.d.ts +142 -0
- package/dist/utils/exit-codes.d.ts.map +1 -0
- package/dist/utils/exit-codes.js +168 -0
- package/dist/utils/exit-codes.js.map +1 -0
- package/dist/utils/package-manager.d.ts +4 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +40 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/dist/utils/pkg.d.ts +3 -0
- package/dist/utils/pkg.d.ts.map +1 -0
- package/dist/utils/pkg.js +24 -0
- package/dist/utils/pkg.js.map +1 -0
- package/dist/utils/provider-injector.d.ts +36 -0
- package/dist/utils/provider-injector.d.ts.map +1 -0
- package/dist/utils/provider-injector.js +223 -0
- package/dist/utils/provider-injector.js.map +1 -0
- package/dist/utils/provider-injector.test.d.ts +2 -0
- package/dist/utils/provider-injector.test.d.ts.map +1 -0
- package/dist/utils/provider-injector.test.js +67 -0
- package/dist/utils/provider-injector.test.js.map +1 -0
- package/dist/utils/scaffold.d.ts +20 -0
- package/dist/utils/scaffold.d.ts.map +1 -0
- package/dist/utils/scaffold.js +197 -0
- package/dist/utils/scaffold.js.map +1 -0
- package/package.json +35 -0
- package/src/commands/audit.ts +234 -0
- package/src/commands/backup.ts +96 -0
- package/src/commands/check.ts +191 -0
- package/src/commands/config.ts +263 -0
- package/src/commands/debug-patterns.test.ts +134 -0
- package/src/commands/debug-patterns.ts +257 -0
- package/src/commands/diagnose.ts +136 -0
- package/src/commands/init.test.ts +82 -0
- package/src/commands/init.ts +536 -0
- package/src/commands/install-hooks.ts +66 -0
- package/src/commands/preflight.test.ts +139 -0
- package/src/commands/preflight.ts +488 -0
- package/src/commands/rename.ts +264 -0
- package/src/commands/scaffold-adapter.test.ts +110 -0
- package/src/commands/scaffold-adapter.ts +250 -0
- package/src/commands/scan.ts +125 -0
- package/src/commands/sync-seed.test.ts +116 -0
- package/src/commands/sync.ts +736 -0
- package/src/commands/transform.ts +151 -0
- package/src/commands/translate/README.md +75 -0
- package/src/commands/translate/csv-handler.ts +301 -0
- package/src/commands/translate/executor.ts +188 -0
- package/src/commands/translate/index.ts +220 -0
- package/src/commands/translate/reporter.ts +138 -0
- package/src/commands/translate/types.ts +56 -0
- package/src/commands/translate.test.ts +173 -0
- package/src/commands/translate.ts +6 -0
- package/src/e2e.test.ts +479 -0
- package/src/fixtures/README.md +61 -0
- package/src/fixtures/basic-react/i18n.config.json +15 -0
- package/src/fixtures/basic-react/locales/de.json +8 -0
- package/src/fixtures/basic-react/locales/en.json +8 -0
- package/src/fixtures/basic-react/locales/fr.json +8 -0
- package/src/fixtures/basic-react/src/App.tsx +15 -0
- package/src/fixtures/basic-react/src/Messages.tsx +12 -0
- package/src/fixtures/nested-locales/i18n.config.json +9 -0
- package/src/fixtures/nested-locales/locales/en.json +23 -0
- package/src/fixtures/nested-locales/locales/fr.json +23 -0
- package/src/fixtures/nested-locales/src/HomePage.tsx +13 -0
- package/src/fixtures/suspicious-keys/i18n.config.json +9 -0
- package/src/fixtures/suspicious-keys/locales/en.json +11 -0
- package/src/fixtures/suspicious-keys/locales/fr.json +11 -0
- package/src/fixtures/suspicious-keys/src/BadKeys.tsx +19 -0
- package/src/index.ts +43 -0
- package/src/integration.test.ts +438 -0
- package/src/utils/diagnostics-exit.test.ts +47 -0
- package/src/utils/diagnostics-exit.ts +63 -0
- package/src/utils/diff-utils.test.ts +36 -0
- package/src/utils/diff-utils.ts +42 -0
- package/src/utils/exit-codes.ts +201 -0
- package/src/utils/package-manager.ts +44 -0
- package/src/utils/pkg.ts +23 -0
- package/src/utils/provider-injector.test.ts +79 -0
- package/src/utils/provider-injector.ts +315 -0
- package/src/utils/scaffold.ts +240 -0
- 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 @@
|
|
|
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
|
+
}
|