@i18nprune/core 0.1.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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/adapters-gp1lXp0T.d.ts +12 -0
  4. package/dist/capabilities-x74cD2Hu.d.ts +48 -0
  5. package/dist/cleanup.d.ts +64 -0
  6. package/dist/cleanup.js +3999 -0
  7. package/dist/config.d.ts +201 -0
  8. package/dist/config.js +2865 -0
  9. package/dist/coreContext-DMaWLvmB.d.ts +388 -0
  10. package/dist/fs-BUYD8ZhA.d.ts +20 -0
  11. package/dist/generate.d.ts +487 -0
  12. package/dist/generate.js +9389 -0
  13. package/dist/humanEmit-ygNlYX-S.d.ts +79 -0
  14. package/dist/index-BQuLEQ9b.d.ts +7 -0
  15. package/dist/index-B_ow_Xvr.d.ts +97 -0
  16. package/dist/index-BgG01AKL.d.ts +287 -0
  17. package/dist/index-CIzZl4W8.d.ts +124 -0
  18. package/dist/index-Csm1w7XD.d.ts +58 -0
  19. package/dist/index-DLwTogCo.d.ts +43 -0
  20. package/dist/index-DVT26v11.d.ts +61 -0
  21. package/dist/index-DdjljwMj.d.ts +39 -0
  22. package/dist/index-DeIw-cZd.d.ts +52 -0
  23. package/dist/index-X50E1FIX.d.ts +50 -0
  24. package/dist/index.d.ts +9180 -0
  25. package/dist/index.js +21888 -0
  26. package/dist/init.d.ts +86 -0
  27. package/dist/init.js +848 -0
  28. package/dist/listWindow-XEFxQZi1.d.ts +30 -0
  29. package/dist/localeTargetCodes-BBIQjauw.d.ts +11 -0
  30. package/dist/locales.d.ts +39 -0
  31. package/dist/locales.js +2288 -0
  32. package/dist/missing-BVCvgUC8.d.ts +10 -0
  33. package/dist/missing.d.ts +85 -0
  34. package/dist/missing.js +5892 -0
  35. package/dist/modeResolve-cGVaY5Hh.d.ts +25 -0
  36. package/dist/path-Bfn3SAts.d.ts +11 -0
  37. package/dist/profile-BwOP9WKh.d.ts +9 -0
  38. package/dist/providers-0uMEfT6q.d.ts +82 -0
  39. package/dist/prune-c6hKZCv_.d.ts +33 -0
  40. package/dist/quality.d.ts +36 -0
  41. package/dist/quality.js +3868 -0
  42. package/dist/report-D5-6bVFj.d.ts +8 -0
  43. package/dist/report-schema.d.ts +102 -0
  44. package/dist/report-schema.js +42 -0
  45. package/dist/resumeCandidates-xR13eEwt.d.ts +200 -0
  46. package/dist/root-2-kCaBvQ.d.ts +1110 -0
  47. package/dist/runtime/edge.d.ts +21 -0
  48. package/dist/runtime/edge.js +87 -0
  49. package/dist/runtime/helpers/sync.d.ts +16 -0
  50. package/dist/runtime/helpers/sync.js +117 -0
  51. package/dist/runtime/node.d.ts +24 -0
  52. package/dist/runtime/node.js +204 -0
  53. package/dist/runtime/web.d.ts +21 -0
  54. package/dist/runtime/web.js +84 -0
  55. package/dist/shared.d.ts +1177 -0
  56. package/dist/shared.js +4897 -0
  57. package/dist/sourceContext-1LQg3HiQ.d.ts +36 -0
  58. package/dist/sourceSurface-mDtwGo1E.d.ts +122 -0
  59. package/dist/sync.d.ts +86 -0
  60. package/dist/sync.js +4971 -0
  61. package/dist/syncSegment-Bx6He2Mu.d.ts +149 -0
  62. package/dist/targets-EmtKyr6F.d.ts +23 -0
  63. package/dist/template-CGM-_WLT.d.ts +139 -0
  64. package/dist/translate-CIHYp7wi.d.ts +77 -0
  65. package/dist/types/shared.d.ts +21 -0
  66. package/dist/types/shared.js +1 -0
  67. package/dist/types.d.ts +1345 -0
  68. package/dist/types.js +1 -0
  69. package/dist/validate.d.ts +126 -0
  70. package/dist/validate.js +3717 -0
  71. package/package.json +128 -0
package/dist/config.js ADDED
@@ -0,0 +1,2865 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/config/defaults/app.ts
4
+ var REFERENCE_POLICY_SAFE_DEFAULTS = {
5
+ treatCommentedCallSitesAsRuntime: false,
6
+ treatNonSourceFileSitesAsRuntime: false,
7
+ uncertainKeyPolicy: "protect",
8
+ stringPresence: "guard",
9
+ stringPresenceMaxHitsPerKey: 5,
10
+ respectPreserve: true
11
+ };
12
+ var DEFAULT_CONFIG = {
13
+ locales: {
14
+ source: "en",
15
+ directory: "locales"
16
+ },
17
+ src: "src",
18
+ functions: ["t"],
19
+ exclude: { useDefaultSkip: true },
20
+ output: {
21
+ list: {}
22
+ },
23
+ cache: {
24
+ enabled: true,
25
+ mode: "readWrite"
26
+ },
27
+ policies: {},
28
+ reference: {
29
+ defaults: { ...REFERENCE_POLICY_SAFE_DEFAULTS },
30
+ commands: {
31
+ cleanup: { ...REFERENCE_POLICY_SAFE_DEFAULTS },
32
+ sync: { ...REFERENCE_POLICY_SAFE_DEFAULTS },
33
+ generate: { ...REFERENCE_POLICY_SAFE_DEFAULTS }
34
+ }
35
+ },
36
+ localeLeaves: {
37
+ mode: "legacy_string",
38
+ sync: {
39
+ stripMetadata: false
40
+ }
41
+ },
42
+ missing: {}
43
+ };
44
+
45
+ // src/shared/constants/translate.ts
46
+ var TRANSLATE_WORKERS_CAP = 64;
47
+ var ENV_TRANSLATE_MAX_WORKERS = "I18NPRUNE_TRANSLATE_MAX_WORKERS";
48
+
49
+ // src/shared/translator/utils/orchestration.ts
50
+ function clampInt(n, lo, hi) {
51
+ if (!Number.isFinite(n)) return lo;
52
+ return Math.min(hi, Math.max(lo, Math.trunc(n)));
53
+ }
54
+ var DEFAULT_PROVIDER_RATE_LIMITS = {
55
+ // Google can sustain higher throughput; default to safe-high (32) while allowing user/config up to 64.
56
+ google: { maxConcurrency: 32, rpm: 1920, rps: 32, intervalMs: 32 },
57
+ mymemory: { maxConcurrency: 2, rpm: 60, rps: 1, intervalMs: 1e3 },
58
+ libre: { maxConcurrency: 6, rpm: 120, rps: 2, intervalMs: 500 },
59
+ deepl: { maxConcurrency: 4, rpm: 90, rps: 1.5, intervalMs: 600 },
60
+ llm: { maxConcurrency: 2, rpm: 30, rps: 0.5, intervalMs: 1500 }
61
+ };
62
+ function parseEnvPositiveInt(raw) {
63
+ if (raw === void 0 || raw.trim() === "") return void 0;
64
+ const n = Number.parseInt(raw.trim(), 10);
65
+ if (!Number.isFinite(n) || n < 1) return void 0;
66
+ return n;
67
+ }
68
+ function resolveTranslateMaxParallel(input) {
69
+ let n = input.configMaxWorkers ?? 1;
70
+ const env = parseEnvPositiveInt(input.envMaxWorkers);
71
+ if (env !== void 0) n = env;
72
+ if (input.workersFlag !== void 0) n = input.workersFlag;
73
+ return clampInt(n, 1, TRANSLATE_WORKERS_CAP);
74
+ }
75
+
76
+ // src/types/translator/policy.ts
77
+ var TRANSLATE_POLICY_DEFAULTS = Object.freeze({
78
+ routing: "single",
79
+ onRateLimit: "backoff",
80
+ onTransientFailure: "retry",
81
+ onQuotaExceeded: "fallback",
82
+ onAuthFailure: "abort",
83
+ onProviderUnavailable: "fallback",
84
+ onIdentityOutput: "flag",
85
+ onIncompleteRun: "confirm",
86
+ handoff: "auto"
87
+ });
88
+
89
+ // src/config/schema/translate.ts
90
+ function clampTranslateMaxWorkers(n) {
91
+ if (!Number.isFinite(n)) return 1;
92
+ return Math.min(TRANSLATE_WORKERS_CAP, Math.max(1, Math.trunc(n)));
93
+ }
94
+ var translateThrottleSchema = z.object({
95
+ maxConcurrency: z.number().int().positive().max(TRANSLATE_WORKERS_CAP).optional().describe("Cap parallel translateLeaf calls for this row (core merges with CLI --workers / translate.workers)."),
96
+ rpm: z.number().int().positive().optional().describe("Vendor-friendly requests/minute ceiling (enforced when orchestration supports it)."),
97
+ rps: z.number().positive().optional().describe("Optional requests/second ceiling (can be fractional, e.g. 0.5 = 1 request every 2 seconds)."),
98
+ intervalMs: z.number().int().nonnegative().optional().describe("Minimum spacing between requests originating from this row / merged defaults.")
99
+ }).strict().transform((t) => ({
100
+ maxConcurrency: t.maxConcurrency,
101
+ rpm: t.rpm,
102
+ rps: t.rps,
103
+ intervalMs: t.intervalMs
104
+ }));
105
+ var translateGoogleRowSchema = z.object({
106
+ id: z.literal("google"),
107
+ enabled: z.boolean().optional().describe("When false, row is ignored for merges (rich-init scaffolding)."),
108
+ rateLimit: translateThrottleSchema.optional()
109
+ }).strict().describe("Google Translate web (gtx) backend.");
110
+ var translateMymemoryRowSchema = z.object({
111
+ id: z.literal("mymemory"),
112
+ enabled: z.boolean().optional(),
113
+ contactEmail: z.string().optional().describe("Fair-use contact email for MyMemory API attribution."),
114
+ rateLimit: translateThrottleSchema.optional()
115
+ }).strict();
116
+ var translateLibreRowSchema = z.object({
117
+ id: z.literal("libre"),
118
+ enabled: z.boolean().optional(),
119
+ baseUrl: z.string().optional().describe("LibreTranslate base URL when not using the public instance."),
120
+ rateLimit: translateThrottleSchema.optional()
121
+ }).strict();
122
+ var translateDeeplRowSchema = z.object({
123
+ id: z.literal("deepl"),
124
+ enabled: z.boolean().optional(),
125
+ apiKey: z.string().optional().describe("DeepL API key (or set via env; see docs)."),
126
+ rateLimit: translateThrottleSchema.optional()
127
+ }).strict();
128
+ var translateLlmRowSchema = z.object({
129
+ id: z.literal("llm"),
130
+ enabled: z.boolean().optional(),
131
+ apiKey: z.string().optional().describe("Bearer / API key for the OpenAI-compatible endpoint."),
132
+ baseUrl: z.string().optional().describe("Base URL including /v1 when required by the host."),
133
+ model: z.string().optional().describe("Chat model id for completions."),
134
+ rateLimit: translateThrottleSchema.optional()
135
+ }).strict();
136
+ var translateProviderRowSchema = z.discriminatedUnion("id", [
137
+ translateGoogleRowSchema,
138
+ translateMymemoryRowSchema,
139
+ translateLibreRowSchema,
140
+ translateDeeplRowSchema,
141
+ translateLlmRowSchema
142
+ ]);
143
+ var translatePrimaryIdSchema = z.enum(["google", "mymemory", "libre", "deepl", "llm"]);
144
+ var translatePolicySchema = z.object({
145
+ routing: z.enum(["single", "auto"]).default(TRANSLATE_POLICY_DEFAULTS.routing).describe(
146
+ "single: one backend per run. auto: ordered fallback chain across enabled providers for retryable backend failures."
147
+ ),
148
+ onRateLimit: z.enum(["backoff", "retry", "fallback", "abort"]).default(TRANSLATE_POLICY_DEFAULTS.onRateLimit).describe('Action for HTTP 429 / "too many requests" outcomes.'),
149
+ onTransientFailure: z.enum(["retry", "fallback", "abort"]).default(TRANSLATE_POLICY_DEFAULTS.onTransientFailure).describe("Action for transient network blips and ECONNRESET-style errors."),
150
+ onQuotaExceeded: z.enum(["fallback", "prompt", "abort"]).default(TRANSLATE_POLICY_DEFAULTS.onQuotaExceeded).describe("Action when the provider explicitly reports daily/monthly quota exhausted."),
151
+ onAuthFailure: z.enum(["abort", "prompt"]).default(TRANSLATE_POLICY_DEFAULTS.onAuthFailure).describe("Action for HTTP 401 / 403 \u2014 never silently swap on credential failure."),
152
+ onProviderUnavailable: z.enum(["fallback", "abort"]).default(TRANSLATE_POLICY_DEFAULTS.onProviderUnavailable).describe("Action for sustained 5xx / DNS unavailable."),
153
+ onIdentityOutput: z.enum(["flag", "fallback", "abort"]).default(TRANSLATE_POLICY_DEFAULTS.onIdentityOutput).describe("Action when the provider returned the source string unchanged."),
154
+ onIncompleteRun: z.enum(["confirm", "write", "discard"]).default(TRANSLATE_POLICY_DEFAULTS.onIncompleteRun).describe("Action when a run cannot finish all leaves (cap hit or interrupt)."),
155
+ maxAttempts: z.number().int().positive().optional().describe(
156
+ "Cross-provider attempts per leaf. When omitted, defaults to providers.length at parse time."
157
+ ),
158
+ handoff: z.enum(["auto", "on", "off"]).default(TRANSLATE_POLICY_DEFAULTS.handoff).describe("Mid-run rescue picker: auto = TTY-only when routing=single; on = always TTY; off = never.")
159
+ }).strict().describe("Translation orchestration: routing + per-outcome actions consumed by the policy resolver.");
160
+ var translateWorkersField = z.number().int().positive().max(TRANSLATE_WORKERS_CAP).describe("Max parallel translateLeaf calls when --workers flag and env omit (1 = serial).");
161
+ var translateInnerSchema = z.object({
162
+ primary: translatePrimaryIdSchema.describe(
163
+ "Default backend when CLI --provider and I18NPRUNE_TRANSLATE_PROVIDER omit; must match an enabled providers[] row."
164
+ ),
165
+ providers: z.array(translateProviderRowSchema).min(1),
166
+ policy: translatePolicySchema.default({ ...TRANSLATE_POLICY_DEFAULTS }),
167
+ workers: translateWorkersField.optional()
168
+ }).strict().transform((t) => ({
169
+ primary: t.primary,
170
+ providers: t.providers,
171
+ /**
172
+ * `maxAttempts` defaults to `providers.length` (one shot per provider in chain) per
173
+ * `translate-policy (shipped)` §6. Resolved here because the default
174
+ * depends on a sibling field — Zod's static `.default()` can't express that.
175
+ */
176
+ policy: { ...t.policy, maxAttempts: t.policy.maxAttempts ?? t.providers.length },
177
+ workers: t.workers ?? 1
178
+ })).superRefine((data, ctx) => {
179
+ const ids = data.providers.map((p) => p.id);
180
+ if (new Set(ids).size !== ids.length) {
181
+ ctx.addIssue({
182
+ code: z.ZodIssueCode.custom,
183
+ message: "translate.providers must not contain duplicate id values",
184
+ path: ["providers"]
185
+ });
186
+ }
187
+ const primaryRow = data.providers.find((p) => p.id === data.primary && p.enabled !== false);
188
+ if (!primaryRow) {
189
+ ctx.addIssue({
190
+ code: z.ZodIssueCode.custom,
191
+ message: "translate.primary must match at least one translate.providers entry with enabled !== false",
192
+ path: ["primary"]
193
+ });
194
+ }
195
+ }).describe("Translation registry + orchestration policy for generate (including `generate --resume`).");
196
+ var translateSchema = translateInnerSchema.optional().describe(
197
+ "Optional translate namespace; omit entirely to follow env + built-in default (google) without file-backed rows."
198
+ );
199
+
200
+ // src/config/schema/define.ts
201
+ function defineConfig(config) {
202
+ const refDef = DEFAULT_CONFIG.reference?.defaults;
203
+ const refCmd = DEFAULT_CONFIG.reference?.commands;
204
+ const merged = {
205
+ ...DEFAULT_CONFIG,
206
+ ...config,
207
+ functions: config.functions?.length ? config.functions : DEFAULT_CONFIG.functions,
208
+ policies: { ...DEFAULT_CONFIG.policies, ...config.policies },
209
+ reference: {
210
+ ...DEFAULT_CONFIG.reference,
211
+ ...config.reference,
212
+ defaults: { ...refDef, ...config.reference?.defaults },
213
+ commands: { ...refCmd, ...config.reference?.commands }
214
+ },
215
+ missing: { ...DEFAULT_CONFIG.missing, ...config.missing },
216
+ locales: { ...DEFAULT_CONFIG.locales, ...config.locales },
217
+ translate: config.translate !== void 0 ? {
218
+ ...DEFAULT_CONFIG.translate,
219
+ ...config.translate,
220
+ workers: config.translate.workers === void 0 ? 1 : typeof config.translate.workers === "number" ? clampTranslateMaxWorkers(config.translate.workers) : config.translate.workers
221
+ } : DEFAULT_CONFIG.translate
222
+ };
223
+ return merged;
224
+ }
225
+
226
+ // src/config/schema/localesCompat.ts
227
+ function defaultStructureForMode(mode) {
228
+ return mode === "flat_file" ? "locale_file" : "locale_per_dir";
229
+ }
230
+ function collectLocalesFilesystemConfigWarnings(locales) {
231
+ const warnings = [];
232
+ const mode = locales.mode ?? "flat_file";
233
+ const structure = locales.structure ?? defaultStructureForMode(mode);
234
+ if (mode === "flat_file" && structure !== "locale_file") {
235
+ warnings.push(
236
+ `locales.structure "${structure}" does not apply when locales.mode is "flat_file"; use "locale_file" or set mode to "locale_directory".`
237
+ );
238
+ }
239
+ if (mode === "locale_directory" && structure === "locale_file") {
240
+ warnings.push(
241
+ `locales.structure "locale_file" does not apply when locales.mode is "locale_directory"; use "locale_per_dir" or "feature_bundle".`
242
+ );
243
+ }
244
+ return warnings;
245
+ }
246
+
247
+ // src/shared/constants/issueCodes.ts
248
+ var ISSUE_PROJECT_LOCALES_SOURCE_NOT_LANGUAGE_CODE = "i18nprune.project.locales_source_not_language_code";
249
+ var ISSUE_PROJECT_LOCALES_SOURCE_NOT_IN_BUNDLE = "i18nprune.project.locales_source_not_in_bundle";
250
+ var ISSUE_LANGUAGES_UNSUPPORTED_LANGUAGE_CODE = "i18nprune.languages.unsupported_language_code";
251
+ var ISSUE_CONFIG_MISSING = "i18nprune.config.missing";
252
+ var ISSUE_CONFIG_INVALID = "i18nprune.config.invalid";
253
+ var ISSUE_CONFIG_LOAD_FAILED = "i18nprune.config.load_failed";
254
+ var ISSUE_TRANSLATE_CONFIG_DEFAULT_APPLIED = "i18nprune.translate.config_default_applied";
255
+
256
+ // src/shared/languages/catalog/languages.json
257
+ var languages_default = [
258
+ {
259
+ code: "ab",
260
+ english: "Abkhazian",
261
+ native: "Abkhazian",
262
+ direction: "ltr"
263
+ },
264
+ {
265
+ code: "ace",
266
+ english: "Acehnese",
267
+ native: "Acehnese",
268
+ direction: "ltr"
269
+ },
270
+ {
271
+ code: "ach",
272
+ english: "Acoli",
273
+ native: "Acoli",
274
+ direction: "ltr"
275
+ },
276
+ {
277
+ code: "af",
278
+ english: "Afrikaans",
279
+ native: "Afrikaans",
280
+ direction: "ltr"
281
+ },
282
+ {
283
+ code: "ak",
284
+ english: "Akan",
285
+ native: "Akan",
286
+ direction: "ltr"
287
+ },
288
+ {
289
+ code: "alz",
290
+ english: "alz",
291
+ native: "alz",
292
+ direction: "ltr"
293
+ },
294
+ {
295
+ code: "am",
296
+ english: "Amharic",
297
+ native: "\u12A0\u121B\u122D\u129B",
298
+ direction: "ltr"
299
+ },
300
+ {
301
+ code: "ar",
302
+ english: "Arabic",
303
+ native: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629",
304
+ direction: "rtl"
305
+ },
306
+ {
307
+ code: "ar-sa",
308
+ english: "Arabic (Saudi Arabia)",
309
+ native: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629 (\u0627\u0644\u0645\u0645\u0644\u0643\u0629 \u0627\u0644\u0639\u0631\u0628\u064A\u0629 \u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629)",
310
+ direction: "rtl"
311
+ },
312
+ {
313
+ code: "as",
314
+ english: "Assamese",
315
+ native: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE",
316
+ direction: "ltr"
317
+ },
318
+ {
319
+ code: "awa",
320
+ english: "Awadhi",
321
+ native: "Awadhi",
322
+ direction: "ltr"
323
+ },
324
+ {
325
+ code: "ay",
326
+ english: "Aymara",
327
+ native: "Aymara",
328
+ direction: "ltr"
329
+ },
330
+ {
331
+ code: "az",
332
+ english: "Azerbaijani",
333
+ native: "az\u0259rbaycan",
334
+ direction: "ltr"
335
+ },
336
+ {
337
+ code: "ba",
338
+ english: "Bashkir",
339
+ native: "Bashkir",
340
+ direction: "ltr"
341
+ },
342
+ {
343
+ code: "ban",
344
+ english: "Balinese",
345
+ native: "Balinese",
346
+ direction: "ltr"
347
+ },
348
+ {
349
+ code: "bbc",
350
+ english: "Batak Toba",
351
+ native: "Batak Toba",
352
+ direction: "ltr"
353
+ },
354
+ {
355
+ code: "be",
356
+ english: "Belarusian",
357
+ native: "\u0431\u0435\u043B\u0430\u0440\u0443\u0441\u043A\u0430\u044F",
358
+ direction: "ltr"
359
+ },
360
+ {
361
+ code: "bem",
362
+ english: "Bemba",
363
+ native: "Ichibemba",
364
+ direction: "ltr"
365
+ },
366
+ {
367
+ code: "bew",
368
+ english: "Betawi",
369
+ native: "Betawi",
370
+ direction: "ltr"
371
+ },
372
+ {
373
+ code: "bg",
374
+ english: "Bulgarian",
375
+ native: "\u0431\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438",
376
+ direction: "ltr"
377
+ },
378
+ {
379
+ code: "bho",
380
+ english: "Bhojpuri",
381
+ native: "\u092D\u094B\u091C\u092A\u0941\u0930\u0940",
382
+ direction: "ltr"
383
+ },
384
+ {
385
+ code: "bik",
386
+ english: "Bikol",
387
+ native: "Bikol",
388
+ direction: "ltr"
389
+ },
390
+ {
391
+ code: "bm",
392
+ english: "Bambara",
393
+ native: "bamanakan",
394
+ direction: "ltr"
395
+ },
396
+ {
397
+ code: "bn",
398
+ english: "Bangla",
399
+ native: "\u09AC\u09BE\u0982\u09B2\u09BE",
400
+ direction: "ltr"
401
+ },
402
+ {
403
+ code: "bn-in",
404
+ english: "Bangla (India)",
405
+ native: "\u09AC\u09BE\u0982\u09B2\u09BE (\u09AD\u09BE\u09B0\u09A4)",
406
+ direction: "ltr"
407
+ },
408
+ {
409
+ code: "br",
410
+ english: "Breton",
411
+ native: "brezhoneg",
412
+ direction: "ltr"
413
+ },
414
+ {
415
+ code: "bs",
416
+ english: "Bosnian",
417
+ native: "bosanski",
418
+ direction: "ltr"
419
+ },
420
+ {
421
+ code: "bs-cyrl",
422
+ english: "Bosnian (Cyrillic)",
423
+ native: "\u0431\u043E\u0441\u0430\u043D\u0441\u043A\u0438 (\u045B\u0438\u0440\u0438\u043B\u0438\u0446\u0430)",
424
+ direction: "ltr"
425
+ },
426
+ {
427
+ code: "bts",
428
+ english: "bts",
429
+ native: "bts",
430
+ direction: "ltr"
431
+ },
432
+ {
433
+ code: "btx",
434
+ english: "btx",
435
+ native: "btx",
436
+ direction: "ltr"
437
+ },
438
+ {
439
+ code: "bua",
440
+ english: "Buriat",
441
+ native: "Buriat",
442
+ direction: "ltr"
443
+ },
444
+ {
445
+ code: "ca",
446
+ english: "Catalan",
447
+ native: "catal\xE0",
448
+ direction: "ltr"
449
+ },
450
+ {
451
+ code: "ceb",
452
+ english: "Cebuano",
453
+ native: "Cebuano",
454
+ direction: "ltr"
455
+ },
456
+ {
457
+ code: "cgg",
458
+ english: "Chiga",
459
+ native: "Rukiga",
460
+ direction: "ltr"
461
+ },
462
+ {
463
+ code: "chm",
464
+ english: "Mari",
465
+ native: "Mari",
466
+ direction: "ltr"
467
+ },
468
+ {
469
+ code: "ckb",
470
+ english: "Central Kurdish",
471
+ native: "\u06A9\u0648\u0631\u062F\u06CC\u06CC \u0646\u0627\u0648\u06D5\u0646\u062F\u06CC",
472
+ direction: "rtl"
473
+ },
474
+ {
475
+ code: "cnh",
476
+ english: "cnh",
477
+ native: "cnh",
478
+ direction: "ltr"
479
+ },
480
+ {
481
+ code: "co",
482
+ english: "Corsican",
483
+ native: "Corsican",
484
+ direction: "ltr"
485
+ },
486
+ {
487
+ code: "crh",
488
+ english: "Crimean Tatar",
489
+ native: "Crimean Tatar",
490
+ direction: "ltr"
491
+ },
492
+ {
493
+ code: "crs",
494
+ english: "Seselwa Creole French",
495
+ native: "Seselwa Creole French",
496
+ direction: "ltr"
497
+ },
498
+ {
499
+ code: "cs",
500
+ english: "Czech",
501
+ native: "\u010De\u0161tina",
502
+ direction: "ltr"
503
+ },
504
+ {
505
+ code: "cv",
506
+ english: "Chuvash",
507
+ native: "\u0447\u04D1\u0432\u0430\u0448",
508
+ direction: "ltr"
509
+ },
510
+ {
511
+ code: "cy",
512
+ english: "Welsh",
513
+ native: "Cymraeg",
514
+ direction: "ltr"
515
+ },
516
+ {
517
+ code: "da",
518
+ english: "Danish",
519
+ native: "dansk",
520
+ direction: "ltr"
521
+ },
522
+ {
523
+ code: "de",
524
+ english: "German",
525
+ native: "Deutsch",
526
+ direction: "ltr"
527
+ },
528
+ {
529
+ code: "din",
530
+ english: "Dinka",
531
+ native: "Dinka",
532
+ direction: "ltr"
533
+ },
534
+ {
535
+ code: "doi",
536
+ english: "Dogri",
537
+ native: "\u0921\u094B\u0917\u0930\u0940",
538
+ direction: "ltr"
539
+ },
540
+ {
541
+ code: "dov",
542
+ english: "dov",
543
+ native: "dov",
544
+ direction: "ltr"
545
+ },
546
+ {
547
+ code: "dv",
548
+ english: "Divehi",
549
+ native: "Divehi",
550
+ direction: "rtl"
551
+ },
552
+ {
553
+ code: "dz",
554
+ english: "Dzongkha",
555
+ native: "\u0F62\u0FAB\u0F7C\u0F44\u0F0B\u0F41",
556
+ direction: "ltr"
557
+ },
558
+ {
559
+ code: "ee",
560
+ english: "Ewe",
561
+ native: "E\u028Begbe",
562
+ direction: "ltr"
563
+ },
564
+ {
565
+ code: "el",
566
+ english: "Greek",
567
+ native: "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC",
568
+ direction: "ltr"
569
+ },
570
+ {
571
+ code: "en",
572
+ english: "English",
573
+ native: "English",
574
+ direction: "ltr"
575
+ },
576
+ {
577
+ code: "en-au",
578
+ english: "Australian English",
579
+ native: "Australian English",
580
+ direction: "ltr"
581
+ },
582
+ {
583
+ code: "en-ca",
584
+ english: "Canadian English",
585
+ native: "Canadian English",
586
+ direction: "ltr"
587
+ },
588
+ {
589
+ code: "en-gb",
590
+ english: "British English",
591
+ native: "British English",
592
+ direction: "ltr"
593
+ },
594
+ {
595
+ code: "en-nz",
596
+ english: "English (New Zealand)",
597
+ native: "English (New Zealand)",
598
+ direction: "ltr"
599
+ },
600
+ {
601
+ code: "en-ph",
602
+ english: "English (Philippines)",
603
+ native: "English (Philippines)",
604
+ direction: "ltr"
605
+ },
606
+ {
607
+ code: "en-us",
608
+ english: "American English",
609
+ native: "American English",
610
+ direction: "ltr"
611
+ },
612
+ {
613
+ code: "en-za",
614
+ english: "English (South Africa)",
615
+ native: "English (South Africa)",
616
+ direction: "ltr"
617
+ },
618
+ {
619
+ code: "eo",
620
+ english: "Esperanto",
621
+ native: "Esperanto",
622
+ direction: "ltr"
623
+ },
624
+ {
625
+ code: "es",
626
+ english: "Spanish",
627
+ native: "espa\xF1ol",
628
+ direction: "ltr"
629
+ },
630
+ {
631
+ code: "es-419",
632
+ english: "Latin American Spanish",
633
+ native: "espa\xF1ol latinoamericano",
634
+ direction: "ltr"
635
+ },
636
+ {
637
+ code: "es-ar",
638
+ english: "Spanish (Argentina)",
639
+ native: "espa\xF1ol (Argentina)",
640
+ direction: "ltr"
641
+ },
642
+ {
643
+ code: "es-cl",
644
+ english: "Spanish (Chile)",
645
+ native: "espa\xF1ol (Chile)",
646
+ direction: "ltr"
647
+ },
648
+ {
649
+ code: "es-co",
650
+ english: "Spanish (Colombia)",
651
+ native: "espa\xF1ol (Colombia)",
652
+ direction: "ltr"
653
+ },
654
+ {
655
+ code: "es-cr",
656
+ english: "Spanish (Costa Rica)",
657
+ native: "espa\xF1ol (Costa Rica)",
658
+ direction: "ltr"
659
+ },
660
+ {
661
+ code: "es-ec",
662
+ english: "Spanish (Ecuador)",
663
+ native: "espa\xF1ol (Ecuador)",
664
+ direction: "ltr"
665
+ },
666
+ {
667
+ code: "es-es",
668
+ english: "European Spanish",
669
+ native: "espa\xF1ol de Espa\xF1a",
670
+ direction: "ltr"
671
+ },
672
+ {
673
+ code: "es-gt",
674
+ english: "Spanish (Guatemala)",
675
+ native: "espa\xF1ol (Guatemala)",
676
+ direction: "ltr"
677
+ },
678
+ {
679
+ code: "es-hn",
680
+ english: "Spanish (Honduras)",
681
+ native: "espa\xF1ol (Honduras)",
682
+ direction: "ltr"
683
+ },
684
+ {
685
+ code: "es-ht",
686
+ english: "Spanish (Haiti)",
687
+ native: "espa\xF1ol (Hait\xED)",
688
+ direction: "ltr"
689
+ },
690
+ {
691
+ code: "es-mx",
692
+ english: "Mexican Spanish",
693
+ native: "espa\xF1ol de M\xE9xico",
694
+ direction: "ltr"
695
+ },
696
+ {
697
+ code: "es-ni",
698
+ english: "Spanish (Nicaragua)",
699
+ native: "espa\xF1ol (Nicaragua)",
700
+ direction: "ltr"
701
+ },
702
+ {
703
+ code: "es-pa",
704
+ english: "Spanish (Panama)",
705
+ native: "espa\xF1ol (Panam\xE1)",
706
+ direction: "ltr"
707
+ },
708
+ {
709
+ code: "es-pe",
710
+ english: "Spanish (Peru)",
711
+ native: "espa\xF1ol (Per\xFA)",
712
+ direction: "ltr"
713
+ },
714
+ {
715
+ code: "es-pr",
716
+ english: "Spanish (Puerto Rico)",
717
+ native: "espa\xF1ol (Puerto Rico)",
718
+ direction: "ltr"
719
+ },
720
+ {
721
+ code: "es-py",
722
+ english: "Spanish (Paraguay)",
723
+ native: "espa\xF1ol (Paraguay)",
724
+ direction: "ltr"
725
+ },
726
+ {
727
+ code: "es-sv",
728
+ english: "Spanish (El Salvador)",
729
+ native: "espa\xF1ol (El Salvador)",
730
+ direction: "ltr"
731
+ },
732
+ {
733
+ code: "es-us",
734
+ english: "Spanish (United States)",
735
+ native: "espa\xF1ol (Estados Unidos)",
736
+ direction: "ltr"
737
+ },
738
+ {
739
+ code: "es-uy",
740
+ english: "Spanish (Uruguay)",
741
+ native: "espa\xF1ol (Uruguay)",
742
+ direction: "ltr"
743
+ },
744
+ {
745
+ code: "es-ve",
746
+ english: "Spanish (Venezuela)",
747
+ native: "espa\xF1ol (Venezuela)",
748
+ direction: "ltr"
749
+ },
750
+ {
751
+ code: "et",
752
+ english: "Estonian",
753
+ native: "eesti",
754
+ direction: "ltr"
755
+ },
756
+ {
757
+ code: "eu",
758
+ english: "Basque",
759
+ native: "euskara",
760
+ direction: "ltr"
761
+ },
762
+ {
763
+ code: "fa",
764
+ english: "Persian",
765
+ native: "\u0641\u0627\u0631\u0633\u06CC",
766
+ direction: "rtl"
767
+ },
768
+ {
769
+ code: "ff",
770
+ english: "Fula",
771
+ native: "Pulaar",
772
+ direction: "rtl"
773
+ },
774
+ {
775
+ code: "fi",
776
+ english: "Finnish",
777
+ native: "suomi",
778
+ direction: "ltr"
779
+ },
780
+ {
781
+ code: "fil",
782
+ english: "Filipino",
783
+ native: "Filipino",
784
+ direction: "ltr"
785
+ },
786
+ {
787
+ code: "fj",
788
+ english: "Fijian",
789
+ native: "Fijian",
790
+ direction: "ltr"
791
+ },
792
+ {
793
+ code: "fr",
794
+ english: "French",
795
+ native: "fran\xE7ais",
796
+ direction: "ltr"
797
+ },
798
+ {
799
+ code: "fr-ca",
800
+ english: "Canadian French",
801
+ native: "fran\xE7ais canadien",
802
+ direction: "ltr"
803
+ },
804
+ {
805
+ code: "fr-ch",
806
+ english: "Swiss French",
807
+ native: "fran\xE7ais suisse",
808
+ direction: "ltr"
809
+ },
810
+ {
811
+ code: "fr-fr",
812
+ english: "French (France)",
813
+ native: "fran\xE7ais (France)",
814
+ direction: "ltr"
815
+ },
816
+ {
817
+ code: "fy",
818
+ english: "Western Frisian",
819
+ native: "Frysk",
820
+ direction: "ltr"
821
+ },
822
+ {
823
+ code: "ga",
824
+ english: "Irish",
825
+ native: "Gaeilge",
826
+ direction: "ltr"
827
+ },
828
+ {
829
+ code: "gaa",
830
+ english: "Ga",
831
+ native: "Ga",
832
+ direction: "ltr"
833
+ },
834
+ {
835
+ code: "gd",
836
+ english: "Scottish Gaelic",
837
+ native: "G\xE0idhlig",
838
+ direction: "ltr"
839
+ },
840
+ {
841
+ code: "gl",
842
+ english: "Galician",
843
+ native: "galego",
844
+ direction: "ltr"
845
+ },
846
+ {
847
+ code: "gn",
848
+ english: "Guarani",
849
+ native: "Guarani",
850
+ direction: "ltr"
851
+ },
852
+ {
853
+ code: "gom",
854
+ english: "Goan Konkani",
855
+ native: "Goan Konkani",
856
+ direction: "ltr"
857
+ },
858
+ {
859
+ code: "gu",
860
+ english: "Gujarati",
861
+ native: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0",
862
+ direction: "ltr"
863
+ },
864
+ {
865
+ code: "ha",
866
+ english: "Hausa",
867
+ native: "Hausa",
868
+ direction: "rtl"
869
+ },
870
+ {
871
+ code: "haw",
872
+ english: "Hawaiian",
873
+ native: "\u02BB\u014Clelo Hawai\u02BBi",
874
+ direction: "ltr"
875
+ },
876
+ {
877
+ code: "he",
878
+ english: "Hebrew",
879
+ native: "\u05E2\u05D1\u05E8\u05D9\u05EA",
880
+ direction: "rtl"
881
+ },
882
+ {
883
+ code: "hi",
884
+ english: "Hindi",
885
+ native: "\u0939\u093F\u0928\u094D\u0926\u0940",
886
+ direction: "ltr"
887
+ },
888
+ {
889
+ code: "hil",
890
+ english: "Hiligaynon",
891
+ native: "Hiligaynon",
892
+ direction: "ltr"
893
+ },
894
+ {
895
+ code: "hmn",
896
+ english: "Hmong",
897
+ native: "Hmong",
898
+ direction: "ltr"
899
+ },
900
+ {
901
+ code: "hr",
902
+ english: "Croatian",
903
+ native: "hrvatski",
904
+ direction: "ltr"
905
+ },
906
+ {
907
+ code: "hrx",
908
+ english: "hrx",
909
+ native: "hrx",
910
+ direction: "ltr"
911
+ },
912
+ {
913
+ code: "ht",
914
+ english: "Haitian Creole",
915
+ native: "Haitian Creole",
916
+ direction: "ltr"
917
+ },
918
+ {
919
+ code: "hu",
920
+ english: "Hungarian",
921
+ native: "magyar",
922
+ direction: "ltr"
923
+ },
924
+ {
925
+ code: "hy",
926
+ english: "Armenian",
927
+ native: "\u0570\u0561\u0575\u0565\u0580\u0565\u0576",
928
+ direction: "ltr"
929
+ },
930
+ {
931
+ code: "id",
932
+ english: "Indonesian",
933
+ native: "Indonesia",
934
+ direction: "ltr"
935
+ },
936
+ {
937
+ code: "ig",
938
+ english: "Igbo",
939
+ native: "Igbo",
940
+ direction: "ltr"
941
+ },
942
+ {
943
+ code: "ilo",
944
+ english: "Iloko",
945
+ native: "Iloko",
946
+ direction: "ltr"
947
+ },
948
+ {
949
+ code: "is",
950
+ english: "Icelandic",
951
+ native: "\xEDslenska",
952
+ direction: "ltr"
953
+ },
954
+ {
955
+ code: "it",
956
+ english: "Italian",
957
+ native: "italiano",
958
+ direction: "ltr"
959
+ },
960
+ {
961
+ code: "iw",
962
+ english: "Hebrew",
963
+ native: "\u05E2\u05D1\u05E8\u05D9\u05EA",
964
+ direction: "rtl"
965
+ },
966
+ {
967
+ code: "ja",
968
+ english: "Japanese",
969
+ native: "\u65E5\u672C\u8A9E",
970
+ direction: "ltr"
971
+ },
972
+ {
973
+ code: "jv",
974
+ english: "Javanese",
975
+ native: "Jawa",
976
+ direction: "ltr"
977
+ },
978
+ {
979
+ code: "jw",
980
+ english: "Javanese",
981
+ native: "Jawa",
982
+ direction: "ltr"
983
+ },
984
+ {
985
+ code: "ka",
986
+ english: "Georgian",
987
+ native: "\u10E5\u10D0\u10E0\u10D7\u10E3\u10DA\u10D8",
988
+ direction: "ltr"
989
+ },
990
+ {
991
+ code: "kk",
992
+ english: "Kazakh",
993
+ native: "\u049B\u0430\u0437\u0430\u049B \u0442\u0456\u043B\u0456",
994
+ direction: "ltr"
995
+ },
996
+ {
997
+ code: "km",
998
+ english: "Khmer",
999
+ native: "\u1781\u17D2\u1798\u17C2\u179A",
1000
+ direction: "ltr"
1001
+ },
1002
+ {
1003
+ code: "kn",
1004
+ english: "Kannada",
1005
+ native: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1",
1006
+ direction: "ltr"
1007
+ },
1008
+ {
1009
+ code: "ko",
1010
+ english: "Korean",
1011
+ native: "\uD55C\uAD6D\uC5B4",
1012
+ direction: "ltr"
1013
+ },
1014
+ {
1015
+ code: "kri",
1016
+ english: "Krio",
1017
+ native: "Krio",
1018
+ direction: "ltr"
1019
+ },
1020
+ {
1021
+ code: "ktu",
1022
+ english: "ktu",
1023
+ native: "ktu",
1024
+ direction: "ltr"
1025
+ },
1026
+ {
1027
+ code: "ku",
1028
+ english: "Kurdish",
1029
+ native: "kurd\xEE (kurmanc\xEE)",
1030
+ direction: "ltr"
1031
+ },
1032
+ {
1033
+ code: "ky",
1034
+ english: "Kyrgyz",
1035
+ native: "\u043A\u044B\u0440\u0433\u044B\u0437\u0447\u0430",
1036
+ direction: "ltr"
1037
+ },
1038
+ {
1039
+ code: "la",
1040
+ english: "Latin",
1041
+ native: "Latin",
1042
+ direction: "ltr"
1043
+ },
1044
+ {
1045
+ code: "lb",
1046
+ english: "Luxembourgish",
1047
+ native: "L\xEBtzebuergesch",
1048
+ direction: "ltr"
1049
+ },
1050
+ {
1051
+ code: "lg",
1052
+ english: "Ganda",
1053
+ native: "Luganda",
1054
+ direction: "ltr"
1055
+ },
1056
+ {
1057
+ code: "li",
1058
+ english: "Limburgish",
1059
+ native: "Limburgish",
1060
+ direction: "ltr"
1061
+ },
1062
+ {
1063
+ code: "lij",
1064
+ english: "Ligurian",
1065
+ native: "ligure",
1066
+ direction: "ltr"
1067
+ },
1068
+ {
1069
+ code: "lmo",
1070
+ english: "Lombard",
1071
+ native: "Lombard",
1072
+ direction: "ltr"
1073
+ },
1074
+ {
1075
+ code: "ln",
1076
+ english: "Lingala",
1077
+ native: "ling\xE1la",
1078
+ direction: "ltr"
1079
+ },
1080
+ {
1081
+ code: "lo",
1082
+ english: "Lao",
1083
+ native: "\u0EA5\u0EB2\u0EA7",
1084
+ direction: "ltr"
1085
+ },
1086
+ {
1087
+ code: "lt",
1088
+ english: "Lithuanian",
1089
+ native: "lietuvi\u0173",
1090
+ direction: "ltr"
1091
+ },
1092
+ {
1093
+ code: "ltg",
1094
+ english: "Latgalian",
1095
+ native: "Latgalian",
1096
+ direction: "ltr"
1097
+ },
1098
+ {
1099
+ code: "luo",
1100
+ english: "Luo",
1101
+ native: "Dholuo",
1102
+ direction: "ltr"
1103
+ },
1104
+ {
1105
+ code: "lus",
1106
+ english: "Mizo",
1107
+ native: "Mizo",
1108
+ direction: "ltr"
1109
+ },
1110
+ {
1111
+ code: "lv",
1112
+ english: "Latvian",
1113
+ native: "latvie\u0161u",
1114
+ direction: "ltr"
1115
+ },
1116
+ {
1117
+ code: "mai",
1118
+ english: "Maithili",
1119
+ native: "\u092E\u0948\u0925\u093F\u0932\u0940",
1120
+ direction: "ltr"
1121
+ },
1122
+ {
1123
+ code: "mak",
1124
+ english: "Makasar",
1125
+ native: "Makasar",
1126
+ direction: "ltr"
1127
+ },
1128
+ {
1129
+ code: "mg",
1130
+ english: "Malagasy",
1131
+ native: "Malagasy",
1132
+ direction: "ltr"
1133
+ },
1134
+ {
1135
+ code: "mi",
1136
+ english: "M\u0101ori",
1137
+ native: "M\u0101ori",
1138
+ direction: "ltr"
1139
+ },
1140
+ {
1141
+ code: "min",
1142
+ english: "Minangkabau",
1143
+ native: "Minangkabau",
1144
+ direction: "ltr"
1145
+ },
1146
+ {
1147
+ code: "mk",
1148
+ english: "Macedonian",
1149
+ native: "\u043C\u0430\u043A\u0435\u0434\u043E\u043D\u0441\u043A\u0438",
1150
+ direction: "ltr"
1151
+ },
1152
+ {
1153
+ code: "ml",
1154
+ english: "Malayalam",
1155
+ native: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02",
1156
+ direction: "ltr"
1157
+ },
1158
+ {
1159
+ code: "mn",
1160
+ english: "Mongolian",
1161
+ native: "\u043C\u043E\u043D\u0433\u043E\u043B",
1162
+ direction: "ltr"
1163
+ },
1164
+ {
1165
+ code: "mni-mtei",
1166
+ english: "Manipuri (Meitei Mayek)",
1167
+ native: "\u09AE\u09C8\u09A4\u09C8\u09B2\u09CB\u09A8\u09CD (\u09AE\u09C0\u09A4\u09C8 \u09AE\u09AF\u09BC\u09C7\u0995)",
1168
+ direction: "ltr"
1169
+ },
1170
+ {
1171
+ code: "mr",
1172
+ english: "Marathi",
1173
+ native: "\u092E\u0930\u093E\u0920\u0940",
1174
+ direction: "ltr"
1175
+ },
1176
+ {
1177
+ code: "ms",
1178
+ english: "Malay",
1179
+ native: "Melayu",
1180
+ direction: "ltr"
1181
+ },
1182
+ {
1183
+ code: "ms-arab",
1184
+ english: "Malay (Arabic)",
1185
+ native: "Melayu (Arab)",
1186
+ direction: "ltr"
1187
+ },
1188
+ {
1189
+ code: "mt",
1190
+ english: "Maltese",
1191
+ native: "Malti",
1192
+ direction: "ltr"
1193
+ },
1194
+ {
1195
+ code: "my",
1196
+ english: "Burmese",
1197
+ native: "\u1019\u103C\u1014\u103A\u1019\u102C",
1198
+ direction: "ltr"
1199
+ },
1200
+ {
1201
+ code: "nb",
1202
+ english: "Norwegian Bokm\xE5l",
1203
+ native: "norsk bokm\xE5l",
1204
+ direction: "ltr"
1205
+ },
1206
+ {
1207
+ code: "ne",
1208
+ english: "Nepali",
1209
+ native: "\u0928\u0947\u092A\u093E\u0932\u0940",
1210
+ direction: "ltr"
1211
+ },
1212
+ {
1213
+ code: "new",
1214
+ english: "Newari",
1215
+ native: "Newari",
1216
+ direction: "ltr"
1217
+ },
1218
+ {
1219
+ code: "nl",
1220
+ english: "Dutch",
1221
+ native: "Nederlands",
1222
+ direction: "ltr"
1223
+ },
1224
+ {
1225
+ code: "nl-be",
1226
+ english: "Flemish",
1227
+ native: "Vlaams",
1228
+ direction: "ltr"
1229
+ },
1230
+ {
1231
+ code: "no",
1232
+ english: "Norwegian",
1233
+ native: "norsk",
1234
+ direction: "ltr"
1235
+ },
1236
+ {
1237
+ code: "nr",
1238
+ english: "South Ndebele",
1239
+ native: "South Ndebele",
1240
+ direction: "ltr"
1241
+ },
1242
+ {
1243
+ code: "nso",
1244
+ english: "Northern Sotho",
1245
+ native: "Northern Sotho",
1246
+ direction: "ltr"
1247
+ },
1248
+ {
1249
+ code: "nus",
1250
+ english: "Nuer",
1251
+ native: "Thok Nath",
1252
+ direction: "ltr"
1253
+ },
1254
+ {
1255
+ code: "ny",
1256
+ english: "Nyanja",
1257
+ native: "Nyanja",
1258
+ direction: "ltr"
1259
+ },
1260
+ {
1261
+ code: "oc",
1262
+ english: "Occitan",
1263
+ native: "occitan",
1264
+ direction: "ltr"
1265
+ },
1266
+ {
1267
+ code: "om",
1268
+ english: "Oromo",
1269
+ native: "Oromoo",
1270
+ direction: "ltr"
1271
+ },
1272
+ {
1273
+ code: "or",
1274
+ english: "Odia",
1275
+ native: "\u0B13\u0B21\u0B3C\u0B3F\u0B06",
1276
+ direction: "ltr"
1277
+ },
1278
+ {
1279
+ code: "pa",
1280
+ english: "Punjabi",
1281
+ native: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40",
1282
+ direction: "ltr"
1283
+ },
1284
+ {
1285
+ code: "pa-arab",
1286
+ english: "Punjabi (Arabic)",
1287
+ native: "\u067E\u0646\u062C\u0627\u0628\u06CC (\u0639\u0631\u0628\u06CC)",
1288
+ direction: "ltr"
1289
+ },
1290
+ {
1291
+ code: "pa-pk",
1292
+ english: "Punjabi (Pakistan)",
1293
+ native: "\u067E\u0646\u062C\u0627\u0628\u06CC (\u067E\u0627\u06A9\u0633\u062A\u0627\u0646)",
1294
+ direction: "ltr"
1295
+ },
1296
+ {
1297
+ code: "pag",
1298
+ english: "Pangasinan",
1299
+ native: "Pangasinan",
1300
+ direction: "ltr"
1301
+ },
1302
+ {
1303
+ code: "pam",
1304
+ english: "Pampanga",
1305
+ native: "Pampanga",
1306
+ direction: "ltr"
1307
+ },
1308
+ {
1309
+ code: "pap",
1310
+ english: "Papiamento",
1311
+ native: "Papiamento",
1312
+ direction: "ltr"
1313
+ },
1314
+ {
1315
+ code: "pl",
1316
+ english: "Polish",
1317
+ native: "polski",
1318
+ direction: "ltr"
1319
+ },
1320
+ {
1321
+ code: "ps",
1322
+ english: "Pashto",
1323
+ native: "\u067E\u069A\u062A\u0648",
1324
+ direction: "rtl"
1325
+ },
1326
+ {
1327
+ code: "pt",
1328
+ english: "Portuguese",
1329
+ native: "portugu\xEAs",
1330
+ direction: "ltr"
1331
+ },
1332
+ {
1333
+ code: "pt-br",
1334
+ english: "Brazilian Portuguese",
1335
+ native: "portugu\xEAs (Brasil)",
1336
+ direction: "ltr"
1337
+ },
1338
+ {
1339
+ code: "pt-pt",
1340
+ english: "European Portuguese",
1341
+ native: "portugu\xEAs europeu",
1342
+ direction: "ltr"
1343
+ },
1344
+ {
1345
+ code: "qu",
1346
+ english: "Quechua",
1347
+ native: "Runasimi",
1348
+ direction: "ltr"
1349
+ },
1350
+ {
1351
+ code: "rn",
1352
+ english: "Rundi",
1353
+ native: "Ikirundi",
1354
+ direction: "ltr"
1355
+ },
1356
+ {
1357
+ code: "ro",
1358
+ english: "Romanian",
1359
+ native: "rom\xE2n\u0103",
1360
+ direction: "ltr"
1361
+ },
1362
+ {
1363
+ code: "rom",
1364
+ english: "Romany",
1365
+ native: "Romany",
1366
+ direction: "ltr"
1367
+ },
1368
+ {
1369
+ code: "ru",
1370
+ english: "Russian",
1371
+ native: "\u0440\u0443\u0441\u0441\u043A\u0438\u0439",
1372
+ direction: "ltr"
1373
+ },
1374
+ {
1375
+ code: "rw",
1376
+ english: "Kinyarwanda",
1377
+ native: "Kinyarwanda",
1378
+ direction: "ltr"
1379
+ },
1380
+ {
1381
+ code: "sa",
1382
+ english: "Sanskrit",
1383
+ native: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924 \u092D\u093E\u0937\u093E",
1384
+ direction: "ltr"
1385
+ },
1386
+ {
1387
+ code: "scn",
1388
+ english: "Sicilian",
1389
+ native: "Sicilian",
1390
+ direction: "ltr"
1391
+ },
1392
+ {
1393
+ code: "sd",
1394
+ english: "Sindhi",
1395
+ native: "\u0633\u0646\u068C\u064A",
1396
+ direction: "rtl"
1397
+ },
1398
+ {
1399
+ code: "sg",
1400
+ english: "Sango",
1401
+ native: "S\xE4ng\xF6",
1402
+ direction: "ltr"
1403
+ },
1404
+ {
1405
+ code: "shn",
1406
+ english: "Shan",
1407
+ native: "Shan",
1408
+ direction: "ltr"
1409
+ },
1410
+ {
1411
+ code: "si",
1412
+ english: "Sinhala",
1413
+ native: "\u0DC3\u0DD2\u0D82\u0DC4\u0DBD",
1414
+ direction: "ltr"
1415
+ },
1416
+ {
1417
+ code: "sk",
1418
+ english: "Slovak",
1419
+ native: "sloven\u010Dina",
1420
+ direction: "ltr"
1421
+ },
1422
+ {
1423
+ code: "sl",
1424
+ english: "Slovenian",
1425
+ native: "sloven\u0161\u010Dina",
1426
+ direction: "ltr"
1427
+ },
1428
+ {
1429
+ code: "sm",
1430
+ english: "Samoan",
1431
+ native: "Samoan",
1432
+ direction: "ltr"
1433
+ },
1434
+ {
1435
+ code: "sn",
1436
+ english: "Shona",
1437
+ native: "chiShona",
1438
+ direction: "ltr"
1439
+ },
1440
+ {
1441
+ code: "so",
1442
+ english: "Somali",
1443
+ native: "Soomaali",
1444
+ direction: "ltr"
1445
+ },
1446
+ {
1447
+ code: "sq",
1448
+ english: "Albanian",
1449
+ native: "shqip",
1450
+ direction: "ltr"
1451
+ },
1452
+ {
1453
+ code: "sr",
1454
+ english: "Serbian",
1455
+ native: "\u0441\u0440\u043F\u0441\u043A\u0438",
1456
+ direction: "ltr"
1457
+ },
1458
+ {
1459
+ code: "ss",
1460
+ english: "Swati",
1461
+ native: "Swati",
1462
+ direction: "ltr"
1463
+ },
1464
+ {
1465
+ code: "st",
1466
+ english: "Southern Sotho",
1467
+ native: "Southern Sotho",
1468
+ direction: "ltr"
1469
+ },
1470
+ {
1471
+ code: "su",
1472
+ english: "Sundanese",
1473
+ native: "Basa Sunda",
1474
+ direction: "ltr"
1475
+ },
1476
+ {
1477
+ code: "sv",
1478
+ english: "Swedish",
1479
+ native: "svenska",
1480
+ direction: "ltr"
1481
+ },
1482
+ {
1483
+ code: "sw",
1484
+ english: "Swahili",
1485
+ native: "Kiswahili",
1486
+ direction: "ltr"
1487
+ },
1488
+ {
1489
+ code: "szl",
1490
+ english: "Silesian",
1491
+ native: "\u015Bl\u014Dnski",
1492
+ direction: "ltr"
1493
+ },
1494
+ {
1495
+ code: "ta",
1496
+ english: "Tamil",
1497
+ native: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD",
1498
+ direction: "ltr"
1499
+ },
1500
+ {
1501
+ code: "te",
1502
+ english: "Telugu",
1503
+ native: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41",
1504
+ direction: "ltr"
1505
+ },
1506
+ {
1507
+ code: "tet",
1508
+ english: "Tetum",
1509
+ native: "Tetum",
1510
+ direction: "ltr"
1511
+ },
1512
+ {
1513
+ code: "tg",
1514
+ english: "Tajik",
1515
+ native: "\u0442\u043E\u04B7\u0438\u043A\u04E3",
1516
+ direction: "ltr"
1517
+ },
1518
+ {
1519
+ code: "th",
1520
+ english: "Thai",
1521
+ native: "\u0E44\u0E17\u0E22",
1522
+ direction: "ltr"
1523
+ },
1524
+ {
1525
+ code: "ti",
1526
+ english: "Tigrinya",
1527
+ native: "\u1275\u130D\u122D\u129B",
1528
+ direction: "ltr"
1529
+ },
1530
+ {
1531
+ code: "tk",
1532
+ english: "Turkmen",
1533
+ native: "t\xFCrkmen dili",
1534
+ direction: "ltr"
1535
+ },
1536
+ {
1537
+ code: "tl",
1538
+ english: "Filipino",
1539
+ native: "Filipino",
1540
+ direction: "ltr"
1541
+ },
1542
+ {
1543
+ code: "tn",
1544
+ english: "Tswana",
1545
+ native: "Tswana",
1546
+ direction: "ltr"
1547
+ },
1548
+ {
1549
+ code: "tr",
1550
+ english: "Turkish",
1551
+ native: "T\xFCrk\xE7e",
1552
+ direction: "ltr"
1553
+ },
1554
+ {
1555
+ code: "ts",
1556
+ english: "Tsonga",
1557
+ native: "Tsonga",
1558
+ direction: "ltr"
1559
+ },
1560
+ {
1561
+ code: "tt",
1562
+ english: "Tatar",
1563
+ native: "\u0442\u0430\u0442\u0430\u0440",
1564
+ direction: "ltr"
1565
+ },
1566
+ {
1567
+ code: "ug",
1568
+ english: "Uyghur",
1569
+ native: "\u0626\u06C7\u064A\u063A\u06C7\u0631\u0686\u06D5",
1570
+ direction: "rtl"
1571
+ },
1572
+ {
1573
+ code: "uk",
1574
+ english: "Ukrainian",
1575
+ native: "\u0443\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430",
1576
+ direction: "ltr"
1577
+ },
1578
+ {
1579
+ code: "ur",
1580
+ english: "Urdu",
1581
+ native: "\u0627\u0631\u062F\u0648",
1582
+ direction: "rtl"
1583
+ },
1584
+ {
1585
+ code: "uz",
1586
+ english: "Uzbek",
1587
+ native: "o\u2018zbek",
1588
+ direction: "ltr"
1589
+ },
1590
+ {
1591
+ code: "vi",
1592
+ english: "Vietnamese",
1593
+ native: "Ti\u1EBFng Vi\u1EC7t",
1594
+ direction: "ltr"
1595
+ },
1596
+ {
1597
+ code: "xh",
1598
+ english: "Xhosa",
1599
+ native: "IsiXhosa",
1600
+ direction: "ltr"
1601
+ },
1602
+ {
1603
+ code: "yi",
1604
+ english: "Yiddish",
1605
+ native: "\u05D9\u05D9\u05B4\u05D3\u05D9\u05E9",
1606
+ direction: "rtl"
1607
+ },
1608
+ {
1609
+ code: "yo",
1610
+ english: "Yoruba",
1611
+ native: "\xC8d\xE8 Yor\xF9b\xE1",
1612
+ direction: "ltr"
1613
+ },
1614
+ {
1615
+ code: "yua",
1616
+ english: "yua",
1617
+ native: "yua",
1618
+ direction: "ltr"
1619
+ },
1620
+ {
1621
+ code: "yue",
1622
+ english: "Cantonese",
1623
+ native: "\u7CB5\u8A9E",
1624
+ direction: "ltr"
1625
+ },
1626
+ {
1627
+ code: "zh",
1628
+ english: "Chinese",
1629
+ native: "\u4E2D\u6587",
1630
+ direction: "ltr"
1631
+ },
1632
+ {
1633
+ code: "zh-cn",
1634
+ english: "Chinese (China)",
1635
+ native: "\u4E2D\u6587\uFF08\u4E2D\u56FD\uFF09",
1636
+ direction: "ltr"
1637
+ },
1638
+ {
1639
+ code: "zh-hans",
1640
+ english: "Simplified Chinese",
1641
+ native: "\u7B80\u4F53\u4E2D\u6587",
1642
+ direction: "ltr"
1643
+ },
1644
+ {
1645
+ code: "zh-hant",
1646
+ english: "Traditional Chinese",
1647
+ native: "\u7E41\u9AD4\u4E2D\u6587",
1648
+ direction: "ltr"
1649
+ },
1650
+ {
1651
+ code: "zh-hk",
1652
+ english: "Chinese (Hong Kong SAR China)",
1653
+ native: "\u4E2D\u6587\uFF08\u4E2D\u570B\u9999\u6E2F\u7279\u5225\u884C\u653F\u5340\uFF09",
1654
+ direction: "ltr"
1655
+ },
1656
+ {
1657
+ code: "zh-tw",
1658
+ english: "Chinese (Taiwan)",
1659
+ native: "\u4E2D\u6587\uFF08\u53F0\u7063\uFF09",
1660
+ direction: "ltr"
1661
+ },
1662
+ {
1663
+ code: "zu",
1664
+ english: "Zulu",
1665
+ native: "isiZulu",
1666
+ direction: "ltr"
1667
+ }
1668
+ ];
1669
+
1670
+ // src/shared/languages/normalize.ts
1671
+ function normalizeLanguageCode(code) {
1672
+ return code.trim().toLowerCase().replace(/_/g, "-");
1673
+ }
1674
+
1675
+ // src/shared/languages/catalog/index.ts
1676
+ var generatedLanguageCatalog = languages_default;
1677
+ function buildLanguageCatalog(raw) {
1678
+ return Object.freeze(raw.map((r) => ({ ...r, code: normalizeLanguageCode(r.code) })));
1679
+ }
1680
+ function filterLanguageCatalog(catalog2, filter) {
1681
+ const all = [...catalog2];
1682
+ const q = filter?.trim().toLowerCase();
1683
+ if (!q) return all.sort((a, b) => a.code.localeCompare(b.code));
1684
+ return all.filter(
1685
+ (r) => r.code.includes(q) || r.english.toLowerCase().includes(q) || r.native.toLowerCase().includes(q)
1686
+ ).sort((a, b) => a.code.localeCompare(b.code));
1687
+ }
1688
+ function getLanguageByCodeFromCatalog(catalog2, code) {
1689
+ const n = normalizeLanguageCode(code);
1690
+ return catalog2.find((r) => r.code === n);
1691
+ }
1692
+ function suggestCatalogCodesForInvalidInputFromCatalog(catalog2, code, maxCodesInCatalogHint = 5) {
1693
+ const n = normalizeLanguageCode(code);
1694
+ const all = [...catalog2].sort((a, b) => a.code.localeCompare(b.code));
1695
+ const out = [];
1696
+ const seen = /* @__PURE__ */ new Set();
1697
+ const push = (c) => {
1698
+ if (!seen.has(c)) {
1699
+ seen.add(c);
1700
+ out.push(c);
1701
+ }
1702
+ };
1703
+ for (const r of filterLanguageCatalog(catalog2, n)) {
1704
+ push(r.code);
1705
+ if (out.length >= maxCodesInCatalogHint) return out;
1706
+ }
1707
+ if (n.length >= 2) {
1708
+ const prefix = n.slice(0, 2);
1709
+ for (const r of all) {
1710
+ if (r.code.startsWith(prefix)) push(r.code);
1711
+ if (out.length >= maxCodesInCatalogHint) return out;
1712
+ }
1713
+ }
1714
+ for (const r of all) {
1715
+ push(r.code);
1716
+ if (out.length >= maxCodesInCatalogHint) return out;
1717
+ }
1718
+ return out;
1719
+ }
1720
+
1721
+ // src/config/locales/sourceValidate.ts
1722
+ var MAX_CATALOG_HINT = 5;
1723
+ var LANGUAGE_CODE_SHAPE = /^[a-z]{2}(-[a-z0-9]{2,8})*$/i;
1724
+ function catalog() {
1725
+ return buildLanguageCatalog(generatedLanguageCatalog);
1726
+ }
1727
+ function classifyLocalesSourceInput(raw) {
1728
+ const trimmed = raw.trim();
1729
+ if (trimmed.length === 0) return "invalid_shape";
1730
+ if (trimmed.includes("/") || trimmed.includes("\\")) return "path";
1731
+ if (trimmed.toLowerCase().endsWith(".json")) return "json_filename";
1732
+ if (!LANGUAGE_CODE_SHAPE.test(trimmed)) return "invalid_shape";
1733
+ return "language_code";
1734
+ }
1735
+ function catalogHintLine(code) {
1736
+ const hints = suggestCatalogCodesForInvalidInputFromCatalog(catalog(), code, MAX_CATALOG_HINT);
1737
+ return hints.length > 0 ? hints.join(", ") : null;
1738
+ }
1739
+ function localesSourceFieldMessage(raw, detail) {
1740
+ return `locales.source is ${JSON.stringify(raw)} in your config \u2014 ${detail}`;
1741
+ }
1742
+ function unsupportedLanguageCodeFailure(code, raw) {
1743
+ const hint = catalogHintLine(code);
1744
+ if (hint === null) {
1745
+ return {
1746
+ ok: false,
1747
+ issueCode: ISSUE_PROJECT_LOCALES_SOURCE_NOT_LANGUAGE_CODE,
1748
+ message: localesSourceFieldMessage(
1749
+ raw,
1750
+ `"${code}" is not a supported language code. Set a BCP47 tag only (e.g. en, pt-br), not a file path or *.json. Run i18nprune languages for the full catalog.`
1751
+ )
1752
+ };
1753
+ }
1754
+ return {
1755
+ ok: false,
1756
+ issueCode: ISSUE_LANGUAGES_UNSUPPORTED_LANGUAGE_CODE,
1757
+ message: localesSourceFieldMessage(
1758
+ raw,
1759
+ `"${code}" is not a supported language code \u2014 try: ${hint}. Set a BCP47 tag only (not a path or *.json). Run i18nprune languages for the full list.`
1760
+ )
1761
+ };
1762
+ }
1763
+ function rejectNotLanguageCode(raw, detail) {
1764
+ return {
1765
+ ok: false,
1766
+ issueCode: ISSUE_PROJECT_LOCALES_SOURCE_NOT_LANGUAGE_CODE,
1767
+ message: localesSourceFieldMessage(raw, detail)
1768
+ };
1769
+ }
1770
+ function validateLocalesSourceConfigValue(raw) {
1771
+ const trimmed = raw.trim();
1772
+ const kind = classifyLocalesSourceInput(trimmed);
1773
+ if (kind === "path") {
1774
+ const tail = trimmed.replace(/\\/g, "/").split("/").pop() ?? trimmed;
1775
+ if (tail.toLowerCase().endsWith(".json")) {
1776
+ const guess = normalizeLanguageCode(tail.replace(/\.json$/i, ""));
1777
+ if (!getLanguageByCodeFromCatalog(catalog(), guess)) {
1778
+ return unsupportedLanguageCodeFailure(guess, trimmed);
1779
+ }
1780
+ return rejectNotLanguageCode(
1781
+ trimmed,
1782
+ `use the language code "${guess}" instead of a file path (paths belong in locales.directory / layout, not locales.source).`
1783
+ );
1784
+ }
1785
+ return rejectNotLanguageCode(
1786
+ trimmed,
1787
+ "must be a BCP47 language code (e.g. en, pt-br), not a file path \u2014 remove directory segments and use only the locale tag."
1788
+ );
1789
+ }
1790
+ if (kind === "json_filename") {
1791
+ const guess = normalizeLanguageCode(trimmed.replace(/\.json$/i, ""));
1792
+ if (getLanguageByCodeFromCatalog(catalog(), guess)) {
1793
+ return rejectNotLanguageCode(
1794
+ trimmed,
1795
+ `use the language code "${guess}" instead of the filename "${trimmed}" (locales.source is the tag only; JSON paths come from locales.directory and layout).`
1796
+ );
1797
+ }
1798
+ const hinted = unsupportedLanguageCodeFailure(guess, trimmed);
1799
+ if (!hinted.ok && hinted.issueCode === ISSUE_LANGUAGES_UNSUPPORTED_LANGUAGE_CODE) {
1800
+ return hinted;
1801
+ }
1802
+ return rejectNotLanguageCode(
1803
+ trimmed,
1804
+ `use a supported language code instead of "${trimmed}". Run i18nprune languages for supported tags.`
1805
+ );
1806
+ }
1807
+ if (kind === "invalid_shape") {
1808
+ const candidate = normalizeLanguageCode(trimmed);
1809
+ const hinted = unsupportedLanguageCodeFailure(candidate, trimmed);
1810
+ if (!hinted.ok && hinted.issueCode === ISSUE_LANGUAGES_UNSUPPORTED_LANGUAGE_CODE) {
1811
+ return hinted;
1812
+ }
1813
+ return rejectNotLanguageCode(
1814
+ trimmed,
1815
+ "is not a valid language code shape. Use a BCP47 tag (e.g. en, pt-br). Run i18nprune languages for supported codes."
1816
+ );
1817
+ }
1818
+ const code = normalizeLanguageCode(trimmed);
1819
+ if (!getLanguageByCodeFromCatalog(catalog(), code)) {
1820
+ return unsupportedLanguageCodeFailure(code, trimmed);
1821
+ }
1822
+ return { ok: true, code };
1823
+ }
1824
+ function issueLocalesSourceNotInBundle(input) {
1825
+ const present = input.presentCodes.length > 0 ? input.presentCodes.map((c) => normalizeLanguageCode(c)).join(", ") : "(none discovered)";
1826
+ return {
1827
+ issueCode: ISSUE_PROJECT_LOCALES_SOURCE_NOT_IN_BUNDLE,
1828
+ message: `locales.source is "${input.sourceCode}" but no locale JSON segments for that code were found under ${input.directory}. Locales on disk: ${present}.`
1829
+ };
1830
+ }
1831
+
1832
+ // src/config/schema/root.ts
1833
+ var preserveSchema = z.object({
1834
+ copyKeys: z.array(z.string()).optional().describe("Locale key paths that must mirror the source locale verbatim (exact matches)."),
1835
+ copyPrefixes: z.array(z.string()).optional().describe("Key path prefixes treated like copyKeys (prefix match).")
1836
+ }).optional().describe("Preserve policy: keys/prefixes that must not be freely translated or drifted.");
1837
+ var paritySchema = z.object({
1838
+ excludeKeys: z.array(z.string()).optional().describe("Key paths excluded from source-identical / drift checks (exact)."),
1839
+ excludePrefixes: z.array(z.string()).optional().describe("Key path prefixes excluded from parity checks."),
1840
+ excludeValues: z.array(z.string()).optional().describe("Locale string values excluded from parity checks.")
1841
+ }).optional().describe("Parity policy: exclusions for \u201Csame as source\u201D style checks.");
1842
+ var policiesSchema = z.object({
1843
+ preserve: preserveSchema,
1844
+ parity: paritySchema
1845
+ }).optional().describe("High-level preserve / parity rules used across sync, quality, cleanup, generate, etc.");
1846
+ var missingCommandSchema = z.object({
1847
+ placeholder: z.string().optional().describe("String merged at each new key path for missing; omit for built-in sentinel.")
1848
+ }).strict().optional().describe("Defaults for the missing command.");
1849
+ var outputSchema = z.object({
1850
+ list: z.object({
1851
+ top: z.number().int().positive().max(1e5).optional().describe("Default max rows for bounded human lists when --top is omitted."),
1852
+ full: z.boolean().optional().describe("When true, show all rows where the command supports it."),
1853
+ maxCap: z.number().int().positive().max(1e6).optional().describe("Hard ceiling for list size even when full is used.")
1854
+ }).strict().optional().describe("Human-readable list caps for previews and summaries.")
1855
+ }).strict().optional().describe("CLI-wide defaults for human list-style output.");
1856
+ var referenceDefaultsSchema = z.object({
1857
+ treatCommentedCallSitesAsRuntime: z.boolean().optional().describe("When false, translation calls inside comments do not count as runtime evidence."),
1858
+ treatNonSourceFileSitesAsRuntime: z.boolean().optional().describe("When false, non-source file call sites are ignored for uncertainty."),
1859
+ uncertainKeyPolicy: z.enum(["protect", "allow", "warn_only"]).optional().describe("How dynamic / uncertain key prefixes are handled (protect is safest)."),
1860
+ stringPresence: z.enum(["guard", "warn", "off"]).optional().describe("Whether ripgrep checks locale strings in src before destructive cleanup."),
1861
+ stringPresenceMaxHitsPerKey: z.number().int().positive().max(1e3).optional().describe("Max rg JSON matches recorded per key (performance cap)."),
1862
+ respectPreserve: z.boolean().optional().describe("When true, `generate --resume` skips paths matching policies.preserve (same as full generate).")
1863
+ }).passthrough().describe("Reference / uncertainty policy fields (defaults or per-operation override).");
1864
+ var referenceCommandsSchema = z.object({
1865
+ cleanup: referenceDefaultsSchema.optional().describe("Overrides reference.defaults when running i18nprune cleanup."),
1866
+ sync: referenceDefaultsSchema.optional().describe("Overrides reference.defaults when running i18nprune sync."),
1867
+ generate: referenceDefaultsSchema.optional().describe("Overrides reference.defaults when running i18nprune generate (including `generate --resume`).")
1868
+ }).passthrough().describe(
1869
+ "Per-operation reference overrides. Supported keys: cleanup, sync, generate. Unknown keys are preserved for forward compatibility."
1870
+ ).optional();
1871
+ var referenceSchema = z.object({
1872
+ defaults: referenceDefaultsSchema.optional().describe("Baseline merged before reference.commands.<operation> for each operation."),
1873
+ commands: referenceCommandsSchema
1874
+ }).passthrough().optional().describe("Uncertainty and string-presence policy: defaults plus optional per-operation blocks.");
1875
+ var excludeSchema = z.object({
1876
+ preset: z.enum(["production"]).optional().describe("Built-in preset for common production skip sets (when defined)."),
1877
+ dirs: z.array(z.union([z.string(), z.instanceof(RegExp)])).optional().describe("Directory names or regexes to skip while scanning."),
1878
+ files: z.array(z.union([z.string(), z.instanceof(RegExp)])).optional().describe("File paths or regexes to skip."),
1879
+ extensions: z.array(z.union([z.string(), z.instanceof(RegExp)])).optional().describe("File extensions to skip."),
1880
+ patterns: z.array(z.instanceof(RegExp)).optional().describe("Path regexes to skip under src."),
1881
+ useDefaultSkip: z.boolean().optional().describe("When true, apply built-in directory skips (node_modules, dist, etc.).")
1882
+ }).strict().optional().describe("Scanner skip rules beyond CLI flags.");
1883
+ var scannerSchema = z.object({
1884
+ mode: z.enum(["serial", "concurrent", "auto"]).optional().describe("Scan worker layout: serial, concurrent, or auto-tuned."),
1885
+ concurrency: z.number().int().positive().max(4096).optional().describe("Worker count hint when mode is concurrent or auto."),
1886
+ hardCap: z.number().int().positive().max(4096).optional().describe("Upper bound on concurrent units (core may clamp).")
1887
+ }).strict().optional().describe("Optional scan orchestration hints.");
1888
+ var cacheSchema = z.object({
1889
+ enabled: z.boolean().optional().describe("Master switch for core-owned project analysis cache."),
1890
+ dir: z.string().optional().describe("Cache root directory. Relative paths resolve from the project root; omit to use the host default."),
1891
+ profile: z.enum(["safe", "balanced", "fast"]).optional().describe(
1892
+ "Named preset for cache rebuild policy. Default 'balanced' when omitted. Explicit rebuild, threshold, or mode below override the profile when set."
1893
+ ),
1894
+ mode: z.enum(["readWrite", "readOnly"]).optional().describe(
1895
+ "readWrite (default): cache may persist hits and misses. readOnly: reuse valid cache on disk but skip all cache writes (meta, files index, snapshots). Overrides cache.profile when set."
1896
+ ),
1897
+ rebuild: z.enum(["partial", "full"]).optional().describe(
1898
+ "How to rebuild analysis.json on cache miss. Overrides cache.profile when set. 'partial': patch from delta when safe. 'full': always run a full project scan."
1899
+ ),
1900
+ fullRescanThresholdPercent: z.number().min(0).max(100).optional().describe(
1901
+ "When rebuild is 'partial', fall back to a full src scan if (added+changed+deleted) src files reach this percent of tracked src files. Overrides cache.profile when set. Ignored when rebuild is 'full'."
1902
+ )
1903
+ }).strict().optional().describe("Project cache policy shared by core operations. Hosts provide runtime adapters and the default root.");
1904
+ var localeLeavesSchema = z.object({
1905
+ mode: z.enum(["structured", "legacy_string"]).optional().describe("JSON leaf shape: structured objects vs plain strings."),
1906
+ sync: z.object({
1907
+ stripMetadata: z.boolean().optional().describe("When true, sync may strip structured metadata back to plain strings.")
1908
+ }).strict().optional().describe("Options that apply when running sync.")
1909
+ }).strict().optional().describe("How sync and generate read/write JSON terminals.");
1910
+ var localesFilesystemSchema = z.object({
1911
+ source: z.string().describe("Source locale language code (e.g. en, pt-BR). Not a file path \u2014 layout + directory locate JSON segments."),
1912
+ directory: z.string().describe("Directory containing locale JSON segment files, relative to the config file directory."),
1913
+ mode: z.enum(["flat_file", "locale_directory"]).optional().describe("Filesystem layout: `flat_file` is one JSON file per locale code under `directory`."),
1914
+ structure: z.enum(["locale_file", "locale_per_dir", "feature_bundle"]).optional().describe("How multiple JSON files group into one logical locale.")
1915
+ }).strict().describe("Locale files on disk: source document + bundle root, with optional topology hints.");
1916
+ var patchingSchema = z.object({
1917
+ enabled: z.boolean().optional().describe("Master switch for loader / generated-file patching."),
1918
+ recipe: z.enum(["loader_generated"]).optional().describe("Which patching recipe to run when enabled."),
1919
+ loaderPath: z.string().optional().describe("Path to generated patching loader output (for example src/i18n/loaders.generated.ts)."),
1920
+ configPath: z.string().optional().describe("Path to JSON consumed by the loader."),
1921
+ localeJsonImportBase: z.string().optional().describe(
1922
+ "Path to the locales directory relative to the folder that contains i18nprune.config.* (often the repo root). Core computes import() URLs inside loaders.generated.ts from this."
1923
+ ),
1924
+ sizeLimitBytes: z.number().int().positive().max(16 * 1024 * 1024).optional().describe("Max bytes to read when patching (safety)."),
1925
+ mode: z.enum(["warn_skip", "strict"]).optional().describe("warn_skip logs and continues on oversize; strict fails the run.")
1926
+ }).strict().optional().describe("Auto-patch loader wiring for patch / generate --patch.");
1927
+ var configSchema = z.object({
1928
+ locales: localesFilesystemSchema,
1929
+ src: z.string().describe("Root directory scanned for translation helper calls."),
1930
+ functions: z.array(z.string()).min(1).describe("Function names treated as translation entry points (e.g. t)."),
1931
+ exclude: excludeSchema,
1932
+ output: outputSchema,
1933
+ scanner: scannerSchema,
1934
+ cache: cacheSchema,
1935
+ policies: policiesSchema,
1936
+ reference: referenceSchema,
1937
+ localeLeaves: localeLeavesSchema,
1938
+ patching: patchingSchema,
1939
+ missing: missingCommandSchema,
1940
+ translate: translateSchema
1941
+ }).describe("i18nprune project configuration (merged with defaults then validated by parseI18nPruneConfig).");
1942
+ var ConfigValidationError = class extends Error {
1943
+ constructor(message, zodError, issueCode) {
1944
+ super(message);
1945
+ this.zodError = zodError;
1946
+ this.issueCode = issueCode;
1947
+ this.name = "ConfigValidationError";
1948
+ }
1949
+ zodError;
1950
+ issueCode;
1951
+ };
1952
+ function isConfigValidationError(err) {
1953
+ if (err instanceof ConfigValidationError) return true;
1954
+ if (typeof err !== "object" || err === null || !(err instanceof Error)) return false;
1955
+ if (err.name !== "ConfigValidationError") return false;
1956
+ const code = err.issueCode;
1957
+ return code === void 0 || typeof code === "string";
1958
+ }
1959
+ function parseI18nPruneConfig(raw) {
1960
+ const r = configSchema.safeParse(raw);
1961
+ if (!r.success) {
1962
+ throw new ConfigValidationError(
1963
+ `Invalid i18nprune config: ${r.error.message}`,
1964
+ r.error
1965
+ );
1966
+ }
1967
+ const sourceCheck = validateLocalesSourceConfigValue(r.data.locales.source);
1968
+ if (!sourceCheck.ok) {
1969
+ throw new ConfigValidationError(sourceCheck.message, void 0, sourceCheck.issueCode);
1970
+ }
1971
+ const locales = { ...r.data.locales, source: sourceCheck.code };
1972
+ return { ...r.data, locales };
1973
+ }
1974
+
1975
+ // src/shared/locales/enumerate/parseSegmentLocale.ts
1976
+ function localeCodeForSegment(structure, path, segment) {
1977
+ if (structure === "locale_file") {
1978
+ if (segment.relativePath.includes("/")) return null;
1979
+ return path.basename(segment.absolutePath, ".json");
1980
+ }
1981
+ if (structure === "locale_per_dir") {
1982
+ const slash = segment.relativePath.indexOf("/");
1983
+ if (slash < 0) return null;
1984
+ const locale = segment.relativePath.slice(0, slash);
1985
+ return locale.length > 0 ? locale : null;
1986
+ }
1987
+ if (structure === "feature_bundle") {
1988
+ return path.basename(segment.absolutePath, ".json");
1989
+ }
1990
+ return null;
1991
+ }
1992
+
1993
+ // src/shared/locales/diagnostics/structuralParity.ts
1994
+ function localeStructuralSlot(structure, relativePath) {
1995
+ if (structure === "locale_per_dir") {
1996
+ const slash = relativePath.indexOf("/");
1997
+ if (slash < 0) return null;
1998
+ const slot = relativePath.slice(slash + 1);
1999
+ return slot.length > 0 ? slot : null;
2000
+ }
2001
+ if (structure === "feature_bundle") {
2002
+ const slash = relativePath.lastIndexOf("/");
2003
+ if (slash < 0) return null;
2004
+ const slot = relativePath.slice(0, slash);
2005
+ return slot.length > 0 ? slot : null;
2006
+ }
2007
+ return null;
2008
+ }
2009
+ function slotsByLocale(structure, segments) {
2010
+ const byLocale = /* @__PURE__ */ new Map();
2011
+ for (const segment of segments) {
2012
+ const slot = localeStructuralSlot(structure, segment.relativePath);
2013
+ if (slot === null) continue;
2014
+ let set = byLocale.get(segment.locale);
2015
+ if (!set) {
2016
+ set = /* @__PURE__ */ new Set();
2017
+ byLocale.set(segment.locale, set);
2018
+ }
2019
+ set.add(slot);
2020
+ }
2021
+ return byLocale;
2022
+ }
2023
+ function pickReferenceLocale(byLocale, preferred) {
2024
+ if (preferred !== void 0 && byLocale.has(preferred)) return preferred;
2025
+ let best = null;
2026
+ let bestSize = -1;
2027
+ for (const [locale, slots] of byLocale) {
2028
+ if (slots.size > bestSize) {
2029
+ best = locale;
2030
+ bestSize = slots.size;
2031
+ }
2032
+ }
2033
+ return best;
2034
+ }
2035
+ function collectLocaleStructuralParityDiagnostics(input) {
2036
+ const { structure, segments } = input;
2037
+ if (structure !== "locale_per_dir" && structure !== "feature_bundle") {
2038
+ return [];
2039
+ }
2040
+ const byLocale = slotsByLocale(structure, segments);
2041
+ if (byLocale.size < 2) return [];
2042
+ const reference = pickReferenceLocale(byLocale, input.referenceLocale);
2043
+ if (reference === null) return [];
2044
+ const referenceSlots = byLocale.get(reference);
2045
+ if (!referenceSlots || referenceSlots.size === 0) return [];
2046
+ const diagnostics = [];
2047
+ for (const [locale, slots] of byLocale) {
2048
+ if (locale === reference) continue;
2049
+ for (const slot of referenceSlots) {
2050
+ if (!slots.has(slot)) {
2051
+ diagnostics.push({
2052
+ level: "warn",
2053
+ code: "locale_structure_slot_missing",
2054
+ message: `locale ${locale} is missing segment slot ${slot} (present for reference locale ${reference})`
2055
+ });
2056
+ }
2057
+ }
2058
+ for (const slot of slots) {
2059
+ if (!referenceSlots.has(slot)) {
2060
+ diagnostics.push({
2061
+ level: "warn",
2062
+ code: "locale_structure_slot_extra",
2063
+ message: `locale ${locale} has extra segment slot ${slot} (not present for reference locale ${reference})`
2064
+ });
2065
+ }
2066
+ }
2067
+ }
2068
+ diagnostics.sort((a, b) => a.message.localeCompare(b.message));
2069
+ return diagnostics;
2070
+ }
2071
+
2072
+ // src/shared/errors/internal.ts
2073
+ var I18nPruneError = class extends Error {
2074
+ code;
2075
+ issueCode;
2076
+ constructor(message, code, options) {
2077
+ super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
2078
+ this.name = "I18nPruneError";
2079
+ this.code = code;
2080
+ this.issueCode = options?.issueCode;
2081
+ }
2082
+ };
2083
+
2084
+ // src/runtime/helpers/sync/assert.ts
2085
+ function isThenable(value) {
2086
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
2087
+ }
2088
+ function assertSyncPortResult(value, label, at) {
2089
+ if (isThenable(value)) {
2090
+ throw new I18nPruneError(
2091
+ `Synchronous ${label} requires a plain value (got a Promise at ${at})`,
2092
+ "USAGE"
2093
+ );
2094
+ }
2095
+ return value;
2096
+ }
2097
+
2098
+ // src/runtime/helpers/sync/fs.ts
2099
+ function existsRuntimeFsSync(filePath, fs) {
2100
+ return assertSyncPortResult(fs.exists(filePath), "fs.exists", filePath);
2101
+ }
2102
+ function listRuntimeFsDirSync(dirPath, fs) {
2103
+ return assertSyncPortResult(fs.listDir(dirPath), "fs.listDir", dirPath);
2104
+ }
2105
+
2106
+ // src/shared/constants/locales.ts
2107
+ var MAX_LOCALE_SEGMENT_TREE_DEPTH = 16;
2108
+
2109
+ // src/shared/locales/enumerate/walkJsonTree.ts
2110
+ function posixRelative(pathApi, root, absolute) {
2111
+ let rel = pathApi.relative(root, absolute);
2112
+ if (rel.startsWith("..") || pathApi.isAbsolute(rel)) {
2113
+ rel = pathApi.basename(absolute);
2114
+ }
2115
+ return rel.replace(/\\/g, "/");
2116
+ }
2117
+ function walkLocaleJsonSegments(input) {
2118
+ const { fs, path, rootAbsolute, recursive } = input;
2119
+ const maxDepth = input.maxDepth ?? MAX_LOCALE_SEGMENT_TREE_DEPTH;
2120
+ const out = [];
2121
+ function visit(dirAbsolute, depth) {
2122
+ if (!existsRuntimeFsSync(dirAbsolute, fs)) return;
2123
+ const entries = listRuntimeFsDirSync(dirAbsolute, fs);
2124
+ for (const entry of entries) {
2125
+ const childAbsolute = path.join(dirAbsolute, entry.name);
2126
+ if (entry.kind === "file" && entry.name.endsWith(".json")) {
2127
+ out.push({
2128
+ absolutePath: childAbsolute,
2129
+ relativePath: posixRelative(path, rootAbsolute, childAbsolute)
2130
+ });
2131
+ } else if (recursive && entry.kind === "directory" && depth < maxDepth) {
2132
+ visit(childAbsolute, depth + 1);
2133
+ }
2134
+ }
2135
+ }
2136
+ visit(rootAbsolute, 0);
2137
+ return out;
2138
+ }
2139
+
2140
+ // src/shared/locales/enumerate/listLocaleSegments.ts
2141
+ function listLocaleSegments(input) {
2142
+ const diagnostics = [];
2143
+ const { layout, fs, path } = input;
2144
+ const recursive = layout.structure !== "locale_file";
2145
+ const walked = walkLocaleJsonSegments({
2146
+ fs,
2147
+ path,
2148
+ rootAbsolute: layout.directoryAbsolute,
2149
+ recursive
2150
+ });
2151
+ const segments = [];
2152
+ for (const segment of walked) {
2153
+ const locale = localeCodeForSegment(layout.structure, path, segment);
2154
+ if (locale === null) continue;
2155
+ segments.push({
2156
+ locale,
2157
+ relativePath: segment.relativePath,
2158
+ absolutePath: segment.absolutePath
2159
+ });
2160
+ }
2161
+ segments.sort((a, b) => {
2162
+ const byLocale = a.locale.localeCompare(b.locale);
2163
+ if (byLocale !== 0) return byLocale;
2164
+ return a.relativePath.localeCompare(b.relativePath);
2165
+ });
2166
+ diagnostics.push(
2167
+ ...collectLocaleStructuralParityDiagnostics({
2168
+ structure: layout.structure,
2169
+ segments
2170
+ })
2171
+ );
2172
+ return { segments, diagnostics };
2173
+ }
2174
+
2175
+ // src/shared/locales/layout/requireStructure.ts
2176
+ function localesMode(config) {
2177
+ return config.mode ?? "flat_file";
2178
+ }
2179
+ function isLocalesStructureRequired(config) {
2180
+ return localesMode(config) === "locale_directory" && config.structure === void 0;
2181
+ }
2182
+ function resolveLocalesStructure(config) {
2183
+ if (config.structure !== void 0) {
2184
+ return config.structure;
2185
+ }
2186
+ if (localesMode(config) === "flat_file") {
2187
+ return "locale_file";
2188
+ }
2189
+ throw new Error("locales.structure is required when locales.mode is locale_directory");
2190
+ }
2191
+
2192
+ // src/shared/locales/layout/resolveLayout.ts
2193
+ function resolveLocalesLayout(config, directoryAbsolute) {
2194
+ const mode = localesMode(config);
2195
+ const structure = resolveLocalesStructure(config);
2196
+ return { mode, structure, directoryAbsolute, config };
2197
+ }
2198
+
2199
+ // src/config/locales/sourceResolve.ts
2200
+ var STRUCTURE_GUESS_ORDER = [
2201
+ "locale_per_dir",
2202
+ "feature_bundle",
2203
+ "locale_file"
2204
+ ];
2205
+ function resolveSourceLocaleAbsoluteBeforeStructureKnown(input) {
2206
+ const { directoryAbsolute, path, fs, sourceCode } = input;
2207
+ if (fs) {
2208
+ const walked = walkLocaleJsonSegments({
2209
+ fs,
2210
+ path,
2211
+ rootAbsolute: directoryAbsolute,
2212
+ recursive: true
2213
+ });
2214
+ const matches = [];
2215
+ for (const segment of walked) {
2216
+ for (const structure of STRUCTURE_GUESS_ORDER) {
2217
+ const locale = localeCodeForSegment(structure, path, segment);
2218
+ if (locale !== null && normalizeLanguageCode(locale) === sourceCode) {
2219
+ matches.push({ relativePath: segment.relativePath, absolutePath: segment.absolutePath });
2220
+ break;
2221
+ }
2222
+ }
2223
+ }
2224
+ if (matches.length > 0) {
2225
+ const sorted = matches.slice().sort((a, b) => a.relativePath.localeCompare(b.relativePath));
2226
+ return sorted[0].absolutePath;
2227
+ }
2228
+ }
2229
+ return path.join(directoryAbsolute, `${sourceCode}.json`);
2230
+ }
2231
+ function pickPrimarySegmentAbsolute(path, directoryAbsolute, matches) {
2232
+ const sorted = matches.slice().sort((a, b) => a.relativePath.localeCompare(b.relativePath));
2233
+ return path.join(directoryAbsolute, sorted[0].relativePath);
2234
+ }
2235
+ function resolveSourceLocaleAbsolutePath(input) {
2236
+ const code = normalizeLanguageCode(input.locales.source);
2237
+ if (isLocalesStructureRequired(input.locales)) {
2238
+ return resolveSourceLocaleAbsoluteBeforeStructureKnown({
2239
+ directoryAbsolute: input.directoryAbsolute,
2240
+ path: input.path,
2241
+ fs: input.fs,
2242
+ sourceCode: code
2243
+ });
2244
+ }
2245
+ const layout = resolveLocalesLayout(input.locales, input.directoryAbsolute);
2246
+ if (layout.mode === "flat_file" && layout.structure === "locale_file") {
2247
+ return input.path.join(input.directoryAbsolute, `${code}.json`);
2248
+ }
2249
+ if (input.fs) {
2250
+ const { segments } = listLocaleSegments({ layout, fs: input.fs, path: input.path });
2251
+ const matches = segments.filter((s) => normalizeLanguageCode(s.locale) === code);
2252
+ if (matches.length > 0) {
2253
+ return pickPrimarySegmentAbsolute(input.path, input.directoryAbsolute, matches);
2254
+ }
2255
+ }
2256
+ return input.path.join(input.directoryAbsolute, `${code}.json`);
2257
+ }
2258
+ function resolveSourceLocaleAbsoluteFromRelPaths(input) {
2259
+ const code = normalizeLanguageCode(input.locales.source);
2260
+ const layout = resolveLocalesLayout(input.locales, input.directoryAbsolute);
2261
+ const dirRel = input.path.relative(input.projectRootAbsolute, input.directoryAbsolute).replace(/\\/g, "/");
2262
+ const prefix = dirRel === "." || dirRel === "" ? "" : `${dirRel}/`;
2263
+ const matches = [];
2264
+ for (const rel of input.relPaths) {
2265
+ const normalized = rel.replace(/\\/g, "/");
2266
+ if (!normalized.endsWith(".json")) continue;
2267
+ if (prefix && !normalized.startsWith(prefix) && normalized !== `${dirRel}/${code}.json`) {
2268
+ continue;
2269
+ }
2270
+ const underDir = prefix && normalized.startsWith(prefix) ? normalized.slice(prefix.length) : normalized;
2271
+ const absolutePath = input.path.join(input.projectRootAbsolute, normalized);
2272
+ const locale = localeCodeForSegment(layout.structure, input.path, {
2273
+ absolutePath,
2274
+ relativePath: underDir
2275
+ });
2276
+ if (locale !== null && normalizeLanguageCode(locale) === code) {
2277
+ matches.push({ relativePath: underDir, absolutePath });
2278
+ }
2279
+ }
2280
+ if (matches.length > 0) {
2281
+ const sorted = matches.slice().sort((a, b) => a.relativePath.localeCompare(b.relativePath));
2282
+ return sorted[0].absolutePath;
2283
+ }
2284
+ return resolveSourceLocaleAbsolutePath({
2285
+ locales: input.locales,
2286
+ directoryAbsolute: input.directoryAbsolute,
2287
+ path: input.path
2288
+ });
2289
+ }
2290
+
2291
+ // src/config/defaults/core.ts
2292
+ var CORE_CONFIG_DEFAULT_INPUT = {};
2293
+
2294
+ // src/shared/constants/result.ts
2295
+ var RESULT_API_VERSION = "1";
2296
+ var DOCS_ISSUES_PAGE_PATH = "/issues";
2297
+
2298
+ // src/shared/docs/issueAnchors.ts
2299
+ var DOC_ISSUE_PARENT_SEGMENTS = /* @__PURE__ */ new Set([
2300
+ "cli",
2301
+ "cleanup",
2302
+ "config",
2303
+ "context",
2304
+ "doctor",
2305
+ "generate",
2306
+ "io",
2307
+ "languages",
2308
+ "locale",
2309
+ "locales",
2310
+ "missing",
2311
+ "patching",
2312
+ "paths",
2313
+ "project",
2314
+ "quality",
2315
+ "report",
2316
+ "scan",
2317
+ "share",
2318
+ "sync",
2319
+ "translate",
2320
+ "validate"
2321
+ ]);
2322
+ function issueDocHeadingSlug(raw) {
2323
+ return raw.toLowerCase().replace(/[.\s_]+/g, "-").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2324
+ }
2325
+ function splitIssueCode(code) {
2326
+ return code.trim().split(".");
2327
+ }
2328
+ function buildIssueCodeDocLinkParts(code) {
2329
+ const trimmed = code.trim();
2330
+ const parts = splitIssueCode(trimmed);
2331
+ let parent = null;
2332
+ if (parts.length >= 3 && parts[0] === "i18nprune") {
2333
+ const p = parts[1];
2334
+ if (DOC_ISSUE_PARENT_SEGMENTS.has(p)) parent = p;
2335
+ }
2336
+ let anchor;
2337
+ if (parent) {
2338
+ anchor = issueDocHeadingSlug(parts.slice(2).join("."));
2339
+ } else {
2340
+ anchor = issueDocHeadingSlug(trimmed.includes(".") ? trimmed.replace(/\./g, "_") : trimmed);
2341
+ }
2342
+ const sitePagePath = parent ? `${DOCS_ISSUES_PAGE_PATH}/${parent}` : DOCS_ISSUES_PAGE_PATH;
2343
+ const repoDocPath = parent ? `issues/${parent}` : "issues";
2344
+ return { parent, anchor, sitePagePath, repoDocPath };
2345
+ }
2346
+ function issueCodeRepoDocPathForIssueCode(code) {
2347
+ return buildIssueCodeDocLinkParts(code).repoDocPath;
2348
+ }
2349
+
2350
+ // src/shared/errors/normalize.ts
2351
+ function isErrnoCode(error, code) {
2352
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
2353
+ }
2354
+ function normalizeUnknownError(error, input) {
2355
+ if (error instanceof I18nPruneError) return error;
2356
+ const extra = input.issueCode !== void 0 ? { issueCode: input.issueCode } : void 0;
2357
+ if (error instanceof Error) {
2358
+ return new I18nPruneError(`${input.when}: ${error.message}`, input.defaultCode, { cause: error, ...extra });
2359
+ }
2360
+ return new I18nPruneError(`${input.when}: ${String(error)}`, input.defaultCode, { cause: error, ...extra });
2361
+ }
2362
+ function issueFromI18nPruneError(error, input) {
2363
+ const code = error.issueCode ?? input.codeByErrorCode?.[error.code] ?? input.fallbackCode;
2364
+ const base = {
2365
+ severity: "error",
2366
+ code,
2367
+ message: error.message
2368
+ };
2369
+ const withPath = input.path !== void 0 ? { ...base, path: input.path } : base;
2370
+ if (!code.startsWith("i18nprune.")) return withPath;
2371
+ return { ...withPath, docPath: issueCodeRepoDocPathForIssueCode(code) };
2372
+ }
2373
+
2374
+ // src/shared/json/parse.ts
2375
+ var I18nPruneJsonParseError = class extends I18nPruneError {
2376
+ filePath;
2377
+ line;
2378
+ column;
2379
+ offset;
2380
+ constructor(input) {
2381
+ const options = input.issueCode !== void 0 ? { cause: input.cause, issueCode: input.issueCode } : { cause: input.cause };
2382
+ super(input.message, input.code, options);
2383
+ this.name = "I18nPruneJsonParseError";
2384
+ this.filePath = input.filePath;
2385
+ this.line = input.location.line;
2386
+ this.column = input.location.column;
2387
+ this.offset = input.location.offset;
2388
+ }
2389
+ };
2390
+ function locationFromOffset(text, offset) {
2391
+ let line = 1;
2392
+ let column = 1;
2393
+ const capped = Math.max(0, Math.min(offset, text.length));
2394
+ for (let i = 0; i < capped; i += 1) {
2395
+ if (text.charCodeAt(i) === 10) {
2396
+ line += 1;
2397
+ column = 1;
2398
+ } else {
2399
+ column += 1;
2400
+ }
2401
+ }
2402
+ return { line, column, offset };
2403
+ }
2404
+ function getJsonParseLocation(error, text) {
2405
+ const message = error instanceof Error ? error.message : String(error);
2406
+ const positionMatch = /\bposition\s+(\d+)\b/i.exec(message);
2407
+ if (positionMatch?.[1]) {
2408
+ return locationFromOffset(text, Number.parseInt(positionMatch[1], 10));
2409
+ }
2410
+ const lineColumnMatch = /\bline\s+(\d+)\s+column\s+(\d+)\b/i.exec(message);
2411
+ if (lineColumnMatch?.[1] && lineColumnMatch[2]) {
2412
+ return {
2413
+ line: Number.parseInt(lineColumnMatch[1], 10),
2414
+ column: Number.parseInt(lineColumnMatch[2], 10)
2415
+ };
2416
+ }
2417
+ return {};
2418
+ }
2419
+ function formatJsonParseMessage(filePath, location, cause) {
2420
+ const subject = filePath ? `Invalid JSON in ${filePath}` : "Invalid JSON";
2421
+ const at = location.line !== void 0 && location.column !== void 0 ? ` at line ${String(location.line)}, column ${String(location.column)}` : location.offset !== void 0 ? ` at offset ${String(location.offset)}` : "";
2422
+ const detail = cause instanceof Error ? cause.message : String(cause);
2423
+ return `${subject}${at}: ${detail}`;
2424
+ }
2425
+ function parseJsonText(text, options = {}) {
2426
+ try {
2427
+ return JSON.parse(text);
2428
+ } catch (cause) {
2429
+ const location = getJsonParseLocation(cause, text);
2430
+ throw new I18nPruneJsonParseError({
2431
+ message: formatJsonParseMessage(options.filePath, location, cause),
2432
+ code: options.code ?? "IO",
2433
+ cause,
2434
+ location,
2435
+ ...options.issueCode !== void 0 ? { issueCode: options.issueCode } : {},
2436
+ ...options.filePath !== void 0 ? { filePath: options.filePath } : {}
2437
+ });
2438
+ }
2439
+ }
2440
+
2441
+ // src/shared/constants/cache.ts
2442
+ var DEFAULT_CACHE_PROFILE_ID = "balanced";
2443
+ var CACHE_PROFILE_DEFAULTS = {
2444
+ safe: {
2445
+ rebuild: "full",
2446
+ fullRescanThresholdPercent: 10,
2447
+ mode: "readWrite"
2448
+ },
2449
+ balanced: {
2450
+ rebuild: "partial",
2451
+ fullRescanThresholdPercent: 40,
2452
+ mode: "readWrite"
2453
+ },
2454
+ fast: {
2455
+ rebuild: "partial",
2456
+ fullRescanThresholdPercent: 70,
2457
+ mode: "readWrite"
2458
+ }
2459
+ };
2460
+
2461
+ // src/cache/resolveConfig.ts
2462
+ function clampThresholdPercent(value) {
2463
+ if (value < 0) return 0;
2464
+ if (value > 100) return 100;
2465
+ return value;
2466
+ }
2467
+ function resolveCacheConfig(cache) {
2468
+ const profileId = cache?.profile ?? DEFAULT_CACHE_PROFILE_ID;
2469
+ const profile = CACHE_PROFILE_DEFAULTS[profileId] ?? CACHE_PROFILE_DEFAULTS[DEFAULT_CACHE_PROFILE_ID];
2470
+ const threshold = cache?.fullRescanThresholdPercent !== void 0 ? clampThresholdPercent(cache.fullRescanThresholdPercent) : profile.fullRescanThresholdPercent;
2471
+ return {
2472
+ enabled: cache?.enabled ?? true,
2473
+ profile: profileId,
2474
+ mode: cache?.mode ?? profile.mode,
2475
+ rebuild: cache?.rebuild ?? profile.rebuild,
2476
+ fullRescanThresholdPercent: threshold,
2477
+ ...cache?.dir !== void 0 ? { dir: cache.dir } : {}
2478
+ };
2479
+ }
2480
+
2481
+ // src/shared/options/listWindow.ts
2482
+ var LIST_WINDOW_DEFAULT_TOP = 200;
2483
+ var LIST_WINDOW_HARD_CAP = 1e4;
2484
+ function clampPositiveInt(raw, fallback) {
2485
+ if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw) || raw < 1) {
2486
+ return fallback;
2487
+ }
2488
+ return raw;
2489
+ }
2490
+ function resolveListWindow(input, options) {
2491
+ const hardCap = clampPositiveInt(options?.hardCap, LIST_WINDOW_HARD_CAP);
2492
+ const defaultTopRaw = clampPositiveInt(options?.defaultTop, LIST_WINDOW_DEFAULT_TOP);
2493
+ const defaultTop = Math.min(defaultTopRaw, hardCap);
2494
+ if (input?.full === true) {
2495
+ return {
2496
+ top: hardCap,
2497
+ full: true,
2498
+ limit: hardCap,
2499
+ hardCap,
2500
+ clamped: false
2501
+ };
2502
+ }
2503
+ const requestedTop = input?.top;
2504
+ const top = clampPositiveInt(requestedTop, defaultTop);
2505
+ const limit = Math.min(top, hardCap);
2506
+ const isRequestedValidNumber = typeof requestedTop === "number" && Number.isFinite(requestedTop) && Number.isInteger(requestedTop);
2507
+ return {
2508
+ top,
2509
+ full: false,
2510
+ limit,
2511
+ hardCap,
2512
+ clamped: isRequestedValidNumber && requestedTop > hardCap
2513
+ };
2514
+ }
2515
+
2516
+ // src/shared/scanner/config.ts
2517
+ var SCANNER_DEFAULT_MODE = "auto";
2518
+ var SCANNER_DEFAULT_CONCURRENCY = 16;
2519
+ var SCANNER_DEFAULT_HARD_CAP = 32;
2520
+ function clampInt2(value, min, max) {
2521
+ if (!Number.isFinite(value)) return min;
2522
+ const n = Math.trunc(value);
2523
+ if (n < min) return min;
2524
+ if (n > max) return max;
2525
+ return n;
2526
+ }
2527
+ function resolveScannerConfig(input, options) {
2528
+ const hardCap = clampInt2(input?.hardCap ?? options?.defaultHardCap ?? SCANNER_DEFAULT_HARD_CAP, 1, 4096);
2529
+ const mode = input?.mode ?? options?.defaultMode ?? SCANNER_DEFAULT_MODE;
2530
+ const requested = clampInt2(
2531
+ input?.concurrency ?? options?.defaultConcurrency ?? SCANNER_DEFAULT_CONCURRENCY,
2532
+ 1,
2533
+ hardCap
2534
+ );
2535
+ const effectiveConcurrency = mode === "serial" ? 1 : requested;
2536
+ return {
2537
+ mode,
2538
+ concurrency: requested,
2539
+ hardCap,
2540
+ effectiveConcurrency
2541
+ };
2542
+ }
2543
+
2544
+ // src/config/resolve/core.ts
2545
+ function mergeCoreConfigInputs(a, b) {
2546
+ return {
2547
+ output: {
2548
+ list: {
2549
+ ...a?.output?.list ?? {},
2550
+ ...b?.output?.list ?? {}
2551
+ }
2552
+ },
2553
+ scanner: {
2554
+ ...a?.scanner ?? {},
2555
+ ...b?.scanner ?? {}
2556
+ },
2557
+ cache: {
2558
+ ...a?.cache ?? {},
2559
+ ...b?.cache ?? {}
2560
+ }
2561
+ };
2562
+ }
2563
+ function resolveCoreConfig(input, options) {
2564
+ const list = input?.output?.list;
2565
+ return {
2566
+ output: {
2567
+ list: resolveListWindow(
2568
+ list ? { top: list.top, full: list.full } : void 0,
2569
+ {
2570
+ ...options?.listWindow,
2571
+ ...typeof list?.maxCap === "number" ? { hardCap: list.maxCap } : {}
2572
+ }
2573
+ )
2574
+ },
2575
+ scanner: resolveScannerConfig(input?.scanner, options?.scanner),
2576
+ cache: resolveCacheConfig(input?.cache)
2577
+ };
2578
+ }
2579
+ function resolveCoreConfigLayers(layers, options) {
2580
+ const merged = layers.reduce((acc, layer) => mergeCoreConfigInputs(acc, layer.input), {});
2581
+ return resolveCoreConfig(merged, options);
2582
+ }
2583
+
2584
+ // src/config/resolve/load.ts
2585
+ function normalizeLoadError(configPath, error) {
2586
+ if (isErrnoCode(error, "ENOENT")) {
2587
+ return new I18nPruneError(`Config file not found: ${configPath}`, "CONFIG_MISSING", {
2588
+ cause: error,
2589
+ issueCode: ISSUE_CONFIG_MISSING
2590
+ });
2591
+ }
2592
+ if (error instanceof SyntaxError) {
2593
+ return new I18nPruneError(`Config parse failed at ${configPath}: ${error.message}`, "CONFIG_INVALID", {
2594
+ cause: error,
2595
+ issueCode: ISSUE_CONFIG_INVALID
2596
+ });
2597
+ }
2598
+ return normalizeUnknownError(error, {
2599
+ when: `Config load failed at ${configPath}`,
2600
+ defaultCode: "IO",
2601
+ issueCode: ISSUE_CONFIG_LOAD_FAILED
2602
+ });
2603
+ }
2604
+ function issueFromI18nError(error, configPath) {
2605
+ return issueFromI18nPruneError(error, {
2606
+ codeByErrorCode: {
2607
+ CONFIG_MISSING: "i18nprune.config.missing",
2608
+ CONFIG_INVALID: "i18nprune.config.invalid"
2609
+ },
2610
+ fallbackCode: "i18nprune.config.load_failed",
2611
+ path: configPath
2612
+ });
2613
+ }
2614
+ async function loadCoreConfigFromPath(input) {
2615
+ const parseText = input.parseText ?? ((text, configPath) => parseJsonText(text, {
2616
+ filePath: configPath,
2617
+ code: "CONFIG_INVALID",
2618
+ issueCode: ISSUE_CONFIG_INVALID
2619
+ }));
2620
+ const cwd = input.runtime?.system?.cwd();
2621
+ const effectivePath = cwd && input.runtime?.path && !input.runtime.path.isAbsolute(input.configPath) ? input.runtime.path.resolve(cwd, input.configPath) : input.configPath;
2622
+ try {
2623
+ const text = await input.readText(effectivePath);
2624
+ const parsed = await parseText(text, effectivePath);
2625
+ const asInput = typeof parsed === "object" && parsed !== null ? parsed : {};
2626
+ return resolveCoreConfig(asInput, input.resolveOptions);
2627
+ } catch (error) {
2628
+ throw normalizeLoadError(effectivePath, error);
2629
+ }
2630
+ }
2631
+ async function tryLoadCoreConfigFromPath(input, cwd = input.runtime?.system?.cwd() ?? "") {
2632
+ try {
2633
+ const data = await loadCoreConfigFromPath(input);
2634
+ return {
2635
+ ok: true,
2636
+ kind: "core.config",
2637
+ data,
2638
+ issues: [],
2639
+ meta: { apiVersion: RESULT_API_VERSION, cwd }
2640
+ };
2641
+ } catch (error) {
2642
+ const normalized = error instanceof I18nPruneError ? error : normalizeLoadError(input.configPath, error);
2643
+ return {
2644
+ ok: false,
2645
+ kind: "failed",
2646
+ issues: [issueFromI18nError(normalized, input.configPath)],
2647
+ meta: { apiVersion: RESULT_API_VERSION, cwd }
2648
+ };
2649
+ }
2650
+ }
2651
+
2652
+ // src/config/resolve/mergePartial.ts
2653
+ function mergePartialConfigIntoBase(base, partial) {
2654
+ const out = { ...base ?? {} };
2655
+ for (const [k, v] of Object.entries(partial)) {
2656
+ const existing = out[k];
2657
+ if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
2658
+ out[k] = mergePartialConfigIntoBase(existing, v);
2659
+ } else {
2660
+ out[k] = v;
2661
+ }
2662
+ }
2663
+ return out;
2664
+ }
2665
+
2666
+ // src/shared/translator/providers/registry.ts
2667
+ var GOOGLE_DESCRIPTOR = {
2668
+ id: "google",
2669
+ label: "Google Translate (unofficial gtx endpoint)",
2670
+ kind: "public_http",
2671
+ envVars: []
2672
+ };
2673
+ var MYMEMORY_DESCRIPTOR = {
2674
+ id: "mymemory",
2675
+ label: "MyMemory Translation API (free tier)",
2676
+ kind: "public_http",
2677
+ envVars: [],
2678
+ configKeys: [
2679
+ {
2680
+ key: "translate.providers row (id=mymemory).contactEmail",
2681
+ description: "Optional MyMemory contact email",
2682
+ optional: true
2683
+ }
2684
+ ]
2685
+ };
2686
+ var LIBRE_DESCRIPTOR = {
2687
+ id: "libre",
2688
+ label: "LibreTranslate-compatible HTTP API",
2689
+ kind: "public_http",
2690
+ envVars: [
2691
+ {
2692
+ key: "I18NPRUNE_TRANSLATE_LIBRE_URL",
2693
+ description: "LibreTranslate instance origin (https://\u2026), no trailing slash",
2694
+ required: false
2695
+ }
2696
+ ],
2697
+ configKeys: [
2698
+ {
2699
+ key: "translate.providers row (id=libre).baseUrl",
2700
+ description: "Instance origin (`https://\u2026`); superseded when `I18NPRUNE_TRANSLATE_LIBRE_URL` is set",
2701
+ optional: true
2702
+ }
2703
+ ]
2704
+ };
2705
+ var DEEPL_DESCRIPTOR = {
2706
+ id: "deepl",
2707
+ label: "DeepL API",
2708
+ kind: "api_key",
2709
+ envVars: [
2710
+ {
2711
+ key: "I18NPRUNE_TRANSLATE_DEEPL_API_KEY",
2712
+ description: "DeepL authentication key (preferred when using DeepL)",
2713
+ required: false
2714
+ }
2715
+ ],
2716
+ configKeys: [
2717
+ {
2718
+ key: "translate.providers row (id=deepl).apiKey",
2719
+ description: "DeepL auth key \u2014 superseded by `I18NPRUNE_TRANSLATE_DEEPL_API_KEY` when set",
2720
+ optional: true
2721
+ }
2722
+ ]
2723
+ };
2724
+ var LLM_DESCRIPTOR = {
2725
+ id: "llm",
2726
+ label: "OpenAI-compatible chat/completions API (LLM)",
2727
+ kind: "llm",
2728
+ envVars: [
2729
+ {
2730
+ key: "I18NPRUNE_TRANSLATE_LLM_API_KEY",
2731
+ description: "Bearer token for the OpenAI-compatible API",
2732
+ required: false
2733
+ },
2734
+ {
2735
+ key: "I18NPRUNE_TRANSLATE_LLM_BASE_URL",
2736
+ description: "API base URL (e.g. https://api.openai.com/v1)",
2737
+ required: false
2738
+ },
2739
+ {
2740
+ key: "I18NPRUNE_TRANSLATE_LLM_MODEL",
2741
+ description: "Model id (e.g. gpt-4o-mini)",
2742
+ required: false
2743
+ }
2744
+ ],
2745
+ configKeys: [
2746
+ {
2747
+ key: "translate.providers row (id=llm): apiKey, baseUrl, model",
2748
+ description: "OpenAI-compatible host; env `I18NPRUNE_TRANSLATE_LLM_*` supersede matching fields",
2749
+ optional: true
2750
+ }
2751
+ ]
2752
+ };
2753
+ var DESCRIPTORS = [
2754
+ GOOGLE_DESCRIPTOR,
2755
+ MYMEMORY_DESCRIPTOR,
2756
+ LIBRE_DESCRIPTOR,
2757
+ DEEPL_DESCRIPTOR,
2758
+ LLM_DESCRIPTOR
2759
+ ];
2760
+ new Map(
2761
+ DESCRIPTORS.map((d) => [d.id, d])
2762
+ );
2763
+ function defaultResolvedTranslationOptions() {
2764
+ return { provider: "google" };
2765
+ }
2766
+
2767
+ // src/config/resolve/translate.ts
2768
+ function clampInt3(n, lo, hi) {
2769
+ if (!Number.isFinite(n)) return lo;
2770
+ return Math.min(hi, Math.max(lo, Math.trunc(n)));
2771
+ }
2772
+ function toWarning(message) {
2773
+ return {
2774
+ severity: "warning",
2775
+ code: ISSUE_TRANSLATE_CONFIG_DEFAULT_APPLIED,
2776
+ message
2777
+ };
2778
+ }
2779
+ function pickEnabledProviderRow(rows, providerId) {
2780
+ return rows?.find((row) => row.id === providerId && row.enabled !== false);
2781
+ }
2782
+ function resolveProviderOrder(input) {
2783
+ const head = input.providerIdPin ?? input.config?.primary ?? defaultResolvedTranslationOptions().provider;
2784
+ const order = [head];
2785
+ if (input.config?.policy?.routing !== "auto") return order;
2786
+ for (const row of input.config?.providers ?? []) {
2787
+ if (row.enabled === false) continue;
2788
+ if (!order.includes(row.id)) order.push(row.id);
2789
+ }
2790
+ return order;
2791
+ }
2792
+ function resolveProviderProfile(config, providerId) {
2793
+ const defaults = DEFAULT_PROVIDER_RATE_LIMITS[providerId];
2794
+ const row = pickEnabledProviderRow(config?.providers, providerId)?.rateLimit;
2795
+ const profile = {
2796
+ maxConcurrency: clampInt3(row?.maxConcurrency ?? defaults.maxConcurrency, 1, TRANSLATE_WORKERS_CAP),
2797
+ rpm: row?.rpm ?? defaults.rpm,
2798
+ rps: row?.rps ?? defaults.rps,
2799
+ intervalMs: row?.intervalMs ?? defaults.intervalMs
2800
+ };
2801
+ const startRateLimit = profile.rpm === void 0 && profile.rps === void 0 && profile.intervalMs === void 0 ? void 0 : { rpm: profile.rpm, rps: profile.rps, intervalMs: profile.intervalMs };
2802
+ return { id: providerId, profile, startRateLimit };
2803
+ }
2804
+ function resolveTranslateConfig(input) {
2805
+ const warnings = [];
2806
+ const routing = input.config?.policy?.routing === "auto" ? "auto" : "single";
2807
+ if (input.config === void 0) {
2808
+ warnings.push(
2809
+ toWarning(
2810
+ "translate config omitted; using defaults (provider=google, routing=single, workers=1)."
2811
+ )
2812
+ );
2813
+ }
2814
+ if (input.config?.policy?.routing === void 0) {
2815
+ warnings.push(toWarning('translate.policy.routing omitted; defaulting to "single".'));
2816
+ }
2817
+ const providerOrder = resolveProviderOrder({
2818
+ config: input.config,
2819
+ providerIdPin: input.pin?.providerId
2820
+ });
2821
+ const effectiveProviderId = providerOrder[0];
2822
+ if (input.config?.primary === void 0 && input.pin?.providerId === void 0) {
2823
+ warnings.push(toWarning('translate.primary omitted; defaulting to provider "google".'));
2824
+ }
2825
+ if (input.pin?.providerId !== void 0) {
2826
+ warnings.push(toWarning(`provider pin applied; using "${input.pin.providerId}" first.`));
2827
+ }
2828
+ const requestedWorkers = resolveTranslateMaxParallel({
2829
+ configMaxWorkers: input.config?.workers,
2830
+ workersFlag: input.pin?.workers,
2831
+ envMaxWorkers: input.env?.[ENV_TRANSLATE_MAX_WORKERS]
2832
+ });
2833
+ if (input.config?.workers === void 0 && input.pin?.workers === void 0) {
2834
+ warnings.push(toWarning("translate.workers omitted; defaulting to 1 worker."));
2835
+ }
2836
+ const providerRows = {};
2837
+ for (const providerId of Object.keys(DEFAULT_PROVIDER_RATE_LIMITS)) {
2838
+ providerRows[providerId] = resolveProviderProfile(input.config, providerId);
2839
+ }
2840
+ const effectiveWorkers = clampInt3(
2841
+ Math.min(requestedWorkers, providerRows[effectiveProviderId].profile.maxConcurrency),
2842
+ 1,
2843
+ TRANSLATE_WORKERS_CAP
2844
+ );
2845
+ if (effectiveWorkers < requestedWorkers) {
2846
+ warnings.push(
2847
+ toWarning(
2848
+ `provider "${effectiveProviderId}" maxConcurrency=${String(providerRows[effectiveProviderId].profile.maxConcurrency)} capped workers from ${String(requestedWorkers)} to ${String(effectiveWorkers)}.`
2849
+ )
2850
+ );
2851
+ }
2852
+ return {
2853
+ resolved: {
2854
+ providerOrder,
2855
+ effectiveProviderId,
2856
+ requestedWorkers,
2857
+ effectiveWorkers,
2858
+ providers: providerRows,
2859
+ routing
2860
+ },
2861
+ warnings
2862
+ };
2863
+ }
2864
+
2865
+ export { CORE_CONFIG_DEFAULT_INPUT, ConfigValidationError, DEFAULT_CONFIG, REFERENCE_POLICY_SAFE_DEFAULTS, clampTranslateMaxWorkers, classifyLocalesSourceInput, collectLocalesFilesystemConfigWarnings, configSchema, defineConfig, isConfigValidationError, issueLocalesSourceNotInBundle, loadCoreConfigFromPath, localesFilesystemSchema, mergeCoreConfigInputs, mergePartialConfigIntoBase, parseI18nPruneConfig, resolveCoreConfig, resolveCoreConfigLayers, resolveSourceLocaleAbsoluteFromRelPaths, resolveSourceLocaleAbsolutePath, resolveTranslateConfig, tryLoadCoreConfigFromPath, validateLocalesSourceConfigValue };