# Lowmoor 日本語化キット

『**Lowmoor Idle RPG: An Adventurer's Chronicle**』（作者: Bogurodzic）を日本語で遊ぶためのキットです。

**対応バージョン: 1.6.3**（2026-08-26 配信）。**画面上の文字の約 91%**（実測）が日本語になります。

> ### 1.6.3 でゲームのコードが難読化されました
>
> **この版から、ゲーム側の変数名・関数名がすべて 1〜2 文字に潰されました**
> （`index.html` は 43,311 行 → 2,895 行の 1 行構成に）。訳文の差し替えそのものは
> 影響を受けませんが、**日本語の語順へ組み替える処理は名前を目印にしていたので全滅**し、
> 作り直しています。
>
> あわせて、走査器が正規表現リテラルを飛ばせずに位置がずれる不具合を直しました。
> `/["']/.test(x)` の中の `"` を文字列の始まりと読んでしまい、そこから先が全部ずれて、
> **コードの断片を文字列だと思って訳して**いました。この修正だけで、
> 当たる訳文が **8,014 件 → 9,552 件**に増えています。
>
> 日本語比率が 93% → **91%** と下がっているのは、訳が減ったのではなく
> **ゲーム側の文章が増えた**ためです。画面に残る英語は **17件**で、すべて
> 「英語のままが正しい」もの（通貨単位 gp、デスクの意匠名、ライセンス表記）です。
>
> ### 訳文以外を書き換えていないかを機械で確かめました
>
> 英語の原版と日本語版を突き合わせる `tools/systemcheck.mjs` を新しく入れました。
> localStorage の鍵、`getElementById` の名前、`classList` の操作、`addEventListener` の種別、
> Web Audio と canvas の列挙値、`fetch` の宛先、セーブのスキーマ番号（v17）――
> **いずれも英語版と完全に一致**し、消えたものは 0 件でした。
>
> 実行時の確認も通しています。未捕捉例外 0 / ゲージ 23 本すべて正常 /
> 文字化け 0 / 音は英語版と一致（ノード 78・start 34 回）/ セーブコードの往復 OK。
>
> **1.6.1 以前を当てていた方へ**: 1.6.1 の初版には音が全く鳴らなくなる不具合があり、
> 2026-08-24 に修正済みです。このキットで入れ直せば直ります（セーブには影響しません）。
>
> 詳しくは `記録/CHANGELOG-1.6.3.md`、その前は `記録/CHANGELOG-1.6.1.md` と
> `記録/CHANGELOG-1.6.0.md` を見てください。

配布元: web-autoai.com

---

## 不具合・未翻訳の報告はこちらへ

作者の Bogurodzic 氏から許可をいただいて、**Steam のディスカッションに日本語のスレッド**を立てました。

    【日本語化】Lowmoor 日本語化キット公開しました（ver1.6.3対応）
    https://steamcommunity.com/app/4977360/discussions/0/587309829716975351/

訳のおかしいところ、まだ英語で残っている画面、字体を変えたときの表示崩れ、
適用スクリプトのエラー、「この訳語はこうしたほうがいい」という提案——どれも歓迎します。
**日本語で構いません。** Steam のアカウントがあれば誰でも書き込めます。

手を動かして直せる人は、そのまま `tm.json` を書き換えてください（「翻訳に参加する.md」）。
そこまでではないけれど気になる、という程度でも、スレッドに書いてもらえるだけで十分助かります。

---

## いちばん短い手順

1. **Node.js 18 以上**を入れる → https://nodejs.org （LTS でOK）
2. Steam で Lowmoor を**終了**する
3. `適用する.ps1` を右クリック →「**PowerShell で実行**」
4. 終わり

スクリプトが Steam のライブラリから Lowmoor を自動で探し、
あなたの `app.asar` を展開 → 翻訳を適用 → 詰め直す、までを全部やります。
元の `app.asar` は `app.asar.en.bak` として同じ場所に残ります。

英語に戻したいときは `元に戻す.ps1` を実行してください。

> **PowerShell がスクリプトを拒否する場合**
> 「このシステムではスクリプトの実行が無効になっています」と出たら、
> PowerShell を開いて次を実行してから、もう一度やってください。
> ```powershell
> Set-ExecutionPolicy -Scope Process -Bypass
> ```
> （この窓を閉じるまでの一時的な設定です。PC全体の設定は変わりません。）

---

## ゲーム本体は入っていません

このキットに入っているのは**翻訳データとスクリプトだけ**です。
ゲームのファイルは1バイトも含まれていません。

適用スクリプトは、**あなたが持っている正規のゲーム**を読み、その場で日本語版を作ります。
そのため Lowmoor を持っていない人がこのキットだけで遊ぶことはできません。
Lowmoor は Steam と itch.io で**基本プレイ無料**です。まず本体を入手してください。

この形にしているのは、作者の著作物を勝手に再配布しないためです。
翻訳データはこちらのものなので自由に使えますが、ゲームは作者のものです。

---

## 何が日本語になって、何が英語のまま残るか

**日本語になるところ**

- 入会画面（種族12 / 職業12 / 出自の全項目、能力値、ボタン）
- メインのプレイ画面（パネル見出し、装備部位、章立て、在庫、負担、雇い人）
- **戦闘ログ**（討伐・拾得・売却・功績。日本語の語順に組み替えてあります）
- **モンスター名179種すべて**（TRPGの慣用表記に手で当て直し）
- モンスター図鑑の解説文、実績、トロフィーケース、統計
- ギルドショップ、ギルドハウス、名誉録、技能ツリー、設定、ヘルプ
- 幕クリアの長文、イベントの選択肢、噂、環境描写、ボス演出、掲示板の依頼

**英語のまま残るところ**（意図的に残しています）

| 内容 | 理由 |
|---|---|
| 冒険者名・雇い人名 | 固有名詞。訳さないほうが読みやすい |
| 作品名 `Lowmoor` | 同上 |
| ログのごく一部 | JavaScript の式と地の文が同じ文字列に同居しており、訳すと式が壊れる |
| Muster（オンライン催事）のダイアログ本文 | 同上 |

残る英字は全体で **1,258 字**です（日本語 16,117 字に対して）。

これは「機械翻訳が失敗した」のではなく、**壊れた日本語を出すくらいなら英語のまま残す**という
方針でそうしています（`patch.mjs` が適用前に検証し、落ちたものは差し戻しません）。

ここを潰す作業に興味がある方は「**翻訳に参加する.md**」を読んでください。
`out-fragment-unsafe.json` に該当の980件が全部入っています。

---

## 動作検証（実行して確かめてあります）

Chrome を CDP で操作し、例外と `console.error` を全部拾いながら通しでプレイさせた結果です。

| 項目 | 結果 |
|---|---|
| 未捕捉例外 | **0件** |
| console.error / warning | **0件** |
| LINT（ブラウザAPIの引数に日本語が立っていないか） | **clean** |
| SYNTAX CHECK | **OK** |
| 早送り6時間（実エンジンを `fastForward`） | 第8階層 / ランク13 / 2,588 kill / 64,179 ゴールド |
| 文字化け（U+FFFD） | 0 |
| テンプレート式（`${`）の画面露出 | 0 |
| HTMLエンティティの混入 | 0 |
| セーブコードの往復 | OK（スキーマ **v17**、復号成功） |
| 各ウィンドウの開閉 | 設定・獣譜・実績・統計・技能ツリー・商店・ギルドハウス すべてOK |
| **画面上の日本語比率** | **91%** |
| 画面に残る英語 | **17件**（すべて意図的） |
| 翻訳メモリ | **9,458件** |
| 1.6.0 で新規に訳した分 | **2,012件 / 54,661文字**（約25分） |
| 1.6.3 で新規に訳した分 | **328件 / 9,013文字** |
| 残る未訳 | **59件**（JWT・CSSセレクタ等、訳してはいけないもの） |
| 複数形の `s` の残り | **0件** |
| ゲージの塗り（描画結果を実測） | 23本すべて 不正なクリップ 0 / 幅ゼロ 0 |
| **API列挙値の日本語混入**（`apienum.mjs`） | **0件** |
| **音の再生**（`audiotest.mjs` で実測） | **audioLive=true / ノード 4 / start 2** |

差し替え用の `app.asar` はオリジナルと全ファイルを突き合わせてあり、
**`game/index.html` 以外はバイト単位で同一**です。

検証に使ったスクリプトは `tools/cdptest.mjs` として同梱しています。
訳に手を入れたあと、同じやり方で自分の環境でも確かめられます（Node 22 以上が必要）。

### 最初のビルドは必ずクラッシュしました

参考までに、この検証で見つかって直った実バグを書いておきます。典型的なローカライズ事故でした。

```
TypeError: Cannot read properties of undefined (reading 'length')
  at pick -> assizeSpend -> makeEquipName -> gainEquip -> completeTask -> tick
```

`Shield` や `Helm` といった装備部位名が、表示用の文字列リテラル `["Weapon","Shield",...]` と、
データ表の**クォートなしオブジェクトキー** `ARMOR_BASES = {Shield:[...]}` の両方で使われていて、
片方だけ日本語になった結果 `ARMOR_BASES["盾"]` が `undefined` になり、
装備をドロップした瞬間に落ちていました。

`patch.mjs` に対策が2つ入っています。

1. **クォートなしキーの追随** — 訳した語が識別子としてキーに使われている箇所を検出し、
   `{Shield:` → `{"盾":` と同時に書き換える（27箇所を自動修正）
2. **プロパティ名の保護** — `LEGACY.mods.open` のようにドット記法で読まれている名前と同じ
   文字列リテラルは、書き換えようがないので**最初から訳さない**（150件を英語のまま保護）

**短い英単語1語を訳語に足すときは、この事故に注意してください。**

この種の事故はほかにも2件ありました。どちらも「構文としては正しいので構文チェックを通り、
実行して初めて落ちる」種類です。

- `scrollIntoView({block:"nearest"})` の `"nearest"` を訳してしまい、階層表示の更新のたびに例外
- `querySelectorAll("canvas[data-plate]")` の `plate` が訳され、CSSセレクタが壊れた

そこで **出力を読み直して、ブラウザAPIの引数位置に日本語が立っていないかを検査する lint**（10種）を
`patch.mjs` に追加しました。訳してはいけない文字列を弾く門も3つあります
（`identifier-property` / `browser-api-value` / `internal-key`）。

詳しくは「翻訳に参加する.md」へ。

---

## 訳の品質について正直なところ

ローカルLLM（PLaMo）＋用語集107語による通し翻訳に、**手訳1,015件**（モンスター名179種、討伐動詞30種、希少度・品質・素材・武具の部位名、種族/職業/出自の全36項目ほか）と、
日本語の語順への組み替え規則を重ねたものです。
**「読める」水準であって「作品として仕上がっている」水準ではありません。**

本作の英文は1981年頃のTRPGモジュールを模した古語調で、その文体こそが作品の核です。
「クリティカル率+3%」ではなく "+3% clean strokes"（+3%の清き一撃）と書く、あの調子です。
そこは機械翻訳では出ません。

だからこそ**手を入れてくれる人を歓迎します**。`tools/tm.json` は原文をキーにしたただのJSONで、
書き換えて `node build-ja.mjs` を走らせるだけで反映されます。
詳しくは「翻訳に参加する.md」へ。

---

## 字体を7種類から選べます

設定ウィンドウの「可読性」に**「字体」**という行が増えています。画面全体の日本語の書体を、
7種類から選べます。選んだものはこの機械にだけ憶えます（**セーブには入りません**。
書体は機械ごとの好みであって、冒険者の記録ではないからです）。

| 選択肢 | 出どころ | 本文の大きさ |
|---|---|---|
| MS Pゴシック（既定） | Windows 同梱 | 11px |
| MS P明朝 | Windows 同梱 | 11px |
| BIZ UD P明朝 | Windows 同梱 | 12px |
| UDデジタル教科書体 | Windows 同梱 | 12px |
| Zen Old Mincho（古書） | **キットに同梱** | 12px |
| Klee One（手書き） | **キットに同梱** | 12px |
| DotGothic16（ドット・要広窓） | **キットに同梱** | 16px |

**書体ごとに適正な大きさが違うので、字体と一緒に大きさも切り替わります。**
特に DotGothic16 は設計サイズが 16px で、12px に落とすと点が潰れてただのゴシックに見えます。
そのため 16px のままにしてあり、**窓が狭いと収まりません**（1920幅なら溢れません）。

下の3書体は SIL OFL のフォントを、**使う文字だけに絞って `index.html` の中へ埋め込んで**います。
Webフォントとして外から取りに来るのではないので、
**ゲームの「ネットワークへ接続しない」という約束はそのまま保たれます。**
別途フォントを入れておく必要もありません。ライセンスは `fonts/` を見てください。

---

## ゲームが更新されたら

Steam が Lowmoor を更新すると、日本語版の `app.asar` は英語版で上書きされます。
**増えた文だけを訳し直して作り直す**手順を `更新の手順.md` にまとめました。
既訳は再利用されるので、たいていは数分で終わります。

```powershell
node update.mjs            # 差分を数えるだけ（訳さない）
node update.mjs --run --pack   # 未訳分だけ訳して、ビルド・検査・詰め直しまで
```

**更新されたかどうかだけ知りたいとき**は、ゲームを落とさずに Steam の depot 情報を引けます。

```sh
node steam-size.mjs
```

ビルドIDと更新時刻、インストール／ダウンロードのサイズが出ます。
控えておいたビルドIDと違っていれば更新されています。

**未訳が60件前後残るのは正常です。** JWT や CSS セレクタなど、訳してはいけないものが
毎回この数だけ残ります。

---

## ⚠ セーブデータについて（いちばん重要な注意）

**日本語版で始めた年代記は、日本語版で続けてください。**

セーブファイルそのものには手を触れておらず、形式もスキーマ番号（v13）も変えていないので
読み書きは問題なくできます。しかし**セーブの中身には、種族・職業・装備部位・獣の名前が
「そのまま値として」入ります。** 日本語版で作った年代記は内部値が日本語になるため、
**英語版に戻すと装備や種族の参照が外れます。**

具体的に守ってほしいこと:

- 日本語版で始めた年代記は日本語版で続ける
- **Steam がゲームを更新して英語に戻ったら、その年代記を再開する前に `適用する.ps1` を再実行する**
- 英語版で作った既存の年代記を日本語版で開くのも、同じ理由で避ける（新規に始めるのが安全）
- 版を切り替える前に `Export Save` でコードを控えておく。どちらの版でも取れます

これを根本的に解消するには、書き出し／読み込みのときに内部値を英日で変換する層が必要です
（`overrides.json` の逆引きで実装できます）。**次に手を入れるなら、ここが最有力の課題です。**

そのほか:

- セーブの置き場所は `%APPDATA%\lowmoor-desktop` と Steam Cloud の `chronicle.save`
- Steam がゲームを更新すると `app.asar` は上書きされ、英語に戻ります。`適用する.ps1` を再実行してください

---

## 中身

```
はじめにお読みください.md    このファイル
利用条件.md                  改変・再配布・禁止事項
翻訳に参加する.md            訳を良くする手順（AIの使い方を含む）
適用する.ps1                 Steam版に適用（全自動）
元に戻す.ps1                 英語版に戻す
tools/
  build-ja.mjs               ビルド一括実行（これだけ覚えれば足ります）
  patch.mjs                  訳文の差し戻し・語順の組み替え・検証・lint・構文チェック
  extract.mjs                JSの文字列リテラルを抽出・分類
  extract2.mjs               分類を作り直した版（取りこぼしを潰したもの）
  extract3.mjs               さらに絞り込んだ版
  extract-html.mjs           静的HTML側のテキストと属性を抽出
  gaps.mjs                   まだ英語で残っている箇所を洗い出す
  repair-tm.mjs              DeepLが落とした/足したタグを原文に合わせて直す
  translate.mjs              DeepL API 呼び出し（訳を増やすときだけ使う）
  cdptest.mjs                動作検証。Chrome を叩いて通しでプレイさせ、例外とゲージの塗りを測る
  apienum.mjs                ブラウザAPIの綴りに日本語が混ざっていないか調べる（build-ja が自動で呼ぶ）
  audiotest.mjs              音が鳴りに行っているかを実測する（Chrome が要ります）
  fontpatch.mjs              設定に「字体」の切替を足し、woff2 を埋め込む
  fontload.mjs / fonttest.mjs / fontpreview.mjs
                             字体の読み込み確認・表示検査・見本の書き出し
  lexicon.mjs                機構説明の語彙表を切り出して、画面に出る文言を再現する
  giftcheck.mjs              種族・職業・出自の贈り物の説明文を点検する
  translate-plamo.mjs        ローカルLLM(PLaMo)での通し翻訳。用語を伏せ字にして訳語を差し戻す
  check-tags.mjs             連結断片のタグ位置がずれていないか調べる
  pair-fragments.mjs         前後の断片を対にして取り出す（タグ位置の手直し用）
  tagfix.mjs                 対にした断片の差し替え
  audit-tm.mjs               訳語の一貫性・文体を集計する
  scan-css.mjs               CSSの値や単位の形をした文字列を洗い出す
  scan-sentinels.mjs         伏せ字が残っていないか調べる
  retry-warned.mjs           警告の出た訳だけ訳し直す
  tm.json                    翻訳メモリ 9,458件
  overrides.json             手訳の上書き層 1,015件。tm.json より優先
  monsters-ja.json           モンスター名199種の手訳（1.6.0 の入替20種を含む）
  glossary.tsv               用語集 107語
  out-fragment-unsafe.json   未訳のまま残した980件
  out-label-skip.json        分類上スキップした995件
  パイプライン解説.md        各スクリプトの詳細
fonts/
  *.woff2                    同梱する字体3種（使う文字だけに絞ったもの）
  OFL-*.txt                  それぞれのライセンス（SIL OFL 1.1）
  subset.py / used-chars.txt 絞り込みの再実行用
  お読みください.md          字体の出どころとライセンスの説明
記録/
  実施報告.md                何をどうやったかの全記録
  翻訳の残り課題.md          まだ直っていない訳の一覧
```
