Custom locales: shipping やさしい日本語 (Easy Japanese) alongside your standard languages

· · 7 min read · by Arvid Andersson
i18n Localization Custom locales Japanese Rails
Custom locales: shipping やさしい日本語 (Easy Japanese) alongside your standard languages

Most i18n tooling assumes every language fits a two-letter code. Real products sometimes need more. We recently got a question from a team whose product ships in Japanese, and in Easy Japanese for users still learning the language. Their locale is ja_easy.

What is やさしい日本語 (Easy Japanese)?

やさしい日本語 is a standardized plain-language register of Japanese, developed for residents who are not yet fluent: immigrants, foreign workers, exchange students. Japanese government agencies publish official guidelines for it: short sentences, everyday vocabulary, hiragana-friendly spelling, no honorific keigo.

It is not a dialect and not a translation shortcut. It's a deliberate version of Japanese, written for a specific audience. If your product serves language learners or new residents in Japan, it may well be your most important locale.

There's no ISO code for it. So teams pick their own, like ja_easy, and most localization tools then reject it outright or silently skip the files.

We've added support for custom locales

This is a first version, and a feature we're actively exploring. Here's how it works today: you declare the locale when setting up your project with localhero init, giving it a name and the base language it builds on. It lands in localhero.json like this:

{
  "sourceLocale": "en",
  "outputLocales": ["ja", "ja_easy", "vi", "th"],
  "customLocales": [
    { "code": "ja_easy", "name": "Easy Japanese", "baseLanguage": "ja" }
  ]
}

That's the whole setup. From there, ja_easy behaves like any other language:

  • config/locales/ja_easy.yml is picked up by file scanning, push, pull, and CI, including namespaced files like devise.ja_easy.yml
  • The base language drives the linguistic machinery: plural rules, the 🇯🇵 flag in the dashboard, language-specific translation guidance
  • Your existing Easy Japanese copy imports as-is. We treat it as its own content, not as a translation of your English
  • Translations show up for human review per pull request, like every other locale

The declaration matters. We deliberately don't blanket-accept unknown codes, because a typo like jp should fail loudly instead of quietly creating a broken language. Declaring customLocales is what tells us this one is real, and here is what it means.

What AI translation into Easy Japanese looks like

While looking into this, we dug into the やさしい日本語 guidelines and tuned our translation engine to understand the style. So when translating into Easy Japanese you don't just get Japanese: you get hiragana-leaning spelling with 分かち書き spacing, vocabulary a JLPT N4-N5 learner knows, polite です・ます form without keigo, and loanwords kept in katakana the way Japanese actually writes them.

The same source string, Try the exam again, side by side:

Translation Reads roughly as
ja (standard) もう一度受験する Retake the examination: compact, kanji-dense, a formal compound verb
ja_easy もう いちど しけん を うけます You take the test one more time: everyday words, spelled out in hiragana, spaces between phrases

We keep this honest with automated evals: a test suite asserts the register holds: katakana loanwords never degrade into hiragana, no keigo slips in, and ambiguous spellings like きのう (which can read as feature or yesterday) get resolved properly. AI translation of a register is only useful if it's consistent.

And if you'd rather write Easy Japanese yourself and only use Localhero for review, that works too. Custom locales don't force AI translation on you.

Your product, your locales

Sometimes languages in real products don't match tidy standard lists. We want Localhero to work great for what your product actually needs, and custom locales are part of that: you define the language versions that matter to your users, and the platform treats them like any other locale. That said, when a standard code exists, sticking to it is usually the right call. Custom locales are for the cases the standards don't cover.

If you're building for language learners, new residents, or any audience that standard locale codes don't describe: this is a supported path, not a workaround.

We're also genuinely curious how teams handle this today. If you ship a simplified-language version of your product, like やさしい日本語, Leichte Sprache or plain English, or you've wanted to and your tooling got in the way, we'd love to talk and learn how you solve it: hi@localhero.ai.

FAQ

What is a custom locale in i18n? A language version of your product that has no standard ISO code. You invent one instead. Easy Japanese is the clearest example: teams write it as ja_easy, a plain-language register of Japanese for people still learning the language. Leichte Sprache in German and plain-English versions work the same way. The content is real and the audience is real; the code just isn't on anyone's list. That's where most tooling falls over. It validates locale codes against a fixed set and either rejects the file or skips it without saying anything.

Why do most localization tools reject a locale like ja_easy? Because they validate against ISO 639 and treat anything else as a typo. Which is a reasonable default, jp is not a language code and should fail loudly. The problem is that a strict allowlist and a useful product are not the same thing. Our approach is to let you declare custom locales explicitly in localhero.json, with a name and a base language. A declared code is treated as a first-class language; an undeclared one still fails. A typo doesn't quietly create a broken language in your project.

What does the base language of a custom locale do? It drives the linguistic machinery that a made-up code can't imply on its own. For ja_easy with a base of ja, that means Japanese plural rules, the Japanese flag in the dashboard and the language-specific translation guidance we already apply to Japanese. Without a base language, a tool has no way to know whether your custom code needs one plural form or six. Declaring it is what turns an arbitrary string into something the system can reason about.

Can AI translate into Easy Japanese, or do we have to write it ourselves? Both work. Localhero.ai ships tuned against the published やさしい日本語 guidelines, as a register the translation engine knows rather than something you prompt for per string. Translating into ja_easy gives you hiragana-leaning spelling with 分かち書き spacing, JLPT N4-N5 vocabulary, polite です・ます without keigo and loanwords kept in katakana. We hold that with automated evals, since a register is only useful if it stays consistent: the suite asserts that katakana loanwords don't degrade into hiragana, that keigo doesn't slip back in and that ambiguous spellings resolve correctly. If you'd rather write Easy Japanese yourself and use Localhero.ai only for review and for keeping it in sync with the rest of your locales, custom locales don't force AI translation on you.

Will my existing Easy Japanese copy get overwritten when I import it? No. Imported content for a custom locale is treated as its own content, not as a translation of your English source. A key that already has non-blank content is never overwritten on import. Already-translated keys also cost no credits. So a team that has been hand-writing ja_easy for two years keeps all of it. Automation only fills the genuine gaps.

Should we use a custom locale or a standard one? Use the standard code whenever one exists. Regional variants like pt_BR or nb for Norwegian Bokmål are standard and every tool in your stack will understand them. Custom locales are for the cases the standards genuinely don't cover, a deliberate register or reading level aimed at a specific audience. If you invent a code where a real one exists, you give up interoperability with everything downstream, from your framework to your analytics, for nothing in return.

Ready to ship without translation delays?

No credit card required. Need help migrating? Just reach out.