Of course! java.text.MessageFormat is a powerful and versatile class in Java for creating and formatting strings with dynamic data. It's part of the standard Java library (java.text) and is designed to handle internationalization (i18n) and localization (l10n) gracefully.

Let's break it down, from the basics to more advanced use cases.
What is MessageFormat?
At its core, MessageFormat allows you to create template strings with placeholders. You then provide the actual values for these placeholders, and MessageFormat inserts them into the template, applying necessary formatting rules.
The key advantage over simple string concatenation () or String.format() is its ability to handle grammatical variations based on the input data (e.g., singular vs. plural, gender).
Basic Syntax
The pattern string uses curly braces to mark placeholders. The simplest form is {0}, {1}, {2}, etc., where the number corresponds to the index of the argument in the Object array you provide.

Example: Simple Formatting
import java.text.MessageFormat;
import java.util.Date;
public class MessageFormatDemo {
public static void main(String[] args) {
// 1. Simple argument replacement
String pattern = "Hello, {0}! Your balance is {1, number, currency}.";
Object[] arguments = {"Alice", 12345.67};
String formattedMessage = MessageFormat.format(pattern, arguments);
System.out.println(formattedMessage);
// Output: Hello, Alice! Your balance is $12,345.67. (Assuming default Locale)
}
}
Explanation:
{0}is replaced by the first element of theargumentsarray ("Alice").{1, number, currency}is more complex. It tellsMessageFormatto:- Take the argument at index 1 (
67). - Format it as a
number. - Use the
currencysubformat.
- Take the argument at index 1 (
Placeholders: The Anatomy of {argumentNumber, formatType, formatStyle}
A placeholder can have three optional parts, separated by commas:
{argumentNumber, formatType, formatStyle}
-
argumentNumber: The index of the argument in theObject[]array (e.g.,0,1,2). This is mandatory.
(图片来源网络,侵删) -
formatType: The general type of formatting to apply. Common types include:number: Formats the argument as a number. You can specify aformatStyle.date: Formats the argument as a date.time: Formats the argument as a time.choice: A special type for handling plurals and grammatical choices (see advanced section).
-
formatStyle: The specific style for theformatType. This can be:- A predefined keyword like
integer,currency,percent,short,medium,long,full. - A custom pattern string (e.g., for numbers or dates).
- A predefined keyword like
Example: Different Format Types
import java.text.MessageFormat;
import java.util.Date;
public class MessageFormatTypes {
public static void main(String[] args) {
Object[] arguments = {987654321L, new Date(), 0.75};
// Number formatting
String numberPattern = "The number is {0, number}. As currency: {0, number, currency}. As percent: {0, number, percent}.";
System.out.println(MessageFormat.format(numberPattern, arguments[0]));
// Output: The number is 987,654,321. As currency: $987,654,321.00. As percent: 75,000%.
// Date formatting
String datePattern = "Today is {0, date, long}.";
System.out.println(MessageFormat.format(datePattern, arguments[1]));
// Output: Today is October 26, 2025. (Example date)
// Custom number pattern
String customPattern = "Custom format: {0, number, #,###.00}";
System.out.println(MessageFormat.format(customPattern, 12345.678));
// Output: Custom format: 12,345.68
}
}
Advanced Feature: Plurals and Grammatical Choice (choice)
This is where MessageFormat truly shines. The choice format type allows you to define different output strings based on a numeric value, most commonly to handle singular and plural forms.
The syntax for the choice format is a bit different. You define a set of ranges, and for each range, you provide a template string.
Syntax: {argumentNumber, choice, min|template#max|template}
Example: Handling Plurals
Let's say we want to display a message about the number of files downloaded.
import java.text.MessageFormat;
public class MessageFormatChoice {
public static void main(String[] args) {
String choicePattern = "There {0, choice, 0#are no files|1#is one file|1<are {0, number, integer} files}.";
// Test with 0 files
System.out.println(MessageFormat.format(choicePattern, 0));
// Output: There are no files.
// Test with 1 file
System.out.println(MessageFormat.format(choicePattern, 1));
// Output: There is one file.
// Test with 5 files
System.out.println(MessageFormat.format(choicePattern, 5));
// Output: There are 5 files.
// Test with 1234 files
System.out.println(MessageFormat.format(choicePattern, 1234));
// Output: There are 1,234 files.
}
}
Explanation of the pattern:
{0, choice, 0#are no files|1#is one file|1<are {0, number, integer} files}
0#are no files: If the argument at index 0 is in the range0 <= x < 1, use the template"are no files".1#is one file: If the argument is in the range1 <= x < 1(i.e., exactly 1), use the template"is one file".1<are {0, number, integer} files: If the argument is in the rangex > 1, use the template"are {0, number, integer} files". Notice you can even nest another format specifier inside the choice template!
Internationalization (i18n) and Locale
MessageFormat is Locale-aware. The formatting of numbers, currencies, and dates changes based on the Locale you provide. This is crucial for building applications for a global audience.
Example: Locale Sensitivity
import java.text.MessageFormat;
import java.util.Locale;
public class MessageFormatLocale {
public static void main(String[] args) {
double amount = 1500.50;
Object[] arguments = {amount};
// Pattern for formatting a currency amount
String pattern = "The price is {0, number, currency}.";
// Format for US Locale
MessageFormat usFormat = new MessageFormat(pattern, Locale.US);
System.out.println(usFormat.format(arguments));
// Output: The price is $1,500.50.
// Format for French Locale (France)
MessageFormat frFormat = new MessageFormat(pattern, Locale.FRANCE);
System.out.println(frFormat.format(arguments));
// Output: The price is 1 500,50 €.
// Note: The space and comma/decimal point separators are different.
// Format for German Locale (Germany)
MessageFormat deFormat = new MessageFormat(pattern, Locale.GERMANY);
System.out.println(deFormat.format(arguments));
// Output: The price is 1.500,50 €.
}
}
MessageFormat vs. String.format()
It's common to wonder which one to use. Here's a quick comparison:
| Feature | MessageFormat |
String.format() |
|---|---|---|
| Primary Use Case | User-facing messages (labels, notifications, logs). | Programmatic string construction (e.g., SQL queries, file paths). |
| Argument Indexing | Named by position: {0}, {1}. Order in the array matters. |
Named by position: %1$s, %2$d. Order in the argument list matters. |
| Pluralization/Grammar | Excellent, built-in support via the choice format. |
No built-in support. You must implement it manually with if/else or ternary operators. |
| Formatting | Rich, Locale-aware formatting for numbers, dates, and times. |
Very powerful and precise formatting via format specifiers (e.g., %d, %f, %tF). |
| Performance | Generally slower due to parsing the pattern. | Generally faster for simple, repeated formatting. |
| Complexity | Pattern syntax can be complex, |
