ICU MessageFormat vs gettext Comparison

Compare ICU MessageFormat with GNU gettext for internationalization. Covers syntax differences, plural handling, tooling, and when to use each approach.

Comparison

Detailed Explanation

ICU MessageFormat vs GNU gettext

ICU MessageFormat and GNU gettext are the two most widely used internationalization systems. Understanding their differences helps teams choose the right approach for their project.

Syntax Comparison

Simple string:

Feature ICU gettext
Variable Hello, {name} Hello, %s or Hello, %(name)s
Plural {n, plural, one {# file} other {# files}} ngettext("1 file", "%d files", n)
Gender {g, select, male {He} other {They}} Not supported natively

Plural Handling

ICU embeds all plural forms in a single message:

{count, plural,
    =0 {No messages}
    one {# message}
    other {# messages}
}

gettext uses separate function calls:

ngettext("One message", "%d messages", count)

For languages with more than 2 plural forms, gettext uses a Plural-Forms header in PO files:

"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n>=2 && n<=4 ? 1 : 2);\n"

ICU's approach is more powerful because:

  1. All plural forms are visible in one place
  2. Categories are named (one, few, many, other) not numbered (0, 1, 2)
  3. Exact matches (=0, =1) are supported inline
  4. Nesting with select/selectordinal is possible

Gender and Select

ICU natively supports select for arbitrary categories:

{gender, select,
    male {He replied.}
    female {She replied.}
    other {They replied.}
}

gettext has no built-in mechanism for this. Teams typically work around it with:

  • Multiple message IDs (reply_male, reply_female, reply_other)
  • Custom context functions
  • Post-processing string templates

Tooling Ecosystem

Aspect ICU gettext
File format JSON, XLIFF, ARB PO/POT (purpose-built)
Extraction Library-specific CLI xgettext (universal)
Translation tools Crowdin, Lokalise, Phrase Poedit, Weblate, Transifex
Runtime overhead Parsing at runtime Pre-compiled catalogs
Framework support React, Angular, Vue, Java C, Python, PHP, Ruby

When to Use ICU

  • Web applications (React, Angular, Vue) -- native library support
  • Mobile apps (iOS, Android) -- platform ICU support
  • Complex messages with plural + select combinations
  • New projects starting fresh with i18n

When to Use gettext

  • C/C++ applications -- deeply integrated
  • Python/Django/Flask -- mature ecosystem
  • PHP -- built-in gettext extension
  • Existing projects already using PO files
  • Simple messages without complex plural/select needs

Migration Path

Many teams start with gettext and migrate to ICU as their i18n needs grow. The migration typically involves:

  1. Converting PO files to JSON with ICU syntax
  2. Replacing printf-style placeholders with named arguments
  3. Converting ngettext calls to ICU plural
  4. Adding select for gender-aware messages
  5. Updating extraction and build tooling

Key Takeaway

ICU MessageFormat is the more modern and capable system, particularly for complex messages involving multiple variables, plural rules, and categorical selections. gettext remains a solid choice for projects in its native ecosystems (C, Python, PHP) with simpler i18n requirements.

Use Case

Engineering leads evaluating internationalization approaches for a new project, or teams considering migration from gettext to ICU MessageFormat for better plural and gender support.

Try It — ICU Message Format Tester

Open full tool