Quick Reference

i18n Quick Reference Guide

A condensed reference for common i18n tasks, patterns, and troubleshooting. Keep this handy during development and deployment.

Quick Commands

IntlPull CLI

Terminal
1# Validate before deployment
2npx @intlpullhq/cli validate --all
3
4# Check translation coverage
5npx @intlpullhq/cli status
6
7# Download translations
8npx @intlpullhq/cli download --branch main --languages en,es,fr
9
10# Upload new keys
11npx @intlpullhq/cli upload
12
13# Find missing translations
14npx @intlpullhq/cli check
15
16# Auto-fix missing translations
17npx @intlpullhq/cli fix

Git Workflow

Terminal
1# Create feature branch for new language
2git checkout -b feature/add-spanish
3
4# Check what changed
5npx @intlpullhq/cli diff
6
7# Commit translations
8git add locales/
9git commit -m "feat(i18n): add Spanish translations"

Common Patterns

React (next-intl)

TSX
1import { useTranslations } from 'next-intl';
2
3export default function Component() {
4  const t = useTranslations('namespace');
5  
6  // Simple translation
7  <h1>{t('title')}</h1>
8  
9  // With variables
10  <p>{t('welcome', { name: user.name })}</p>
11  
12  // With pluralization
13  <span>{t('items', { count: items.length })}</span>
14  
15  // With rich text
16  <p>{t.rich('terms', {
17    link: (chunks) => <a href="/terms">{chunks}</a>
18  })}</p>
19}

React (react-i18next)

TSX
1import { useTranslation } from 'react-i18next';
2
3export default function Component() {
4  const { t } = useTranslation('namespace');
5  
6  // Simple translation
7  <h1>{t('title')}</h1>
8  
9  // With variables
10  <p>{t('welcome', { name: user.name })}</p>
11  
12  // With pluralization
13  <span>{t('items', { count: items.length })}</span>
14}

Vue (vue-i18n)

VUE
1<template>
2  <!-- Simple translation -->
3  <h1>{{ $t('title') }}</h1>
4  
5  <!-- With variables -->
6  <p>{{ $t('welcome', { name: user.name }) }}</p>
7  
8  <!-- With pluralization -->
9  <span>{{ $tc('items', items.length) }}</span>
10</template>
11
12<script setup>
13import { useI18n } from 'vue-i18n';
14
15const { t, tc } = useI18n();
16</script>

Backend (Node.js)

TypeScript
1import i18next from 'i18next';
2
3// Initialize
4await i18next.init({
5  lng: 'en',
6  resources: {
7    en: { translation: require('./locales/en.json') },
8    es: { translation: require('./locales/es.json') },
9  },
10});
11
12// Use in API responses
13app.get('/api/error', (req, res) => {
14  const lang = req.headers['accept-language'] || 'en';
15  res.json({
16    error: i18next.t('errors.not_found', { lng: lang })
17  });
18});

Translation File Formats

JSON (Flat)

JSON
1{
2  "auth.login.title": "Sign In",
3  "auth.login.button": "Log In",
4  "auth.login.forgot": "Forgot password?"
5}

JSON (Nested)

JSON
1{
2  "auth": {
3    "login": {
4      "title": "Sign In",
5      "button": "Log In",
6      "forgot": "Forgot password?"
7    }
8  }
9}

JSON (with Pluralization)

JSON
1{
2  "items": {
3    "zero": "No items",
4    "one": "{{count}} item",
5    "other": "{{count}} items"
6  }
7}

YAML

YAML
1auth:
2  login:
3    title: Sign In
4    button: Log In
5    forgot: Forgot password?

ARB (Flutter)

JSON
1{
2  "@@locale": "en",
3  "loginTitle": "Sign In",
4  "@loginTitle": {
5    "description": "Title for login page"
6  },
7  "welcomeMessage": "Welcome, {name}!",
8  "@welcomeMessage": {
9    "description": "Welcome message with user name",
10    "placeholders": {
11      "name": {
12        "type": "String"
13      }
14    }
15  }
16}

Key Naming Conventions

Recommended Structure

{namespace}.{section}.{element}.{variant}

Examples

auth.login.button.submit auth.login.button.cancel auth.login.error.invalid_credentials auth.signup.form.email.label auth.signup.form.email.placeholder auth.signup.form.email.error.invalid dashboard.header.nav.home dashboard.header.nav.settings dashboard.header.user.greeting errors.validation.required errors.validation.email.invalid errors.validation.password.too_short common.button.save common.button.cancel common.button.delete common.loading common.error.generic

Anti-Patterns (Avoid)

button1 // Not descriptive text2 // Not descriptive loginButtonText // Inconsistent casing auth_login_button // Inconsistent separator authLoginButton // Inconsistent format

Locale Codes

Common Locales

en English (generic) en-US English (United States) en-GB English (United Kingdom) es Spanish (generic) es-ES Spanish (Spain) es-MX Spanish (Mexico) fr French (generic) fr-FR French (France) fr-CA French (Canada) de German de-DE German (Germany) de-AT German (Austria) it Italian pt Portuguese (generic) pt-BR Portuguese (Brazil) pt-PT Portuguese (Portugal) ja Japanese ko Korean zh Chinese (generic) zh-CN Chinese (Simplified) zh-TW Chinese (Traditional) ar Arabic he Hebrew ru Russian nl Dutch pl Polish tr Turkish

RTL Languages

ar Arabic he Hebrew fa Persian/Farsi ur Urdu

Date/Time Formatting

JavaScript (Intl API)

TypeScript
1// Date formatting
2const date = new Date();
3const formatter = new Intl.DateTimeFormat('es-ES', {
4  year: 'numeric',
5  month: 'long',
6  day: 'numeric'
7});
8formatter.format(date); // "12 de febrero de 2026"
9
10// Time formatting
11const timeFormatter = new Intl.DateTimeFormat('en-US', {
12  hour: 'numeric',
13  minute: 'numeric',
14  hour12: true
15});
16timeFormatter.format(date); // "3:45 PM"
17
18// Relative time
19const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
20rtf.format(-1, 'day');  // "yesterday"
21rtf.format(2, 'day');   // "in 2 days"

date-fns

TypeScript
1import { format } from 'date-fns';
2import { es, fr, de } from 'date-fns/locale';
3
4format(new Date(), 'PPP', { locale: es }); // "12 de febrero de 2026"
5format(new Date(), 'PPP', { locale: fr }); // "12 février 2026"
6format(new Date(), 'PPP', { locale: de }); // "12. Februar 2026"

Number/Currency Formatting

JavaScript (Intl API)

TypeScript
1// Number formatting
2const number = 1234567.89;
3new Intl.NumberFormat('de-DE').format(number);  // "1.234.567,89"
4new Intl.NumberFormat('en-US').format(number);  // "1,234,567.89"
5new Intl.NumberFormat('fr-FR').format(number);  // "1 234 567,89"
6
7// Currency formatting
8const price = 1234.56;
9new Intl.NumberFormat('en-US', { 
10  style: 'currency', 
11  currency: 'USD' 
12}).format(price);  // "$1,234.56"
13
14new Intl.NumberFormat('de-DE', { 
15  style: 'currency', 
16  currency: 'EUR' 
17}).format(price);  // "1.234,56 €"
18
19new Intl.NumberFormat('ja-JP', { 
20  style: 'currency', 
21  currency: 'JPY' 
22}).format(price);  // "¥1,235"
23
24// Percentage
25new Intl.NumberFormat('en-US', { 
26  style: 'percent' 
27}).format(0.85);  // "85%"

RTL Support

CSS

CSS
1/* Use logical properties */
2.element {
3  margin-inline-start: 1rem;  /* Instead of margin-left */
4  margin-inline-end: 1rem;    /* Instead of margin-right */
5  padding-inline: 1rem;       /* Instead of padding-left/right */
6}
7
8/* RTL-specific styles */
9[dir="rtl"] .element {
10  /* Styles for RTL languages */
11}
12
13/* Flip icons/images that indicate direction */
14[dir="rtl"] .arrow-right {
15  transform: scaleX(-1);
16}

HTML

HTML
1<!-- Set direction on html element -->
2<html lang="ar" dir="rtl">
3
4<!-- Or on specific elements -->
5<div dir="rtl">
6  <p>هذا نص عربي</p>
7</div>

React

TSX
1// Detect RTL
2const isRTL = ['ar', 'he', 'fa', 'ur'].includes(locale);
3
4// Apply direction
5<html lang={locale} dir={isRTL ? 'rtl' : 'ltr'}>

SEO Configuration

hreflang Tags

HTML
1<!-- In <head> -->
2<link rel="alternate" hreflang="en" href="https://example.com/en/page" />
3<link rel="alternate" hreflang="es" href="https://example.com/es/page" />
4<link rel="alternate" hreflang="fr" href="https://example.com/fr/page" />
5<link rel="alternate" hreflang="x-default" href="https://example.com/en/page" />

Next.js (App Router)

TSX
1// app/[locale]/layout.tsx
2export async function generateMetadata({ params }: { params: { locale: string } }) {
3  return {
4    alternates: {
5      canonical: `/${params.locale}`,
6      languages: {
7        'en': '/en',
8        'es': '/es',
9        'fr': '/fr',
10      },
11    },
12  };
13}

Sitemap

XML
1<?xml version="1.0" encoding="UTF-8"?>
2<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
3        xmlns:xhtml="http://www.w3.org/1999/xhtml">
4  <url>
5    <loc>https://example.com/en/page</loc>
6    <xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/page"/>
7    <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/page"/>
8  </url>
9</urlset>

Common Issues & Solutions

Issue: Translation keys showing instead of text

Cause: Translation file not loaded or key doesn't exist Solution:

Terminal
1# Check if translation exists
2npx @intlpullhq/cli check
3
4# Verify file is loaded
5console.log(i18n.getResourceBundle('en', 'namespace'));

Issue: Variables not substituting

Cause: Incorrect placeholder syntax Solution:

JSON
1// Correct
2{ "welcome": "Hello, {{name}}!" }
3
4// Incorrect
5{ "welcome": "Hello, {name}!" }
6{ "welcome": "Hello, $name!" }

Issue: Pluralization not working

Cause: Missing plural forms or incorrect count Solution:

JSON
1{
2  "items": {
3    "zero": "No items",
4    "one": "{{count}} item",
5    "other": "{{count}} items"
6  }
7}

Issue: Layout breaks with long translations

Cause: Fixed widths or insufficient space Solution:

CSS
1/* Use flexible layouts */
2.button {
3  min-width: 100px;
4  width: auto;
5  padding: 0.5rem 1rem;
6}
7
8/* Add overflow handling */
9.text {
10  overflow-wrap: break-word;
11  word-break: break-word;
12}

Issue: Special characters display as �

Cause: Incorrect encoding Solution:

HTML
<!-- Ensure UTF-8 encoding -->
<meta charset="UTF-8">
JSON
// Save files as UTF-8 without BOM

Issue: RTL layout broken

Cause: Using directional properties instead of logical Solution:

CSS
1/* Replace */
2margin-left: 1rem;
3
4/* With */
5margin-inline-start: 1rem;

Performance Optimization

Code Splitting

TypeScript
1// Lazy load translations
2const loadTranslations = async (locale: string) => {
3  const translations = await import(`./locales/${locale}.json`);
4  return translations.default;
5};

Caching

TypeScript
1// Cache translations in localStorage
2const CACHE_KEY = 'translations';
3const CACHE_VERSION = '1.0.0';
4
5function cacheTranslations(locale: string, data: any) {
6  localStorage.setItem(`${CACHE_KEY}_${locale}`, JSON.stringify({
7    version: CACHE_VERSION,
8    data,
9  }));
10}
11
12function getCachedTranslations(locale: string) {
13  const cached = localStorage.getItem(`${CACHE_KEY}_${locale}`);
14  if (!cached) return null;
15  
16  const { version, data } = JSON.parse(cached);
17  if (version !== CACHE_VERSION) return null;
18  
19  return data;
20}

Bundle Size

Terminal
1# Analyze bundle size
2npx @next/bundle-analyzer
3
4# Check translation file sizes
5du -sh locales/*
6
7# Minify translation files
8npx json-minify locales/en.json > locales/en.min.json

Pseudo-Localization

What is Pseudo-Localization?

Testing technique that simulates translation without actual translation, helping identify i18n issues early.

Implementation

TypeScript
1// Simple pseudo-localization function
2function pseudoLocalize(text: string): string {
3  const accents: Record<string, string> = {
4    'a': 'ȧ', 'e': 'ḗ', 'i': 'ī', 'o': 'ǿ', 'u': 'ŭ',
5    'A': 'Ȧ', 'E': 'Ḗ', 'I': 'Ī', 'O': 'Ǿ', 'U': 'Ŭ',
6  };
7  
8  // Add accents
9  let result = text.replace(/[aeiouAEIOU]/g, char => accents[char] || char);
10  
11  // Extend length by 30% (simulate German/Finnish)
12  result = `[${result}]`;
13  
14  return result;
15}
16
17// Usage
18pseudoLocalize("Hello World"); // "[Ḗḗllǿ Ẇǿrld]"

What to Test

  • All text displays correctly (no encoding issues)
  • UI doesn't break with longer text
  • No hardcoded strings (they won't be pseudo-localized)
  • Placeholders work correctly
  • Special characters display properly

Mobile Examples

iOS (Swift)

Swift
1// Simple localization
2let title = NSLocalizedString("login.title", comment: "Login screen title")
3
4// With variables
5let welcome = String(format: NSLocalizedString("welcome.message", comment: ""), userName)
6
7// Pluralization
8let itemCount = String.localizedStringWithFormat(
9    NSLocalizedString("items.count", comment: ""),
10    count
11)
12
13// Date formatting
14let formatter = DateFormatter()
15formatter.locale = Locale.current
16formatter.dateStyle = .medium
17let dateString = formatter.string(from: date)

Android (Kotlin)

Kotlin
1// Simple localization
2val title = getString(R.string.login_title)
3
4// With variables
5val welcome = getString(R.string.welcome_message, userName)
6
7// Pluralization
8val itemCount = resources.getQuantityString(
9    R.plurals.items_count,
10    count,
11    count
12)
13
14// Date formatting
15val formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())
16val dateString = formatter.format(date)

React Native

TypeScript
1import { useTranslation } from 'react-i18next';
2import { Platform } from 'react-native';
3
4export default function Component() {
5  const { t } = useTranslation();
6  
7  // Simple translation
8  <Text>{t('login.title')}</Text>
9  
10  // Platform-specific
11  <Text>{t(`common.action_${Platform.OS}`)}</Text>
12  // Uses: common.action_ios or common.action_android
13  
14  // With variables
15  <Text>{t('welcome.message', { name: user.name })}</Text>
16}

Security Best Practices

Input Sanitization

TypeScript
1import DOMPurify from 'dompurify';
2
3// Sanitize before displaying
4function SafeTranslation({ text }: { text: string }) {
5  const sanitized = DOMPurify.sanitize(text);
6  return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
7}

Locale Validation

TypeScript
1// Whitelist allowed locales
2const ALLOWED_LOCALES = ['en', 'es', 'fr', 'de', 'ja', 'ar'];
3
4function validateLocale(locale: string): string {
5  const normalized = locale.toLowerCase().split('-')[0];
6  return ALLOWED_LOCALES.includes(normalized) ? normalized : 'en';
7}
8
9// Use in route handler
10app.get('/:locale/*', (req, res) => {
11  const locale = validateLocale(req.params.locale);
12  // ... rest of handler
13});

Translation File Integrity

TypeScript
1import crypto from 'crypto';
2
3// Generate checksum for translation file
4function generateChecksum(content: string): string {
5  return crypto.createHash('sha256').update(content).digest('hex');
6}
7
8// Verify before loading
9async function loadTranslations(locale: string) {
10  const content = await fetch(`/locales/${locale}.json`).then(r => r.text());
11  const checksum = generateChecksum(content);
12  
13  // Verify against known good checksum
14  if (checksum !== KNOWN_CHECKSUMS[locale]) {
15    throw new Error('Translation file integrity check failed');
16  }
17  
18  return JSON.parse(content);
19}

Performance Tips

Lazy Loading

TypeScript
1// Next.js - lazy load translations
2import dynamic from 'next/dynamic';
3
4const TranslationProvider = dynamic(
5  () => import('./TranslationProvider'),
6  { ssr: false }
7);

Memoization

TypeScript
1import { useMemo } from 'react';
2
3function useTranslation(namespace: string) {
4  const translations = useMemo(
5    () => loadTranslations(namespace),
6    [namespace]
7  );
8  
9  return translations;
10}

Service Worker Caching

TypeScript
1// Cache translation files
2self.addEventListener('install', (event) => {
3  event.waitUntil(
4    caches.open('translations-v1').then((cache) => {
5      return cache.addAll([
6        '/locales/en.json',
7        '/locales/es.json',
8        '/locales/fr.json',
9      ]);
10    })
11  );
12});

Testing Checklist

Pre-Deployment

  • All translations complete for target languages
  • No missing translation keys
  • Placeholders work correctly
  • Pluralization works for all languages
  • Date/time formatting correct
  • Number/currency formatting correct
  • RTL layout works (if applicable)
  • SEO metadata translated
  • hreflang tags configured
  • All pages tested in all languages

Visual Testing

  • Test with longest translations (German, Finnish)
  • Test with shortest translations (Chinese, Japanese)
  • Test button text doesn't overflow
  • Test form layouts
  • Test navigation menus
  • Test modals and dialogs
  • Test mobile responsive layouts

Browser Testing

  • Chrome
  • Firefox
  • Safari
  • Edge
  • iOS Safari
  • Android Chrome

Useful Resources

Documentation

Tools

Communities


Quick Tips:

  1. Always use translation keys, never hardcode strings
  2. Provide context for translators (comments, screenshots)
  3. Test with real translations, not Lorem Ipsum
  4. Use logical CSS properties for RTL support
  5. Validate translations before deployment
  6. Monitor for missing translations in production
  7. Keep translation files in version control
  8. Automate translation validation in CI/CD
  9. Test on real devices, not just emulators
  10. Plan for growth - design for 50+ languages from day one