next-intl renders static messages as plain strings, while messages with arguments use ICU syntax for interpolation, plurals, and rich text via the t() function's second parameter.
In next-intl, messages are defined in JSON files and rendered using the useTranslations hook (client/server) or getTranslations (async server). Static messages are returned as-is, while dynamic messages use ICU syntax to handle interpolation, plurals, dates, and rich text formatting through the second argument of the translation function.
Static messages contain no dynamic placeholders. They are rendered exactly as defined in the JSON file using t('key'). The return value is a plain string that can be used directly in components.
Dynamic values are inserted into messages using curly braces {placeholder}. The second argument to t() is an object where keys match the placeholders. Value names must be alphanumeric and can contain underscores; dashes are not supported.
Pluralization uses the plural ICU syntax. The # marker inserts the formatted number. Supported plural categories include zero, one, two, few, many, and other (varies by locale). Use selectordinal for ordinal numbers (e.g., 1st, 2nd, 3rd).
Dates and numbers can be formatted using ICU syntax within messages. Common formats include date, time, number, and currency. Use useFormatter for complex formatting needs outside messages.
For messages containing HTML-like tags that should map to React components, use t.rich(). Tags in the message are replaced with component functions. Self-closing tags require a closing tag (e.g., <br></br>) due to ICU parser requirements.
The select argument works like a switch statement for mapping identifiers to human-readable text. The other case is required as a fallback.
In async Server Components, use getTranslations() from next-intl/server. For static rendering with output: 'export', include generateStaticParams for the [locale] route and call setRequestLocale before using translations.
Messages can be nested for better organization using dot notation. The namespace passed to useTranslations should be the lowest common denominator containing all messages the component needs. Use useTranslations() without arguments to access the full tree.
Static messages are rendered directly from JSON without placeholders
Dynamic values use the second argument object: t('key', { arg: value })
ICU syntax supports pluralization (plural, selectordinal), selection (select), and date/number formatting
Rich text uses t.rich() to map tags to React components
Use getTranslations() for async Server Components, useTranslations() for Client Components and shared components
Enable static rendering with generateStaticParams and setRequestLocale
Namespace keys cannot contain '.' characters except to express nesting