akari-video 1.3.0-beta.4 → 1.3.0-beta.5

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.
Files changed (312) hide show
  1. package/bin/akari.mjs +3 -2
  2. package/package.json +4 -1
  3. package/src/assets-command.mjs +2 -1
  4. package/src/captions-command.mjs +2 -1
  5. package/src/capture-command.mjs +2 -6
  6. package/src/clean-command.mjs +38 -28
  7. package/src/cli.mjs +28 -21
  8. package/src/doctor-command.mjs +26 -25
  9. package/src/generate-command.mjs +2 -1
  10. package/src/history-policy.mjs +2 -0
  11. package/src/i18n.mjs +47 -0
  12. package/src/internal-command.mjs +8 -7
  13. package/src/kits.mjs +21 -20
  14. package/src/media-command.mjs +2 -1
  15. package/src/messages.mjs +87 -102
  16. package/src/migrate-command.mjs +22 -24
  17. package/src/narration-command.mjs +164 -149
  18. package/src/new-command.mjs +35 -18
  19. package/src/repo-assets.mjs +10 -0
  20. package/src/runtime-diagnostics.mjs +8 -7
  21. package/src/self-update.mjs +20 -19
  22. package/src/skills-command.mjs +12 -14
  23. package/src/speakers-command.mjs +17 -0
  24. package/src/store-command.mjs +59 -62
  25. package/src/store-device-connect.mjs +13 -12
  26. package/src/storyboard-command.mjs +4 -3
  27. package/src/task-labels.mjs +15 -8
  28. package/src/tools-command.mjs +17 -0
  29. package/src/tts-engines.mjs +32 -24
  30. package/src/update-check.mjs +3 -2
  31. package/src/vendor-sources.mjs +18 -1
  32. package/src/voice-command.mjs +83 -82
  33. package/src/world-command.mjs +2 -1
  34. package/vendor/.akari-capability-sources.json +18 -0
  35. package/vendor/docs/contract-2026-08-28-gpu-export-v0.md +3 -0
  36. package/vendor/docs/contract-2026-10-10-channel-people-design-v0.md +41 -0
  37. package/vendor/docs/contract-2026-10-11-content-language.md +13 -0
  38. package/vendor/docs/contract-2026-10-11-speaker-split-v0.md +112 -0
  39. package/vendor/docs/contract-2026-10-11-ui-i18n.md +15 -0
  40. package/vendor/packages/akari-launcher/package.json +4 -1
  41. package/vendor/packages/akari-launcher/src/history-policy.mjs +2 -0
  42. package/vendor/packages/akari-tools/bin/speakers.mjs +91 -0
  43. package/vendor/packages/akari-tools/bin/tools.mjs +304 -0
  44. package/vendor/packages/akari-tools/package.json +4 -1
  45. package/vendor/packages/akari-tools/src/common/main-module.mjs +11 -0
  46. package/vendor/packages/akari-tools/src/decision-log/i18n.mjs +15 -0
  47. package/vendor/packages/akari-tools/src/i18n.mjs +24 -0
  48. package/vendor/packages/akari-tools/src/media/common.mjs +227 -0
  49. package/vendor/packages/akari-voice/package.json +11 -0
  50. package/vendor/packages/akari-voice/src/audio.mjs +38 -0
  51. package/vendor/packages/akari-voice/src/centroids.mjs +62 -0
  52. package/vendor/packages/akari-voice/src/diarize.mjs +102 -0
  53. package/vendor/packages/akari-voice/src/dictionary.mjs +22 -0
  54. package/vendor/packages/akari-voice/src/embed.mjs +77 -0
  55. package/vendor/packages/akari-voice/src/fbank.mjs +100 -0
  56. package/vendor/packages/akari-voice/src/identify.mjs +38 -0
  57. package/vendor/packages/akari-voice/src/index.mjs +9 -0
  58. package/vendor/packages/akari-voice/src/voice-home.mjs +56 -0
  59. package/vendor/packages/akari-voice/src/voices-file.mjs +41 -0
  60. package/vendor/packages/analysis-report/render-analysis-report.mjs +11 -3
  61. package/vendor/packages/analysis-report/report-i18n.mjs +61 -0
  62. package/vendor/packages/analysis-report/template.html +21 -21
  63. package/vendor/packages/asset-resolver/src/project-root.mjs +26 -0
  64. package/vendor/packages/asset-resolver/test/project-root.test.mjs +109 -0
  65. package/vendor/packages/content-language/README.md +9 -0
  66. package/vendor/packages/content-language/package.json +13 -0
  67. package/vendor/packages/content-language/src/caption-defaults.json +16 -0
  68. package/vendor/packages/content-language/src/index.mjs +42 -0
  69. package/vendor/packages/content-language/src/node.mjs +104 -0
  70. package/vendor/packages/content-language/test/content-language.test.mjs +114 -0
  71. package/vendor/packages/decision-log-report/render-decision-log-report.mjs +38 -33
  72. package/vendor/packages/decision-log-report/template.html +19 -19
  73. package/vendor/packages/edit-lint/README.md +1 -0
  74. package/vendor/packages/edit-lint/src/edit-lint.mjs +13 -11
  75. package/vendor/packages/edit-lint/src/engine-capabilities.json +1 -0
  76. package/vendor/packages/edit-lint/src/i18n.mjs +29 -0
  77. package/vendor/packages/edit-lint/src/license-findings.mjs +4 -3
  78. package/vendor/packages/edit-lint/src/lint/captions.mjs +27 -9
  79. package/vendor/packages/edit-lint/src/lint/cuts-tracks.mjs +4 -5
  80. package/vendor/packages/edit-lint/src/lint/edit-v2.mjs +18 -8
  81. package/vendor/packages/edit-lint/src/lint/engine-capabilities.mjs +8 -5
  82. package/vendor/packages/edit-lint/src/lint/intake.mjs +5 -3
  83. package/vendor/packages/edit-lint/src/lint/look-adjust.mjs +6 -5
  84. package/vendor/packages/edit-lint/src/lint/media-checks.mjs +10 -10
  85. package/vendor/packages/edit-lint/src/lint/overlays.mjs +13 -12
  86. package/vendor/packages/edit-lint/src/lint/review.mjs +2 -1
  87. package/vendor/packages/edit-lint/src/lint/shared.mjs +2 -1
  88. package/vendor/packages/edit-lint/src/lint/v2-projection.mjs +8 -7
  89. package/vendor/packages/edit-lint/src/report.mjs +12 -11
  90. package/vendor/packages/edit-lint/src/world-scene-declaration.mjs +20 -18
  91. package/vendor/packages/edit-store/lib/caption-display.js +27 -6
  92. package/vendor/packages/edit-store/lib/edit-v2-item-write.d.ts +3 -0
  93. package/vendor/packages/edit-store/lib/edit-v2-item-write.js +15 -3
  94. package/vendor/packages/edit-store/lib/edit-v2.d.ts +3 -0
  95. package/vendor/packages/edit-store/lib/edit-v2.js +9 -0
  96. package/vendor/packages/edit-store/lib/generated/edit-v2-keys.d.ts +1 -1
  97. package/vendor/packages/edit-store/lib/generated/edit-v2-keys.js +1 -1
  98. package/vendor/packages/edit-store/lib/transform-keyframe-edit.d.ts +17 -2
  99. package/vendor/packages/edit-store/lib/transform-keyframe-edit.js +102 -7
  100. package/vendor/packages/edit-store/lib/webview-kernel.js +33 -0
  101. package/vendor/packages/i18n/index.cjs +107 -0
  102. package/vendor/packages/i18n/index.d.ts +13 -0
  103. package/vendor/packages/i18n/index.mjs +2 -0
  104. package/vendor/packages/i18n/locales/en/annotations.json +1149 -0
  105. package/vendor/packages/i18n/locales/en/annotationscommon.json +896 -0
  106. package/vendor/packages/i18n/locales/en/cli.json +734 -0
  107. package/vendor/packages/i18n/locales/en/common.json +6 -0
  108. package/vendor/packages/i18n/locales/en/companion.json +19 -0
  109. package/vendor/packages/i18n/locales/en/decision-log.json +28 -0
  110. package/vendor/packages/i18n/locales/en/inspector.json +1324 -0
  111. package/vendor/packages/i18n/locales/en/lint-messages.json +146 -0
  112. package/vendor/packages/i18n/locales/en/lint.json +14 -0
  113. package/vendor/packages/i18n/locales/en/partner.json +287 -0
  114. package/vendor/packages/i18n/locales/en/preview.json +675 -0
  115. package/vendor/packages/i18n/locales/en/project.json +873 -0
  116. package/vendor/packages/i18n/locales/en/render.json +116 -0
  117. package/vendor/packages/i18n/locales/en/report-analysis.json +114 -0
  118. package/vendor/packages/i18n/locales/en/report-cards.json +328 -0
  119. package/vendor/packages/i18n/locales/en/report-decision.json +46 -0
  120. package/vendor/packages/i18n/locales/en/report-project.json +22 -0
  121. package/vendor/packages/i18n/locales/en/settings.json +6 -0
  122. package/vendor/packages/i18n/locales/en/shell.json +558 -0
  123. package/vendor/packages/i18n/locales/en/skills.json +54 -0
  124. package/vendor/packages/i18n/locales/en/surfaces.json +2226 -0
  125. package/vendor/packages/i18n/locales/en/tabs.json +5 -0
  126. package/vendor/packages/i18n/locales/en/theme.json +32 -0
  127. package/vendor/packages/i18n/locales/en/tools.json +245 -0
  128. package/vendor/packages/i18n/locales/en/transcript.json +755 -0
  129. package/vendor/packages/i18n/locales/en/vibe.json +290 -0
  130. package/vendor/packages/i18n/locales/en/world.json +22 -0
  131. package/vendor/packages/i18n/locales/index.cjs +63 -0
  132. package/vendor/packages/i18n/locales/ja/annotations.json +1149 -0
  133. package/vendor/packages/i18n/locales/ja/annotationscommon.json +896 -0
  134. package/vendor/packages/i18n/locales/ja/cli.json +734 -0
  135. package/vendor/packages/i18n/locales/ja/common.json +6 -0
  136. package/vendor/packages/i18n/locales/ja/companion.json +19 -0
  137. package/vendor/packages/i18n/locales/ja/decision-log.json +28 -0
  138. package/vendor/packages/i18n/locales/ja/inspector.json +1324 -0
  139. package/vendor/packages/i18n/locales/ja/lint-messages.json +146 -0
  140. package/vendor/packages/i18n/locales/ja/lint.json +14 -0
  141. package/vendor/packages/i18n/locales/ja/partner.json +287 -0
  142. package/vendor/packages/i18n/locales/ja/preview.json +675 -0
  143. package/vendor/packages/i18n/locales/ja/project.json +873 -0
  144. package/vendor/packages/i18n/locales/ja/render.json +116 -0
  145. package/vendor/packages/i18n/locales/ja/report-analysis.json +114 -0
  146. package/vendor/packages/i18n/locales/ja/report-cards.json +328 -0
  147. package/vendor/packages/i18n/locales/ja/report-decision.json +46 -0
  148. package/vendor/packages/i18n/locales/ja/report-project.json +22 -0
  149. package/vendor/packages/i18n/locales/ja/settings.json +6 -0
  150. package/vendor/packages/i18n/locales/ja/shell.json +558 -0
  151. package/vendor/packages/i18n/locales/ja/skills.json +54 -0
  152. package/vendor/packages/i18n/locales/ja/surfaces.json +2226 -0
  153. package/vendor/packages/i18n/locales/ja/tabs.json +5 -0
  154. package/vendor/packages/i18n/locales/ja/theme.json +32 -0
  155. package/vendor/packages/i18n/locales/ja/tools.json +245 -0
  156. package/vendor/packages/i18n/locales/ja/transcript.json +755 -0
  157. package/vendor/packages/i18n/locales/ja/vibe.json +290 -0
  158. package/vendor/packages/i18n/locales/ja/world.json +22 -0
  159. package/vendor/packages/i18n/package.json +16 -0
  160. package/vendor/packages/i18n/test/catalog-loader.test.mjs +78 -0
  161. package/vendor/packages/i18n/test/child-process-language.test.mjs +37 -0
  162. package/vendor/packages/i18n/test/i18n.test.mjs +149 -0
  163. package/vendor/packages/media-bin/bin/audio-measure.mjs +4 -7
  164. package/vendor/packages/media-bin/test/audio-measure.test.mjs +26 -0
  165. package/vendor/packages/overlay-runtime/runtimes.mjs +49 -12
  166. package/vendor/packages/overlay-runtime/src/interaction.css +17 -0
  167. package/vendor/packages/overlay-runtime/src/interaction.js +451 -12
  168. package/vendor/packages/overlay-runtime/src/item-motion.js +32 -1
  169. package/vendor/packages/overlay-runtime/src/overlay-runtime.js +37 -1
  170. package/vendor/packages/overlay-runtime/src/parts.mjs +3 -1
  171. package/vendor/packages/overlay-runtime/src/text-split.js +9 -4
  172. package/vendor/packages/overlay-runtime/src/three-runtime.js +395 -9
  173. package/vendor/packages/project-scaffold/src/index.mjs +51 -25
  174. package/vendor/packages/render-cut/src/fragment-assets.mjs +5 -4
  175. package/vendor/packages/render-cut/src/i18n.mjs +23 -0
  176. package/vendor/packages/render-cut/src/render-inputs.mjs +9 -2
  177. package/vendor/packages/schemas/analysis.schema.json +5 -0
  178. package/vendor/packages/schemas/bin/validate-captions.mjs +6 -19
  179. package/vendor/packages/schemas/bin/validate-intake.mjs +11 -1
  180. package/vendor/packages/schemas/edit.schema.json +6 -0
  181. package/vendor/packages/schemas/engine-capabilities.json +1 -0
  182. package/vendor/packages/schemas/intake.schema.json +5 -0
  183. package/vendor/packages/schemas/test/content-language.test.mjs +44 -0
  184. package/vendor/packages/schemas/test/engine-capabilities.test.mjs +1 -1
  185. package/vendor/packages/schemas/test/fixtures/content-language/analysis.json +9 -0
  186. package/vendor/packages/schemas/test/fixtures/content-language/intake.json +9 -0
  187. package/vendor/packages/schemas/test/validate-captions.test.mjs +8 -0
  188. package/vendor/packages/word-book/src/index.mjs +5 -5
  189. package/vendor/skills/address-review/SKILL.md +53 -55
  190. package/vendor/skills/akari/SKILL.md +86 -75
  191. package/vendor/skills/analyze-footage/SKILL.md +44 -40
  192. package/vendor/skills/analyze-footage/analysis-json.md +49 -48
  193. package/vendor/skills/analyze-footage/bin/speechanalyzer-helper.swift +9 -4
  194. package/vendor/skills/analyze-footage/bin/test/transcribe-sa-locale.test.mjs +166 -0
  195. package/vendor/skills/analyze-footage/bin/transcribe-sa.mjs +47 -16
  196. package/vendor/skills/analyze-footage/events-and-hooks.md +75 -82
  197. package/vendor/skills/analyze-footage/keyframes-and-review.md +41 -50
  198. package/vendor/skills/analyze-footage/lang/en.md +23 -0
  199. package/vendor/skills/analyze-footage/lang/ja.md +9 -0
  200. package/vendor/skills/analyze-footage/media-and-transcript.md +65 -43
  201. package/vendor/skills/analyze-footage/person-matte.md +96 -145
  202. package/vendor/skills/analyze-footage/references/akari-data-contract.md +3 -2
  203. package/vendor/skills/analyze-footage/vision-tracks.md +107 -216
  204. package/vendor/skills/analyze-footage/workflow.md +65 -64
  205. package/vendor/skills/analyze-project/SKILL.md +59 -61
  206. package/vendor/skills/analyze-project/collect.md +31 -35
  207. package/vendor/skills/analyze-project/decision-record.md +43 -42
  208. package/vendor/skills/analyze-project/interpretation.md +122 -132
  209. package/vendor/skills/analyze-project/interview.md +44 -46
  210. package/vendor/skills/analyze-project/references/interpretation-shape.md +15 -17
  211. package/vendor/skills/analyze-project/references/open-questions-examples.md +26 -26
  212. package/vendor/skills/analyze-project/validate-and-render.md +37 -38
  213. package/vendor/skills/bake-3d/SKILL.md +49 -55
  214. package/vendor/skills/beat-sync-edit/SKILL.md +50 -64
  215. package/vendor/skills/beat-sync-edit/generator.md +38 -48
  216. package/vendor/skills/beat-sync-edit/pitfalls.md +32 -37
  217. package/vendor/skills/beat-sync-edit/verify-loop.md +30 -43
  218. package/vendor/skills/compile-review-session/SKILL.md +44 -46
  219. package/vendor/skills/compile-review-session/compilation-rules.md +141 -146
  220. package/vendor/skills/create-project/SKILL.md +58 -54
  221. package/vendor/skills/critique-cut/SKILL.md +62 -66
  222. package/vendor/skills/critique-cut/workflow.md +65 -67
  223. package/vendor/skills/declare-audio/SKILL.md +32 -52
  224. package/vendor/skills/declare-audio/after-save.md +24 -35
  225. package/vendor/skills/declare-audio/launch.md +29 -40
  226. package/vendor/skills/declare-audio/what-to-declare.md +30 -38
  227. package/vendor/skills/design-world/SKILL.md +31 -33
  228. package/vendor/skills/design-world/guide.md +12 -9
  229. package/vendor/skills/design-world/world.md +30 -32
  230. package/vendor/skills/edit-lint/SKILL.md +112 -100
  231. package/vendor/skills/edit-lint/preview.md +26 -26
  232. package/vendor/skills/edit-plan/SKILL.md +41 -80
  233. package/vendor/skills/edit-plan/approvals-and-generation.md +57 -66
  234. package/vendor/skills/edit-plan/autonomy.md +46 -46
  235. package/vendor/skills/edit-plan/avatar-resolution.md +91 -150
  236. package/vendor/skills/edit-plan/beat-sync.md +275 -436
  237. package/vendor/skills/edit-plan/beats.md +63 -101
  238. package/vendor/skills/edit-plan/emphasis-detection.md +110 -169
  239. package/vendor/skills/edit-plan/execution.md +94 -132
  240. package/vendor/skills/edit-plan/expression-selection.md +138 -218
  241. package/vendor/skills/edit-plan/lang/en.md +17 -0
  242. package/vendor/skills/edit-plan/lang/ja.md +76 -0
  243. package/vendor/skills/edit-plan/plan-json.md +29 -51
  244. package/vendor/skills/edit-plan/recipe.md +38 -70
  245. package/vendor/skills/edit-plan/report-guide.md +126 -182
  246. package/vendor/skills/edit-plan/workflow.md +54 -95
  247. package/vendor/skills/export-nle/SKILL.md +52 -68
  248. package/vendor/skills/generate-media/SKILL.md +28 -28
  249. package/vendor/skills/generate-media/chain.md +20 -20
  250. package/vendor/skills/generate-media/engines.md +17 -15
  251. package/vendor/skills/generate-media/still.md +25 -29
  252. package/vendor/skills/generate-media/video.md +25 -26
  253. package/vendor/skills/generate-narration/SKILL.md +28 -30
  254. package/vendor/skills/generate-narration/avatar-voice.md +25 -41
  255. package/vendor/skills/generate-narration/engines.md +85 -125
  256. package/vendor/skills/generate-narration/lang/en.md +23 -0
  257. package/vendor/skills/generate-narration/lang/ja.md +68 -0
  258. package/vendor/skills/generate-narration/reading-text.md +14 -29
  259. package/vendor/skills/generate-narration/voice-profile-setup.md +47 -69
  260. package/vendor/skills/harvest-asset/SKILL.md +105 -82
  261. package/vendor/skills/manage-connections/SKILL.md +89 -96
  262. package/vendor/skills/manage-connections/bin/doctor.mjs +105 -50
  263. package/vendor/skills/manage-connections/bin/test/doctor-i18n.test.mjs +76 -0
  264. package/vendor/skills/overlay-authoring/3d.md +284 -516
  265. package/vendor/skills/overlay-authoring/SKILL.md +42 -47
  266. package/vendor/skills/overlay-authoring/device-materials.md +16 -38
  267. package/vendor/skills/overlay-authoring/glass.md +34 -58
  268. package/vendor/skills/overlay-authoring/lang/en.md +40 -0
  269. package/vendor/skills/overlay-authoring/lang/ja.md +58 -0
  270. package/vendor/skills/overlay-authoring/motion.md +97 -116
  271. package/vendor/skills/overlay-authoring/table.md +32 -32
  272. package/vendor/skills/overlay-authoring/telop.md +115 -154
  273. package/vendor/skills/overlay-authoring/text-behind-person.md +96 -137
  274. package/vendor/skills/overlay-authoring/thumbnail.md +72 -72
  275. package/vendor/skills/overlay-authoring/world.md +14 -39
  276. package/vendor/skills/render-cut/SKILL.md +63 -89
  277. package/vendor/skills/research-plan/SKILL.md +53 -49
  278. package/vendor/skills/research-plan/competitor.md +29 -34
  279. package/vendor/skills/research-plan/ideate.md +32 -37
  280. package/vendor/skills/research-plan/lang/en.md +5 -0
  281. package/vendor/skills/research-plan/lang/ja.md +14 -0
  282. package/vendor/skills/research-plan/shotlist.md +28 -28
  283. package/vendor/skills/research-plan/storyboard.md +50 -49
  284. package/vendor/skills/research-plan/trends.md +21 -21
  285. package/vendor/skills/setup-audio-library/SKILL.md +76 -88
  286. package/vendor/skills/setup-audio-library/assisted-fetch.md +39 -42
  287. package/vendor/skills/setup-audio-library/candidate-list.md +36 -40
  288. package/vendor/skills/setup-audio-library/drop-folder.md +43 -51
  289. package/vendor/skills/setup-audio-library/first-party.md +42 -42
  290. package/vendor/skills/setup-audio-library/gallery.md +31 -37
  291. package/vendor/skills/setup-audio-library/store-unlock.md +24 -25
  292. package/vendor/skills/setup-chat-approval/SKILL.md +27 -27
  293. package/vendor/skills/setup-chat-approval/setup-token.md +34 -34
  294. package/vendor/skills/setup-chat-approval/smoke-test.md +25 -25
  295. package/vendor/skills/setup-library/SKILL.md +32 -32
  296. package/vendor/skills/setup-library/fetch-and-validate.md +73 -57
  297. package/vendor/skills/setup-library/starter-pack.md +35 -32
  298. package/vendor/skills/setup-library/tools-check.md +52 -49
  299. package/vendor/skills/setup-remote/SKILL.md +28 -28
  300. package/vendor/skills/setup-remote/doctor-and-install.md +28 -28
  301. package/vendor/skills/setup-remote/serve-preview.md +35 -34
  302. package/vendor/skills/setup-remote/taildrop-inbox.md +40 -38
  303. package/vendor/skills/verify/SKILL.md +197 -128
  304. package/vendor/templates/project-default/.claude/settings.json +3 -1
  305. package/vendor/templates/project-default/AGENTS.md +83 -83
  306. package/vendor/templates/project-default/CLAUDE.md +58 -57
  307. package/vendor/templates/project-default/akari.sh +54 -41
  308. package/vendor/packages/project-scaffold/test/create-project.test.mjs +0 -193
  309. package/vendor/packages/project-scaffold/test/hidden-adapters.test.mjs +0 -16
  310. package/vendor/packages/project-scaffold/test/kit-skill-adapter.test.mjs +0 -38
  311. package/vendor/packages/project-scaffold/test/opencode-scaffold.test.mjs +0 -94
  312. package/vendor/packages/project-scaffold/test/skill-adapter-link.test.mjs +0 -210
package/bin/akari.mjs CHANGED
@@ -22,6 +22,7 @@ import { resolveRuntimePaths } from '../src/runtime-diagnostics.mjs';
22
22
  import { maybeApplyPendingUpdateOnLaunch, resolveInstalledVersionInfo } from '../src/update-check.mjs';
23
23
  import { describeCliHelp, describeInstalledVersions, firstArgumentTypoError } from '../src/messages.mjs';
24
24
  import { suggestFirstArgument } from '../src/first-arg-guard.mjs';
25
+ import { cliText } from '../src/i18n.mjs';
25
26
 
26
27
  // `akari --version` / `-v`: インストール済みの版を表示するだけの最小コマンド
27
28
  // (タスク契約 2026-08-11-update-u4-cli-self-update の受け入れ条件 —
@@ -41,8 +42,8 @@ async function printVersion() {
41
42
  // `--help` は claude/opencode へそのまま転送されてしまっていた — AKARI Video 自身の
42
43
  // コマンド一覧が一度も出ない行き止まりだったため新設した)。
43
44
  async function printCliHelp() {
44
- for (const line of [...describeCliHelp(), ' world ワールド地図を検査・生成・プレビュー・停留所移動',
45
- ' skills 入口スキルを配置・削除・確認(install/remove --entry, status --json)']) {
45
+ for (const line of [...describeCliHelp(), cliText('cli.help.commands.world'),
46
+ cliText('cli.help.commands.skills')]) {
46
47
  console.log(line);
47
48
  }
48
49
  return { exitCode: 0 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akari-video",
3
- "version": "1.3.0-beta.4",
3
+ "version": "1.3.0-beta.5",
4
4
  "description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,6 +34,9 @@
34
34
  "engines": {
35
35
  "node": ">=20"
36
36
  },
37
+ "devDependencies": {
38
+ "@akari-video/content-language": "0.0.0"
39
+ },
37
40
  "scripts": {
38
41
  "prepack": "node scripts/prepack.mjs",
39
42
  "postpack": "node scripts/prepack.mjs clean",
@@ -3,6 +3,7 @@ import { dirname, resolve } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
 
5
5
  import { resolveLauncherAssets } from './repo-assets.mjs';
6
+ import { cliText } from './i18n.mjs';
6
7
  import { assetsResolverUnavailableError } from './messages.mjs';
7
8
 
8
9
  /**
@@ -35,7 +36,7 @@ export async function runAssetsCommand(args, options = {}) {
35
36
  // first-party Sounds fetcher, while the resolver catalog contains only individual IDs.
36
37
  if (args[0] === 'fetch' && /^akari-sounds-[a-z0-9-]+$/u.test(args[1] ?? '')) {
37
38
  if (!assets?.audioFetchScriptPath) {
38
- logError('AKARI Sounds の取得スクリプトが同梱されていません');
39
+ logError(cliText('cli.assets.error.soundsScriptMissing'));
39
40
  return { exitCode: 1 };
40
41
  }
41
42
  const spawn = options.spawnSoundsCli ?? defaultSpawnAssetsCli;
@@ -2,12 +2,13 @@ import { spawnSync } from "node:child_process";
2
2
  import { existsSync } from "node:fs";
3
3
 
4
4
  import { resolveLauncherAssets } from "./repo-assets.mjs";
5
+ import { cliText } from './i18n.mjs';
5
6
 
6
7
  export async function runCaptionsCommand(argv, options = {}) {
7
8
  const logError = options.error ?? options.logError ?? ((line) => console.error(line));
8
9
  const assets = options.assets ?? resolveLauncherAssets();
9
10
  if (!assets.captionsScript || !existsSync(assets.captionsScript)) {
10
- logError("akari captions の実行スクリプトが見つかりません。AKARI Video を再インストールしてください。");
11
+ logError(cliText('cli.captions.error.scriptMissing'));
11
12
  return { exitCode: 1 };
12
13
  }
13
14
 
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
2
2
  import { existsSync } from "node:fs";
3
3
 
4
4
  import { CAPTURE_SCRIPT_RELATIVE, resolveLauncherAssets } from "./repo-assets.mjs";
5
+ import { cliText } from './i18n.mjs';
5
6
 
6
7
  export async function runCaptureCommand(argv, options = {}) {
7
8
  const logError = options.error ?? options.logError ?? ((line) => console.error(line));
@@ -10,12 +11,7 @@ export async function runCaptureCommand(argv, options = {}) {
10
11
  // 再インストールで直る欠けではない(同梱漏れは配布物側の問題)。欠けている同梱物と
11
12
  // 探索した置き場を名指しして、報告・切り分けに使える形で止める(issue #74)。
12
13
  const missing = assets.captureScript ?? CAPTURE_SCRIPT_RELATIVE;
13
- logError(
14
- `akari capture の実行スクリプトが見つかりません: ${missing}`
15
- + `(探索先: ${assets.repoRoot ?? "不明"})。`
16
- + "この AKARI Video 配布物に packages/akari-tools/bin/capture.mjs が同梱されていません。"
17
- + "バージョンと OS を添えて https://github.com/AkariLabs/akari-video/issues へ報告してください。",
18
- );
14
+ logError(cliText('cli.capture.error.scriptMissing', { missing, root: assets.repoRoot ?? cliText('cli.capture.label.unknown') }));
19
15
  return { exitCode: 1 };
20
16
  }
21
17
 
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import { createInterface } from 'node:readline/promises';
4
4
 
5
5
  import { classifyProject } from './clean-manifest.mjs';
6
+ import { cliText, cliValue } from './i18n.mjs';
6
7
 
7
8
  export async function runCleanCommand(args, options = {}) {
8
9
  const log = options.log ?? ((line) => console.log(line));
@@ -24,7 +25,7 @@ export async function runCleanCommand(args, options = {}) {
24
25
  const editStat = await statPath(path.join(parsed.projectRoot, 'edit.json'));
25
26
  if (!editStat.isFile()) throw Object.assign(new Error('not a file'), { code: 'ENOENT' });
26
27
  } catch {
27
- error(`edit.json が見つかりません: ${parsed.projectRoot}`);
28
+ error(cliText('cli.clean.error.editMissing', { root: parsed.projectRoot }));
28
29
  return { exitCode: 2 };
29
30
  }
30
31
 
@@ -37,32 +38,32 @@ export async function runCleanCommand(args, options = {}) {
37
38
  readFile: options.readFile,
38
39
  });
39
40
  } catch (cause) {
40
- error(`プロジェクトを調べられません: ${messageOf(cause)}`);
41
+ error(cliText('cli.clean.error.inspectFailed', { message: messageOf(cause) }));
41
42
  return { exitCode: 2 };
42
43
  }
43
44
 
44
45
  if (parsed.json) {
45
- log(JSON.stringify(classification));
46
+ log(JSON.stringify(localizeClassification(classification)));
46
47
  } else {
47
48
  for (const line of formatClassification(classification)) log(line);
48
49
  for (const warning of provenanceWarnings(classification)) log(warning);
49
50
  }
50
51
 
51
52
  if (parsed.dryRun || classification.disposable.length === 0) {
52
- if (!parsed.json && classification.disposable.length === 0) log('削除対象はありません。');
53
+ if (!parsed.json && classification.disposable.length === 0) log(cliText('cli.clean.status.nothingToDelete'));
53
54
  return { exitCode: 0, classification, removed: [], failures: [] };
54
55
  }
55
56
 
56
57
  if (!parsed.yes) {
57
58
  const isTTY = options.isTTY ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
58
59
  if (!isTTY) {
59
- error('非 TTY では明示承認が必要です。一覧を確認し、--yes を付けて再実行してください。');
60
+ error(cliText('cli.clean.error.approvalRequired'));
60
61
  return { exitCode: 2, classification, removed: [], failures: [] };
61
62
  }
62
63
  const answer = options.prompt ? await options.prompt() : await promptForConfirmation();
63
64
  const accepted = typeof answer === 'boolean' ? answer : /^(?:y|yes)$/iu.test(String(answer).trim());
64
65
  if (!accepted) {
65
- if (!parsed.json) log('削除しませんでした。プロジェクトは変更されていません。');
66
+ if (!parsed.json) log(cliText('cli.clean.status.cancelled'));
66
67
  return { exitCode: 0, classification, removed: [], failures: [] };
67
68
  }
68
69
  }
@@ -75,7 +76,7 @@ export async function runCleanCommand(args, options = {}) {
75
76
  if (!isInside(parsed.projectRoot, target) || target === parsed.projectRoot) {
76
77
  const failure = { path: entry.path, code: 'OUTSIDE_PROJECT' };
77
78
  failures.push(failure);
78
- error(`削除に失敗しました: ${entry.path} (${failure.code})`);
79
+ error(cliText('cli.clean.error.deleteFailed', { path: entry.path, code: failure.code }));
79
80
  continue;
80
81
  }
81
82
  try {
@@ -84,15 +85,15 @@ export async function runCleanCommand(args, options = {}) {
84
85
  } catch (cause) {
85
86
  const code = errorCode(cause);
86
87
  failures.push({ path: entry.path, code });
87
- error(`削除に失敗しました: ${entry.path} (${code})`);
88
+ error(cliText('cli.clean.error.deleteFailed', { path: entry.path, code }));
88
89
  }
89
90
  }
90
91
 
91
92
  if (failures.length > 0) {
92
- error('一部を削除できませんでした。AKARI Video と書き出し処理を終了してから再実行してください。');
93
+ error(cliText('cli.clean.error.partialDelete'));
93
94
  return { exitCode: 1, classification, removed, failures };
94
95
  }
95
- if (!parsed.json) log(`削除しました: ${removed.length} 件`);
96
+ if (!parsed.json) log(cliText('cli.clean.status.deleted', { count: removed.length, n: removed.length }));
96
97
  return { exitCode: 0, classification, removed, failures };
97
98
  }
98
99
 
@@ -100,9 +101,9 @@ export function parseArguments(args, cwd = process.cwd()) {
100
101
  const allowed = new Set(['--dry-run', '--yes', '--json', '--help']);
101
102
  const flags = args.filter((value) => value.startsWith('-'));
102
103
  const unknown = [...new Set(flags.filter((value) => !allowed.has(value)))];
103
- if (unknown.length > 0) return { ok: false, message: `未知のオプションです: ${unknown.join(', ')}` };
104
+ if (unknown.length > 0) return { ok: false, message: cliText('cli.clean.error.unknownOption', { options: unknown.join(', ') }) };
104
105
  const positional = args.filter((value) => !value.startsWith('-'));
105
- if (positional.length > 1) return { ok: false, message: '引数のプロジェクトディレクトリは 1 つだけ指定できます。' };
106
+ if (positional.length > 1) return { ok: false, message: cliText('cli.clean.error.multipleRoots') };
106
107
  const flagSet = new Set(flags);
107
108
  return {
108
109
  ok: true,
@@ -116,39 +117,48 @@ export function parseArguments(args, cwd = process.cwd()) {
116
117
 
117
118
  export function cleanHelp() {
118
119
  return [
119
- '使い方: akari clean [dir] [--dry-run] [--yes] [--json] [--help]',
120
+ cliText('cli.clean.help.usage'),
120
121
  '',
121
- '使い捨ての中間ファイル、保持する正本、判断が必要なものを一覧します。',
122
- '既定では一覧だけを表示し、削除前には必ず確認します。',
122
+ cliText('cli.clean.help.description'),
123
+ cliText('cli.clean.help.default'),
123
124
  '',
124
- ' --dry-run 一覧だけを表示して削除しない',
125
- ' --yes 一覧を表示した後、削除可能なものだけを削除',
126
- ' --json 分類結果を JSON で表示',
127
- ' --help このヘルプを表示',
125
+ ...['dryRun', 'yes', 'json', 'help'].map((name) => cliText(`cli.clean.help.${name}`)),
128
126
  ];
129
127
  }
130
128
 
131
129
  export function formatClassification(classification) {
132
130
  const lines = [];
133
131
  for (const [className, heading] of [
134
- ['disposable', '削除可能:'],
135
- ['keep', '保持:'],
136
- ['undecided', '判断保留:'],
132
+ ['disposable', cliText('cli.clean.category.disposable')],
133
+ ['keep', cliText('cli.clean.category.keep')],
134
+ ['undecided', cliText('cli.clean.category.undecided')],
137
135
  ]) {
138
136
  lines.push(heading);
139
137
  const entries = classification[className];
140
- if (entries.length === 0) lines.push(' (なし)');
138
+ if (entries.length === 0) lines.push(cliText('cli.clean.category.empty'));
141
139
  for (const entry of entries) {
142
140
  const provenance = entry.provenance
143
- ? ` / 由来: ${entry.provenance.origin} / 生成器: ${entry.provenance.generator}`
141
+ ? cliText('cli.clean.entry.provenance', { origin: entry.provenance.origin, generator: entry.provenance.generator })
144
142
  : '';
145
- lines.push(` ${entry.path} | ${entry.files} ファイル | ${formatBytes(entry.bytes)} | ${entry.reason}${provenance}`);
143
+ lines.push(cliText('cli.clean.entry.row', { path: entry.path, files: entry.files, n: entry.files, bytes: formatBytes(entry.bytes), reason: cliValue('cli.clean.reason.', entry.reason), provenance }));
146
144
  }
147
145
  }
148
- lines.push(`削除可能 合計 ${formatBytes(classification.totals.disposable.bytes)}`);
146
+ lines.push(cliText('cli.clean.category.total', { bytes: formatBytes(classification.totals.disposable.bytes) }));
149
147
  return lines;
150
148
  }
151
149
 
150
+ function localizeClassification(classification) {
151
+ const result = { ...classification };
152
+ for (const category of ['disposable', 'keep', 'undecided']) {
153
+ result[category] = classification[category].map((entry) => ({
154
+ ...entry,
155
+ reason: cliValue('cli.clean.reason.', entry.reason),
156
+ ...(entry.held_reason ? { held_reason: cliValue('cli.clean.reason.', entry.held_reason) } : {}),
157
+ }));
158
+ }
159
+ return result;
160
+ }
161
+
152
162
  export function formatBytes(bytes) {
153
163
  if (bytes < 1024) return `${bytes} B`;
154
164
  const units = ['KB', 'MB', 'GB'];
@@ -164,13 +174,13 @@ export function formatBytes(bytes) {
164
174
  function provenanceWarnings(classification) {
165
175
  return classification.keep
166
176
  .filter((entry) => entry.provenance?.origin_exists === false)
167
- .map((entry) => `[警告] 由来の計画ファイルが見当たりません: ${entry.provenance.origin}`);
177
+ .map((entry) => cliText('cli.clean.warning.provenanceMissing', { origin: entry.provenance.origin }));
168
178
  }
169
179
 
170
180
  async function promptForConfirmation() {
171
181
  const readline = createInterface({ input: process.stdin, output: process.stdout });
172
182
  try {
173
- return await readline.question('削除可能な中間ファイルだけを削除しますか? [y/N] ');
183
+ return await readline.question(cliText('cli.clean.prompt.delete'));
174
184
  } finally {
175
185
  readline.close();
176
186
  }
package/src/cli.mjs CHANGED
@@ -26,8 +26,11 @@ import { runDecisionLogCommand } from './decision-log-command.mjs';
26
26
  import { runCaptionsCommand } from './captions-command.mjs';
27
27
  import { runCaptureCommand } from './capture-command.mjs';
28
28
  import { runMediaCommand } from './media-command.mjs';
29
+ import { runSpeakersCommand } from './speakers-command.mjs';
30
+ import { runToolsCommand } from './tools-command.mjs';
29
31
  import { runWordBookCommand } from './word-book-command.mjs';
30
32
  import { resolveRuntimePaths } from './runtime-diagnostics.mjs';
33
+ import { cliText } from './i18n.mjs';
31
34
 
32
35
  // run を直接呼ぶ利用者と入口で、同じサブコマンドの表を使う。
33
36
  export const runSubcommands = {
@@ -35,6 +38,8 @@ export const runSubcommands = {
35
38
  captions: runCaptionsCommand,
36
39
  capture: runCaptureCommand,
37
40
  media: runMediaCommand,
41
+ speakers: runSpeakersCommand,
42
+ tools: runToolsCommand,
38
43
  'word-book': runWordBookCommand,
39
44
  };
40
45
 
@@ -51,7 +56,7 @@ export async function run(args, options = {}) {
51
56
  const retiredBrowserCommand = 'chrome';
52
57
  if (args[0] === retiredBrowserCommand) {
53
58
  const error = options.error ?? ((line) => console.error(line));
54
- error(`akari ${retiredBrowserCommand} は廃止されました(Chrome は不要になりました)`);
59
+ error(cliText('cli.launcher.error.retiredBrowser', { command: retiredBrowserCommand }));
55
60
  return { exitCode: 1 };
56
61
  }
57
62
  if (Object.hasOwn(runSubcommands, args[0])) return runSubcommands[args[0]](args.slice(1), options);
@@ -84,9 +89,9 @@ export async function run(args, options = {}) {
84
89
  const bareNonInteractive = args.length === 0 && !isTTY;
85
90
  if (!isTTY && !autoConfirm && !hereOnly && !detectProjectState(projectRoot).scaffolded) {
86
91
  const output = bareNonInteractive ? log : (options.error ?? ((line) => console.error(line)));
87
- output(`このフォルダーは AKARI Video プロジェクトとしてまだセットアップされていません: ${projectRoot}`);
92
+ output(cliText('cli.launcher.project.uninitialized', { root: projectRoot }));
88
93
  output(bareNonInteractive
89
- ? '非対話シェルのため状態確認のみで終了します。'
94
+ ? cliText('cli.launcher.project.nonInteractive')
90
95
  : nonInteractiveUninitializedGuidance());
91
96
  return { exitCode: bareNonInteractive ? 0 : 2, scaffolded: false, claudeLaunched: false, opencodeLaunched: false };
92
97
  }
@@ -100,48 +105,50 @@ export async function run(args, options = {}) {
100
105
  try {
101
106
  const creatorRoot = await defaultLoadCreatorRootModule(assets);
102
107
  const migration = await creatorRoot?.migrateAssetLibrary({ env, platform, automatic: true, notify: log });
103
- for (const failure of migration?.failures ?? []) log(`素材の移行を再試行します: ${failure.message}`);
108
+ for (const failure of migration?.failures ?? []) log(cliText('cli.launcher.assets.migrationRetry', { message: failure.message }));
104
109
  } catch (error) {
105
- log(`素材の移行を再試行します: ${error.message}`);
110
+ log(cliText('cli.launcher.assets.migrationRetry', { message: error.message }));
106
111
  }
107
112
 
108
113
  let state = detectProjectState(projectRoot);
109
114
 
110
115
  if (!state.scaffolded) {
111
- log(`このフォルダーは AKARI Video プロジェクトとしてまだセットアップされていません: ${projectRoot}`);
116
+ log(cliText('cli.launcher.project.uninitialized', { root: projectRoot }));
112
117
  if (!assets.templateDir || !assets.scaffoldModulePath) {
113
- log('プロジェクト雛形が見つからないため、雛形の作成をスキップしました。');
118
+ log(cliText('cli.launcher.project.templateMissing'));
114
119
  } else {
115
- log('プロジェクトの雛形を作成します…');
120
+ log(cliText('cli.launcher.project.creating'));
116
121
  try {
117
122
  const report = await scaffold(projectRoot, assets);
118
- log(`プロジェクトを作成しました(コピー ${report.copy.copiedFiles.length} 件 / 補完 ${report.fallback.writtenFiles.length} 件 / git: ${report.git.action})。`);
123
+ log(cliText('cli.launcher.project.created', { copied: report.copy.copiedFiles.length,
124
+ fallback: cliText('cli.launcher.project.fallbackCount', { count: report.fallback.writtenFiles.length, n: report.fallback.writtenFiles.length }),
125
+ git: report.git.action, n: report.copy.copiedFiles.length }));
119
126
  } catch (error) {
120
127
  // scaffold の失敗で claude 起動まで止めない(「最後に claude を exec」は不変条件)。
121
- log(`プロジェクトの雛形作成でエラーが発生しました(続行します): ${error instanceof Error ? error.message : String(error)}`);
128
+ log(cliText('cli.launcher.project.createFailed', { message: error instanceof Error ? error.message : String(error) }));
122
129
  }
123
130
  state = detectProjectState(projectRoot);
124
131
  }
125
132
  } else {
126
- log(`既存の AKARI Video プロジェクトを検出しました: ${projectRoot}`);
133
+ log(cliText('cli.launcher.project.existing', { root: projectRoot }));
127
134
  }
128
135
 
129
136
  const taskLabels = loadTaskLabels(assets.schemasSourceDir);
130
137
  log(describeIntake(state.intake, taskLabels));
131
138
 
132
139
  if (state.scaffolded && assets.doctorScript) {
133
- log('接続状態を確認します…');
140
+ log(cliText('cli.launcher.doctor.checking'));
134
141
  try {
135
142
  runDoctor(assets.doctorScript, projectRoot);
136
143
  } catch (error) {
137
- log(`接続確認でエラーが発生しました(続行します): ${error instanceof Error ? error.message : String(error)}`);
144
+ log(cliText('cli.launcher.doctor.failed', { message: error instanceof Error ? error.message : String(error) }));
138
145
  }
139
146
  const runtimeDiagnostics = options.runtimeDiagnostics ?? resolveRuntimePaths({ ...options, env, platform });
140
147
  log(describeVersionStatus(versionInfo, readCacheSync(resolveCachePath(env)), runtimeDiagnostics));
141
148
  }
142
149
 
143
150
  if (bareNonInteractive) {
144
- log('非対話シェルのため状態確認のみで終了します。');
151
+ log(cliText('cli.launcher.project.nonInteractive'));
145
152
  return { exitCode: 0, scaffolded: state.scaffolded, claudeLaunched: false, opencodeLaunched: false };
146
153
  }
147
154
 
@@ -161,11 +168,11 @@ export async function run(args, options = {}) {
161
168
  try {
162
169
  await (options.showAssetIntro ?? maybeShowAssetIntroNotice)({ env, log });
163
170
  } catch (error) {
164
- log(`素材案内の表示でエラーが発生しました(続行します): ${error instanceof Error ? error.message : String(error)}`);
171
+ log(cliText('cli.launcher.assets.noticeFailed', { message: error instanceof Error ? error.message : String(error) }));
165
172
  }
166
173
 
167
174
  if (useOpencode) {
168
- log('opencode を起動します…');
175
+ log(cliText('cli.launcher.agent.startOpencode'));
169
176
  const opencodePath = resolveOpencode();
170
177
  if (!opencodePath) {
171
178
  log(opencodeMissingGuidance());
@@ -179,14 +186,14 @@ export async function run(args, options = {}) {
179
186
  } else {
180
187
  const claudePath = resolveClaude();
181
188
  if (claudePath) {
182
- log('Claude Code を起動します…');
189
+ log(cliText('cli.launcher.agent.startClaude'));
183
190
  const claudeArgs = autoConfirm ? ['--permission-mode', 'acceptEdits', ...filteredArgs] : filteredArgs;
184
191
  const result = spawnClaude(claudePath, claudeArgs, projectRoot);
185
192
  const exitCode = typeof result.status === 'number' ? result.status : (result.error ? 1 : 0);
186
193
  return { exitCode, scaffolded: state.scaffolded, claudeLaunched: true };
187
194
  }
188
195
 
189
- log('Claude Code が見つかりません。opencode を起動します…');
196
+ log(cliText('cli.launcher.agent.claudeMissing'));
190
197
  const opencodePath = resolveOpencode();
191
198
  if (!opencodePath) {
192
199
  log(claudeMissingGuidance());
@@ -296,8 +303,8 @@ export async function runUpdateCommand(args, options = {}) {
296
303
  if (forceRequested
297
304
  && versionInfo.installRefStatus === 'missing'
298
305
  && runtimeDiagnostics.render_cut.origin !== 'monorepo') {
299
- log('この CLI からは install.sh 経路の本体を入れ直せません。');
300
- log('導入するには `curl -fsSL https://raw.githubusercontent.com/AkariLabs/akari-video/main/install.sh | bash` を実行してください(デスクトップ版だけで使う場合は不要です)。');
306
+ log(cliText('cli.update.force.unavailable'));
307
+ log(cliText('cli.update.force.installGuide'));
301
308
  }
302
309
  return { exitCode: 0 };
303
310
  }
@@ -308,7 +315,7 @@ export async function runUpdateCommand(args, options = {}) {
308
315
  for (const line of describeInstalledVersions(versionInfo, runtimeDiagnostics)) {
309
316
  log(line);
310
317
  }
311
- log(`最新バージョン: v${feed.product}`);
318
+ log(cliText('cli.update.feed.latest', { version: feed.product, channel: '' }));
312
319
  if (forceRequested) {
313
320
  log(describeForceReinstall(versionInfo, feed.product));
314
321
  }
@@ -1,22 +1,18 @@
1
1
  import { doctorExitCode, resolveDoctorReport } from './runtime-diagnostics.mjs';
2
+ import { cliText, cliLanguage } from './i18n.mjs';
2
3
 
3
- const usage = [
4
- '使い方: akari doctor [--json]',
5
- '',
6
- '必須部品の実在、解決元、PATH を確認します。',
7
- ' --json 機械可読な診断結果を 1 オブジェクトで出力',
8
- ].join('\n');
4
+ const usage = () => ['usage', 'blank', 'description', 'json'].map((name) => name === 'blank' ? '' : cliText(`cli.doctor.help.${name}`)).join('\n');
9
5
 
10
6
  export async function runDoctorCommand(argv, options = {}) {
11
7
  const log = options.log ?? ((line) => process.stdout.write(`${line}\n`));
12
8
  const error = options.error ?? ((line) => process.stderr.write(`${line}\n`));
13
9
  if (argv.includes('--help') || argv.includes('-h')) {
14
- log(usage);
10
+ log(usage());
15
11
  return { exitCode: 0 };
16
12
  }
17
13
  const unknown = argv.find((argument) => argument !== '--json');
18
14
  if (unknown) {
19
- error(`不明な doctor オプションです: ${unknown}\n${usage}`);
15
+ error(`${cliText('cli.doctor.error.unknownOption', { option: unknown })}\n${usage()}`);
20
16
  return { exitCode: 2 };
21
17
  }
22
18
 
@@ -30,49 +26,54 @@ export function formatDoctorReport(report) {
30
26
  const rows = [
31
27
  ['cli', 'ok', `v${report.cli.version} — ${report.cli.entry_path}`],
32
28
  ['node', report.cli.node?.runtime ?? 'unknown', report.cli.node
33
- ? `v${report.cli.node.version} — ${report.cli.node.exec_path}${report.cli.node.runtime === 'electron' ? ' (Node として使うには ELECTRON_RUN_AS_NODE=1 を付けて呼ぶ)' : ''}`
34
- : '診断情報がありません'],
29
+ ? `v${report.cli.node.version} — ${report.cli.node.exec_path}${report.cli.node.runtime === 'electron' ? cliText('cli.doctor.detail.electronNode') : ''}`
30
+ : cliText('cli.doctor.detail.noInfo')],
35
31
  ['app_managed', report.app_managed.status, detail(report.app_managed.path, report.app_managed.version)],
36
32
  ['app_bundle', report.app_bundle.found ? 'found' : 'missing', detail(report.app_bundle.path, report.app_bundle.version)],
37
- ['render_cut', report.render_cut.origin, report.render_cut.path ?? '見つかりません'],
38
- ['edit_lint', report.edit_lint.origin, report.edit_lint.path ?? '見つかりません'],
39
- ['ffmpeg', report.ffmpeg.origin, report.ffmpeg.path ?? '見つかりません'],
40
- ['ffprobe', report.ffprobe.origin, report.ffprobe.path ?? '見つかりません'],
41
- ['gpu_export', report.gpu_export?.available ? 'ok' : 'unavailable', report.gpu_export?.reason ?? '診断情報がありません'],
33
+ ['render_cut', report.render_cut.origin, report.render_cut.path ?? cliText('cli.doctor.detail.missing')],
34
+ ['edit_lint', report.edit_lint.origin, report.edit_lint.path ?? cliText('cli.doctor.detail.missing')],
35
+ ['ffmpeg', report.ffmpeg.origin, report.ffmpeg.path ?? cliText('cli.doctor.detail.missing')],
36
+ ['ffprobe', report.ffprobe.origin, report.ffprobe.path ?? cliText('cli.doctor.detail.missing')],
37
+ ['gpu_export', report.gpu_export?.available ? 'ok' : 'unavailable', localizeGpuReason(report.gpu_export?.reason) ?? cliText('cli.doctor.detail.noInfo')],
42
38
  ['fal_key', report.fal_key?.source ?? 'missing', report.fal_key?.source === 'env'
43
- ? '環境変数 FAL_KEY'
39
+ ? cliText('cli.doctor.detail.falEnv')
44
40
  : report.fal_key?.source === 'credentials.env'
45
41
  ? report.fal_key.credentials_path
46
- : '環境変数にも credentials.env にもありません'],
47
- ['鍵の置き場', report.fal_key?.location ?? 'なし', '新 / 旧 / 両方'],
42
+ : cliText('cli.doctor.detail.falMissing')],
43
+ [cliText('cli.doctor.detail.keyLocation'), cliText(`cli.doctor.location.${({ 新: 'new', 旧: 'old', 両方: 'both', なし: 'none' })[report.fal_key?.location] ?? 'none'}`), cliText('cli.doctor.detail.keyLocationLegend')],
48
44
  ['path', report.path.on_path ? 'ok' : 'missing', report.path.cli_shim_dir],
49
45
  ];
50
46
  const widths = [
51
- Math.max('項目'.length, ...rows.map((row) => row[0].length)),
52
- Math.max('状態'.length, ...rows.map((row) => row[1].length)),
47
+ Math.max(cliText('cli.doctor.table.item').length, ...rows.map((row) => row[0].length)),
48
+ Math.max(cliText('cli.doctor.table.status').length, ...rows.map((row) => row[1].length)),
53
49
  ];
54
50
  const lines = [
55
51
  'AKARI Video doctor',
56
52
  '',
57
- `${'項目'.padEnd(widths[0])} ${'状態'.padEnd(widths[1])} 詳細`,
53
+ `${cliText('cli.doctor.table.item').padEnd(widths[0])} ${cliText('cli.doctor.table.status').padEnd(widths[1])} ${cliText('cli.doctor.table.detail')}`,
58
54
  `${'-'.repeat(widths[0])} ${'-'.repeat(widths[1])} ${'-'.repeat(20)}`,
59
55
  ...rows.map((row) => `${row[0].padEnd(widths[0])} ${row[1].padEnd(widths[1])} ${row[2]}`),
60
56
  '',
61
- `判定: ${report.verdict}`,
57
+ cliText('cli.doctor.table.verdict', { verdict: report.verdict }),
62
58
  ];
63
59
  const steps = [...report.next_steps];
64
60
  if (report.cli.node?.runtime === 'electron' && report.cli.node.exec_path && report.render_cut.path) {
65
61
  const quote = (value) => `"${value.replaceAll('"', '\\"')}"`;
66
- steps.push(`Node が PATH に無い場合の書き出し: ELECTRON_RUN_AS_NODE=1 ${quote(report.cli.node.exec_path)} ${quote(report.render_cut.path)} <project-root>`);
62
+ steps.push(cliText('cli.doctor.detail.nodeExport', { node: quote(report.cli.node.exec_path), render: quote(report.render_cut.path) }));
67
63
  }
68
64
  if (steps.length > 0) {
69
- lines.push('次の手順:');
65
+ lines.push(cliText('cli.doctor.table.nextSteps'));
70
66
  lines.push(...steps.map((step) => ` - ${step}`));
71
67
  }
72
68
  return lines.join('\n');
73
69
  }
74
70
 
75
71
  function detail(path, version) {
76
- const location = path ?? '見つかりません';
72
+ const location = path ?? cliText('cli.doctor.detail.missing');
77
73
  return version ? `v${version} — ${location}` : location;
78
74
  }
75
+
76
+ function localizeGpuReason(reason) {
77
+ if (!reason || cliLanguage() === 'ja') return reason;
78
+ return reason.replace(cliText('cli.doctor.detail.gpuRepoReason', {}, 'ja'), cliText('cli.doctor.detail.gpuRepoReason'));
79
+ }
@@ -1,13 +1,14 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
 
3
3
  import { resolveLauncherAssets } from "./repo-assets.mjs";
4
+ import { cliText } from './i18n.mjs';
4
5
 
5
6
  export async function runGenerateCommand(args, options = {}) {
6
7
  const logError = options.logError ?? ((line) => console.error(line));
7
8
  const assets = options.assets ?? resolveLauncherAssets();
8
9
  const spawn = options.spawn ?? spawnSync;
9
10
  if (!assets.generateScript) {
10
- logError("akari generate の実行スクリプトが見つかりません。完全な AKARI Video を再導入してください:");
11
+ logError(cliText('cli.generate.error.scriptMissing'));
11
12
  logError(" npm install -g akari-video");
12
13
  return { exitCode: 1 };
13
14
  }
@@ -63,6 +63,8 @@ const HISTORY_BLOCK_BODY = [
63
63
  '',
64
64
  '# 素材の分析結果は作り直すのに手間がかかるので、変更履歴に残します。',
65
65
  '!.akari/sidecars/**',
66
+ '# 声の特徴量(個人識別符号)は履歴に入れない',
67
+ '.akari/sidecars/**/voices.json',
66
68
  '',
67
69
  '# パソコンが作る一時ファイル。',
68
70
  '.DS_Store',
package/src/i18n.mjs ADDED
@@ -0,0 +1,47 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { join, resolve } from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+
6
+ const sourceUrl = import.meta.url;
7
+ const require = createRequire(sourceUrl || pathToFileURL(__filename));
8
+ const roots = sourceUrl
9
+ ? ['../../i18n', '../vendor/packages/i18n'].map(root => fileURLToPath(new URL(`${root}/`, sourceUrl)))
10
+ : [...(process.resourcesPath ? [join(process.resourcesPath, 'packages/i18n')] : []),
11
+ resolve(process.cwd(), '../../packages/i18n'), resolve(process.cwd(), 'packages/i18n')];
12
+ let core = null;
13
+ for (const root of roots) {
14
+ try { core = require(join(root, 'index.cjs')); break; }
15
+ catch { /* Try the packaged copy next. */ }
16
+ }
17
+ const normalizeLanguage = core?.normalizeLanguage ?? ((value) => typeof value === 'string' && /^ja/i.test(value) ? 'ja' : 'en');
18
+ const getLanguage = core?.getLanguage ?? (() => normalizeLanguage(process.env.AKARI_LANG ?? Intl.DateTimeFormat().resolvedOptions().locale));
19
+
20
+ function catalog(language) {
21
+ for (const root of roots) {
22
+ try { return JSON.parse(readFileSync(join(root, 'locales', language, 'cli.json'), 'utf8')); }
23
+ catch { /* Try the packaged catalog next. */ }
24
+ }
25
+ return {};
26
+ }
27
+
28
+ const catalogs = { ja: catalog('ja'), en: catalog('en') };
29
+ export const cliLanguage = getLanguage;
30
+
31
+ export function cliText(key, params = {}, language = getLanguage()) {
32
+ const selectedLanguage = normalizeLanguage(language);
33
+ const chosen = catalogs[selectedLanguage];
34
+ const n = Number(params.n);
35
+ const suffix = Number.isFinite(n) ? new Intl.PluralRules(selectedLanguage).select(n) : null;
36
+ const requested = suffix && (Object.hasOwn(chosen, `${key}_${suffix}`) || Object.hasOwn(catalogs.en, `${key}_${suffix}`))
37
+ ? `${key}_${suffix}` : key;
38
+ const fallback = requested.endsWith('_one') ? `${key}_other` : requested;
39
+ const value = chosen[requested] ?? chosen[fallback] ?? catalogs.en[requested] ?? catalogs.en[fallback] ?? key;
40
+ return value.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => params[name] === undefined ? match : String(params[name]));
41
+ }
42
+
43
+ export function cliValue(prefix, value) {
44
+ if (getLanguage() === 'ja' || typeof value !== 'string') return value;
45
+ const entry = Object.entries(catalogs.ja).find(([key, source]) => key.startsWith(prefix) && source === value);
46
+ return entry ? cliText(entry[0]) : value;
47
+ }