Now.js Framework Documentation

Now.js Framework Documentation

I18nManager - Internationalization System

EN 24 Nov 2025 08:35

I18nManager - Internationalization System

Documentation for I18nManager, a multi-language (Internationalization/Localization) management system.

📋 Table of Contents

  1. Overview
  2. Installation and Import
  3. Basic Usage
  4. Translation Files
  5. HTML Integration
  6. Dynamic Translation
  7. Parameter Interpolation
  8. Locale Management
  9. Configuration
  10. Usage Examples
  11. API Reference
  12. Best Practices

Overview

I18nManager is a multi-language management system that helps applications support multiple languages easily.

Key Features

  • Multiple Locales: Support for multiple languages
  • Auto Detection: Automatically detect browser language
  • HTML Integration: Auto-update DOM elements
  • Parameter Interpolation: Insert variable values in translations
  • Lazy Loading: Load translations when needed
  • LocalStorage: Remember selected language
  • Fallback: Use fallback when translation not found
  • Nested Keys: Support nested object keys
  • Event System: Emit events on language change
  • No Translation Mode: Skip translation for English

Use Cases

Suitable for:

  • Multi-language applications
  • Global web applications
  • User preference localization
  • Content management systems
  • E-commerce platforms
  • SaaS applications

Installation and Import

I18nManager loads with Now.js Framework:

// No need to import - ready to use immediately
console.log(window.I18nManager); // I18nManager object

// Access through Now.js
const i18n = Now.getManager('i18n');

Dependencies

I18nManager requires these dependencies:

  • ErrorManager - For error handling
  • ApiService or simpleFetch - For loading translation files
  • EventManager - For emitting events (optional)

Basic Usage

1. Initialization

// Basic initialization
await I18nManager.init({
  enabled: true,
  defaultLocale: 'en',
  availableLocales: ['en', 'th', 'ja'],
  storageKey: 'app_lang',
  useBrowserLocale: true,
  noTranslateEnglish: true
});

// Check current locale
console.log(I18nManager.getCurrentLocale()); // 'en'

2. Change Language

// Change language to Thai
await I18nManager.setLocale('th');

// Change language and force update
await I18nManager.setLocale('en', true);

3. Translate Text

// Translate using key
const greeting = I18nManager.translate('greeting.hello');
console.log(greeting); // 'Hello'

// Translate with parameters
const welcome = I18nManager.translate('greeting.welcome', {
  name: 'John'
});
console.log(welcome); // 'Welcome John'

4. Listen to Events

// Listen for locale change
Now.on('locale:changed', (data) => {
  console.log('Locale changed to:', data.locale);
  console.log('Forced:', data.forced);
});

// Listen for initialization
Now.on('i18n:initialized', () => {
  console.log('I18n system ready');
});

// Listen for element updates
Now.on('i18n:elements:updated', (data) => {
  console.log('Updated elements:', data.elementsUpdated);
  console.log('Current locale:', data.locale);
});

Translation Files

File Structure

Translation files must be in the translations/ folder in JSON format:

translations/
├── en.json
├── th.json
├── ja.json
└── zh.json

Translation File Format

// translations/en.json
{
  "app": {
    "name": "My Application",
    "tagline": "The best app ever"
  },
  "greeting": {
    "hello": "Hello",
    "goodbye": "Goodbye",
    "welcome": "Welcome {name}",
    "welcomeBack": "Welcome back, {name}!"
  },
  "button": {
    "save": "Save",
    "cancel": "Cancel",
    "delete": "Delete",
    "confirm": "Confirm"
  },
  "validation": {
    "required": "This field is required",
    "email": "Please enter a valid email",
    "minLength": "Minimum {min} characters required",
    "maxLength": "Maximum {max} characters allowed"
  },
  "message": {
    "success": "Operation completed successfully",
    "error": "An error occurred",
    "loading": "Loading..."
  }
}
// translations/th.json
{
  "app": {
    "name": "แอปพลิเคชันของฉัน",
    "tagline": "แอปที่ดีที่สุด"
  },
  "greeting": {
    "hello": "สวัสดี",
    "goodbye": "ลาก่อน",
    "welcome": "ยินดีต้อนรับ {name}",
    "welcomeBack": "ยินดีต้อนรับกลับมา {name}"
  },
  "button": {
    "save": "บันทึก",
    "cancel": "ยกเลิก",
    "delete": "ลบ",
    "confirm": "ยืนยัน"
  },
  "validation": {
    "required": "กรุณากรอกข้อมูลในช่องนี้",
    "email": "กรุณากรอกอีเมลให้ถูกต้อง",
    "minLength": "ต้องมีอย่างน้อย {min} ตัวอักษร",
    "maxLength": "มีได้สูงสุด {max} ตัวอักษร"
  },
  "message": {
    "success": "ดำเนินการสำเร็จ",
    "error": "เกิดข้อผิดพลาด",
    "loading": "กำลังโหลด..."
  }
}

Nested Keys

// Use dot notation for nested keys
I18nManager.translate('app.name'); // 'My Application'
I18nManager.translate('greeting.hello'); // 'Hello'
I18nManager.translate('validation.required'); // 'This field is required'

HTML Integration

data-i18n Attribute

<!-- Use data-i18n for auto-translation -->
<h1 data-i18n="app.name">My Application</h1>
<p data-i18n="app.tagline">The best app ever</p>

<!-- Buttons -->
<button data-i18n="button.save">Save</button>
<button data-i18n="button.cancel">Cancel</button>
<button data-i18n="button.delete">Delete</button>

<!-- Input placeholders -->
<input type="text" placeholder="Email" data-i18n="form.email">
<textarea placeholder="Message" data-i18n="form.message"></textarea>

Auto Translation

// When language changes, elements are auto-translated
await I18nManager.setLocale('th');
// <h1 data-i18n="app.name">แอปพลิเคชันของฉัน</h1>
// <button data-i18n="button.save">บันทึก</button>

Update Specific Container

// Update only in container
const container = document.querySelector('#content');
await I18nManager.updateElements(container);

// Update entire page
await I18nManager.updateElements();

Dynamic Translation

translate() Method

// Translate text in JavaScript
const greeting = I18nManager.translate('greeting.hello');
console.log(greeting); // 'Hello' or 'สวัสดี'

// Translate with fallback
const message = I18nManager.translate('unknown.key');
console.log(message); // 'unknown.key' (fallback to key)

Translate with Locale

// Translate in specific locale (without changing current locale)
const enGreeting = I18nManager.translate('greeting.hello', {}, 'en');
const thGreeting = I18nManager.translate('greeting.hello', {}, 'th');

console.log(enGreeting); // 'Hello'
console.log(thGreeting); // 'สวัสดี'

Get Translator Function

// Create translator function for specific locale
const t = I18nManager.getTranslator('th');

console.log(t('greeting.hello')); // 'สวัสดี'
console.log(t('button.save')); // 'บันทึก'

// With parameters
console.log(t('greeting.welcome', {name: 'John'})); // 'ยินดีต้อนรับ John'

Parameter Interpolation

Basic Interpolation

// Translation with {key} placeholders
// "welcome": "Welcome {name}"

const message = I18nManager.translate('greeting.welcome', {
  name: 'John'
});
console.log(message); // 'Welcome John'

Multiple Parameters

// "userInfo": "User {name} has {count} items"

const info = I18nManager.translate('user.info', {
  name: 'Alice',
  count: 5
});
console.log(info); // 'User Alice has 5 items'

Validation Messages

// "minLength": "Minimum {min} characters required"
// "maxLength": "Maximum {max} characters allowed"

const minMsg = I18nManager.translate('validation.minLength', {
  min: 8
});
console.log(minMsg); // 'Minimum 8 characters required'

const maxMsg = I18nManager.translate('validation.maxLength', {
  max: 100
});
console.log(maxMsg); // 'Maximum 100 characters allowed'

Nested Parameter Keys

// Parameters can reference other keys
// "message": "Welcome to {app.name}"

const welcome = I18nManager.translate('greeting.message');
console.log(welcome); // 'Welcome to My Application'

Locale Management

Get Current Locale

const currentLocale = I18nManager.getCurrentLocale();
console.log(currentLocale); // 'en'

Get Available Translations

// Get all translations for current locale
const translations = I18nManager.getTranslations();
console.log(translations);
// {
//   app: { name: 'My Application', ... },
//   greeting: { hello: 'Hello', ... },
//   ...
// }

// Get translations for specific locale
const thTranslations = I18nManager.getTranslations('th');

Get Key Translations

// Get translations of a key across all locales
const translations = I18nManager.getKeyTranslations('greeting.hello');
console.log(translations);
// {
//   en: 'Hello',
//   th: 'สวัสดี',
//   ja: 'こんにちは'
// }

Check Translation Exists

// Check if translation exists
const exists = I18nManager.hasTranslation('greeting.hello');
console.log(exists); // true

const notExists = I18nManager.hasTranslation('unknown.key');
console.log(notExists); // false

// Check in specific locale
const hasThTranslation = I18nManager.hasTranslation('greeting.hello', 'th');

Configuration

Configuration Options

const config = {
  // Enable/disable i18n system
  enabled: true,

  // Default locale
  defaultLocale: 'en',

  // Supported locales
  availableLocales: ['en', 'th', 'ja', 'zh'],

  // localStorage key for storing selected locale
  storageKey: 'app_lang',

  // Use browser language as default
  useBrowserLocale: true,

  // Skip translation for English
  // When true, English won't need to load translation file
  noTranslateEnglish: true
};

await I18nManager.init(config);

noTranslateEnglish Option

// When noTranslateEnglish = true
// English will return key directly without loading translation file

await I18nManager.init({
  enabled: true,
  defaultLocale: 'en',
  availableLocales: ['en', 'th'],
  noTranslateEnglish: true
});

// When locale is 'en', won't load en.json
await I18nManager.setLocale('en');
I18nManager.translate('greeting.hello'); // 'greeting.hello'

// When changed to 'th', will load th.json
await I18nManager.setLocale('th');
I18nManager.translate('greeting.hello'); // 'สวัสดี'

Disable I18n

// Disable i18n system
await I18nManager.init({
  enabled: false
});

// When disabled, all methods do nothing
await I18nManager.setLocale('th'); // does nothing
I18nManager.translate('key'); // returns 'key'

Usage Examples

1. Multi-language Website

// Initialize i18n
await I18nManager.init({
  enabled: true,
  defaultLocale: 'en',
  availableLocales: ['en', 'th', 'ja', 'zh'],
  storageKey: 'website_lang',
  useBrowserLocale: true
});

// HTML
/*
<nav>
  <select id="lang-selector">
    <option value="en">English</option>
    <option value="th">ไทย</option>
    <option value="ja">日本語</option>
    <option value="zh">中文</option>
  </select>
</nav>

<main>
  <h1 data-i18n="page.title">Welcome</h1>
  <p data-i18n="page.description">This is a multi-language website</p>
  <button data-i18n="button.getStarted">Get Started</button>
</main>
*/

// Language selector
const selector = document.querySelector('#lang-selector');
selector.value = I18nManager.getCurrentLocale();

selector.addEventListener('change', async (e) => {
  await I18nManager.setLocale(e.target.value);
});

// Listen for locale changes
Now.on('locale:changed', (data) => {
  console.log('Language changed to:', data.locale);

  // Update other UI as needed
  updateUI();
});

2. Form Validation with I18n

// Validation with translated messages
function validateForm(formData) {
  const errors = [];

  // Required field
  if (!formData.email) {
    errors.push(I18nManager.translate('validation.required'));
  }

  // Email format
  if (formData.email && !isValidEmail(formData.email)) {
    errors.push(I18nManager.translate('validation.email'));
  }

  // Min length
  if (formData.password && formData.password.length < 8) {
    errors.push(I18nManager.translate('validation.minLength', {
      min: 8
    }));
  }

  // Max length
  if (formData.bio && formData.bio.length > 500) {
    errors.push(I18nManager.translate('validation.maxLength', {
      max: 500
    }));
  }

  return errors;
}

// Usage
const formData = {
  email: '',
  password: '123',
  bio: 'Some text...'
};

const errors = validateForm(formData);
errors.forEach(error => {
  console.error(error);
});

3. Dynamic Content Translation

// Translate content loaded from API
async function loadUserProfile(userId) {
  const user = await fetchUser(userId);

  // Translate UI
  const container = document.querySelector('#profile');
  container.innerHTML = `
    <h2>${user.name}</h2>
    <p data-i18n="profile.memberSince">Member since</p>
    <p>${user.joinDate}</p>
    <button data-i18n="button.edit">Edit</button>
    <button data-i18n="button.save">Save</button>
  `;

  // Update translations
  await I18nManager.updateElements(container);
}

4. Notification Messages

// Create notification with translation
function showNotification(type, messageKey, params = {}) {
  const message = I18nManager.translate(messageKey, params);

  const notification = document.createElement('div');
  notification.className = `notification notification-${type}`;
  notification.textContent = message;

  document.body.appendChild(notification);

  setTimeout(() => notification.remove(), 3000);
}

// Usage
showNotification('success', 'message.success');
showNotification('error', 'message.error');
showNotification('info', 'user.welcomeBack', {name: 'John'});

5. Shopping Cart with I18n

// E-commerce cart
class ShoppingCart {
  constructor() {
    this.items = [];
  }

  addItem(product, quantity) {
    this.items.push({product, quantity});

    const message = I18nManager.translate('cart.itemAdded', {
      name: product.name,
      quantity: quantity
    });

    showNotification('success', message);
  }

  removeItem(index) {
    const item = this.items[index];
    this.items.splice(index, 1);

    const message = I18nManager.translate('cart.itemRemoved', {
      name: item.product.name
    });

    showNotification('info', message);
  }

  getTotal() {
    const total = this.items.reduce((sum, item) => {
      return sum + (item.product.price * item.quantity);
    }, 0);

    return I18nManager.translate('cart.total', {
      amount: total.toFixed(2)
    });
  }

  render() {
    const container = document.querySelector('#cart');

    if (this.items.length === 0) {
      container.innerHTML = `
        <p data-i18n="cart.empty">Your cart is empty</p>
      `;
    } else {
      container.innerHTML = `
        <h2 data-i18n="cart.title">Shopping Cart</h2>
        <ul>
          ${this.items.map(item => `
            <li>
              ${item.product.name} x ${item.quantity}
              <button data-i18n="button.remove">Remove</button>
            </li>
          `).join('')}
        </ul>
        <p>${this.getTotal()}</p>
        <button data-i18n="button.checkout">Checkout</button>
      `;
    }

    I18nManager.updateElements(container);
  }
}

6. Date and Time Localization

// Format dates by locale
function formatDate(date) {
  const locale = I18nManager.getCurrentLocale();

  const formatted = new Intl.DateTimeFormat(locale, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }).format(date);

  return formatted;
}

// Relative time
function getRelativeTime(date) {
  const now = new Date();
  const diff = now - date;

  const seconds = Math.floor(diff / 1000);
  const minutes = Math.floor(seconds / 60);
  const hours = Math.floor(minutes / 60);
  const days = Math.floor(hours / 24);

  if (days > 0) {
    return I18nManager.translate('time.daysAgo', {days});
  } else if (hours > 0) {
    return I18nManager.translate('time.hoursAgo', {hours});
  } else if (minutes > 0) {
    return I18nManager.translate('time.minutesAgo', {minutes});
  } else {
    return I18nManager.translate('time.justNow');
  }
}

// Usage
const date = new Date('2024-01-15');
console.log(formatDate(date)); // 'January 15, 2024' or '15 มกราคม 2024'

const postDate = new Date(Date.now() - 3600000); // 1 hour ago
console.log(getRelativeTime(postDate)); // '1 hour ago' or '1 ชั่วโมงที่แล้ว'

7. Multi-language Modal

// Modal with i18n
class Modal {
  constructor(contentKey, options = {}) {
    this.contentKey = contentKey;
    this.options = options;
  }

  show() {
    const modal = document.createElement('div');
    modal.className = 'modal';
    modal.innerHTML = `
      <div class="modal-overlay"></div>
      <div class="modal-content">
        <h2 data-i18n="${this.contentKey}.title">Title</h2>
        <p data-i18n="${this.contentKey}.message">Message</p>
        <div class="modal-actions">
          <button class="btn-cancel" data-i18n="button.cancel">Cancel</button>
          <button class="btn-confirm" data-i18n="button.confirm">Confirm</button>
        </div>
      </div>
    `;

    document.body.appendChild(modal);

    // Translate
    I18nManager.updateElements(modal);

    // Events
    modal.querySelector('.btn-cancel').addEventListener('click', () => {
      this.close();
    });

    modal.querySelector('.btn-confirm').addEventListener('click', () => {
      if (this.options.onConfirm) {
        this.options.onConfirm();
      }
      this.close();
    });

    modal.querySelector('.modal-overlay').addEventListener('click', () => {
      this.close();
    });

    this.modal = modal;
  }

  close() {
    if (this.modal) {
      this.modal.remove();
      this.modal = null;
    }
  }
}

// Usage
const deleteModal = new Modal('modal.deleteConfirm', {
  onConfirm: () => {
    deleteItem();
  }
});

deleteModal.show();

8. Search with Autocomplete

// Search autocomplete with i18n
class SearchAutocomplete {
  constructor(inputElement) {
    this.input = inputElement;
    this.results = [];
    this.setupUI();
  }

  setupUI() {
    // Placeholder
    const placeholder = I18nManager.translate('search.placeholder');
    this.input.setAttribute('placeholder', placeholder);

    // Results container
    this.container = document.createElement('div');
    this.container.className = 'autocomplete-results';
    this.input.parentElement.appendChild(this.container);

    // Events
    this.input.addEventListener('input', (e) => {
      this.search(e.target.value);
    });

    // Update on locale change
    Now.on('locale:changed', () => {
      const placeholder = I18nManager.translate('search.placeholder');
      this.input.setAttribute('placeholder', placeholder);
      this.render();
    });
  }

  async search(query) {
    if (!query) {
      this.results = [];
      this.render();
      return;
    }

    // Search API
    this.results = await searchAPI(query);
    this.render();
  }

  render() {
    if (this.results.length === 0) {
      this.container.innerHTML = `
        <div class="no-results" data-i18n="search.noResults">
          No results found
        </div>
      `;
    } else {
      this.container.innerHTML = `
        <div class="results-header" data-i18n="search.resultsCount">
          Found {count} results
        </div>
        <ul>
          ${this.results.map(result => `
            <li data-id="${result.id}">${result.name}</li>
          `).join('')}
        </ul>
      `;
    }

    I18nManager.updateElements(this.container);
  }
}

9. User Profile Settings

// User settings with language preference
class UserSettings {
  constructor(user) {
    this.user = user;
  }

  async saveLanguage(locale) {
    // Save to database
    await api.updateUser(this.user.id, {
      language: locale
    });

    // Change language
    await I18nManager.setLocale(locale);

    // Show notification
    const message = I18nManager.translate('settings.languageUpdated');
    showNotification('success', message);
  }

  render() {
    const container = document.querySelector('#settings');
    container.innerHTML = `
      <div class="settings-section">
        <h3 data-i18n="settings.language">Language</h3>
        <select id="language-select">
          <option value="en">English</option>
          <option value="th">ไทย</option>
          <option value="ja">日本語</option>
        </select>
      </div>

      <div class="settings-section">
        <h3 data-i18n="settings.notifications">Notifications</h3>
        <label>
          <input type="checkbox" id="email-notifications">
          <span data-i18n="settings.emailNotifications">Email notifications</span>
        </label>
      </div>

      <button data-i18n="button.save">Save</button>
    `;

    // Translate
    I18nManager.updateElements(container);

    // Set current language
    const select = container.querySelector('#language-select');
    select.value = I18nManager.getCurrentLocale();

    // Events
    select.addEventListener('change', (e) => {
      this.saveLanguage(e.target.value);
    });
  }
}

10. Error Messages

// Error handling with i18n
class ErrorHandler {
  static handle(error, context = {}) {
    let messageKey = 'error.unknown';
    let params = {};

    // Map error types to translation keys
    switch (error.code) {
      case 'NETWORK_ERROR':
        messageKey = 'error.network';
        break;
      case 'UNAUTHORIZED':
        messageKey = 'error.unauthorized';
        break;
      case 'NOT_FOUND':
        messageKey = 'error.notFound';
        params = {resource: context.resource};
        break;
      case 'VALIDATION_ERROR':
        messageKey = 'error.validation';
        params = {field: context.field};
        break;
      case 'SERVER_ERROR':
        messageKey = 'error.server';
        break;
      default:
        messageKey = 'error.unknown';
    }

    const message = I18nManager.translate(messageKey, params);

    // Show error
    showErrorNotification(message);

    // Log
    console.error(message, error);
  }
}

// Usage
try {
  await api.deleteUser(userId);
} catch (error) {
  ErrorHandler.handle(error, {
    resource: 'user',
    action: 'delete'
  });
}

API Reference

Configuration

{
  enabled: boolean,              // Enable/disable i18n system
  defaultLocale: string,         // Default locale
  availableLocales: string[],    // Supported locales
  storageKey: string,           // localStorage key
  useBrowserLocale: boolean,    // Use browser locale
  noTranslateEnglish: boolean   // Skip English translation
}

Methods

Initialization

// Initialize i18n system
init(options?: object): Promise<I18nManager>

Locale Management

// Set current locale
setLocale(locale: string, force?: boolean): Promise<void>

// Get current locale
getCurrentLocale(): string

Translation

// Translate a key
translate(key: string, params?: object, locale?: string): string

// Get translator function
getTranslator(locale: string): (key: string, params?: object) => string

// Get translations for locale
getTranslations(locale?: string): object

// Get translations for a key across all locales
getKeyTranslations(key: string): object

// Check if translation exists
hasTranslation(key: string, locale?: string): boolean

Element Updates

// Update translations in DOM
updateTranslations()

// Update specific elements
updateElements(container?: Element)

Internal Methods

// Load translations from file
loadTranslations(locale: string): Promise<void>

// Get translation with fallback
getTranslation(key: string, translations: object, params?: object): string

// Get fallback translation
getFallbackTranslation(key: string, params: object): string

// Interpolate parameters
interpolate(text: string, params: object, translations?: object): string

// Load initial locale
loadInitialLocale(): Promise<void>

Events

// I18n initialized
'i18n:initialized'

// Locale changed
'locale:changed' -> {
  locale: string,
  forced: boolean
}

// Elements updated
'i18n:elements:updated' -> {
  container: Element,
  elementsUpdated: number,
  locale: string
}

State

{
  current: string | null,        // Current locale
  initialized: boolean,          // Initialized
  translations: Map,            // Translation cache
  disabled: boolean             // System disabled
}

Best Practices

1. ✅ Use Nested Keys

// ✅ Good: organized structure
{
  "user": {
    "profile": {
      "name": "Name",
      "email": "Email"
    }
  }
}

I18nManager.translate('user.profile.name');

// ❌ Bad: flat structure
{
  "userProfileName": "Name",
  "userProfileEmail": "Email"
}

2. ✅ Use data-i18n for Static Content

<!-- ✅ Good: auto-translated -->
<h1 data-i18n="page.title">Welcome</h1>

<!-- ❌ Bad: manual translation required -->
<h1 id="title">Welcome</h1>
<script>
  document.getElementById('title').textContent =
    I18nManager.translate('page.title');
</script>

3. ✅ Keep Translations in Separate Files

// ✅ Good: separate files
translations/
  ├── en.json
  ├── th.json
  └── ja.json

// ❌ Bad: embedded in code
const translations = {
  en: {...},
  th: {...}
};

4. ✅ Use Parameters for Dynamic Content

// ✅ Good: parameterized
{
  "welcome": "Welcome {name}"
}
I18nManager.translate('welcome', {name: 'John'});

// ❌ Bad: string concatenation
{
  "welcome": "Welcome "
}
I18nManager.translate('welcome') + name;

5. ✅ Provide Fallback

// ✅ Good: use defaultLocale as fallback
await I18nManager.init({
  defaultLocale: 'en',
  availableLocales: ['en', 'th', 'ja']
});

// Missing translation will use key as fallback
I18nManager.translate('unknown.key'); // 'unknown.key'

6. ✅ Handle Loading States

// ✅ Good: show loading
async function changeLanguage(locale) {
  showLoading();
  try {
    await I18nManager.setLocale(locale);
  } catch (error) {
    showError(error);
  } finally {
    hideLoading();
  }
}

7. ✅ Use Consistent Key Naming

// ✅ Good: consistent naming
{
  "button.save": "Save",
  "button.cancel": "Cancel",
  "button.delete": "Delete"
}

// ❌ Bad: inconsistent
{
  "saveBtn": "Save",
  "cancelButton": "Cancel",
  "delete": "Delete"
}

8. ✅ Update Elements After Dynamic Content

// ✅ Good: update after loading
async function loadContent() {
  const data = await fetchData();

  const container = document.querySelector('#content');
  container.innerHTML = generateHTML(data);

  // Update translations
  await I18nManager.updateElements(container);
}

9. ✅ Store User Preference

// ✅ Good: automatically stored
await I18nManager.init({
  storageKey: 'app_lang' // Will remember selected language
});

// ✅ Good: sync with server
Now.on('locale:changed', async (data) => {
  await api.updateUserPreference({
    language: data.locale
  });
});

10. ✅ Test All Locales

// ✅ Good: test all languages
async function testTranslations() {
  const locales = ['en', 'th', 'ja'];

  for (const locale of locales) {
    await I18nManager.setLocale(locale);

    // Check important keys
    const keys = [
      'app.name',
      'button.save',
      'message.success'
    ];

    keys.forEach(key => {
      const translation = I18nManager.translate(key);
      console.log(`${locale} - ${key}:`, translation);
    });
  }
}

Summary

Key Features

Feature Supported Notes
Multiple Locales Unlimited
Auto Detection Browser locale
HTML Integration data-i18n attribute
Parameter Interpolation {key} syntax
Lazy Loading Load when needed
LocalStorage Remember language
Fallback Use key or default locale
Nested Keys Dot notation
Event System locale:changed event
No Translation Mode noTranslateEnglish

When to Use I18nManager

Use Case Use I18nManager? Notes
Multi-language app ✅ Yes Primary use case
User language preference ✅ Yes LocalStorage + API
Dynamic content translation ✅ Yes translate() method
Static HTML translation ✅ Yes data-i18n attribute
Form validation messages ✅ Yes Parameterized messages
Error messages ✅ Yes Consistent error handling
Date/Time formatting ⚠️ Partial Use with Intl API
Currency formatting ⚠️ Partial Use with Intl API