scribe-cms 0.0.10 → 0.0.12
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/dist/cli/index.cjs +193 -111
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +191 -111
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +188 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +186 -110
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +118 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +116 -1
- package/dist/runtime.js.map +1 -1
- package/dist/src/core/types.d.ts +0 -2
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/loader/create-loader.d.ts.map +1 -1
- package/dist/src/translate/gemini-client.d.ts +5 -0
- package/dist/src/translate/gemini-client.d.ts.map +1 -1
- package/dist/src/translate/normalize-mdx-body.d.ts +9 -0
- package/dist/src/translate/normalize-mdx-body.d.ts.map +1 -0
- package/dist/src/translate/resolve-translate-config.d.ts.map +1 -1
- package/dist/src/translate/validate-mdx-body.d.ts +16 -0
- package/dist/src/translate/validate-mdx-body.d.ts.map +1 -0
- package/dist/src/validate/validate-project.d.ts.map +1 -1
- package/dist/studio/server.cjs +6 -0
- package/dist/studio/server.cjs.map +1 -1
- package/dist/studio/server.js +4 -0
- package/dist/studio/server.js.map +1 -1
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,9 @@ var path3 = require('path');
|
|
|
5
5
|
var fs2 = require('fs');
|
|
6
6
|
var matter = require('gray-matter');
|
|
7
7
|
var Database = require('better-sqlite3');
|
|
8
|
+
var remarkMdx = require('remark-mdx');
|
|
9
|
+
var remarkParse = require('remark-parse');
|
|
10
|
+
var unified = require('unified');
|
|
8
11
|
var jiti = require('jiti');
|
|
9
12
|
var crypto = require('crypto');
|
|
10
13
|
var genai = require('@google/genai');
|
|
@@ -15,6 +18,8 @@ var path3__default = /*#__PURE__*/_interopDefault(path3);
|
|
|
15
18
|
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
16
19
|
var matter__default = /*#__PURE__*/_interopDefault(matter);
|
|
17
20
|
var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
21
|
+
var remarkMdx__default = /*#__PURE__*/_interopDefault(remarkMdx);
|
|
22
|
+
var remarkParse__default = /*#__PURE__*/_interopDefault(remarkParse);
|
|
18
23
|
|
|
19
24
|
// ../../node_modules/tsup/assets/cjs_shims.js
|
|
20
25
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
@@ -698,6 +703,135 @@ function bulkLoadTranslations(db) {
|
|
|
698
703
|
return db.prepare(`SELECT * FROM translations ORDER BY content_type, en_slug, locale`).all();
|
|
699
704
|
}
|
|
700
705
|
|
|
706
|
+
// src/translate/normalize-mdx-body.ts
|
|
707
|
+
function needsMdxEscapeNormalization(body) {
|
|
708
|
+
return /\\n[ \t/>]|<[\w.-][^>]*\\n/.test(body);
|
|
709
|
+
}
|
|
710
|
+
function normalizeTranslatedMdxBody(body) {
|
|
711
|
+
if (!needsMdxEscapeNormalization(body)) {
|
|
712
|
+
return { body, adjusted: false };
|
|
713
|
+
}
|
|
714
|
+
const normalized = body.replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
715
|
+
return { body: normalized, adjusted: normalized !== body };
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// src/translate/sanitize-mdx-jsx.ts
|
|
719
|
+
function sanitizeMdxJsxAttributeQuotes(body) {
|
|
720
|
+
let adjusted = false;
|
|
721
|
+
let out = "";
|
|
722
|
+
let i = 0;
|
|
723
|
+
while (i < body.length) {
|
|
724
|
+
if (body[i] !== "<" || !/[\w.]/.test(body[i + 1] ?? "")) {
|
|
725
|
+
out += body[i];
|
|
726
|
+
i += 1;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
const tagStart = i;
|
|
730
|
+
i += 1;
|
|
731
|
+
while (i < body.length && /[\w.-]/.test(body[i] ?? "")) i += 1;
|
|
732
|
+
const tagName = body.slice(tagStart + 1, i);
|
|
733
|
+
let tagOut = `<${tagName}`;
|
|
734
|
+
let tagAdjusted = false;
|
|
735
|
+
while (i < body.length && body[i] !== ">" && body[i] !== "/") {
|
|
736
|
+
while (i < body.length && /\s/.test(body[i] ?? "")) {
|
|
737
|
+
tagOut += body[i];
|
|
738
|
+
i += 1;
|
|
739
|
+
}
|
|
740
|
+
if (i >= body.length || body[i] === ">" || body[i] === "/") break;
|
|
741
|
+
const attrStart = i;
|
|
742
|
+
while (i < body.length && /[\w:.-]/.test(body[i] ?? "")) i += 1;
|
|
743
|
+
body.slice(attrStart, i);
|
|
744
|
+
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
745
|
+
if (body[i] !== "=") {
|
|
746
|
+
tagOut += body.slice(attrStart, i);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
tagOut += body.slice(attrStart, i);
|
|
750
|
+
tagOut += "=";
|
|
751
|
+
i += 1;
|
|
752
|
+
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
753
|
+
const quote = body[i];
|
|
754
|
+
if (quote !== '"' && quote !== "'") {
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
if (quote === "'") {
|
|
758
|
+
const valStart2 = i + 1;
|
|
759
|
+
i += 1;
|
|
760
|
+
while (i < body.length) {
|
|
761
|
+
if (body[i] === "\\") {
|
|
762
|
+
i += 2;
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (body[i] === "'") break;
|
|
766
|
+
i += 1;
|
|
767
|
+
}
|
|
768
|
+
tagOut += body.slice(valStart2 - 1, i + 1);
|
|
769
|
+
i += 1;
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
const valStart = i + 1;
|
|
773
|
+
i += 1;
|
|
774
|
+
let closeIdx = -1;
|
|
775
|
+
for (let scan = valStart; scan < body.length; scan += 1) {
|
|
776
|
+
if (body[scan] !== '"') continue;
|
|
777
|
+
let j = scan + 1;
|
|
778
|
+
while (j < body.length && /\s/.test(body[j] ?? "")) j += 1;
|
|
779
|
+
if (j < body.length && (body[j] === ">" || body[j] === "/" || /[a-zA-Z]/.test(body[j] ?? ""))) {
|
|
780
|
+
closeIdx = scan;
|
|
781
|
+
break;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (closeIdx === -1) {
|
|
785
|
+
tagOut += body.slice(valStart - 1, i);
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
const value = body.slice(valStart, closeIdx);
|
|
789
|
+
const hasInternalQuote = value.includes('"');
|
|
790
|
+
if (hasInternalQuote) {
|
|
791
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
792
|
+
tagOut += `'${escaped}'`;
|
|
793
|
+
tagAdjusted = true;
|
|
794
|
+
} else {
|
|
795
|
+
tagOut += `"${value}"`;
|
|
796
|
+
}
|
|
797
|
+
i = closeIdx + 1;
|
|
798
|
+
}
|
|
799
|
+
if (tagAdjusted) adjusted = true;
|
|
800
|
+
tagOut += body[i] ?? "";
|
|
801
|
+
out += tagOut;
|
|
802
|
+
i += 1;
|
|
803
|
+
}
|
|
804
|
+
return { body: out, adjusted };
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/translate/validate-mdx-body.ts
|
|
808
|
+
var parser = unified.unified().use(remarkParse__default.default).use(remarkMdx__default.default);
|
|
809
|
+
function prepareTranslatedMdxBody(body) {
|
|
810
|
+
const normalized = normalizeTranslatedMdxBody(body);
|
|
811
|
+
const sanitized = sanitizeMdxJsxAttributeQuotes(normalized.body);
|
|
812
|
+
return {
|
|
813
|
+
body: sanitized.body,
|
|
814
|
+
adjusted: normalized.adjusted || sanitized.adjusted
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function validateMdxBody(body) {
|
|
818
|
+
try {
|
|
819
|
+
parser.parse(body);
|
|
820
|
+
return { ok: true };
|
|
821
|
+
} catch (error) {
|
|
822
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
823
|
+
return { ok: false, error: message };
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function assertValidTranslatedMdxBody(body) {
|
|
827
|
+
const prepared = prepareTranslatedMdxBody(body);
|
|
828
|
+
const validated = validateMdxBody(prepared.body);
|
|
829
|
+
if (!validated.ok) {
|
|
830
|
+
throw new Error(`MDX validation failed: ${validated.error}`);
|
|
831
|
+
}
|
|
832
|
+
return prepared;
|
|
833
|
+
}
|
|
834
|
+
|
|
701
835
|
// src/loader/normalize-en.ts
|
|
702
836
|
function normalizeEnFrontmatter(data) {
|
|
703
837
|
const out = { ...data };
|
|
@@ -818,7 +952,7 @@ function buildDocumentFromTranslation(row, enDoc, type, config) {
|
|
|
818
952
|
noindex: seo.noindex,
|
|
819
953
|
canonicalPathOverride: seo.canonicalPathOverride,
|
|
820
954
|
frontmatter,
|
|
821
|
-
content: row.body
|
|
955
|
+
content: prepareTranslatedMdxBody(row.body).body
|
|
822
956
|
};
|
|
823
957
|
}
|
|
824
958
|
var DEV_REVALIDATE_MS = 1500;
|
|
@@ -2086,6 +2220,20 @@ function listEnSlugs3(rootDir, contentDir) {
|
|
|
2086
2220
|
if (!fs2__default.default.existsSync(dir)) return [];
|
|
2087
2221
|
return fs2__default.default.readdirSync(dir).filter(isPublishableContentFile).map((f) => f.replace(/\.(md|mdx)$/, ""));
|
|
2088
2222
|
}
|
|
2223
|
+
function validateDocumentMdxBody(issues, input) {
|
|
2224
|
+
const preparedBody = prepareTranslatedMdxBody(input.body).body;
|
|
2225
|
+
const mdxValidation = validateMdxBody(preparedBody);
|
|
2226
|
+
if (!mdxValidation.ok) {
|
|
2227
|
+
issues.push({
|
|
2228
|
+
level: "error",
|
|
2229
|
+
contentType: input.contentType,
|
|
2230
|
+
enSlug: input.enSlug,
|
|
2231
|
+
locale: input.locale,
|
|
2232
|
+
field: "body",
|
|
2233
|
+
message: `Invalid MDX: ${mdxValidation.error}`
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2089
2237
|
function validateProject(config) {
|
|
2090
2238
|
const issues = [];
|
|
2091
2239
|
const project = createProject(config);
|
|
@@ -2144,6 +2292,12 @@ function validateProject(config) {
|
|
|
2144
2292
|
})) {
|
|
2145
2293
|
issues.push(issue);
|
|
2146
2294
|
}
|
|
2295
|
+
validateDocumentMdxBody(issues, {
|
|
2296
|
+
contentType: type.id,
|
|
2297
|
+
enSlug,
|
|
2298
|
+
locale: config.defaultLocale,
|
|
2299
|
+
body: enDoc.content
|
|
2300
|
+
});
|
|
2147
2301
|
for (const locale of config.locales) {
|
|
2148
2302
|
if (locale === config.defaultLocale) continue;
|
|
2149
2303
|
const row = getTranslation(db, type.id, enSlug, locale);
|
|
@@ -2159,15 +2313,22 @@ function validateProject(config) {
|
|
|
2159
2313
|
message: issue.message
|
|
2160
2314
|
});
|
|
2161
2315
|
}
|
|
2316
|
+
const preparedBody = prepareTranslatedMdxBody(row.body).body;
|
|
2162
2317
|
for (const issue of validateDocumentAssets(config, {
|
|
2163
2318
|
contentType: type.id,
|
|
2164
2319
|
enSlug,
|
|
2165
2320
|
locale,
|
|
2166
2321
|
frontmatter: localeFm,
|
|
2167
|
-
body:
|
|
2322
|
+
body: preparedBody
|
|
2168
2323
|
})) {
|
|
2169
2324
|
issues.push(issue);
|
|
2170
2325
|
}
|
|
2326
|
+
validateDocumentMdxBody(issues, {
|
|
2327
|
+
contentType: type.id,
|
|
2328
|
+
enSlug,
|
|
2329
|
+
locale,
|
|
2330
|
+
body: row.body
|
|
2331
|
+
});
|
|
2171
2332
|
}
|
|
2172
2333
|
}
|
|
2173
2334
|
try {
|
|
@@ -2316,12 +2477,20 @@ function normalizeGeminiDisplayName(model) {
|
|
|
2316
2477
|
// src/translate/gemini-client.ts
|
|
2317
2478
|
var DEFAULT_MODEL = DEFAULT_GEMINI_MODEL;
|
|
2318
2479
|
function extractJson(text) {
|
|
2319
|
-
const
|
|
2480
|
+
const trimmed = text.trim();
|
|
2481
|
+
const fenced = trimmed.match(/^```(?:json)?\s*\n?([\s\S]*?)\n?```$/);
|
|
2320
2482
|
if (fenced?.[1]) return fenced[1].trim();
|
|
2321
|
-
const start =
|
|
2322
|
-
const end =
|
|
2323
|
-
if (start >= 0 && end > start) return
|
|
2324
|
-
return
|
|
2483
|
+
const start = trimmed.indexOf("{");
|
|
2484
|
+
const end = trimmed.lastIndexOf("}");
|
|
2485
|
+
if (start >= 0 && end > start) return trimmed.slice(start, end + 1);
|
|
2486
|
+
return trimmed;
|
|
2487
|
+
}
|
|
2488
|
+
function parseGeminiResponse(text) {
|
|
2489
|
+
try {
|
|
2490
|
+
return JSON.parse(text.trim());
|
|
2491
|
+
} catch {
|
|
2492
|
+
return JSON.parse(extractJson(text));
|
|
2493
|
+
}
|
|
2325
2494
|
}
|
|
2326
2495
|
async function translatePageWithGemini(input) {
|
|
2327
2496
|
const apiKey = input.apiKey ?? process.env.GEMINI_API_KEY;
|
|
@@ -2342,7 +2511,7 @@ async function translatePageWithGemini(input) {
|
|
|
2342
2511
|
}
|
|
2343
2512
|
});
|
|
2344
2513
|
const raw = response.text ?? "";
|
|
2345
|
-
const parsed =
|
|
2514
|
+
const parsed = parseGeminiResponse(raw);
|
|
2346
2515
|
if (!parsed.frontmatter || typeof parsed.body !== "string") {
|
|
2347
2516
|
throw new Error("Gemini response missing frontmatter/body");
|
|
2348
2517
|
}
|
|
@@ -2499,24 +2668,21 @@ function buildGeminiResponseSchema(schema, slugStrategy) {
|
|
|
2499
2668
|
}
|
|
2500
2669
|
|
|
2501
2670
|
// src/translate/resolve-translate-config.ts
|
|
2502
|
-
|
|
2671
|
+
var BUILTIN_TRANSLATE_RULES = [
|
|
2672
|
+
"Do not translate brand or product names unless the brand has a well-known localized name in the target market (rare). Keep the original spelling and capitalization.",
|
|
2673
|
+
"Return the MDX body with real line breaks; do not use JSON escape sequences like \\n or \\t in the body string.",
|
|
2674
|
+
'In JSX attributes (e.g. FaqItem question="..."), use single quotes when the value contains double-quote characters (e.g. Hebrew \u05D3\u05D5\u05D0"\u05DC), or escape them as \\".'
|
|
2675
|
+
];
|
|
2676
|
+
function slugStrategyRules(slugStrategy) {
|
|
2503
2677
|
if (slugStrategy === "localized") {
|
|
2504
|
-
|
|
2678
|
+
return [
|
|
2505
2679
|
"Provide a URL slug in JSON field `slug` that is Localized into the target language.",
|
|
2506
2680
|
"Base the slug on the meaning of the translated title \u2014 do NOT reuse the English slug words.",
|
|
2507
2681
|
"Slug MUST be ASCII only: a-z, 0-9, hyphens. No uppercase, accents, underscores, or spaces.",
|
|
2508
2682
|
"For non-Latin languages, write the words in the target language and transliterate them into Latin script (e.g. Russian -> romanized Russian, not English).",
|
|
2509
2683
|
"Do NOT append locale codes to the slug (e.g. -fr, -he, -zh-cn). Locale routing is handled by the URL prefix, not the slug.",
|
|
2510
|
-
|
|
2684
|
+
"Preserve 4-digit years and proper nouns in slugs."
|
|
2511
2685
|
];
|
|
2512
|
-
if (preserveTerms?.length) {
|
|
2513
|
-
rules.push(
|
|
2514
|
-
`Preserve these terms verbatim in slugs (lowercase): ${preserveTerms.join(", ")}.`
|
|
2515
|
-
);
|
|
2516
|
-
} else {
|
|
2517
|
-
rules.push("Preserve 4-digit years and proper nouns in slugs.");
|
|
2518
|
-
}
|
|
2519
|
-
return rules;
|
|
2520
2686
|
}
|
|
2521
2687
|
return ["Do NOT output a slug \u2014 slugStrategy is fixed."];
|
|
2522
2688
|
}
|
|
@@ -2529,9 +2695,10 @@ function mergeTranslateConfig(project, type, slugStrategy) {
|
|
|
2529
2695
|
promptOverride: type?.prompt ?? project?.prompt,
|
|
2530
2696
|
context: mergeContext(project, type),
|
|
2531
2697
|
rules: [
|
|
2698
|
+
...BUILTIN_TRANSLATE_RULES,
|
|
2532
2699
|
...project?.rules ?? [],
|
|
2533
2700
|
...type?.rules ?? [],
|
|
2534
|
-
...slugStrategyRules(slugStrategy
|
|
2701
|
+
...slugStrategyRules(slugStrategy)
|
|
2535
2702
|
],
|
|
2536
2703
|
model: type?.model ?? project?.defaultModel
|
|
2537
2704
|
};
|
|
@@ -2540,95 +2707,6 @@ function resolveTranslateConfig(config, type) {
|
|
|
2540
2707
|
return mergeTranslateConfig(config.translate, type.translate, type.slugStrategy);
|
|
2541
2708
|
}
|
|
2542
2709
|
|
|
2543
|
-
// src/translate/sanitize-mdx-jsx.ts
|
|
2544
|
-
function sanitizeMdxJsxAttributeQuotes(body) {
|
|
2545
|
-
let adjusted = false;
|
|
2546
|
-
let out = "";
|
|
2547
|
-
let i = 0;
|
|
2548
|
-
while (i < body.length) {
|
|
2549
|
-
if (body[i] !== "<" || !/[\w.]/.test(body[i + 1] ?? "")) {
|
|
2550
|
-
out += body[i];
|
|
2551
|
-
i += 1;
|
|
2552
|
-
continue;
|
|
2553
|
-
}
|
|
2554
|
-
const tagStart = i;
|
|
2555
|
-
i += 1;
|
|
2556
|
-
while (i < body.length && /[\w.-]/.test(body[i] ?? "")) i += 1;
|
|
2557
|
-
const tagName = body.slice(tagStart + 1, i);
|
|
2558
|
-
let tagOut = `<${tagName}`;
|
|
2559
|
-
let tagAdjusted = false;
|
|
2560
|
-
while (i < body.length && body[i] !== ">" && body[i] !== "/") {
|
|
2561
|
-
while (i < body.length && /\s/.test(body[i] ?? "")) {
|
|
2562
|
-
tagOut += body[i];
|
|
2563
|
-
i += 1;
|
|
2564
|
-
}
|
|
2565
|
-
if (i >= body.length || body[i] === ">" || body[i] === "/") break;
|
|
2566
|
-
const attrStart = i;
|
|
2567
|
-
while (i < body.length && /[\w:.-]/.test(body[i] ?? "")) i += 1;
|
|
2568
|
-
body.slice(attrStart, i);
|
|
2569
|
-
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
2570
|
-
if (body[i] !== "=") {
|
|
2571
|
-
tagOut += body.slice(attrStart, i);
|
|
2572
|
-
continue;
|
|
2573
|
-
}
|
|
2574
|
-
tagOut += body.slice(attrStart, i);
|
|
2575
|
-
tagOut += "=";
|
|
2576
|
-
i += 1;
|
|
2577
|
-
while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
|
|
2578
|
-
const quote = body[i];
|
|
2579
|
-
if (quote !== '"' && quote !== "'") {
|
|
2580
|
-
continue;
|
|
2581
|
-
}
|
|
2582
|
-
if (quote === "'") {
|
|
2583
|
-
const valStart2 = i + 1;
|
|
2584
|
-
i += 1;
|
|
2585
|
-
while (i < body.length) {
|
|
2586
|
-
if (body[i] === "\\") {
|
|
2587
|
-
i += 2;
|
|
2588
|
-
continue;
|
|
2589
|
-
}
|
|
2590
|
-
if (body[i] === "'") break;
|
|
2591
|
-
i += 1;
|
|
2592
|
-
}
|
|
2593
|
-
tagOut += body.slice(valStart2 - 1, i + 1);
|
|
2594
|
-
i += 1;
|
|
2595
|
-
continue;
|
|
2596
|
-
}
|
|
2597
|
-
const valStart = i + 1;
|
|
2598
|
-
i += 1;
|
|
2599
|
-
let closeIdx = -1;
|
|
2600
|
-
for (let scan = valStart; scan < body.length; scan += 1) {
|
|
2601
|
-
if (body[scan] !== '"') continue;
|
|
2602
|
-
let j = scan + 1;
|
|
2603
|
-
while (j < body.length && /\s/.test(body[j] ?? "")) j += 1;
|
|
2604
|
-
if (j < body.length && (body[j] === ">" || body[j] === "/" || /[a-zA-Z]/.test(body[j] ?? ""))) {
|
|
2605
|
-
closeIdx = scan;
|
|
2606
|
-
break;
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
if (closeIdx === -1) {
|
|
2610
|
-
tagOut += body.slice(valStart - 1, i);
|
|
2611
|
-
break;
|
|
2612
|
-
}
|
|
2613
|
-
const value = body.slice(valStart, closeIdx);
|
|
2614
|
-
const hasInternalQuote = value.includes('"');
|
|
2615
|
-
if (hasInternalQuote) {
|
|
2616
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
2617
|
-
tagOut += `'${escaped}'`;
|
|
2618
|
-
tagAdjusted = true;
|
|
2619
|
-
} else {
|
|
2620
|
-
tagOut += `"${value}"`;
|
|
2621
|
-
}
|
|
2622
|
-
i = closeIdx + 1;
|
|
2623
|
-
}
|
|
2624
|
-
if (tagAdjusted) adjusted = true;
|
|
2625
|
-
tagOut += body[i] ?? "";
|
|
2626
|
-
out += tagOut;
|
|
2627
|
-
i += 1;
|
|
2628
|
-
}
|
|
2629
|
-
return { body: out, adjusted };
|
|
2630
|
-
}
|
|
2631
|
-
|
|
2632
2710
|
// src/translate/validate-translation.ts
|
|
2633
2711
|
function formatZodIssues(error) {
|
|
2634
2712
|
return error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
|
|
@@ -2762,7 +2840,7 @@ async function translatePage(config, item, options = {}) {
|
|
|
2762
2840
|
if (!validated.ok) {
|
|
2763
2841
|
throw new Error(`Translation validation failed: ${validated.error}`);
|
|
2764
2842
|
}
|
|
2765
|
-
const { body: translatedBody, adjusted: mdxAdjusted } =
|
|
2843
|
+
const { body: translatedBody, adjusted: mdxAdjusted } = assertValidTranslatedMdxBody(
|
|
2766
2844
|
result.parsed.body
|
|
2767
2845
|
);
|
|
2768
2846
|
const writeDb = openStore(config, "readwrite");
|