Localizing an RPG Maker MV/MZ game: where the text hides
You built a game in RPG Maker and now you want it in another language. The good news is that MV and MZ store their project data as plain JSON, which means every line of text you wrote is sitting in readable files you can process with a script rather than locked inside a binary you cannot open. The bad news is that the engine never asked you to keep text in one place, so it is spread across a dozen files, several kinds of container, and a few places that do not look like text at all.
That scattering is the actual difficulty. Translation itself is a known quantity; finding every string, getting it out in an order a translator can work with, and getting it back in without corrupting your project is where RPG Maker projects go wrong. It is also why the number of lines you think you have is usually lower than the number you actually have.
This article maps where the text lives, describes a round trip that is safe to repeat, and covers the things that break most often: control codes, the message window, and fonts. It deliberately does not tell you which plugin to use — that landscape changes, and the way to choose one is with a checklist, which is near the end.
Where the text actually lives
Open your project's data folder and you will find the whole database as JSON. Each file holds one kind of record, and player-facing text is mixed in with numbers and flags rather than kept separately. The list below is the inventory to work from — anything not on it is a string that will still be in your source language on release day.
- Database records — names, descriptions, and messages on items, skills, states, actors, classes, and enemies
- System.json — the game title, the currency unit, and the interface terms (command names, parameter labels, and the stock system messages)
- Map files — every event on that map, with its dialogue and choices stored inside the event's command list
- CommonEvents.json and Troops.json — text in common events and in battle event pages, which is easy to forget because you rarely browse those
- js/plugins.js — plugin parameters, including any player-facing wording you typed into the plugin manager
- img/ — titles, logos, signs, and any picture with text drawn into it, which is not text at all as far as tooling is concerned
data/ Actors.json Classes.json Skills.json Items.json Weapons.json Armors.json Enemies.json States.json System.json CommonEvents.json Troops.json Map001.json Map002.json ...
Event text is a list of commands, and the round trip has to survive it
Inside a map file, an event page holds an ordered list of commands, each with a numeric code and a parameters array. A message is not one block of text — it is a setup command followed by one command per displayed line, and choices are a different command with its own array of options. That structure is what your extractor has to walk.
Rather than trusting a list of command codes from an article, find them empirically: put a distinctive test line into an event, save, and search the map file for it. The command that holds your line is the one you need, and doing it this way also confirms your understanding of the version you are actually on. Do the same for choices and for scrolling text, since they are stored differently.
One warning that costs people whole afternoons: the editor keeps the project in memory and writes the data files when it saves. If you edit the JSON by hand or by script while the project is open in the editor, the next save overwrites your changes without asking. Close the editor before running any import, and keep the project in version control so you can see exactly what your script changed.
The temptation with the export itself is to do it once, translate, and paste everything back. That works exactly one time. Since your game will keep changing, what you want is a repeatable export and import: a script that walks the data files and writes a table of every translatable string with a stable identifier, plus a matching script that reads a translated table back in.
The identifier is the part worth designing. Do not use the source text as the key — the day you fix a typo in the source, every translation attached to it is orphaned. Build the key from the location instead: which file, which record or event, which field, which line. That key stays valid when the wording changes, which is what lets you re-export and see the difference as a small set of new and changed rows rather than a completely different file.
key,ja,en system.terms.command.attack,攻撃,Attack item.0007.name,ポーション,Potion item.0007.desc,HPを 500 回復する。,Restores 500 HP. map001.ev003.p1.text.01,\N[1]は 宝箱を 開けた!,\N[1] opens the chest! common.0012.choice.02,やめておく,Not right now
Control codes are not text
RPG Maker messages contain escape sequences that the engine interprets at display time rather than printing. A translator who does not know that will helpfully translate them, delete them, or re-space them, and the result is either a literal backslash on screen or an error nobody sees until that scene plays.
The codes that insert a name or a number deserve particular attention, because they are holes in the sentence, and languages disagree about what can be built around a hole. Japanese attaches particles after an inserted name, so a line reads naturally with the code almost anywhere. English needs the name in a position the grammar allows, and needs to know whether what follows should be singular or plural. A line written to flow around an inserted value in one language often cannot be made to flow in another without rewriting the sentence.
So give the translator permission to move a code within its line, and tell them what each one will actually contain — a party member's name, an item name, a number that could be one or many. In your export, a context column saying 'variable 12 is the number of keys the player is carrying' turns a guess into a translation.
Tell them just as explicitly that the codes themselves must survive unchanged: same spelling, same index, same count. Moving one is fine. Renaming, deleting, or duplicating one is not, because the engine matches them exactly. This is the single most common class of bug in a translated RPG Maker project, and it is entirely mechanical — which means it can be checked mechanically rather than by eye.
One presentation detail: inside the JSON files these appear with the backslash escaped, so what you read in the data file and what the engine displays are not spelled identically. Whatever table you hand to a translator should show them in the form the translator has to type back, and your import script owns the conversion. The engine's own help documents the full set; below are the ones you will meet constantly.
\V[n] inserts the value of a game variable \N[n] inserts an actor's name \C[n] changes text colour from here on \I[n] draws an icon \. \| \! timing: short wait, longer wait, wait for input
What breaks on screen: window size and fonts
The default message window shows a fixed number of lines — four, unless something in your project changes it — and a fixed width. That is a hard limit on how much a single message command can display, and it is where most translations of Japanese-authored games run into trouble: the same meaning frequently needs more characters in a European language than it did in Japanese, so a message that filled the window exactly now overflows it.
The fix is usually to split one message into two, and that is an important detail for planning, because splitting a message is an edit to the event's command list, not to a string. A translator working in a spreadsheet cannot do it. Someone has to open the project, or your import script has to support splitting, or you accept that a pass of manual event editing is part of the job. Decide which before translation starts, not after. Choices, the name box, and any custom window have their own width limits, and they overflow more quietly than dialogue does, because a truncated choice still looks like a choice.
There are established ways to soften this — smaller text, automatic word wrapping, wider windows — and plugins exist for all of them. Whatever you use, verify the result by playing the scenes rather than by reading the parameter you set, because these settings interact with each other and with any other plugin that draws text.
Fonts are the other half of what breaks on screen. MV and MZ render text with a font supplied in the project's fonts folder, and a font only draws the characters it contains. This bites in both directions: adding Japanese, Korean, or Chinese to a project whose font covers only Latin gives you missing-glyph boxes, while a font chosen for Japanese may lack the accented Latin characters that French, Spanish, German, or Polish need — so those languages break in a way that is easy to miss if you only test English.
Check every target language against the actual font, in the running game, on the screens where text appears, rather than previewing the font in isolation. Check the font's licence before you ship it inside a commercial game, too; embedding is a specific permission that not every font grants.
The data files themselves are UTF-8 JSON, which is fine until a tool in your pipeline rewrites them in a different encoding or adds a byte order mark. Exporting to a spreadsheet and saving back out is the classic place this happens. Keep the round trip in a script you control, keep everything in version control, and if characters ever turn into garbage, look at the tool that last wrote the file rather than at the translation.
Runtime switching or separate builds
There are two viable shapes for a multilingual RPG Maker release. The first is a plugin that keeps translations in an external file and substitutes text at display time, so one build ships with a language option. The second is a separate build per language: duplicate the project, replace the text, deploy each one.
Separate builds have real appeal for a first release — no plugin to trust, nothing to debug — but the cost arrives with your first content update, since every change has to be made in every copy and the copies drift apart. If you plan to keep updating, or to ship more than two languages, that cost compounds quickly. Note also that anyone can read the data folder of a deployed game, which is why fan translations of RPG Maker games exist at all; separate builds do not hide anything a single build would expose.
If you go the plugin route, choose with a checklist rather than a description, and test it against your own project before the translation exists. The questions below are the ones that decide whether a plugin fits your game.
- Does it cover database records and System terms, or only message text?
- Does it handle choices, scrolling text, and battle messages?
- What happens to control codes — are they preserved exactly, and does it document that?
- Does it work alongside the other plugins you already use, especially any that draw text?
- Is the translation stored in a plain-text format you can diff in version control?
- How does it behave when a line is missing — does it fall back to the source language or show a blank?
- Is it maintained, and does it state which engine versions it supports?
Before you ship
Most of what goes wrong in a translated RPG Maker game is mechanical, which means it can be found without playing the whole game in a language you cannot read. Do the mechanical checks on the files first, then spend your play time on the things only a person can judge.
- Every key present in the source table has a value in every language, and no value is accidentally the source text
- Control codes match between source and translation in spelling, index, and count on every row
- No stray full-width spaces, unclosed brackets, or line breaks that arrived from a spreadsheet
- Every font renders every character used in every language, checked in the running game
- Messages, choices, and the name box are read on screen in the longest language, not only in the table
- Images with text baked in have a version per language, or a decision on record that they do not need one
- Plugin parameters and the game title in System.json were included in the export, not left in the source language