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.
@@ -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;AAuD7C,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,CAuIjC"}
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
- for (const code of localeCodes) {
1890
- if (row.slug.endsWith(`-${code}`)) {
1891
- issues.push({
1892
- contentTypeId: row.content_type,
1893
- enSlug: row.en_slug,
1894
- locale: row.locale,
1895
- slug: row.slug,
1896
- message: `Translation slug "${row.slug}" ends with locale code "-${code}"`
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: "error",
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 localized URL slug in JSON field `slug`.",
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 locales, transliterate into Latin script."
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 slug = type.slugStrategy === "localized" ? result.parsed.slug ?? existing?.slug ?? item.enSlug : item.enSlug;
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 {