[{"data":1,"prerenderedAt":390},["ShallowReactive",2],{"content-/eurekapu-i18n-with-elevenlabs":3,"all-pages-for-dir":388,"og-image-/eurekapu-i18n-with-elevenlabs":389},{"id":4,"title":5,"body":6,"category":369,"description":370,"extension":371,"meta":372,"navigation":373,"ogImage":374,"path":375,"project_name":376,"published":377,"publishedAt":378,"seo":379,"stem":380,"tags":381,"todo":386,"unpublished":377,"updatedAt":374,"__hash__":387},"pages/2026-05/2026-05-08/eurekapu-i18n-with-elevenlabs.md","Nuxt 3 簿記アプリを多言語化 — Codex 4thレビューと ElevenLabs 英語音声生成",{"type":7,"value":8,"toc":356},"minimark",[9,14,23,62,69,72,76,95,113,124,128,138,149,153,156,174,192,196,199,202,206,212,219,223,226,236,254,268,297,300,304,307,310,317,321,332,339,343,346,349,352],[10,11,13],"h2",{"id":12},"多言語化の計画書を-codex-に4回叩かれた","多言語化の計画書を Codex に4回叩かれた",[15,16,17,18,22],"p",{},"eurekapu-nuxt4 を ja/en の2言語に分ける計画を朝イチで書き始めた。",[19,20,21],"code",{},"memo/2026-05-08/cockpit-i18n-plan.md"," に下書きを置いて、すぐに Codex に投げた。",[24,25,30],"pre",{"className":26,"code":27,"language":28,"meta":29,"style":29},"language-bash shiki shiki-themes vitesse-light vitesse-light","codex exec -m gpt-5.5 \"このプランをレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: ...\"\n","bash","",[19,31,32],{"__ignoreMap":29},[33,34,37,41,45,49,52,56,59],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"senZ8","codex",[33,42,44],{"class":43},"sdGka"," exec",[33,46,48],{"class":47},"snbK4"," -m",[33,50,51],{"class":43}," gpt-5.5",[33,53,55],{"class":54},"sMJiu"," \"",[33,57,58],{"class":43},"このプランをレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: ...",[33,60,61],{"class":54},"\"\n",[15,63,64,65,68],{},"1stレビューでは「対象外ページに英語URLでアクセスしたときの挙動が定義されていない」「localePath の fallback がない」など、設計の穴を3つ突かれた。プランを直して ",[19,66,67],{},"resume --last"," で再レビュー。2nd では route name の生成方法が曖昧と指摘されて自動生成スクリプト案を追記。3rd で hreflang/canonical の SEO 配慮が抜けていると指摘され、useLocaleHead を組み込む方針に書き直した。4th でようやく「致命的な点はない」が返ってきた。",[15,70,71],{},"一発で OK が出るのを期待していたが、4回叩かれたおかげで Phase 1 の実装に入る前に設計の輪郭がはっきり見えた。レビュアーがいない一人開発で、Codex は無料で殴ってくれるリードとして機能する。",[10,73,75],{"id":74},"nuxtjsi18n-と-route-name-自動生成","@nuxtjs/i18n と route name 自動生成",[24,77,79],{"className":26,"code":78,"language":28,"meta":29,"style":29},"pnpm add -D @nuxtjs/i18n@9.5.6\n",[19,80,81],{"__ignoreMap":29},[33,82,83,86,89,92],{"class":35,"line":36},[33,84,85],{"class":39},"pnpm",[33,87,88],{"class":43}," add",[33,90,91],{"class":47}," -D",[33,93,94],{"class":43}," @nuxtjs/i18n@9.5.6\n",[15,96,97,100,101,104,105,108,109,112],{},[19,98,99],{},"nuxt.config.ts"," に i18n 設定を足した。strategy は ",[19,102,103],{},"prefix_except_default","、defaultLocale は ",[19,106,107],{},"ja","、対象外ページは ",[19,110,111],{},"/en/..."," でアクセスすると 404 を返す。",[15,114,115,116,119,120,123],{},"317ページ分のルート定義を手で書く気はなかった。",[19,117,118],{},"pages/"," を再帰的に走査して route name を吐くスクリプトを ",[19,121,122],{},"scripts/generate-i18n-routes.ts"," に置いた。1本走らせると pages.ts に対応表が降ってきた。手動で書いていたら半日溶けていた工程が、5分で終わった。",[10,125,127],{"id":126},"langswitcher-と-localepath","LangSwitcher と localePath",[15,129,130,133,134,137],{},[19,131,132],{},"components/LangSwitcher.vue"," を1つ作った。useLocaleHead で hreflang・canonical・og:locale を ",[19,135,136],{},"\u003Chead>"," に流し込む。SEO 都合で必要な3点セットが、コンポーザブル1つで埋まる。",[15,139,140,141,144,145,148],{},"リンクは全て ",[19,142,143],{},"localePath('/lessons/...')"," 経由に直した。対象外ページ（英語版を用意しないページ）にスイッチャーが向くと壊れるので、fallback で ",[19,146,147],{},"/lessons"," を出す分岐を入れた。",[10,150,152],{"id":151},"phase-1-が想定より早く終わったので全部いこう","Phase 1 が想定より早く終わったので「全部いこう」",[15,154,155],{},"午前中で Phase 1（i18n 基盤）が終わった。当初は Phase 2-4（コンテンツ本体の英訳）を別日に切る計画だったが、基盤の動きが想定通りすぎて止める理由がない。「全部いこう」と判断して、そのままコンテンツ本体に突っ込んだ。",[15,157,158,161,162,165,166,169,170,173],{},[19,159,160],{},"accountsMaster"," には optional な ",[19,163,164],{},"labelKey"," を追加して、",[19,167,168],{},"Account.name","（日本語）はそのまま残した。既存の日本語ロジックを壊さずに、表示だけ多言語化できる構造にした。BS / PL / CS / SS / JournalCard / JournalExample コンポーネントを順に開いて、テンプレート内の文字列を全部 ",[19,171,172],{},"t()"," で包んだ。",[15,175,176,177,180,181,184,185,180,188,191],{},"取引25件には ",[19,178,179],{},"titleKey"," と ",[19,182,183],{},"scenarioKey"," を追加。",[19,186,187],{},"ja.json",[19,189,190],{},"en.json"," に英訳を流し込んで、画面で en に切り替えると英語で取引タイトルが出るところまで通した。",[10,193,195],{"id":194},"ガイド29件-svg-22件-アニメーション-22件をサブエージェント並列で","ガイド29件 + SVG 22件 + アニメーション 22件をサブエージェント並列で",[15,197,198],{},"ガイドテキストとSVG内ラベルとアニメーションキャプションを全部1人で英訳すると、たぶん2日溶ける。サブエージェントを並列で6本立てて分担させた。プロンプトに「会計用語は IFRS 表記寄せ、Income Statement / Cash Flow Statement / Statement of Changes in Equity」を入れて統一した。",[15,200,201],{},"並列で走らせている間、メイン側で ElevenLabs の検証に取り掛かった。",[10,203,205],{"id":204},"elevenlabs-で-no01-の英語音声を試験生成","ElevenLabs で No.01 の英語音声を試験生成",[15,207,208,211],{},[19,209,210],{},".env"," に既に ElevenLabs の API キーは入れていた（VOICEVOX のずんだもん辞典作業のときに使ったやつ）。No.01（株式発行のシナリオ）のガイド音声4本だけを試験生成。706文字を消費して 853KB の mp3 が4本出てきた。",[15,213,214,215,218],{},"聞いてみると、数字の読み上げで違和感があった。",[19,216,217],{},"-30"," を「マイナスサーティ」と発音せず、ハイフンを無視してしまう。「ネガティブサーティ」と読んでしまう箇所もある。",[10,220,222],{"id":221},"preprocess-を3段階で拡充した","preprocess を3段階で拡充した",[15,224,225],{},"英語版「ユーザー辞書」の代わりに、API に渡す前のテキストに前処理を噛ませることにした。",[15,227,228,229,231,232,235],{},"最初は ",[19,230,217],{}," → ",[19,233,234],{}," minus 30"," の置換だけ。これで数字は通るようになった。",[15,237,238,239,242,243,246,247,249,250,253],{},"次に簿記特有の ",[19,240,241],{},"▲"," 記号で再びつまずいた。日本の財務諸表では金額のマイナスを ",[19,244,245],{},"▲100"," と書く慣習があり、英語で読み上げると無視される。",[19,248,241],{}," も ",[19,251,252],{},"minus"," に置換するルールを追加した。",[15,255,256,257,260,261,260,264,267],{},"最後にナレーションの正式表記の問題。日本語版では ",[19,258,259],{},"P/L"," ",[19,262,263],{},"CS",[19,265,266],{},"SS"," のような略号で書いていたが、英語ナレーションが「ピー・スラッシュ・エル」と読んでしまう。preprocess で以下を置換した。",[269,270,271,283,290],"ul",{},[272,273,274,277,278,231,280],"li",{},[19,275,276],{},"P&L"," / ",[19,279,259],{},[19,281,282],{},"Income Statement",[272,284,285,231,287],{},[19,286,263],{},[19,288,289],{},"Cash Flow Statement",[272,291,292,231,294],{},[19,293,266],{},[19,295,296],{},"Statement of Changes in Equity",[15,298,299],{},"3段階で拡充した結果、No.01 の音声を聴き直すと自然に通るようになった。",[10,301,303],{"id":302},"no02no09-を生成10問で打ち止め","No.02〜No.09 を生成。10問で打ち止め",[15,305,306],{},"No.01 で preprocess が安定したので、No.02 から No.09 まで一気に生成した。48ステップ分・8,245文字を消費して、累積 11MB になった。",[15,308,309],{},"ElevenLabs の月1万クレジットの上限を見ると、残りで全25取引は明らかに足りない。今月は10問までで打ち止めにする判断をした。残り15問は来月以降に分割する。",[15,311,312,313,316],{},"来月の自分が忘れないように ",[19,314,315],{},"memo/2026-06-01/"," ディレクトリを切って、英語音声続き対応のメモを置いた。積み残しを来月の頭に投げる運用は、今月の自分が来月の自分にチケットを切る感覚で意外と効く。",[10,318,320],{"id":319},"getguideaudiopath-を-locale-連動に","getGuideAudioPath を locale 連動に",[15,322,323,324,327,328,331],{},"最後に音声配信側の調整。",[19,325,326],{},"getGuideAudioPath()"," を ja/en で分岐するように直して、locale が en のときは ",[19,329,330],{},"audio-assets/en/..."," を返すようにした。",[15,333,334,335,338],{},"dev 配信ミドルウェアは元々 ",[19,336,337],{},"audio-assets"," ディレクトリを直接マップする実装だったので、変更不要だった。設計が綺麗だった過去の自分に感謝した。",[10,340,342],{"id":341},"_1日の振り返り","1日の振り返り",[15,344,345],{},"Codex に4回叩かれた朝、Phase 1 が早く終わって「全部いこう」と判断した昼、ElevenLabs の preprocess を3段階で詰めた夕方、来月の自分にチケットを切った夜。1日の中で4つのフェーズが流れた。",[15,347,348],{},"人間は方針判断（「Phase を一気にやる」「10問で打ち止める」）と、画面と耳でのチェック（音声を聴いて違和感を拾う）に集中した。残りの実装・英訳・スクリプト生成は全部 Claude Code とサブエージェントが回した。",[15,350,351],{},"英語版の続きは月初に再開する。",[353,354,355],"style",{},"html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":29,"searchDepth":357,"depth":357,"links":358},2,[359,360,361,362,363,364,365,366,367,368],{"id":12,"depth":357,"text":13},{"id":74,"depth":357,"text":75},{"id":126,"depth":357,"text":127},{"id":151,"depth":357,"text":152},{"id":194,"depth":357,"text":195},{"id":204,"depth":357,"text":205},{"id":221,"depth":357,"text":222},{"id":302,"depth":357,"text":303},{"id":319,"depth":357,"text":320},{"id":341,"depth":357,"text":342},"dev","eurekapu-nuxt4 に @nuxtjs/i18n を入れて317ページ分のルートを ja/en に分け、ElevenLabs で英語ガイド音声を10問まで生成した1日の作業ログ。Codex 再帰レビュー、Phase 一気実行、preprocess の拡充の経緯を記録。","md",{},true,null,"/eurekapu-i18n-with-elevenlabs","eurekapu-nuxt4",false,"2026-05-08T00:00:00.000Z",{"title":5,"description":370},"2026-05/2026-05-08/eurekapu-i18n-with-elevenlabs",[376,382,383,384,385],"i18n","Nuxt","ElevenLabs","Codex","memo","nfCpP_g3qswDR_TNIIZUgEiHsojfa2oCZTP4UvE0fMg",[],"https://log.eurekapu.com/og/blog/eurekapu-i18n-with-elevenlabs.png?v=2026-05-08T00%3A00%3A00.000Z&title=Nuxt%203%20%E7%B0%BF%E8%A8%98%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E5%A4%9A%E8%A8%80%E8%AA%9E%E5%8C%96%20%E2%80%94%20Codex%204th%E3%83%AC%E3%83%93%E3%83%A5%E3%83%BC%E3%81%A8%20ElevenLabs%20%E8%8B%B1%E8%AA%9E%E9%9F%B3%E5%A3%B0%E7%94%9F%E6%88%90&author=Kei%20Komatsu&sig=ca471243992872f3",1782528834025]