Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion icu4c/source/i18n/messageformat2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ FunctionContext MessageFormatter::makeFunctionContext(const FunctionOptions& opt
UMFBidiOption dir = ubidi_getMFOption(options.getStringFunctionOption(options::U_DIR));
UnicodeString id = options.getStringFunctionOption(options::U_ID);

return FunctionContext(locale, dir, id);
return FunctionContext(locale, formattingLocale, dir, id);
}

// Resolves a function's options
Expand Down
17 changes: 15 additions & 2 deletions icu4c/source/i18n/messageformat2_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ namespace message2 {
return *this;
}

MessageFormatter::Builder& MessageFormatter::Builder::setFormattingLocale(const Locale& loc) {
formattingLocale = loc;
return *this;
}

MessageFormatter::Builder& MessageFormatter::Builder::setDataModel(MFDataModel&& newDataModel) {
clearState();
hasPattern = false;
Expand Down Expand Up @@ -119,7 +124,11 @@ namespace message2 {
return MessageFormatter(*this, errorCode);
}

MessageFormatter::Builder::Builder(UErrorCode& errorCode) : locale(Locale::getDefault()), customMFFunctionRegistry(nullptr) {
MessageFormatter::Builder::Builder(UErrorCode& errorCode) :
locale(Locale::getDefault()),
formattingLocale(),
customMFFunctionRegistry(nullptr) {
formattingLocale.setToBogus(); // indicating setFormattingLocale was not called
// Initialize errors
errors = new StaticErrors(errorCode);
CHECK_ERROR(errorCode);
Expand Down Expand Up @@ -152,7 +161,10 @@ namespace message2 {

// MessageFormatter

MessageFormatter::MessageFormatter(const MessageFormatter::Builder& builder, UErrorCode &success) : locale(builder.locale), customMFFunctionRegistry(builder.customMFFunctionRegistry) {
MessageFormatter::MessageFormatter(const MessageFormatter::Builder& builder, UErrorCode &success) :
locale(builder.locale),
formattingLocale(builder.formattingLocale.isBogus() ? builder.locale : builder.formattingLocale),
customMFFunctionRegistry(builder.customMFFunctionRegistry) {
CHECK_ERROR(success);

// Set up the standard function registry
Expand Down Expand Up @@ -236,6 +248,7 @@ namespace message2 {
cleanup();

locale = std::move(other.locale);
formattingLocale = std::move(other.formattingLocale);
standardMFFunctionRegistry = std::move(other.standardMFFunctionRegistry);
customMFFunctionRegistry = other.customMFFunctionRegistry;
dataModel = std::move(other.dataModel);
Expand Down
21 changes: 12 additions & 9 deletions icu4c/source/i18n/messageformat2_function_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -734,15 +734,16 @@ StandardFunctions::NumberValue::NumberValue(const Number& parent,
return;
}

formattingLocale = context.getFormattingLocale();
locale = context.getLocale();
opts = options.mergeOptions(arg.getResolvedOptions(), errorCode);
innerValue = arg.unwrap();
functionName = UnicodeString(parent.isInteger ? "integer" : "number");
inputDir = context.getDirection();
dir = outputDirectionalityFromUDir(inputDir, locale);
dir = outputDirectionalityFromUDir(inputDir, formattingLocale);

number::LocalizedNumberFormatter realFormatter;
realFormatter = formatterForOptions(parent, locale, opts, errorCode);
realFormatter = formatterForOptions(parent, formattingLocale, opts, errorCode);

int64_t integerValue = 0;

Expand Down Expand Up @@ -1063,6 +1064,7 @@ StandardFunctions::DateTimeValue::DateTimeValue(DateTime::DateTimeType type,
}

locale = context.getLocale();
formattingLocale = context.getFormattingLocale();
opts = options.mergeOptions(arg.getResolvedOptions(), errorCode);
innerValue = arg.unwrap();
switch (type) {
Expand All @@ -1077,7 +1079,7 @@ StandardFunctions::DateTimeValue::DateTimeValue(DateTime::DateTimeType type,
break;
}
inputDir = context.getDirection();
dir = outputDirectionalityFromUDir(inputDir, locale);
dir = outputDirectionalityFromUDir(inputDir, formattingLocale);

const Formattable* source = &innerValue;

Expand Down Expand Up @@ -1119,19 +1121,19 @@ StandardFunctions::DateTimeValue::DateTimeValue(DateTime::DateTimeType type,
timeStyle = stringToStyle(opts.getStringFunctionOption(timeStyleName), errorCode);

if (useDate && !useTime) {
df.adoptInstead(DateFormat::createDateInstance(dateStyle, locale));
df.adoptInstead(DateFormat::createDateInstance(dateStyle, formattingLocale));
} else if (useTime && !useDate) {
df.adoptInstead(DateFormat::createTimeInstance(timeStyle, locale));
df.adoptInstead(DateFormat::createTimeInstance(timeStyle, formattingLocale));
} else {
df.adoptInstead(DateFormat::createDateTimeInstance(dateStyle, timeStyle, locale));
df.adoptInstead(DateFormat::createDateTimeInstance(dateStyle, timeStyle, formattingLocale));
}
} else if (type == DateTimeType::kDate) {
dateStyle = stringToStyle(opts.getStringFunctionOption(styleName), errorCode);
df.adoptInstead(DateFormat::createDateInstance(dateStyle, locale));
df.adoptInstead(DateFormat::createDateInstance(dateStyle, formattingLocale));
} else {
// :time
timeStyle = stringToStyle(opts.getStringFunctionOption(styleName), errorCode);
df.adoptInstead(DateFormat::createTimeInstance(timeStyle, locale));
df.adoptInstead(DateFormat::createTimeInstance(timeStyle, formattingLocale));
}
} else {
// Build up a skeleton based on the field options, then use that to
Expand Down Expand Up @@ -1483,14 +1485,15 @@ StandardFunctions::StringValue::StringValue(const FunctionContext& context,
const FunctionOptions&,
UErrorCode& status) {
CHECK_ERROR(status);
formattingLocale = context.getFormattingLocale();
locale = context.getLocale();
innerValue = val.unwrap();
functionName = UnicodeString("string");
inputDir = context.getDirection();
dir = stringOutputDirection(inputDir);
// No options
// Convert to string
formattedString = formattableToString(context.getLocale(), innerValue, status);
formattedString = formattableToString(formattingLocale, innerValue, status);
}

void StandardFunctions::StringValue::selectKeys(const UnicodeString* keys,
Expand Down
33 changes: 30 additions & 3 deletions icu4c/source/i18n/unicode/messageformat2.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,18 @@ namespace message2 {
*/
U_I18N_API const Locale& getLocale() const { return locale; }

/**
* Acceses the formatting locale that this `MessageFormatter` object
* was created with. If no formatting locale was explicitly set,
* returns the same locale as getLocale().
*
* @return A reference to the formatting locale.
*
* @internal ICU 79 technology preview
* @deprecated This API is for technology preview only.
*/
U_I18N_API const Locale& getFormattingLocale() { return formattingLocale; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it confusing to refer to the two locales as "locale" and "formatting locale" -- perhaps rename "locale" to indicate what it does in contrast with the formatting locale?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Will need to address Mihai's comment also.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps selectionLocale and formattingLocale ?


/**
* Serializes the data model as a string in MessageFormat 2.0 syntax.
*
Expand Down Expand Up @@ -230,6 +242,7 @@ namespace message2 {
// Ignored if `setPattern()` wasn't called
StaticErrors* errors;
Locale locale;
Locale formattingLocale;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation

// Not owned
const MFFunctionRegistry* customMFFunctionRegistry;
// Error behavior; see comment in `MessageFormatter` class
Expand All @@ -244,7 +257,7 @@ namespace message2 {
void clearState();
public:
/**
* Sets the locale to use for formatting.
* Sets the locale to use for this context
*
* @param locale The desired locale.
* @return A reference to the builder.
Expand All @@ -253,6 +266,18 @@ namespace message2 {
* @deprecated This API is for technology preview only.
*/
U_I18N_API Builder& setLocale(const Locale& locale);
/**
* Sets the locale used by formatter factories (:number, :date,
* etc.) for rendering output. Selector factories (plural rules)
* continue to use the locale set via setLocale().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of #3536 there are no factories anymore and formatters and selectors aren't distinguished, so I think this comment needs updating? Perhaps it just applies to built-in functions?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, this was from pre-3536

* The formatting locale defaults to the same one set by setLocale().
* @param locale The desired locale
* @return A reference to the builder.
*
* @internal ICU 79 technology preview
* @deprecated This API is for technology preview only.
*/
U_I18N_API Builder& setFormattingLocale(const Locale& locale);
/**
* Sets the pattern (contents of the message) and parses it
* into a data model. If a data model was
Expand Down Expand Up @@ -501,8 +526,10 @@ namespace message2 {
void clearErrors() const;
void cleanup() noexcept;

// The locale this MessageFormatter was created with
/* const */ Locale locale;
// The locale set by setLocale()
Locale locale;
// The locale set by setformattinglocale()
Locale formattingLocale;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation


// Registry for built-in functions
MFFunctionRegistry standardMFFunctionRegistry;
Expand Down
24 changes: 21 additions & 3 deletions icu4c/source/i18n/unicode/messageformat2_function_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,15 @@ namespace message2 {
* @deprecated This API is for technology preview only.
*/
U_I18N_API const Locale& getLocale() const { return locale; }
/**
* Returns the formatting locale from this context.
*
* @return Formatting Locale the context was created with.
*
* @internal ICU 79 technology preview
* @deprecated This API is for technology preview only.
*/
U_I18N_API const Locale& getFormattingLocale() const { return formattingLocale; }
/**
* Returns the text direction from this context.
*
Expand All @@ -270,11 +279,12 @@ namespace message2 {
friend class MessageFormatter;

Locale locale;
Locale formattingLocale;
UMFBidiOption dir;
UnicodeString id;

FunctionContext(const Locale& loc, UMFBidiOption d, UnicodeString i)
: locale(loc), dir(d), id(i) {}
FunctionContext(const Locale& loc, const Locale& formatLoc, UMFBidiOption d, UnicodeString i)
: locale(loc), formattingLocale(formatLoc), dir(d), id(i) {}
}; // class FunctionContext

class FunctionValue;
Expand Down Expand Up @@ -515,13 +525,21 @@ namespace message2 {
*/
UnicodeString fallback;
/**
* Locale from u:locale option.
* Locale from function context.
* Must be set from function context.
*
* @internal ICU 79 technology preview
* @deprecated This API is for technology preview only.
*/
Locale locale;
/**
* Formatting Locale from function context.
* Must be set from function context.
*
* @internal ICU 79 technology preview
* @deprecated This API is for technology preview only.
*/
Locale formattingLocale;
/**
* Directionality of formatted result.
* Defaults to U_MF_DIRECTIONALITY_UNKNOWN if not set
Expand Down
38 changes: 38 additions & 0 deletions icu4c/source/test/intltest/messageformat2test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ TestMessageFormat2::runIndexedTest(int32_t index, UBool exec,
TESTCASE_AUTO(testLowLoneSurrogate);
TESTCASE_AUTO(testLoneSurrogateInQuotedLiteral);
TESTCASE_AUTO(dataDrivenTests);
TESTCASE_AUTO(testSetFormatLocale);
TESTCASE_AUTO_END;
}

Expand Down Expand Up @@ -479,6 +480,43 @@ void TestMessageFormat2::dataDrivenTests() {
jsonTestsFromFiles(errorCode);
}

void TestMessageFormat2::testSetFormatLocale() {
IcuTestErrorCode errorCode(*this, "testSetFormatLocale");
UParseError parseError;

UnicodeString pattern(u""
".input {$count :number} .input {$name :string} .input {$birthday :date}\n"
".match $count\n"
"many {{PASS {$count} @ {$birthday :date style=short}}}\n"
"* {{FAIL wrong bucket {$count} @ {$birthday :date style=short}}}\n"
);
MessageFormatter mf = MessageFormatter::Builder(errorCode)
.setErrorHandlingBehavior(MessageFormatter::U_MF_BEST_EFFORT)
.setPattern(pattern, parseError, errorCode)
.setLocale(Locale("pl"))
.setFormattingLocale(Locale("en"))
.build(errorCode);

if (errorCode.errIfFailureAndReset("build")) {
errln("UParseError = %d:%d", parseError.line, parseError.offset);
return;
}

std::map<UnicodeString, message2::Formattable> argsBuilder;
argsBuilder["count"] = message2::Formattable((int64_t)13);
argsBuilder["name"] = message2::Formattable("US");
argsBuilder["birthday"] = message2::Formattable("1776-07-04");
MessageArguments args(argsBuilder, errorCode);

UnicodeString result = mf.formatToString(args, errorCode);
if (errorCode.errIfFailureAndReset("formatToString")) {
return;
}
UnicodeString expectedResult(u"PASS 13 @ 7/4/76");

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test will FAIL on the existing code if set to the "en" locale (because it won't get the Polish plural buckets).

And the date format will be wrong (will be in Polish) if the formatting locale wasn't set to "en". 4.07.1776

assertEquals("testSetFormatLocale", expectedResult, result);
}


TestCase::~TestCase() {}
TestCase::Builder::~Builder() {}

Expand Down
1 change: 1 addition & 0 deletions icu4c/source/test/intltest/messageformat2test.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class TestMessageFormat2: public IntlTest {
void testBidiAPI(void);
void testAPI(void);
void testAPISimple(void);
void testSetFormatLocale(void);

private:
void jsonTestsFromFiles(IcuTestErrorCode&);
Expand Down
Loading