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"
7 #include "base/i18n/rtl.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/string_piece.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/icu/source/common/unicode/unistr.h"
15 #include "third_party/icu/source/i18n/unicode/datefmt.h"
16 #include "third_party/icu/source/i18n/unicode/msgfmt.h"
18 typedef testing::Test MessageFormatterTest
;
23 class MessageFormatterTest
: public testing::Test
{
25 MessageFormatterTest() {
26 original_locale_
= GetConfiguredLocale();
27 SetICUDefaultLocale("en-US");
29 ~MessageFormatterTest() override
{
30 SetICUDefaultLocale(original_locale_
);
34 std::string original_locale_
;
39 void AppendFormattedDateTime(const scoped_ptr
<icu::DateFormat
>& df
,
40 const Time
& now
, std::string
* result
) {
41 icu::UnicodeString formatted
;
42 df
->format(static_cast<UDate
>(now
.ToJsTime()), formatted
).
43 toUTF8String(*result
);
48 TEST_F(MessageFormatterTest
, PluralNamedArgs
) {
49 const string16 pattern
= ASCIIToUTF16(
50 "{num_people, plural, "
51 "=0 {I met nobody in {place}.}"
52 "=1 {I met a person in {place}.}"
53 "other {I met # people in {place}.}}");
55 std::string result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
56 pattern
, "num_people", 0, "place", "Paris"));
57 EXPECT_EQ("I met nobody in Paris.", result
);
58 result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
59 pattern
, "num_people", 1, "place", "Paris"));
60 EXPECT_EQ("I met a person in Paris.", result
);
61 result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
62 pattern
, "num_people", 5, "place", "Paris"));
63 EXPECT_EQ("I met 5 people in Paris.", result
);
66 TEST_F(MessageFormatterTest
, PluralNamedArgsWithOffset
) {
67 const string16 pattern
= ASCIIToUTF16(
68 "{num_people, plural, offset:1 "
69 "=0 {I met nobody in {place}.}"
70 "=1 {I met {person} in {place}.}"
71 "=2 {I met {person} and one other person in {place}.}"
72 "=13 {I met {person} and a dozen other people in {place}.}"
73 "other {I met {person} and # other people in {place}.}}");
75 std::string result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
76 pattern
, "num_people", 0, "place", "Paris"));
77 EXPECT_EQ("I met nobody in Paris.", result
);
78 // {person} is ignored if {num_people} is 0.
79 result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
80 pattern
, "num_people", 0, "place", "Paris", "person", "Peter"));
81 EXPECT_EQ("I met nobody in Paris.", result
);
82 result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
83 pattern
, "num_people", 1, "place", "Paris", "person", "Peter"));
84 EXPECT_EQ("I met Peter in Paris.", result
);
85 result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
86 pattern
, "num_people", 2, "place", "Paris", "person", "Peter"));
87 EXPECT_EQ("I met Peter and one other person in Paris.", result
);
88 result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
89 pattern
, "num_people", 13, "place", "Paris", "person", "Peter"));
90 EXPECT_EQ("I met Peter and a dozen other people in Paris.", result
);
91 result
= UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
92 pattern
, "num_people", 50, "place", "Paris", "person", "Peter"));
93 EXPECT_EQ("I met Peter and 49 other people in Paris.", result
);
96 TEST_F(MessageFormatterTest
, PluralNumberedArgs
) {
97 const string16 pattern
= ASCIIToUTF16(
99 "=1 {The cert for {0} expired yesterday.}"
100 "=7 {The cert for {0} expired a week ago.}"
101 "other {The cert for {0} expired # days ago.}}");
103 std::string result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
104 pattern
, "example.com", 1));
105 EXPECT_EQ("The cert for example.com expired yesterday.", result
);
106 result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
107 pattern
, "example.com", 7));
108 EXPECT_EQ("The cert for example.com expired a week ago.", result
);
109 result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
110 pattern
, "example.com", 15));
111 EXPECT_EQ("The cert for example.com expired 15 days ago.", result
);
114 TEST_F(MessageFormatterTest
, PluralNumberedArgsWithDate
) {
115 const string16 pattern
= ASCIIToUTF16(
117 "=1 {The cert for {0} expired yesterday. Today is {2,date,full}}"
118 "other {The cert for {0} expired # days ago. Today is {2,date,full}}}");
120 base::Time now
= base::Time::Now();
121 using icu::DateFormat
;
122 scoped_ptr
<DateFormat
> df(DateFormat::createDateInstance(DateFormat::FULL
));
123 std::string second_sentence
= " Today is ";
124 AppendFormattedDateTime(df
, now
, &second_sentence
);
126 std::string result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
127 pattern
, "example.com", 1, now
));
128 EXPECT_EQ("The cert for example.com expired yesterday." + second_sentence
,
130 result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
131 pattern
, "example.com", 15, now
));
132 EXPECT_EQ("The cert for example.com expired 15 days ago." + second_sentence
,
136 TEST_F(MessageFormatterTest
, DateTimeAndNumber
) {
137 // Note that using 'mph' for all locales is not a good i18n practice.
138 const string16 pattern
= ASCIIToUTF16(
139 "At {0,time, short} on {0,date, medium}, "
140 "there was {1} at building {2,number,integer}. "
141 "The speed of the wind was {3,number,###.#} mph.");
143 using icu::DateFormat
;
144 scoped_ptr
<DateFormat
> tf(DateFormat::createTimeInstance(DateFormat::SHORT
));
145 scoped_ptr
<DateFormat
> df(DateFormat::createDateInstance(DateFormat::MEDIUM
));
147 base::Time now
= base::Time::Now();
148 std::string expected
= "At ";
149 AppendFormattedDateTime(tf
, now
, &expected
);
150 expected
.append(" on ");
151 AppendFormattedDateTime(df
, now
, &expected
);
152 expected
.append(", there was an explosion at building 3. "
153 "The speed of the wind was 37.4 mph.");
155 EXPECT_EQ(expected
, UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
156 pattern
, now
, "an explosion", 3, 37.413)));
159 TEST_F(MessageFormatterTest
, SelectorSingleOrMultiple
) {
160 const string16 pattern
= ASCIIToUTF16(
162 "single {Select a file to upload.}"
163 "multiple {Select files to upload.}"
166 std::string result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
168 EXPECT_EQ("Select a file to upload.", result
);
169 result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
170 pattern
, "multiple"));
171 EXPECT_EQ("Select files to upload.", result
);
173 // fallback if a parameter is not selectors specified in the message pattern.
174 result
= UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
176 EXPECT_EQ("UNUSED", result
);