How Do I Add Multiple Languages to a Website with JavaScript? 3 Practical Methods

How Do I Add Multiple Languages to a Website with JavaScript? 3 Practical Methods

Imagine putting a “closed” sign on your website for 75% of the world. That’s what a single-language site does! If you’re building something great with JavaScript, the next logical step is reaching a global audience.

Adding other languages sounds expensive and complex. It doesn’t have to be.

We’re going to break down the three most practical ways to get your website talking to users everywhere, from a simple setup to a professional, automated system. Let’s make this easy!

i18n vs. l10n: What’s the Difference?

Two confusing terms you’ll hear are i18n and l10n. Think of them like this:

  • Internationalization (i18n): This is the developer’s job. It means getting your code ready to handle multiple languages. You’re building the “buckets” for the translations and setting up the code to automatically handle things like date, time, and currency formatting.
  • Localization (l10n): It includes the translation of all the text, but also changes to visuals, choosing the right currency symbol, and ensuring that the entire experience feels natural for users in that country.

Our three methods focus on the i18n part: setting up the code structure.

How Does the Code Know Which Language to Load?

Before you load any text, your JavaScript app needs to know the user’s preferred language. The three main ways your application determines this are:

  1. Browser Language: Reading the navigator.language property (the most common default).
  2. URL Parameter/Path: Loading the language based on /en/page or page?lang=en.
  3. User Preference: Storing a choice in localStorage or a cookie.

Method 1: The Simple JSON Dictionary

This is the fastest and cleanest way to get started. It’s perfect for small, hobby, or portfolio projects.

You just create separate files to hold all your website text. Your JavaScript application then loads the correct file based on the user’s language preference and replaces text keys (e.g., "welcome_message") with the corresponding value (e.g., "Welcome!").

// en.json

{

"welcome_message": "Welcome!",

"call_to_action": "Read More"

}

 

// es.json

{

"welcome_message": "¡Bienvenido!",

"call_to_action": "Leer más"

}

Your JavaScript just needs to grab the right file based on the user’s language and swap out the text. This is a foundational way of localizing JavaScript.

Pros of such a method:

  • Your translations are completely separate from your code.
  • Easy to read, so translators can easily handle simple JSON files.

Cons:

  • You need to write the code that fetches the file and updates the text manually.
  • If you need complicated things like plurals (“1 item” vs. “5 items”), you’ll be writing a lot of custom logic.

This method is best for small websites, portfolios, or tiny single-page apps (SPAs). It keeps your setup minimal, but you’ll outgrow it fast.

Method 2: Using HTML Data Attributes

This technique uses built-in HTML functionality to avoid heavy JavaScript DOM manipulation.  It’s ideal for static sites or legacy projects.

Instead of fetching text from a separate file and updating the DOM, you embed the translation key directly into the HTML element using a data-i18n attribute. Your JavaScript simply reads this key and swaps the element’s content with the corresponding translation.

<h1 data-i18n=”page_title”></h1>

<button data-i18n=”sign_up_btn”></button>

Pros of Using HTML Data Attributes:

  • The translation key is visible right next to the element, making debugging fast.
  • Minimal JavaScript is used, where the script only needs to loop through elements with the attribute, keeping logic minimal.

Cons of Using HTML Data Attributes:

  • Your code can get messy with all those data-i18n tags
  • Terrible for content that loads later, like messages that pop up after a button click.
  • Potential for minor XSS vulnerability if translation keys are dynamically rendered from an unsafe source.

HTML Data Attributes are ideal for static websites or legacy projects where content and structure are closely tied, and you want to minimize the JavaScript footprint.

Method 3: Leveraging a Lightweight Library

This is the professional, future-proof choice. So why re-invent the wheel?

Libraries like Lingui or i18next were built specifically to handle the complex linguistic requirements of human languages. They manage advanced features like pluralization, gender-specific text, contextual phrases, and correct date/currency formatting automatically.

For example, Lingui uses standard message formats and can extract strings directly from your source code using simple functions, often called macros like t or plural.

Here’s a simplified view using Lingui to handle a simple variable:

Simple Lingui Example: Variables

This approach uses the modern t template literal macro to define a translatable string with a variable:

// 1. Import the 't' macro

import { t } from '@lingui/core/macro';

 

const name = 'Alex';

// The 't' macro marks this string as translatable and captures the variable.

const welcomeMessage = t`Welcome, ${name}!`;

 

i18n.locale = 'fr';

// Output: "Bienvenue, Alex!"

Advanced Lingui Examples: Plurals, Dates, and Context

A basic variable is easy, but complex rules like plurals, dates, and gender context are not. Libraries handle this for you:

Example 1: Pluralization (Manages multiple plural forms across languages)

We use the specific plural macro here, as recommended by your editor, which makes the translation rule explicit:

// Use the dedicated 'plural' macro for clean rule definition

import { plural } from '@lingui/core/macro';

 

const count = 5;

const itemMessage = plural(count, {

one: `# item in cart`,

other: `# items in cart`,

});

// Output (English): "5 items in cart"

Example 2: Dates (Format dates correctly for the current locale)

For complex ICU formats like dates, the t template literal is still used:

import { t } from '@lingui/core/macro';

 

const today = new Date('2025-01-20');

const dateMessage = t`Last updated on ${today}`;

 

i18n.locale = 'fr-FR';

// Output (French): "Dernière mise à jour le 20/01/2025"

Example 3: Context/Gender (Selects the correct word based on a variable like gender)

// Use the 't' macro for select/gender ICU formats

import { t } from '@lingui/core/macro';

 

const gender = 'male';

const userContext = select(gender, {

male: "He logged in",

female: "She logged in",

other: "They logged in",

});

 

// Output: "He logged in."

Pros and Cons of Using JS i18n Libraries

Pros:

  • Truly Scalable: Handles high complexity and large numbers of languages without breaking down.
  • Automation: Manages all complex linguistic rules (like plurals and gender) automatically, saving countless hours of custom coding.
  • Integration: Offers framework integration (React, Vue, Angular, etc.).

Cons:

  • Dependency: Adds an external library to your project, which increases your overall bundle size slightly.
  • Initial Setup: Requires a small amount of initial time to read the documentation and get the configuration set up.

This method is the best choice for any serious, scalable project that you plan to grow. It’s the industry standard for modern web applications.

The Next Level: Automation with a TMS

If your project is large, you’ll realize that managing translation files manually quickly turns into a nightmare of copy-pasting, version control conflicts, and constant back-and-forth with translators. This wastes engineering time and leads to errors.

The Solution is Continuous Localization

Once you’ve chosen Method 3 (a library), the next logical step is to connect it to a Translation Management System (TMS). Think of a TMS as a central, automated hub for all your translations.

This creates a smooth, continuous localization workflow:

  1. You сode: When you add a new string to your JavaScript, the TMS’s integration automatically detects and extracts that new text.
  2. TMS translates: The new text is sent directly to your translators, AI tools, or internal team within the platform, where they work efficiently.
  3. TMS delivers: As soon as a translation is finished, the TMS automatically pushes the updated translation files (like your JSON or PO files) back to your code repository, ready for immediate deployment.

With a TMS, localizing JavaScript becomes a structured, continuous project. Your developers no longer need to deal with translation files, and your website stays up-to-date across all languages.

Conclusion: Stop Coding, Start Automating

The best approach depends on your project’s size, but here’s the simple rule:

  • If you need a minimal setup for a portfolio or hobby site, the simple JSON file method is fine.
  • If your goal is a scalable product, invest in a JS i18n library from the start.

The best code is the code you don’t have to write. Remember, the goal isn’t just to add a second language. The goal is to create an efficient, automated process that makes localization easy as you scale.