ai-localize-config 2.0.3 → 2.0.4
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/package.json +21 -2
- package/src/index.ts +0 -2
- package/src/loader.ts +0 -256
- package/src/schema.ts +0 -127
- package/tsconfig.json +0 -9
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-localize-config",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Configuration loader and schema validator for ai-localize-core",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md",
|
|
11
|
+
"CHANGELOG.md"
|
|
12
|
+
],
|
|
8
13
|
"exports": {
|
|
9
14
|
".": {
|
|
10
15
|
"types": "./dist/index.d.ts",
|
|
@@ -12,11 +17,25 @@
|
|
|
12
17
|
"require": "./dist/index.js"
|
|
13
18
|
}
|
|
14
19
|
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"i18n",
|
|
22
|
+
"localization",
|
|
23
|
+
"l10n",
|
|
24
|
+
"internationalization",
|
|
25
|
+
"ai-localize",
|
|
26
|
+
"config",
|
|
27
|
+
"configuration",
|
|
28
|
+
"zod",
|
|
29
|
+
"cosmiconfig"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
15
34
|
"dependencies": {
|
|
16
35
|
"zod": "^3.22.4",
|
|
17
36
|
"cosmiconfig": "^9.0.0",
|
|
18
37
|
"dotenv": "^16.4.1",
|
|
19
|
-
"ai-localize-shared": "2.0.
|
|
38
|
+
"ai-localize-shared": "2.0.4"
|
|
20
39
|
},
|
|
21
40
|
"devDependencies": {
|
|
22
41
|
"tsup": "^8.0.1",
|
package/src/index.ts
DELETED
package/src/loader.ts
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import { cosmiconfig } from 'cosmiconfig';
|
|
4
|
-
import * as dotenv from 'dotenv';
|
|
5
|
-
|
|
6
|
-
import type { LocalizationConfig } from 'ai-localize-shared';
|
|
7
|
-
import { CONFIG_FILE_NAME } from 'ai-localize-shared';
|
|
8
|
-
import { LocalizationConfigSchema } from './schema.js';
|
|
9
|
-
|
|
10
|
-
export interface ConfigLoadResult {
|
|
11
|
-
config: LocalizationConfig;
|
|
12
|
-
filePath: string | null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Loads and validates the ai-localize configuration.
|
|
17
|
-
* Searches for config in standard locations using cosmiconfig.
|
|
18
|
-
*/
|
|
19
|
-
export async function loadConfig(
|
|
20
|
-
cwd = process.cwd(),
|
|
21
|
-
overrides: Partial<LocalizationConfig> = {}
|
|
22
|
-
): Promise<ConfigLoadResult> {
|
|
23
|
-
// Load .env files for AWS credentials etc.
|
|
24
|
-
dotenv.config({ path: path.join(cwd, '.env') });
|
|
25
|
-
dotenv.config({ path: path.join(cwd, '.env.local') });
|
|
26
|
-
|
|
27
|
-
const explorer = cosmiconfig('ai-localize', {
|
|
28
|
-
searchPlaces: [
|
|
29
|
-
CONFIG_FILE_NAME,
|
|
30
|
-
'ai-localize.config.js',
|
|
31
|
-
'ai-localize.config.ts',
|
|
32
|
-
'.ai-localizerc',
|
|
33
|
-
'.ai-localizerc.json',
|
|
34
|
-
'.ai-localizerc.js',
|
|
35
|
-
'package.json',
|
|
36
|
-
],
|
|
37
|
-
packageProp: 'aiLocalize',
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
let rawConfig: Record<string, unknown> = {};
|
|
41
|
-
let filePath: string | null = null;
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const result = await explorer.search(cwd);
|
|
45
|
-
if (result) {
|
|
46
|
-
rawConfig = result.config as Record<string, unknown>;
|
|
47
|
-
filePath = result.filepath;
|
|
48
|
-
}
|
|
49
|
-
} catch (err) {
|
|
50
|
-
// Config not found — use defaults
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Merge env vars for AWS
|
|
54
|
-
const awsFromEnv = extractAwsFromEnv();
|
|
55
|
-
if (awsFromEnv && !rawConfig.aws) {
|
|
56
|
-
rawConfig.aws = awsFromEnv;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const merged = { ...rawConfig, ...overrides };
|
|
60
|
-
const validated = LocalizationConfigSchema.parse(merged);
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
config: validated as LocalizationConfig,
|
|
64
|
-
filePath,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Extract AWS configuration from environment variables */
|
|
69
|
-
function extractAwsFromEnv(): Record<string, string> | null {
|
|
70
|
-
const bucket = process.env.AI_LOCALIZE_S3_BUCKET || process.env.AWS_S3_BUCKET;
|
|
71
|
-
const distributionId =
|
|
72
|
-
process.env.AI_LOCALIZE_CF_DISTRIBUTION_ID || process.env.AWS_CF_DISTRIBUTION_ID;
|
|
73
|
-
|
|
74
|
-
if (!bucket || !distributionId) return null;
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
bucket,
|
|
78
|
-
distributionId,
|
|
79
|
-
region: process.env.AWS_REGION || process.env.AI_LOCALIZE_AWS_REGION || 'us-east-1',
|
|
80
|
-
cdnBaseUrl: process.env.AI_LOCALIZE_CDN_BASE_URL || '',
|
|
81
|
-
legacyCdnPattern: process.env.AI_LOCALIZE_LEGACY_CDN_PATTERN || '',
|
|
82
|
-
profile: process.env.AWS_PROFILE || '',
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Writes a fully-populated default config file to the project root.
|
|
88
|
-
*
|
|
89
|
-
* Every supported configuration field is included so users can see all
|
|
90
|
-
* available options and edit them directly. Fields that are optional/unused
|
|
91
|
-
* are set to their default values or left as empty strings / empty arrays.
|
|
92
|
-
*/
|
|
93
|
-
export function writeDefaultConfig(cwd = process.cwd(), framework = 'unknown'): string {
|
|
94
|
-
const configPath = path.join(cwd, CONFIG_FILE_NAME);
|
|
95
|
-
|
|
96
|
-
const defaultConfig = {
|
|
97
|
-
// ── Framework ────────────────────────────────────────────────────────────
|
|
98
|
-
// Detected automatically by ai-localize init.
|
|
99
|
-
// Values: "react" | "react-cra" | "react-vite" | "react-nextjs"
|
|
100
|
-
// | "angular" | "angular-ngx" | "angular-i18n"
|
|
101
|
-
// | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown"
|
|
102
|
-
framework,
|
|
103
|
-
|
|
104
|
-
// ── Languages ────────────────────────────────────────────────────────────
|
|
105
|
-
// Source language — used as reference for missing-translation detection.
|
|
106
|
-
defaultLanguage: 'en',
|
|
107
|
-
|
|
108
|
-
// Additional languages to generate locale files for.
|
|
109
|
-
// All target files are seeded with the source value (no blank entries).
|
|
110
|
-
targetLanguages: ['fr', 'de'],
|
|
111
|
-
|
|
112
|
-
// ── Directories ──────────────────────────────────────────────────────────
|
|
113
|
-
// Root of your source code — scanning starts here.
|
|
114
|
-
// Keys are generated relative to this directory.
|
|
115
|
-
sourceDir: 'src',
|
|
116
|
-
|
|
117
|
-
// Where locale JSON files are written.
|
|
118
|
-
localesDir: 'locales',
|
|
119
|
-
|
|
120
|
-
// ── Locale file layout ───────────────────────────────────────────────────
|
|
121
|
-
// "nested" (default): one file per language + namespace
|
|
122
|
-
// locales/en/common.json, locales/en/dashboard.json
|
|
123
|
-
// "flat": one file per language, all keys merged
|
|
124
|
-
// locales/en.json (non-default namespace keys are prefixed: "dashboard.header.title")
|
|
125
|
-
localeStructure: 'nested',
|
|
126
|
-
|
|
127
|
-
// ── Key style ────────────────────────────────────────────────────────────
|
|
128
|
-
// Controls the format of locale keys generated for scanned hardcoded text.
|
|
129
|
-
//
|
|
130
|
-
// "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
131
|
-
// src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
132
|
-
// → "settings.settings_page.save_changes"
|
|
133
|
-
//
|
|
134
|
-
// "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
135
|
-
// The key equals the value converted to UPPER_SNAKE_CASE:
|
|
136
|
-
// "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
137
|
-
// "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
138
|
-
// This mirrors the staticKeys pattern and is ideal for projects that
|
|
139
|
-
// prefer constant-style i18n keys.
|
|
140
|
-
keyStyle: 'path',
|
|
141
|
-
|
|
142
|
-
// ── Scan filtering ───────────────────────────────────────────────────────
|
|
143
|
-
// Directories/patterns to skip during scanning.
|
|
144
|
-
ignorePatterns: ['node_modules', 'dist', '.git', 'coverage'],
|
|
145
|
-
|
|
146
|
-
// Optional: only scan files matching these glob patterns.
|
|
147
|
-
// If empty/omitted, all files under sourceDir are scanned.
|
|
148
|
-
// Example: ["src/components/**/*.tsx", "src/pages/**/*.tsx"]
|
|
149
|
-
includePatterns: [],
|
|
150
|
-
|
|
151
|
-
// ── Key generation ───────────────────────────────────────────────────────
|
|
152
|
-
// Optional prefix prepended to every generated key.
|
|
153
|
-
// Example: "myapp" → "myapp.components.button.save"
|
|
154
|
-
keyPrefix: '',
|
|
155
|
-
|
|
156
|
-
// Optional explicit list of namespace names.
|
|
157
|
-
// If omitted, namespace is derived from the first path segment after sourceDir.
|
|
158
|
-
namespaces: [],
|
|
159
|
-
|
|
160
|
-
// ── Static keys ──────────────────────────────────────────────────────────
|
|
161
|
-
// Key/value pairs to inject into every generated locale file regardless of
|
|
162
|
-
// what the scanner finds. Useful for enum labels, status codes, and other
|
|
163
|
-
// constant strings that are not hardcoded in source files.
|
|
164
|
-
//
|
|
165
|
-
// In nested mode these keys land in the default namespace file
|
|
166
|
-
// (translation.json). In flat mode they are merged at the top level.
|
|
167
|
-
//
|
|
168
|
-
// Example:
|
|
169
|
-
// "staticKeys": {
|
|
170
|
-
// "MAX_COUNT": "Max Count",
|
|
171
|
-
// "ALLOWED": "Allowed",
|
|
172
|
-
// "DISABLED": "Disabled",
|
|
173
|
-
// "UNLIMITED": "Unlimited"
|
|
174
|
-
// }
|
|
175
|
-
staticKeys: {},
|
|
176
|
-
|
|
177
|
-
// ── Incremental scanning ─────────────────────────────────────────────────
|
|
178
|
-
// Cache file hashes between runs — only re-scan changed files.
|
|
179
|
-
incrementalCache: true,
|
|
180
|
-
cacheDir: '.ai-localize-cache',
|
|
181
|
-
|
|
182
|
-
// ── Codemod behaviour ────────────────────────────────────────────────────
|
|
183
|
-
// Controls what the codemod injects when wrapping hardcoded strings.
|
|
184
|
-
//
|
|
185
|
-
// importPackage: npm package name OR project-relative path to the hook file.
|
|
186
|
-
// npm package: "react-i18next"
|
|
187
|
-
// local hook path: "src/hooks/useTranslation" (path relative to project root)
|
|
188
|
-
// The codemod computes the correct relative import path per file automatically.
|
|
189
|
-
//
|
|
190
|
-
// hookName: the hook function to import and call.
|
|
191
|
-
// Default: "useTranslation"
|
|
192
|
-
//
|
|
193
|
-
// translationFunction: the accessor destructured from the hook.
|
|
194
|
-
// Default: "t"
|
|
195
|
-
//
|
|
196
|
-
// namespace: passed as argument to the hook, e.g. useTranslation("common").
|
|
197
|
-
// Leave empty to omit the argument.
|
|
198
|
-
//
|
|
199
|
-
// accessorStyle:
|
|
200
|
-
// "function" (default) → t('key')
|
|
201
|
-
// "bracket" → t['key']
|
|
202
|
-
codemods: {
|
|
203
|
-
importPackage: 'react-i18next',
|
|
204
|
-
hookName: 'useTranslation',
|
|
205
|
-
translationFunction: 't',
|
|
206
|
-
namespace: '',
|
|
207
|
-
accessorStyle: 'function',
|
|
208
|
-
},
|
|
209
|
-
|
|
210
|
-
// ── AWS / CloudFront (optional) ──────────────────────────────────────────
|
|
211
|
-
// Required only for CDN migration commands (migrate-cdn, upload-assets, replace-cdn).
|
|
212
|
-
// Leave all fields empty if not using CDN migration.
|
|
213
|
-
// Credentials can also be supplied via environment variables (see docs).
|
|
214
|
-
aws: {
|
|
215
|
-
region: 'us-east-1',
|
|
216
|
-
bucket: '',
|
|
217
|
-
distributionId: '',
|
|
218
|
-
cdnBaseUrl: '',
|
|
219
|
-
legacyCdnPattern: '',
|
|
220
|
-
assetsPrefix: 'assets',
|
|
221
|
-
profile: '',
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
// ── Plugins ──────────────────────────────────────────────────────────────
|
|
225
|
-
// Paths to custom plugin modules. See docs/PLUGIN_SYSTEM.md for details.
|
|
226
|
-
plugins: [],
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf-8');
|
|
230
|
-
return configPath;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/** Validate an existing config file */
|
|
234
|
-
export function validateConfig(configPath: string): {
|
|
235
|
-
valid: boolean;
|
|
236
|
-
errors: string[];
|
|
237
|
-
} {
|
|
238
|
-
try {
|
|
239
|
-
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
240
|
-
const parsed = JSON.parse(raw);
|
|
241
|
-
LocalizationConfigSchema.parse(parsed);
|
|
242
|
-
return { valid: true, errors: [] };
|
|
243
|
-
} catch (err: unknown) {
|
|
244
|
-
if (err && typeof err === 'object' && 'errors' in err) {
|
|
245
|
-
const zodError = err as { errors: Array<{ path: unknown[]; message: string }> };
|
|
246
|
-
return {
|
|
247
|
-
valid: false,
|
|
248
|
-
errors: zodError.errors.map((e) => `${e.path.join('.')}: ${e.message}`),
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
valid: false,
|
|
253
|
-
errors: [err instanceof Error ? err.message : String(err)],
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
}
|
package/src/schema.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
const AwsConfigSchema = z.object({
|
|
4
|
-
region: z.string().default('us-east-1'),
|
|
5
|
-
bucket: z.string().min(1, 'S3 bucket name is required'),
|
|
6
|
-
distributionId: z.string().min(1, 'CloudFront distribution ID is required'),
|
|
7
|
-
cdnBaseUrl: z.string().url().optional(),
|
|
8
|
-
legacyCdnPattern: z.string().optional(),
|
|
9
|
-
assetsPrefix: z.string().default('assets'),
|
|
10
|
-
profile: z.string().optional(),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const FrameworkSchema = z.enum([
|
|
14
|
-
'react',
|
|
15
|
-
'react-cra',
|
|
16
|
-
'react-vite',
|
|
17
|
-
'react-nextjs',
|
|
18
|
-
'angular',
|
|
19
|
-
'angular-ngx',
|
|
20
|
-
'angular-i18n',
|
|
21
|
-
'vue',
|
|
22
|
-
'vue-i18n',
|
|
23
|
-
'jquery',
|
|
24
|
-
'vanilla-js',
|
|
25
|
-
'jsp',
|
|
26
|
-
'unknown',
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Optional codemod behaviour overrides.
|
|
31
|
-
* Controls which i18n library / hook / pipe the codemod injects.
|
|
32
|
-
*/
|
|
33
|
-
const CodemodConfigSchema = z.object({
|
|
34
|
-
/**
|
|
35
|
-
* The npm package to import from.
|
|
36
|
-
* React default: "react-i18next"
|
|
37
|
-
* Vue default: "vue-i18n"
|
|
38
|
-
* Angular default: "@ngx-translate/core"
|
|
39
|
-
*/
|
|
40
|
-
importPackage: z.string().optional(),
|
|
41
|
-
/**
|
|
42
|
-
* The hook / function name to import and call.
|
|
43
|
-
* React default: "useTranslation"
|
|
44
|
-
* Vue default: "useI18n"
|
|
45
|
-
*/
|
|
46
|
-
hookName: z.string().optional(),
|
|
47
|
-
/**
|
|
48
|
-
* The translation function identifier returned by the hook (default: "t").
|
|
49
|
-
*/
|
|
50
|
-
translationFunction: z.string().optional(),
|
|
51
|
-
/**
|
|
52
|
-
* Namespace to pass as the first argument to the hook call.
|
|
53
|
-
* When omitted no namespace argument is injected.
|
|
54
|
-
*/
|
|
55
|
-
namespace: z.string().optional(),
|
|
56
|
-
/**
|
|
57
|
-
* How the translation accessor is written in generated code.
|
|
58
|
-
* "function" (default) — t('key')
|
|
59
|
-
* "bracket" — t['key']
|
|
60
|
-
*/
|
|
61
|
-
accessorStyle: z.enum(['function', 'bracket']).optional(),
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
export const LocalizationConfigSchema = z.object({
|
|
65
|
-
framework: FrameworkSchema.default('unknown'),
|
|
66
|
-
defaultLanguage: z.string().default('en'),
|
|
67
|
-
targetLanguages: z.array(z.string()).default([]),
|
|
68
|
-
sourceDir: z.string().default('src'),
|
|
69
|
-
localesDir: z.string().default('locales'),
|
|
70
|
-
keyPrefix: z.string().optional(),
|
|
71
|
-
namespaces: z.array(z.string()).optional(),
|
|
72
|
-
ignorePatterns: z
|
|
73
|
-
.array(z.string())
|
|
74
|
-
.default(['node_modules', 'dist', '.git', 'coverage']),
|
|
75
|
-
includePatterns: z.array(z.string()).optional(),
|
|
76
|
-
incrementalCache: z.boolean().default(true),
|
|
77
|
-
cacheDir: z.string().default('.ai-localize-cache'),
|
|
78
|
-
aws: AwsConfigSchema.optional().nullable(),
|
|
79
|
-
plugins: z.array(z.string()).default([]),
|
|
80
|
-
/** Codemod behaviour overrides — all fields are optional. */
|
|
81
|
-
codemods: CodemodConfigSchema.optional(),
|
|
82
|
-
/**
|
|
83
|
-
* Locale file layout.
|
|
84
|
-
* "nested" (default) — one file per language + namespace:
|
|
85
|
-
* locales/en/common.json, locales/fr/dashboard.json
|
|
86
|
-
* "flat" — one file per language, all keys merged:
|
|
87
|
-
* locales/en.json, locales/fr.json
|
|
88
|
-
*/
|
|
89
|
-
localeStructure: z.enum(['nested', 'flat']).default('nested'),
|
|
90
|
-
/**
|
|
91
|
-
* Controls the format of generated locale keys for hardcoded text found
|
|
92
|
-
* during scanning.
|
|
93
|
-
*
|
|
94
|
-
* "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
95
|
-
* src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
96
|
-
* → "settings.settings_page.save_changes"
|
|
97
|
-
*
|
|
98
|
-
* "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
99
|
-
* The key equals the value converted to UPPER_SNAKE_CASE:
|
|
100
|
-
* "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
101
|
-
* "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
102
|
-
* This matches the pattern used by staticKeys and is ideal for
|
|
103
|
-
* projects that use constant-style i18n keys.
|
|
104
|
-
*/
|
|
105
|
-
keyStyle: z.enum(['path', 'screaming_snake']).default('path'),
|
|
106
|
-
/**
|
|
107
|
-
* Static locale keys to inject into every generated locale file.
|
|
108
|
-
* These key/value pairs are merged with scanned keys and written to all
|
|
109
|
-
* languages (defaultLanguage + targetLanguages).
|
|
110
|
-
*
|
|
111
|
-
* Example:
|
|
112
|
-
* "staticKeys": {
|
|
113
|
-
* "MAX_COUNT": "Max Count",
|
|
114
|
-
* "ALLOWED": "Allowed",
|
|
115
|
-
* "DISABLED": "Disabled",
|
|
116
|
-
* "UNLIMITED": "Unlimited"
|
|
117
|
-
* }
|
|
118
|
-
*
|
|
119
|
-
* In nested mode these keys are placed in the default namespace file
|
|
120
|
-
* (translation.json / common.json). In flat mode they are merged at the
|
|
121
|
-
* top level of the single per-language file.
|
|
122
|
-
*/
|
|
123
|
-
staticKeys: z.record(z.string(), z.string()).optional(),
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
export type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
|
|
127
|
-
export type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
|