Multi-Language Localization Skill
Purpose
Implement proper internationalization (i18n) and localization (l10n) for static websites supporting multiple languages.
Core Principles
- Language Declaration
<!-- ✅ Always declare language --> <html lang="en">
<!-- For Swedish --> <html lang="sv">
<!-- For Arabic (RTL) --> <html lang="ar" dir="rtl">
- File Structure
index.html (en - English, default) index_sv.html (sv - Swedish) index_da.html (da - Danish) index_no.html (no - Norwegian) index_fi.html (fi - Finnish) index_de.html (de - German) index_fr.html (fr - French) index_es.html (es - Spanish) index_nl.html (nl - Dutch) index_ar.html (ar - Arabic, RTL) index_he.html (he - Hebrew, RTL) index_ja.html (ja - Japanese) index_ko.html (ko - Korean) index_zh.html (zh - Chinese)
- Language Switcher
<nav aria-label="Language selection"> <ul> <li><a href="index.html" hreflang="en">English</a></li> <li><a href="index_sv.html" hreflang="sv">Svenska</a></li> <li><a href="index_ar.html" hreflang="ar">العربية</a></li> </ul> </nav>
- RTL Support
/* RTL-specific styles */ [dir="rtl"] .element { text-align: right; direction: rtl; }
/* Use logical properties / .element { margin-inline-start: 1rem; / Not margin-left / padding-inline-end: 1rem; / Not padding-right */ }
- Cultural Considerations
-
Date formats (US: MM/DD/YYYY, EU: DD/MM/YYYY)
-
Number formats (1,000.00 vs 1.000,00)
-
Currency symbols
-
Text direction (LTR vs RTL)
-
Color meanings (cultural significance)
SEO Best Practices
Hreflang Tags
<link rel="alternate" hreflang="en" href="https://example.com/index.html"> <link rel="alternate" hreflang="sv" href="https://example.com/index_sv.html"> <link rel="alternate" hreflang="x-default" href="https://example.com/index.html">
Sitemap
<url> <loc>https://example.com/index.html</loc> <xhtml:link rel="alternate" hreflang="sv" href="https://example.com/index_sv.html"/> </url>
Testing
-
Native speaker review
-
RTL layout testing
-
Character encoding verification
-
Cultural appropriateness check
-
SEO validation (hreflang)
Remember
-
Native Speakers: Use professional translation
-
Cultural Context: Consider cultural differences
-
RTL Support: Test right-to-left languages
-
Consistent UX: Same experience across languages
-
SEO: Proper hreflang and sitemap
References
-
W3C Internationalization
-
MDN Localization
Number and Date Formatting (Production Data)
This section provides validated formatting from actual translated news articles (Feb 2026).
Number Formatting by Language
Thousands Separator and Decimal Point:
Language Thousands Decimal Example Usage
English (en) comma (,) period (.) 1,000.50 International standard
Swedish (sv) space ( ) comma (,) 1 000,50 Official Swedish standard
German (de) space ( ) or period (.) comma (,) 1 000,50 or 1.000,50 Space preferred
French (fr) space ( ) comma (,) 1 000,50 Official French standard
Spanish (es) space ( ) or period (.) comma (,) 1 000,50 or 1.000,50 Space preferred
Dutch (nl) period (.) comma (,) 1.000,50 Period for thousands
Danish (da) period (.) comma (,) 1.000,50 Period for thousands
Norwegian (no) space ( ) comma (,) 1 000,50 Official Norwegian standard
Finnish (fi) space ( ) comma (,) 1 000,50 Official Finnish standard
Japanese (ja) comma (,) period (.) 1,000.50 Western style
Korean (ko) comma (,) period (.) 1,000.50 Western style
Chinese (zh) comma (,) period (.) 1,000.50 Western style
Arabic (ar) comma (,) period (.) ١٬٠٠٠٫٥٠ or 1,000.50 Arabic-Indic optional
Hebrew (he) comma (,) period (.) 1,000.50 Western style
Implementation:
// Format numbers for language with documented separators function formatNumber(num, lang) { const formats = { 'en': { thousands: ',', decimal: '.' }, 'sv': { thousands: ' ', decimal: ',' }, 'de': { thousands: ' ', decimal: ',' }, 'fr': { thousands: ' ', decimal: ',' }, 'es': { thousands: ' ', decimal: ',' }, 'nl': { thousands: '.', decimal: ',' }, 'da': { thousands: '.', decimal: ',' }, 'no': { thousands: ' ', decimal: ',' }, 'fi': { thousands: ' ', decimal: ',' }, 'ja': { thousands: ',', decimal: '.' }, 'ko': { thousands: ',', decimal: '.' }, 'zh': { thousands: ',', decimal: '.' }, 'ar': { thousands: ',', decimal: '.' }, 'he': { thousands: ',', decimal: '.' } }; const fmt = formats[lang] || formats['en'];
// Normalize to two decimals, then trim to 0–2 as needed const isNegative = num < 0; const absolute = Math.abs(num); const fixed = absolute.toFixed(2); let [intPart, fracPart] = fixed.split('.');
// Insert thousands separators in the integer part intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, fmt.thousands);
// Trim trailing zeros from fractional part to allow 0–2 decimals fracPart = fracPart.replace(/0+$/, '');
let result = intPart; if (fracPart.length > 0) { result += fmt.decimal + fracPart; }
if (isNegative) { result = '-' + result; }
return result; }
Date Formatting by Language
From News Articles (Feb 2026):
Language Format Example Day-Month Order
English (en) Month D, YYYY February 15, 2026 Month first
Swedish (sv) D month YYYY 15 februari 2026 Day first
German (de) D. Month YYYY 15. Februar 2026 Day first (with period)
French (fr) D month YYYY 15 février 2026 Day first
Spanish (es) D de month de YYYY 15 de febrero de 2026 Day first (with "de")
Dutch (nl) D month YYYY 15 februari 2026 Day first
Danish (da) D. month YYYY 15. februar 2026 Day first (with period)
Norwegian (no) D. month YYYY 15. februar 2026 Day first (with period)
Finnish (fi) D. monthkuuta YYYY 15. helmikuuta 2026 Day first (genitive case)
Japanese (ja) YYYY年M月D日 2026年2月15日 Year-Month-Day
Korean (ko) YYYY년 M월 D일 2026년 2월 15일 Year-Month-Day
Chinese (zh) YYYY年M月D日 2026年2月15日 Year-Month-Day
Arabic (ar) D month YYYY ١٥ فبراير ٢٠٢٦ Day first (Arabic-Indic optional)
Hebrew (he) D bmonth YYYY 15 בפברואר 2026 Day first (with ב prefix)
Month Names:
English Swedish German French Spanish Dutch
January januari Januar janvier enero januari
February februari Februar février febrero februari
March mars März mars marzo maart
April april April avril abril april
May maj Mai mai mayo mei
June juni Juni juin junio juni
July juli Juli juillet julio juli
August augusti August août agosto augustus
September september September septembre septiembre september
October oktober Oktober octobre octubre oktober
November november November novembre noviembre november
December december Dezember décembre diciembre december
Day of Week:
English Swedish German French Spanish Dutch Finnish
Monday måndag Montag lundi lunes maandag maanantai
Tuesday tisdag Dienstag mardi martes dinsdag tiistai
Wednesday onsdag Mittwoch mercredi miércoles woensdag keskiviikko
Thursday torsdag Donnerstag jeudi jueves donderdag torstai
Friday fredag Freitag vendredi viernes vrijdag perjantai
Saturday lördag Samstag samedi sábado zaterdag lauantai
Sunday söndag Sonntag dimanche domingo zondag sunnuntai
Time Formatting
24-hour vs. 12-hour:
Language Clock Example Note
English (en) 12-hour 2:30 PM AM/PM required
Swedish (sv) 24-hour 14:30 Period or colon separator
German (de) 24-hour 14:30 Uhr "Uhr" suffix
French (fr) 24-hour 14h30 "h" separator
Spanish (es) 24-hour 14:30 Colon separator
Nordic (da, no, fi) 24-hour 14:30 or 14.30 Period common
CJK (ja, ko, zh) 24-hour 14:30 Colon separator
Arabic (ar) 12-hour ٢:٣٠ م Arabic-Indic optional
Hebrew (he) 24-hour 14:30 Colon separator
Currency Formatting
Swedish Krona (SEK):
Language Format Example Note
English SEK 1,000.50 or 1,000.50 kr SEK 1,000.50 Currency code preferred
Swedish 1 000,50 kr 1 000,50 kr "kr" suffix standard
German 1 000,50 SEK 1 000,50 SEK Currency code suffix
French 1 000,50 SEK 1 000,50 SEK Currency code suffix
Ordinal Numbers
Language Example Pattern
English 1st, 2nd, 3rd, 4th -st, -nd, -rd, -th
Swedish 1:a, 2:a, 3:e, 4:e :a or :e
German 1., 2., 3., 4. Period suffix
French 1er, 2e, 3e, 4e -er for first, -e for rest
Spanish 1.º, 2.º, 3.º, 4.º Masculine ordinal
Practical Implementation
Date Formatting Function:
function formatDate(date, lang) { const months = { en: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], sv: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'], de: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], fr: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], es: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'], da: ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'], no: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'], nl: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], fi: ['tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta', 'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta', 'lokakuuta', 'marraskuuta', 'joulukuuta'] };
const d = date.getDate(); const m = date.getMonth(); const y = date.getFullYear();
if (lang === 'en') return ${months[lang][m]} ${d}, ${y};
if (lang === 'de' || lang === 'da' || lang === 'no') return ${d}. ${months[lang][m]} ${y};
if (lang === 'es') return ${d} de ${months[lang][m]} de ${y};
if (lang === 'fi') return ${d}. ${months[lang][m]} ${y};
if (lang === 'ja') return ${y}年${m+1}月${d}日;
if (lang === 'ko') return ${y}년 ${m+1}월 ${d}일;
if (lang === 'zh') return ${y}年${m+1}月${d}日;
return ${d} ${months[lang]?.[m] || months['en'][m]} ${y}; // Default format with fallback
}
Validation Checklist
For each language version, verify:
-
Numbers use correct thousands separator
-
Numbers use correct decimal point
-
Dates follow language convention
-
Month names correctly translated and case-appropriate
-
Day names correctly translated
-
Time uses 24-hour format (except English)
-
Currency formatted per convention
-
Ordinals follow language pattern
Data Source: Validated against 81 news articles (Feb 2026)
Standards: Unicode CLDR, ISO 8601
Last Updated: 2026-02-15