ai-localize-config 1.0.1 → 2.0.1

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 ADDED
@@ -0,0 +1,20 @@
1
+ # ai-localize-config
2
+
3
+ ## 2.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix config schema validation (aws optional/nullable, framework type), ignore className CSS values in AST scanner, add includePatterns config support for selective file scanning, add --extract-cdns flag to scan command to dump CDN URLs to a JSON file
8
+ - Updated dependencies
9
+ - ai-localize-shared@2.0.1
10
+
11
+ ## 2.0.0
12
+
13
+ ### Major Changes
14
+
15
+ - versoion change
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+ - ai-localize-shared@2.0.0
package/dist/index.d.mts CHANGED
@@ -10,9 +10,10 @@ declare const LocalizationConfigSchema: z.ZodObject<{
10
10
  keyPrefix: z.ZodOptional<z.ZodString>;
11
11
  namespaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
12
  ignorePatterns: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
13
+ includePatterns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
13
14
  incrementalCache: z.ZodDefault<z.ZodBoolean>;
14
15
  cacheDir: z.ZodDefault<z.ZodString>;
15
- aws: z.ZodOptional<z.ZodObject<{
16
+ aws: z.ZodNullable<z.ZodOptional<z.ZodObject<{
16
17
  region: z.ZodDefault<z.ZodString>;
17
18
  bucket: z.ZodString;
18
19
  distributionId: z.ZodString;
@@ -36,8 +37,59 @@ declare const LocalizationConfigSchema: z.ZodObject<{
36
37
  legacyCdnPattern?: string | undefined;
37
38
  assetsPrefix?: string | undefined;
38
39
  profile?: string | undefined;
39
- }>>;
40
+ }>>>;
40
41
  plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
42
+ /** Codemod behaviour overrides — all fields are optional. */
43
+ codemods: z.ZodOptional<z.ZodObject<{
44
+ /**
45
+ * The npm package to import from.
46
+ * React default: "react-i18next"
47
+ * Vue default: "vue-i18n"
48
+ * Angular default: "@ngx-translate/core"
49
+ */
50
+ importPackage: z.ZodOptional<z.ZodString>;
51
+ /**
52
+ * The hook / function name to import and call.
53
+ * React default: "useTranslation"
54
+ * Vue default: "useI18n"
55
+ */
56
+ hookName: z.ZodOptional<z.ZodString>;
57
+ /**
58
+ * The translation function identifier returned by the hook (default: "t").
59
+ */
60
+ translationFunction: z.ZodOptional<z.ZodString>;
61
+ /**
62
+ * Namespace to pass as the first argument to the hook call.
63
+ * When omitted no namespace argument is injected.
64
+ */
65
+ namespace: z.ZodOptional<z.ZodString>;
66
+ /**
67
+ * How the translation accessor is written in generated code.
68
+ * "function" (default) — t('key')
69
+ * "bracket" — t['key']
70
+ */
71
+ accessorStyle: z.ZodOptional<z.ZodEnum<["function", "bracket"]>>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ importPackage?: string | undefined;
74
+ hookName?: string | undefined;
75
+ translationFunction?: string | undefined;
76
+ namespace?: string | undefined;
77
+ accessorStyle?: "function" | "bracket" | undefined;
78
+ }, {
79
+ importPackage?: string | undefined;
80
+ hookName?: string | undefined;
81
+ translationFunction?: string | undefined;
82
+ namespace?: string | undefined;
83
+ accessorStyle?: "function" | "bracket" | undefined;
84
+ }>>;
85
+ /**
86
+ * Locale file layout.
87
+ * "nested" (default) — one file per language + namespace:
88
+ * locales/en/common.json, locales/fr/dashboard.json
89
+ * "flat" — one file per language, all keys merged:
90
+ * locales/en.json, locales/fr.json
91
+ */
92
+ localeStructure: z.ZodDefault<z.ZodEnum<["nested", "flat"]>>;
41
93
  }, "strip", z.ZodTypeAny, {
42
94
  framework: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown";
43
95
  defaultLanguage: string;
@@ -48,8 +100,10 @@ declare const LocalizationConfigSchema: z.ZodObject<{
48
100
  incrementalCache: boolean;
49
101
  cacheDir: string;
50
102
  plugins: string[];
103
+ localeStructure: "flat" | "nested";
51
104
  keyPrefix?: string | undefined;
52
105
  namespaces?: string[] | undefined;
106
+ includePatterns?: string[] | undefined;
53
107
  aws?: {
54
108
  region: string;
55
109
  bucket: string;
@@ -58,6 +112,13 @@ declare const LocalizationConfigSchema: z.ZodObject<{
58
112
  cdnBaseUrl?: string | undefined;
59
113
  legacyCdnPattern?: string | undefined;
60
114
  profile?: string | undefined;
115
+ } | null | undefined;
116
+ codemods?: {
117
+ importPackage?: string | undefined;
118
+ hookName?: string | undefined;
119
+ translationFunction?: string | undefined;
120
+ namespace?: string | undefined;
121
+ accessorStyle?: "function" | "bracket" | undefined;
61
122
  } | undefined;
62
123
  }, {
63
124
  framework?: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown" | undefined;
@@ -68,6 +129,7 @@ declare const LocalizationConfigSchema: z.ZodObject<{
68
129
  keyPrefix?: string | undefined;
69
130
  namespaces?: string[] | undefined;
70
131
  ignorePatterns?: string[] | undefined;
132
+ includePatterns?: string[] | undefined;
71
133
  incrementalCache?: boolean | undefined;
72
134
  cacheDir?: string | undefined;
73
135
  aws?: {
@@ -78,8 +140,16 @@ declare const LocalizationConfigSchema: z.ZodObject<{
78
140
  legacyCdnPattern?: string | undefined;
79
141
  assetsPrefix?: string | undefined;
80
142
  profile?: string | undefined;
81
- } | undefined;
143
+ } | null | undefined;
82
144
  plugins?: string[] | undefined;
145
+ codemods?: {
146
+ importPackage?: string | undefined;
147
+ hookName?: string | undefined;
148
+ translationFunction?: string | undefined;
149
+ namespace?: string | undefined;
150
+ accessorStyle?: "function" | "bracket" | undefined;
151
+ } | undefined;
152
+ localeStructure?: "flat" | "nested" | undefined;
83
153
  }>;
84
154
  type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
85
155
  type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
@@ -94,7 +164,11 @@ interface ConfigLoadResult {
94
164
  */
95
165
  declare function loadConfig(cwd?: string, overrides?: Partial<LocalizationConfig>): Promise<ConfigLoadResult>;
96
166
  /**
97
- * Writes a default config file to the project root.
167
+ * Writes a fully-populated default config file to the project root.
168
+ *
169
+ * Every supported configuration field is included so users can see all
170
+ * available options and edit them directly. Fields that are optional/unused
171
+ * are set to their default values or left as empty strings / empty arrays.
98
172
  */
99
173
  declare function writeDefaultConfig(cwd?: string, framework?: string): string;
100
174
  /** Validate an existing config file */
package/dist/index.d.ts CHANGED
@@ -10,9 +10,10 @@ declare const LocalizationConfigSchema: z.ZodObject<{
10
10
  keyPrefix: z.ZodOptional<z.ZodString>;
11
11
  namespaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
12
  ignorePatterns: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
13
+ includePatterns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
13
14
  incrementalCache: z.ZodDefault<z.ZodBoolean>;
14
15
  cacheDir: z.ZodDefault<z.ZodString>;
15
- aws: z.ZodOptional<z.ZodObject<{
16
+ aws: z.ZodNullable<z.ZodOptional<z.ZodObject<{
16
17
  region: z.ZodDefault<z.ZodString>;
17
18
  bucket: z.ZodString;
18
19
  distributionId: z.ZodString;
@@ -36,8 +37,59 @@ declare const LocalizationConfigSchema: z.ZodObject<{
36
37
  legacyCdnPattern?: string | undefined;
37
38
  assetsPrefix?: string | undefined;
38
39
  profile?: string | undefined;
39
- }>>;
40
+ }>>>;
40
41
  plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
42
+ /** Codemod behaviour overrides — all fields are optional. */
43
+ codemods: z.ZodOptional<z.ZodObject<{
44
+ /**
45
+ * The npm package to import from.
46
+ * React default: "react-i18next"
47
+ * Vue default: "vue-i18n"
48
+ * Angular default: "@ngx-translate/core"
49
+ */
50
+ importPackage: z.ZodOptional<z.ZodString>;
51
+ /**
52
+ * The hook / function name to import and call.
53
+ * React default: "useTranslation"
54
+ * Vue default: "useI18n"
55
+ */
56
+ hookName: z.ZodOptional<z.ZodString>;
57
+ /**
58
+ * The translation function identifier returned by the hook (default: "t").
59
+ */
60
+ translationFunction: z.ZodOptional<z.ZodString>;
61
+ /**
62
+ * Namespace to pass as the first argument to the hook call.
63
+ * When omitted no namespace argument is injected.
64
+ */
65
+ namespace: z.ZodOptional<z.ZodString>;
66
+ /**
67
+ * How the translation accessor is written in generated code.
68
+ * "function" (default) — t('key')
69
+ * "bracket" — t['key']
70
+ */
71
+ accessorStyle: z.ZodOptional<z.ZodEnum<["function", "bracket"]>>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ importPackage?: string | undefined;
74
+ hookName?: string | undefined;
75
+ translationFunction?: string | undefined;
76
+ namespace?: string | undefined;
77
+ accessorStyle?: "function" | "bracket" | undefined;
78
+ }, {
79
+ importPackage?: string | undefined;
80
+ hookName?: string | undefined;
81
+ translationFunction?: string | undefined;
82
+ namespace?: string | undefined;
83
+ accessorStyle?: "function" | "bracket" | undefined;
84
+ }>>;
85
+ /**
86
+ * Locale file layout.
87
+ * "nested" (default) — one file per language + namespace:
88
+ * locales/en/common.json, locales/fr/dashboard.json
89
+ * "flat" — one file per language, all keys merged:
90
+ * locales/en.json, locales/fr.json
91
+ */
92
+ localeStructure: z.ZodDefault<z.ZodEnum<["nested", "flat"]>>;
41
93
  }, "strip", z.ZodTypeAny, {
42
94
  framework: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown";
43
95
  defaultLanguage: string;
@@ -48,8 +100,10 @@ declare const LocalizationConfigSchema: z.ZodObject<{
48
100
  incrementalCache: boolean;
49
101
  cacheDir: string;
50
102
  plugins: string[];
103
+ localeStructure: "flat" | "nested";
51
104
  keyPrefix?: string | undefined;
52
105
  namespaces?: string[] | undefined;
106
+ includePatterns?: string[] | undefined;
53
107
  aws?: {
54
108
  region: string;
55
109
  bucket: string;
@@ -58,6 +112,13 @@ declare const LocalizationConfigSchema: z.ZodObject<{
58
112
  cdnBaseUrl?: string | undefined;
59
113
  legacyCdnPattern?: string | undefined;
60
114
  profile?: string | undefined;
115
+ } | null | undefined;
116
+ codemods?: {
117
+ importPackage?: string | undefined;
118
+ hookName?: string | undefined;
119
+ translationFunction?: string | undefined;
120
+ namespace?: string | undefined;
121
+ accessorStyle?: "function" | "bracket" | undefined;
61
122
  } | undefined;
62
123
  }, {
63
124
  framework?: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown" | undefined;
@@ -68,6 +129,7 @@ declare const LocalizationConfigSchema: z.ZodObject<{
68
129
  keyPrefix?: string | undefined;
69
130
  namespaces?: string[] | undefined;
70
131
  ignorePatterns?: string[] | undefined;
132
+ includePatterns?: string[] | undefined;
71
133
  incrementalCache?: boolean | undefined;
72
134
  cacheDir?: string | undefined;
73
135
  aws?: {
@@ -78,8 +140,16 @@ declare const LocalizationConfigSchema: z.ZodObject<{
78
140
  legacyCdnPattern?: string | undefined;
79
141
  assetsPrefix?: string | undefined;
80
142
  profile?: string | undefined;
81
- } | undefined;
143
+ } | null | undefined;
82
144
  plugins?: string[] | undefined;
145
+ codemods?: {
146
+ importPackage?: string | undefined;
147
+ hookName?: string | undefined;
148
+ translationFunction?: string | undefined;
149
+ namespace?: string | undefined;
150
+ accessorStyle?: "function" | "bracket" | undefined;
151
+ } | undefined;
152
+ localeStructure?: "flat" | "nested" | undefined;
83
153
  }>;
84
154
  type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
85
155
  type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
@@ -94,7 +164,11 @@ interface ConfigLoadResult {
94
164
  */
95
165
  declare function loadConfig(cwd?: string, overrides?: Partial<LocalizationConfig>): Promise<ConfigLoadResult>;
96
166
  /**
97
- * Writes a default config file to the project root.
167
+ * Writes a fully-populated default config file to the project root.
168
+ *
169
+ * Every supported configuration field is included so users can see all
170
+ * available options and edit them directly. Fields that are optional/unused
171
+ * are set to their default values or left as empty strings / empty arrays.
98
172
  */
99
173
  declare function writeDefaultConfig(cwd?: string, framework?: string): string;
100
174
  /** Validate an existing config file */
package/dist/index.js CHANGED
@@ -63,6 +63,36 @@ var FrameworkSchema = import_zod.z.enum([
63
63
  "jsp",
64
64
  "unknown"
65
65
  ]);
66
+ var CodemodConfigSchema = import_zod.z.object({
67
+ /**
68
+ * The npm package to import from.
69
+ * React default: "react-i18next"
70
+ * Vue default: "vue-i18n"
71
+ * Angular default: "@ngx-translate/core"
72
+ */
73
+ importPackage: import_zod.z.string().optional(),
74
+ /**
75
+ * The hook / function name to import and call.
76
+ * React default: "useTranslation"
77
+ * Vue default: "useI18n"
78
+ */
79
+ hookName: import_zod.z.string().optional(),
80
+ /**
81
+ * The translation function identifier returned by the hook (default: "t").
82
+ */
83
+ translationFunction: import_zod.z.string().optional(),
84
+ /**
85
+ * Namespace to pass as the first argument to the hook call.
86
+ * When omitted no namespace argument is injected.
87
+ */
88
+ namespace: import_zod.z.string().optional(),
89
+ /**
90
+ * How the translation accessor is written in generated code.
91
+ * "function" (default) — t('key')
92
+ * "bracket" — t['key']
93
+ */
94
+ accessorStyle: import_zod.z.enum(["function", "bracket"]).optional()
95
+ });
66
96
  var LocalizationConfigSchema = import_zod.z.object({
67
97
  framework: FrameworkSchema.default("unknown"),
68
98
  defaultLanguage: import_zod.z.string().default("en"),
@@ -72,10 +102,21 @@ var LocalizationConfigSchema = import_zod.z.object({
72
102
  keyPrefix: import_zod.z.string().optional(),
73
103
  namespaces: import_zod.z.array(import_zod.z.string()).optional(),
74
104
  ignorePatterns: import_zod.z.array(import_zod.z.string()).default(["node_modules", "dist", ".git", "coverage"]),
105
+ includePatterns: import_zod.z.array(import_zod.z.string()).optional(),
75
106
  incrementalCache: import_zod.z.boolean().default(true),
76
107
  cacheDir: import_zod.z.string().default(".ai-localize-cache"),
77
- aws: AwsConfigSchema.optional(),
78
- plugins: import_zod.z.array(import_zod.z.string()).default([])
108
+ aws: AwsConfigSchema.optional().nullable(),
109
+ plugins: import_zod.z.array(import_zod.z.string()).default([]),
110
+ /** Codemod behaviour overrides — all fields are optional. */
111
+ codemods: CodemodConfigSchema.optional(),
112
+ /**
113
+ * Locale file layout.
114
+ * "nested" (default) — one file per language + namespace:
115
+ * locales/en/common.json, locales/fr/dashboard.json
116
+ * "flat" — one file per language, all keys merged:
117
+ * locales/en.json, locales/fr.json
118
+ */
119
+ localeStructure: import_zod.z.enum(["nested", "flat"]).default("nested")
79
120
  });
80
121
 
81
122
  // src/loader.ts
@@ -136,22 +177,90 @@ function extractAwsFromEnv() {
136
177
  function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
137
178
  const configPath = path.join(cwd, import_ai_localize_shared.CONFIG_FILE_NAME);
138
179
  const defaultConfig = {
180
+ // ── Framework ────────────────────────────────────────────────────────────
181
+ // Detected automatically by ai-localize init.
182
+ // Values: "react" | "react-cra" | "react-vite" | "react-nextjs"
183
+ // | "angular" | "angular-ngx" | "angular-i18n"
184
+ // | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown"
139
185
  framework,
186
+ // ── Languages ────────────────────────────────────────────────────────────
187
+ // Source language — used as reference for missing-translation detection.
140
188
  defaultLanguage: "en",
189
+ // Additional languages to generate locale files for.
190
+ // All target files are seeded with the source value (no blank entries).
141
191
  targetLanguages: ["fr", "de"],
192
+ // ── Directories ──────────────────────────────────────────────────────────
193
+ // Root of your source code — scanning starts here.
194
+ // Keys are generated relative to this directory.
142
195
  sourceDir: "src",
196
+ // Where locale JSON files are written.
143
197
  localesDir: "locales",
198
+ // ── Locale file layout ───────────────────────────────────────────────────
199
+ // "nested" (default): one file per language + namespace
200
+ // locales/en/common.json, locales/en/dashboard.json
201
+ // "flat": one file per language, all keys merged
202
+ // locales/en.json (non-default namespace keys are prefixed: "dashboard.header.title")
203
+ localeStructure: "nested",
204
+ // ── Scan filtering ───────────────────────────────────────────────────────
205
+ // Directories/patterns to skip during scanning.
144
206
  ignorePatterns: ["node_modules", "dist", ".git", "coverage"],
207
+ // Optional: only scan files matching these glob patterns.
208
+ // If empty/omitted, all files under sourceDir are scanned.
209
+ // Example: ["src/components/**/*.tsx", "src/pages/**/*.tsx"]
210
+ includePatterns: [],
211
+ // ── Key generation ───────────────────────────────────────────────────────
212
+ // Optional prefix prepended to every generated key.
213
+ // Example: "myapp" → "myapp.components.button.save"
214
+ keyPrefix: "",
215
+ // Optional explicit list of namespace names.
216
+ // If omitted, namespace is derived from the first path segment after sourceDir.
217
+ namespaces: [],
218
+ // ── Incremental scanning ─────────────────────────────────────────────────
219
+ // Cache file hashes between runs — only re-scan changed files.
145
220
  incrementalCache: true,
146
221
  cacheDir: ".ai-localize-cache",
222
+ // ── Codemod behaviour ────────────────────────────────────────────────────
223
+ // Controls what the codemod injects when wrapping hardcoded strings.
224
+ //
225
+ // importPackage: npm package name OR project-relative path to the hook file.
226
+ // npm package: "react-i18next"
227
+ // local hook path: "src/hooks/useTranslation" (path relative to project root)
228
+ // The codemod computes the correct relative import path per file automatically.
229
+ //
230
+ // hookName: the hook function to import and call.
231
+ // Default: "useTranslation"
232
+ //
233
+ // translationFunction: the accessor destructured from the hook.
234
+ // Default: "t"
235
+ //
236
+ // namespace: passed as argument to the hook, e.g. useTranslation("common").
237
+ // Leave empty to omit the argument.
238
+ //
239
+ // accessorStyle:
240
+ // "function" (default) → t('key')
241
+ // "bracket" → t['key']
242
+ codemods: {
243
+ importPackage: "react-i18next",
244
+ hookName: "useTranslation",
245
+ translationFunction: "t",
246
+ namespace: "",
247
+ accessorStyle: "function"
248
+ },
249
+ // ── AWS / CloudFront (optional) ──────────────────────────────────────────
250
+ // Required only for CDN migration commands (migrate-cdn, upload-assets, replace-cdn).
251
+ // Leave all fields empty if not using CDN migration.
252
+ // Credentials can also be supplied via environment variables (see docs).
147
253
  aws: {
148
254
  region: "us-east-1",
149
255
  bucket: "",
150
256
  distributionId: "",
151
257
  cdnBaseUrl: "",
152
258
  legacyCdnPattern: "",
153
- assetsPrefix: "assets"
259
+ assetsPrefix: "assets",
260
+ profile: ""
154
261
  },
262
+ // ── Plugins ──────────────────────────────────────────────────────────────
263
+ // Paths to custom plugin modules. See docs/PLUGIN_SYSTEM.md for details.
155
264
  plugins: []
156
265
  };
157
266
  fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", "utf-8");
package/dist/index.mjs CHANGED
@@ -24,6 +24,36 @@ var FrameworkSchema = z.enum([
24
24
  "jsp",
25
25
  "unknown"
26
26
  ]);
27
+ var CodemodConfigSchema = z.object({
28
+ /**
29
+ * The npm package to import from.
30
+ * React default: "react-i18next"
31
+ * Vue default: "vue-i18n"
32
+ * Angular default: "@ngx-translate/core"
33
+ */
34
+ importPackage: z.string().optional(),
35
+ /**
36
+ * The hook / function name to import and call.
37
+ * React default: "useTranslation"
38
+ * Vue default: "useI18n"
39
+ */
40
+ hookName: z.string().optional(),
41
+ /**
42
+ * The translation function identifier returned by the hook (default: "t").
43
+ */
44
+ translationFunction: z.string().optional(),
45
+ /**
46
+ * Namespace to pass as the first argument to the hook call.
47
+ * When omitted no namespace argument is injected.
48
+ */
49
+ namespace: z.string().optional(),
50
+ /**
51
+ * How the translation accessor is written in generated code.
52
+ * "function" (default) — t('key')
53
+ * "bracket" — t['key']
54
+ */
55
+ accessorStyle: z.enum(["function", "bracket"]).optional()
56
+ });
27
57
  var LocalizationConfigSchema = z.object({
28
58
  framework: FrameworkSchema.default("unknown"),
29
59
  defaultLanguage: z.string().default("en"),
@@ -33,10 +63,21 @@ var LocalizationConfigSchema = z.object({
33
63
  keyPrefix: z.string().optional(),
34
64
  namespaces: z.array(z.string()).optional(),
35
65
  ignorePatterns: z.array(z.string()).default(["node_modules", "dist", ".git", "coverage"]),
66
+ includePatterns: z.array(z.string()).optional(),
36
67
  incrementalCache: z.boolean().default(true),
37
68
  cacheDir: z.string().default(".ai-localize-cache"),
38
- aws: AwsConfigSchema.optional(),
39
- plugins: z.array(z.string()).default([])
69
+ aws: AwsConfigSchema.optional().nullable(),
70
+ plugins: z.array(z.string()).default([]),
71
+ /** Codemod behaviour overrides — all fields are optional. */
72
+ codemods: CodemodConfigSchema.optional(),
73
+ /**
74
+ * Locale file layout.
75
+ * "nested" (default) — one file per language + namespace:
76
+ * locales/en/common.json, locales/fr/dashboard.json
77
+ * "flat" — one file per language, all keys merged:
78
+ * locales/en.json, locales/fr.json
79
+ */
80
+ localeStructure: z.enum(["nested", "flat"]).default("nested")
40
81
  });
41
82
 
42
83
  // src/loader.ts
@@ -97,22 +138,90 @@ function extractAwsFromEnv() {
97
138
  function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
98
139
  const configPath = path.join(cwd, CONFIG_FILE_NAME);
99
140
  const defaultConfig = {
141
+ // ── Framework ────────────────────────────────────────────────────────────
142
+ // Detected automatically by ai-localize init.
143
+ // Values: "react" | "react-cra" | "react-vite" | "react-nextjs"
144
+ // | "angular" | "angular-ngx" | "angular-i18n"
145
+ // | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown"
100
146
  framework,
147
+ // ── Languages ────────────────────────────────────────────────────────────
148
+ // Source language — used as reference for missing-translation detection.
101
149
  defaultLanguage: "en",
150
+ // Additional languages to generate locale files for.
151
+ // All target files are seeded with the source value (no blank entries).
102
152
  targetLanguages: ["fr", "de"],
153
+ // ── Directories ──────────────────────────────────────────────────────────
154
+ // Root of your source code — scanning starts here.
155
+ // Keys are generated relative to this directory.
103
156
  sourceDir: "src",
157
+ // Where locale JSON files are written.
104
158
  localesDir: "locales",
159
+ // ── Locale file layout ───────────────────────────────────────────────────
160
+ // "nested" (default): one file per language + namespace
161
+ // locales/en/common.json, locales/en/dashboard.json
162
+ // "flat": one file per language, all keys merged
163
+ // locales/en.json (non-default namespace keys are prefixed: "dashboard.header.title")
164
+ localeStructure: "nested",
165
+ // ── Scan filtering ───────────────────────────────────────────────────────
166
+ // Directories/patterns to skip during scanning.
105
167
  ignorePatterns: ["node_modules", "dist", ".git", "coverage"],
168
+ // Optional: only scan files matching these glob patterns.
169
+ // If empty/omitted, all files under sourceDir are scanned.
170
+ // Example: ["src/components/**/*.tsx", "src/pages/**/*.tsx"]
171
+ includePatterns: [],
172
+ // ── Key generation ───────────────────────────────────────────────────────
173
+ // Optional prefix prepended to every generated key.
174
+ // Example: "myapp" → "myapp.components.button.save"
175
+ keyPrefix: "",
176
+ // Optional explicit list of namespace names.
177
+ // If omitted, namespace is derived from the first path segment after sourceDir.
178
+ namespaces: [],
179
+ // ── Incremental scanning ─────────────────────────────────────────────────
180
+ // Cache file hashes between runs — only re-scan changed files.
106
181
  incrementalCache: true,
107
182
  cacheDir: ".ai-localize-cache",
183
+ // ── Codemod behaviour ────────────────────────────────────────────────────
184
+ // Controls what the codemod injects when wrapping hardcoded strings.
185
+ //
186
+ // importPackage: npm package name OR project-relative path to the hook file.
187
+ // npm package: "react-i18next"
188
+ // local hook path: "src/hooks/useTranslation" (path relative to project root)
189
+ // The codemod computes the correct relative import path per file automatically.
190
+ //
191
+ // hookName: the hook function to import and call.
192
+ // Default: "useTranslation"
193
+ //
194
+ // translationFunction: the accessor destructured from the hook.
195
+ // Default: "t"
196
+ //
197
+ // namespace: passed as argument to the hook, e.g. useTranslation("common").
198
+ // Leave empty to omit the argument.
199
+ //
200
+ // accessorStyle:
201
+ // "function" (default) → t('key')
202
+ // "bracket" → t['key']
203
+ codemods: {
204
+ importPackage: "react-i18next",
205
+ hookName: "useTranslation",
206
+ translationFunction: "t",
207
+ namespace: "",
208
+ accessorStyle: "function"
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).
108
214
  aws: {
109
215
  region: "us-east-1",
110
216
  bucket: "",
111
217
  distributionId: "",
112
218
  cdnBaseUrl: "",
113
219
  legacyCdnPattern: "",
114
- assetsPrefix: "assets"
220
+ assetsPrefix: "assets",
221
+ profile: ""
115
222
  },
223
+ // ── Plugins ──────────────────────────────────────────────────────────────
224
+ // Paths to custom plugin modules. See docs/PLUGIN_SYSTEM.md for details.
116
225
  plugins: []
117
226
  };
118
227
  fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", "utf-8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-localize-config",
3
- "version": "1.0.1",
3
+ "version": "2.0.1",
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": "1.0.1"
19
+ "ai-localize-shared": "2.0.1"
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
- 'ai-localize.config.js',
31
- 'ai-localize.config.ts',
32
- '.ai-localizerc',
30
+ 'ai-localize.config.js',
31
+ 'ai-localize.config.ts',
32
+ '.ai-localizerc',
33
33
  '.ai-localizerc.json',
34
34
  '.ai-localizerc.js',
35
- 'package.json',
35
+ 'package.json',
36
36
  ],
37
37
  packageProp: 'aiLocalize',
38
38
  });
@@ -42,12 +42,12 @@ export async function loadConfig(
42
42
 
43
43
  try {
44
44
  const result = await explorer.search(cwd);
45
- if (result) {
45
+ if (result) {
46
46
  rawConfig = result.config as Record<string, unknown>;
47
47
  filePath = result.filepath;
48
48
  }
49
49
  } catch (err) {
50
- // Config not found - use defaults
50
+ // Config not found use defaults
51
51
  }
52
52
 
53
53
  // Merge env vars for AWS
@@ -69,43 +69,129 @@ export async function loadConfig(
69
69
  function extractAwsFromEnv(): Record<string, string> | null {
70
70
  const bucket = process.env.AI_LOCALIZE_S3_BUCKET || process.env.AWS_S3_BUCKET;
71
71
  const distributionId =
72
- process.env.AI_LOCALIZE_CF_DISTRIBUTION_ID || process.env.AWS_CF_DISTRIBUTION_ID;
72
+ process.env.AI_LOCALIZE_CF_DISTRIBUTION_ID || process.env.AWS_CF_DISTRIBUTION_ID;
73
73
 
74
74
  if (!bucket || !distributionId) return null;
75
75
 
76
76
  return {
77
- bucket,
77
+ bucket,
78
78
  distributionId,
79
79
  region: process.env.AWS_REGION || process.env.AI_LOCALIZE_AWS_REGION || 'us-east-1',
80
- cdnBaseUrl: process.env.AI_LOCALIZE_CDN_BASE_URL || '',
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
  };
84
84
  }
85
85
 
86
86
  /**
87
- * Writes a default config file to the project root.
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.
88
92
  */
89
93
  export function writeDefaultConfig(cwd = process.cwd(), framework = 'unknown'): string {
90
94
  const configPath = path.join(cwd, CONFIG_FILE_NAME);
95
+
91
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"
92
102
  framework,
103
+
104
+ // ── Languages ────────────────────────────────────────────────────────────
105
+ // Source language — used as reference for missing-translation detection.
93
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).
94
110
  targetLanguages: ['fr', 'de'],
111
+
112
+ // ── Directories ──────────────────────────────────────────────────────────
113
+ // Root of your source code — scanning starts here.
114
+ // Keys are generated relative to this directory.
95
115
  sourceDir: 'src',
116
+
117
+ // Where locale JSON files are written.
96
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
+ // ── Scan filtering ───────────────────────────────────────────────────────
128
+ // Directories/patterns to skip during scanning.
97
129
  ignorePatterns: ['node_modules', 'dist', '.git', 'coverage'],
98
- incrementalCache: true,
130
+
131
+ // Optional: only scan files matching these glob patterns.
132
+ // If empty/omitted, all files under sourceDir are scanned.
133
+ // Example: ["src/components/**/*.tsx", "src/pages/**/*.tsx"]
134
+ includePatterns: [],
135
+
136
+ // ── Key generation ───────────────────────────────────────────────────────
137
+ // Optional prefix prepended to every generated key.
138
+ // Example: "myapp" → "myapp.components.button.save"
139
+ keyPrefix: '',
140
+
141
+ // Optional explicit list of namespace names.
142
+ // If omitted, namespace is derived from the first path segment after sourceDir.
143
+ namespaces: [],
144
+
145
+ // ── Incremental scanning ─────────────────────────────────────────────────
146
+ // Cache file hashes between runs — only re-scan changed files.
147
+ incrementalCache: true,
99
148
  cacheDir: '.ai-localize-cache',
100
- aws: {
149
+
150
+ // ── Codemod behaviour ────────────────────────────────────────────────────
151
+ // Controls what the codemod injects when wrapping hardcoded strings.
152
+ //
153
+ // importPackage: npm package name OR project-relative path to the hook file.
154
+ // npm package: "react-i18next"
155
+ // local hook path: "src/hooks/useTranslation" (path relative to project root)
156
+ // The codemod computes the correct relative import path per file automatically.
157
+ //
158
+ // hookName: the hook function to import and call.
159
+ // Default: "useTranslation"
160
+ //
161
+ // translationFunction: the accessor destructured from the hook.
162
+ // Default: "t"
163
+ //
164
+ // namespace: passed as argument to the hook, e.g. useTranslation("common").
165
+ // Leave empty to omit the argument.
166
+ //
167
+ // accessorStyle:
168
+ // "function" (default) → t('key')
169
+ // "bracket" → t['key']
170
+ codemods: {
171
+ importPackage: 'react-i18next',
172
+ hookName: 'useTranslation',
173
+ translationFunction: 't',
174
+ namespace: '',
175
+ accessorStyle: 'function',
176
+ },
177
+
178
+ // ── AWS / CloudFront (optional) ──────────────────────────────────────────
179
+ // Required only for CDN migration commands (migrate-cdn, upload-assets, replace-cdn).
180
+ // Leave all fields empty if not using CDN migration.
181
+ // Credentials can also be supplied via environment variables (see docs).
182
+ aws: {
101
183
  region: 'us-east-1',
102
- bucket: '',
184
+ bucket: '',
103
185
  distributionId: '',
104
186
  cdnBaseUrl: '',
105
187
  legacyCdnPattern: '',
106
188
  assetsPrefix: 'assets',
189
+ profile: '',
107
190
  },
108
- plugins: [],
191
+
192
+ // ── Plugins ──────────────────────────────────────────────────────────────
193
+ // Paths to custom plugin modules. See docs/PLUGIN_SYSTEM.md for details.
194
+ plugins: [],
109
195
  };
110
196
 
111
197
  fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf-8');
@@ -125,11 +211,11 @@ export function validateConfig(configPath: string): {
125
211
  } catch (err: unknown) {
126
212
  if (err && typeof err === 'object' && 'errors' in err) {
127
213
  const zodError = err as { errors: Array<{ path: unknown[]; message: string }> };
128
- return {
214
+ return {
129
215
  valid: false,
130
216
  errors: zodError.errors.map((e) => `${e.path.join('.')}: ${e.message}`),
131
217
  };
132
- }
218
+ }
133
219
  return {
134
220
  valid: false,
135
221
  errors: [err instanceof Error ? err.message : String(err)],
package/src/schema.ts CHANGED
@@ -12,7 +12,7 @@ const AwsConfigSchema = z.object({
12
12
 
13
13
  const FrameworkSchema = z.enum([
14
14
  'react',
15
- 'react-cra',
15
+ 'react-cra',
16
16
  'react-vite',
17
17
  'react-nextjs',
18
18
  'angular',
@@ -26,6 +26,41 @@ const FrameworkSchema = z.enum([
26
26
  'unknown',
27
27
  ]);
28
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
+
29
64
  export const LocalizationConfigSchema = z.object({
30
65
  framework: FrameworkSchema.default('unknown'),
31
66
  defaultLanguage: z.string().default('en'),
@@ -37,10 +72,21 @@ export const LocalizationConfigSchema = z.object({
37
72
  ignorePatterns: z
38
73
  .array(z.string())
39
74
  .default(['node_modules', 'dist', '.git', 'coverage']),
75
+ includePatterns: z.array(z.string()).optional(),
40
76
  incrementalCache: z.boolean().default(true),
41
77
  cacheDir: z.string().default('.ai-localize-cache'),
42
- aws: AwsConfigSchema.optional(),
78
+ aws: AwsConfigSchema.optional().nullable(),
43
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'),
44
90
  });
45
91
 
46
92
  export type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;