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.js CHANGED
@@ -11,11 +11,11 @@ import { GoogleGenAI } from '@google/genai';
11
11
  import dotenv from 'dotenv';
12
12
  import { serve } from '@hono/node-server';
13
13
  import { Hono } from 'hono';
14
+ import { CancelPromptError } from '@inquirer/core';
14
15
  import { select, checkbox } from '@inquirer/prompts';
15
16
 
16
17
  var __defProp = Object.defineProperty;
17
18
  var __getOwnPropNames = Object.getOwnPropertyNames;
18
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
19
19
  var __esm = (fn, res) => function __init() {
20
20
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
21
21
  };
@@ -23,7 +23,6 @@ var __export = (target, all) => {
23
23
  for (var name in all)
24
24
  __defProp(target, name, { get: all[name], enumerable: true });
25
25
  };
26
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
27
26
  var init_esm_shims = __esm({
28
27
  "../../node_modules/tsup/assets/esm_shims.js"() {
29
28
  }
@@ -1627,22 +1626,45 @@ function validateDocumentAssets(config, input) {
1627
1626
 
1628
1627
  // src/validate/validate-slug-suffix.ts
1629
1628
  init_esm_shims();
1629
+
1630
+ // src/core/localized-slug.ts
1631
+ init_esm_shims();
1632
+ function findLocaleSuffixInSlug(slug, localeCodes) {
1633
+ const sorted = [...localeCodes].sort((a, b) => b.length - a.length);
1634
+ for (const code of sorted) {
1635
+ if (slug.endsWith(`-${code}`)) {
1636
+ return code;
1637
+ }
1638
+ }
1639
+ return void 0;
1640
+ }
1641
+ function stripLocaleSuffixFromSlug(slug, localeCodes) {
1642
+ const matchedCode = findLocaleSuffixInSlug(slug, localeCodes);
1643
+ if (!matchedCode) {
1644
+ return { slug, stripped: false };
1645
+ }
1646
+ return {
1647
+ slug: slug.slice(0, -(matchedCode.length + 1)),
1648
+ stripped: true,
1649
+ matchedCode
1650
+ };
1651
+ }
1652
+
1653
+ // src/validate/validate-slug-suffix.ts
1630
1654
  function validateTranslationSlugSuffixes(config, db) {
1631
1655
  const issues = [];
1632
1656
  const localeCodes = config.locales.filter((l) => l !== config.defaultLocale);
1633
1657
  for (const row of bulkLoadTranslations(db)) {
1634
1658
  if (row.locale === config.defaultLocale) continue;
1635
- for (const code of localeCodes) {
1636
- if (row.slug.endsWith(`-${code}`)) {
1637
- issues.push({
1638
- contentTypeId: row.content_type,
1639
- enSlug: row.en_slug,
1640
- locale: row.locale,
1641
- slug: row.slug,
1642
- message: `Translation slug "${row.slug}" ends with locale code "-${code}"`
1643
- });
1644
- break;
1645
- }
1659
+ const matchedCode = findLocaleSuffixInSlug(row.slug, localeCodes);
1660
+ if (matchedCode) {
1661
+ issues.push({
1662
+ contentTypeId: row.content_type,
1663
+ enSlug: row.en_slug,
1664
+ locale: row.locale,
1665
+ slug: row.slug,
1666
+ message: `Translation slug "${row.slug}" ends with locale code "-${matchedCode}"`
1667
+ });
1646
1668
  }
1647
1669
  }
1648
1670
  return issues;
@@ -1790,7 +1812,7 @@ function validateProject(config) {
1790
1812
  try {
1791
1813
  for (const issue of validateTranslationSlugSuffixes(config, dbForSuffix)) {
1792
1814
  issues.push({
1793
- level: "error",
1815
+ level: "warning",
1794
1816
  contentType: issue.contentTypeId,
1795
1817
  enSlug: issue.enSlug,
1796
1818
  locale: issue.locale,
@@ -2041,6 +2063,9 @@ function buildPageTranslationPrompt(input) {
2041
2063
  ...input.contextLabel ? [`Document: ${input.contextLabel}`, ""] : [],
2042
2064
  "## Rules",
2043
2065
  ...input.resolved.rules.map((rule) => `- ${rule}`),
2066
+ ...input.slugStrategy === "localized" ? [
2067
+ `- 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.`
2068
+ ] : [],
2044
2069
  "",
2045
2070
  "## Output format",
2046
2071
  "Return ONLY valid JSON with keys:",
@@ -2128,9 +2153,11 @@ init_esm_shims();
2128
2153
  function slugStrategyRules(slugStrategy, preserveTerms) {
2129
2154
  if (slugStrategy === "localized") {
2130
2155
  const rules = [
2131
- "Provide a localized URL slug in JSON field `slug`.",
2156
+ "Provide a URL slug in JSON field `slug` that is TRANSLATED into the target language.",
2157
+ "Base the slug on the meaning of the translated title \u2014 do NOT reuse the English slug words.",
2132
2158
  "Slug MUST be ASCII only: a-z, 0-9, hyphens. No uppercase, accents, underscores, or spaces.",
2133
- "For non-Latin locales, transliterate into Latin script."
2159
+ "For non-Latin languages, write the words in the target language and transliterate them into Latin script (e.g. Russian -> romanized Russian, not English).",
2160
+ "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."
2134
2161
  ];
2135
2162
  if (preserveTerms?.length) {
2136
2163
  rules.push(
@@ -2289,7 +2316,10 @@ async function translatePage(config, item, options = {}) {
2289
2316
  model,
2290
2317
  responseSchema: responseSchema ?? void 0
2291
2318
  });
2292
- const slug = type.slugStrategy === "localized" ? result.parsed.slug ?? existing?.slug ?? item.enSlug : item.enSlug;
2319
+ const rawSlug = type.slugStrategy === "localized" ? result.parsed.slug ?? existing?.slug ?? item.enSlug : item.enSlug;
2320
+ const localeCodes = config.locales.filter((l) => l !== config.defaultLocale);
2321
+ const { slug, stripped, matchedCode } = stripLocaleSuffixFromSlug(rawSlug, localeCodes);
2322
+ const slugAdjusted = stripped && matchedCode ? { from: rawSlug, to: slug, matchedCode } : void 0;
2293
2323
  const validated = validateTranslatedFrontmatter(enDoc, result.parsed.frontmatter, type.schema);
2294
2324
  if (!validated.ok) {
2295
2325
  throw new Error(`Translation validation failed: ${validated.error}`);
@@ -2330,7 +2360,8 @@ async function translatePage(config, item, options = {}) {
2330
2360
  model: result.model,
2331
2361
  usage: result.usage,
2332
2362
  estimatedCostUsd,
2333
- durationMs: Date.now() - startedAt
2363
+ durationMs: Date.now() - startedAt,
2364
+ slugAdjusted
2334
2365
  };
2335
2366
  } catch (error) {
2336
2367
  return {
@@ -2956,16 +2987,6 @@ async function startStudio(project, options = {}) {
2956
2987
 
2957
2988
  // cli/prompt-translate.ts
2958
2989
  init_esm_shims();
2959
-
2960
- // ../../node_modules/@inquirer/core/dist/lib/errors.js
2961
- init_esm_shims();
2962
- var CancelPromptError = class extends Error {
2963
- constructor() {
2964
- super(...arguments);
2965
- __publicField(this, "name", "CancelPromptError");
2966
- __publicField(this, "message", "Prompt was canceled");
2967
- }
2968
- };
2969
2990
  function isInteractive() {
2970
2991
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
2971
2992
  }
@@ -3077,8 +3098,17 @@ function statusForResult(result, dryRun) {
3077
3098
  if (dryRun) return yellow("would translate");
3078
3099
  return green("translated");
3079
3100
  }
3101
+ function slugAdjustedMessage(result) {
3102
+ if (!result.slugAdjusted) return void 0;
3103
+ const { from, to, matchedCode } = result.slugAdjusted;
3104
+ return yellow(
3105
+ `slug adjusted: "${from}" \u2192 "${to}" (stripped -${matchedCode})`
3106
+ );
3107
+ }
3080
3108
  function detailForResult(result) {
3081
3109
  const parts = [];
3110
+ const slugMsg = slugAdjustedMessage(result);
3111
+ if (slugMsg) parts.push(slugMsg);
3082
3112
  if (result.durationMs !== void 0) parts.push(`${(result.durationMs / 1e3).toFixed(1)}s`);
3083
3113
  if (result.usage) {
3084
3114
  parts.push(`${formatTokenCount(result.usage.inputTokens)} in`);
@@ -3112,6 +3142,10 @@ function createTranslateProgressReporter(options = {}) {
3112
3142
  const status = statusForResult(event.result, dryRun);
3113
3143
  const detail = detailForResult(event.result);
3114
3144
  console.log(`${label}: ${status}${detail ? ` (${detail.replace(/\x1b\[[0-9;]*m/g, "")})` : ""}`);
3145
+ const slugMsg = slugAdjustedMessage(event.result);
3146
+ if (slugMsg) {
3147
+ console.log(`[warning] ${labelForResult(event.result)} ${slugMsg.replace(/\x1b\[[0-9;]*m/g, "")}`);
3148
+ }
3115
3149
  if (event.result.failed && event.result.error) {
3116
3150
  console.error(event.result.error);
3117
3151
  }
@@ -3215,6 +3249,14 @@ function createTranslateProgressReporter(options = {}) {
3215
3249
  for (const result of event.results.filter((entry) => entry.failed)) {
3216
3250
  process.stdout.write("\x1B[2K" + red(`${labelForResult(result)}: ${result.error ?? "failed"}`) + "\n");
3217
3251
  }
3252
+ for (const result of event.results.filter((entry) => entry.slugAdjusted)) {
3253
+ const slugMsg = slugAdjustedMessage(result);
3254
+ if (slugMsg) {
3255
+ process.stdout.write(
3256
+ "\x1B[2K" + yellow(`[warning] ${labelForResult(result)} ${slugMsg.replace(/\x1b\[[0-9;]*m/g, "")}`) + "\n"
3257
+ );
3258
+ }
3259
+ }
3218
3260
  break;
3219
3261
  }
3220
3262
  },