← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

ICU Message Format

i18n PHP 7.0+ Intermediate
debt(d7/e3/b5/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints.tools field is not specified. The common mistakes — string concatenation instead of placeholders, missing ext-intl, mixing ICU with sprintf — are not caught by default linters or compilers. Missing ext-intl may throw a runtime error, but incorrect usage patterns (concatenation, mixed formats) only surface during runtime testing or careful review. No standard PHP linter rule targets ICU format misuse by default.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix describes enabling ext-intl and switching to MessageFormatter::formatMessage or a framework wrapper. This is more than a one-line patch — it requires replacing concatenation patterns and potentially updating composer.json — but it stays within a localised component or translation layer rather than spanning the whole codebase.

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

Closest to 'persistent productivity tax' (b5). The choice applies to web and cli contexts broadly. Adopting ICU format means every developer touching the i18n layer must understand ICU syntax, every translator must receive ICU-formatted strings, and the ext-intl dependency must be tracked. It's not architectural (b7+), but it does impose a consistent ongoing cost across multiple work streams touching translations.

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

Closest to 'notable trap — a documented gotcha most devs eventually learn' (t5). The misconception field explicitly states that developers believe ICU is only for plurals, when it also handles gender (select), choice-based text, and contextual formatting. Additionally, mixing ICU with sprintf-style placeholders is an incompatibility trap many developers hit. These are documented gotchas that intermediate developers encounter but don't predict on first use.

About DEBT scoring →

Also Known As

ICU format MessageFormatter message format ICU syntax intl MessageFormatter

TL;DR

A standard syntax for translatable strings that handles pluralisation, gender, number formatting, and date formatting in a single expression — supported by PHP's intl extension via MessageFormatter.

Explanation

ICU Message Format is a syntax developed by IBM's International Components for Unicode project and now maintained by the Unicode Consortium. It enables translators to express all variations of a message in a single string, rather than requiring multiple string keys for different grammatical forms. The syntax supports: plural (count-based forms), select (choice-based forms like gender), number (locale-aware number formatting), date, time, and spellout formatters. A message like 'You have {count, plural, one {# unread message} other {# unread messages}}' is a single translation key that a translator handles in full context. PHP's intl extension provides MessageFormatter::formatMessage(); Symfony's Translation and Laravel's localisation system both have ICU support. The alternative — multiple string keys like 'message_singular' and 'message_plural' — fragments context and forces translators to guess the surrounding UI.

Common Misconception

ICU format is only needed for plural forms. ICU handles all grammatical variations that require different word choices based on runtime values — not just plurals. Gender agreement ('he submitted / she submitted'), choice-based text ('pending / active / cancelled'), and contextual formatting all benefit from ICU's select and plural constructs. Using ICU throughout the i18n layer ensures translators always have full sentence context, which produces better translations than fragmented string keys.

Why It Matters

ICU Message Format solves the context problem in translation: translators receive complete sentences with placeholders rather than fragments they must mentally assemble. A translator seeing 'item' with no context cannot know whether it refers to a cart item, a list item, or a menu item — and cannot know what grammatical form to use. ICU format gives them the entire sentence: 'You added {item_name} to your cart.' The PHP intl extension is enabled by default in most shared hosting and all modern PHP Docker images, making adoption straightforward.

Common Mistakes

  • Using string concatenation instead of placeholders — 'You have ' . $count . ' messages' cannot be correctly translated in languages where word order changes.
  • Not installing the intl PHP extension — MessageFormatter requires ext-intl, which must be listed as a composer.json require.
  • Mixing ICU format with sprintf-style placeholders — they are incompatible; choose one approach consistently.
  • Providing ICU messages only in English and expecting translators to infer the correct plural forms — always provide explicit plural categories for the source language.

Code Examples

✗ Vulnerable
// Fragmented strings + concatenation — untranslatable
$msg = 'You have ' . $count . ' ' . ($count === 1 ? 'message' : 'messages');
// Translator gets 3 separate strings with no context
// Word order fixed — wrong for Japanese, Arabic, German
✓ Fixed
// ICU format — full context, all variations in one string
$pattern = '{count, plural,
    =0    {You have no messages.}
    one   {You have one message.}
    other {You have {count} messages.}
}';
$result = MessageFormatter::formatMessage('en_US', $pattern, ['count' => $count]);

// With gender select
$pattern = '{gender, select, female {She} male {He} other {They}} submitted the form.';
$result  = MessageFormatter::formatMessage($locale, $pattern, ['gender' => $userGender]);

Added 23 Mar 2026
Edited 5 Apr 2026
Views 33
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 2 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Amazonbot 15 Perplexity 6 Ahrefs 3 Google 2 ChatGPT 1 Majestic 1 Meta AI 1
crawler 28 crawler_json 1
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: Low
⚡ Quick Fix
Enable ext-intl, use MessageFormatter::formatMessage($locale, $pattern, $args) — or use Symfony/Laravel translation which wraps ICU internally
📦 Applies To
PHP 7.0+ web cli

✓ schema.org compliant