MessageFormat 2.0

Contents

  1. Overview of MessageFormatter
    1. ICU4J
      1. Examples
        1. Basic usage
        2. Placeholder examples
      2. Plural selection message
      3. Built-in formatter functions
      4. Functions currently implemented
      5. Quickstart guide
        1. Requirements
        2. Maven
          1. Create a new project
          2. Add a dependency to ICU4J 75.1 (or newer)
          3. Add a bit of code
          4. Test it
        3. Gradle
          1. Create a new project
          2. Add a dependency to ICU4J 75.1 (or newer)
          3. Add a bit of code
        4. Test it
        5. Experiment from here
    2. ICU4C

Note: This page describes MessageFormatter, which is a Technical Preview API implementing MessageFormat 2.0. It will be a successor to the current ICU MessageFormat. MessageFormat 2.0 is being developed in a working group, which has created a draft specification. A version of the specification is included in LDML 45. Also see the API docs for MessageFormatter.

Overview of MessageFormatter

The MessageFormatter class is the next iteration of MessageFormat, implemented in both ICU4J and ICU4C This new version builds on the lessons learned from using MessageFormat for 25 years in various environments, when used directly or as a base for other public APIs.

The effort to design a successor to MessageFormat is a specification referred to as MessageFormat 2.0. The reasoning for this effort is shared in the “Why MessageFormat needs a successor” document.

MessageFormat 2.0 is more modular and easier to port and backport. It also provides extension points via interfaces to allow users to supply new formatters and selectors without having to modify the specification. ICU will eventually include support for new formatters, such as intervals, relative time, lists, measurement units, personal names, and more, as well as the ability for users to supply their own custom implementations. These will potentially support use cases like grammatical gender, inflection, markup regimes (such as those require for text-to-speech), and other complex message management needs.

The MessageFormat Working Group, which develops the new data model, semantics, and syntax, is hosted on GitHub. The current specification for the syntax and data model can be found here.

This technical preview implements functions according to the LDML 45 version of the specification.

ICU4J

Examples

Basic usage
import static org.junit.Assert.assertEquals;

import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import com.ibm.icu.message2.MessageFormatter;

@Test
public void testMf2() {
    final Locale enGb = Locale.forLanguageTag("en-GB");
    Map<String, Object> arguments = new HashMap<>();
    arguments.put("name", "John");
    arguments.put("exp", new Date(1679971371000L));  // March 27, 2023, 7:42:51 PM

    MessageFormatter mf2 = MessageFormatter.builder()
        .setPattern("{Hello {$name}, your card expires on {$exp :datetime skeleton=yMMMdE}!}")
        .setLocale(enGb)
        .build();

    assertEquals(
        "Hello John, your card expires on Mon, 27 Mar 2023!",
        mf2.formatToString(arguments));
}
Placeholder examples
Code to set runtime value for placeholder Examples of placeholder in message pattern
arguments.put("name", "John") {$name}
arguments.put("exp", new Date(…)) {$exp :datetime skeleton=yMMMdE}
{$exp :datetime datestyle=full}
arguments.put("val", 3.141592653) {$val}
{$val :number skeleton=(.####)}
No argument for fixed values known at build time {(123456789.531) :number}

Plural selection message

@Test
public void testMf2Selection() {
   final String message = ".match {$count :plural}"
           + " when 1 {You have one notification.}"
           + " when one {You have {$count} notification.}"
           + " when * {You have {$count} notifications.}";
   final Locale enGb = Locale.forLanguageTag("en-GB");
   Map<String, Object> arguments = new HashMap<>();


   MessageFormatter mf2 = MessageFormatter.builder()
       .setPattern(message)
       .setLocale(enGb)
       .build();


   arguments.put("count", 1);
   assertEquals(
       "You have one notification.",
       mf2.formatToString(arguments));


   arguments.put("count", 42);
   assertEquals(
       "You have 42 notifications.",
       mf2.formatToString(arguments));
}

Built-in formatter functions

The tech preview implementation comes with a formatter and selector for numbers (number), date / time formatters (datetime), and a string selector (string), very similar to what MessageFormat offers.

The ICU test code covers most features, and has examples of how to make custom placeholder formatters; you can look for classes that implement com.ibm.icu.message2.FormatterFactory (they are named Custom*Test.java).

Functions currently implemented

These are the functions implemented in ICU 75.1. The functions will change in a future release to be consistent with the current MF2 specification.

datetime Similar to MessageFormat's date and time.
datestyle and timestyle
Similar to argStyle : short | medium | long | full.
Same values are accepted, but we can use both in one placeholder, for example {$due :datetime datestyle=full timestyle=long}.
pattern
Similar to argStyle = argStyleText.
This is bad i18n practice, and will probably be dropped.
This is included just to support migration to MessageFormat 2.
skeleton
Same as argStyle = argSkeletonText.
These are the date/time skeletons as supported by com.ibm.icu.text.SimpleDateFormat.
number Similar to MessageFormat's number.
skeleton
These are the number skeletons as supported by com.ibm.icu.number.NumberFormatter.
minimumFractionDigits
Only implemented to be able to pass the unit tests from the ECMA tech preview implementation, which prefers options bags to skeletons.
TBD if the final function will support skeletons, option backs, or both.
offset
Used to support plural with an offset.
identityReturns the direct string value of the argument (calling toString()).
plural Similar to MessageFormat's plural.
skeleton
These are the number skeletons as supported by com.ibm.icu.number.NumberFormatter.
Can also be indirect, from a local variable of type number (recommended).
offset
Used to support plural with an offset.
Can also be indirect, from a local variable of type number (recommended).
selectordinal Similar to MessageFormat's selectordinal.
For now it accepts the same parameters as plural, although there is no use case for them.
TBD if this will be merged into plural (with some kind option) or not.
selectLiteral match, same as MessageFormat's select.

Quickstart guide

If you don’t have ICU set up, here are instructions for doing that using Maven or Gradle:

Requirements
  • JDK (version 8 or newer)
  • Maven or Gradle
  • Your preferred IDE or text editor
Maven
Create a new project
$ mvn archetype:generate -DgroupId=org.unicode -DartifactId=mf2 -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

$ cd mf2
Add a dependency to ICU4J 75.1 (or newer)

In the pom.xml find the <dependencies> element and add this:

<dependency>
    <groupId>com.ibm.icu</groupId>
    <artifactId>icu4j</artifactId>
    <version>75.1</version>
</dependency>
Add a bit of code

Open the test file (src/test/java/org/unicode/AppTest.java) and copy / paste the include directives and the testMf2() method shown in the previous section.

Test it
$ mvn test
Gradle
Create a new project
$ mkdir mf2

$ cd mf2

$ gradle init --dsl groovy --test-framework junit --type java-application --package org.unicode --project-name mf2
Add a dependency to ICU4J 75.1 (or newer)

In the app/build.gradle file, find the dependencies {...} section add this:

implementation 'com.ibm.icu:icu4j:72.1'
Add a bit of code

Open the test file (src/test/java/org/unicode/AppTest.java) and copy / paste the include directives and the testMf2() method shown in the previous section.

Test it
$ gradle test
Experiment from here

At this point you have a basic application using MessageFormat 2.

You can experiment with more messages using as inspiration:

You should be able to use your preferred IDE (Eclipse, IntelliJ, Visual Studio Code, more), use a different build system, etc.

ICU4C

Some helpful documentation for ICU4C in MF2 is being developed at messageformat.dev, as well an unofficial MF2 syntax documentation and an interactive playground for testing messages.