← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Locale-Aware Formatting

i18n PHP 5.3+ Intermediate
debt(d5/e3/b5/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan and psalm, both specialist static analysis tools. The code_pattern shows hardcoded separators like number_format($price, 2, '.', ',') and date('d/m/Y') — these patterns are detectable by SAST/static analysers but won't be caught by compiler or default linter rules. Not silent in production (users see wrong output), but not caught instantly either.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix indicates replacing hardcoded format strings with NumberFormatter and DateFormatter calls — a straightforward swap within the same component or template layer. However, common_mistakes list four distinct failure modes (currency symbols, date strings, pluralisation, number formatting) meaning it's slightly more than a one-liner but still localised to formatting call sites rather than cross-cutting.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). The term applies_to both web and cli contexts broadly, meaning any output-rendering code across the codebase must be consistent about locale-aware formatting. Developers adding new display features must always consider locale, but it doesn't reshape overall architecture — it's a pervasive formatting discipline rather than a structural choice.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states that developers believe replacing '.' with ',' is sufficient for European number formatting, not realising it also affects grouping separators, currency symbols, percent signs, and date patterns. This contradicts the intuition that number formatting is a simple text substitution, and the common_mistakes reinforce multiple non-obvious failure dimensions — a competent developer will frequently guess wrong about the scope of the fix.

About DEBT scoring →

Also Known As

NumberFormatter DateFormatter Intl extension ICU

TL;DR

Formatting numbers, currencies, and dates according to locale conventions — 1,234.56 in en-US is 1.234,56 in de-DE, and currency symbols and date formats vary by locale.

Explanation

PHP's Intl extension (ICU-based) provides locale-aware formatting: NumberFormatter (numbers, currencies, percentages), DateFormatter (dates, times with locale-specific patterns), and MessageFormatter (pluralisation rules, placeholders). Locale identifiers use BCP 47 format (en-US, de-DE, zh-Hans-CN). Formatting must happen at the presentation layer — store raw values (1234.56, timestamps in UTC) and format for display.

Common Misconception

Replacing '.' with ',' is sufficient for European number formatting — locale formatting also affects grouping separators, currency symbols, percent signs, and date patterns.

Why It Matters

Displaying $1,234.56 to a German user when they expect 1.234,56 € is both confusing and potentially a trust issue — locale-aware formatting is user respect.

Common Mistakes

  • Hardcoding currency symbols — $ means dollars only in en-US context; use NumberFormatter with the currency code.
  • Manual date string replacement for localisation — different locales have completely different date structures.
  • Not handling pluralisation with MessageFormatter — '1 item' vs '2 items' varies by language in non-obvious ways.
  • Assuming number formatting is sufficient — order of day/month/year varies by locale and cannot be fixed by number formatting alone.

Code Examples

✗ Vulnerable
// Manual locale formatting — fragile:
$price = 1234.56;
echo '€' . number_format($price, 2, ',', '.'); // '€1.234,56'
// Hardcoded to German format — wrong for French (1 234,56 €)
✓ Fixed
// Intl NumberFormatter — correct for any locale:
$formatter = new NumberFormatter('de-DE', NumberFormatter::CURRENCY);
echo $formatter->formatCurrency(1234.56, 'EUR'); // '1.234,56 €'

$formatter = new NumberFormatter('fr-FR', NumberFormatter::CURRENCY);
echo $formatter->formatCurrency(1234.56, 'EUR'); // '1 234,56 €'

// Date formatting:
$fmt = new IntlDateFormatter('de-DE', IntlDateFormatter::LONG, IntlDateFormatter::NONE);
echo $fmt->format(new DateTime('2026-03-15')); // '15. März 2026'

Added 15 Mar 2026
Edited 22 Mar 2026
Views 72
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 1 ping T 0 pings F 1 ping S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F 0 pings S 1 ping S 1 ping M 1 ping T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 2 pings F
Applebot 1 PetalBot 1
No pings yesterday
Scrapy 11 Amazonbot 7 Perplexity 7 Ahrefs 5 ChatGPT 5 PetalBot 4 Unknown AI 3 Google 3 Bing 3 SEMrush 3 Claude 2 Majestic 1 Meta AI 1 Twitter/X 1 Applebot 1
crawler 50 crawler_json 6 pre-tracking 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Use NumberFormatter for currency (€1.234,56 in German, $1,234.56 in US), DateFormatter for dates — never hardcode format strings; locale determines thousands separator, decimal point, date order
📦 Applies To
PHP 5.3+ web cli
🔗 Prerequisites
🔍 Detection Hints
number_format($price, 2, '.', ',') hardcoded separators; date('d/m/Y') hardcoded format instead of locale-aware; currency symbol hardcoded
Auto-detectable: ✓ Yes phpstan psalm
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: File Tests: Update


✓ schema.org compliant