What is i18n and Why Every Developer Should Know It

What is i18n and Why Every Developer Should Know It

Most developers live with a curious paradox. We write APIs, set up Docker, debate microservice architecture — yet we hardcode strings directly into the UI:

h1.innerText = "Home";

Then the client says: "We need an Arabic version too."

And suddenly the codebase shatters like glass.

That is where i18n enters the stage.

What does i18n mean?

i18n is shorthand for "internationalization" — there are 18 letters between the "i" and the "n".

But technically, i18n is not just about that.

i18n = Separating text from code and managing language at the system level.

This is not a translation mechanism.

This is an architectural decision.

Why do you need i18n?

If you want to:

then i18n is no longer optional.

The hardcoded string problem

Writing this kind of code is easy:

if (lang === "en") {
    title.innerText = "Projects";
} else {
    title.innerText = "Proyektlər";
}

It works. But it is structurally weak.

The purpose of i18n is to free your code from language-related changes.

Core i18n logic

The principle is simple:

  1. Assign keys to UI strings
  2. Have a separate file per language
  3. The system replaces each key with the correct translation

A simple JS i18n system

1. Language files

/lang/en.json

{
  "nav.home": "Home",
  "nav.projects": "Projects",
  "hero.title": "Privacy & AI Solutions",
  "hero.subtitle": "Scalable digital infrastructure"
}

/lang/az.json

{
  "nav.home": "Ana səhifə",
  "nav.projects": "Layihələr",
  "hero.title": "Privacy & AI Solutions",
  "hero.subtitle": "Scalable digital infrastructure"
}

2. HTML

data-i18n="hero.title">h1> 
data-i18n="hero.subtitle">p>

3. JS Loader

let currentLang = localStorage.getItem("lang") || "en";
let translations = {};

async function loadLanguage(lang) {
    const response = await fetch(`/lang/${lang}.json`);
    translations = await response.json();
    applyTranslations();
}

function applyTranslations() {
    document.querySelectorAll("[data-i18n]").forEach(el => {
        const key = el.getAttribute("data-i18n");
        el.innerText = translations[key] || key;
    });
}

function changeLanguage(lang) {
    localStorage.setItem("lang", lang);
    loadLanguage(lang);
}

loadLanguage(currentLang);

That is all. No framework. No premium plugin.

Server-Side i18n with PHP

If your backend is PHP, you can handle this on the server side too.

/lang/en.php

 "Home",
    "nav.projects" => "Projects",
    "hero.title" => "Privacy & AI Solutions"
];

index.php

$lang = $_GET['lang'] ?? 'az';
$translations = require "lang/$lang.php";

function t($key) {
global $translations;
return $translations[$key] ?? $key;
}
?>

The server renders it. Much better for SEO.

SEO and Multi-language Routing

The correct approach:

site.com/az
site.com/en
site.com/ru

Why? Because:


This is no longer just translation. This is SEO architecture.

The hard part: Dynamic strings

Simple strings are easy. The complexity comes with interpolation and pluralization.

{"cart.items": "You have {count} items in your cart"
}

JS interpolation:

function translate(key, vars = {}) {
    let text = translations[key] || key;
    Object.keys(vars).forEach(v => {
        text = text.replace(`{${v}}`, vars[v]);
    });
    return text;
}

translate("cart.items", { count: 3 });

Deeper challenges

i18n is not just about text. It also covers:

For currency formatting:

new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD'
}).format(1200);

This is where true internationalization begins.

The biggest mistake

Adding i18n as an afterthought.

If you build a project for one language and add i18n later, you will have to refactor all the code.

The right approach: Separate strings from the very beginning.

Why do people get paid for this?

Because people see it as "translation." But i18n is:

This is a code quality indicator.

i18n = Product-level thinking

If a product is built for one language, it is a local project.

If a product is built with i18n:

This is no longer "string swapping." This is system design.

Conclusion

i18n is not complex. It just requires systematic thinking. Every developer can do it.

The key points:

Everything else is implementation.

Programming is often not about algorithms. It is about building the right structure.

i18n is one of those structures.

Back to blogs