JSON files are the backbone of app localization. Understanding how to translate and localize JSON files effectively can save you countless hours and headaches.
Case in point:
My team had just wrapped up a React app localization project when the CEO asked to localize it into 12 additional languages. We already had JSON translation files for English and Spanish, so I thought our three developers could easily manage 12 more languages.
Well, it always looks easy.
But three weeks and 2,000 translation strings later, I learned why JSON translation deserves more attention than most of us give it.
Let me show you how to properly translate JSON files for your app localization project.
What is a JSON file?
JavaScript Object Notation (JSON) is a text-based file format used to group data into key-value pairs structured with curly braces. Each key acts like a label, and each value holds text, numbers, or valid JSON data, which is separated by commas. Each key and string value is surrounded by quotation marks. Booleans, numbers, and null are written without quotes.
Example:
{
"app_name": "Integro",
"welcome_message": "Welcome to Integro Land!",
"button_login": "Log in",
"button_signup": "Sign up"
}
Here “app_name” is a key and “Integro” is its value (string).
JSON format originated as a concise method to pass data between a server and a browser, but it has found wide acceptance in configuration files and translation management.
A few lines are enough to express many details in a clear way. It’s direct, it’s simple to parse, and it’s easily edited in any plain-text editor. That’s why many localization frameworks in JavaScript, Python, Ruby, and other languages treat JSON as a go-to format for storing translations.
Put simply, JSON is human-readable and machine-friendly.
Types of JSON
Depending on the project requirements and setup, you may be using one of the following types of JSON file formatting:
Flat JSON
This style keeps everything at one level. Namespacing is done through dotted or underscored keys:
{
"account.login": "Log in",
"account.logout": "Log out",
"account.profile.edit": "Edit Profile",
"account.profile.save": "Save Changes"
}
A flat JSON is easy to work with when dealing with libraries that prefer single-level lookups. It can also keep files simpler if you dislike multiple nested braces.
Nested JSON
This style groups keys into parent objects. Each parent object might hold child objects or final text strings:
{
"account": {
"login": "Log in",
"logout": "Log out",
"profile": {
"edit": "Edit Profile",
"save": "Save Changes"
}
}
}
Nested JSON is often used in frameworks like Vue, Next.js, or Angular, where nested structures map cleanly to UI components.
Structured JSON
This style extends each key with objects that hold more data:
{
"account.login": {
"translation": "Log in",
"notes": "Used on the sign-in page",
"limit": 15
}
}
There’s a single top-level key and an object inside that might hold fields like “translation,” “notes,” or “context.”
This helps translators see meta-information directly in each entry.
How to translate and localize JSON files, the hard way
Before we begin, copy this sample JSON file into an en.json for English. It’s only about 50 characters of text, but it illustrates most localization basics.
{
"header": "Welcome!",
"cta": "Sign up now",
"messages": {
"greeting": "Hi, {{username}}!",
"items": {
"one": "You have 1 item in your cart",
"other": "You have {{count}} items in your cart"
}
}
}
-
header: Simple text that appears at the top of the page.
-
cta: A call to action that prompts users to register or take a specific action.
-
messages: A nested object for user-specific texts.
-
greeting: Greets the user by name, so it includes a placeholder for the username.
-
items: Shows one text if the user has one item and another text if the user has more than one. The second string uses a placeholder for the number of items.
-
I’ll demonstrate the rest of the JSON localization using this sample file. If you have an existing file, please feel free to follow through to localize your Angular or Laravel app.
Step 1: Copy the en.json into another file
Let’s create a new file es.json for Spanish. The structure mirrors en.json.
{
"header": "",
"cta": "",
"messages": {
"greeting": "",
"items": {
"one": "",
"other": ""
}
}
}
Each key is present, but the strings are empty. The empty strings act as placeholders for the translated text.
Step 2: Add translations
Now, we’ll add the Spanish text to the file as below:
{
"header": "¡Bienvenido!",
"cta": "Regístrate ahora",
"messages": {
"greeting": "Hola, {{username}}!",
"items": {
"one": "Tienes 1 artículo en tu carrito",
"other": "Tienes {{count}} artículos en tu carrito"
}
}
}
The placeholders remain the same {{username}} and {{count}}. Everything else is now in Spanish.
Step 3: Validate the JSON file
Whenever I’m localizing JSON files or modifying them, I prefer to run it through a JSON linter. If you don’t have a linter set up locally, you can also run it through jsonlint.com.
Now, this isn’t really necessary for small JSON snippets like the example I’ve used. But for larger ones, it’s always good to do a quick check before deploying it into your application.
Step 4: Repeat for more languages
Since we had to add 12 languages, we just repeated the same steps above for other languages. This included copying the files for German, French, Lithuanian, Hindi, and all the other languages we were supporting.
Here’s what our directory structure looked like after the entire JSON setup was done:
ninad@Centus (~/Dev) $ tree app/
app/
└── locales
├── en.json # English
├── es.json # Spanish
├── de.json # German
├── fr.json # French
├── lt.json # Lithuanian
├── hi.json # Hindi
├── it.json # Italian
├── ru.json # Russian
├── zh.json # Chinese (Simplified)
├── ja.json # Japanese
├── nl.json # Dutch
├── lv.json # Latvian
└── pt.json # Portuguese
2 directories, 12 files
I’ve added the comments with the full language name in the output.
Once you have a proper directory structure for storing your locales, you can write code to fetch the required appropriate text snippets and display them on the front-end of your application.
Why do I call this method “the hard way”?
Manual editing might seem fine if you only have a handful of pages and a single language. The moment you add more text or multiple languages, it becomes a hassle. I was working with 12 languages for my client’s app.
Each small UI tweak meant going through all JSON files, updating strings by hand, and coordinating with multiple translators. And it became difficult to keep track of what was finished or pending; it took so much time that it delayed other tasks.
That’s why most teams move on to localization management platforms, like Centus.
Such platforms provide a clear interface, track progress for each string, let you collaborate in real time, and keep your translators away from the raw code.
Benefits?
- your developers stay focused
- your managers can track the project’s progress
- your translators can translate without dealing with the code
The use of localization management platforms is a more efficient, less error-prone way to handle multilingual projects.
How to perform JSON translation, the easy way
Let me show you how easy it is to translate JSON online using Centus.
First, you import your JSON files to Centus:
Then, you auto-translate JSON files using built-in integrations with Google Translate, DeepL, or Microsoft Translator.
I know what you’re thinking—Google Translate isn’t perfect. And you’re right. Although they’re up to 90% faster and cheaper, they still need manual fine-tuning, which brings me to another reason you need Centus.
Your team can refine automatic translations in the convenient Centus Editor:
Centus Editor can also be set up to provide automatic glossary suggestions, keeping your translations accurate and consistent throughout your JSON files.
Do your translators need more context?
Simply attach screenshots to help them know the exact location of the translated text.
Similarly, add comments for ambiguous strings. Also, encourage editors and reviewers to comment on translations.
Your translation team will appreciate other Centus’ features, but let’s talk about one you would love. One that can shave days off your development timeline.
I’m talking about fast localization bug fixes.
Here’s the bug-fixing normally goes:
- You spot a truncated string in the app.
- You email translators, request a revision, and wait.
- After getting shortened translations, you rerun regression tests.
- If it’s still not right, the cycle repeats.
This back-and-forth wastes your time, delaying the app's release.
But with Centus, bug-fixing takes mere minutes:
- You spot truncated text in the app.
- You leave a comment for translators directly in Centus.
- Translators make adjustments, and strings are automatically updated via Centus API.
Speaking of Centus API, use it to sync Centus with your code repositories. Thus, you can implement continuous localization, where JSON strings are regularly pushed to Centus and pulled back into your repository as part of your regular development cycle.
Centus has many more time-saving features for app localization, in general, and JSON translation, in particular. Sign up for Centus and try them yourself!
How to convert JSON to XML
If you need to convert JSON to XML to ensure compatibility, use Centus. After you translate JSON to English or any other language, export it as an XML file. Similarly, you can translate XML to JSON.
How to translate JSON to HTML
To translate JSON to HTML, translate your JSON file using Centus first. Then, export the translated file as HTML.
Advanced JSON localization
Large applications need more than a file full of English text. Different languages have grammatical rules that affect how text appears. Some languages have multiple plural forms. Others have gendered terms that change entire sentence structures.
Let’s see how you can implement advanced localization manually.
Pluralization
English typically has two plural forms: singular and plural. But some other languages have more than two forms.
Here’s how you can store the basic pluralization:
{
"items": {
"one": "You have 1 item in your cart",
"other": "You have {{count}} items in your cart"
}
}
The logic picks “one” when the count is exactly 1. It picks “other” for all other numbers. That logic is straightforward for English. French or Spanish also works fine with the same approach.
But some languages, like Russian or Polish, split “other” into several distinct plural forms. A single piece of text might need three or four different endings.
The JSON file might look like this (a hypothetical example for a language with three forms):
{
"items": {
"one": "1 item",
"few": "{{count}} items (few)",
"other": "{{count}} items (many)"
}
}
The localization library you use will generally apply the correct rule. But it’s best to learn your i18n library’s approach to naming these forms.
Gendered sentences
Certain languages require different word choices depending on whether the person is male, female, or another gender. An example might be:
- “He updated his profile.”
- “She updated her profile.”
- “They updated their profile.”
You need your application to have all these variations stored in the JSON. This often means adding multiple keys or storing a single key with nested fields:
{
"profile_update": {
"male": "He updated his profile.",
"female": "She updated her profile.",
"other": "They updated their profile."
}
}
The code checks the user’s gender or preference, and then displays the correct string.
Some languages change adjectives or verbs based on gender, so the impact can be more widespread than just pronouns. So for my use cases, it has always been better to store each gendered sentence separately.
If you try to force everything into a single translation with placeholders, you might get a string like, “{{pronoun}} updated {{possessive}} profile,” and that may break in some languages.
Placeholders
While we’re on the topic of placeholders, let’s talk about how to implement them in localized strings.
JSON translations include placeholders for dynamic content. For instance, the text might say, “Hi, {{username}}!” or “Your total is ${{total}}.” The code replaces those placeholders at runtime.
A translator must preserve these placeholders exactly or the code might break (irrespective of the language).
For instance, the placeholder {{ username }} will remain the same in English as well as Mandarin even though the characters change in Mandarin.
A few guidelines for placeholders:
- Keep them short but descriptive, like {{username}} instead of {{u}}
- Maintain consistent naming across languages
- Explain how placeholders work to translators
- Use a translation management system that can lock or highlight placeholders to prevent accidental changes
Wrapping up
JSON is more than a data storage format. It’s what helps you add language translations in modern applications.
A single file with curly braces can represent the entire UI text for an app. But it can be a long and arduous task to create and maintain these files manually.
A better approach involves:
- Standardized key naming patterns
- A method to handle placeholders and plural forms
- Workflow that includes a translation management platform like Centus
- Understanding of how each language’s grammar can affect translation structure
Each of these points adds stability and helps you scale your projects without risking downtime. So be sure to plan your localization projects with long-term scalability in mind.
Get the week's best content!
By subscribing, you are agreeing to have your personal information managed in accordance with the terms of Centus Privacy Policy ->
Keep learning
12 min. read
How to Translate JSON Files: A Simple Guide
19 min. read
NextJS Internationalization: A Complete Walkthrough
19 min. read
NodeJS Internationalization (i18n) and Localization
21 min. read
Nuxt i18n and Localization: Make Your App Multilingual
18 min. read
JavaScript Localization Guide for Web Apps
22 min. read