@verbatra/sdk 0.6.4 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/index.cjs +718 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +177 -16
- package/dist/index.d.ts +177 -16
- package/dist/index.js +718 -106
- package/dist/index.js.map +1 -1
- package/package.json +16 -9
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync } from 'fs';
|
|
|
4
4
|
import { cosmiconfig } from 'cosmiconfig';
|
|
5
5
|
import { TypeScriptLoader } from 'cosmiconfig-typescript-loader';
|
|
6
6
|
import { randomUUID } from 'crypto';
|
|
7
|
-
import { rm, access,
|
|
7
|
+
import { mkdir, rm, access, open, writeFile, rename } from 'fs/promises';
|
|
8
8
|
import Anthropic from '@anthropic-ai/sdk';
|
|
9
9
|
import * as deepl from 'deepl-node';
|
|
10
10
|
import { createRequire } from 'module';
|
|
@@ -258,6 +258,9 @@ var defaultFs = {
|
|
|
258
258
|
createExclusive: (path, data) => createExclusive(path, data),
|
|
259
259
|
deleteFile: async (path) => {
|
|
260
260
|
await rm(path, { force: true });
|
|
261
|
+
},
|
|
262
|
+
mkdir: async (path) => {
|
|
263
|
+
await mkdir(path, { recursive: true });
|
|
261
264
|
}
|
|
262
265
|
};
|
|
263
266
|
var MAX_GLOSSARY_FILE_BYTES = 1024 * 1024;
|
|
@@ -496,9 +499,109 @@ function assessValueDegeneracy(sourceValue, candidateValue) {
|
|
|
496
499
|
}
|
|
497
500
|
return { degenerate: hasRunawayRepetition(candidate) };
|
|
498
501
|
}
|
|
502
|
+
|
|
503
|
+
// src/locale-path/pattern.ts
|
|
499
504
|
var LOCALE_TOKEN = "{locale}";
|
|
500
|
-
|
|
501
|
-
|
|
505
|
+
var SEPARATOR = /[/\\]/;
|
|
506
|
+
function tokenOccupiesWholeSegments(pattern) {
|
|
507
|
+
return pattern.split(SEPARATOR).every((segment) => segment === LOCALE_TOKEN || !segment.includes(LOCALE_TOKEN));
|
|
508
|
+
}
|
|
509
|
+
function expandPattern(pattern, spelling) {
|
|
510
|
+
return pattern.replaceAll(LOCALE_TOKEN, () => spelling);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// src/locale-path/tag.ts
|
|
514
|
+
var LANGUAGE = /^[a-z]{2,3}$/;
|
|
515
|
+
var SCRIPT = /^[a-z]{4}$/;
|
|
516
|
+
var REGION = /^([a-z]{2}|[0-9]{3})$/;
|
|
517
|
+
var NUMERIC_REGION = /^[0-9]{3}$/;
|
|
518
|
+
var VARIANT = /^([a-z0-9]{5,8}|[0-9][a-z0-9]{3})$/;
|
|
519
|
+
function subtagIf(subtags, index, pattern) {
|
|
520
|
+
const subtag = subtags[index];
|
|
521
|
+
return subtag !== void 0 && pattern.test(subtag) ? subtag : void 0;
|
|
522
|
+
}
|
|
523
|
+
function parseLocaleTag(locale) {
|
|
524
|
+
const subtags = locale.toLowerCase().split("-");
|
|
525
|
+
const language = subtags[0];
|
|
526
|
+
if (language === void 0 || !LANGUAGE.test(language)) {
|
|
527
|
+
return void 0;
|
|
528
|
+
}
|
|
529
|
+
let index = 1;
|
|
530
|
+
const script = subtagIf(subtags, index, SCRIPT);
|
|
531
|
+
index += script === void 0 ? 0 : 1;
|
|
532
|
+
const region = subtagIf(subtags, index, REGION);
|
|
533
|
+
index += region === void 0 ? 0 : 1;
|
|
534
|
+
const variants = subtags.slice(index);
|
|
535
|
+
if (!variants.every((variant) => VARIANT.test(variant))) {
|
|
536
|
+
return void 0;
|
|
537
|
+
}
|
|
538
|
+
return { language, script, region, variants };
|
|
539
|
+
}
|
|
540
|
+
function conventionalSubtags(tag) {
|
|
541
|
+
const subtags = [tag.language];
|
|
542
|
+
if (tag.script !== void 0) {
|
|
543
|
+
subtags.push(tag.script.charAt(0).toUpperCase() + tag.script.slice(1));
|
|
544
|
+
}
|
|
545
|
+
if (tag.region !== void 0) {
|
|
546
|
+
subtags.push(tag.region.toUpperCase());
|
|
547
|
+
}
|
|
548
|
+
subtags.push(...tag.variants);
|
|
549
|
+
return subtags;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/locale-path/android.ts
|
|
553
|
+
var SOURCE_SEGMENT = "values";
|
|
554
|
+
var MAX_BCP47_SUBTAGS = 4;
|
|
555
|
+
var UI_MODE_LANGUAGE_COLLISION = "car";
|
|
556
|
+
function needsBcp47(tag, subtagCount) {
|
|
557
|
+
return tag.script !== void 0 || tag.variants.length > 0 || tag.region !== void 0 && NUMERIC_REGION.test(tag.region) || tag.language === UI_MODE_LANGUAGE_COLLISION || subtagCount >= 3;
|
|
558
|
+
}
|
|
559
|
+
function legacySegment(tag) {
|
|
560
|
+
return tag.region === void 0 ? `${SOURCE_SEGMENT}-${tag.language}` : `${SOURCE_SEGMENT}-${tag.language}-r${tag.region.toUpperCase()}`;
|
|
561
|
+
}
|
|
562
|
+
function androidSegment(locale, isSourceLocale) {
|
|
563
|
+
if (isSourceLocale) {
|
|
564
|
+
return SOURCE_SEGMENT;
|
|
565
|
+
}
|
|
566
|
+
const tag = parseLocaleTag(locale);
|
|
567
|
+
if (tag === void 0) {
|
|
568
|
+
return void 0;
|
|
569
|
+
}
|
|
570
|
+
const subtags = conventionalSubtags(tag);
|
|
571
|
+
if (!needsBcp47(tag, subtags.length)) {
|
|
572
|
+
return legacySegment(tag);
|
|
573
|
+
}
|
|
574
|
+
if (subtags.length > MAX_BCP47_SUBTAGS) {
|
|
575
|
+
return void 0;
|
|
576
|
+
}
|
|
577
|
+
return `${SOURCE_SEGMENT}-b+${subtags.join("+")}`;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// src/locale-path/style.ts
|
|
581
|
+
var LOCALE_STYLES = ["literal", "posix", "android"];
|
|
582
|
+
var SEGMENT_STYLES = /* @__PURE__ */ new Set(["android"]);
|
|
583
|
+
var UNSAFE_IN_SEGMENT = /[/\\\0]/;
|
|
584
|
+
function isSegmentStyle(style) {
|
|
585
|
+
return SEGMENT_STYLES.has(style);
|
|
586
|
+
}
|
|
587
|
+
function posixSpelling(locale) {
|
|
588
|
+
const tag = parseLocaleTag(locale);
|
|
589
|
+
if (tag === void 0 || tag.script !== void 0 || tag.variants.length > 0) {
|
|
590
|
+
return void 0;
|
|
591
|
+
}
|
|
592
|
+
if (tag.region !== void 0 && NUMERIC_REGION.test(tag.region)) {
|
|
593
|
+
return void 0;
|
|
594
|
+
}
|
|
595
|
+
return locale.replaceAll("-", "_");
|
|
596
|
+
}
|
|
597
|
+
function spellLocale(locale, style, isSourceLocale) {
|
|
598
|
+
if (style === "android") {
|
|
599
|
+
return androidSegment(locale, isSourceLocale);
|
|
600
|
+
}
|
|
601
|
+
return style === "posix" ? posixSpelling(locale) : locale;
|
|
602
|
+
}
|
|
603
|
+
function isSafeSpelling(spelling) {
|
|
604
|
+
return spelling.length > 0 && !UNSAFE_IN_SEGMENT.test(spelling) && spelling !== "." && spelling !== "..";
|
|
502
605
|
}
|
|
503
606
|
function checkBatchIntegrity(inputs, extract, compare) {
|
|
504
607
|
const integrity = /* @__PURE__ */ new Map();
|
|
@@ -671,7 +774,7 @@ function deriveJsonSchema(schema) {
|
|
|
671
774
|
result[key] = value;
|
|
672
775
|
}
|
|
673
776
|
}
|
|
674
|
-
return result;
|
|
777
|
+
return { ...result, type: "object" };
|
|
675
778
|
}
|
|
676
779
|
function partitionTranslations(translations, requested) {
|
|
677
780
|
const firstSeen = /* @__PURE__ */ new Map();
|
|
@@ -791,6 +894,8 @@ function assertNotTruncated(truncated) {
|
|
|
791
894
|
var RATE_LIMITED_STATUS = 429;
|
|
792
895
|
var TIMEOUT_STATUS = 408;
|
|
793
896
|
var AUTH_FAILED_STATUSES = /* @__PURE__ */ new Set([401, 403]);
|
|
897
|
+
var SERVER_OUTAGE_MIN_STATUS = 500;
|
|
898
|
+
var SERVER_OUTAGE_MAX_STATUS = 599;
|
|
794
899
|
var RATE_LIMITED_CLASS_NAMES = /* @__PURE__ */ new Set([
|
|
795
900
|
"RateLimitError",
|
|
796
901
|
"TooManyRequestsError"
|
|
@@ -812,6 +917,9 @@ function getErrorStatus(error) {
|
|
|
812
917
|
const status = error.status;
|
|
813
918
|
return typeof status === "number" ? status : void 0;
|
|
814
919
|
}
|
|
920
|
+
function isServerOutageStatus(status) {
|
|
921
|
+
return status !== void 0 && status >= SERVER_OUTAGE_MIN_STATUS && status <= SERVER_OUTAGE_MAX_STATUS;
|
|
922
|
+
}
|
|
815
923
|
function readClassName(error) {
|
|
816
924
|
if (typeof error !== "object" || error === null) {
|
|
817
925
|
return void 0;
|
|
@@ -829,6 +937,9 @@ function classifyProviderError(error) {
|
|
|
829
937
|
if (status === TIMEOUT_STATUS) {
|
|
830
938
|
return "TIMEOUT";
|
|
831
939
|
}
|
|
940
|
+
if (isServerOutageStatus(status)) {
|
|
941
|
+
return "PROVIDER_UNAVAILABLE";
|
|
942
|
+
}
|
|
832
943
|
const className = readClassName(error);
|
|
833
944
|
if (className !== void 0) {
|
|
834
945
|
if (RATE_LIMITED_CLASS_NAMES.has(className)) {
|
|
@@ -860,10 +971,12 @@ var PROVIDER_CALL_FAILED_MESSAGE = "The translation provider request failed.";
|
|
|
860
971
|
var RATE_LIMITED_MESSAGE = "The translation provider rate-limited this request.";
|
|
861
972
|
var TIMEOUT_MESSAGE = "The translation provider request timed out.";
|
|
862
973
|
var AUTH_FAILED_MESSAGE = "The translation provider rejected the request credentials.";
|
|
974
|
+
var PROVIDER_UNAVAILABLE_MESSAGE = "The translation provider is currently unavailable.";
|
|
863
975
|
var MESSAGE_BY_CODE = {
|
|
864
976
|
RATE_LIMITED: RATE_LIMITED_MESSAGE,
|
|
865
977
|
TIMEOUT: TIMEOUT_MESSAGE,
|
|
866
978
|
AUTH_FAILED: AUTH_FAILED_MESSAGE,
|
|
979
|
+
PROVIDER_UNAVAILABLE: PROVIDER_UNAVAILABLE_MESSAGE,
|
|
867
980
|
PROVIDER_ERROR: PROVIDER_CALL_FAILED_MESSAGE
|
|
868
981
|
};
|
|
869
982
|
async function guardProviderCall(call, signal) {
|
|
@@ -944,14 +1057,14 @@ function resolveOpenAiCompatibleKey(customEnvVar) {
|
|
|
944
1057
|
}
|
|
945
1058
|
return OPENAI_COMPATIBLE_KEY_PLACEHOLDER;
|
|
946
1059
|
}
|
|
1060
|
+
function toMutableRequest(request) {
|
|
1061
|
+
return request;
|
|
1062
|
+
}
|
|
947
1063
|
function createDefaultClient() {
|
|
948
1064
|
const sdk = new Anthropic({ apiKey: requireAnthropicKey(), logLevel: "off" });
|
|
949
1065
|
return {
|
|
950
1066
|
messages: {
|
|
951
|
-
create: async (body, options) => await sdk.messages.create(
|
|
952
|
-
body,
|
|
953
|
-
options
|
|
954
|
-
)
|
|
1067
|
+
create: async (body, options) => await sdk.messages.create(toMutableRequest(body), options)
|
|
955
1068
|
}
|
|
956
1069
|
};
|
|
957
1070
|
}
|
|
@@ -1369,7 +1482,7 @@ function createDefaultClient3() {
|
|
|
1369
1482
|
models: {
|
|
1370
1483
|
generateContent: (request) => withGeminiRetry(
|
|
1371
1484
|
async () => await ai.models.generateContent(
|
|
1372
|
-
request
|
|
1485
|
+
toMutableRequest(request)
|
|
1373
1486
|
),
|
|
1374
1487
|
request.config.abortSignal
|
|
1375
1488
|
)
|
|
@@ -1533,7 +1646,7 @@ function createDefaultClient4() {
|
|
|
1533
1646
|
chat: {
|
|
1534
1647
|
completions: {
|
|
1535
1648
|
create: async (body, options) => await sdk.chat.completions.create(
|
|
1536
|
-
body,
|
|
1649
|
+
toMutableRequest(body),
|
|
1537
1650
|
options
|
|
1538
1651
|
)
|
|
1539
1652
|
}
|
|
@@ -1734,7 +1847,7 @@ function createDefaultClient5(config) {
|
|
|
1734
1847
|
chat: {
|
|
1735
1848
|
completions: {
|
|
1736
1849
|
create: async (body, options) => await sdk.chat.completions.create(
|
|
1737
|
-
body,
|
|
1850
|
+
toMutableRequest(body),
|
|
1738
1851
|
options
|
|
1739
1852
|
)
|
|
1740
1853
|
}
|
|
@@ -1816,7 +1929,13 @@ var verbatraConfigSchema = z.strictObject({
|
|
|
1816
1929
|
targetLocales: z.array(z.string().min(1)).min(1),
|
|
1817
1930
|
format: supportedFormatSchema,
|
|
1818
1931
|
files: z.strictObject({
|
|
1819
|
-
pattern: z.string().min(1)
|
|
1932
|
+
pattern: z.string().min(1),
|
|
1933
|
+
/**
|
|
1934
|
+
* How the `{locale}` token in `pattern` is spelled for each locale. Absent means `"literal"`,
|
|
1935
|
+
* the configured tag verbatim, which is what every layout except Android and the POSIX
|
|
1936
|
+
* underscore layouts needs. See `LocaleStyle` for what each style produces.
|
|
1937
|
+
*/
|
|
1938
|
+
localeStyle: z.enum(LOCALE_STYLES).optional()
|
|
1820
1939
|
}),
|
|
1821
1940
|
provider: providerConfigSchema,
|
|
1822
1941
|
/**
|
|
@@ -1837,8 +1956,9 @@ var verbatraConfigSchema = z.strictObject({
|
|
|
1837
1956
|
* Opt-in plural-category generation, off by default. When true, and only for an i18next-JSON project
|
|
1838
1957
|
* translated by an LLM provider, verbatra synthesizes the CLDR plural forms a target language
|
|
1839
1958
|
* requires but the source does not supply (for example Polish few/many). The per-run
|
|
1840
|
-
* `generatePlurals` option on `translate` overrides this.
|
|
1841
|
-
*
|
|
1959
|
+
* `generatePlurals` option on `translate` overrides this. A plural form the target file already holds
|
|
1960
|
+
* is adopted, never generated over. Unsupported cases (DeepL, non-i18next, an unknown language) fall
|
|
1961
|
+
* back to the per-locale plural warning.
|
|
1842
1962
|
*/
|
|
1843
1963
|
generatePlurals: z.boolean().optional(),
|
|
1844
1964
|
/**
|
|
@@ -1973,6 +2093,63 @@ async function loadConfig(options = {}) {
|
|
|
1973
2093
|
const { config } = await loadConfigWithMeta(options);
|
|
1974
2094
|
return config;
|
|
1975
2095
|
}
|
|
2096
|
+
var DEFAULT_LOCALE_STYLE = "literal";
|
|
2097
|
+
function validatePattern(pattern, style) {
|
|
2098
|
+
if (!pattern.includes(LOCALE_TOKEN)) {
|
|
2099
|
+
throw new SdkError(
|
|
2100
|
+
"LOCALE_LAYOUT_INVALID",
|
|
2101
|
+
`The files pattern "${pattern}" must contain the ${LOCALE_TOKEN} token.`
|
|
2102
|
+
);
|
|
2103
|
+
}
|
|
2104
|
+
if (isSegmentStyle(style) && !tokenOccupiesWholeSegments(pattern)) {
|
|
2105
|
+
throw new SdkError(
|
|
2106
|
+
"LOCALE_LAYOUT_INVALID",
|
|
2107
|
+
`The "${style}" locale style expands ${LOCALE_TOKEN} to a whole path segment, so the token must stand alone between separators, but the pattern "${pattern}" embeds it in a segment.`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
function safeSpelling(locale, style, sourceLocale) {
|
|
2112
|
+
const spelling = spellLocale(locale, style, locale === sourceLocale);
|
|
2113
|
+
if (spelling === void 0) {
|
|
2114
|
+
throw new SdkError(
|
|
2115
|
+
"LOCALE_LAYOUT_INVALID",
|
|
2116
|
+
`The "${style}" locale style has no valid path spelling for the locale "${locale}".`
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
if (!isSafeSpelling(spelling)) {
|
|
2120
|
+
throw new SdkError(
|
|
2121
|
+
"LOCALE_LAYOUT_INVALID",
|
|
2122
|
+
`The locale "${locale}" expands to "${spelling}" under the "${style}" locale style, which is not a single path segment.`
|
|
2123
|
+
);
|
|
2124
|
+
}
|
|
2125
|
+
return spelling;
|
|
2126
|
+
}
|
|
2127
|
+
function buildForwardMap(config, pathFor) {
|
|
2128
|
+
const forward = /* @__PURE__ */ new Map();
|
|
2129
|
+
for (const locale of [config.sourceLocale, ...config.targetLocales]) {
|
|
2130
|
+
const path = pathFor(locale);
|
|
2131
|
+
const claimed = forward.get(path);
|
|
2132
|
+
if (claimed !== void 0 && claimed !== locale) {
|
|
2133
|
+
throw new SdkError(
|
|
2134
|
+
"LOCALE_PATH_COLLISION",
|
|
2135
|
+
`The locales "${claimed}" and "${locale}" both resolve to ${path}.`
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
forward.set(path, locale);
|
|
2139
|
+
}
|
|
2140
|
+
return forward;
|
|
2141
|
+
}
|
|
2142
|
+
function createLocalePathResolver(cwd, config) {
|
|
2143
|
+
const style = config.files.localeStyle ?? DEFAULT_LOCALE_STYLE;
|
|
2144
|
+
const pattern = config.files.pattern;
|
|
2145
|
+
validatePattern(pattern, style);
|
|
2146
|
+
const pathFor = (locale) => resolve(cwd, expandPattern(pattern, safeSpelling(locale, style, config.sourceLocale)));
|
|
2147
|
+
const forward = buildForwardMap(config, pathFor);
|
|
2148
|
+
return {
|
|
2149
|
+
pathFor,
|
|
2150
|
+
localeFor: (absolutePath) => forward.get(resolve(cwd, absolutePath))
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
1976
2153
|
var LOCAL_DIR_NAME = ".verbatra-local";
|
|
1977
2154
|
var LOCK_FILE_GUARD_STEM = "_lockfile";
|
|
1978
2155
|
var DEFAULT_POLL_INTERVAL_MS = 100;
|
|
@@ -3994,7 +4171,7 @@ function selectAdapter(format, registry = createDefaultRegistry()) {
|
|
|
3994
4171
|
|
|
3995
4172
|
// src/flow/read-target.ts
|
|
3996
4173
|
async function readTargetResource(input) {
|
|
3997
|
-
const path =
|
|
4174
|
+
const path = input.resolver.pathFor(input.locale);
|
|
3998
4175
|
if (!await input.fs.fileExists(path)) {
|
|
3999
4176
|
return { locale: input.locale, namespace: "", format: input.format, entries: /* @__PURE__ */ new Map() };
|
|
4000
4177
|
}
|
|
@@ -4021,7 +4198,7 @@ function selectLocales(config, requested) {
|
|
|
4021
4198
|
|
|
4022
4199
|
// src/flow/source.ts
|
|
4023
4200
|
async function readSource(config, cwd, fs, adapter) {
|
|
4024
|
-
const sourcePath =
|
|
4201
|
+
const sourcePath = createLocalePathResolver(cwd, config).pathFor(config.sourceLocale);
|
|
4025
4202
|
if (!await fs.fileExists(sourcePath)) {
|
|
4026
4203
|
throw new SdkError(
|
|
4027
4204
|
"SOURCE_UNREADABLE",
|
|
@@ -4042,8 +4219,7 @@ async function readSource(config, cwd, fs, adapter) {
|
|
|
4042
4219
|
// src/flow/diff-locales.ts
|
|
4043
4220
|
async function readTarget(cwd, config, adapter, fs, locale) {
|
|
4044
4221
|
return readTargetResource({
|
|
4045
|
-
cwd,
|
|
4046
|
-
filesPattern: config.files.pattern,
|
|
4222
|
+
resolver: createLocalePathResolver(cwd, config),
|
|
4047
4223
|
format: config.format,
|
|
4048
4224
|
locale,
|
|
4049
4225
|
adapter,
|
|
@@ -4164,7 +4340,7 @@ async function editEntry(input, deps = {}) {
|
|
|
4164
4340
|
}
|
|
4165
4341
|
const merged = new Map(target.entries);
|
|
4166
4342
|
merged.set(input.key, { ...sourceEntry, value: input.value, namespace: target.namespace });
|
|
4167
|
-
const path =
|
|
4343
|
+
const path = createLocalePathResolver(cwd, config).pathFor(locale);
|
|
4168
4344
|
await adapter.write(
|
|
4169
4345
|
{ locale, namespace: target.namespace, format: config.format, entries: merged },
|
|
4170
4346
|
path
|
|
@@ -4387,7 +4563,7 @@ async function retranslateEntry(input, deps = {}) {
|
|
|
4387
4563
|
}
|
|
4388
4564
|
const merged = new Map(target.entries);
|
|
4389
4565
|
merged.set(input.key, { ...sourceEntry, value, namespace: target.namespace });
|
|
4390
|
-
const path =
|
|
4566
|
+
const path = createLocalePathResolver(cwd, config).pathFor(locale);
|
|
4391
4567
|
await adapter.write(
|
|
4392
4568
|
{ locale, namespace: target.namespace, format: config.format, entries: merged },
|
|
4393
4569
|
path
|
|
@@ -4806,6 +4982,9 @@ function syntheticEntry(item) {
|
|
|
4806
4982
|
meaning: `CLDR plural category "${item.category}"`
|
|
4807
4983
|
};
|
|
4808
4984
|
}
|
|
4985
|
+
function isAdopted(item, targetKeys, baseline) {
|
|
4986
|
+
return targetKeys.has(item.targetKey) && !baseline.has(item.targetKey);
|
|
4987
|
+
}
|
|
4809
4988
|
function staleItems(items, baseline) {
|
|
4810
4989
|
return items.filter((item) => {
|
|
4811
4990
|
const hash = generatedLockHash(item.governingEntries, item.category);
|
|
@@ -4825,7 +5004,10 @@ function buildRequest2(context, entries) {
|
|
|
4825
5004
|
}
|
|
4826
5005
|
async function generatePluralForms(context) {
|
|
4827
5006
|
const plan = planPluralGeneration(context.source, context.targetLocale, context.format);
|
|
4828
|
-
const
|
|
5007
|
+
const candidates = plan.items.filter(
|
|
5008
|
+
(item) => !isAdopted(item, context.targetKeys, context.baseline)
|
|
5009
|
+
);
|
|
5010
|
+
const stale = staleItems(candidates, context.baseline);
|
|
4829
5011
|
if (stale.length === 0) {
|
|
4830
5012
|
return EMPTY_RESULT;
|
|
4831
5013
|
}
|
|
@@ -5015,8 +5197,7 @@ async function shouldWriteTarget(params, path, changed) {
|
|
|
5015
5197
|
}
|
|
5016
5198
|
async function runLocale(params) {
|
|
5017
5199
|
const target = await readTargetResource({
|
|
5018
|
-
|
|
5019
|
-
filesPattern: params.filesPattern,
|
|
5200
|
+
resolver: params.resolver,
|
|
5020
5201
|
format: params.format,
|
|
5021
5202
|
locale: params.targetLocale,
|
|
5022
5203
|
adapter: params.adapter,
|
|
@@ -5093,11 +5274,11 @@ async function runLocale(params) {
|
|
|
5093
5274
|
merged.set(key, { ...hit.source, value: hit.value, namespace: target.namespace });
|
|
5094
5275
|
}
|
|
5095
5276
|
}
|
|
5096
|
-
const generation = await runGeneration(params, provider);
|
|
5277
|
+
const generation = await runGeneration(params, provider, new Set(target.entries.keys()));
|
|
5097
5278
|
for (const form of generation.accepted) {
|
|
5098
5279
|
merged.set(form.targetKey, { ...form.entry, namespace: target.namespace });
|
|
5099
5280
|
}
|
|
5100
|
-
const path =
|
|
5281
|
+
const path = params.resolver.pathFor(params.targetLocale);
|
|
5101
5282
|
const writeNeeded = await shouldWriteTarget(params, path, {
|
|
5102
5283
|
accepted: accepted.size,
|
|
5103
5284
|
pruned: pruned.length,
|
|
@@ -5160,7 +5341,7 @@ var NO_GENERATION_RESULT = {
|
|
|
5160
5341
|
usage: void 0,
|
|
5161
5342
|
tripped: false
|
|
5162
5343
|
};
|
|
5163
|
-
async function runGeneration(params, provider) {
|
|
5344
|
+
async function runGeneration(params, provider, targetKeys) {
|
|
5164
5345
|
if (!params.generatePlurals || provider.kind !== "llm") {
|
|
5165
5346
|
return NO_GENERATION_RESULT;
|
|
5166
5347
|
}
|
|
@@ -5174,6 +5355,7 @@ async function runGeneration(params, provider) {
|
|
|
5174
5355
|
glossary: params.glossary,
|
|
5175
5356
|
tone: params.tone,
|
|
5176
5357
|
baseline: params.baseline,
|
|
5358
|
+
targetKeys,
|
|
5177
5359
|
maxBatchSize: params.maxBatchSize,
|
|
5178
5360
|
budget: params.budget
|
|
5179
5361
|
});
|
|
@@ -5416,7 +5598,7 @@ function buildLocaleRunParams(context, targetLocale, baseline) {
|
|
|
5416
5598
|
adapter: context.adapter,
|
|
5417
5599
|
provider: context.provider,
|
|
5418
5600
|
cwd: context.cwd,
|
|
5419
|
-
|
|
5601
|
+
resolver: context.resolver,
|
|
5420
5602
|
sourceLocale: context.config.sourceLocale,
|
|
5421
5603
|
targetLocale,
|
|
5422
5604
|
format: context.config.format,
|
|
@@ -5570,6 +5752,7 @@ async function translate2(input, deps = {}) {
|
|
|
5570
5752
|
config.maxTokens,
|
|
5571
5753
|
config.budgetBehavior ?? DEFAULT_BUDGET_BEHAVIOR
|
|
5572
5754
|
);
|
|
5755
|
+
const resolver = createLocalePathResolver(cwd, config);
|
|
5573
5756
|
const adapter = selectAdapter(config.format, deps.adapterRegistry);
|
|
5574
5757
|
const provider = dryRun ? void 0 : selectProvider(config.provider, deps.createProvider);
|
|
5575
5758
|
const source = await readSource(config, cwd, fs, adapter);
|
|
@@ -5580,6 +5763,7 @@ async function translate2(input, deps = {}) {
|
|
|
5580
5763
|
provider,
|
|
5581
5764
|
cwd,
|
|
5582
5765
|
config,
|
|
5766
|
+
resolver,
|
|
5583
5767
|
prune,
|
|
5584
5768
|
generatePlurals,
|
|
5585
5769
|
maxBatchSize,
|
|
@@ -5620,6 +5804,123 @@ var ExchangeError = class extends Error {
|
|
|
5620
5804
|
this.code = code;
|
|
5621
5805
|
}
|
|
5622
5806
|
};
|
|
5807
|
+
var DELIMITER = {
|
|
5808
|
+
csv: ",",
|
|
5809
|
+
tsv: " "
|
|
5810
|
+
};
|
|
5811
|
+
var FORBIDDEN_FILE_NAME_CHARS = /[\\/:*?"<>|\p{Cc}]/u;
|
|
5812
|
+
function assertPlainFileLocale(locale) {
|
|
5813
|
+
if (locale.length === 0 || locale === "." || locale === "..") {
|
|
5814
|
+
throw new ExchangeError(
|
|
5815
|
+
"WORKBOOK_INVALID",
|
|
5816
|
+
`The locale "${locale}" cannot be an interchange file name: it must name a file, not a directory.`
|
|
5817
|
+
);
|
|
5818
|
+
}
|
|
5819
|
+
if (FORBIDDEN_FILE_NAME_CHARS.test(locale)) {
|
|
5820
|
+
throw new ExchangeError(
|
|
5821
|
+
"WORKBOOK_INVALID",
|
|
5822
|
+
`The locale "${locale}" cannot be an interchange file name: it must not contain a path separator, a control character, or any of : * ? " < > | .`
|
|
5823
|
+
);
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
function delimitedFileName(locale, format) {
|
|
5827
|
+
assertPlainFileLocale(locale);
|
|
5828
|
+
return `${locale}.${format}`;
|
|
5829
|
+
}
|
|
5830
|
+
var COLUMN = {
|
|
5831
|
+
key: 1,
|
|
5832
|
+
source: 2,
|
|
5833
|
+
current: 3,
|
|
5834
|
+
status: 4,
|
|
5835
|
+
translation: 5,
|
|
5836
|
+
sourceHash: 6,
|
|
5837
|
+
context: 7,
|
|
5838
|
+
reviewStatus: 8,
|
|
5839
|
+
reviewReasons: 9
|
|
5840
|
+
};
|
|
5841
|
+
var HEADERS = [
|
|
5842
|
+
"Key",
|
|
5843
|
+
"Source",
|
|
5844
|
+
"Current translation",
|
|
5845
|
+
"Status",
|
|
5846
|
+
"Translation",
|
|
5847
|
+
"Source hash",
|
|
5848
|
+
"Context",
|
|
5849
|
+
"Review status",
|
|
5850
|
+
"Review reasons"
|
|
5851
|
+
];
|
|
5852
|
+
var HEADER_ROW = 1;
|
|
5853
|
+
var INSTRUCTIONS_SHEET_NAME = "Instructions";
|
|
5854
|
+
var rowSchema = z.object({
|
|
5855
|
+
key: z.string().min(1),
|
|
5856
|
+
source: z.string(),
|
|
5857
|
+
currentTarget: z.string(),
|
|
5858
|
+
status: z.enum(["new", "changed", "unchanged"]),
|
|
5859
|
+
sourceHash: z.string(),
|
|
5860
|
+
translation: z.string(),
|
|
5861
|
+
context: z.string(),
|
|
5862
|
+
reviewStatus: z.enum(["ok", "review"]).catch("ok"),
|
|
5863
|
+
reviewReasons: z.string().catch("")
|
|
5864
|
+
});
|
|
5865
|
+
var MALFORMED_ROW_COLUMN = "Status";
|
|
5866
|
+
function cellAt(cells, column) {
|
|
5867
|
+
return cells[column - 1] ?? "";
|
|
5868
|
+
}
|
|
5869
|
+
function parseRowCells(cells) {
|
|
5870
|
+
const result = rowSchema.safeParse({
|
|
5871
|
+
key: cellAt(cells, COLUMN.key),
|
|
5872
|
+
source: cellAt(cells, COLUMN.source),
|
|
5873
|
+
currentTarget: cellAt(cells, COLUMN.current),
|
|
5874
|
+
status: cellAt(cells, COLUMN.status),
|
|
5875
|
+
sourceHash: cellAt(cells, COLUMN.sourceHash),
|
|
5876
|
+
translation: cellAt(cells, COLUMN.translation).trim(),
|
|
5877
|
+
context: cellAt(cells, COLUMN.context),
|
|
5878
|
+
reviewStatus: cellAt(cells, COLUMN.reviewStatus),
|
|
5879
|
+
reviewReasons: cellAt(cells, COLUMN.reviewReasons)
|
|
5880
|
+
});
|
|
5881
|
+
if (!result.success) {
|
|
5882
|
+
return { ok: false, column: MALFORMED_ROW_COLUMN };
|
|
5883
|
+
}
|
|
5884
|
+
return { ok: true, row: result.data };
|
|
5885
|
+
}
|
|
5886
|
+
function rowCells(row) {
|
|
5887
|
+
const cells = new Array(HEADERS.length).fill("");
|
|
5888
|
+
cells[COLUMN.key - 1] = row.key;
|
|
5889
|
+
cells[COLUMN.source - 1] = row.source;
|
|
5890
|
+
cells[COLUMN.current - 1] = row.currentTarget;
|
|
5891
|
+
cells[COLUMN.status - 1] = row.status;
|
|
5892
|
+
cells[COLUMN.translation - 1] = row.translation;
|
|
5893
|
+
cells[COLUMN.sourceHash - 1] = row.sourceHash;
|
|
5894
|
+
cells[COLUMN.context - 1] = row.context;
|
|
5895
|
+
cells[COLUMN.reviewStatus - 1] = row.reviewStatus;
|
|
5896
|
+
cells[COLUMN.reviewReasons - 1] = row.reviewReasons;
|
|
5897
|
+
return cells;
|
|
5898
|
+
}
|
|
5899
|
+
var QUOTE = '"';
|
|
5900
|
+
var ESCAPED_QUOTE = '""';
|
|
5901
|
+
var LINE_BREAK = "\n";
|
|
5902
|
+
var UTF8_BOM = "\uFEFF";
|
|
5903
|
+
function needsQuoting(value, delimiter) {
|
|
5904
|
+
return value.includes(delimiter) || value.includes(QUOTE) || value.includes("\n") || value.includes("\r") || value !== value.trim();
|
|
5905
|
+
}
|
|
5906
|
+
function encodeField(value, delimiter) {
|
|
5907
|
+
if (!needsQuoting(value, delimiter)) {
|
|
5908
|
+
return value;
|
|
5909
|
+
}
|
|
5910
|
+
return `${QUOTE}${value.replaceAll(QUOTE, ESCAPED_QUOTE)}${QUOTE}`;
|
|
5911
|
+
}
|
|
5912
|
+
function encodeRecord(fields, delimiter) {
|
|
5913
|
+
return fields.map((field) => encodeField(field, delimiter)).join(delimiter);
|
|
5914
|
+
}
|
|
5915
|
+
function buildDelimited(sheet, format) {
|
|
5916
|
+
const delimiter = DELIMITER[format];
|
|
5917
|
+
const records = [encodeRecord(HEADERS, delimiter)];
|
|
5918
|
+
for (const row of sheet.rows) {
|
|
5919
|
+
records.push(encodeRecord(rowCells(row), delimiter));
|
|
5920
|
+
}
|
|
5921
|
+
const text = `${records.join(LINE_BREAK)}${LINE_BREAK}`;
|
|
5922
|
+
return format === "csv" ? `${UTF8_BOM}${text}` : text;
|
|
5923
|
+
}
|
|
5623
5924
|
var INSTRUCTIONS_LINES = [
|
|
5624
5925
|
"How to use this workbook",
|
|
5625
5926
|
"",
|
|
@@ -5648,30 +5949,6 @@ var INSTRUCTIONS_LINES = [
|
|
|
5648
5949
|
"a value with a broken placeholder, invalid ICU, or a source that changed since export is",
|
|
5649
5950
|
"withheld (not written) and reported so you can fix and resubmit it."
|
|
5650
5951
|
];
|
|
5651
|
-
var COLUMN = {
|
|
5652
|
-
key: 1,
|
|
5653
|
-
source: 2,
|
|
5654
|
-
current: 3,
|
|
5655
|
-
status: 4,
|
|
5656
|
-
translation: 5,
|
|
5657
|
-
sourceHash: 6,
|
|
5658
|
-
context: 7,
|
|
5659
|
-
reviewStatus: 8,
|
|
5660
|
-
reviewReasons: 9
|
|
5661
|
-
};
|
|
5662
|
-
var HEADERS = [
|
|
5663
|
-
"Key",
|
|
5664
|
-
"Source",
|
|
5665
|
-
"Current translation",
|
|
5666
|
-
"Status",
|
|
5667
|
-
"Translation",
|
|
5668
|
-
"Source hash",
|
|
5669
|
-
"Context",
|
|
5670
|
-
"Review status",
|
|
5671
|
-
"Review reasons"
|
|
5672
|
-
];
|
|
5673
|
-
var HEADER_ROW = 1;
|
|
5674
|
-
var INSTRUCTIONS_SHEET_NAME = "Instructions";
|
|
5675
5952
|
var READ_ONLY_FILL = {
|
|
5676
5953
|
type: "pattern",
|
|
5677
5954
|
pattern: "solid",
|
|
@@ -5831,6 +6108,12 @@ async function buildWorkbook(model) {
|
|
|
5831
6108
|
const protectedBytes = await protectWorkbookStructure(serialized);
|
|
5832
6109
|
return Uint8Array.prototype.slice.call(protectedBytes);
|
|
5833
6110
|
}
|
|
6111
|
+
var DEFAULT_DELIMITED_LIMITS = {
|
|
6112
|
+
maxInputBytes: 32 * 1024 * 1024,
|
|
6113
|
+
maxRowsPerFile: 1e5,
|
|
6114
|
+
maxFieldsPerRow: 64,
|
|
6115
|
+
maxFieldLength: 1024 * 1024
|
|
6116
|
+
};
|
|
5834
6117
|
var DEFAULT_WORKBOOK_LIMITS = {
|
|
5835
6118
|
maxDecompressedBytes: 64 * 1024 * 1024,
|
|
5836
6119
|
maxEntryCount: 1024,
|
|
@@ -5838,6 +6121,205 @@ var DEFAULT_WORKBOOK_LIMITS = {
|
|
|
5838
6121
|
maxRowsPerSheet: 1e5,
|
|
5839
6122
|
maxCellsPerRow: 64
|
|
5840
6123
|
};
|
|
6124
|
+
var QUOTE2 = '"';
|
|
6125
|
+
var HEADER_RECORD = 1;
|
|
6126
|
+
function scanQuotedField(text, start) {
|
|
6127
|
+
const parts = [];
|
|
6128
|
+
let cursor = start;
|
|
6129
|
+
for (; ; ) {
|
|
6130
|
+
const quote = text.indexOf(QUOTE2, cursor);
|
|
6131
|
+
if (quote === -1) {
|
|
6132
|
+
parts.push(text.slice(cursor));
|
|
6133
|
+
return { value: parts.join(""), next: text.length };
|
|
6134
|
+
}
|
|
6135
|
+
parts.push(text.slice(cursor, quote));
|
|
6136
|
+
if (text[quote + 1] !== QUOTE2) {
|
|
6137
|
+
return { value: parts.join(""), next: quote + 1 };
|
|
6138
|
+
}
|
|
6139
|
+
parts.push(QUOTE2);
|
|
6140
|
+
cursor = quote + 2;
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
function scanPlainField(text, start, delimiter) {
|
|
6144
|
+
let cursor = start;
|
|
6145
|
+
while (cursor < text.length) {
|
|
6146
|
+
const char = text[cursor];
|
|
6147
|
+
if (char === delimiter || char === "\n" || char === "\r") {
|
|
6148
|
+
break;
|
|
6149
|
+
}
|
|
6150
|
+
cursor += 1;
|
|
6151
|
+
}
|
|
6152
|
+
return { value: text.slice(start, cursor), next: cursor };
|
|
6153
|
+
}
|
|
6154
|
+
function scanField(text, start, delimiter) {
|
|
6155
|
+
if (text[start] !== QUOTE2) {
|
|
6156
|
+
return scanPlainField(text, start, delimiter);
|
|
6157
|
+
}
|
|
6158
|
+
const quoted = scanQuotedField(text, start + 1);
|
|
6159
|
+
const trailing = scanPlainField(text, quoted.next, delimiter);
|
|
6160
|
+
return { value: `${quoted.value}${trailing.value}`, next: trailing.next };
|
|
6161
|
+
}
|
|
6162
|
+
function countLineBreaks(value) {
|
|
6163
|
+
let breaks = 0;
|
|
6164
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
6165
|
+
const char = value[index];
|
|
6166
|
+
if (char === "\n") {
|
|
6167
|
+
breaks += 1;
|
|
6168
|
+
} else if (char === "\r") {
|
|
6169
|
+
breaks += 1;
|
|
6170
|
+
if (value[index + 1] === "\n") {
|
|
6171
|
+
index += 1;
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
return breaks;
|
|
6176
|
+
}
|
|
6177
|
+
function scanRecord(text, start, delimiter, limits) {
|
|
6178
|
+
const fields = [];
|
|
6179
|
+
let breaks = 0;
|
|
6180
|
+
let cursor = start;
|
|
6181
|
+
for (; ; ) {
|
|
6182
|
+
const field = scanField(text, cursor, delimiter);
|
|
6183
|
+
assertFieldLength(field.value, limits);
|
|
6184
|
+
fields.push(field.value);
|
|
6185
|
+
breaks += countLineBreaks(field.value);
|
|
6186
|
+
assertFieldCount(fields.length, limits);
|
|
6187
|
+
cursor = field.next;
|
|
6188
|
+
if (text[cursor] !== delimiter) {
|
|
6189
|
+
return { fields, breaks, next: cursor };
|
|
6190
|
+
}
|
|
6191
|
+
cursor += 1;
|
|
6192
|
+
}
|
|
6193
|
+
}
|
|
6194
|
+
function consumeLineBreak(text, cursor) {
|
|
6195
|
+
if (text[cursor] === "\r") {
|
|
6196
|
+
return text[cursor + 1] === "\n" ? cursor + 2 : cursor + 1;
|
|
6197
|
+
}
|
|
6198
|
+
return text[cursor] === "\n" ? cursor + 1 : cursor;
|
|
6199
|
+
}
|
|
6200
|
+
function scanRecords(text, delimiter, limits) {
|
|
6201
|
+
const records = [];
|
|
6202
|
+
let cursor = 0;
|
|
6203
|
+
let line = 1;
|
|
6204
|
+
while (cursor < text.length) {
|
|
6205
|
+
const record = scanRecord(text, cursor, delimiter, limits);
|
|
6206
|
+
records.push({ fields: record.fields, line });
|
|
6207
|
+
assertRecordCount(records.length, limits);
|
|
6208
|
+
const next = consumeLineBreak(text, record.next);
|
|
6209
|
+
if (next === record.next) {
|
|
6210
|
+
break;
|
|
6211
|
+
}
|
|
6212
|
+
line += record.breaks + 1;
|
|
6213
|
+
cursor = next;
|
|
6214
|
+
}
|
|
6215
|
+
return records;
|
|
6216
|
+
}
|
|
6217
|
+
function assertInputBytes(text, limits) {
|
|
6218
|
+
if (Buffer.byteLength(text, "utf8") > limits.maxInputBytes) {
|
|
6219
|
+
throw new ExchangeError(
|
|
6220
|
+
"WORKBOOK_INVALID",
|
|
6221
|
+
`The interchange file is larger than the maximum of ${limits.maxInputBytes} bytes.`
|
|
6222
|
+
);
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
function assertFieldLength(value, limits) {
|
|
6226
|
+
if (value.length > limits.maxFieldLength) {
|
|
6227
|
+
throw new ExchangeError(
|
|
6228
|
+
"WORKBOOK_INVALID",
|
|
6229
|
+
`The interchange file has a field longer than the maximum of ${limits.maxFieldLength} characters.`
|
|
6230
|
+
);
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
function assertFieldCount(count, limits) {
|
|
6234
|
+
if (count > limits.maxFieldsPerRow) {
|
|
6235
|
+
throw new ExchangeError(
|
|
6236
|
+
"WORKBOOK_INVALID",
|
|
6237
|
+
`The interchange file has a row with more than the maximum of ${limits.maxFieldsPerRow} fields.`
|
|
6238
|
+
);
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
function assertRecordCount(count, limits) {
|
|
6242
|
+
if (count - HEADER_RECORD > limits.maxRowsPerFile) {
|
|
6243
|
+
throw new ExchangeError(
|
|
6244
|
+
"WORKBOOK_INVALID",
|
|
6245
|
+
`The interchange file has more than the maximum of ${limits.maxRowsPerFile} rows.`
|
|
6246
|
+
);
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
function assertHeaderRecord(records) {
|
|
6250
|
+
const header = records[0]?.fields;
|
|
6251
|
+
if (header === void 0 || header.length !== HEADERS.length) {
|
|
6252
|
+
throw new ExchangeError(
|
|
6253
|
+
"WORKBOOK_INVALID",
|
|
6254
|
+
`The interchange file does not start with the expected header line of ${HEADERS.length} columns.`
|
|
6255
|
+
);
|
|
6256
|
+
}
|
|
6257
|
+
for (const [index, label] of HEADERS.entries()) {
|
|
6258
|
+
if (header[index] !== label) {
|
|
6259
|
+
throw new ExchangeError(
|
|
6260
|
+
"WORKBOOK_INVALID",
|
|
6261
|
+
`The interchange file's header line does not match the expected columns (column ${index + 1} should be "${label}").`
|
|
6262
|
+
);
|
|
6263
|
+
}
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
function fieldCountColumn(fieldCount) {
|
|
6267
|
+
const label = HEADERS.at(Math.min(fieldCount, HEADERS.length - 1));
|
|
6268
|
+
if (label === void 0) {
|
|
6269
|
+
return MALFORMED_ROW_COLUMN;
|
|
6270
|
+
}
|
|
6271
|
+
return label;
|
|
6272
|
+
}
|
|
6273
|
+
function isBlankRecord(fields) {
|
|
6274
|
+
return fields.length === 1 && fields[0] === "";
|
|
6275
|
+
}
|
|
6276
|
+
function readRecord(fields, locale, at, into) {
|
|
6277
|
+
const { row, line } = at;
|
|
6278
|
+
if (isBlankRecord(fields)) {
|
|
6279
|
+
return;
|
|
6280
|
+
}
|
|
6281
|
+
if (fields.length !== HEADERS.length) {
|
|
6282
|
+
into.malformed.push({ locale, row, line, column: fieldCountColumn(fields.length) });
|
|
6283
|
+
return;
|
|
6284
|
+
}
|
|
6285
|
+
if (fields[COLUMN.key - 1] === "") {
|
|
6286
|
+
return;
|
|
6287
|
+
}
|
|
6288
|
+
const outcome = parseRowCells(fields);
|
|
6289
|
+
if (!outcome.ok) {
|
|
6290
|
+
into.malformed.push({ locale, row, line, column: outcome.column });
|
|
6291
|
+
return;
|
|
6292
|
+
}
|
|
6293
|
+
if (into.seenKeys.has(outcome.row.key)) {
|
|
6294
|
+
into.duplicates.push({ locale, key: outcome.row.key, row, line });
|
|
6295
|
+
return;
|
|
6296
|
+
}
|
|
6297
|
+
into.seenKeys.add(outcome.row.key);
|
|
6298
|
+
into.rows.push(outcome.row);
|
|
6299
|
+
}
|
|
6300
|
+
function readDelimited(input, options = {}) {
|
|
6301
|
+
const limits = options.limits ?? DEFAULT_DELIMITED_LIMITS;
|
|
6302
|
+
assertInputBytes(input.text, limits);
|
|
6303
|
+
const text = input.text.startsWith(UTF8_BOM) ? input.text.slice(UTF8_BOM.length) : input.text;
|
|
6304
|
+
const records = scanRecords(text, DELIMITER[input.format], limits);
|
|
6305
|
+
assertHeaderRecord(records);
|
|
6306
|
+
const into = {
|
|
6307
|
+
rows: [],
|
|
6308
|
+
malformed: [],
|
|
6309
|
+
duplicates: [],
|
|
6310
|
+
seenKeys: /* @__PURE__ */ new Set()
|
|
6311
|
+
};
|
|
6312
|
+
for (const [index, record] of records.entries()) {
|
|
6313
|
+
if (index >= HEADER_RECORD) {
|
|
6314
|
+
readRecord(record.fields, input.locale, { row: index + 1, line: record.line }, into);
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
return {
|
|
6318
|
+
sheets: [{ locale: input.locale, rows: into.rows }],
|
|
6319
|
+
malformedRows: into.malformed,
|
|
6320
|
+
duplicateKeys: into.duplicates
|
|
6321
|
+
};
|
|
6322
|
+
}
|
|
5841
6323
|
function assertNoDoctype2(name, xml) {
|
|
5842
6324
|
if (/<!DOCTYPE/i.test(xml) || /<!ENTITY/i.test(xml)) {
|
|
5843
6325
|
throw new ExchangeError(
|
|
@@ -5914,17 +6396,6 @@ async function guardWorkbookBytes(bytes, limits) {
|
|
|
5914
6396
|
assertNoDoctype2(file.name, content);
|
|
5915
6397
|
}
|
|
5916
6398
|
}
|
|
5917
|
-
var rowSchema = z.object({
|
|
5918
|
-
key: z.string().min(1),
|
|
5919
|
-
source: z.string(),
|
|
5920
|
-
currentTarget: z.string(),
|
|
5921
|
-
status: z.enum(["new", "changed", "unchanged"]),
|
|
5922
|
-
sourceHash: z.string(),
|
|
5923
|
-
translation: z.string(),
|
|
5924
|
-
context: z.string(),
|
|
5925
|
-
reviewStatus: z.enum(["ok", "review"]).catch("ok"),
|
|
5926
|
-
reviewReasons: z.string().catch("")
|
|
5927
|
-
});
|
|
5928
6399
|
function cellString(cell) {
|
|
5929
6400
|
const value = cell.value;
|
|
5930
6401
|
if (value === null || value === void 0) {
|
|
@@ -5949,24 +6420,8 @@ function assertHeader(sheet) {
|
|
|
5949
6420
|
);
|
|
5950
6421
|
}
|
|
5951
6422
|
}
|
|
5952
|
-
var MALFORMED_ROW_COLUMN = "Status";
|
|
5953
6423
|
function parseRow(row) {
|
|
5954
|
-
|
|
5955
|
-
key: cellString(row.getCell(COLUMN.key)),
|
|
5956
|
-
source: cellString(row.getCell(COLUMN.source)),
|
|
5957
|
-
currentTarget: cellString(row.getCell(COLUMN.current)),
|
|
5958
|
-
status: cellString(row.getCell(COLUMN.status)),
|
|
5959
|
-
sourceHash: cellString(row.getCell(COLUMN.sourceHash)),
|
|
5960
|
-
translation: cellString(row.getCell(COLUMN.translation)).trim(),
|
|
5961
|
-
context: cellString(row.getCell(COLUMN.context)),
|
|
5962
|
-
reviewStatus: cellString(row.getCell(COLUMN.reviewStatus)),
|
|
5963
|
-
reviewReasons: cellString(row.getCell(COLUMN.reviewReasons))
|
|
5964
|
-
};
|
|
5965
|
-
const result = rowSchema.safeParse(candidate);
|
|
5966
|
-
if (!result.success) {
|
|
5967
|
-
return { ok: false, column: MALFORMED_ROW_COLUMN };
|
|
5968
|
-
}
|
|
5969
|
-
return { ok: true, row: result.data };
|
|
6424
|
+
return parseRowCells(HEADERS.map((_, index) => cellString(row.getCell(index + 1))));
|
|
5970
6425
|
}
|
|
5971
6426
|
function readDataSheet(sheet, limits) {
|
|
5972
6427
|
assertHeader(sheet);
|
|
@@ -6040,8 +6495,52 @@ async function readWorkbook(bytes, options = {}) {
|
|
|
6040
6495
|
return { sheets, malformedRows, duplicateKeys };
|
|
6041
6496
|
}
|
|
6042
6497
|
|
|
6498
|
+
// src/flow/workbook/exchange-format.ts
|
|
6499
|
+
function isDelimitedFormat(format) {
|
|
6500
|
+
return format === "csv" || format === "tsv";
|
|
6501
|
+
}
|
|
6502
|
+
var MANIFEST_VERSION = 1;
|
|
6503
|
+
var MAX_MANIFEST_BYTES = 1024 * 1024;
|
|
6504
|
+
function exportManifestFileName(format) {
|
|
6505
|
+
return `.verbatra-export-${format}.json`;
|
|
6506
|
+
}
|
|
6507
|
+
var manifestSchema = z.object({
|
|
6508
|
+
version: z.number().int().positive(),
|
|
6509
|
+
format: z.enum(["csv", "tsv"]),
|
|
6510
|
+
locales: z.array(z.string())
|
|
6511
|
+
});
|
|
6512
|
+
async function writeExportManifest(fs, directory, format, locales) {
|
|
6513
|
+
const manifest = { version: MANIFEST_VERSION, format, locales: [...locales] };
|
|
6514
|
+
await fs.writeFile(
|
|
6515
|
+
join(directory, exportManifestFileName(format)),
|
|
6516
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
6517
|
+
`
|
|
6518
|
+
);
|
|
6519
|
+
}
|
|
6520
|
+
async function readExportedLocales(fs, directory, format) {
|
|
6521
|
+
const read = await fs.readFileBounded(
|
|
6522
|
+
join(directory, exportManifestFileName(format)),
|
|
6523
|
+
MAX_MANIFEST_BYTES
|
|
6524
|
+
);
|
|
6525
|
+
if (read.kind !== "ok") {
|
|
6526
|
+
return void 0;
|
|
6527
|
+
}
|
|
6528
|
+
let parsed;
|
|
6529
|
+
try {
|
|
6530
|
+
parsed = JSON.parse(read.content);
|
|
6531
|
+
} catch {
|
|
6532
|
+
return void 0;
|
|
6533
|
+
}
|
|
6534
|
+
const result = manifestSchema.safeParse(parsed);
|
|
6535
|
+
if (!result.success || result.data.version !== MANIFEST_VERSION || result.data.format !== format) {
|
|
6536
|
+
return void 0;
|
|
6537
|
+
}
|
|
6538
|
+
return new Set(result.data.locales);
|
|
6539
|
+
}
|
|
6540
|
+
|
|
6043
6541
|
// src/flow/workbook/export-workbook.ts
|
|
6044
6542
|
var DEFAULT_WORKBOOK_PATH = "verbatra-translations.xlsx";
|
|
6543
|
+
var DEFAULT_DELIMITED_PATH = "verbatra-translations";
|
|
6045
6544
|
function reasonLabel(reason) {
|
|
6046
6545
|
return reason.toLowerCase().replace(/_/g, "-");
|
|
6047
6546
|
}
|
|
@@ -6105,6 +6604,21 @@ function buildRows(source, target, baseline, includeUnchanged, adapter, glossary
|
|
|
6105
6604
|
}
|
|
6106
6605
|
return [...rows].sort((a, b) => a.key < b.key ? -1 : 1);
|
|
6107
6606
|
}
|
|
6607
|
+
async function writeDelimitedFiles(fs, directory, format, sheets) {
|
|
6608
|
+
await fs.mkdir?.(directory);
|
|
6609
|
+
for (const sheet of sheets) {
|
|
6610
|
+
await fs.writeFile(
|
|
6611
|
+
join(directory, delimitedFileName(sheet.locale, format)),
|
|
6612
|
+
buildDelimited(sheet, format)
|
|
6613
|
+
);
|
|
6614
|
+
}
|
|
6615
|
+
await writeExportManifest(
|
|
6616
|
+
fs,
|
|
6617
|
+
directory,
|
|
6618
|
+
format,
|
|
6619
|
+
sheets.map((sheet) => sheet.locale)
|
|
6620
|
+
);
|
|
6621
|
+
}
|
|
6108
6622
|
async function exportWorkbook(input, deps = {}) {
|
|
6109
6623
|
const config = input.config;
|
|
6110
6624
|
const cwd = input.cwd ?? process.cwd();
|
|
@@ -6127,10 +6641,17 @@ async function exportWorkbook(input, deps = {}) {
|
|
|
6127
6641
|
return { locale, rows };
|
|
6128
6642
|
})
|
|
6129
6643
|
);
|
|
6130
|
-
const
|
|
6131
|
-
const
|
|
6132
|
-
|
|
6133
|
-
|
|
6644
|
+
const format = input.format ?? "xlsx";
|
|
6645
|
+
const path = resolve(
|
|
6646
|
+
cwd,
|
|
6647
|
+
input.out ?? (isDelimitedFormat(format) ? DEFAULT_DELIMITED_PATH : DEFAULT_WORKBOOK_PATH)
|
|
6648
|
+
);
|
|
6649
|
+
if (isDelimitedFormat(format)) {
|
|
6650
|
+
await writeDelimitedFiles(fs, path, format, sheets);
|
|
6651
|
+
} else {
|
|
6652
|
+
const model = { sheets };
|
|
6653
|
+
await fs.writeBytes(path, await buildWorkbook(model));
|
|
6654
|
+
}
|
|
6134
6655
|
return {
|
|
6135
6656
|
path,
|
|
6136
6657
|
locales: sheets.map((sheet) => ({ locale: sheet.locale, rows: sheet.rows.length }))
|
|
@@ -6259,6 +6780,7 @@ function importLocale(params) {
|
|
|
6259
6780
|
|
|
6260
6781
|
// src/flow/workbook/import-workbook.ts
|
|
6261
6782
|
var MAX_WORKBOOK_FILE_BYTES = 64 * 1024 * 1024;
|
|
6783
|
+
var MAX_DELIMITED_FILE_BYTES = 32 * 1024 * 1024;
|
|
6262
6784
|
async function readWorkbookBytes(path, fs) {
|
|
6263
6785
|
const read = await fs.readBytesBounded(path, MAX_WORKBOOK_FILE_BYTES);
|
|
6264
6786
|
if (read.kind === "missing") {
|
|
@@ -6272,6 +6794,75 @@ async function readWorkbookBytes(path, fs) {
|
|
|
6272
6794
|
}
|
|
6273
6795
|
return read.bytes;
|
|
6274
6796
|
}
|
|
6797
|
+
async function readDelimitedText(path, fs) {
|
|
6798
|
+
const read = await fs.readFileBounded(path, MAX_DELIMITED_FILE_BYTES);
|
|
6799
|
+
if (read.kind === "missing") {
|
|
6800
|
+
return void 0;
|
|
6801
|
+
}
|
|
6802
|
+
if (read.kind === "too-large") {
|
|
6803
|
+
throw new SdkError(
|
|
6804
|
+
"SOURCE_INVALID",
|
|
6805
|
+
`The interchange file at ${path} exceeds the maximum allowed size of ${MAX_DELIMITED_FILE_BYTES} bytes.`
|
|
6806
|
+
);
|
|
6807
|
+
}
|
|
6808
|
+
return read.content;
|
|
6809
|
+
}
|
|
6810
|
+
async function collectDelimitedSources(path, config, fs, format) {
|
|
6811
|
+
const single = await readDelimitedText(path, fs);
|
|
6812
|
+
if (single !== void 0) {
|
|
6813
|
+
return { sources: [{ locale: basename(path, `.${format}`), text: single }], staleLocales: [] };
|
|
6814
|
+
}
|
|
6815
|
+
const exported = await readExportedLocales(fs, path, format);
|
|
6816
|
+
const sources = [];
|
|
6817
|
+
const staleLocales = [];
|
|
6818
|
+
for (const locale of config.targetLocales) {
|
|
6819
|
+
const text = await readDelimitedText(join(path, delimitedFileName(locale, format)), fs);
|
|
6820
|
+
if (text === void 0) {
|
|
6821
|
+
continue;
|
|
6822
|
+
}
|
|
6823
|
+
if (exported !== void 0 && !exported.has(locale)) {
|
|
6824
|
+
staleLocales.push(locale);
|
|
6825
|
+
continue;
|
|
6826
|
+
}
|
|
6827
|
+
sources.push({ locale, text });
|
|
6828
|
+
}
|
|
6829
|
+
if (sources.length === 0 && staleLocales.length === 0) {
|
|
6830
|
+
throw new SdkError(
|
|
6831
|
+
"SOURCE_UNREADABLE",
|
|
6832
|
+
`No ${format} file was found at ${path}, and it holds no <locale>.${format} file for any configured target locale.`
|
|
6833
|
+
);
|
|
6834
|
+
}
|
|
6835
|
+
return { sources, staleLocales };
|
|
6836
|
+
}
|
|
6837
|
+
function parseDelimitedSources(sources, format) {
|
|
6838
|
+
const sheets = [];
|
|
6839
|
+
const malformedRows = [];
|
|
6840
|
+
const duplicateKeys = [];
|
|
6841
|
+
for (const source of sources) {
|
|
6842
|
+
const data = readDelimited({ text: source.text, locale: source.locale, format });
|
|
6843
|
+
sheets.push(...data.sheets);
|
|
6844
|
+
malformedRows.push(...data.malformedRows);
|
|
6845
|
+
duplicateKeys.push(...data.duplicateKeys);
|
|
6846
|
+
}
|
|
6847
|
+
return { sheets, malformedRows, duplicateKeys };
|
|
6848
|
+
}
|
|
6849
|
+
async function readImportData(path, config, fs, format) {
|
|
6850
|
+
try {
|
|
6851
|
+
if (isDelimitedFormat(format)) {
|
|
6852
|
+
const handoff = await collectDelimitedSources(path, config, fs, format);
|
|
6853
|
+
return {
|
|
6854
|
+
data: parseDelimitedSources(handoff.sources, format),
|
|
6855
|
+
staleLocales: handoff.staleLocales
|
|
6856
|
+
};
|
|
6857
|
+
}
|
|
6858
|
+
return { data: await readWorkbook(await readWorkbookBytes(path, fs)), staleLocales: [] };
|
|
6859
|
+
} catch (error) {
|
|
6860
|
+
if (error instanceof SdkError) {
|
|
6861
|
+
throw error;
|
|
6862
|
+
}
|
|
6863
|
+
throw new SdkError("SOURCE_INVALID", errorMessage(error));
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6275
6866
|
function mergeAccepted(target, accepted) {
|
|
6276
6867
|
const merged = new Map(target.entries);
|
|
6277
6868
|
for (const [key, { value, source }] of accepted) {
|
|
@@ -6312,18 +6903,43 @@ function computeLockEntries2(source, merged, baseline, accepted) {
|
|
|
6312
6903
|
}
|
|
6313
6904
|
var MissingSheetError = class extends Error {
|
|
6314
6905
|
code = "WORKBOOK_SHEET_MISSING";
|
|
6315
|
-
constructor(locale) {
|
|
6906
|
+
constructor(locale, format) {
|
|
6316
6907
|
super(
|
|
6317
|
-
`The workbook has no sheet (tab) for the configured target locale "${locale}". The tab may have been renamed, deleted, or reordered out of the workbook.`
|
|
6908
|
+
isDelimitedFormat(format) ? `The handoff has no "${delimitedFileName(locale, format)}" file for the configured target locale "${locale}". The file may have been renamed, deleted, or left out of the directory.` : `The workbook has no sheet (tab) for the configured target locale "${locale}". The tab may have been renamed, deleted, or reordered out of the workbook.`
|
|
6318
6909
|
);
|
|
6319
6910
|
this.name = "MissingSheetError";
|
|
6320
6911
|
}
|
|
6321
6912
|
};
|
|
6913
|
+
var StaleHandoffFileError = class extends Error {
|
|
6914
|
+
code = "HANDOFF_FILE_STALE";
|
|
6915
|
+
constructor(locale, format) {
|
|
6916
|
+
super(
|
|
6917
|
+
`The file "${delimitedFileName(locale, format)}" is left over from an earlier export that included the target locale "${locale}"; the most recent export into this directory did not. Its rows were not applied, because they reflect that earlier run. Re-export the locale to refresh the file, or delete it.`
|
|
6918
|
+
);
|
|
6919
|
+
this.name = "StaleHandoffFileError";
|
|
6920
|
+
}
|
|
6921
|
+
};
|
|
6922
|
+
function absentLocaleFailures(config, sheets, format, staleLocales) {
|
|
6923
|
+
const present = new Set(sheets.map((sheet) => sheet.locale));
|
|
6924
|
+
const stale = new Set(staleLocales);
|
|
6925
|
+
const failures = [];
|
|
6926
|
+
for (const locale of config.targetLocales) {
|
|
6927
|
+
if (present.has(locale)) {
|
|
6928
|
+
continue;
|
|
6929
|
+
}
|
|
6930
|
+
const error = stale.has(locale) && isDelimitedFormat(format) ? new StaleHandoffFileError(locale, format) : new MissingSheetError(locale, format);
|
|
6931
|
+
failures.push(failureSummary(locale, error));
|
|
6932
|
+
}
|
|
6933
|
+
return failures;
|
|
6934
|
+
}
|
|
6935
|
+
function lineOf(reported) {
|
|
6936
|
+
return reported.line === void 0 ? {} : { line: reported.line };
|
|
6937
|
+
}
|
|
6322
6938
|
async function runSheet(ctx, sheet, lock) {
|
|
6323
6939
|
if (!ctx.config.targetLocales.includes(sheet.locale)) {
|
|
6324
6940
|
throw new SdkError(
|
|
6325
6941
|
"CONFIG_INVALID",
|
|
6326
|
-
`The workbook has a sheet named "${sheet.locale}", which is not a configured target locale. It may be a renamed, added, or reordered tab; leave every language tab named exactly as exported.`
|
|
6942
|
+
isDelimitedFormat(ctx.format) ? `The handoff has a file named "${sheet.locale}.${ctx.format}", whose locale is not a configured target locale. Name every interchange file exactly as it was exported.` : `The workbook has a sheet named "${sheet.locale}", which is not a configured target locale. It may be a renamed, added, or reordered tab; leave every language tab named exactly as exported.`
|
|
6327
6943
|
);
|
|
6328
6944
|
}
|
|
6329
6945
|
const target = await readTarget(ctx.cwd, ctx.config, ctx.adapter, ctx.fs, sheet.locale);
|
|
@@ -6335,15 +6951,15 @@ async function runSheet(ctx, sheet, lock) {
|
|
|
6335
6951
|
baseline,
|
|
6336
6952
|
adapter: ctx.adapter,
|
|
6337
6953
|
sourceInvalidIcuKeys: ctx.sourceInvalidIcuKeys,
|
|
6338
|
-
malformedRows: ctx.malformedRows.filter((problem) => problem.locale === sheet.locale).map((problem) => ({ row: problem.row, column: problem.column })),
|
|
6339
|
-
duplicateKeys: ctx.duplicateKeys.filter((duplicate) => duplicate.locale === sheet.locale).map((duplicate) => ({ key: duplicate.key, row: duplicate.row }))
|
|
6954
|
+
malformedRows: ctx.malformedRows.filter((problem) => problem.locale === sheet.locale).map((problem) => ({ row: problem.row, column: problem.column, ...lineOf(problem) })),
|
|
6955
|
+
duplicateKeys: ctx.duplicateKeys.filter((duplicate) => duplicate.locale === sheet.locale).map((duplicate) => ({ key: duplicate.key, row: duplicate.row, ...lineOf(duplicate) }))
|
|
6340
6956
|
});
|
|
6341
6957
|
if (ctx.dryRun) {
|
|
6342
6958
|
return { summary, lockEntries: {}, cacheAdditions: {} };
|
|
6343
6959
|
}
|
|
6344
6960
|
const merged = mergeAccepted(target, accepted);
|
|
6345
6961
|
if (accepted.size > 0) {
|
|
6346
|
-
const path =
|
|
6962
|
+
const path = createLocalePathResolver(ctx.cwd, ctx.config).pathFor(sheet.locale);
|
|
6347
6963
|
await ctx.adapter.write(
|
|
6348
6964
|
{
|
|
6349
6965
|
locale: sheet.locale,
|
|
@@ -6367,14 +6983,13 @@ async function importWorkbook(input, deps = {}) {
|
|
|
6367
6983
|
const fs = deps.fs ?? defaultFs;
|
|
6368
6984
|
const adapter = selectAdapter(config.format, deps.adapterRegistry);
|
|
6369
6985
|
const source = await readSource(config, cwd, fs, adapter);
|
|
6370
|
-
const
|
|
6371
|
-
const
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
}
|
|
6986
|
+
const format = input.format ?? "xlsx";
|
|
6987
|
+
const { data, staleLocales } = await readImportData(
|
|
6988
|
+
resolve(cwd, input.workbook),
|
|
6989
|
+
config,
|
|
6990
|
+
fs,
|
|
6991
|
+
format
|
|
6992
|
+
);
|
|
6378
6993
|
const lock = await readLockFile(lockFilePath(cwd), fs);
|
|
6379
6994
|
const ctx = {
|
|
6380
6995
|
config,
|
|
@@ -6385,7 +7000,8 @@ async function importWorkbook(input, deps = {}) {
|
|
|
6385
7000
|
sourceInvalidIcuKeys: source.invalidIcuKeys,
|
|
6386
7001
|
dryRun,
|
|
6387
7002
|
malformedRows: data.malformedRows,
|
|
6388
|
-
duplicateKeys: data.duplicateKeys
|
|
7003
|
+
duplicateKeys: data.duplicateKeys,
|
|
7004
|
+
format
|
|
6389
7005
|
};
|
|
6390
7006
|
const summaries = [];
|
|
6391
7007
|
const cacheAdditions = /* @__PURE__ */ new Map();
|
|
@@ -6410,12 +7026,7 @@ async function importWorkbook(input, deps = {}) {
|
|
|
6410
7026
|
summaries.push(failureSummary(sheet.locale, error));
|
|
6411
7027
|
}
|
|
6412
7028
|
}
|
|
6413
|
-
|
|
6414
|
-
for (const locale of config.targetLocales) {
|
|
6415
|
-
if (!presentLocales.has(locale)) {
|
|
6416
|
-
summaries.push(failureSummary(locale, new MissingSheetError(locale)));
|
|
6417
|
-
}
|
|
6418
|
-
}
|
|
7029
|
+
summaries.push(...absentLocaleFailures(config, data.sheets, format, staleLocales));
|
|
6419
7030
|
if (!dryRun) {
|
|
6420
7031
|
await feedTranslationMemory(cwd, fs, computeFingerprint(config), cacheAdditions);
|
|
6421
7032
|
}
|
|
@@ -6464,7 +7075,8 @@ async function watch(input, deps = {}) {
|
|
|
6464
7075
|
const debounceMs = input.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
6465
7076
|
const fs = deps.fs ?? defaultFs;
|
|
6466
7077
|
resolveRunConcurrency(input.concurrency, false, input.config);
|
|
6467
|
-
const
|
|
7078
|
+
const resolver = createLocalePathResolver(cwd, input.config);
|
|
7079
|
+
const sourcePath = resolver.pathFor(input.config.sourceLocale);
|
|
6468
7080
|
if (!await fs.fileExists(sourcePath)) {
|
|
6469
7081
|
throw new SdkError(
|
|
6470
7082
|
"SOURCE_UNREADABLE",
|
|
@@ -6547,6 +7159,6 @@ async function watch(input, deps = {}) {
|
|
|
6547
7159
|
return { stop };
|
|
6548
7160
|
}
|
|
6549
7161
|
|
|
6550
|
-
export { CACHE_FILE_NAME, DEFAULT_WORKBOOK_PATH, LOCK_FILE_NAME, SdkError, check, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate2 as translate, verbatraConfigSchema, watch };
|
|
7162
|
+
export { CACHE_FILE_NAME, DEFAULT_DELIMITED_PATH, DEFAULT_WORKBOOK_PATH, LOCK_FILE_NAME, SdkError, check, createLocalePathResolver, defineConfig, diff, diffLocaleSnapshots, editEntry, exportWorkbook, importWorkbook, keyIntegrity, keyValue, loadConfig, loadConfigWithMeta, loadLockFile, lockState, readLocaleFileSnapshot, retranslateEntry, runStatus, scaffoldingMetadata, translate2 as translate, verbatraConfigSchema, watch };
|
|
6551
7163
|
//# sourceMappingURL=index.js.map
|
|
6552
7164
|
//# sourceMappingURL=index.js.map
|