※この記事は はてなエンジニア Advent Calendar 2025 の1月13日の記事です。
tunacook/vscode-naninovel-distiller というVSCodeの拡張機能を作ってみた。
前々から作らないとなと思っていたが、VSCodeの拡張機能なんて作るの初めてだったので、色々面白かった。
作った理由
Naninovel製インディゲームのローカライズする時に予算感の見積もりをするのに役立ちそうだから!
今NaninovelというUnityのアセットを使ってノベルゲームを作っている。
インディゲームのローカライズ費用は、だいたいワード毎や文字数毎での費用決定が多くて、実際にどのぐらいの予算感になるのかをざっくりとでも(正確じゃなくても)知ることができるので便利そうだと思ったから。
これはローカライズする会社や翻訳者にもよるけど、 日本語->他言語 の場合は文字数、英語->他言語の場合はワード数で見ることが多そうなイメージがあるのだった。
Naninovelのシナリオファイルにはシナリオ文章以外にも、
- スクリプト構文(
@から始まるAPI) Commands • Naninovel - コメント構文(
;から始まる文字列) https://naninovel.com/guide/naninovel-scripts#comment-lines - ルビ等インラインの構文
<ruby="ルビの内容">テキスト</ruby>
のようなことを書かねばならず、純粋な文字数カウントだとノイズが多くて正確なシナリオ文字数が分からなかった。
また、テキストエディタのカウント方式によっては改行も1文字にカウントされる(改行文字を改行文字としてカウントしている)場合もあるため、その辺の揺らぎも排除したかった。
できること
文字数のカウント
純粋なシナリオの文字数だけをカウントする。 Naninovelのスクリプト構文やコメント構文などは除外。

この時、話者のラベルはカウントに含めない。
ワード数のカウント
同じようにワードを検出してその数を数える。
日本語のワード検出があまり精度高くないのだけど、今のところローカライズ見積もりにおいては重要じゃないのでそのままになっている。

話者のリストアップ
シナリオ中のセリフは
話す人: こんにちは!
みたいな構文で表現するのだけど、話す人:のところをユニークにリストアップできるようにした

Naninovelのシナリオ書式仕様を表現するライブラリも作った
昨年のアドベントカレンダーで、Textmesh proの豆腐を検出するGithub Actionsを作ったという話を書いた。
その時に作ったNaninovelシナリオ構文のパースができる処理を、独立したライブラリにしておいた。
今回も、もちろん内部的な構文判定をこれで行っている。 こちらも安定してきたらバージョニングしたいわね。。
VSCode拡張機能の実装は案外簡単かも
VSCode拡張機能の実装を初めてやったけど、結構簡単にできそうだという感触があった。
pnpm install -g yo generator-code
でジェネレータを入れて、
yo code
で対話的に作っていけばひな形を作ることができる。
VSCodeでプロジェクトを開いて、F5キーでビルドすると、該当の拡張機能が入った状態で新しくVSCodeのウィンドウが開く。そのウィンドウで見たいファイルを開いて、その拡張機能がどのように適用されるかを見ていけば良い。
CIも作った
typescriptのプロダクトのCIは業務でもたくさん作ってきたのだけど、WebアプリケーションやライブラリじゃなくVSCode拡張のCIは書いたことがなかった。
pnpm run test ではあるんだけども(今回yarnじゃなくpnpmを使ってみてる)、VSCodeテスト実行に必要なモジュールをインストールする必要があって、ちょっと新鮮な部分があった
今後
色々まだ要件を満たしていないところもあるので改善していきたい
- 話者ラベルリストはその文字数もカウントするようにする
- 日本語でのワード検出の精度をある程度高める(ローカライズにおいてはそんなに必要なさそうだけど、どうせなら高めたい)
- すでにローカライズのためのハッシュ管理されている場合はそれを見ないようにする
- シナリオ校正のために印刷する用として、スクリプト構文やコメントを除いてPDF出力できるようにする
- ルビは構文をパースしてゲーム側のように文字上に小さく乗せられるようにする
この辺をどうにかしつつ、個人で開発しているノベルゲームでドッグフーディングをして、ある程度できたらMarketplaceで公開したいと思う。
そのうちCDも作りたいけど、デプロイは自動化しなくても良いかも?