ai-localize-config 2.0.1 → 2.0.3
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/CHANGELOG.md +18 -0
- package/dist/index.d.mts +41 -3
- package/dist/index.d.ts +41 -3
- package/dist/index.js +72 -8
- package/dist/index.mjs +72 -8
- package/package.json +2 -2
- package/src/loader.ts +61 -29
- package/src/schema.ts +38 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# ai-localize-config
|
|
2
2
|
|
|
3
|
+
## 2.0.3
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- **`keyStyle` Zod schema field** — `ConfigSchema` now validates `keyStyle: "path" | "screaming_snake"` (default `"path"`); controls whether locale keys are hierarchical dot-notation or UPPER_SNAKE_CASE derived from the text value
|
|
8
|
+
- **`staticKeys` Zod schema field** — `ConfigSchema` now validates `staticKeys: Record<string, string>` (default `{}`); entries are injected into every locale file at extract/full-migrate time
|
|
9
|
+
|
|
10
|
+
## 2.0.2
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- **`CodemodConfigSchema` added** — Zod schema extended with `codemods` field (accessorStyle, translationFunction, namespace) and `localeStructure` field; both fields are now properly validated and surfaced in config loading
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- ai-localize-shared@2.0.2
|
|
20
|
+
|
|
3
21
|
## 2.0.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -64,9 +64,9 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
64
64
|
*/
|
|
65
65
|
namespace: z.ZodOptional<z.ZodString>;
|
|
66
66
|
/**
|
|
67
|
-
|
|
67
|
+
* How the translation accessor is written in generated code.
|
|
68
68
|
* "function" (default) — t('key')
|
|
69
|
-
|
|
69
|
+
* "bracket" — t['key']
|
|
70
70
|
*/
|
|
71
71
|
accessorStyle: z.ZodOptional<z.ZodEnum<["function", "bracket"]>>;
|
|
72
72
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -87,9 +87,43 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
87
87
|
* "nested" (default) — one file per language + namespace:
|
|
88
88
|
* locales/en/common.json, locales/fr/dashboard.json
|
|
89
89
|
* "flat" — one file per language, all keys merged:
|
|
90
|
-
*
|
|
90
|
+
* locales/en.json, locales/fr.json
|
|
91
91
|
*/
|
|
92
92
|
localeStructure: z.ZodDefault<z.ZodEnum<["nested", "flat"]>>;
|
|
93
|
+
/**
|
|
94
|
+
* Controls the format of generated locale keys for hardcoded text found
|
|
95
|
+
* during scanning.
|
|
96
|
+
*
|
|
97
|
+
* "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
98
|
+
* src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
99
|
+
* → "settings.settings_page.save_changes"
|
|
100
|
+
*
|
|
101
|
+
* "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
102
|
+
* The key equals the value converted to UPPER_SNAKE_CASE:
|
|
103
|
+
* "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
104
|
+
* "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
105
|
+
* This matches the pattern used by staticKeys and is ideal for
|
|
106
|
+
* projects that use constant-style i18n keys.
|
|
107
|
+
*/
|
|
108
|
+
keyStyle: z.ZodDefault<z.ZodEnum<["path", "screaming_snake"]>>;
|
|
109
|
+
/**
|
|
110
|
+
* Static locale keys to inject into every generated locale file.
|
|
111
|
+
* These key/value pairs are merged with scanned keys and written to all
|
|
112
|
+
* languages (defaultLanguage + targetLanguages).
|
|
113
|
+
*
|
|
114
|
+
* Example:
|
|
115
|
+
* "staticKeys": {
|
|
116
|
+
* "MAX_COUNT": "Max Count",
|
|
117
|
+
* "ALLOWED": "Allowed",
|
|
118
|
+
* "DISABLED": "Disabled",
|
|
119
|
+
* "UNLIMITED": "Unlimited"
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* In nested mode these keys are placed in the default namespace file
|
|
123
|
+
* (translation.json / common.json). In flat mode they are merged at the
|
|
124
|
+
* top level of the single per-language file.
|
|
125
|
+
*/
|
|
126
|
+
staticKeys: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
93
127
|
}, "strip", z.ZodTypeAny, {
|
|
94
128
|
framework: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown";
|
|
95
129
|
defaultLanguage: string;
|
|
@@ -101,6 +135,7 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
101
135
|
cacheDir: string;
|
|
102
136
|
plugins: string[];
|
|
103
137
|
localeStructure: "flat" | "nested";
|
|
138
|
+
keyStyle: "path" | "screaming_snake";
|
|
104
139
|
keyPrefix?: string | undefined;
|
|
105
140
|
namespaces?: string[] | undefined;
|
|
106
141
|
includePatterns?: string[] | undefined;
|
|
@@ -120,6 +155,7 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
120
155
|
namespace?: string | undefined;
|
|
121
156
|
accessorStyle?: "function" | "bracket" | undefined;
|
|
122
157
|
} | undefined;
|
|
158
|
+
staticKeys?: Record<string, string> | undefined;
|
|
123
159
|
}, {
|
|
124
160
|
framework?: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown" | undefined;
|
|
125
161
|
defaultLanguage?: string | undefined;
|
|
@@ -150,6 +186,8 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
150
186
|
accessorStyle?: "function" | "bracket" | undefined;
|
|
151
187
|
} | undefined;
|
|
152
188
|
localeStructure?: "flat" | "nested" | undefined;
|
|
189
|
+
keyStyle?: "path" | "screaming_snake" | undefined;
|
|
190
|
+
staticKeys?: Record<string, string> | undefined;
|
|
153
191
|
}>;
|
|
154
192
|
type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
|
|
155
193
|
type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
|
package/dist/index.d.ts
CHANGED
|
@@ -64,9 +64,9 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
64
64
|
*/
|
|
65
65
|
namespace: z.ZodOptional<z.ZodString>;
|
|
66
66
|
/**
|
|
67
|
-
|
|
67
|
+
* How the translation accessor is written in generated code.
|
|
68
68
|
* "function" (default) — t('key')
|
|
69
|
-
|
|
69
|
+
* "bracket" — t['key']
|
|
70
70
|
*/
|
|
71
71
|
accessorStyle: z.ZodOptional<z.ZodEnum<["function", "bracket"]>>;
|
|
72
72
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -87,9 +87,43 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
87
87
|
* "nested" (default) — one file per language + namespace:
|
|
88
88
|
* locales/en/common.json, locales/fr/dashboard.json
|
|
89
89
|
* "flat" — one file per language, all keys merged:
|
|
90
|
-
*
|
|
90
|
+
* locales/en.json, locales/fr.json
|
|
91
91
|
*/
|
|
92
92
|
localeStructure: z.ZodDefault<z.ZodEnum<["nested", "flat"]>>;
|
|
93
|
+
/**
|
|
94
|
+
* Controls the format of generated locale keys for hardcoded text found
|
|
95
|
+
* during scanning.
|
|
96
|
+
*
|
|
97
|
+
* "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
98
|
+
* src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
99
|
+
* → "settings.settings_page.save_changes"
|
|
100
|
+
*
|
|
101
|
+
* "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
102
|
+
* The key equals the value converted to UPPER_SNAKE_CASE:
|
|
103
|
+
* "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
104
|
+
* "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
105
|
+
* This matches the pattern used by staticKeys and is ideal for
|
|
106
|
+
* projects that use constant-style i18n keys.
|
|
107
|
+
*/
|
|
108
|
+
keyStyle: z.ZodDefault<z.ZodEnum<["path", "screaming_snake"]>>;
|
|
109
|
+
/**
|
|
110
|
+
* Static locale keys to inject into every generated locale file.
|
|
111
|
+
* These key/value pairs are merged with scanned keys and written to all
|
|
112
|
+
* languages (defaultLanguage + targetLanguages).
|
|
113
|
+
*
|
|
114
|
+
* Example:
|
|
115
|
+
* "staticKeys": {
|
|
116
|
+
* "MAX_COUNT": "Max Count",
|
|
117
|
+
* "ALLOWED": "Allowed",
|
|
118
|
+
* "DISABLED": "Disabled",
|
|
119
|
+
* "UNLIMITED": "Unlimited"
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* In nested mode these keys are placed in the default namespace file
|
|
123
|
+
* (translation.json / common.json). In flat mode they are merged at the
|
|
124
|
+
* top level of the single per-language file.
|
|
125
|
+
*/
|
|
126
|
+
staticKeys: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
93
127
|
}, "strip", z.ZodTypeAny, {
|
|
94
128
|
framework: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown";
|
|
95
129
|
defaultLanguage: string;
|
|
@@ -101,6 +135,7 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
101
135
|
cacheDir: string;
|
|
102
136
|
plugins: string[];
|
|
103
137
|
localeStructure: "flat" | "nested";
|
|
138
|
+
keyStyle: "path" | "screaming_snake";
|
|
104
139
|
keyPrefix?: string | undefined;
|
|
105
140
|
namespaces?: string[] | undefined;
|
|
106
141
|
includePatterns?: string[] | undefined;
|
|
@@ -120,6 +155,7 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
120
155
|
namespace?: string | undefined;
|
|
121
156
|
accessorStyle?: "function" | "bracket" | undefined;
|
|
122
157
|
} | undefined;
|
|
158
|
+
staticKeys?: Record<string, string> | undefined;
|
|
123
159
|
}, {
|
|
124
160
|
framework?: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown" | undefined;
|
|
125
161
|
defaultLanguage?: string | undefined;
|
|
@@ -150,6 +186,8 @@ declare const LocalizationConfigSchema: z.ZodObject<{
|
|
|
150
186
|
accessorStyle?: "function" | "bracket" | undefined;
|
|
151
187
|
} | undefined;
|
|
152
188
|
localeStructure?: "flat" | "nested" | undefined;
|
|
189
|
+
keyStyle?: "path" | "screaming_snake" | undefined;
|
|
190
|
+
staticKeys?: Record<string, string> | undefined;
|
|
153
191
|
}>;
|
|
154
192
|
type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
|
|
155
193
|
type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
|
package/dist/index.js
CHANGED
|
@@ -87,10 +87,10 @@ var CodemodConfigSchema = import_zod.z.object({
|
|
|
87
87
|
*/
|
|
88
88
|
namespace: import_zod.z.string().optional(),
|
|
89
89
|
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
* How the translation accessor is written in generated code.
|
|
91
|
+
* "function" (default) — t('key')
|
|
92
|
+
* "bracket" — t['key']
|
|
93
|
+
*/
|
|
94
94
|
accessorStyle: import_zod.z.enum(["function", "bracket"]).optional()
|
|
95
95
|
});
|
|
96
96
|
var LocalizationConfigSchema = import_zod.z.object({
|
|
@@ -114,9 +114,43 @@ var LocalizationConfigSchema = import_zod.z.object({
|
|
|
114
114
|
* "nested" (default) — one file per language + namespace:
|
|
115
115
|
* locales/en/common.json, locales/fr/dashboard.json
|
|
116
116
|
* "flat" — one file per language, all keys merged:
|
|
117
|
-
*
|
|
117
|
+
* locales/en.json, locales/fr.json
|
|
118
118
|
*/
|
|
119
|
-
localeStructure: import_zod.z.enum(["nested", "flat"]).default("nested")
|
|
119
|
+
localeStructure: import_zod.z.enum(["nested", "flat"]).default("nested"),
|
|
120
|
+
/**
|
|
121
|
+
* Controls the format of generated locale keys for hardcoded text found
|
|
122
|
+
* during scanning.
|
|
123
|
+
*
|
|
124
|
+
* "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
125
|
+
* src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
126
|
+
* → "settings.settings_page.save_changes"
|
|
127
|
+
*
|
|
128
|
+
* "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
129
|
+
* The key equals the value converted to UPPER_SNAKE_CASE:
|
|
130
|
+
* "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
131
|
+
* "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
132
|
+
* This matches the pattern used by staticKeys and is ideal for
|
|
133
|
+
* projects that use constant-style i18n keys.
|
|
134
|
+
*/
|
|
135
|
+
keyStyle: import_zod.z.enum(["path", "screaming_snake"]).default("path"),
|
|
136
|
+
/**
|
|
137
|
+
* Static locale keys to inject into every generated locale file.
|
|
138
|
+
* These key/value pairs are merged with scanned keys and written to all
|
|
139
|
+
* languages (defaultLanguage + targetLanguages).
|
|
140
|
+
*
|
|
141
|
+
* Example:
|
|
142
|
+
* "staticKeys": {
|
|
143
|
+
* "MAX_COUNT": "Max Count",
|
|
144
|
+
* "ALLOWED": "Allowed",
|
|
145
|
+
* "DISABLED": "Disabled",
|
|
146
|
+
* "UNLIMITED": "Unlimited"
|
|
147
|
+
* }
|
|
148
|
+
*
|
|
149
|
+
* In nested mode these keys are placed in the default namespace file
|
|
150
|
+
* (translation.json / common.json). In flat mode they are merged at the
|
|
151
|
+
* top level of the single per-language file.
|
|
152
|
+
*/
|
|
153
|
+
staticKeys: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
|
|
120
154
|
});
|
|
121
155
|
|
|
122
156
|
// src/loader.ts
|
|
@@ -180,7 +214,7 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
180
214
|
// ── Framework ────────────────────────────────────────────────────────────
|
|
181
215
|
// Detected automatically by ai-localize init.
|
|
182
216
|
// Values: "react" | "react-cra" | "react-vite" | "react-nextjs"
|
|
183
|
-
//
|
|
217
|
+
// | "angular" | "angular-ngx" | "angular-i18n"
|
|
184
218
|
// | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown"
|
|
185
219
|
framework,
|
|
186
220
|
// ── Languages ────────────────────────────────────────────────────────────
|
|
@@ -201,6 +235,20 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
201
235
|
// "flat": one file per language, all keys merged
|
|
202
236
|
// locales/en.json (non-default namespace keys are prefixed: "dashboard.header.title")
|
|
203
237
|
localeStructure: "nested",
|
|
238
|
+
// ── Key style ────────────────────────────────────────────────────────────
|
|
239
|
+
// Controls the format of locale keys generated for scanned hardcoded text.
|
|
240
|
+
//
|
|
241
|
+
// "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
242
|
+
// src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
243
|
+
// → "settings.settings_page.save_changes"
|
|
244
|
+
//
|
|
245
|
+
// "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
246
|
+
// The key equals the value converted to UPPER_SNAKE_CASE:
|
|
247
|
+
// "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
248
|
+
// "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
249
|
+
// This mirrors the staticKeys pattern and is ideal for projects that
|
|
250
|
+
// prefer constant-style i18n keys.
|
|
251
|
+
keyStyle: "path",
|
|
204
252
|
// ── Scan filtering ───────────────────────────────────────────────────────
|
|
205
253
|
// Directories/patterns to skip during scanning.
|
|
206
254
|
ignorePatterns: ["node_modules", "dist", ".git", "coverage"],
|
|
@@ -215,6 +263,22 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
215
263
|
// Optional explicit list of namespace names.
|
|
216
264
|
// If omitted, namespace is derived from the first path segment after sourceDir.
|
|
217
265
|
namespaces: [],
|
|
266
|
+
// ── Static keys ──────────────────────────────────────────────────────────
|
|
267
|
+
// Key/value pairs to inject into every generated locale file regardless of
|
|
268
|
+
// what the scanner finds. Useful for enum labels, status codes, and other
|
|
269
|
+
// constant strings that are not hardcoded in source files.
|
|
270
|
+
//
|
|
271
|
+
// In nested mode these keys land in the default namespace file
|
|
272
|
+
// (translation.json). In flat mode they are merged at the top level.
|
|
273
|
+
//
|
|
274
|
+
// Example:
|
|
275
|
+
// "staticKeys": {
|
|
276
|
+
// "MAX_COUNT": "Max Count",
|
|
277
|
+
// "ALLOWED": "Allowed",
|
|
278
|
+
// "DISABLED": "Disabled",
|
|
279
|
+
// "UNLIMITED": "Unlimited"
|
|
280
|
+
// }
|
|
281
|
+
staticKeys: {},
|
|
218
282
|
// ── Incremental scanning ─────────────────────────────────────────────────
|
|
219
283
|
// Cache file hashes between runs — only re-scan changed files.
|
|
220
284
|
incrementalCache: true,
|
|
@@ -238,7 +302,7 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
238
302
|
//
|
|
239
303
|
// accessorStyle:
|
|
240
304
|
// "function" (default) → t('key')
|
|
241
|
-
// "bracket"
|
|
305
|
+
// "bracket" → t['key']
|
|
242
306
|
codemods: {
|
|
243
307
|
importPackage: "react-i18next",
|
|
244
308
|
hookName: "useTranslation",
|
package/dist/index.mjs
CHANGED
|
@@ -48,10 +48,10 @@ var CodemodConfigSchema = z.object({
|
|
|
48
48
|
*/
|
|
49
49
|
namespace: z.string().optional(),
|
|
50
50
|
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
* How the translation accessor is written in generated code.
|
|
52
|
+
* "function" (default) — t('key')
|
|
53
|
+
* "bracket" — t['key']
|
|
54
|
+
*/
|
|
55
55
|
accessorStyle: z.enum(["function", "bracket"]).optional()
|
|
56
56
|
});
|
|
57
57
|
var LocalizationConfigSchema = z.object({
|
|
@@ -75,9 +75,43 @@ var LocalizationConfigSchema = z.object({
|
|
|
75
75
|
* "nested" (default) — one file per language + namespace:
|
|
76
76
|
* locales/en/common.json, locales/fr/dashboard.json
|
|
77
77
|
* "flat" — one file per language, all keys merged:
|
|
78
|
-
*
|
|
78
|
+
* locales/en.json, locales/fr.json
|
|
79
79
|
*/
|
|
80
|
-
localeStructure: z.enum(["nested", "flat"]).default("nested")
|
|
80
|
+
localeStructure: z.enum(["nested", "flat"]).default("nested"),
|
|
81
|
+
/**
|
|
82
|
+
* Controls the format of generated locale keys for hardcoded text found
|
|
83
|
+
* during scanning.
|
|
84
|
+
*
|
|
85
|
+
* "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
86
|
+
* src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
87
|
+
* → "settings.settings_page.save_changes"
|
|
88
|
+
*
|
|
89
|
+
* "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
90
|
+
* The key equals the value converted to UPPER_SNAKE_CASE:
|
|
91
|
+
* "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
92
|
+
* "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
93
|
+
* This matches the pattern used by staticKeys and is ideal for
|
|
94
|
+
* projects that use constant-style i18n keys.
|
|
95
|
+
*/
|
|
96
|
+
keyStyle: z.enum(["path", "screaming_snake"]).default("path"),
|
|
97
|
+
/**
|
|
98
|
+
* Static locale keys to inject into every generated locale file.
|
|
99
|
+
* These key/value pairs are merged with scanned keys and written to all
|
|
100
|
+
* languages (defaultLanguage + targetLanguages).
|
|
101
|
+
*
|
|
102
|
+
* Example:
|
|
103
|
+
* "staticKeys": {
|
|
104
|
+
* "MAX_COUNT": "Max Count",
|
|
105
|
+
* "ALLOWED": "Allowed",
|
|
106
|
+
* "DISABLED": "Disabled",
|
|
107
|
+
* "UNLIMITED": "Unlimited"
|
|
108
|
+
* }
|
|
109
|
+
*
|
|
110
|
+
* In nested mode these keys are placed in the default namespace file
|
|
111
|
+
* (translation.json / common.json). In flat mode they are merged at the
|
|
112
|
+
* top level of the single per-language file.
|
|
113
|
+
*/
|
|
114
|
+
staticKeys: z.record(z.string(), z.string()).optional()
|
|
81
115
|
});
|
|
82
116
|
|
|
83
117
|
// src/loader.ts
|
|
@@ -141,7 +175,7 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
141
175
|
// ── Framework ────────────────────────────────────────────────────────────
|
|
142
176
|
// Detected automatically by ai-localize init.
|
|
143
177
|
// Values: "react" | "react-cra" | "react-vite" | "react-nextjs"
|
|
144
|
-
//
|
|
178
|
+
// | "angular" | "angular-ngx" | "angular-i18n"
|
|
145
179
|
// | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown"
|
|
146
180
|
framework,
|
|
147
181
|
// ── Languages ────────────────────────────────────────────────────────────
|
|
@@ -162,6 +196,20 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
162
196
|
// "flat": one file per language, all keys merged
|
|
163
197
|
// locales/en.json (non-default namespace keys are prefixed: "dashboard.header.title")
|
|
164
198
|
localeStructure: "nested",
|
|
199
|
+
// ── Key style ────────────────────────────────────────────────────────────
|
|
200
|
+
// Controls the format of locale keys generated for scanned hardcoded text.
|
|
201
|
+
//
|
|
202
|
+
// "path" (default) — hierarchical dot-notation derived from file path + text:
|
|
203
|
+
// src/pages/settings/SettingsPage.tsx + "Save Changes"
|
|
204
|
+
// → "settings.settings_page.save_changes"
|
|
205
|
+
//
|
|
206
|
+
// "screaming_snake" — UPPER_SNAKE_CASE derived solely from the text value.
|
|
207
|
+
// The key equals the value converted to UPPER_SNAKE_CASE:
|
|
208
|
+
// "Save Changes" → key: "SAVE_CHANGES", value: "Save Changes"
|
|
209
|
+
// "Max Count" → key: "MAX_COUNT", value: "Max Count"
|
|
210
|
+
// This mirrors the staticKeys pattern and is ideal for projects that
|
|
211
|
+
// prefer constant-style i18n keys.
|
|
212
|
+
keyStyle: "path",
|
|
165
213
|
// ── Scan filtering ───────────────────────────────────────────────────────
|
|
166
214
|
// Directories/patterns to skip during scanning.
|
|
167
215
|
ignorePatterns: ["node_modules", "dist", ".git", "coverage"],
|
|
@@ -176,6 +224,22 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
176
224
|
// Optional explicit list of namespace names.
|
|
177
225
|
// If omitted, namespace is derived from the first path segment after sourceDir.
|
|
178
226
|
namespaces: [],
|
|
227
|
+
// ── Static keys ──────────────────────────────────────────────────────────
|
|
228
|
+
// Key/value pairs to inject into every generated locale file regardless of
|
|
229
|
+
// what the scanner finds. Useful for enum labels, status codes, and other
|
|
230
|
+
// constant strings that are not hardcoded in source files.
|
|
231
|
+
//
|
|
232
|
+
// In nested mode these keys land in the default namespace file
|
|
233
|
+
// (translation.json). In flat mode they are merged at the top level.
|
|
234
|
+
//
|
|
235
|
+
// Example:
|
|
236
|
+
// "staticKeys": {
|
|
237
|
+
// "MAX_COUNT": "Max Count",
|
|
238
|
+
// "ALLOWED": "Allowed",
|
|
239
|
+
// "DISABLED": "Disabled",
|
|
240
|
+
// "UNLIMITED": "Unlimited"
|
|
241
|
+
// }
|
|
242
|
+
staticKeys: {},
|
|
179
243
|
// ── Incremental scanning ─────────────────────────────────────────────────
|
|
180
244
|
// Cache file hashes between runs — only re-scan changed files.
|
|
181
245
|
incrementalCache: true,
|
|
@@ -199,7 +263,7 @@ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
|
|
|
199
263
|
//
|
|
200
264
|
// accessorStyle:
|
|
201
265
|
// "function" (default) → t('key')
|
|
202
|
-
// "bracket"
|
|
266
|
+
// "bracket" → t['key']
|
|
203
267
|
codemods: {
|
|
204
268
|
importPackage: "react-i18next",
|
|
205
269
|
hookName: "useTranslation",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-localize-config",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Configuration loader and schema validator for ai-localize-core",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"zod": "^3.22.4",
|
|
17
17
|
"cosmiconfig": "^9.0.0",
|
|
18
18
|
"dotenv": "^16.4.1",
|
|
19
|
-
"ai-localize-shared": "2.0.
|
|
19
|
+
"ai-localize-shared": "2.0.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsup": "^8.0.1",
|
package/src/loader.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { CONFIG_FILE_NAME } from 'ai-localize-shared';
|
|
|
8
8
|
import { LocalizationConfigSchema } from './schema.js';
|
|
9
9
|
|
|
10
10
|
export interface ConfigLoadResult {
|
|
11
|
-
config: LocalizationConfig;
|
|
11
|
+
config: LocalizationConfig;
|
|
12
12
|
filePath: string | null;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -27,12 +27,12 @@ export async function loadConfig(
|
|
|
27
27
|
const explorer = cosmiconfig('ai-localize', {
|
|
28
28
|
searchPlaces: [
|
|
29
29
|
CONFIG_FILE_NAME,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
'ai-localize.config.js',
|
|
31
|
+
'ai-localize.config.ts',
|
|
32
|
+
'.ai-localizerc',
|
|
33
33
|
'.ai-localizerc.json',
|
|
34
34
|
'.ai-localizerc.js',
|
|
35
|
-
|
|
35
|
+
'package.json',
|
|
36
36
|
],
|
|
37
37
|
packageProp: 'aiLocalize',
|
|
38
38
|
});
|
|
@@ -77,7 +77,7 @@ function extractAwsFromEnv(): Record<string, string> | null {
|
|
|
77
77
|
bucket,
|
|
78
78
|
distributionId,
|
|
79
79
|
region: process.env.AWS_REGION || process.env.AI_LOCALIZE_AWS_REGION || 'us-east-1',
|
|
80
|
-
|
|
80
|
+
cdnBaseUrl: process.env.AI_LOCALIZE_CDN_BASE_URL || '',
|
|
81
81
|
legacyCdnPattern: process.env.AI_LOCALIZE_LEGACY_CDN_PATTERN || '',
|
|
82
82
|
profile: process.env.AWS_PROFILE || '',
|
|
83
83
|
};
|
|
@@ -97,15 +97,15 @@ export function writeDefaultConfig(cwd = process.cwd(), framework = 'unknown'):
|
|
|
97
97
|
// ── Framework ────────────────────────────────────────────────────────────
|
|
98
98
|
// Detected automatically by ai-localize init.
|
|
99
99
|
// Values: "react" | "react-cra" | "react-vite" | "react-nextjs"
|
|
100
|
-
//
|
|
100
|
+
// | "angular" | "angular-ngx" | "angular-i18n"
|
|
101
101
|
// | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown"
|
|
102
102
|
framework,
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
// ── Languages ────────────────────────────────────────────────────────────
|
|
105
105
|
// Source language — used as reference for missing-translation detection.
|
|
106
106
|
defaultLanguage: 'en',
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
// Additional languages to generate locale files for.
|
|
109
109
|
// All target files are seeded with the source value (no blank entries).
|
|
110
110
|
targetLanguages: ['fr', 'de'],
|
|
111
111
|
|
|
@@ -124,11 +124,26 @@ export function writeDefaultConfig(cwd = process.cwd(), framework = 'unknown'):
|
|
|
124
124
|
// locales/en.json (non-default namespace keys are prefixed: "dashboard.header.title")
|
|
125
125
|
localeStructure: 'nested',
|
|
126
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
|
+
|
|
127
142
|
// ── Scan filtering ───────────────────────────────────────────────────────
|
|
128
|
-
|
|
143
|
+
// Directories/patterns to skip during scanning.
|
|
129
144
|
ignorePatterns: ['node_modules', 'dist', '.git', 'coverage'],
|
|
130
145
|
|
|
131
|
-
|
|
146
|
+
// Optional: only scan files matching these glob patterns.
|
|
132
147
|
// If empty/omitted, all files under sourceDir are scanned.
|
|
133
148
|
// Example: ["src/components/**/*.tsx", "src/pages/**/*.tsx"]
|
|
134
149
|
includePatterns: [],
|
|
@@ -140,38 +155,55 @@ export function writeDefaultConfig(cwd = process.cwd(), framework = 'unknown'):
|
|
|
140
155
|
|
|
141
156
|
// Optional explicit list of namespace names.
|
|
142
157
|
// If omitted, namespace is derived from the first path segment after sourceDir.
|
|
143
|
-
|
|
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: {},
|
|
144
176
|
|
|
145
177
|
// ── Incremental scanning ─────────────────────────────────────────────────
|
|
146
178
|
// Cache file hashes between runs — only re-scan changed files.
|
|
147
|
-
|
|
179
|
+
incrementalCache: true,
|
|
148
180
|
cacheDir: '.ai-localize-cache',
|
|
149
181
|
|
|
150
182
|
// ── Codemod behaviour ────────────────────────────────────────────────────
|
|
151
183
|
// Controls what the codemod injects when wrapping hardcoded strings.
|
|
152
184
|
//
|
|
153
|
-
|
|
185
|
+
// importPackage: npm package name OR project-relative path to the hook file.
|
|
154
186
|
// npm package: "react-i18next"
|
|
155
187
|
// local hook path: "src/hooks/useTranslation" (path relative to project root)
|
|
156
188
|
// The codemod computes the correct relative import path per file automatically.
|
|
157
189
|
//
|
|
158
190
|
// hookName: the hook function to import and call.
|
|
159
|
-
|
|
191
|
+
// Default: "useTranslation"
|
|
160
192
|
//
|
|
161
|
-
|
|
193
|
+
// translationFunction: the accessor destructured from the hook.
|
|
162
194
|
// Default: "t"
|
|
163
195
|
//
|
|
164
|
-
|
|
196
|
+
// namespace: passed as argument to the hook, e.g. useTranslation("common").
|
|
165
197
|
// Leave empty to omit the argument.
|
|
166
198
|
//
|
|
167
|
-
|
|
199
|
+
// accessorStyle:
|
|
168
200
|
// "function" (default) → t('key')
|
|
169
|
-
// "bracket"
|
|
201
|
+
// "bracket" → t['key']
|
|
170
202
|
codemods: {
|
|
171
203
|
importPackage: 'react-i18next',
|
|
172
204
|
hookName: 'useTranslation',
|
|
173
205
|
translationFunction: 't',
|
|
174
|
-
|
|
206
|
+
namespace: '',
|
|
175
207
|
accessorStyle: 'function',
|
|
176
208
|
},
|
|
177
209
|
|
|
@@ -179,14 +211,14 @@ export function writeDefaultConfig(cwd = process.cwd(), framework = 'unknown'):
|
|
|
179
211
|
// Required only for CDN migration commands (migrate-cdn, upload-assets, replace-cdn).
|
|
180
212
|
// Leave all fields empty if not using CDN migration.
|
|
181
213
|
// Credentials can also be supplied via environment variables (see docs).
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
214
|
+
aws: {
|
|
215
|
+
region: 'us-east-1',
|
|
216
|
+
bucket: '',
|
|
185
217
|
distributionId: '',
|
|
186
218
|
cdnBaseUrl: '',
|
|
187
219
|
legacyCdnPattern: '',
|
|
188
220
|
assetsPrefix: 'assets',
|
|
189
|
-
|
|
221
|
+
profile: '',
|
|
190
222
|
},
|
|
191
223
|
|
|
192
224
|
// ── Plugins ──────────────────────────────────────────────────────────────
|
|
@@ -205,19 +237,19 @@ export function validateConfig(configPath: string): {
|
|
|
205
237
|
} {
|
|
206
238
|
try {
|
|
207
239
|
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
208
|
-
|
|
209
|
-
|
|
240
|
+
const parsed = JSON.parse(raw);
|
|
241
|
+
LocalizationConfigSchema.parse(parsed);
|
|
210
242
|
return { valid: true, errors: [] };
|
|
211
243
|
} catch (err: unknown) {
|
|
212
244
|
if (err && typeof err === 'object' && 'errors' in err) {
|
|
213
|
-
|
|
214
|
-
|
|
245
|
+
const zodError = err as { errors: Array<{ path: unknown[]; message: string }> };
|
|
246
|
+
return {
|
|
215
247
|
valid: false,
|
|
216
248
|
errors: zodError.errors.map((e) => `${e.path.join('.')}: ${e.message}`),
|
|
217
249
|
};
|
|
218
250
|
}
|
|
219
251
|
return {
|
|
220
|
-
|
|
252
|
+
valid: false,
|
|
221
253
|
errors: [err instanceof Error ? err.message : String(err)],
|
|
222
254
|
};
|
|
223
255
|
}
|
package/src/schema.ts
CHANGED
|
@@ -54,9 +54,9 @@ const CodemodConfigSchema = z.object({
|
|
|
54
54
|
*/
|
|
55
55
|
namespace: z.string().optional(),
|
|
56
56
|
/**
|
|
57
|
-
|
|
57
|
+
* How the translation accessor is written in generated code.
|
|
58
58
|
* "function" (default) — t('key')
|
|
59
|
-
|
|
59
|
+
* "bracket" — t['key']
|
|
60
60
|
*/
|
|
61
61
|
accessorStyle: z.enum(['function', 'bracket']).optional(),
|
|
62
62
|
});
|
|
@@ -64,7 +64,7 @@ const CodemodConfigSchema = z.object({
|
|
|
64
64
|
export const LocalizationConfigSchema = z.object({
|
|
65
65
|
framework: FrameworkSchema.default('unknown'),
|
|
66
66
|
defaultLanguage: z.string().default('en'),
|
|
67
|
-
|
|
67
|
+
targetLanguages: z.array(z.string()).default([]),
|
|
68
68
|
sourceDir: z.string().default('src'),
|
|
69
69
|
localesDir: z.string().default('locales'),
|
|
70
70
|
keyPrefix: z.string().optional(),
|
|
@@ -84,9 +84,43 @@ export const LocalizationConfigSchema = z.object({
|
|
|
84
84
|
* "nested" (default) — one file per language + namespace:
|
|
85
85
|
* locales/en/common.json, locales/fr/dashboard.json
|
|
86
86
|
* "flat" — one file per language, all keys merged:
|
|
87
|
-
*
|
|
87
|
+
* locales/en.json, locales/fr.json
|
|
88
88
|
*/
|
|
89
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(),
|
|
90
124
|
});
|
|
91
125
|
|
|
92
126
|
export type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
|