@better-i18n/cli 0.1.8 → 0.2.2
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/analyzer/dynamic-matcher.d.ts +53 -0
- package/dist/analyzer/dynamic-matcher.d.ts.map +1 -0
- package/dist/analyzer/dynamic-matcher.js +93 -0
- package/dist/analyzer/dynamic-matcher.js.map +1 -0
- package/dist/analyzer/index.d.ts +13 -4
- package/dist/analyzer/index.d.ts.map +1 -1
- package/dist/analyzer/index.js +165 -22
- package/dist/analyzer/index.js.map +1 -1
- package/dist/analyzer/rules/data-structure.d.ts +13 -0
- package/dist/analyzer/rules/data-structure.d.ts.map +1 -0
- package/dist/analyzer/rules/data-structure.js +264 -0
- package/dist/analyzer/rules/data-structure.js.map +1 -0
- package/dist/analyzer/rules/index.d.ts +10 -7
- package/dist/analyzer/rules/index.d.ts.map +1 -1
- package/dist/analyzer/rules/index.js +10 -7
- package/dist/analyzer/rules/index.js.map +1 -1
- package/dist/analyzer/rules/jsx-text.js +1 -1
- package/dist/analyzer/rules/jsx-text.js.map +1 -1
- package/dist/analyzer/rules/translation-function.d.ts.map +1 -1
- package/dist/analyzer/rules/translation-function.js +94 -6
- package/dist/analyzer/rules/translation-function.js.map +1 -1
- package/dist/analyzer/types.d.ts +28 -3
- package/dist/analyzer/types.d.ts.map +1 -1
- package/dist/commands/check.d.ts +14 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +70 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/doctor.d.ts +20 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +77 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +158 -272
- package/dist/commands/sync.js.map +1 -1
- package/dist/context/detector.js +1 -1
- package/dist/context/detector.js.map +1 -1
- package/dist/context/oidc.d.ts +33 -0
- package/dist/context/oidc.d.ts.map +1 -0
- package/dist/context/oidc.js +63 -0
- package/dist/context/oidc.js.map +1 -0
- package/dist/doctor/index.d.ts +65 -0
- package/dist/doctor/index.d.ts.map +1 -0
- package/dist/doctor/index.js +253 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/doctor/project-discovery.d.ts +37 -0
- package/dist/doctor/project-discovery.d.ts.map +1 -0
- package/dist/doctor/project-discovery.js +140 -0
- package/dist/doctor/project-discovery.js.map +1 -0
- package/dist/doctor/score.d.ts +41 -0
- package/dist/doctor/score.d.ts.map +1 -0
- package/dist/doctor/score.js +88 -0
- package/dist/doctor/score.js.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -1
- package/dist/reporters/doctor-eslint.d.ts +20 -0
- package/dist/reporters/doctor-eslint.d.ts.map +1 -0
- package/dist/reporters/doctor-eslint.js +175 -0
- package/dist/reporters/doctor-eslint.js.map +1 -0
- package/dist/reporters/doctor-json.d.ts +12 -0
- package/dist/reporters/doctor-json.d.ts.map +1 -0
- package/dist/reporters/doctor-json.js +21 -0
- package/dist/reporters/doctor-json.js.map +1 -0
- package/dist/reporters/doctor-report.d.ts +23 -0
- package/dist/reporters/doctor-report.d.ts.map +1 -0
- package/dist/reporters/doctor-report.js +62 -0
- package/dist/reporters/doctor-report.js.map +1 -0
- package/dist/reporters/eslint-style.js +4 -4
- package/dist/reporters/eslint-style.js.map +1 -1
- package/dist/rules/categories.d.ts +36 -0
- package/dist/rules/categories.d.ts.map +1 -0
- package/dist/rules/categories.js +80 -0
- package/dist/rules/categories.js.map +1 -0
- package/dist/rules/code/index.d.ts +9 -0
- package/dist/rules/code/index.d.ts.map +1 -0
- package/dist/rules/code/index.js +9 -0
- package/dist/rules/code/index.js.map +1 -0
- package/dist/rules/code/jsx-attribute.d.ts +12 -0
- package/dist/rules/code/jsx-attribute.d.ts.map +1 -0
- package/dist/rules/code/jsx-attribute.js +78 -0
- package/dist/rules/code/jsx-attribute.js.map +1 -0
- package/dist/rules/code/jsx-text.d.ts +12 -0
- package/dist/rules/code/jsx-text.d.ts.map +1 -0
- package/dist/rules/code/jsx-text.js +47 -0
- package/dist/rules/code/jsx-text.js.map +1 -0
- package/dist/rules/code/string-variable.d.ts +13 -0
- package/dist/rules/code/string-variable.d.ts.map +1 -0
- package/dist/rules/code/string-variable.js +185 -0
- package/dist/rules/code/string-variable.js.map +1 -0
- package/dist/rules/code/ternary-locale.d.ts +12 -0
- package/dist/rules/code/ternary-locale.d.ts.map +1 -0
- package/dist/rules/code/ternary-locale.js +53 -0
- package/dist/rules/code/ternary-locale.js.map +1 -0
- package/dist/rules/code/toast-message.d.ts +13 -0
- package/dist/rules/code/toast-message.d.ts.map +1 -0
- package/dist/rules/code/toast-message.js +101 -0
- package/dist/rules/code/toast-message.js.map +1 -0
- package/dist/rules/extraction/data-structure.d.ts +13 -0
- package/dist/rules/extraction/data-structure.d.ts.map +1 -0
- package/dist/rules/extraction/data-structure.js +212 -0
- package/dist/rules/extraction/data-structure.js.map +1 -0
- package/dist/rules/extraction/index.d.ts +6 -0
- package/dist/rules/extraction/index.d.ts.map +1 -0
- package/dist/rules/extraction/index.js +6 -0
- package/dist/rules/extraction/index.js.map +1 -0
- package/dist/rules/extraction/translation-function.d.ts +12 -0
- package/dist/rules/extraction/translation-function.d.ts.map +1 -0
- package/dist/rules/extraction/translation-function.js +153 -0
- package/dist/rules/extraction/translation-function.js.map +1 -0
- package/dist/rules/health/index.d.ts +16 -0
- package/dist/rules/health/index.d.ts.map +1 -0
- package/dist/rules/health/index.js +22 -0
- package/dist/rules/health/index.js.map +1 -0
- package/dist/rules/health/missing-translations.d.ts +13 -0
- package/dist/rules/health/missing-translations.d.ts.map +1 -0
- package/dist/rules/health/missing-translations.js +48 -0
- package/dist/rules/health/missing-translations.js.map +1 -0
- package/dist/rules/health/orphan-keys.d.ts +16 -0
- package/dist/rules/health/orphan-keys.d.ts.map +1 -0
- package/dist/rules/health/orphan-keys.js +50 -0
- package/dist/rules/health/orphan-keys.js.map +1 -0
- package/dist/rules/health/placeholder-mismatch.d.ts +32 -0
- package/dist/rules/health/placeholder-mismatch.d.ts.map +1 -0
- package/dist/rules/health/placeholder-mismatch.js +120 -0
- package/dist/rules/health/placeholder-mismatch.js.map +1 -0
- package/dist/rules/registry.d.ts +84 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +11 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/utils/cdn-client.d.ts +30 -0
- package/dist/utils/cdn-client.d.ts.map +1 -0
- package/dist/utils/cdn-client.js +59 -0
- package/dist/utils/cdn-client.js.map +1 -0
- package/dist/utils/json-keys.d.ts +60 -0
- package/dist/utils/json-keys.d.ts.map +1 -0
- package/dist/utils/json-keys.js +103 -0
- package/dist/utils/json-keys.js.map +1 -0
- package/dist/utils/key-comparison.d.ts +67 -0
- package/dist/utils/key-comparison.d.ts.map +1 -0
- package/dist/utils/key-comparison.js +299 -0
- package/dist/utils/key-comparison.js.map +1 -0
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/context/detector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAG1C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,OAAO,CAAC,GAAG,EAAE,EAChC,YAAY,GAAG,KAAK;IAEpB,IAAI,CAAC;QACH,qDAAqD;QACrD,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,eAAe,CAAC,WAAW,CAAC,CAAC;QACtC,CAAC;QAED,mDAAmD;QACnD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,aAAa,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAClD,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;gBAC/B,MAAM,UAAU,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;gBACjD,IAAI,UAAU,EAAE,CAAC;oBACf,OAAO,eAAe,CAAC,UAAU,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/context/detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Actions OIDC Token Fetcher
|
|
3
|
+
*
|
|
4
|
+
* Detects GitHub Actions environment and fetches an OIDC identity token
|
|
5
|
+
* for zero-secret authentication with the Better i18n API.
|
|
6
|
+
*
|
|
7
|
+
* How it works:
|
|
8
|
+
* 1. GitHub Actions injects ACTIONS_ID_TOKEN_REQUEST_URL + TOKEN
|
|
9
|
+
* 2. We request a JWT from that URL with our audience ("better-i18n")
|
|
10
|
+
* 3. The JWT is signed by GitHub's JWKS — API can verify without shared secrets
|
|
11
|
+
*
|
|
12
|
+
* References:
|
|
13
|
+
* - https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Check if running in GitHub Actions with OIDC support.
|
|
17
|
+
*
|
|
18
|
+
* OIDC requires:
|
|
19
|
+
* - `GITHUB_ACTIONS=true` (running in GA)
|
|
20
|
+
* - `ACTIONS_ID_TOKEN_REQUEST_URL` (OIDC endpoint available)
|
|
21
|
+
* - `ACTIONS_ID_TOKEN_REQUEST_TOKEN` (bearer token for the endpoint)
|
|
22
|
+
*
|
|
23
|
+
* The last two are only present when the workflow has `permissions: id-token: write`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isGitHubActionsWithOidc(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Fetch a GitHub Actions OIDC token for the given audience.
|
|
28
|
+
*
|
|
29
|
+
* @param audience - Token audience claim (default: "better-i18n")
|
|
30
|
+
* @returns JWT string, or null if OIDC is unavailable or the request fails
|
|
31
|
+
*/
|
|
32
|
+
export declare function fetchGitHubOidcToken(audience?: string): Promise<string | null>;
|
|
33
|
+
//# sourceMappingURL=oidc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../../src/context/oidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,GAAE,MAAyB,GAClC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BxB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Actions OIDC Token Fetcher
|
|
3
|
+
*
|
|
4
|
+
* Detects GitHub Actions environment and fetches an OIDC identity token
|
|
5
|
+
* for zero-secret authentication with the Better i18n API.
|
|
6
|
+
*
|
|
7
|
+
* How it works:
|
|
8
|
+
* 1. GitHub Actions injects ACTIONS_ID_TOKEN_REQUEST_URL + TOKEN
|
|
9
|
+
* 2. We request a JWT from that URL with our audience ("better-i18n")
|
|
10
|
+
* 3. The JWT is signed by GitHub's JWKS — API can verify without shared secrets
|
|
11
|
+
*
|
|
12
|
+
* References:
|
|
13
|
+
* - https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_AUDIENCE = "better-i18n";
|
|
16
|
+
/**
|
|
17
|
+
* Check if running in GitHub Actions with OIDC support.
|
|
18
|
+
*
|
|
19
|
+
* OIDC requires:
|
|
20
|
+
* - `GITHUB_ACTIONS=true` (running in GA)
|
|
21
|
+
* - `ACTIONS_ID_TOKEN_REQUEST_URL` (OIDC endpoint available)
|
|
22
|
+
* - `ACTIONS_ID_TOKEN_REQUEST_TOKEN` (bearer token for the endpoint)
|
|
23
|
+
*
|
|
24
|
+
* The last two are only present when the workflow has `permissions: id-token: write`.
|
|
25
|
+
*/
|
|
26
|
+
export function isGitHubActionsWithOidc() {
|
|
27
|
+
return (process.env.GITHUB_ACTIONS === "true" &&
|
|
28
|
+
!!process.env.ACTIONS_ID_TOKEN_REQUEST_URL &&
|
|
29
|
+
!!process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Fetch a GitHub Actions OIDC token for the given audience.
|
|
33
|
+
*
|
|
34
|
+
* @param audience - Token audience claim (default: "better-i18n")
|
|
35
|
+
* @returns JWT string, or null if OIDC is unavailable or the request fails
|
|
36
|
+
*/
|
|
37
|
+
export async function fetchGitHubOidcToken(audience = DEFAULT_AUDIENCE) {
|
|
38
|
+
const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
|
39
|
+
const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
|
40
|
+
if (!requestUrl || !requestToken) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
// Append audience as query parameter
|
|
45
|
+
const url = new URL(requestUrl);
|
|
46
|
+
url.searchParams.set("audience", audience);
|
|
47
|
+
const response = await fetch(url.toString(), {
|
|
48
|
+
headers: {
|
|
49
|
+
Authorization: `Bearer ${requestToken}`,
|
|
50
|
+
Accept: "application/json; api-version=2.0",
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const data = (await response.json());
|
|
57
|
+
return data.value ?? null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=oidc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../src/context/oidc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAEvC;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,MAAM;QACrC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B;QAC1C,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAC7C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,gBAAgB;IAEnC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;IAC5D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC;IAEhE,IAAI,CAAC,UAAU,IAAI,CAAC,YAAY,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,qCAAqC;QACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;QAChC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE3C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,YAAY,EAAE;gBACvC,MAAM,EAAE,mCAAmC;aAC5C;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;QAC3D,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Doctor — Programmatic API
|
|
3
|
+
*
|
|
4
|
+
* diagnose() is the core entry point. It orchestrates:
|
|
5
|
+
* 1. Project context detection (i18n.config.ts)
|
|
6
|
+
* 2. Code analysis (AST-based key extraction)
|
|
7
|
+
* 3. Locale file discovery and loading
|
|
8
|
+
* 4. Health rule execution
|
|
9
|
+
* 5. Issue → Diagnostic adaptation
|
|
10
|
+
* 6. Health score calculation
|
|
11
|
+
*
|
|
12
|
+
* Separation from the CLI command follows react-doctor's
|
|
13
|
+
* diagnose() vs scan() pattern: pure data vs UI concerns.
|
|
14
|
+
*/
|
|
15
|
+
import type { I18nDiagnostic } from "../rules/registry.js";
|
|
16
|
+
import { type HealthScore } from "./score.js";
|
|
17
|
+
export interface DoctorOptions {
|
|
18
|
+
/** Directory to scan (default: cwd) */
|
|
19
|
+
dir?: string;
|
|
20
|
+
/** Skip AST code analysis */
|
|
21
|
+
skipCode?: boolean;
|
|
22
|
+
/** Skip translation file health checks */
|
|
23
|
+
skipHealth?: boolean;
|
|
24
|
+
/** Verbose logging */
|
|
25
|
+
verbose?: boolean;
|
|
26
|
+
/** Pass threshold for health score (default: 70) */
|
|
27
|
+
passThreshold?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface DoctorReport {
|
|
30
|
+
/** ISO timestamp of when the report was generated */
|
|
31
|
+
runAt: string;
|
|
32
|
+
/** Total duration in milliseconds */
|
|
33
|
+
durationMs: number;
|
|
34
|
+
/** Git information (if available) */
|
|
35
|
+
git?: {
|
|
36
|
+
commit?: string;
|
|
37
|
+
ref?: string;
|
|
38
|
+
actor?: string;
|
|
39
|
+
repository?: string;
|
|
40
|
+
repositoryId?: string;
|
|
41
|
+
};
|
|
42
|
+
/** All diagnostics from all sources */
|
|
43
|
+
diagnostics: I18nDiagnostic[];
|
|
44
|
+
/** Health score calculation */
|
|
45
|
+
score: HealthScore;
|
|
46
|
+
/** Summary statistics */
|
|
47
|
+
summary: {
|
|
48
|
+
total: number;
|
|
49
|
+
errors: number;
|
|
50
|
+
warnings: number;
|
|
51
|
+
infos: number;
|
|
52
|
+
byCategory: Record<string, number>;
|
|
53
|
+
filesScanned: number;
|
|
54
|
+
keysChecked: number;
|
|
55
|
+
localesChecked: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run i18n Doctor analysis on a directory.
|
|
60
|
+
*
|
|
61
|
+
* This is the pure data API — no console output, no spinners.
|
|
62
|
+
* Use DoctorReport with a reporter for formatted output.
|
|
63
|
+
*/
|
|
64
|
+
export declare function diagnose(directory: string, options?: DoctorOptions): Promise<DoctorReport>;
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doctor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH,OAAO,KAAK,EAEV,cAAc,EACf,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAIpE,MAAM,WAAW,aAAa;IAC5B,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sBAAsB;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,GAAG,CAAC,EAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,uCAAuC;IACvC,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,+BAA+B;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,yBAAyB;IACzB,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAID;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CA8EvB"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Doctor — Programmatic API
|
|
3
|
+
*
|
|
4
|
+
* diagnose() is the core entry point. It orchestrates:
|
|
5
|
+
* 1. Project context detection (i18n.config.ts)
|
|
6
|
+
* 2. Code analysis (AST-based key extraction)
|
|
7
|
+
* 3. Locale file discovery and loading
|
|
8
|
+
* 4. Health rule execution
|
|
9
|
+
* 5. Issue → Diagnostic adaptation
|
|
10
|
+
* 6. Health score calculation
|
|
11
|
+
*
|
|
12
|
+
* Separation from the CLI command follows react-doctor's
|
|
13
|
+
* diagnose() vs scan() pattern: pure data vs UI concerns.
|
|
14
|
+
*/
|
|
15
|
+
import { execFileSync } from "node:child_process";
|
|
16
|
+
import { relative } from "node:path";
|
|
17
|
+
import { collectFiles } from "../analyzer/file-collector.js";
|
|
18
|
+
import { analyzeFile } from "../analyzer/index.js";
|
|
19
|
+
import { detectProjectContext } from "../context/detector.js";
|
|
20
|
+
import { RULE_CATEGORY_MAP, RULE_HELP_MAP } from "../rules/categories.js";
|
|
21
|
+
import { healthRules } from "../rules/health/index.js";
|
|
22
|
+
import { flattenToRecord } from "../utils/json-keys.js";
|
|
23
|
+
import { fetchLocaleFile, fetchManifest } from "../utils/cdn-client.js";
|
|
24
|
+
import { discoverLocaleFiles, loadLocaleTranslations, } from "./project-discovery.js";
|
|
25
|
+
import { calculateHealthScore } from "./score.js";
|
|
26
|
+
// ── Main API ─────────────────────────────────────────────────────────
|
|
27
|
+
/**
|
|
28
|
+
* Run i18n Doctor analysis on a directory.
|
|
29
|
+
*
|
|
30
|
+
* This is the pure data API — no console output, no spinners.
|
|
31
|
+
* Use DoctorReport with a reporter for formatted output.
|
|
32
|
+
*/
|
|
33
|
+
export async function diagnose(directory, options = {}) {
|
|
34
|
+
const startTime = Date.now();
|
|
35
|
+
const diagnostics = [];
|
|
36
|
+
// 1. Detect project context
|
|
37
|
+
const context = await detectProjectContext(directory, false);
|
|
38
|
+
const lintConfig = context?.lint;
|
|
39
|
+
// 2. Code analysis (AST-based key extraction + hardcoded string detection)
|
|
40
|
+
let filesScanned = 0;
|
|
41
|
+
const codeKeys = new Set();
|
|
42
|
+
if (!options.skipCode) {
|
|
43
|
+
const { codeDiagnostics, keys, fileCount } = await runCodeAnalysis(directory, lintConfig, options.verbose);
|
|
44
|
+
diagnostics.push(...codeDiagnostics);
|
|
45
|
+
for (const key of keys)
|
|
46
|
+
codeKeys.add(key);
|
|
47
|
+
filesScanned = fileCount;
|
|
48
|
+
}
|
|
49
|
+
// 3. Locale file discovery and health rule execution
|
|
50
|
+
let keysChecked = 0;
|
|
51
|
+
let localesChecked = 0;
|
|
52
|
+
if (!options.skipHealth) {
|
|
53
|
+
const healthResult = await runHealthAnalysis(directory, context, codeKeys, options.verbose);
|
|
54
|
+
diagnostics.push(...healthResult.diagnostics);
|
|
55
|
+
keysChecked = healthResult.keysChecked;
|
|
56
|
+
localesChecked = healthResult.localesChecked;
|
|
57
|
+
}
|
|
58
|
+
// 4. Calculate score
|
|
59
|
+
const score = calculateHealthScore(diagnostics, options.passThreshold ?? 70);
|
|
60
|
+
// 5. Build summary
|
|
61
|
+
const byCategory = {};
|
|
62
|
+
let errors = 0;
|
|
63
|
+
let warnings = 0;
|
|
64
|
+
let infos = 0;
|
|
65
|
+
for (const d of diagnostics) {
|
|
66
|
+
byCategory[d.category] = (byCategory[d.category] || 0) + 1;
|
|
67
|
+
if (d.severity === "error")
|
|
68
|
+
errors++;
|
|
69
|
+
else if (d.severity === "warning")
|
|
70
|
+
warnings++;
|
|
71
|
+
else
|
|
72
|
+
infos++;
|
|
73
|
+
}
|
|
74
|
+
// 6. Try to get git info
|
|
75
|
+
const git = getGitInfo();
|
|
76
|
+
return {
|
|
77
|
+
runAt: new Date().toISOString(),
|
|
78
|
+
durationMs: Date.now() - startTime,
|
|
79
|
+
git,
|
|
80
|
+
diagnostics,
|
|
81
|
+
score,
|
|
82
|
+
summary: {
|
|
83
|
+
total: diagnostics.length,
|
|
84
|
+
errors,
|
|
85
|
+
warnings,
|
|
86
|
+
infos,
|
|
87
|
+
byCategory,
|
|
88
|
+
filesScanned,
|
|
89
|
+
keysChecked,
|
|
90
|
+
localesChecked,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// ── Code Analysis Pipeline ──────────────────────────────────────────
|
|
95
|
+
async function runCodeAnalysis(directory, lintConfig, verbose) {
|
|
96
|
+
const files = await collectFiles({ rootDir: directory });
|
|
97
|
+
const codeDiagnostics = [];
|
|
98
|
+
const keys = [];
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
try {
|
|
101
|
+
const { issues } = await analyzeFile(file, lintConfig, verbose);
|
|
102
|
+
for (const issue of issues) {
|
|
103
|
+
// Extract translation keys
|
|
104
|
+
if (issue.severity === "info" && issue.key) {
|
|
105
|
+
keys.push(issue.key);
|
|
106
|
+
}
|
|
107
|
+
// Convert hardcoded string warnings to diagnostics
|
|
108
|
+
if (issue.severity === "warning") {
|
|
109
|
+
codeDiagnostics.push(adaptIssueToDiagnostic(issue, directory));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Skip files that can't be analyzed
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { codeDiagnostics, keys, fileCount: files.length };
|
|
118
|
+
}
|
|
119
|
+
// ── Health Analysis Pipeline ────────────────────────────────────────
|
|
120
|
+
async function runHealthAnalysis(directory, context, codeKeys, verbose) {
|
|
121
|
+
// Try to load translations
|
|
122
|
+
let translations = {};
|
|
123
|
+
let sourceLocale = context?.defaultLocale || "en";
|
|
124
|
+
let targetLocales = [];
|
|
125
|
+
// Strategy 1: CDN (if config exists)
|
|
126
|
+
if (context?.cdnBaseUrl) {
|
|
127
|
+
const cdnResult = await loadFromCdn(context, verbose);
|
|
128
|
+
if (cdnResult) {
|
|
129
|
+
translations = cdnResult.translations;
|
|
130
|
+
sourceLocale = cdnResult.sourceLocale;
|
|
131
|
+
targetLocales = cdnResult.targetLocales;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Strategy 2: Local files (if CDN not available or didn't work)
|
|
135
|
+
if (Object.keys(translations).length === 0) {
|
|
136
|
+
const discovery = discoverLocaleFiles(directory);
|
|
137
|
+
if (discovery) {
|
|
138
|
+
translations = loadLocaleTranslations(discovery);
|
|
139
|
+
// Infer source locale
|
|
140
|
+
if (translations[sourceLocale]) {
|
|
141
|
+
targetLocales = discovery.locales.filter((l) => l !== sourceLocale);
|
|
142
|
+
}
|
|
143
|
+
else if (discovery.locales.length > 0) {
|
|
144
|
+
sourceLocale = discovery.locales[0];
|
|
145
|
+
targetLocales = discovery.locales.slice(1);
|
|
146
|
+
}
|
|
147
|
+
if (verbose) {
|
|
148
|
+
console.log(`[VERBOSE] Discovered ${discovery.locales.length} locales in ${discovery.localeDir} (${discovery.pattern})`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (Object.keys(translations).length === 0) {
|
|
153
|
+
return { diagnostics: [], keysChecked: 0, localesChecked: 0 };
|
|
154
|
+
}
|
|
155
|
+
// Run health rules
|
|
156
|
+
const ruleContext = {
|
|
157
|
+
sourceLocale,
|
|
158
|
+
targetLocales,
|
|
159
|
+
translations,
|
|
160
|
+
codeKeys,
|
|
161
|
+
rootDir: directory,
|
|
162
|
+
projectContext: context,
|
|
163
|
+
};
|
|
164
|
+
const diagnostics = [];
|
|
165
|
+
for (const rule of healthRules) {
|
|
166
|
+
diagnostics.push(...rule.run(ruleContext));
|
|
167
|
+
}
|
|
168
|
+
const keysChecked = Object.keys(translations[sourceLocale] || {}).length;
|
|
169
|
+
const localesChecked = targetLocales.length + 1; // +1 for source
|
|
170
|
+
return { diagnostics, keysChecked, localesChecked };
|
|
171
|
+
}
|
|
172
|
+
// ── CDN Loading ─────────────────────────────────────────────────────
|
|
173
|
+
async function loadFromCdn(context, verbose) {
|
|
174
|
+
try {
|
|
175
|
+
const cdnBaseUrl = context.cdnBaseUrl || "https://cdn.better-i18n.com";
|
|
176
|
+
const manifest = await fetchManifest(cdnBaseUrl, context.workspaceId, context.projectSlug);
|
|
177
|
+
const sourceLanguage = manifest.languages.find((l) => l.isSource);
|
|
178
|
+
const sourceLocale = sourceLanguage?.code || context.defaultLocale || "en";
|
|
179
|
+
const targetLocales = manifest.languages
|
|
180
|
+
.filter((l) => !l.isSource)
|
|
181
|
+
.map((l) => l.code);
|
|
182
|
+
const translations = {};
|
|
183
|
+
// Fetch all locales
|
|
184
|
+
const allLocales = [sourceLocale, ...targetLocales];
|
|
185
|
+
for (const locale of allLocales) {
|
|
186
|
+
const data = await fetchLocaleFile(cdnBaseUrl, context.workspaceId, context.projectSlug, locale);
|
|
187
|
+
if (data) {
|
|
188
|
+
translations[locale] = flattenToRecord(data);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (verbose) {
|
|
192
|
+
console.log(`[VERBOSE] Loaded ${allLocales.length} locales from CDN`);
|
|
193
|
+
}
|
|
194
|
+
return { translations, sourceLocale, targetLocales };
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ── Issue → Diagnostic Adapter ──────────────────────────────────────
|
|
201
|
+
/**
|
|
202
|
+
* Convert legacy analyzer Issue to I18nDiagnostic.
|
|
203
|
+
*
|
|
204
|
+
* Maps issue.type to rule ID, looks up category and help from central maps.
|
|
205
|
+
*/
|
|
206
|
+
function adaptIssueToDiagnostic(issue, rootDir) {
|
|
207
|
+
const ruleId = mapIssueTypeToRuleId(issue);
|
|
208
|
+
return {
|
|
209
|
+
filePath: relative(rootDir, issue.file),
|
|
210
|
+
line: issue.line,
|
|
211
|
+
column: issue.column,
|
|
212
|
+
rule: ruleId,
|
|
213
|
+
category: RULE_CATEGORY_MAP[ruleId] || "Code",
|
|
214
|
+
severity: issue.severity === "info" ? "info" : issue.severity,
|
|
215
|
+
message: issue.message,
|
|
216
|
+
help: RULE_HELP_MAP[ruleId] || "",
|
|
217
|
+
key: issue.key,
|
|
218
|
+
namespace: issue.namespace,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function mapIssueTypeToRuleId(issue) {
|
|
222
|
+
const msg = issue.message.toLowerCase();
|
|
223
|
+
if (msg.includes("jsx text") || issue.type === "jsx-text")
|
|
224
|
+
return "jsx-text";
|
|
225
|
+
if (msg.includes("jsx attribute") || issue.type === "jsx-attribute")
|
|
226
|
+
return "jsx-attribute";
|
|
227
|
+
if (msg.includes("ternary") || issue.type === "ternary-locale")
|
|
228
|
+
return "ternary-locale";
|
|
229
|
+
if (msg.includes("toast") || issue.type === "toast-message")
|
|
230
|
+
return "toast-message";
|
|
231
|
+
return "string-variable";
|
|
232
|
+
}
|
|
233
|
+
// ── Git Info ────────────────────────────────────────────────────────
|
|
234
|
+
function getGitInfo() {
|
|
235
|
+
try {
|
|
236
|
+
const commit = execFileSync("git", ["rev-parse", "HEAD"], {
|
|
237
|
+
encoding: "utf-8",
|
|
238
|
+
}).trim();
|
|
239
|
+
const ref = execFileSync("git", ["symbolic-ref", "--short", "HEAD"], { encoding: "utf-8" }).trim();
|
|
240
|
+
return {
|
|
241
|
+
commit,
|
|
242
|
+
ref,
|
|
243
|
+
// GitHub Actions env vars
|
|
244
|
+
actor: process.env.GITHUB_ACTOR,
|
|
245
|
+
repository: process.env.GITHUB_REPOSITORY,
|
|
246
|
+
repositoryId: process.env.GITHUB_REPOSITORY_ID,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/doctor/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale File Discovery
|
|
3
|
+
*
|
|
4
|
+
* Automatically discovers translation files in a project directory.
|
|
5
|
+
* Supports multiple directory conventions and file patterns.
|
|
6
|
+
*
|
|
7
|
+
* Discovery strategy:
|
|
8
|
+
* 1. If CDN base URL exists in config → fetch from CDN (handled elsewhere)
|
|
9
|
+
* 2. Search common local directories: locales/, messages/, i18n/, public/locales/
|
|
10
|
+
* 3. Detect file pattern: en.json (flat) vs en/common.json (namespaced)
|
|
11
|
+
*/
|
|
12
|
+
/** Result of locale file discovery */
|
|
13
|
+
export interface LocaleDiscoveryResult {
|
|
14
|
+
/** Root directory containing locale files */
|
|
15
|
+
localeDir: string;
|
|
16
|
+
/** Detected file pattern */
|
|
17
|
+
pattern: "flat" | "namespaced";
|
|
18
|
+
/** Map of locale code → file paths */
|
|
19
|
+
localeFiles: Record<string, string[]>;
|
|
20
|
+
/** All detected locale codes */
|
|
21
|
+
locales: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Discover locale files in a project directory.
|
|
25
|
+
*
|
|
26
|
+
* Scans well-known directories for JSON translation files and
|
|
27
|
+
* determines the file organization pattern (flat vs namespaced).
|
|
28
|
+
*/
|
|
29
|
+
export declare function discoverLocaleFiles(rootDir: string): LocaleDiscoveryResult | null;
|
|
30
|
+
/**
|
|
31
|
+
* Load all translation files for discovered locales.
|
|
32
|
+
*
|
|
33
|
+
* Returns a merged flat key→value map per locale.
|
|
34
|
+
* For namespaced files, prefixes keys with the filename (sans extension).
|
|
35
|
+
*/
|
|
36
|
+
export declare function loadLocaleTranslations(discovery: LocaleDiscoveryResult): Record<string, Record<string, string>>;
|
|
37
|
+
//# sourceMappingURL=project-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-discovery.d.ts","sourceRoot":"","sources":["../../src/doctor/project-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqBH,sCAAsC;AACtC,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC;IAC/B,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,gCAAgC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,GACd,qBAAqB,GAAG,IAAI,CAkE9B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,qBAAqB,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA0BxC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale File Discovery
|
|
3
|
+
*
|
|
4
|
+
* Automatically discovers translation files in a project directory.
|
|
5
|
+
* Supports multiple directory conventions and file patterns.
|
|
6
|
+
*
|
|
7
|
+
* Discovery strategy:
|
|
8
|
+
* 1. If CDN base URL exists in config → fetch from CDN (handled elsewhere)
|
|
9
|
+
* 2. Search common local directories: locales/, messages/, i18n/, public/locales/
|
|
10
|
+
* 3. Detect file pattern: en.json (flat) vs en/common.json (namespaced)
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
13
|
+
import { basename, extname, join, resolve } from "node:path";
|
|
14
|
+
/** Well-known directories that commonly contain locale files */
|
|
15
|
+
const LOCALE_DIRS = [
|
|
16
|
+
"locales",
|
|
17
|
+
"locale",
|
|
18
|
+
"messages",
|
|
19
|
+
"i18n",
|
|
20
|
+
"translations",
|
|
21
|
+
"lang",
|
|
22
|
+
"public/locales",
|
|
23
|
+
"public/messages",
|
|
24
|
+
"src/locales",
|
|
25
|
+
"src/messages",
|
|
26
|
+
"src/i18n",
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Discover locale files in a project directory.
|
|
30
|
+
*
|
|
31
|
+
* Scans well-known directories for JSON translation files and
|
|
32
|
+
* determines the file organization pattern (flat vs namespaced).
|
|
33
|
+
*/
|
|
34
|
+
export function discoverLocaleFiles(rootDir) {
|
|
35
|
+
const absRoot = resolve(rootDir);
|
|
36
|
+
for (const dir of LOCALE_DIRS) {
|
|
37
|
+
const candidate = join(absRoot, dir);
|
|
38
|
+
if (!existsSync(candidate) || !statSync(candidate).isDirectory())
|
|
39
|
+
continue;
|
|
40
|
+
const entries = readdirSync(candidate);
|
|
41
|
+
// Pattern 1: Flat files — en.json, tr.json, de.json
|
|
42
|
+
const jsonFiles = entries.filter((e) => extname(e) === ".json" && !e.startsWith("."));
|
|
43
|
+
if (jsonFiles.length > 0) {
|
|
44
|
+
const localeFiles = {};
|
|
45
|
+
for (const file of jsonFiles) {
|
|
46
|
+
const locale = basename(file, ".json");
|
|
47
|
+
if (isLikelyLocaleCode(locale)) {
|
|
48
|
+
localeFiles[locale] = [join(candidate, file)];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (Object.keys(localeFiles).length >= 1) {
|
|
52
|
+
return {
|
|
53
|
+
localeDir: candidate,
|
|
54
|
+
pattern: "flat",
|
|
55
|
+
localeFiles,
|
|
56
|
+
locales: Object.keys(localeFiles).sort(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Pattern 2: Namespaced dirs — en/common.json, en/auth.json
|
|
61
|
+
const subdirs = entries.filter((e) => {
|
|
62
|
+
const fullPath = join(candidate, e);
|
|
63
|
+
return (statSync(fullPath).isDirectory() &&
|
|
64
|
+
isLikelyLocaleCode(e) &&
|
|
65
|
+
!e.startsWith("."));
|
|
66
|
+
});
|
|
67
|
+
if (subdirs.length >= 1) {
|
|
68
|
+
const localeFiles = {};
|
|
69
|
+
for (const sub of subdirs) {
|
|
70
|
+
const subPath = join(candidate, sub);
|
|
71
|
+
const nsFiles = readdirSync(subPath).filter((f) => extname(f) === ".json");
|
|
72
|
+
if (nsFiles.length > 0) {
|
|
73
|
+
localeFiles[sub] = nsFiles.map((f) => join(subPath, f));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (Object.keys(localeFiles).length >= 1) {
|
|
77
|
+
return {
|
|
78
|
+
localeDir: candidate,
|
|
79
|
+
pattern: "namespaced",
|
|
80
|
+
localeFiles,
|
|
81
|
+
locales: Object.keys(localeFiles).sort(),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Load all translation files for discovered locales.
|
|
90
|
+
*
|
|
91
|
+
* Returns a merged flat key→value map per locale.
|
|
92
|
+
* For namespaced files, prefixes keys with the filename (sans extension).
|
|
93
|
+
*/
|
|
94
|
+
export function loadLocaleTranslations(discovery) {
|
|
95
|
+
const result = {};
|
|
96
|
+
for (const [locale, files] of Object.entries(discovery.localeFiles)) {
|
|
97
|
+
const merged = {};
|
|
98
|
+
for (const filePath of files) {
|
|
99
|
+
try {
|
|
100
|
+
const raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
101
|
+
if (discovery.pattern === "namespaced") {
|
|
102
|
+
// Prefix keys with namespace (filename without extension)
|
|
103
|
+
const namespace = basename(filePath, ".json");
|
|
104
|
+
flattenWithPrefix(raw, namespace, merged);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
flattenWithPrefix(raw, "", merged);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Skip malformed JSON files
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
result[locale] = merged;
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Flatten nested JSON object into a flat key→value map with optional prefix.
|
|
120
|
+
*/
|
|
121
|
+
function flattenWithPrefix(obj, prefix, target) {
|
|
122
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
123
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
124
|
+
if (typeof value === "string") {
|
|
125
|
+
target[fullKey] = value;
|
|
126
|
+
}
|
|
127
|
+
else if (typeof value === "object" &&
|
|
128
|
+
value !== null &&
|
|
129
|
+
!Array.isArray(value)) {
|
|
130
|
+
flattenWithPrefix(value, fullKey, target);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if a string looks like a locale code (e.g., "en", "tr", "zh-Hans", "pt-BR").
|
|
136
|
+
*/
|
|
137
|
+
function isLikelyLocaleCode(str) {
|
|
138
|
+
return /^[a-z]{2}(-[a-zA-Z]{2,4})?$/.test(str);
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=project-discovery.js.map
|