• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2022 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 package com.ibm.icu.message2;
5 
6 import java.util.Locale;
7 import java.util.Map;
8 
9 /**
10  * {@code MessageFormatter} is the next iteration of {@link com.ibm.icu.text.MessageFormat}.
11  *
12  * <p>This new version builds on what we learned from using {@code MessageFormat} for 20 years
13  * in various environments, either exposed "as is" or as a base for other public APIs.</p>
14  *
15  * <p>It is more modular, easier to backport, and provides extension points to add new
16  * formatters and selectors without having to modify the specification.</p>
17  *
18  * <p>We will be able to add formatters for intervals, relative times, lists, measurement units,
19  * people names, and more, and support custom formatters implemented by developers
20  * outside of ICU itself, for company or even product specific needs.</p>
21  *
22  * <p>MessageFormat 2 will support more complex grammatical features, such as gender, inflections,
23  * and tagging parts of the message for style changes or speech.</p>
24  *
25  * <p>The reasoning for this effort is shared in the
26  * <a target="github" href="https://github.com/unicode-org/message-format-wg/blob/main/docs/why_mf_next.md">“Why
27  * MessageFormat needs a successor”</a> document.</p>
28  *
29  * <p>The “MessageFormat 2” project, which develops the new data model, semantics, and syntax,
30  * is hosted on <a target="github" href="https://github.com/unicode-org/message-format-wg">GitHub</a>.</p>
31  *
32  * <p>The current specification for the syntax and data model can be found
33  * <a target="github" href="https://github.com/unicode-org/message-format-wg/blob/main/spec/syntax.md">here</a>.</p>
34  *
35  * <p>This tech preview implements enough of the {@code MessageFormat} functions to be useful,
36  * but the final set of functions and the parameters accepted by those functions is not yet finalized.</p>
37  *
38  * <p>These are the functions interpreted right now:</p>
39  *
40  * <table border="1">
41  * <tr>
42  *   <td rowspan="4">{@code datetime}</td>
43  *   <td>Similar to the ICU {@code "date"} and {@code "time"}.</td>
44  * </tr>
45  *
46  *   <tr><td>{@code datestyle} and {@code timestyle}<br>
47  *   Similar to {@code argStyle : short | medium | long | full}.<br>
48  *   Same values are accepted, but we can use both in one placeholder,
49  *   for example <code>{$due :datetime datestyle=full timestyle=long}</code>.
50  *   </td></tr>
51  *
52  *   <tr><td>{@code pattern}<br>
53  *   Similar to {@code argStyle = argStyleText}.<br>
54  *   This is bad i18n practice, and will probably be dropped.<br>
55  *   This is included just to support migration to MessageFormat 2.
56  *   </td></tr>
57  *
58  *   <tr><td>{@code skeleton}<br>
59  *   Same as {@code argStyle = argSkeletonText}.<br>
60  *   These are the date/time skeletons as supported by {@link com.ibm.icu.text.SimpleDateFormat}.
61  *   </td></tr>
62  *
63  * <tr>
64  *   <td rowspan="4">{@code number}</td>
65  *   <td>Similar to the ICU "number".</td>
66  * </tr>
67  *
68  *   <tr><td>{@code skeleton}<br>
69  *   These are the number skeletons as supported by {@link com.ibm.icu.number.NumberFormatter}.</td></tr>
70  *
71  *   <tr><td>{@code minimumFractionDigits}<br>
72  *   Only implemented to be able to pass the unit tests from the ECMA tech preview implementation,
73  *   which prefers options bags to skeletons.<br>
74  *   TBD if the final {@number} function will support skeletons, option backs, or both.</td></tr>
75  *
76  *   <tr><td>{@code offset}<br>
77  *   Used to support plural with an offset.</td></tr>
78  *
79  * <tr><td >{@code identity}</td><td>Returns the direct string value of the argument (calling {@code toString()}).</td></tr>
80  *
81  * <tr>
82  *   <td rowspan="3">{@code plural}</td>
83  *   <td>Similar to the ICU {@code "plural"}.</td>
84  * </tr>
85  *
86  *   <tr><td>{@code skeleton}<br>
87  *   These are the number skeletons as supported by {@link com.ibm.icu.number.NumberFormatter}.<br>
88  *   Can also be indirect, from a local variable of type {@code number} (recommended).</td></tr>
89  *
90  *   <tr><td>{@code offset}<br>
91  *   Used to support plural with an offset.<br>
92  *   Can also be indirect, from a local variable of type {@code number} (recommended).</td></tr>
93  *
94  * <tr>
95  *   <td>{@code selectordinal}</td>
96  *   <td>Similar to the ICU {@code "selectordinal"}.<br>
97  * For now it accepts the same parameters as "plural", although there is no use case for them.<br>
98  * TBD if this will be merged into "plural" (with some {@code kind} option) or not.</td></tr>
99  *
100  * <tr><td>{@code select}</td><td>Literal match, same as the ICU4 {@code "select"}.</td></tr>
101  * </table>
102  *
103  * @internal ICU 72 technology preview
104  * @deprecated This API is for technology preview only.
105  */
106 @Deprecated
107 public class MessageFormatter {
108     private final Locale locale;
109     private final String pattern;
110     private final Mf2FunctionRegistry functionRegistry;
111     private final Mf2DataModel dataModel;
112     private final Mf2DataModelFormatter modelFormatter;
113 
MessageFormatter(Builder builder)114     private MessageFormatter(Builder builder) {
115         this.locale = builder.locale;
116         this.functionRegistry = builder.functionRegistry;
117         if ((builder.pattern == null && builder.dataModel == null)
118                 || (builder.pattern != null && builder.dataModel != null)) {
119             throw new IllegalArgumentException("You need to set either a pattern, or a dataModel, but not both.");
120         }
121 
122         if (builder.dataModel != null) {
123             this.dataModel = builder.dataModel;
124             this.pattern = Mf2Serializer.dataModelToString(this.dataModel);
125         } else {
126             this.pattern = builder.pattern;
127             Mf2Serializer tree = new Mf2Serializer();
128             Mf2Parser parser = new Mf2Parser(pattern, tree);
129             try {
130                 parser.parse_Message();
131                 dataModel = tree.build();
132             } catch (Mf2Parser.ParseException pe) {
133                 throw new IllegalArgumentException(
134                         "Parse error:\n"
135                         + "Message: <<" + pattern + ">>\n"
136                         + "Error:" + parser.getErrorMessage(pe) + "\n");
137             }
138         }
139         modelFormatter = new Mf2DataModelFormatter(dataModel, locale, functionRegistry);
140     }
141 
142     /**
143      * Creates a builder.
144      *
145      * @return the Builder.
146      *
147      * @internal ICU 72 technology preview
148      * @deprecated This API is for technology preview only.
149      */
150     @Deprecated
builder()151     public static Builder builder() {
152         return new Builder();
153     }
154 
155     /**
156      * Get the locale to use for all the formatting and selections in
157      * the current {@code MessageFormatter}.
158      *
159      * @return the locale.
160      *
161      * @internal ICU 72 technology preview
162      * @deprecated This API is for technology preview only.
163      */
164     @Deprecated
getLocale()165     public Locale getLocale() {
166         return locale;
167     }
168 
169     /**
170      * Get the pattern (the serialized message in MessageFormat 2 syntax) of
171      * the current {@code MessageFormatter}.
172      *
173      * <p>If the {@code MessageFormatter} was created from an {@link Mf2DataModel}
174      * the this string is generated from that model.</p>
175      *
176      * @return the pattern.
177      *
178      * @internal ICU 72 technology preview
179      * @deprecated This API is for technology preview only.
180      */
181     @Deprecated
getPattern()182     public String getPattern() {
183         return pattern;
184     }
185 
186     /**
187      * Give public access to the message data model.
188      *
189      * <p>This data model is similar to the functionality we have today
190      * in {@link com.ibm.icu.text.MessagePatternUtil} maybe even a bit more higher level.</p>
191      *
192      * <p>We can also imagine a model where one parses the string syntax, takes the data model,
193      * modifies it, and then uses that modified model to create a {@code MessageFormatter}.</p>
194      *
195      * @return the data model.
196      *
197      * @internal ICU 72 technology preview
198      * @deprecated This API is for technology preview only.
199      */
200     @Deprecated
getDataModel()201     public Mf2DataModel getDataModel() {
202         return dataModel;
203     }
204 
205     /**
206      * Formats a map of objects by iterating over the MessageFormat's pattern,
207      * with the plain text “as is” and the arguments replaced by the formatted objects.
208      *
209      * @param arguments a map of objects to be formatted and substituted.
210      * @return the string representing the message with parameters replaced.
211      *
212      * @throws IllegalArgumentException when something goes wrong
213      *         (for example wrong argument type, or null arguments, etc.)
214      *
215      * @internal ICU 72 technology preview
216      * @deprecated This API is for technology preview only.
217      */
218     @Deprecated
formatToString(Map<String, Object> arguments)219     public String formatToString(Map<String, Object> arguments) {
220         return modelFormatter.format(arguments);
221     }
222 
223     /**
224      * Not yet implemented: formats a map of objects by iterating over the MessageFormat's
225      * pattern, with the plain text “as is” and the arguments replaced by the formatted objects.
226      *
227      * @param arguments a map of objects to be formatted and substituted.
228      * @return the {@link FormattedMessage} class representing the message with parameters replaced.
229      *
230      * @internal ICU 72 technology preview
231      * @deprecated This API is for technology preview only.
232      */
233     @Deprecated
format(Map<String, Object> arguments)234     public FormattedMessage format(Map<String, Object> arguments) {
235         throw new RuntimeException("Not yet implemented.");
236     }
237 
238     /**
239      * A {@code Builder} used to build instances of {@link MessageFormatter}.
240      *
241      * @internal ICU 72 technology preview
242      * @deprecated This API is for technology preview only.
243      */
244     @Deprecated
245     public static class Builder {
246         private Locale locale = Locale.getDefault(Locale.Category.FORMAT);
247         private String pattern = null;
248         private Mf2FunctionRegistry functionRegistry = Mf2FunctionRegistry.builder().build();
249         private Mf2DataModel dataModel = null;
250 
251         // Prevent direct creation
Builder()252         private Builder() {
253         }
254 
255         /**
256          * Sets the locale to use for all formatting and selection operations.
257          *
258          * @param locale the locale to set.
259          * @return the builder, for fluent use.
260          *
261          * @internal ICU 72 technology preview
262          * @deprecated This API is for technology preview only.
263          */
264         @Deprecated
setLocale(Locale locale)265         public Builder setLocale(Locale locale) {
266             this.locale = locale;
267             return this;
268         }
269 
270         /**
271          * Sets the pattern (in MessageFormat 2 syntax) used to create the message.<br>
272          * It conflicts with the data model, so it will reset it (the last call on setter wins).
273          *
274          * @param pattern the pattern to set.
275          * @return the builder, for fluent use.
276          *
277          * @internal ICU 72 technology preview
278          * @deprecated This API is for technology preview only.
279          */
280         @Deprecated
setPattern(String pattern)281         public Builder setPattern(String pattern) {
282             this.pattern = pattern;
283             this.dataModel = null;
284             return this;
285         }
286 
287         /**
288          * Sets an instance of {@link Mf2FunctionRegistry} that should register any
289          * custom functions used by the message.
290          *
291          * <p>There is no need to do this in order to use standard functions
292          * (for example date / time / number formatting, plural / ordinal / literal selection).<br>
293          * The exact set of standard functions, with the types they format and the options
294          * they accept is still TBD.</p>
295          *
296          * @param functionRegistry the function registry to set.
297          * @return the builder, for fluent use.
298          *
299          * @internal ICU 72 technology preview
300          * @deprecated This API is for technology preview only.
301          */
302         @Deprecated
setFunctionRegistry(Mf2FunctionRegistry functionRegistry)303         public Builder setFunctionRegistry(Mf2FunctionRegistry functionRegistry) {
304             this.functionRegistry = functionRegistry;
305             return this;
306         }
307 
308         /**
309          * Sets the data model used to create the message.<br>
310          * It conflicts with the pattern, so it will reset it (the last call on setter wins).
311          *
312          * @param dataModel the pattern to set.
313          * @return the builder, for fluent use.
314          *
315          * @internal ICU 72 technology preview
316          * @deprecated This API is for technology preview only.
317          */
318         @Deprecated
setDataModel(Mf2DataModel dataModel)319         public Builder setDataModel(Mf2DataModel dataModel) {
320             this.dataModel = dataModel;
321             this.pattern = null;
322             return this;
323         }
324 
325         /**
326          * Builds an instance of {@link MessageFormatter}.
327          *
328          * @return the {@link MessageFormatter} created.
329          *
330          * @internal ICU 72 technology preview
331          * @deprecated This API is for technology preview only.
332          */
333         @Deprecated
build()334         public MessageFormatter build() {
335             return new MessageFormatter(this);
336         }
337     }
338 }
339