【Minecraft】Radical Cobblemon Trainersの日本語化リソースパックを作る
Table of Contents
Radical Cobblemon Trainersの日本語化リソースパックを作った話
概要
最近Cobblemonにハマっていて、その中のside modであるRadical Cobblemon Trainers(以後、RCTと呼ぶ)の日本語化リソースパック&データパックを作った。
Cobblemon is 何
Cobblemonとは、簡単に言うとマイクラの世界をポケモンの世界に変えるMod。似たModにPixelmonがあるが、 こちらは完全にポケモンの世界と置き換えるコンセプトなのに対し、Cobblemonはマイクラの標準の機能は維持しつつ、ポケモンの世界観をいい感じにフュージョンする感じのModだ。
RCTはそのCobblemonの世界にNPCのトレーナーを追加するMod。
やっていく
ModrinthからRCTをダウンロードすると、以下のような構成になっている。
.
|-- assets
| `-- rctmod
| `-- lang
| `-- en_us.json
`-- data
`-- rctmod
`-- trainers
|-- ace_trainer_abel_04a5.json
|-- ace_trainer_alexa_0194.json
`-- ...lang配下にトレーナーのセリフ。trainers配下にそれぞれのトレーナーのconfig(名前とか)が入っているので、これらを頑張って翻訳していく。
langのほうは雑にAIに翻訳を投げて、ja_jp.jsonを生成した。一部ポケモン由来の固有名詞とかはいい感じにwebを参照させて、名前のマッピングを行った。しっかりチェックしてないのでもしかしたら誤訳があるかも。ただ意味はある程度通じるレベルにはなってると思う。
trainersのほうが面倒で、それぞれのtrainerファイルは以下のような構造になっている。
{
"name": "Fisherman Andrew",
"team": [
{
"species": "froslass",
"gender": "FEMALE",
"level": 88,
"nature": "timid",
"ability": "snowcloak",
"moveset": [
"auroraveil",
"blizzard",
"shadowball",
"freezedry"
],
"ivs": {
"hp": 31,
"atk": 31,
"def": 31,
"spa": 31,
"spd": 31,
"spe": 31
},
...
肩書+名前みたいな感じ。はじめは名前を全て日本語名にマッピングしようと思ったが、ポケモンWikiを見ても、見つからない名前とかがあるので全部マッピングするのは諦めた。
アプローチとしては、各trainersファイルからnameの箇所を抜き出し、肩書き名+名前に分解。肩書き名のマッピングを記載したtitle_map.jsonに該当の肩書きがあれば書き換え、そうでなければskipする。
title_map.jsonを作る前準備として肩書き一覧は事前に以下のように、jqで抜いておく。
jq -r '
def extract_title:
split(" ")
| .[0:-1]
| join(" ");
if .identity? then
.identity | extract_title
elif (.name | test("&| and ")) | not then
.name | extract_title
else
empty
end
' *.json | sort -u > title_candidates.txtすると、以下のようなファイルができるのでこれをAIに投げていい感じの翻訳をつける。
Ace Trainer
Aroma Lady
Artist
Battle Girl
Beauty
Biker
Bird Keeper
Black Belt
...目視で確認して、イケてない訳は手動で修正する。
{
"Ace Trainer": "エーストレーナー",
"Aroma Lady": "アロマなおねえさん",
"Artist": "げいじゅつか",
"Battle Girl": "バトルガール",
"Beauty": "おとなのおねえさん",
"Biker": "ぼうそうぞく",
"Bird Keeper": "とりつかい",
"Black Belt": "からておう",
... 後はこのファイルをもとに、trainers配下のjsonファイルの肩書き部分を置き換える。
...
def localize_name(original_name: str, trainer: dict) -> str | None:
# ---- 固有名 完全一致 ----
if original_name in PROPER_NAME_MAP:
return PROPER_NAME_MAP[original_name]
# ---- 正規化(Swimmer♂ 等)----
norm_title, norm_gender, normalized = normalize_name(original_name) # 後述
# ---- title 決定 ----
if norm_title:
title = norm_title
rest = normalized
else:
tokens = original_name.split()
title = None
rest = None
for i in range(len(tokens), 0, -1):
candidate = " ".join(tokens[:i])
if candidate in TITLE_MAP:
title = candidate
rest = " ".join(tokens[i:])
break
if not title:
print(f"[skip] title not found: {original_name}")
return None
title_entry = TITLE_MAP.get(title)
...
一部、肩書きのみでは訳を当てられないパターンがあることに後から気づいた。たとえばSwimmerなどはgenderを見ないとだめ。
{
"name": "Swimmer Evan",
"team": [
{
"species": "golduck",
"gender": "FEMALE",
そのため、title_mapのほうで以下のように定義しておき、
"Swimmer": {
"FEMALE": "ビキニのおねえさん",
"MALE": "かいパンやろう"
},性別で分岐するようにした。
...
def normalize_name(name: str):
"""
Swimmer♂ David -> ("Swimmer", "MALE", "David")
Tuber♀ Alice -> ("Tuber", "FEMALE", "Alice")
"""
m = re.match(r"^(Swimmer|Tuber)(♂|♀)\s+(.*)$", name)
if m:
title, mark, rest = m.groups()
gender = "MALE" if mark == "♂" else "FEMALE"
return title, gender, rest
return None, None, name
def get_trainer_gender(trainer: dict):
for p in trainer.get("team", []):
g = p.get("gender")
if g in ("MALE", "FEMALE"):
return g
return None
def localize_name(original_name: str, trainer: dict) -> str | None:
# ---- 固有名 完全一致 ----
if original_name in PROPER_NAME_MAP:
return PROPER_NAME_MAP[original_name]
# ---- 正規化(Swimmer♂ 等)----
norm_title, norm_gender, normalized = normalize_name(original_name)
# ---- title 決定 ----
if norm_title:
title = norm_title
rest = normalized
else:
tokens = original_name.split()
title = None
rest = None
for i in range(len(tokens), 0, -1):
candidate = " ".join(tokens[:i])
if candidate in TITLE_MAP:
title = candidate
rest = " ".join(tokens[i:])
break
if not title:
print(f"[skip] title not found: {original_name}")
return None
title_entry = TITLE_MAP.get(title)
# ---- 性別分岐 ----
if isinstance(title_entry, dict):
gender = norm_gender or get_trainer_gender(trainer)
if not gender or gender not in title_entry:
print(f"[skip] gender unknown: {original_name}")
return None
jp_title = title_entry[gender]
else:
jp_title = title_entry
...
あと、以下のようなユニークキャラは肩書きがないので別途名前をマッピングしたファイルを用意することにした。カスミって英語だとMistyなんだ。
{
"name": "Misty",
"identity": "Leader Misty",
...{
"Brock": "タケシ",
"Misty": "カスミ",
"Lt. Surge": "マチス",
...localize.py実行時、マッピングしたファイルに名前が見つからない場合は以下のようにskipと表示されるので、Mod本体のバージョンが上がっても、見つからない肩書きだけtitle_map.jsonに追加した後、localize.pyを実行するだけでリソースパックが作れる状態にできた。
[skip] title not found: Youngster Chad
導入方法
以下で公開してます。

Releases · 4nm1tsu/radical-cobblemon-trainers-ja
対象バージョンのパック(zipファイル)をダウンロードしたら、.minecraft/resourcepacksフォルダに入れた後、ゲーム内でリソースパックを有効化してください。
また、ワールドのdatapacksにも同じzipファイルを導入後、/datapack enableコマンドで有効化してください。/reloadコマンドでin-gameでの読み込みが可能です。
誤訳があったらIssueで知らせてください。
以上。