ai-localize-config 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,106 @@
1
+ import { z } from 'zod';
2
+ import { LocalizationConfig } from '@ai-localize/shared';
3
+
4
+ declare const LocalizationConfigSchema: z.ZodObject<{
5
+ framework: z.ZodDefault<z.ZodEnum<["react", "react-cra", "react-vite", "react-nextjs", "angular", "angular-ngx", "angular-i18n", "vue", "vue-i18n", "jquery", "vanilla-js", "jsp", "unknown"]>>;
6
+ defaultLanguage: z.ZodDefault<z.ZodString>;
7
+ targetLanguages: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
8
+ sourceDir: z.ZodDefault<z.ZodString>;
9
+ localesDir: z.ZodDefault<z.ZodString>;
10
+ keyPrefix: z.ZodOptional<z.ZodString>;
11
+ namespaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
+ ignorePatterns: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
13
+ incrementalCache: z.ZodDefault<z.ZodBoolean>;
14
+ cacheDir: z.ZodDefault<z.ZodString>;
15
+ aws: z.ZodOptional<z.ZodObject<{
16
+ region: z.ZodDefault<z.ZodString>;
17
+ bucket: z.ZodString;
18
+ distributionId: z.ZodString;
19
+ cdnBaseUrl: z.ZodOptional<z.ZodString>;
20
+ legacyCdnPattern: z.ZodOptional<z.ZodString>;
21
+ assetsPrefix: z.ZodDefault<z.ZodString>;
22
+ profile: z.ZodOptional<z.ZodString>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ region: string;
25
+ bucket: string;
26
+ distributionId: string;
27
+ assetsPrefix: string;
28
+ cdnBaseUrl?: string | undefined;
29
+ legacyCdnPattern?: string | undefined;
30
+ profile?: string | undefined;
31
+ }, {
32
+ bucket: string;
33
+ distributionId: string;
34
+ region?: string | undefined;
35
+ cdnBaseUrl?: string | undefined;
36
+ legacyCdnPattern?: string | undefined;
37
+ assetsPrefix?: string | undefined;
38
+ profile?: string | undefined;
39
+ }>>;
40
+ plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ framework: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown";
43
+ defaultLanguage: string;
44
+ targetLanguages: string[];
45
+ sourceDir: string;
46
+ localesDir: string;
47
+ ignorePatterns: string[];
48
+ incrementalCache: boolean;
49
+ cacheDir: string;
50
+ plugins: string[];
51
+ keyPrefix?: string | undefined;
52
+ namespaces?: string[] | undefined;
53
+ aws?: {
54
+ region: string;
55
+ bucket: string;
56
+ distributionId: string;
57
+ assetsPrefix: string;
58
+ cdnBaseUrl?: string | undefined;
59
+ legacyCdnPattern?: string | undefined;
60
+ profile?: string | undefined;
61
+ } | undefined;
62
+ }, {
63
+ framework?: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown" | undefined;
64
+ defaultLanguage?: string | undefined;
65
+ targetLanguages?: string[] | undefined;
66
+ sourceDir?: string | undefined;
67
+ localesDir?: string | undefined;
68
+ keyPrefix?: string | undefined;
69
+ namespaces?: string[] | undefined;
70
+ ignorePatterns?: string[] | undefined;
71
+ incrementalCache?: boolean | undefined;
72
+ cacheDir?: string | undefined;
73
+ aws?: {
74
+ bucket: string;
75
+ distributionId: string;
76
+ region?: string | undefined;
77
+ cdnBaseUrl?: string | undefined;
78
+ legacyCdnPattern?: string | undefined;
79
+ assetsPrefix?: string | undefined;
80
+ profile?: string | undefined;
81
+ } | undefined;
82
+ plugins?: string[] | undefined;
83
+ }>;
84
+ type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
85
+ type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
86
+
87
+ interface ConfigLoadResult {
88
+ config: LocalizationConfig;
89
+ filePath: string | null;
90
+ }
91
+ /**
92
+ * Loads and validates the ai-localize configuration.
93
+ * Searches for config in standard locations using cosmiconfig.
94
+ */
95
+ declare function loadConfig(cwd?: string, overrides?: Partial<LocalizationConfig>): Promise<ConfigLoadResult>;
96
+ /**
97
+ * Writes a default config file to the project root.
98
+ */
99
+ declare function writeDefaultConfig(cwd?: string, framework?: string): string;
100
+ /** Validate an existing config file */
101
+ declare function validateConfig(configPath: string): {
102
+ valid: boolean;
103
+ errors: string[];
104
+ };
105
+
106
+ export { type ConfigLoadResult, type LocalizationConfigInput, type LocalizationConfigOutput, LocalizationConfigSchema, loadConfig, validateConfig, writeDefaultConfig };
@@ -0,0 +1,106 @@
1
+ import { z } from 'zod';
2
+ import { LocalizationConfig } from '@ai-localize/shared';
3
+
4
+ declare const LocalizationConfigSchema: z.ZodObject<{
5
+ framework: z.ZodDefault<z.ZodEnum<["react", "react-cra", "react-vite", "react-nextjs", "angular", "angular-ngx", "angular-i18n", "vue", "vue-i18n", "jquery", "vanilla-js", "jsp", "unknown"]>>;
6
+ defaultLanguage: z.ZodDefault<z.ZodString>;
7
+ targetLanguages: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
8
+ sourceDir: z.ZodDefault<z.ZodString>;
9
+ localesDir: z.ZodDefault<z.ZodString>;
10
+ keyPrefix: z.ZodOptional<z.ZodString>;
11
+ namespaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
+ ignorePatterns: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
13
+ incrementalCache: z.ZodDefault<z.ZodBoolean>;
14
+ cacheDir: z.ZodDefault<z.ZodString>;
15
+ aws: z.ZodOptional<z.ZodObject<{
16
+ region: z.ZodDefault<z.ZodString>;
17
+ bucket: z.ZodString;
18
+ distributionId: z.ZodString;
19
+ cdnBaseUrl: z.ZodOptional<z.ZodString>;
20
+ legacyCdnPattern: z.ZodOptional<z.ZodString>;
21
+ assetsPrefix: z.ZodDefault<z.ZodString>;
22
+ profile: z.ZodOptional<z.ZodString>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ region: string;
25
+ bucket: string;
26
+ distributionId: string;
27
+ assetsPrefix: string;
28
+ cdnBaseUrl?: string | undefined;
29
+ legacyCdnPattern?: string | undefined;
30
+ profile?: string | undefined;
31
+ }, {
32
+ bucket: string;
33
+ distributionId: string;
34
+ region?: string | undefined;
35
+ cdnBaseUrl?: string | undefined;
36
+ legacyCdnPattern?: string | undefined;
37
+ assetsPrefix?: string | undefined;
38
+ profile?: string | undefined;
39
+ }>>;
40
+ plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ framework: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown";
43
+ defaultLanguage: string;
44
+ targetLanguages: string[];
45
+ sourceDir: string;
46
+ localesDir: string;
47
+ ignorePatterns: string[];
48
+ incrementalCache: boolean;
49
+ cacheDir: string;
50
+ plugins: string[];
51
+ keyPrefix?: string | undefined;
52
+ namespaces?: string[] | undefined;
53
+ aws?: {
54
+ region: string;
55
+ bucket: string;
56
+ distributionId: string;
57
+ assetsPrefix: string;
58
+ cdnBaseUrl?: string | undefined;
59
+ legacyCdnPattern?: string | undefined;
60
+ profile?: string | undefined;
61
+ } | undefined;
62
+ }, {
63
+ framework?: "react" | "react-cra" | "react-vite" | "react-nextjs" | "angular" | "angular-ngx" | "angular-i18n" | "vue" | "vue-i18n" | "jquery" | "vanilla-js" | "jsp" | "unknown" | undefined;
64
+ defaultLanguage?: string | undefined;
65
+ targetLanguages?: string[] | undefined;
66
+ sourceDir?: string | undefined;
67
+ localesDir?: string | undefined;
68
+ keyPrefix?: string | undefined;
69
+ namespaces?: string[] | undefined;
70
+ ignorePatterns?: string[] | undefined;
71
+ incrementalCache?: boolean | undefined;
72
+ cacheDir?: string | undefined;
73
+ aws?: {
74
+ bucket: string;
75
+ distributionId: string;
76
+ region?: string | undefined;
77
+ cdnBaseUrl?: string | undefined;
78
+ legacyCdnPattern?: string | undefined;
79
+ assetsPrefix?: string | undefined;
80
+ profile?: string | undefined;
81
+ } | undefined;
82
+ plugins?: string[] | undefined;
83
+ }>;
84
+ type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
85
+ type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
86
+
87
+ interface ConfigLoadResult {
88
+ config: LocalizationConfig;
89
+ filePath: string | null;
90
+ }
91
+ /**
92
+ * Loads and validates the ai-localize configuration.
93
+ * Searches for config in standard locations using cosmiconfig.
94
+ */
95
+ declare function loadConfig(cwd?: string, overrides?: Partial<LocalizationConfig>): Promise<ConfigLoadResult>;
96
+ /**
97
+ * Writes a default config file to the project root.
98
+ */
99
+ declare function writeDefaultConfig(cwd?: string, framework?: string): string;
100
+ /** Validate an existing config file */
101
+ declare function validateConfig(configPath: string): {
102
+ valid: boolean;
103
+ errors: string[];
104
+ };
105
+
106
+ export { type ConfigLoadResult, type LocalizationConfigInput, type LocalizationConfigOutput, LocalizationConfigSchema, loadConfig, validateConfig, writeDefaultConfig };
package/dist/index.js ADDED
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ LocalizationConfigSchema: () => LocalizationConfigSchema,
34
+ loadConfig: () => loadConfig,
35
+ validateConfig: () => validateConfig,
36
+ writeDefaultConfig: () => writeDefaultConfig
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/schema.ts
41
+ var import_zod = require("zod");
42
+ var AwsConfigSchema = import_zod.z.object({
43
+ region: import_zod.z.string().default("us-east-1"),
44
+ bucket: import_zod.z.string().min(1, "S3 bucket name is required"),
45
+ distributionId: import_zod.z.string().min(1, "CloudFront distribution ID is required"),
46
+ cdnBaseUrl: import_zod.z.string().url().optional(),
47
+ legacyCdnPattern: import_zod.z.string().optional(),
48
+ assetsPrefix: import_zod.z.string().default("assets"),
49
+ profile: import_zod.z.string().optional()
50
+ });
51
+ var FrameworkSchema = import_zod.z.enum([
52
+ "react",
53
+ "react-cra",
54
+ "react-vite",
55
+ "react-nextjs",
56
+ "angular",
57
+ "angular-ngx",
58
+ "angular-i18n",
59
+ "vue",
60
+ "vue-i18n",
61
+ "jquery",
62
+ "vanilla-js",
63
+ "jsp",
64
+ "unknown"
65
+ ]);
66
+ var LocalizationConfigSchema = import_zod.z.object({
67
+ framework: FrameworkSchema.default("unknown"),
68
+ defaultLanguage: import_zod.z.string().default("en"),
69
+ targetLanguages: import_zod.z.array(import_zod.z.string()).default([]),
70
+ sourceDir: import_zod.z.string().default("src"),
71
+ localesDir: import_zod.z.string().default("locales"),
72
+ keyPrefix: import_zod.z.string().optional(),
73
+ namespaces: import_zod.z.array(import_zod.z.string()).optional(),
74
+ ignorePatterns: import_zod.z.array(import_zod.z.string()).default(["node_modules", "dist", ".git", "coverage"]),
75
+ incrementalCache: import_zod.z.boolean().default(true),
76
+ cacheDir: import_zod.z.string().default(".ai-localize-cache"),
77
+ aws: AwsConfigSchema.optional(),
78
+ plugins: import_zod.z.array(import_zod.z.string()).default([])
79
+ });
80
+
81
+ // src/loader.ts
82
+ var fs = __toESM(require("fs"));
83
+ var path = __toESM(require("path"));
84
+ var import_cosmiconfig = require("cosmiconfig");
85
+ var dotenv = __toESM(require("dotenv"));
86
+ var import_shared = require("@ai-localize/shared");
87
+ async function loadConfig(cwd = process.cwd(), overrides = {}) {
88
+ dotenv.config({ path: path.join(cwd, ".env") });
89
+ dotenv.config({ path: path.join(cwd, ".env.local") });
90
+ const explorer = (0, import_cosmiconfig.cosmiconfig)("ai-localize", {
91
+ searchPlaces: [
92
+ import_shared.CONFIG_FILE_NAME,
93
+ "ai-localize.config.js",
94
+ "ai-localize.config.ts",
95
+ ".ai-localizerc",
96
+ ".ai-localizerc.json",
97
+ ".ai-localizerc.js",
98
+ "package.json"
99
+ ],
100
+ packageProp: "aiLocalize"
101
+ });
102
+ let rawConfig = {};
103
+ let filePath = null;
104
+ try {
105
+ const result = await explorer.search(cwd);
106
+ if (result) {
107
+ rawConfig = result.config;
108
+ filePath = result.filepath;
109
+ }
110
+ } catch (err) {
111
+ }
112
+ const awsFromEnv = extractAwsFromEnv();
113
+ if (awsFromEnv && !rawConfig.aws) {
114
+ rawConfig.aws = awsFromEnv;
115
+ }
116
+ const merged = { ...rawConfig, ...overrides };
117
+ const validated = LocalizationConfigSchema.parse(merged);
118
+ return {
119
+ config: validated,
120
+ filePath
121
+ };
122
+ }
123
+ function extractAwsFromEnv() {
124
+ const bucket = process.env.AI_LOCALIZE_S3_BUCKET || process.env.AWS_S3_BUCKET;
125
+ const distributionId = process.env.AI_LOCALIZE_CF_DISTRIBUTION_ID || process.env.AWS_CF_DISTRIBUTION_ID;
126
+ if (!bucket || !distributionId) return null;
127
+ return {
128
+ bucket,
129
+ distributionId,
130
+ region: process.env.AWS_REGION || process.env.AI_LOCALIZE_AWS_REGION || "us-east-1",
131
+ cdnBaseUrl: process.env.AI_LOCALIZE_CDN_BASE_URL || "",
132
+ legacyCdnPattern: process.env.AI_LOCALIZE_LEGACY_CDN_PATTERN || "",
133
+ profile: process.env.AWS_PROFILE || ""
134
+ };
135
+ }
136
+ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
137
+ const configPath = path.join(cwd, import_shared.CONFIG_FILE_NAME);
138
+ const defaultConfig = {
139
+ framework,
140
+ defaultLanguage: "en",
141
+ targetLanguages: ["fr", "de"],
142
+ sourceDir: "src",
143
+ localesDir: "locales",
144
+ ignorePatterns: ["node_modules", "dist", ".git", "coverage"],
145
+ incrementalCache: true,
146
+ cacheDir: ".ai-localize-cache",
147
+ aws: {
148
+ region: "us-east-1",
149
+ bucket: "",
150
+ distributionId: "",
151
+ cdnBaseUrl: "",
152
+ legacyCdnPattern: "",
153
+ assetsPrefix: "assets"
154
+ },
155
+ plugins: []
156
+ };
157
+ fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", "utf-8");
158
+ return configPath;
159
+ }
160
+ function validateConfig(configPath) {
161
+ try {
162
+ const raw = fs.readFileSync(configPath, "utf-8");
163
+ const parsed = JSON.parse(raw);
164
+ LocalizationConfigSchema.parse(parsed);
165
+ return { valid: true, errors: [] };
166
+ } catch (err) {
167
+ if (err && typeof err === "object" && "errors" in err) {
168
+ const zodError = err;
169
+ return {
170
+ valid: false,
171
+ errors: zodError.errors.map((e) => `${e.path.join(".")}: ${e.message}`)
172
+ };
173
+ }
174
+ return {
175
+ valid: false,
176
+ errors: [err instanceof Error ? err.message : String(err)]
177
+ };
178
+ }
179
+ }
180
+ // Annotate the CommonJS export names for ESM import in node:
181
+ 0 && (module.exports = {
182
+ LocalizationConfigSchema,
183
+ loadConfig,
184
+ validateConfig,
185
+ writeDefaultConfig
186
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,146 @@
1
+ // src/schema.ts
2
+ import { z } from "zod";
3
+ var 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
+ var FrameworkSchema = z.enum([
13
+ "react",
14
+ "react-cra",
15
+ "react-vite",
16
+ "react-nextjs",
17
+ "angular",
18
+ "angular-ngx",
19
+ "angular-i18n",
20
+ "vue",
21
+ "vue-i18n",
22
+ "jquery",
23
+ "vanilla-js",
24
+ "jsp",
25
+ "unknown"
26
+ ]);
27
+ var LocalizationConfigSchema = z.object({
28
+ framework: FrameworkSchema.default("unknown"),
29
+ defaultLanguage: z.string().default("en"),
30
+ targetLanguages: z.array(z.string()).default([]),
31
+ sourceDir: z.string().default("src"),
32
+ localesDir: z.string().default("locales"),
33
+ keyPrefix: z.string().optional(),
34
+ namespaces: z.array(z.string()).optional(),
35
+ ignorePatterns: z.array(z.string()).default(["node_modules", "dist", ".git", "coverage"]),
36
+ incrementalCache: z.boolean().default(true),
37
+ cacheDir: z.string().default(".ai-localize-cache"),
38
+ aws: AwsConfigSchema.optional(),
39
+ plugins: z.array(z.string()).default([])
40
+ });
41
+
42
+ // src/loader.ts
43
+ import * as fs from "fs";
44
+ import * as path from "path";
45
+ import { cosmiconfig } from "cosmiconfig";
46
+ import * as dotenv from "dotenv";
47
+ import { CONFIG_FILE_NAME } from "@ai-localize/shared";
48
+ async function loadConfig(cwd = process.cwd(), overrides = {}) {
49
+ dotenv.config({ path: path.join(cwd, ".env") });
50
+ dotenv.config({ path: path.join(cwd, ".env.local") });
51
+ const explorer = cosmiconfig("ai-localize", {
52
+ searchPlaces: [
53
+ CONFIG_FILE_NAME,
54
+ "ai-localize.config.js",
55
+ "ai-localize.config.ts",
56
+ ".ai-localizerc",
57
+ ".ai-localizerc.json",
58
+ ".ai-localizerc.js",
59
+ "package.json"
60
+ ],
61
+ packageProp: "aiLocalize"
62
+ });
63
+ let rawConfig = {};
64
+ let filePath = null;
65
+ try {
66
+ const result = await explorer.search(cwd);
67
+ if (result) {
68
+ rawConfig = result.config;
69
+ filePath = result.filepath;
70
+ }
71
+ } catch (err) {
72
+ }
73
+ const awsFromEnv = extractAwsFromEnv();
74
+ if (awsFromEnv && !rawConfig.aws) {
75
+ rawConfig.aws = awsFromEnv;
76
+ }
77
+ const merged = { ...rawConfig, ...overrides };
78
+ const validated = LocalizationConfigSchema.parse(merged);
79
+ return {
80
+ config: validated,
81
+ filePath
82
+ };
83
+ }
84
+ function extractAwsFromEnv() {
85
+ const bucket = process.env.AI_LOCALIZE_S3_BUCKET || process.env.AWS_S3_BUCKET;
86
+ const distributionId = process.env.AI_LOCALIZE_CF_DISTRIBUTION_ID || process.env.AWS_CF_DISTRIBUTION_ID;
87
+ if (!bucket || !distributionId) return null;
88
+ return {
89
+ bucket,
90
+ distributionId,
91
+ region: process.env.AWS_REGION || process.env.AI_LOCALIZE_AWS_REGION || "us-east-1",
92
+ cdnBaseUrl: process.env.AI_LOCALIZE_CDN_BASE_URL || "",
93
+ legacyCdnPattern: process.env.AI_LOCALIZE_LEGACY_CDN_PATTERN || "",
94
+ profile: process.env.AWS_PROFILE || ""
95
+ };
96
+ }
97
+ function writeDefaultConfig(cwd = process.cwd(), framework = "unknown") {
98
+ const configPath = path.join(cwd, CONFIG_FILE_NAME);
99
+ const defaultConfig = {
100
+ framework,
101
+ defaultLanguage: "en",
102
+ targetLanguages: ["fr", "de"],
103
+ sourceDir: "src",
104
+ localesDir: "locales",
105
+ ignorePatterns: ["node_modules", "dist", ".git", "coverage"],
106
+ incrementalCache: true,
107
+ cacheDir: ".ai-localize-cache",
108
+ aws: {
109
+ region: "us-east-1",
110
+ bucket: "",
111
+ distributionId: "",
112
+ cdnBaseUrl: "",
113
+ legacyCdnPattern: "",
114
+ assetsPrefix: "assets"
115
+ },
116
+ plugins: []
117
+ };
118
+ fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n", "utf-8");
119
+ return configPath;
120
+ }
121
+ function validateConfig(configPath) {
122
+ try {
123
+ const raw = fs.readFileSync(configPath, "utf-8");
124
+ const parsed = JSON.parse(raw);
125
+ LocalizationConfigSchema.parse(parsed);
126
+ return { valid: true, errors: [] };
127
+ } catch (err) {
128
+ if (err && typeof err === "object" && "errors" in err) {
129
+ const zodError = err;
130
+ return {
131
+ valid: false,
132
+ errors: zodError.errors.map((e) => `${e.path.join(".")}: ${e.message}`)
133
+ };
134
+ }
135
+ return {
136
+ valid: false,
137
+ errors: [err instanceof Error ? err.message : String(err)]
138
+ };
139
+ }
140
+ }
141
+ export {
142
+ LocalizationConfigSchema,
143
+ loadConfig,
144
+ validateConfig,
145
+ writeDefaultConfig
146
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "ai-localize-config",
3
+ "version": "1.0.0",
4
+ "description": "Configuration loader and schema validator for ai-localize-core",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "dependencies": {
16
+ "zod": "^3.22.4",
17
+ "cosmiconfig": "^9.0.0",
18
+ "dotenv": "^16.4.1",
19
+ "ai-localize-shared": "1.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "tsup": "^8.0.1",
23
+ "typescript": "^5.3.3",
24
+ "vitest": "^1.2.1"
25
+ },
26
+ "license": "MIT",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "scripts": {
31
+ "build": "tsup src/index.ts --format cjs,esm --dts",
32
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "vitest run",
35
+ "lint": "eslint src --ext .ts"
36
+ }
37
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './schema.js';
2
+ export * from './loader.js';
package/src/loader.ts ADDED
@@ -0,0 +1,138 @@
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 default config file to the project root.
88
+ */
89
+ export function writeDefaultConfig(cwd = process.cwd(), framework = 'unknown'): string {
90
+ const configPath = path.join(cwd, CONFIG_FILE_NAME);
91
+ const defaultConfig = {
92
+ framework,
93
+ defaultLanguage: 'en',
94
+ targetLanguages: ['fr', 'de'],
95
+ sourceDir: 'src',
96
+ localesDir: 'locales',
97
+ ignorePatterns: ['node_modules', 'dist', '.git', 'coverage'],
98
+ incrementalCache: true,
99
+ cacheDir: '.ai-localize-cache',
100
+ aws: {
101
+ region: 'us-east-1',
102
+ bucket: '',
103
+ distributionId: '',
104
+ cdnBaseUrl: '',
105
+ legacyCdnPattern: '',
106
+ assetsPrefix: 'assets',
107
+ },
108
+ plugins: [],
109
+ };
110
+
111
+ fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf-8');
112
+ return configPath;
113
+ }
114
+
115
+ /** Validate an existing config file */
116
+ export function validateConfig(configPath: string): {
117
+ valid: boolean;
118
+ errors: string[];
119
+ } {
120
+ try {
121
+ const raw = fs.readFileSync(configPath, 'utf-8');
122
+ const parsed = JSON.parse(raw);
123
+ LocalizationConfigSchema.parse(parsed);
124
+ return { valid: true, errors: [] };
125
+ } catch (err: unknown) {
126
+ if (err && typeof err === 'object' && 'errors' in err) {
127
+ const zodError = err as { errors: Array<{ path: unknown[]; message: string }> };
128
+ return {
129
+ valid: false,
130
+ errors: zodError.errors.map((e) => `${e.path.join('.')}: ${e.message}`),
131
+ };
132
+ }
133
+ return {
134
+ valid: false,
135
+ errors: [err instanceof Error ? err.message : String(err)],
136
+ };
137
+ }
138
+ }
package/src/schema.ts ADDED
@@ -0,0 +1,47 @@
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
+ export const LocalizationConfigSchema = z.object({
30
+ framework: FrameworkSchema.default('unknown'),
31
+ defaultLanguage: z.string().default('en'),
32
+ targetLanguages: z.array(z.string()).default([]),
33
+ sourceDir: z.string().default('src'),
34
+ localesDir: z.string().default('locales'),
35
+ keyPrefix: z.string().optional(),
36
+ namespaces: z.array(z.string()).optional(),
37
+ ignorePatterns: z
38
+ .array(z.string())
39
+ .default(['node_modules', 'dist', '.git', 'coverage']),
40
+ incrementalCache: z.boolean().default(true),
41
+ cacheDir: z.string().default('.ai-localize-cache'),
42
+ aws: AwsConfigSchema.optional(),
43
+ plugins: z.array(z.string()).default([]),
44
+ });
45
+
46
+ export type LocalizationConfigInput = z.input<typeof LocalizationConfigSchema>;
47
+ export type LocalizationConfigOutput = z.output<typeof LocalizationConfigSchema>;
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }