Data-i18n for Small Web Projects: A Complete Guide to Internationalization

Search for a command to run...

No comments yet. Be the first to comment.
Mind maps are an excellent addition to an MCP ecosystem when they help users understand relationships between data, tools, and reasoning. They should complement—not replace—traditional dashboards, tab
Most AI agents today depend heavily on cloud APIs. They're fast, but every request costs money, depends on an internet connection, and sends your data to external providers. Over the weekend, I experi
How I use Fable 5 to research, build, and maintain custom Claude Code plugins, marketplaces, agents, and skills—creating reusable AI tooling that works across every project. 01 · The problem with copy

The way people search is changing. Is your site ready? For the past two decades, SEO (Search Engine Optimization) was the undisputed king of web visibility. Rank on Google's first page and you win tr
How we made 30 GB PSD uploads feel instant without expensive backend conversions. When we first built our media upload system, the architecture looked completely normal. A user uploaded a file, the b

Modern web applications are used by people across different countries and languages. If your website only supports one language, it limits your audience. This is where internationalization (i18n) comes in.
In this blog, we will explore how to implement a simple and powerful data-i18n system in a small web project using HTML, CSS, and JavaScript, without any heavy frameworks.
Internationalization (i18n) is the process of designing your application so it can easily support multiple languages and regions.
The abbreviation i18n means:
i → first letter
18 → number of letters between
n → last letter
Example:
internationalization
i + 18 letters + n
Internationalization allows applications to switch between languages like:
English
French
Spanish
Hindi
Japanese
without rewriting the entire application.
data-i18n?data-i18n is a custom HTML attribute used to mark elements that should be translated.
Instead of hardcoding text in HTML, we assign a translation key.
Example:
<h1 data-i18n="title">Welcome</h1>
<p data-i18n="description">This is a demo app.</p>
Here:
title
description
are translation keys, not the actual translation.
JavaScript will dynamically replace the text based on the selected language.
The data-i18n system usually follows these steps:
HTML contains translation keys
Translations are stored in JSON files
JavaScript loads the selected language
JavaScript replaces the text dynamically
Flow diagram:
HTML (data-i18n keys)
↓
Translation JSON files
↓
JavaScript loads selected language
↓
Text updated dynamically
First, we mark the text that needs translation.
<!DOCTYPE html>
<html>
<head>
<title>i18n Demo</title>
</head>
<body>
<h1 data-i18n="title">Welcome</h1>
<p data-i18n="description">
This is a demo application.
</p>
<button data-i18n="login">Login</button>
<div>
<button onclick="loadLanguage('en')">English</button>
<button onclick="loadLanguage('fr')">French</button>
</div>
<script src="script.js"></script>
</body>
</html>
Notice that we still include default text so the page does not appear empty before JavaScript loads.
Create a language folder.
project
│
├── index.html
├── script.js
│
└── lang
├── en.json
└── fr.json
lang/en.json
{
"title": "Welcome",
"description": "This is a demo application.",
"login": "Login"
}
lang/fr.json
{
"title": "Bienvenue",
"description": "Ceci est une application de démonstration.",
"login": "Connexion"
}
Each key must match the data-i18n value.
Now we create a function that loads the selected language and updates the page.
script.js
async function loadLanguage(lang) {
const response = await fetch(`./lang/${lang}.json`);
const translations = await response.json();
const elements = document.querySelectorAll("[data-i18n]");
elements.forEach(element => {
const key = element.getAttribute("data-i18n");
if (translations[key]) {
element.textContent = translations[key];
}
});
}
This script does three things:
Loads the translation JSON file
Finds all elements with data-i18n
Replaces the text with the translated value
We can switch languages using buttons.
<button onclick="loadLanguage('en')">English</button>
<button onclick="loadLanguage('fr')">French</button>
When clicked:
loadLanguage("en") → loads English
loadLanguage("fr") → loads French
The page text updates instantly.
In real applications we usually save the user's preferred language.
We can use localStorage.
Example:
async function loadLanguage(lang) {
localStorage.setItem("language", lang);
const response = await fetch(`./lang/${lang}.json`);
const translations = await response.json();
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.dataset.i18n;
if (translations[key]) {
el.textContent = translations[key];
}
});
}
window.addEventListener("DOMContentLoaded", () => {
const savedLang = localStorage.getItem("language") || "en";
loadLanguage(savedLang);
});
Now the site remembers the user's language.
Sometimes we need to translate attributes like:
placeholders
titles
aria labels
Example:
<input type="text" data-i18n-placeholder="search" placeholder="Search">
Translation file:
{
"search": "Search..."
}
JavaScript:
document.querySelectorAll("[data-i18n-placeholder]").forEach(el => {
const key = el.getAttribute("data-i18n-placeholder");
el.placeholder = translations[key];
});
For small projects this approach is perfect, but large applications may require optimization.
Possible improvements:
Load only the language being used.
Store translations in memory to avoid repeated network requests.
Load frequently used languages on startup.
data-i18n ApproachNo frameworks required.
Translation logic stays separate from markup.
Only a few lines of JavaScript.
Translators can edit JSON files without touching code.
Works with any frontend stack.
Although useful, the data-i18n approach has some limitations.
Languages like Russian or Arabic require complex plural logic.
Formatting currencies and dates requires additional libraries.
Large projects often require structured translations.
If your project becomes large, consider using a professional library like:
i18next
react-i18next
vue-i18n
These tools support:
pluralization
date formatting
dynamic translations
server side rendering
language detection
The data-i18n pattern works well for:
Landing pages
SaaS dashboards
Documentation sites
Small web apps
Portfolio websites
MVP products
For example, if you build a multi-language SaaS dashboard, you can translate UI labels like:
Dashboard
Analytics
Settings
Logout
without changing the layout.
Internationalization is an important feature for modern web applications. Even small projects benefit from supporting multiple languages.
The data-i18n approach provides a lightweight and flexible solution for implementing translations using simple HTML attributes and JSON files.
With just a small amount of JavaScript, you can create a system that:
dynamically switches languages
loads translations from JSON files
remembers user preferences
keeps HTML clean and maintainable
As your application grows, you can later migrate to advanced libraries while keeping the same translation structure.