1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/platform/text/locale_icu.h"
32 
33 #include <unicode/uvernum.h>
34 #include <memory>
35 #include "testing/gtest/include/gtest/gtest.h"
36 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
37 
38 namespace blink {
39 
40 class LocaleICUTest : public testing::Test {
41  public:
42   // Labels class is used for printing results in EXPECT_EQ macro.
43   class Labels {
44    public:
Labels(const Vector<String> labels)45     Labels(const Vector<String> labels) : labels_(labels) {}
46 
47     // FIXME: We should use Vector<T>::operator==() if it works.
operator ==(const Labels & other) const48     bool operator==(const Labels& other) const {
49       if (labels_.size() != other.labels_.size())
50         return false;
51       for (unsigned index = 0; index < labels_.size(); ++index) {
52         if (labels_[index] != other.labels_[index])
53           return false;
54       }
55       return true;
56     }
57 
ToString() const58     String ToString() const {
59       StringBuilder builder;
60       builder.Append("labels(");
61       for (unsigned index = 0; index < labels_.size(); ++index) {
62         if (index)
63           builder.Append(", ");
64         builder.Append('"');
65         builder.Append(labels_[index]);
66         builder.Append('"');
67       }
68       builder.Append(')');
69       return builder.ToString();
70     }
71 
72    private:
73     Vector<String> labels_;
74   };
75 
76  protected:
LabelsFromTwoElements(const String & element1,const String & element2)77   Labels LabelsFromTwoElements(const String& element1, const String& element2) {
78     Vector<String> labels = Vector<String>();
79     labels.push_back(element1);
80     labels.push_back(element2);
81     return Labels(labels);
82   }
83 
MonthFormat(const char * locale_string)84   String MonthFormat(const char* locale_string) {
85     auto locale = std::make_unique<LocaleICU>(locale_string);
86     return locale->MonthFormat();
87   }
88 
LocalizedDateFormatText(const char * locale_string)89   String LocalizedDateFormatText(const char* locale_string) {
90     auto locale = std::make_unique<LocaleICU>(locale_string);
91     return locale->TimeFormat();
92   }
93 
LocalizedShortDateFormatText(const char * locale_string)94   String LocalizedShortDateFormatText(const char* locale_string) {
95     auto locale = std::make_unique<LocaleICU>(locale_string);
96     return locale->ShortTimeFormat();
97   }
98 
ShortMonthLabel(const char * locale_string,unsigned index)99   String ShortMonthLabel(const char* locale_string, unsigned index) {
100     auto locale = std::make_unique<LocaleICU>(locale_string);
101     return locale->ShortMonthLabels()[index];
102   }
103 
ShortStandAloneMonthLabel(const char * locale_string,unsigned index)104   String ShortStandAloneMonthLabel(const char* locale_string, unsigned index) {
105     auto locale = std::make_unique<LocaleICU>(locale_string);
106     return locale->ShortStandAloneMonthLabels()[index];
107   }
108 
StandAloneMonthLabel(const char * locale_string,unsigned index)109   String StandAloneMonthLabel(const char* locale_string, unsigned index) {
110     auto locale = std::make_unique<LocaleICU>(locale_string);
111     return locale->StandAloneMonthLabels()[index];
112   }
113 
TimeAMPMLabels(const char * locale_string)114   Labels TimeAMPMLabels(const char* locale_string) {
115     auto locale = std::make_unique<LocaleICU>(locale_string);
116     return Labels(locale->TimeAMPMLabels());
117   }
118 
IsRTL(const char * locale_string)119   bool IsRTL(const char* locale_string) {
120     auto locale = std::make_unique<LocaleICU>(locale_string);
121     return locale->IsRTL();
122   }
123 };
124 
operator <<(std::ostream & os,const LocaleICUTest::Labels & labels)125 std::ostream& operator<<(std::ostream& os,
126                          const LocaleICUTest::Labels& labels) {
127   return os << labels.ToString();
128 }
129 
TEST_F(LocaleICUTest,isRTL)130 TEST_F(LocaleICUTest, isRTL) {
131   EXPECT_TRUE(IsRTL("ar-EG"));
132   EXPECT_FALSE(IsRTL("en-us"));
133   EXPECT_FALSE(IsRTL("ja-jp"));
134   EXPECT_FALSE(IsRTL("**invalid**"));
135 }
136 
TEST_F(LocaleICUTest,monthFormat)137 TEST_F(LocaleICUTest, monthFormat) {
138   EXPECT_EQ("MMMM yyyy", MonthFormat("en_US"));
139   EXPECT_EQ("MMMM yyyy", MonthFormat("fr"));
140   EXPECT_EQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88", MonthFormat("ja").Utf8());
141 }
142 
TEST_F(LocaleICUTest,localizedDateFormatText)143 TEST_F(LocaleICUTest, localizedDateFormatText) {
144   // Note: EXPECT_EQ(String, String) doesn't print result as string.
145   EXPECT_EQ("h:mm:ss a", LocalizedDateFormatText("en_US"));
146   EXPECT_EQ("HH:mm:ss", LocalizedDateFormatText("fr"));
147   EXPECT_EQ("H:mm:ss", LocalizedDateFormatText("ja"));
148 }
149 
TEST_F(LocaleICUTest,localizedShortDateFormatText)150 TEST_F(LocaleICUTest, localizedShortDateFormatText) {
151   EXPECT_EQ("h:mm a", LocalizedShortDateFormatText("en_US"));
152   EXPECT_EQ("HH:mm", LocalizedShortDateFormatText("fr"));
153   EXPECT_EQ("H:mm", LocalizedShortDateFormatText("ja"));
154 }
155 
TEST_F(LocaleICUTest,standAloneMonthLabels)156 TEST_F(LocaleICUTest, standAloneMonthLabels) {
157   EXPECT_EQ("January", StandAloneMonthLabel("en_US", 0));
158   EXPECT_EQ("June", StandAloneMonthLabel("en_US", 5));
159   EXPECT_EQ("December", StandAloneMonthLabel("en_US", 11));
160 
161 #if U_ICU_VERSION_MAJOR_NUM >= 54
162   EXPECT_EQ("Janvier", StandAloneMonthLabel("fr_FR", 0));
163   EXPECT_EQ("Juin", StandAloneMonthLabel("fr_FR", 5));
164   EXPECT_EQ(
165       "D\xC3\xA9"
166       "cembre",
167       StandAloneMonthLabel("fr_FR", 11).Utf8());
168 #else
169   EXPECT_EQ("janvier", standAloneMonthLabel("fr_FR", 0));
170   EXPECT_EQ("juin", standAloneMonthLabel("fr_FR", 5));
171   EXPECT_EQ(
172       "d\xC3\xA9"
173       "cembre",
174       standAloneMonthLabel("fr_FR", 11));
175 #endif
176 
177   EXPECT_EQ("1\xE6\x9C\x88", StandAloneMonthLabel("ja_JP", 0).Utf8());
178   EXPECT_EQ("6\xE6\x9C\x88", StandAloneMonthLabel("ja_JP", 5).Utf8());
179   EXPECT_EQ("12\xE6\x9C\x88", StandAloneMonthLabel("ja_JP", 11).Utf8());
180 
181   EXPECT_EQ("\xD0\x9C\xD0\xB0\xD1\x80\xD1\x82",
182             StandAloneMonthLabel("ru_RU", 2).Utf8());
183   EXPECT_EQ("\xD0\x9C\xD0\xB0\xD0\xB9",
184             StandAloneMonthLabel("ru_RU", 4).Utf8());
185 }
186 
TEST_F(LocaleICUTest,shortMonthLabels)187 TEST_F(LocaleICUTest, shortMonthLabels) {
188   EXPECT_EQ("Jan", ShortMonthLabel("en_US", 0));
189   EXPECT_EQ("Jan", ShortStandAloneMonthLabel("en_US", 0));
190   EXPECT_EQ("Dec", ShortMonthLabel("en_US", 11));
191   EXPECT_EQ("Dec", ShortStandAloneMonthLabel("en_US", 11));
192 
193 #if U_ICU_VERSION_MAJOR_NUM >= 54
194   EXPECT_EQ("janv.", ShortMonthLabel("fr_FR", 0));
195   EXPECT_EQ("Janv.", ShortStandAloneMonthLabel("fr_FR", 0));
196   EXPECT_EQ(
197       "d\xC3\xA9"
198       "c.",
199       ShortMonthLabel("fr_FR", 11).Utf8());
200   EXPECT_EQ(
201       "D\xC3\xA9"
202       "c.",
203       ShortStandAloneMonthLabel("fr_FR", 11).Utf8());
204 #else
205   EXPECT_EQ("janv.", shortMonthLabel("fr_FR", 0));
206   EXPECT_EQ("janv.", shortStandAloneMonthLabel("fr_FR", 0));
207   EXPECT_EQ(
208       "d\xC3\xA9"
209       "c.",
210       shortMonthLabel("fr_FR", 11));
211   EXPECT_EQ(
212       "d\xC3\xA9"
213       "c.",
214       shortStandAloneMonthLabel("fr_FR", 11));
215 #endif
216 
217   EXPECT_EQ("1\xE6\x9C\x88", ShortMonthLabel("ja_JP", 0).Utf8());
218   EXPECT_EQ("1\xE6\x9C\x88", ShortStandAloneMonthLabel("ja_JP", 0).Utf8());
219   EXPECT_EQ("12\xE6\x9C\x88", ShortMonthLabel("ja_JP", 11).Utf8());
220   EXPECT_EQ("12\xE6\x9C\x88", ShortStandAloneMonthLabel("ja_JP", 11).Utf8());
221 
222   EXPECT_EQ("\xD0\xBC\xD0\xB0\xD1\x80.", ShortMonthLabel("ru_RU", 2).Utf8());
223   EXPECT_EQ("\xD0\x9C\xD0\xB0\xD1\x80\xD1\x82",
224             ShortStandAloneMonthLabel("ru_RU", 2).Utf8());
225   EXPECT_EQ("\xD0\xBC\xD0\xB0\xD1\x8F", ShortMonthLabel("ru_RU", 4).Utf8());
226   EXPECT_EQ("\xD0\x9C\xD0\xB0\xD0\xB9",
227             ShortStandAloneMonthLabel("ru_RU", 4).Utf8());
228 }
229 
TEST_F(LocaleICUTest,timeAMPMLabels)230 TEST_F(LocaleICUTest, timeAMPMLabels) {
231   EXPECT_EQ(LabelsFromTwoElements("AM", "PM"), TimeAMPMLabels("en_US"));
232   EXPECT_EQ(LabelsFromTwoElements("AM", "PM"), TimeAMPMLabels("fr"));
233 
234   UChar ja_am[3] = {0x5348, 0x524d, 0};
235   UChar ja_pm[3] = {0x5348, 0x5F8C, 0};
236   EXPECT_EQ(LabelsFromTwoElements(String(ja_am), String(ja_pm)),
237             TimeAMPMLabels("ja"));
238 }
239 
TestDecimalSeparator(const AtomicString & locale_identifier)240 static String TestDecimalSeparator(const AtomicString& locale_identifier) {
241   std::unique_ptr<Locale> locale = Locale::Create(locale_identifier);
242   return locale->LocalizedDecimalSeparator();
243 }
244 
TEST_F(LocaleICUTest,localizedDecimalSeparator)245 TEST_F(LocaleICUTest, localizedDecimalSeparator) {
246   EXPECT_EQ(String("."), TestDecimalSeparator("en_US"));
247   EXPECT_EQ(String(","), TestDecimalSeparator("fr"));
248 }
249 
TestNumberIsReversible(const AtomicString & locale_identifier,const char * original,const char * should_have=nullptr)250 void TestNumberIsReversible(const AtomicString& locale_identifier,
251                             const char* original,
252                             const char* should_have = nullptr) {
253   std::unique_ptr<Locale> locale = Locale::Create(locale_identifier);
254   String localized = locale->ConvertToLocalizedNumber(original);
255   if (should_have)
256     EXPECT_TRUE(localized.Contains(should_have));
257   String converted = locale->ConvertFromLocalizedNumber(localized);
258   EXPECT_EQ(original, converted);
259 }
260 
TestNumbers(const char * locale_string)261 void TestNumbers(const char* locale_string) {
262   TestNumberIsReversible(locale_string, "123456789012345678901234567890");
263   TestNumberIsReversible(locale_string, "-123.456");
264   TestNumberIsReversible(locale_string, ".456");
265   TestNumberIsReversible(locale_string, "-0.456");
266 }
267 
TEST_F(LocaleICUTest,reversible)268 TEST_F(LocaleICUTest, reversible) {
269   TestNumberIsReversible("en_US", "123456789012345678901234567890");
270   TestNumberIsReversible("en_US", "-123.456", ".");
271   TestNumberIsReversible("en_US", ".456", ".");
272   TestNumberIsReversible("en_US", "-0.456", ".");
273 
274   TestNumberIsReversible("fr", "123456789012345678901234567890");
275   TestNumberIsReversible("fr", "-123.456", ",");
276   TestNumberIsReversible("fr", ".456", ",");
277   TestNumberIsReversible("fr", "-0.456", ",");
278 
279   // Persian locale has a negative prefix and a negative suffix.
280   TestNumbers("fa");
281 
282   // Test some of major locales.
283   TestNumbers("ar");
284   TestNumbers("de_DE");
285   TestNumbers("es_ES");
286   TestNumbers("ja_JP");
287   TestNumbers("ko_KR");
288   TestNumbers("zh_CN");
289   TestNumbers("zh_HK");
290   TestNumbers("zh_TW");
291 }
292 
293 }  // namespace blink
294