{
    "slug": "locale_formatting",
    "term": "Locale-Aware Formatting",
    "category": "i18n",
    "difficulty": "intermediate",
    "short": "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.",
    "long": "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.",
    "aliases": [
        "NumberFormatter",
        "DateFormatter",
        "Intl extension",
        "ICU"
    ],
    "tags": [
        "i18n",
        "php",
        "formatting",
        "locale"
    ],
    "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."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "unicode_basics",
        "timezone_handling",
        "php_intl_extension",
        "php_intl_i18n"
    ],
    "prerequisites": [
        "php_intl_i18n",
        "unicode_basics",
        "timezone_handling"
    ],
    "refs": [
        "https://www.php.net/manual/en/class.numberformatter.php"
    ],
    "bad_code": "// Manual locale formatting — fragile:\n$price = 1234.56;\necho '€' . number_format($price, 2, ',', '.'); // '€1.234,56'\n// Hardcoded to German format — wrong for French (1 234,56 €)",
    "good_code": "// Intl NumberFormatter — correct for any locale:\n$formatter = new NumberFormatter('de-DE', NumberFormatter::CURRENCY);\necho $formatter->formatCurrency(1234.56, 'EUR'); // '1.234,56 €'\n\n$formatter = new NumberFormatter('fr-FR', NumberFormatter::CURRENCY);\necho $formatter->formatCurrency(1234.56, 'EUR'); // '1 234,56 €'\n\n// Date formatting:\n$fmt = new IntlDateFormatter('de-DE', IntlDateFormatter::LONG, IntlDateFormatter::NONE);\necho $fmt->format(new DateTime('2026-03-15')); // '15. März 2026'",
    "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",
    "severity": "medium",
    "effort": "medium",
    "created": "2026-03-15",
    "updated": "2026-03-22",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/locale_formatting",
        "html_url": "https://codeclaritylab.com/glossary/locale_formatting",
        "json_url": "https://codeclaritylab.com/glossary/locale_formatting.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Locale-Aware Formatting](https://codeclaritylab.com/glossary/locale_formatting) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/locale_formatting"
            }
        }
    }
}