localization.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import { formatLengthCount, resolveLengthCountingMode } from "@actalk/inkos-core";
  2. export type CliLanguage = "zh" | "en";
  3. type WriteIssue = {
  4. readonly severity: string;
  5. readonly category: string;
  6. readonly description: string;
  7. };
  8. type WriteResultShape = {
  9. readonly chapterNumber: number;
  10. readonly title: string;
  11. readonly wordCount: number;
  12. readonly status: string;
  13. readonly revised: boolean;
  14. readonly issues: ReadonlyArray<WriteIssue>;
  15. readonly auditPassed?: boolean;
  16. readonly passedAudit?: boolean;
  17. };
  18. type ImportResultShape = {
  19. readonly importedCount: number;
  20. readonly totalWords: number;
  21. readonly nextChapter: number;
  22. readonly continueBookId: string;
  23. };
  24. function localize(language: CliLanguage, messages: { zh: string; en: string }): string {
  25. return language === "en" ? messages.en : messages.zh;
  26. }
  27. export function resolveCliLanguage(language?: string): CliLanguage {
  28. return language === "en" ? "en" : "zh";
  29. }
  30. export function formatBookCreateCreating(
  31. language: CliLanguage,
  32. title: string,
  33. genre: string,
  34. platform: string,
  35. ): string {
  36. return localize(language, {
  37. zh: `创建书籍 "${title}"(${genre} / ${platform})...`,
  38. en: `Creating book "${title}" (${genre} / ${platform})...`,
  39. });
  40. }
  41. export function formatBookCreateCreated(language: CliLanguage, bookId: string): string {
  42. return localize(language, {
  43. zh: `已创建书籍:${bookId}`,
  44. en: `Book created: ${bookId}`,
  45. });
  46. }
  47. export function formatBookCreateLocation(language: CliLanguage, bookId: string): string {
  48. return localize(language, {
  49. zh: ` 位置:books/${bookId}/`,
  50. en: ` Location: books/${bookId}/`,
  51. });
  52. }
  53. export function formatBookCreateFoundationReady(language: CliLanguage): string {
  54. return localize(language, {
  55. zh: " 故事圣经、大纲和书籍规则已生成。",
  56. en: " Story bible, outline, book rules generated.",
  57. });
  58. }
  59. export function formatBookCreateNextStep(language: CliLanguage, bookId: string): string {
  60. return localize(language, {
  61. zh: `下一步:inkos write next ${bookId}`,
  62. en: `Next: inkos write next ${bookId}`,
  63. });
  64. }
  65. export function formatWriteNextProgress(
  66. language: CliLanguage,
  67. current: number,
  68. total: number,
  69. bookId: string,
  70. ): string {
  71. return localize(language, {
  72. zh: `[${current}/${total}] 为「${bookId}」撰写章节...`,
  73. en: `[${current}/${total}] Writing chapter for "${bookId}"...`,
  74. });
  75. }
  76. export function formatWriteNextResultLines(
  77. language: CliLanguage,
  78. result: WriteResultShape,
  79. ): string[] {
  80. const auditPassed = result.auditPassed ?? result.passedAudit ?? false;
  81. const lengthLabel = formatLengthCount(result.wordCount, resolveLengthCountingMode(language));
  82. const lines = [
  83. localize(language, {
  84. zh: ` 第${result.chapterNumber}章:${result.title}`,
  85. en: ` Chapter ${result.chapterNumber}: ${result.title}`,
  86. }),
  87. localize(language, {
  88. zh: ` 字数:${lengthLabel}`,
  89. en: ` Length: ${lengthLabel}`,
  90. }),
  91. localize(language, {
  92. zh: ` 审计:${auditPassed ? "通过" : "需复核"}`,
  93. en: ` Audit: ${auditPassed ? "PASSED" : "NEEDS REVIEW"}`,
  94. }),
  95. ];
  96. if (result.revised) {
  97. lines.push(localize(language, {
  98. zh: " 自动修正:已执行(已修复关键问题)",
  99. en: " Auto-revised: YES (critical issues were fixed)",
  100. }));
  101. }
  102. lines.push(localize(language, {
  103. zh: ` 状态:${result.status}`,
  104. en: ` Status: ${result.status}`,
  105. }));
  106. if (result.issues.length > 0) {
  107. lines.push(localize(language, {
  108. zh: " 问题:",
  109. en: " Issues:",
  110. }));
  111. for (const issue of result.issues) {
  112. lines.push(` [${issue.severity}] ${issue.category}: ${issue.description}`);
  113. }
  114. }
  115. return lines;
  116. }
  117. export function formatWriteNextComplete(language: CliLanguage): string {
  118. return localize(language, {
  119. zh: "完成。",
  120. en: "Done.",
  121. });
  122. }
  123. export function formatImportChaptersDiscovery(
  124. language: CliLanguage,
  125. chapterCount: number,
  126. bookId: string,
  127. ): string {
  128. return localize(language, {
  129. zh: `发现 ${chapterCount} 章,准备导入到「${bookId}」。`,
  130. en: `Found ${chapterCount} chapters to import into "${bookId}".`,
  131. });
  132. }
  133. export function formatImportChaptersResume(
  134. language: CliLanguage,
  135. resumeFrom: number,
  136. ): string {
  137. return localize(language, {
  138. zh: `从第 ${resumeFrom} 章继续导入。`,
  139. en: `Resuming from chapter ${resumeFrom}.`,
  140. });
  141. }
  142. export function formatImportChaptersComplete(
  143. language: CliLanguage,
  144. result: ImportResultShape,
  145. ): string[] {
  146. const lengthLabel = formatLengthCount(result.totalWords, resolveLengthCountingMode(language));
  147. return [
  148. localize(language, {
  149. zh: "导入完成:",
  150. en: "Import complete:",
  151. }),
  152. localize(language, {
  153. zh: ` 已导入章节:${result.importedCount}`,
  154. en: ` Chapters imported: ${result.importedCount}`,
  155. }),
  156. localize(language, {
  157. zh: ` 总长度:${lengthLabel}`,
  158. en: ` Total length: ${lengthLabel}`,
  159. }),
  160. localize(language, {
  161. zh: ` 下一章编号:${result.nextChapter}`,
  162. en: ` Next chapter number: ${result.nextChapter}`,
  163. }),
  164. "",
  165. localize(language, {
  166. zh: `运行 "inkos write next ${result.continueBookId}" 继续写作。`,
  167. en: `Run "inkos write next ${result.continueBookId}" to continue writing.`,
  168. }),
  169. ];
  170. }
  171. export function formatImportCanonStart(
  172. language: CliLanguage,
  173. parentBookId: string,
  174. targetBookId: string,
  175. ): string {
  176. return localize(language, {
  177. zh: `把 "${parentBookId}" 的正典导入到 "${targetBookId}"...`,
  178. en: `Importing canon from "${parentBookId}" into "${targetBookId}"...`,
  179. });
  180. }
  181. export function formatImportCanonComplete(language: CliLanguage): string[] {
  182. return [
  183. localize(language, {
  184. zh: "正典已导入:story/parent_canon.md",
  185. en: "Canon imported: story/parent_canon.md",
  186. }),
  187. localize(language, {
  188. zh: "Writer 和 auditor 会在番外模式下自动识别这个文件。",
  189. en: "Writer and auditor will auto-detect this file for spinoff mode.",
  190. }),
  191. ];
  192. }