localization.ts 6.3 KB

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