Multi-Language Localization Skill
🔴 AI FIRST Quality Principle
Apply the AI FIRST principle: never accept first-pass quality. Minimum 2 iterations. Read all output, improve every section. No shortcuts.
Purpose
Implement proper internationalization (i18n) and localization (l10n) for static websites supporting multiple languages.
Core Principles
1. Language Declaration
<!-- ✅ Always declare language -->
<html lang="en">
<!-- For Swedish -->
<html lang="sv">
<!-- For Arabic (RTL) -->
<html lang="ar" dir="rtl">
2. 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)
3. 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>
4. 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 */
}
5. 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
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:
Data Source: Validated against 81 news articles (Feb 2026)
Standards: Unicode CLDR, ISO 8601
Last Updated: 2026-02-15
1---2name: multi-language-localization3description: Best practices for multi-language static websites with proper i18n and l10n support4license: Apache-2.05---67# Multi-Language Localization Skill8910## 🔴 AI FIRST Quality Principle1112> **Apply the AI FIRST principle: never accept first-pass quality. Minimum 2 iterations. Read all output, improve every section. No shortcuts.**1314## Purpose1516Implement proper internationalization (i18n) and localization (l10n) for static websites supporting multiple languages.1718## Core Principles1920### 1. Language Declaration21```html22<!-- ✅ Always declare language -->23<html lang="en">2425<!-- For Swedish -->26<html lang="sv">2728<!-- For Arabic (RTL) -->29<html lang="ar" dir="rtl">30```3132### 2. File Structure33```34index.html (en - English, default)35index_sv.html (sv - Swedish)36index_da.html (da - Danish)37index_no.html (no - Norwegian)38index_fi.html (fi - Finnish)39index_de.html (de - German)40index_fr.html (fr - French)41index_es.html (es - Spanish)42index_nl.html (nl - Dutch)43index_ar.html (ar - Arabic, RTL)44index_he.html (he - Hebrew, RTL)45index_ja.html (ja - Japanese)46index_ko.html (ko - Korean)47index_zh.html (zh - Chinese)48```4950### 3. Language Switcher51```html52<nav aria-label="Language selection">53 <ul>54 <li><a href="index.html" hreflang="en">English</a></li>55 <li><a href="index_sv.html" hreflang="sv">Svenska</a></li>56 <li><a href="index_ar.html" hreflang="ar">العربية</a></li>57 </ul>58</nav>59```6061### 4. RTL Support62```css63/* RTL-specific styles */64[dir="rtl"] .element {65 text-align: right;66 direction: rtl;67}6869/* Use logical properties */70.element {71 margin-inline-start: 1rem; /* Not margin-left */72 padding-inline-end: 1rem; /* Not padding-right */73}74```7576### 5. Cultural Considerations77- Date formats (US: MM/DD/YYYY, EU: DD/MM/YYYY)78- Number formats (1,000.00 vs 1.000,00)79- Currency symbols80- Text direction (LTR vs RTL)81- Color meanings (cultural significance)8283## SEO Best Practices8485### Hreflang Tags86```html87<link rel="alternate" hreflang="en" href="https://example.com/index.html">88<link rel="alternate" hreflang="sv" href="https://example.com/index_sv.html">89<link rel="alternate" hreflang="x-default" href="https://example.com/index.html">90```9192### Sitemap93```xml94<url>95 <loc>https://example.com/index.html</loc>96 <xhtml:link rel="alternate" hreflang="sv" href="https://example.com/index_sv.html"/>97</url>98```99100## Testing101102- Native speaker review103- RTL layout testing104- Character encoding verification105- Cultural appropriateness check106- SEO validation (hreflang)107108## Remember109110- **Native Speakers**: Use professional translation111- **Cultural Context**: Consider cultural differences112- **RTL Support**: Test right-to-left languages113- **Consistent UX**: Same experience across languages114- **SEO**: Proper hreflang and sitemap115116## References117118- [W3C Internationalization](https://www.w3.org/International/)119- [MDN Localization](https://developer.mozilla.org/en-US/docs/Mozilla/Localization)120121---122123## Number and Date Formatting (Production Data)124125This section provides **validated formatting** from actual translated news articles (Feb 2026).126127### Number Formatting by Language128129**Thousands Separator and Decimal Point**:130131| Language | Thousands | Decimal | Example | Usage |132|----------|-----------|---------|---------|-------|133| English (en) | comma (,) | period (.) | 1,000.50 | International standard |134| Swedish (sv) | space ( ) | comma (,) | 1 000,50 | Official Swedish standard |135| German (de) | space ( ) or period (.) | comma (,) | 1 000,50 or 1.000,50 | Space preferred |136| French (fr) | space ( ) | comma (,) | 1 000,50 | Official French standard |137| Spanish (es) | space ( ) or period (.) | comma (,) | 1 000,50 or 1.000,50 | Space preferred |138| Dutch (nl) | period (.) | comma (,) | 1.000,50 | Period for thousands |139| Danish (da) | period (.) | comma (,) | 1.000,50 | Period for thousands |140| Norwegian (no) | space ( ) | comma (,) | 1 000,50 | Official Norwegian standard |141| Finnish (fi) | space ( ) | comma (,) | 1 000,50 | Official Finnish standard |142| Japanese (ja) | comma (,) | period (.) | 1,000.50 | Western style |143| Korean (ko) | comma (,) | period (.) | 1,000.50 | Western style |144| Chinese (zh) | comma (,) | period (.) | 1,000.50 | Western style |145| Arabic (ar) | comma (,) | period (.) | ١٬٠٠٠٫٥٠ or 1,000.50 | Arabic-Indic optional |146| Hebrew (he) | comma (,) | period (.) | 1,000.50 | Western style |147148**Implementation**:149```javascript150// Format numbers for language with documented separators151function formatNumber(num, lang) {152 const formats = {153 'en': { thousands: ',', decimal: '.' },154 'sv': { thousands: ' ', decimal: ',' },155 'de': { thousands: ' ', decimal: ',' },156 'fr': { thousands: ' ', decimal: ',' },157 'es': { thousands: ' ', decimal: ',' },158 'nl': { thousands: '.', decimal: ',' },159 'da': { thousands: '.', decimal: ',' },160 'no': { thousands: ' ', decimal: ',' },161 'fi': { thousands: ' ', decimal: ',' },162 'ja': { thousands: ',', decimal: '.' },163 'ko': { thousands: ',', decimal: '.' },164 'zh': { thousands: ',', decimal: '.' },165 'ar': { thousands: ',', decimal: '.' },166 'he': { thousands: ',', decimal: '.' }167 };168 const fmt = formats[lang] || formats['en'];169 170 // Normalize to two decimals, then trim to 0–2 as needed171 const isNegative = num < 0;172 const absolute = Math.abs(num);173 const fixed = absolute.toFixed(2);174 let [intPart, fracPart] = fixed.split('.');175 176 // Insert thousands separators in the integer part177 intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, fmt.thousands);178 179 // Trim trailing zeros from fractional part to allow 0–2 decimals180 fracPart = fracPart.replace(/0+$/, '');181 182 let result = intPart;183 if (fracPart.length > 0) {184 result += fmt.decimal + fracPart;185 }186 187 if (isNegative) {188 result = '-' + result;189 }190 191 return result;192}193```194195### Date Formatting by Language196197**From News Articles (Feb 2026)**:198199| Language | Format | Example | Day-Month Order |200|----------|--------|---------|-----------------|201| English (en) | Month D, YYYY | February 15, 2026 | Month first |202| Swedish (sv) | D month YYYY | 15 februari 2026 | Day first |203| German (de) | D. Month YYYY | 15. Februar 2026 | Day first (with period) |204| French (fr) | D month YYYY | 15 février 2026 | Day first |205| Spanish (es) | D de month de YYYY | 15 de febrero de 2026 | Day first (with "de") |206| Dutch (nl) | D month YYYY | 15 februari 2026 | Day first |207| Danish (da) | D. month YYYY | 15. februar 2026 | Day first (with period) |208| Norwegian (no) | D. month YYYY | 15. februar 2026 | Day first (with period) |209| Finnish (fi) | D. monthkuuta YYYY | 15. helmikuuta 2026 | Day first (genitive case) |210| Japanese (ja) | YYYY年M月D日 | 2026年2月15日 | Year-Month-Day |211| Korean (ko) | YYYY년 M월 D일 | 2026년 2월 15일 | Year-Month-Day |212| Chinese (zh) | YYYY年M月D日 | 2026年2月15日 | Year-Month-Day |213| Arabic (ar) | D month YYYY | ١٥ فبراير ٢٠٢٦ | Day first (Arabic-Indic optional) |214| Hebrew (he) | D bmonth YYYY | 15 בפברואר 2026 | Day first (with ב prefix) |215216**Month Names**:217218| English | Swedish | German | French | Spanish | Dutch |219|---------|---------|--------|--------|---------|-------|220| January | januari | Januar | janvier | enero | januari |221| February | februari | Februar | février | febrero | februari |222| March | mars | März | mars | marzo | maart |223| April | april | April | avril | abril | april |224| May | maj | Mai | mai | mayo | mei |225| June | juni | Juni | juin | junio | juni |226| July | juli | Juli | juillet | julio | juli |227| August | augusti | August | août | agosto | augustus |228| September | september | September | septembre | septiembre | september |229| October | oktober | Oktober | octobre | octubre | oktober |230| November | november | November | novembre | noviembre | november |231| December | december | Dezember | décembre | diciembre | december |232233**Day of Week**:234235| English | Swedish | German | French | Spanish | Dutch | Finnish |236|---------|---------|--------|--------|---------|-------|---------|237| Monday | måndag | Montag | lundi | lunes | maandag | maanantai |238| Tuesday | tisdag | Dienstag | mardi | martes | dinsdag | tiistai |239| Wednesday | onsdag | Mittwoch | mercredi | miércoles | woensdag | keskiviikko |240| Thursday | torsdag | Donnerstag | jeudi | jueves | donderdag | torstai |241| Friday | fredag | Freitag | vendredi | viernes | vrijdag | perjantai |242| Saturday | lördag | Samstag | samedi | sábado | zaterdag | lauantai |243| Sunday | söndag | Sonntag | dimanche | domingo | zondag | sunnuntai |244245### Time Formatting246247**24-hour vs. 12-hour**:248249| Language | Clock | Example | Note |250|----------|-------|---------|------|251| English (en) | 12-hour | 2:30 PM | AM/PM required |252| Swedish (sv) | 24-hour | 14:30 | Period or colon separator |253| German (de) | 24-hour | 14:30 Uhr | "Uhr" suffix |254| French (fr) | 24-hour | 14h30 | "h" separator |255| Spanish (es) | 24-hour | 14:30 | Colon separator |256| Nordic (da, no, fi) | 24-hour | 14:30 or 14.30 | Period common |257| CJK (ja, ko, zh) | 24-hour | 14:30 | Colon separator |258| Arabic (ar) | 12-hour | ٢:٣٠ م | Arabic-Indic optional |259| Hebrew (he) | 24-hour | 14:30 | Colon separator |260261### Currency Formatting262263**Swedish Krona (SEK)**:264265| Language | Format | Example | Note |266|----------|--------|---------|------|267| English | SEK 1,000.50 or 1,000.50 kr | SEK 1,000.50 | Currency code preferred |268| Swedish | 1 000,50 kr | 1 000,50 kr | "kr" suffix standard |269| German | 1 000,50 SEK | 1 000,50 SEK | Currency code suffix |270| French | 1 000,50 SEK | 1 000,50 SEK | Currency code suffix |271272### Ordinal Numbers273274| Language | Example | Pattern |275|----------|---------|---------|276| English | 1st, 2nd, 3rd, 4th | -st, -nd, -rd, -th |277| Swedish | 1:a, 2:a, 3:e, 4:e | :a or :e |278| German | 1., 2., 3., 4. | Period suffix |279| French | 1er, 2e, 3e, 4e | -er for first, -e for rest |280| Spanish | 1.º, 2.º, 3.º, 4.º | Masculine ordinal |281282### Practical Implementation283284**Date Formatting Function**:285```javascript286function formatDate(date, lang) {287 const months = {288 en: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],289 sv: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'],290 de: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],291 fr: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],292 es: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],293 da: ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'],294 no: ['januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'],295 nl: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],296 fi: ['tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta', 'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta', 'lokakuuta', 'marraskuuta', 'joulukuuta']297 };298 299 const d = date.getDate();300 const m = date.getMonth();301 const y = date.getFullYear();302 303 if (lang === 'en') return `${months[lang][m]} ${d}, ${y}`;304 if (lang === 'de' || lang === 'da' || lang === 'no') return `${d}. ${months[lang][m]} ${y}`;305 if (lang === 'es') return `${d} de ${months[lang][m]} de ${y}`;306 if (lang === 'fi') return `${d}. ${months[lang][m]} ${y}`;307 if (lang === 'ja') return `${y}年${m+1}月${d}日`;308 if (lang === 'ko') return `${y}년 ${m+1}월 ${d}일`;309 if (lang === 'zh') return `${y}年${m+1}月${d}日`;310 return `${d} ${months[lang]?.[m] || months['en'][m]} ${y}`; // Default format with fallback311}312```313314### Validation Checklist315316For each language version, verify:317- [ ] Numbers use correct thousands separator318- [ ] Numbers use correct decimal point319- [ ] Dates follow language convention320- [ ] Month names correctly translated and case-appropriate321- [ ] Day names correctly translated322- [ ] Time uses 24-hour format (except English)323- [ ] Currency formatted per convention324- [ ] Ordinals follow language pattern325326---327328**Data Source**: Validated against 81 news articles (Feb 2026) 329**Standards**: Unicode CLDR, ISO 8601 330**Last Updated**: 2026-02-15