+ void emit(const UChar32 (&)[N]);
+ void emit(const UnicodeString&);
+ void emit(const Literal&);
+ void emit(const Key&);
+ void emit(const SelectorKeys&);
+ void emit(const Operand&);
+ void emit(const Reserved&);
+ void emit(const Expression&);
+ void emit(const PatternPart&);
+ void emit(const Pattern&);
+ void emit(const Variant*);
+ void emitAttributes(const OptionMap&);
+ void emit(const OptionMap&);
+ void serializeUnsupported();
+ void serializeDeclarations();
+ void serializeSelectors();
+ void serializeVariants();
+ }; // class Serializer
+
+} // namespace message2
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif /* U_SHOW_CPLUSPLUS_API */
+
+#endif // MESSAGEFORMAT_SERIALIZER_H
+
+#endif // U_HIDE_DEPRECATED_API
+// eof
+
diff --git a/icu4c/source/i18n/sources.txt b/icu4c/source/i18n/sources.txt
index a1af43b93e8..77532f55646 100644
--- a/icu4c/source/i18n/sources.txt
+++ b/icu4c/source/i18n/sources.txt
@@ -94,6 +94,17 @@ measunit.cpp
measunit_extra.cpp
measure.cpp
msgfmt.cpp
+messageformat2.cpp
+messageformat2_arguments.cpp
+messageformat2_checker.cpp
+messageformat2_data_model.cpp
+messageformat2_errors.cpp
+messageformat2_evaluation.cpp
+messageformat2_formatter.cpp
+messageformat2_formattable.cpp
+messageformat2_function_registry.cpp
+messageformat2_parser.cpp
+messageformat2_serializer.cpp
name2uni.cpp
nfrs.cpp
nfrule.cpp
diff --git a/icu4c/source/i18n/unicode/messageformat2.h b/icu4c/source/i18n/unicode/messageformat2.h
new file mode 100644
index 00000000000..e43601973bd
--- /dev/null
+++ b/icu4c/source/i18n/unicode/messageformat2.h
@@ -0,0 +1,406 @@
+// © 2024 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#ifndef MESSAGEFORMAT2_H
+#define MESSAGEFORMAT2_H
+
+#if U_SHOW_CPLUSPLUS_API
+
+#if !UCONFIG_NO_FORMATTING
+
+/**
+ * \file
+ * \brief C++ API: Formats messages using the draft MessageFormat 2.0.
+ */
+
+#include "unicode/messageformat2_arguments.h"
+#include "unicode/messageformat2_data_model.h"
+#include "unicode/messageformat2_function_registry.h"
+#include "unicode/unistr.h"
+
+#ifndef U_HIDE_DEPRECATED_API
+
+U_NAMESPACE_BEGIN
+
+namespace message2 {
+
+ class CachedFormatters;
+ class Environment;
+ class MessageContext;
+ class ResolvedSelector;
+ class StaticErrors;
+
+ /**
+ * MessageFormatter is a Technical Preview API implementing MessageFormat 2.0.
+ *
+ *
See the
+ * description of the syntax with examples and use cases and the corresponding
+ * ABNF grammar.
+ *
+ * The MessageFormatter class is mutable and movable. It is not copyable.
+ * (It is mutable because if it has a custom function registry, the registry may include
+ * `FormatterFactory` objects implementing custom formatters, which are allowed to contain
+ * mutable state.)
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ class U_I18N_API MessageFormatter : public UObject {
+ // Note: This class does not currently inherit from the existing
+ // `Format` class.
+ public:
+ /**
+ * Move assignment operator:
+ * The source MessageFormatter will be left in a valid but undefined state.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ MessageFormatter& operator=(MessageFormatter&&) noexcept;
+ /**
+ * Destructor.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ virtual ~MessageFormatter();
+
+ /**
+ * Formats the message to a string, using the data model that was previously set or parsed,
+ * and the given `arguments` object.
+ *
+ * @param arguments Reference to message arguments
+ * @param status Input/output error code used to indicate syntax errors, data model
+ * errors, resolution errors, formatting errors, selection errors, as well
+ * as other errors (such as memory allocation failures). Partial output
+ * is still provided in the presence of most error types.
+ * @return The string result of formatting the message with the given arguments.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ UnicodeString formatToString(const MessageArguments& arguments, UErrorCode &status);
+
+ /**
+ * Not yet implemented; formats the message to a `FormattedMessage` object,
+ * using the data model that was previously set or parsed,
+ * and the given `arguments` object.
+ *
+ * @param arguments Reference to message arguments
+ * @param status Input/output error code used to indicate syntax errors, data model
+ * errors, resolution errors, formatting errors, selection errors, as well
+ * as other errors (such as memory allocation failures). Partial output
+ * is still provided in the presence of most error types.
+ * @return The `FormattedMessage` representing the formatted message.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ FormattedMessage format(const MessageArguments& arguments, UErrorCode &status) const {
+ (void) arguments;
+ if (U_SUCCESS(status)) {
+ status = U_UNSUPPORTED_ERROR;
+ }
+ return FormattedMessage(status);
+ }
+
+ /**
+ * Accesses the locale that this `MessageFormatter` object was created with.
+ *
+ * @return A reference to the locale.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ const Locale& getLocale() const { return locale; }
+
+ /**
+ * Serializes the data model as a string in MessageFormat 2.0 syntax.
+ *
+ * @return result A string representation of the data model.
+ * The string is a valid MessageFormat 2.0 message.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ UnicodeString getPattern() const;
+
+ /**
+ * Accesses the data model referred to by this
+ * `MessageFormatter` object.
+ *
+ * @return A reference to the data model.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ const MFDataModel& getDataModel() const;
+
+ /**
+ * The mutable Builder class allows each part of the MessageFormatter to be initialized
+ * separately; calling its `build()` method yields an immutable MessageFormatter.
+ *
+ * Not copyable or movable.
+ */
+ class U_I18N_API Builder : public UObject {
+ private:
+ friend class MessageFormatter;
+
+ // The pattern to be parsed to generate the formatted message
+ UnicodeString pattern;
+ bool hasPattern = false;
+ bool hasDataModel = false;
+ // The data model to be used to generate the formatted message
+ // Initialized either by `setDataModel()`, or by the parser
+ // through a call to `setPattern()`
+ MFDataModel dataModel;
+ // Normalized representation of the pattern;
+ // ignored if `setPattern()` wasn't called
+ UnicodeString normalizedInput;
+ // Errors (internal representation of parse errors)
+ // Ignored if `setPattern()` wasn't called
+ StaticErrors* errors;
+ Locale locale;
+ // Not owned
+ const MFFunctionRegistry* customMFFunctionRegistry;
+
+ public:
+ /**
+ * Sets the locale to use for formatting.
+ *
+ * @param locale The desired locale.
+ * @return A reference to the builder.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ Builder& setLocale(const Locale& locale);
+ /**
+ * Sets the pattern (contents of the message) and parses it
+ * into a data model. If a data model was
+ * previously set, it is removed.
+ *
+ * @param pattern A string in MessageFormat 2.0 syntax.
+ * @param parseError Struct to receive information on the position
+ * of an error within the pattern.
+ * @param status Input/output error code. If the
+ * pattern cannot be parsed, set to failure code.
+ * @return A reference to the builder.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ Builder& setPattern(const UnicodeString& pattern, UParseError& parseError, UErrorCode& status);
+ /**
+ * Sets a custom function registry.
+ *
+ * @param functionRegistry Reference to the function registry to use.
+ * `functionRegistry` is not copied,
+ * and the caller must ensure its lifetime contains
+ * the lifetime of the `MessageFormatter` object built by this
+ * builder.
+ * @return A reference to the builder.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ Builder& setFunctionRegistry(const MFFunctionRegistry& functionRegistry);
+ /**
+ * Sets a data model. If a pattern was previously set, it is removed.
+ *
+ * @param dataModel Data model to format. Passed by move.
+ * @return A reference to the builder.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ Builder& setDataModel(MFDataModel&& dataModel);
+ /**
+ * Constructs a new immutable MessageFormatter using the pattern or data model
+ * that was previously set, and the locale (if it was previously set)
+ * or default locale (otherwise).
+ *
+ * The builder object (`this`) can still be used after calling `build()`.
+ *
+ * @param status Input/output error code. If neither the pattern
+ * nor the data model is set, set to failure code.
+ * @return The new MessageFormatter object
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ MessageFormatter build(UErrorCode& status) const;
+ /**
+ * Default constructor.
+ * Returns a Builder with the default locale and with no
+ * data model or pattern set. Either `setPattern()`
+ * or `setDataModel()` has to be called before calling `build()`.
+ *
+ * @param status Input/output error code.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ Builder(UErrorCode& status);
+ /**
+ * Destructor.
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ virtual ~Builder();
+ }; // class MessageFormatter::Builder
+
+ // TODO: Shouldn't be public; only used for testing
+ /**
+ * Returns a string consisting of the input with optional spaces removed.
+ *
+ * @return A normalized string representation of the input
+ *
+ * @internal ICU 75.0 technology preview
+ * @deprecated This API is for technology preview only.
+ */
+ const UnicodeString& getNormalizedPattern() const { return normalizedInput; }
+
+ private:
+ friend class Builder;
+ friend class MessageContext;
+
+ MessageFormatter(const MessageFormatter::Builder& builder, UErrorCode &status);
+
+ MessageFormatter() = delete; // default constructor not implemented
+
+ // Do not define default assignment operator
+ const MessageFormatter &operator=(const MessageFormatter &) = delete;
+
+ ResolvedSelector resolveVariables(const Environment& env, const data_model::Operand&, MessageContext&, UErrorCode &) const;
+ ResolvedSelector resolveVariables(const Environment& env, const data_model::Expression&, MessageContext&, UErrorCode &) const;
+
+ // Selection methods
+
+ // Takes a vector of FormattedPlaceholders
+ void resolveSelectors(MessageContext&, const Environment& env, UErrorCode&, UVector&) const;
+ // Takes a vector of vectors of strings (input) and a vector of PrioritizedVariants (output)
+ void filterVariants(const UVector&, UVector&, UErrorCode&) const;
+ // Takes a vector of vectors of strings (input) and a vector of PrioritizedVariants (input/output)
+ void sortVariants(const UVector&, UVector&, UErrorCode&) const;
+ // Takes a vector of strings (input) and a vector of strings (output)
+ void matchSelectorKeys(const UVector&, MessageContext&, ResolvedSelector&& rv, UVector&, UErrorCode&) const;
+ // Takes a vector of FormattedPlaceholders (input),
+ // and a vector of vectors of strings (output)
+ void resolvePreferences(MessageContext&, UVector&, UVector&, UErrorCode&) const;
+
+ // Formatting methods
+ [[nodiscard]] FormattedPlaceholder formatLiteral(const data_model::Literal&) const;
+ void formatPattern(MessageContext&, const Environment&, const data_model::Pattern&, UErrorCode&, UnicodeString&) const;
+ // Formats a call to a formatting function
+ // Dispatches on argument type
+ [[nodiscard]] FormattedPlaceholder evalFormatterCall(FormattedPlaceholder&& argument,
+ MessageContext& context,
+ UErrorCode& status) const;
+ // Dispatches on function name
+ [[nodiscard]] FormattedPlaceholder evalFormatterCall(const FunctionName& functionName,
+ FormattedPlaceholder&& argument,
+ FunctionOptions&& options,
+ MessageContext& context,
+ UErrorCode& status) const;
+ // Formats an expression that appears as a selector
+ ResolvedSelector formatSelectorExpression(const Environment& env, const data_model::Expression&, MessageContext&, UErrorCode&) const;
+ // Formats an expression that appears in a pattern or as the definition of a local variable
+ [[nodiscard]] FormattedPlaceholder formatExpression(const Environment&, const data_model::Expression&, MessageContext&, UErrorCode&) const;
+ [[nodiscard]] FunctionOptions resolveOptions(const Environment& env, const OptionMap&, MessageContext&, UErrorCode&) const;
+ [[nodiscard]] FormattedPlaceholder formatOperand(const Environment&, const data_model::Operand&, MessageContext&, UErrorCode&) const;
+ [[nodiscard]] FormattedPlaceholder evalArgument(const data_model::VariableName&, MessageContext&, UErrorCode&) const;
+ void formatSelectors(MessageContext& context, const Environment& env, UErrorCode &status, UnicodeString& result) const;
+
+ // Function registry methods
+ bool hasCustomMFFunctionRegistry() const {
+ return (customMFFunctionRegistry != nullptr);
+ }
+
+ // Precondition: custom function registry exists
+ // Note: this is non-const because the values in the MFFunctionRegistry are mutable
+ // (a FormatterFactory can have mutable state)
+ const MFFunctionRegistry& getCustomMFFunctionRegistry() const;
+
+ bool isCustomFormatter(const FunctionName&) const;
+ FormatterFactory* lookupFormatterFactory(MessageContext&, const FunctionName&, UErrorCode& status) const;
+ bool isBuiltInSelector(const FunctionName&) const;
+ bool isBuiltInFormatter(const FunctionName&) const;
+ bool isCustomSelector(const FunctionName&) const;
+ const SelectorFactory* lookupSelectorFactory(MessageContext&, const FunctionName&, UErrorCode&) const;
+ bool isSelector(const FunctionName& fn) const { return isBuiltInSelector(fn) || isCustomSelector(fn); }
+ bool isFormatter(const FunctionName& fn) const { return isBuiltInFormatter(fn) || isCustomFormatter(fn); }
+ const Formatter* maybeCachedFormatter(MessageContext&, const FunctionName&, UErrorCode&) const;
+
+ Selector* getSelector(MessageContext&, const FunctionName&, UErrorCode&) const;
+ const Formatter& getFormatter(MessageContext&, const FunctionName&, UErrorCode&) const;
+ bool getDefaultFormatterNameByType(const UnicodeString&, FunctionName&) const;
+
+ // Checking for resolution errors
+ void checkDeclarations(MessageContext&, Environment*&, UErrorCode&) const;
+ void check(MessageContext&, const Environment&, const data_model::Expression&, UErrorCode&) const;
+ void check(MessageContext&, const Environment&, const data_model::Operand&, UErrorCode&) const;
+ void check(MessageContext&, const Environment&, const OptionMap&, UErrorCode&) const;
+
+ void initErrors(UErrorCode&);
+ void clearErrors() const;
+ void cleanup() noexcept;
+
+ // The locale this MessageFormatter was created with
+ /* const */ Locale locale;
+
+ // Registry for built-in functions
+ MFFunctionRegistry standardMFFunctionRegistry;
+ // Registry for custom functions; may be null if no custom registry supplied
+ // Note: this is *not* owned by the MessageFormatter object
+ // The reason for this choice is to have a non-destructive MessageFormatter::Builder,
+ // while also not requiring the function registry to be deeply-copyable. Making the
+ // function registry copyable would impose a requirement on any implementations
+ // of the FormatterFactory and SelectorFactory interfaces to implement a custom
+ // clone() method, which is necessary to avoid sharing between copies of the
+ // function registry (and thus double-frees)
+ // Not deeply immutable (the values in the function registry are mutable,
+ // as a FormatterFactory can have mutable state
+ const MFFunctionRegistry* customMFFunctionRegistry;
+
+ // Data model, representing the parsed message
+ MFDataModel dataModel;
+
+ // Normalized version of the input string (optional whitespace removed)
+ UnicodeString normalizedInput;
+
+ // Formatter cache
+ // Must be a raw pointer to avoid including the internal header file
+ // defining CachedFormatters
+ // Owned by `this`
+ // TODO: This is an optimization that the "TemperatureFormatter" test
+ // (ported from ICU4J) was checking for; however, that test was removed
+ // in order to make `setFormatter()` safe, so maybe this should be
+ // removed too
+ CachedFormatters* cachedFormatters;
+
+ // Errors -- only used while parsing and checking for data model errors; then
+ // the MessageContext keeps track of errors
+ // Must be a raw pointer to avoid including the internal header file
+ // defining StaticErrors
+ // Owned by `this`
+ StaticErrors* errors;
+
+ }; // class MessageFormatter
+
+} // namespace message2
+
+U_NAMESPACE_END
+
+#endif // U_HIDE_DEPRECATED_API
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif /* U_SHOW_CPLUSPLUS_API */
+
+#endif // MESSAGEFORMAT2_H
+
+// eof
diff --git a/icu4c/source/i18n/unicode/messageformat2_arguments.h b/icu4c/source/i18n/unicode/messageformat2_arguments.h
new file mode 100644
index 00000000000..c1690056552
--- /dev/null
+++ b/icu4c/source/i18n/unicode/messageformat2_arguments.h
@@ -0,0 +1,143 @@
+// © 2024 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#ifndef MESSAGEFORMAT2_ARGUMENTS_H
+#define MESSAGEFORMAT2_ARGUMENTS_H
+
+#if U_SHOW_CPLUSPLUS_API
+
+#if !UCONFIG_NO_FORMATTING
+
+/**
+ * \file
+ * \brief C++ API: Formats messages using the draft MessageFormat 2.0.
+ */
+
+#include "unicode/messageformat2_data_model_names.h"
+#include "unicode/messageformat2_formattable.h"
+#include "unicode/unistr.h"
+
+#ifndef U_HIDE_DEPRECATED_API
+
+#include