scribe-cms 0.0.6 → 0.0.7
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 +71 -29
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +70 -28
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/translate-progress.d.ts.map +1 -1
- package/dist/index.cjs +45 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +45 -16
- package/dist/index.js.map +1 -1
- package/dist/src/core/localized-slug.d.ts +8 -0
- package/dist/src/core/localized-slug.d.ts.map +1 -0
- package/dist/src/translate/page-translator.d.ts +5 -0
- package/dist/src/translate/page-translator.d.ts.map +1 -1
- package/dist/src/translate/prompts/translation-prompt.d.ts.map +1 -1
- package/dist/src/translate/resolve-translate-config.d.ts.map +1 -1
- package/dist/src/validate/validate-slug-suffix.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate-progress.d.ts","sourceRoot":"","sources":["../../cli/translate-progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"translate-progress.d.ts","sourceRoot":"","sources":["../../cli/translate-progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,qCAAqC,CAAC;AAiE7C,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACjD,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,+BAA+B,CAAC,OAAO,GAAE;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,yBAAyB,CAmJjC"}
|
package/dist/index.cjs
CHANGED
|
@@ -1880,23 +1880,43 @@ function validateDocumentAssets(config, input) {
|
|
|
1880
1880
|
return issues;
|
|
1881
1881
|
}
|
|
1882
1882
|
|
|
1883
|
+
// src/core/localized-slug.ts
|
|
1884
|
+
function findLocaleSuffixInSlug(slug, localeCodes) {
|
|
1885
|
+
const sorted = [...localeCodes].sort((a, b) => b.length - a.length);
|
|
1886
|
+
for (const code of sorted) {
|
|
1887
|
+
if (slug.endsWith(`-${code}`)) {
|
|
1888
|
+
return code;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return void 0;
|
|
1892
|
+
}
|
|
1893
|
+
function stripLocaleSuffixFromSlug(slug, localeCodes) {
|
|
1894
|
+
const matchedCode = findLocaleSuffixInSlug(slug, localeCodes);
|
|
1895
|
+
if (!matchedCode) {
|
|
1896
|
+
return { slug, stripped: false };
|
|
1897
|
+
}
|
|
1898
|
+
return {
|
|
1899
|
+
slug: slug.slice(0, -(matchedCode.length + 1)),
|
|
1900
|
+
stripped: true,
|
|
1901
|
+
matchedCode
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1883
1905
|
// src/validate/validate-slug-suffix.ts
|
|
1884
1906
|
function validateTranslationSlugSuffixes(config, db) {
|
|
1885
1907
|
const issues = [];
|
|
1886
1908
|
const localeCodes = config.locales.filter((l) => l !== config.defaultLocale);
|
|
1887
1909
|
for (const row of bulkLoadTranslations(db)) {
|
|
1888
1910
|
if (row.locale === config.defaultLocale) continue;
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
break;
|
|
1899
|
-
}
|
|
1911
|
+
const matchedCode = findLocaleSuffixInSlug(row.slug, localeCodes);
|
|
1912
|
+
if (matchedCode) {
|
|
1913
|
+
issues.push({
|
|
1914
|
+
contentTypeId: row.content_type,
|
|
1915
|
+
enSlug: row.en_slug,
|
|
1916
|
+
locale: row.locale,
|
|
1917
|
+
slug: row.slug,
|
|
1918
|
+
message: `Translation slug "${row.slug}" ends with locale code "-${matchedCode}"`
|
|
1919
|
+
});
|
|
1900
1920
|
}
|
|
1901
1921
|
}
|
|
1902
1922
|
return issues;
|
|
@@ -2044,7 +2064,7 @@ function validateProject(config) {
|
|
|
2044
2064
|
try {
|
|
2045
2065
|
for (const issue of validateTranslationSlugSuffixes(config, dbForSuffix)) {
|
|
2046
2066
|
issues.push({
|
|
2047
|
-
level: "
|
|
2067
|
+
level: "warning",
|
|
2048
2068
|
contentType: issue.contentTypeId,
|
|
2049
2069
|
enSlug: issue.enSlug,
|
|
2050
2070
|
locale: issue.locale,
|
|
@@ -2263,6 +2283,9 @@ function buildPageTranslationPrompt(input) {
|
|
|
2263
2283
|
...input.contextLabel ? [`Document: ${input.contextLabel}`, ""] : [],
|
|
2264
2284
|
"## Rules",
|
|
2265
2285
|
...input.resolved.rules.map((rule) => `- ${rule}`),
|
|
2286
|
+
...input.slugStrategy === "localized" ? [
|
|
2287
|
+
`- The slug MUST be written in ${localeName}, derived from the ${localeName} title and its meaning \u2014 never the English slug. Transliterate non-Latin ${localeName} into ASCII Latin.`
|
|
2288
|
+
] : [],
|
|
2266
2289
|
"",
|
|
2267
2290
|
"## Output format",
|
|
2268
2291
|
"Return ONLY valid JSON with keys:",
|
|
@@ -2346,9 +2369,11 @@ function buildGeminiResponseSchema(schema, slugStrategy) {
|
|
|
2346
2369
|
function slugStrategyRules(slugStrategy, preserveTerms) {
|
|
2347
2370
|
if (slugStrategy === "localized") {
|
|
2348
2371
|
const rules = [
|
|
2349
|
-
"Provide a
|
|
2372
|
+
"Provide a URL slug in JSON field `slug` that is TRANSLATED into the target language.",
|
|
2373
|
+
"Base the slug on the meaning of the translated title \u2014 do NOT reuse the English slug words.",
|
|
2350
2374
|
"Slug MUST be ASCII only: a-z, 0-9, hyphens. No uppercase, accents, underscores, or spaces.",
|
|
2351
|
-
"For non-Latin
|
|
2375
|
+
"For non-Latin languages, write the words in the target language and transliterate them into Latin script (e.g. Russian -> romanized Russian, not English).",
|
|
2376
|
+
"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."
|
|
2352
2377
|
];
|
|
2353
2378
|
if (preserveTerms?.length) {
|
|
2354
2379
|
rules.push(
|
|
@@ -2506,7 +2531,10 @@ async function translatePage(config, item, options = {}) {
|
|
|
2506
2531
|
model,
|
|
2507
2532
|
responseSchema: responseSchema ?? void 0
|
|
2508
2533
|
});
|
|
2509
|
-
const
|
|
2534
|
+
const rawSlug = type.slugStrategy === "localized" ? result.parsed.slug ?? existing?.slug ?? item.enSlug : item.enSlug;
|
|
2535
|
+
const localeCodes = config.locales.filter((l) => l !== config.defaultLocale);
|
|
2536
|
+
const { slug, stripped, matchedCode } = stripLocaleSuffixFromSlug(rawSlug, localeCodes);
|
|
2537
|
+
const slugAdjusted = stripped && matchedCode ? { from: rawSlug, to: slug, matchedCode } : void 0;
|
|
2510
2538
|
const validated = validateTranslatedFrontmatter(enDoc, result.parsed.frontmatter, type.schema);
|
|
2511
2539
|
if (!validated.ok) {
|
|
2512
2540
|
throw new Error(`Translation validation failed: ${validated.error}`);
|
|
@@ -2547,7 +2575,8 @@ async function translatePage(config, item, options = {}) {
|
|
|
2547
2575
|
model: result.model,
|
|
2548
2576
|
usage: result.usage,
|
|
2549
2577
|
estimatedCostUsd,
|
|
2550
|
-
durationMs: Date.now() - startedAt
|
|
2578
|
+
durationMs: Date.now() - startedAt,
|
|
2579
|
+
slugAdjusted
|
|
2551
2580
|
};
|
|
2552
2581
|
} catch (error) {
|
|
2553
2582
|
return {
|