{: .no_toc }
{: .no_toc .text-delta }
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. Also see the API docs for MessageFormatter
.
MessageFormatter
In ICU4J, the MessageFormatter
class is the next iteration of MessageFormat. This new version will build 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 succesor to MessageFormat
will result in 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 will be more modular and easier to port and backport. It will also provide 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 enough functions for MessageFormater
to be useful in many situations, but the final set of functions and the parameters accepted by those functions is not yet finalized.
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)); }
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} |
@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)); }
The tech preview implementation comes with formatters for numbers (number
), date / time (datetime
), plural selectors (plural
and selectordinal
), and general selector (select
), 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
).
These are the functions implemented right now:
If you don't have ICU set up, here are instructions for doing that using Maven or Gradle:
$ mvn archetype:generate -DgroupId=org.unicode -DartifactId=mf2 -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false $ cd mf2
In the pom.xml
find the <dependencies>
element and add this:
<dependency> <groupId>com.ibm.icu</groupId> <artifactId>icu4j</artifactId> <version>72.1</version> </dependency>
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.
$ mvn test
$ mkdir mf2 $ cd mf2 $ gradle init --dsl groovy --test-framework junit --type java-application --package org.unicode --project-name mf2
In the app/build.gradle
file, find the dependencies {...}
section add this:
implementation 'com.ibm.icu:icu4j:72.1'
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.
$ gradle test
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.