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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 23 * DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "core/platform/text/mac/LocaleMac.h" 28 29 #include <gtest/gtest.h> 30 #include "core/platform/DateComponents.h" 31 #include "wtf/DateMath.h" 32 #include "wtf/MathExtras.h" 33 #include "wtf/PassOwnPtr.h" 34 #include "wtf/text/CString.h" 35 36 using namespace WebCore; 37 38 class LocaleMacTest : public ::testing::Test { 39 protected: 40 enum { 41 January = 0, February, March, 42 April, May, June, 43 July, August, September, 44 October, November, December, 45 }; 46 47 enum { 48 Sunday = 0, Monday, Tuesday, 49 Wednesday, Thursday, Friday, 50 Saturday, 51 }; 52 53 DateComponents dateComponents(int year, int month, int day) 54 { 55 DateComponents date; 56 date.setMillisecondsSinceEpochForDate(msForDate(year, month, day)); 57 return date; 58 } 59 60 DateComponents timeComponents(int hour, int minute, int second, int millisecond) 61 { 62 DateComponents date; 63 date.setMillisecondsSinceMidnight(hour * msPerHour + minute * msPerMinute + second * msPerSecond + millisecond); 64 return date; 65 } 66 67 double msForDate(int year, int month, int day) 68 { 69 return dateToDaysFrom1970(year, month, day) * msPerDay; 70 } 71 72 String formatWeek(const String& localeString, const String& isoString) 73 { 74 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 75 DateComponents date; 76 unsigned end; 77 date.parseWeek(isoString, 0, end); 78 return locale->formatDateTime(date); 79 } 80 81 String formatMonth(const String& localeString, const String& isoString, bool useShortFormat) 82 { 83 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 84 DateComponents date; 85 unsigned end; 86 date.parseMonth(isoString, 0, end); 87 return locale->formatDateTime(date, (useShortFormat ? Locale::FormatTypeShort : Locale::FormatTypeMedium)); 88 } 89 90 String formatDate(const String& localeString, int year, int month, int day) 91 { 92 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 93 return locale->formatDateTime(dateComponents(year, month, day)); 94 } 95 96 String formatTime(const String& localeString, int hour, int minute, int second, int millisecond, bool useShortFormat) 97 { 98 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 99 return locale->formatDateTime(timeComponents(hour, minute, second, millisecond), (useShortFormat ? Locale::FormatTypeShort : Locale::FormatTypeMedium)); 100 } 101 102 #if ENABLE(CALENDAR_PICKER) 103 unsigned firstDayOfWeek(const String& localeString) 104 { 105 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 106 return locale->firstDayOfWeek(); 107 } 108 109 String monthLabel(const String& localeString, unsigned index) 110 { 111 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 112 return locale->monthLabels()[index]; 113 } 114 115 String weekDayShortLabel(const String& localeString, unsigned index) 116 { 117 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 118 return locale->weekDayShortLabels()[index]; 119 } 120 121 bool isRTL(const String& localeString) 122 { 123 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 124 return locale->isRTL(); 125 } 126 #endif 127 128 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 129 String monthFormat(const String& localeString) 130 { 131 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 132 return locale->monthFormat(); 133 } 134 135 String timeFormat(const String& localeString) 136 { 137 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 138 return locale->timeFormat(); 139 } 140 141 String shortTimeFormat(const String& localeString) 142 { 143 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 144 return locale->shortTimeFormat(); 145 } 146 147 String shortMonthLabel(const String& localeString, unsigned index) 148 { 149 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 150 return locale->shortMonthLabels()[index]; 151 } 152 153 String standAloneMonthLabel(const String& localeString, unsigned index) 154 { 155 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 156 return locale->standAloneMonthLabels()[index]; 157 } 158 159 String shortStandAloneMonthLabel(const String& localeString, unsigned index) 160 { 161 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 162 return locale->shortStandAloneMonthLabels()[index]; 163 } 164 165 String timeAMPMLabel(const String& localeString, unsigned index) 166 { 167 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 168 return locale->timeAMPMLabels()[index]; 169 } 170 171 String decimalSeparator(const String& localeString) 172 { 173 OwnPtr<LocaleMac> locale = LocaleMac::create(localeString); 174 return locale->localizedDecimalSeparator(); 175 } 176 #endif 177 }; 178 179 TEST_F(LocaleMacTest, formatWeek) 180 { 181 EXPECT_STREQ("Week 04, 2005", formatWeek("en_US", "2005-W04").utf8().data()); 182 EXPECT_STREQ("Week 52, 2005", formatWeek("en_US", "2005-W52").utf8().data()); 183 } 184 185 TEST_F(LocaleMacTest, formatMonth) 186 { 187 EXPECT_STREQ("April 2005", formatMonth("en_US", "2005-04", false).utf8().data()); 188 EXPECT_STREQ("avril 2005", formatMonth("fr_FR", "2005-04", false).utf8().data()); 189 EXPECT_STREQ("2005\xE5\xB9\xB4" "04\xE6\x9C\x88", formatMonth("ja_JP", "2005-04", false).utf8().data()); 190 191 EXPECT_STREQ("Apr 2005", formatMonth("en_US", "2005-04", true).utf8().data()); 192 EXPECT_STREQ("avr. 2005", formatMonth("fr_FR", "2005-04", true).utf8().data()); 193 EXPECT_STREQ("2005\xE5\xB9\xB4" "04\xE6\x9C\x88", formatMonth("ja_JP", "2005-04", true).utf8().data()); 194 } 195 196 TEST_F(LocaleMacTest, formatDate) 197 { 198 EXPECT_STREQ("04/27/2005", formatDate("en_US", 2005, April, 27).utf8().data()); 199 EXPECT_STREQ("27/04/2005", formatDate("fr_FR", 2005, April, 27).utf8().data()); 200 // Do not test ja_JP locale. OS X 10.8 and 10.7 have different formats. 201 } 202 203 TEST_F(LocaleMacTest, formatTime) 204 { 205 EXPECT_STREQ("1:23 PM", formatTime("en_US", 13, 23, 00, 000, true).utf8().data()); 206 EXPECT_STREQ("13:23", formatTime("fr_FR", 13, 23, 00, 000, true).utf8().data()); 207 EXPECT_STREQ("13:23", formatTime("ja_JP", 13, 23, 00, 000, true).utf8().data()); 208 EXPECT_STREQ("\xD9\xA1:\xD9\xA2\xD9\xA3 \xD9\x85", formatTime("ar", 13, 23, 00, 000, true).utf8().data()); 209 EXPECT_STREQ("\xDB\xB1\xDB\xB3:\xDB\xB2\xDB\xB3", formatTime("fa", 13, 23, 00, 000, true).utf8().data()); 210 211 EXPECT_STREQ("12:00 AM", formatTime("en_US", 00, 00, 00, 000, true).utf8().data()); 212 EXPECT_STREQ("00:00", formatTime("fr_FR", 00, 00, 00, 000, true).utf8().data()); 213 EXPECT_STREQ("0:00", formatTime("ja_JP", 00, 00, 00, 000, true).utf8().data()); 214 EXPECT_STREQ("\xD9\xA1\xD9\xA2:\xD9\xA0\xD9\xA0 \xD8\xB5", formatTime("ar", 00, 00, 00, 000, true).utf8().data()); 215 EXPECT_STREQ("\xDB\xB0:\xDB\xB0\xDB\xB0", formatTime("fa", 00, 00, 00, 000, true).utf8().data()); 216 217 EXPECT_STREQ("7:07:07.007 AM", formatTime("en_US", 07, 07, 07, 007, false).utf8().data()); 218 EXPECT_STREQ("07:07:07,007", formatTime("fr_FR", 07, 07, 07, 007, false).utf8().data()); 219 EXPECT_STREQ("7:07:07.007", formatTime("ja_JP", 07, 07, 07, 007, false).utf8().data()); 220 EXPECT_STREQ("\xD9\xA7:\xD9\xA0\xD9\xA7:\xD9\xA0\xD9\xA7\xD9\xAB\xD9\xA0\xD9\xA0\xD9\xA7 \xD8\xB5", formatTime("ar", 07, 07, 07, 007, false).utf8().data()); 221 EXPECT_STREQ("\xDB\xB7:\xDB\xB0\xDB\xB7:\xDB\xB0\xDB\xB7\xD9\xAB\xDB\xB0\xDB\xB0\xDB\xB7", formatTime("fa", 07, 07, 07, 007, false).utf8().data()); 222 } 223 224 #if ENABLE(CALENDAR_PICKER) 225 TEST_F(LocaleMacTest, firstDayOfWeek) 226 { 227 EXPECT_EQ(Sunday, firstDayOfWeek("en_US")); 228 EXPECT_EQ(Monday, firstDayOfWeek("fr_FR")); 229 EXPECT_EQ(Sunday, firstDayOfWeek("ja_JP")); 230 } 231 232 TEST_F(LocaleMacTest, monthLabels) 233 { 234 EXPECT_STREQ("January", monthLabel("en_US", January).utf8().data()); 235 EXPECT_STREQ("June", monthLabel("en_US", June).utf8().data()); 236 EXPECT_STREQ("December", monthLabel("en_US", December).utf8().data()); 237 238 EXPECT_STREQ("janvier", monthLabel("fr_FR", January).utf8().data()); 239 EXPECT_STREQ("juin", monthLabel("fr_FR", June).utf8().data()); 240 EXPECT_STREQ("d\xC3\xA9" "cembre", monthLabel("fr_FR", December).utf8().data()); 241 242 EXPECT_STREQ("1\xE6\x9C\x88", monthLabel("ja_JP", January).utf8().data()); 243 EXPECT_STREQ("6\xE6\x9C\x88", monthLabel("ja_JP", June).utf8().data()); 244 EXPECT_STREQ("12\xE6\x9C\x88", monthLabel("ja_JP", December).utf8().data()); 245 } 246 247 TEST_F(LocaleMacTest, weekDayShortLabels) 248 { 249 EXPECT_STREQ("Sun", weekDayShortLabel("en_US", Sunday).utf8().data()); 250 EXPECT_STREQ("Wed", weekDayShortLabel("en_US", Wednesday).utf8().data()); 251 EXPECT_STREQ("Sat", weekDayShortLabel("en_US", Saturday).utf8().data()); 252 253 EXPECT_STREQ("dim.", weekDayShortLabel("fr_FR", Sunday).utf8().data()); 254 EXPECT_STREQ("mer.", weekDayShortLabel("fr_FR", Wednesday).utf8().data()); 255 EXPECT_STREQ("sam.", weekDayShortLabel("fr_FR", Saturday).utf8().data()); 256 257 EXPECT_STREQ("\xE6\x97\xA5", weekDayShortLabel("ja_JP", Sunday).utf8().data()); 258 EXPECT_STREQ("\xE6\xB0\xB4", weekDayShortLabel("ja_JP", Wednesday).utf8().data()); 259 EXPECT_STREQ("\xE5\x9C\x9F", weekDayShortLabel("ja_JP", Saturday).utf8().data()); 260 } 261 262 TEST_F(LocaleMacTest, isRTL) 263 { 264 EXPECT_TRUE(isRTL("ar-eg")); 265 EXPECT_FALSE(isRTL("en-us")); 266 EXPECT_FALSE(isRTL("ja-jp")); 267 EXPECT_FALSE(isRTL("**invalid**")); 268 } 269 #endif 270 271 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 272 TEST_F(LocaleMacTest, monthFormat) 273 { 274 EXPECT_STREQ("MMMM yyyy", monthFormat("en_US").utf8().data()); 275 EXPECT_STREQ("yyyy\xE5\xB9\xB4M\xE6\x9C\x88", monthFormat("ja_JP").utf8().data()); 276 277 // fr_FR and ru return different results on OS versions. 278 // "MMM yyyy" "LLL yyyy" on 10.6 and 10.7 279 // "MMM y" "LLL y" on 10.8 280 } 281 282 TEST_F(LocaleMacTest, timeFormat) 283 { 284 EXPECT_STREQ("h:mm:ss a", timeFormat("en_US").utf8().data()); 285 EXPECT_STREQ("HH:mm:ss", timeFormat("fr_FR").utf8().data()); 286 EXPECT_STREQ("H:mm:ss", timeFormat("ja_JP").utf8().data()); 287 } 288 289 TEST_F(LocaleMacTest, shortTimeFormat) 290 { 291 EXPECT_STREQ("h:mm a", shortTimeFormat("en_US").utf8().data()); 292 EXPECT_STREQ("HH:mm", shortTimeFormat("fr_FR").utf8().data()); 293 EXPECT_STREQ("H:mm", shortTimeFormat("ja_JP").utf8().data()); 294 } 295 296 TEST_F(LocaleMacTest, standAloneMonthLabels) 297 { 298 EXPECT_STREQ("January", standAloneMonthLabel("en_US", January).utf8().data()); 299 EXPECT_STREQ("June", standAloneMonthLabel("en_US", June).utf8().data()); 300 EXPECT_STREQ("December", standAloneMonthLabel("en_US", December).utf8().data()); 301 302 EXPECT_STREQ("janvier", standAloneMonthLabel("fr_FR", January).utf8().data()); 303 EXPECT_STREQ("juin", standAloneMonthLabel("fr_FR", June).utf8().data()); 304 EXPECT_STREQ("d\xC3\xA9" "cembre", standAloneMonthLabel("fr_FR", December).utf8().data()); 305 306 EXPECT_STREQ("1\xE6\x9C\x88", standAloneMonthLabel("ja_JP", January).utf8().data()); 307 EXPECT_STREQ("6\xE6\x9C\x88", standAloneMonthLabel("ja_JP", June).utf8().data()); 308 EXPECT_STREQ("12\xE6\x9C\x88", standAloneMonthLabel("ja_JP", December).utf8().data()); 309 } 310 311 TEST_F(LocaleMacTest, shortMonthLabels) 312 { 313 EXPECT_STREQ("Jan", shortMonthLabel("en_US", 0).utf8().data()); 314 EXPECT_STREQ("Jan", shortStandAloneMonthLabel("en_US", 0).utf8().data()); 315 EXPECT_STREQ("Dec", shortMonthLabel("en_US", 11).utf8().data()); 316 EXPECT_STREQ("Dec", shortStandAloneMonthLabel("en_US", 11).utf8().data()); 317 318 EXPECT_STREQ("janv.", shortMonthLabel("fr_FR", 0).utf8().data()); 319 EXPECT_STREQ("janv.", shortStandAloneMonthLabel("fr_FR", 0).utf8().data()); 320 EXPECT_STREQ("d\xC3\xA9" "c.", shortMonthLabel("fr_FR", 11).utf8().data()); 321 EXPECT_STREQ("d\xC3\xA9" "c.", shortStandAloneMonthLabel("fr_FR", 11).utf8().data()); 322 323 EXPECT_STREQ("1\xE6\x9C\x88", shortMonthLabel("ja_JP", 0).utf8().data()); 324 EXPECT_STREQ("1\xE6\x9C\x88", shortStandAloneMonthLabel("ja_JP", 0).utf8().data()); 325 EXPECT_STREQ("12\xE6\x9C\x88", shortMonthLabel("ja_JP", 11).utf8().data()); 326 EXPECT_STREQ("12\xE6\x9C\x88", shortStandAloneMonthLabel("ja_JP", 11).utf8().data()); 327 328 EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x80\xD1\x82\xD0\xB0", shortMonthLabel("ru_RU", 2).utf8().data()); 329 EXPECT_STREQ("\xD0\xBC\xD0\xB0\xD1\x8F", shortMonthLabel("ru_RU", 4).utf8().data()); 330 // The ru_RU locale returns different stand-alone month labels on OS versions. 331 // "\xD0\xBC\xD0\xB0\xD1\x80\xD1\x82" "\xD0\xBC\xD0\xB0\xD0\xB9" on 10.6 and 10.7 332 // "\xD0\x9C\xD0\xB0\xD1\x80\xD1\x82" "\xD0\x9C\xD0\xB0\xD0\xB9" on 10.8 333 } 334 335 TEST_F(LocaleMacTest, timeAMPMLabels) 336 { 337 EXPECT_STREQ("AM", timeAMPMLabel("en_US", 0).utf8().data()); 338 EXPECT_STREQ("PM", timeAMPMLabel("en_US", 1).utf8().data()); 339 340 EXPECT_STREQ("AM", timeAMPMLabel("fr_FR", 0).utf8().data()); 341 EXPECT_STREQ("PM", timeAMPMLabel("fr_FR", 1).utf8().data()); 342 343 EXPECT_STREQ("\xE5\x8D\x88\xE5\x89\x8D", timeAMPMLabel("ja_JP", 0).utf8().data()); 344 EXPECT_STREQ("\xE5\x8D\x88\xE5\xBE\x8C", timeAMPMLabel("ja_JP", 1).utf8().data()); 345 } 346 347 TEST_F(LocaleMacTest, decimalSeparator) 348 { 349 EXPECT_STREQ(".", decimalSeparator("en_US").utf8().data()); 350 EXPECT_STREQ(",", decimalSeparator("fr_FR").utf8().data()); 351 } 352 #endif 353 354 TEST_F(LocaleMacTest, invalidLocale) 355 { 356 EXPECT_STREQ(monthLabel("en_US", January).utf8().data(), monthLabel("foo", January).utf8().data()); 357 EXPECT_STREQ(decimalSeparator("en_US").utf8().data(), decimalSeparator("foo").utf8().data()); 358 } 359 360 static void testNumberIsReversible(const AtomicString& localeString, const char* original, const char* shouldHave = 0) 361 { 362 OwnPtr<Locale> locale = Locale::create(localeString); 363 String localized = locale->convertToLocalizedNumber(original); 364 if (shouldHave) 365 EXPECT_TRUE(localized.contains(shouldHave)); 366 String converted = locale->convertFromLocalizedNumber(localized); 367 EXPECT_STREQ(original, converted.utf8().data()); 368 } 369 370 void testNumbers(const AtomicString& localeString, const char* decimalSeparatorShouldBe = 0) 371 { 372 testNumberIsReversible(localeString, "123456789012345678901234567890"); 373 testNumberIsReversible(localeString, "-123.456", decimalSeparatorShouldBe); 374 testNumberIsReversible(localeString, ".456", decimalSeparatorShouldBe); 375 testNumberIsReversible(localeString, "-0.456", decimalSeparatorShouldBe); 376 } 377 378 TEST_F(LocaleMacTest, localizedNumberRoundTrip) 379 { 380 // Test some of major locales. 381 testNumbers("en_US", "."); 382 testNumbers("fr_FR", ","); 383 testNumbers("ar"); 384 testNumbers("de_DE"); 385 testNumbers("es_ES"); 386 testNumbers("fa"); 387 testNumbers("ja_JP"); 388 testNumbers("ko_KR"); 389 testNumbers("zh_CN"); 390 testNumbers("zh_HK"); 391 testNumbers("zh_TW"); 392 } 393