1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/i18n/message_formatter.h"
9 #include "base/i18n/rtl.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/icu/source/common/unicode/unistr.h"
16 #include "third_party/icu/source/i18n/unicode/datefmt.h"
17 #include "third_party/icu/source/i18n/unicode/msgfmt.h"
19 typedef testing::Test MessageFormatterTest;
24 class MessageFormatterTest : public testing::Test {
26 MessageFormatterTest() {
27 original_locale_ = GetConfiguredLocale();
28 SetICUDefaultLocale("en-US");
30 ~MessageFormatterTest() override {
31 SetICUDefaultLocale(original_locale_);
35 std::string original_locale_;
40 void AppendFormattedDateTime(const std::unique_ptr<icu::DateFormat>& df,
42 std::string* result) {
43 icu::UnicodeString formatted;
44 df->format(static_cast<UDate>(now.ToJsTime()), formatted).
45 toUTF8String(*result);
50 TEST_F(MessageFormatterTest, PluralNamedArgs) {
51 const string16 pattern = ASCIIToUTF16(
52 "{num_people, plural, "
53 "=0 {I met nobody in {place}.}"
54 "=1 {I met a person in {place}.}"
55 "other {I met # people in {place}.}}");
57 std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
58 pattern, "num_people", 0, "place", "Paris"));
59 EXPECT_EQ("I met nobody in Paris.", result);
60 result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
61 pattern, "num_people", 1, "place", "Paris"));
62 EXPECT_EQ("I met a person in Paris.", result);
63 result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
64 pattern, "num_people", 5, "place", "Paris"));
65 EXPECT_EQ("I met 5 people in Paris.", result);
68 TEST_F(MessageFormatterTest, PluralNamedArgsWithOffset) {
69 const string16 pattern = ASCIIToUTF16(
70 "{num_people, plural, offset:1 "
71 "=0 {I met nobody in {place}.}"
72 "=1 {I met {person} in {place}.}"
73 "=2 {I met {person} and one other person in {place}.}"
74 "=13 {I met {person} and a dozen other people in {place}.}"
75 "other {I met {person} and # other people in {place}.}}");
77 std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
78 pattern, "num_people", 0, "place", "Paris"));
79 EXPECT_EQ("I met nobody in Paris.", result);
80 // {person} is ignored if {num_people} is 0.
81 result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
82 pattern, "num_people", 0, "place", "Paris", "person", "Peter"));
83 EXPECT_EQ("I met nobody in Paris.", result);
84 result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
85 pattern, "num_people", 1, "place", "Paris", "person", "Peter"));
86 EXPECT_EQ("I met Peter in Paris.", result);
87 result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
88 pattern, "num_people", 2, "place", "Paris", "person", "Peter"));
89 EXPECT_EQ("I met Peter and one other person in Paris.", result);
90 result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
91 pattern, "num_people", 13, "place", "Paris", "person", "Peter"));
92 EXPECT_EQ("I met Peter and a dozen other people in Paris.", result);
93 result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
94 pattern, "num_people", 50, "place", "Paris", "person", "Peter"));
95 EXPECT_EQ("I met Peter and 49 other people in Paris.", result);
98 TEST_F(MessageFormatterTest, PluralNumberedArgs) {
99 const string16 pattern = ASCIIToUTF16(
101 "=1 {The cert for {0} expired yesterday.}"
102 "=7 {The cert for {0} expired a week ago.}"
103 "other {The cert for {0} expired # days ago.}}");
105 std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
106 pattern, "example.com", 1));
107 EXPECT_EQ("The cert for example.com expired yesterday.", result);
108 result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
109 pattern, "example.com", 7));
110 EXPECT_EQ("The cert for example.com expired a week ago.", result);
111 result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
112 pattern, "example.com", 15));
113 EXPECT_EQ("The cert for example.com expired 15 days ago.", result);
116 TEST_F(MessageFormatterTest, PluralNumberedArgsWithDate) {
117 const string16 pattern = ASCIIToUTF16(
119 "=1 {The cert for {0} expired yesterday. Today is {2,date,full}}"
120 "other {The cert for {0} expired # days ago. Today is {2,date,full}}}");
122 base::Time now = base::Time::Now();
123 using icu::DateFormat;
124 std::unique_ptr<DateFormat> df(
125 DateFormat::createDateInstance(DateFormat::FULL));
126 std::string second_sentence = " Today is ";
127 AppendFormattedDateTime(df, now, &second_sentence);
129 std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
130 pattern, "example.com", 1, now));
131 EXPECT_EQ("The cert for example.com expired yesterday." + second_sentence,
133 result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
134 pattern, "example.com", 15, now));
135 EXPECT_EQ("The cert for example.com expired 15 days ago." + second_sentence,
139 TEST_F(MessageFormatterTest, DateTimeAndNumber) {
140 // Note that using 'mph' for all locales is not a good i18n practice.
141 const string16 pattern = ASCIIToUTF16(
142 "At {0,time, short} on {0,date, medium}, "
143 "there was {1} at building {2,number,integer}. "
144 "The speed of the wind was {3,number,###.#} mph.");
146 using icu::DateFormat;
147 std::unique_ptr<DateFormat> tf(
148 DateFormat::createTimeInstance(DateFormat::SHORT));
149 std::unique_ptr<DateFormat> df(
150 DateFormat::createDateInstance(DateFormat::MEDIUM));
152 base::Time now = base::Time::Now();
153 std::string expected = "At ";
154 AppendFormattedDateTime(tf, now, &expected);
155 expected.append(" on ");
156 AppendFormattedDateTime(df, now, &expected);
157 expected.append(", there was an explosion at building 3. "
158 "The speed of the wind was 37.4 mph.");
160 EXPECT_EQ(expected, UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
161 pattern, now, "an explosion", 3, 37.413)));
164 TEST_F(MessageFormatterTest, SelectorSingleOrMultiple) {
165 const string16 pattern = ASCIIToUTF16(
167 "single {Select a file to upload.}"
168 "multiple {Select files to upload.}"
171 std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
173 EXPECT_EQ("Select a file to upload.", result);
174 result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
175 pattern, "multiple"));
176 EXPECT_EQ("Select files to upload.", result);
178 // fallback if a parameter is not selectors specified in the message pattern.
179 result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
181 EXPECT_EQ("UNUSED", result);