OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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. |
| 4 |
| 5 #include "ui/base/l10n/formatter.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "grit/ui_strings.h" |
| 11 #include "third_party/icu/source/common/unicode/unistr.h" |
| 12 #include "ui/base/l10n/l10n_util_plurals.h" |
| 13 |
| 14 namespace ui { |
| 15 |
| 16 // static |
| 17 bool FormatterContainer::force_fallback_ = false; |
| 18 |
| 19 static const size_t kNumberPluralities = 6; |
| 20 struct Pluralities { |
| 21 int Ids[kNumberPluralities]; |
| 22 const char* fallback_one; |
| 23 const char* fallback_other; |
| 24 }; |
| 25 |
| 26 static const Pluralities IDS_ELAPSED_SHORT_SEC = { |
| 27 { IDS_TIME_ELAPSED_SECS_DEFAULT, IDS_TIME_ELAPSED_SEC_SINGULAR, |
| 28 IDS_TIME_ELAPSED_SECS_ZERO, IDS_TIME_ELAPSED_SECS_TWO, |
| 29 IDS_TIME_ELAPSED_SECS_FEW, IDS_TIME_ELAPSED_SECS_MANY }, |
| 30 "one{# sec ago}", |
| 31 " other{# secs ago}" |
| 32 }; |
| 33 static const Pluralities IDS_ELAPSED_SHORT_MIN = { |
| 34 { IDS_TIME_ELAPSED_MINS_DEFAULT, IDS_TIME_ELAPSED_MIN_SINGULAR, |
| 35 IDS_TIME_ELAPSED_MINS_ZERO, IDS_TIME_ELAPSED_MINS_TWO, |
| 36 IDS_TIME_ELAPSED_MINS_FEW, IDS_TIME_ELAPSED_MINS_MANY }, |
| 37 "one{# min ago}", |
| 38 " other{# mins ago}" |
| 39 }; |
| 40 static const Pluralities IDS_ELAPSED_HOUR = { |
| 41 { IDS_TIME_ELAPSED_HOURS_DEFAULT, IDS_TIME_ELAPSED_HOUR_SINGULAR, |
| 42 IDS_TIME_ELAPSED_HOURS_ZERO, IDS_TIME_ELAPSED_HOURS_TWO, |
| 43 IDS_TIME_ELAPSED_HOURS_FEW, IDS_TIME_ELAPSED_HOURS_MANY }, |
| 44 "one{# hour ago}", |
| 45 " other{# hours ago}" |
| 46 }; |
| 47 static const Pluralities IDS_ELAPSED_DAY = { |
| 48 { IDS_TIME_ELAPSED_DAYS_DEFAULT, IDS_TIME_ELAPSED_DAY_SINGULAR, |
| 49 IDS_TIME_ELAPSED_DAYS_ZERO, IDS_TIME_ELAPSED_DAYS_TWO, |
| 50 IDS_TIME_ELAPSED_DAYS_FEW, IDS_TIME_ELAPSED_DAYS_MANY }, |
| 51 "one{# day ago}", |
| 52 " other{# days ago}" |
| 53 }; |
| 54 |
| 55 static const Pluralities IDS_REMAINING_SHORT_SEC = { |
| 56 { IDS_TIME_REMAINING_SECS_DEFAULT, IDS_TIME_REMAINING_SEC_SINGULAR, |
| 57 IDS_TIME_REMAINING_SECS_ZERO, IDS_TIME_REMAINING_SECS_TWO, |
| 58 IDS_TIME_REMAINING_SECS_FEW, IDS_TIME_REMAINING_SECS_MANY }, |
| 59 "one{# sec left}", |
| 60 " other{# secs left}" |
| 61 }; |
| 62 static const Pluralities IDS_REMAINING_SHORT_MIN = { |
| 63 { IDS_TIME_REMAINING_MINS_DEFAULT, IDS_TIME_REMAINING_MIN_SINGULAR, |
| 64 IDS_TIME_REMAINING_MINS_ZERO, IDS_TIME_REMAINING_MINS_TWO, |
| 65 IDS_TIME_REMAINING_MINS_FEW, IDS_TIME_REMAINING_MINS_MANY }, |
| 66 "one{# min left}", |
| 67 " other{# mins left}" |
| 68 }; |
| 69 |
| 70 static const Pluralities IDS_REMAINING_LONG_SEC = { |
| 71 { IDS_TIME_REMAINING_LONG_SECS_DEFAULT, IDS_TIME_REMAINING_LONG_SEC_SINGULAR, |
| 72 IDS_TIME_REMAINING_LONG_SECS_ZERO, IDS_TIME_REMAINING_LONG_SECS_TWO, |
| 73 IDS_TIME_REMAINING_LONG_SECS_FEW, IDS_TIME_REMAINING_LONG_SECS_MANY }, |
| 74 "one{# second left}", |
| 75 " other{# seconds left}" |
| 76 }; |
| 77 static const Pluralities IDS_REMAINING_LONG_MIN = { |
| 78 { IDS_TIME_REMAINING_LONG_MINS_DEFAULT, IDS_TIME_REMAINING_LONG_MIN_SINGULAR, |
| 79 IDS_TIME_REMAINING_LONG_MINS_ZERO, IDS_TIME_REMAINING_LONG_MINS_TWO, |
| 80 IDS_TIME_REMAINING_LONG_MINS_FEW, IDS_TIME_REMAINING_LONG_MINS_MANY }, |
| 81 "one{# minute left}", |
| 82 " other{# minutes left}" |
| 83 }; |
| 84 static const Pluralities IDS_REMAINING_HOUR = { |
| 85 { IDS_TIME_REMAINING_HOURS_DEFAULT, IDS_TIME_REMAINING_HOUR_SINGULAR, |
| 86 IDS_TIME_REMAINING_HOURS_ZERO, IDS_TIME_REMAINING_HOURS_TWO, |
| 87 IDS_TIME_REMAINING_HOURS_FEW, IDS_TIME_REMAINING_HOURS_MANY }, |
| 88 "one{# hour left}", |
| 89 " other{# hours left}" |
| 90 }; |
| 91 static const Pluralities IDS_REMAINING_DAY = { |
| 92 { IDS_TIME_REMAINING_DAYS_DEFAULT, IDS_TIME_REMAINING_DAY_SINGULAR, |
| 93 IDS_TIME_REMAINING_DAYS_ZERO, IDS_TIME_REMAINING_DAYS_TWO, |
| 94 IDS_TIME_REMAINING_DAYS_FEW, IDS_TIME_REMAINING_DAYS_MANY }, |
| 95 "one{# day left}", |
| 96 " other{# days left}" |
| 97 }; |
| 98 |
| 99 static const Pluralities IDS_DURATION_SHORT_SEC = { |
| 100 { IDS_TIME_SECS_DEFAULT, IDS_TIME_SEC_SINGULAR, IDS_TIME_SECS_ZERO, |
| 101 IDS_TIME_SECS_TWO, IDS_TIME_SECS_FEW, IDS_TIME_SECS_MANY }, |
| 102 "one{# sec}", |
| 103 " other{# secs}" |
| 104 }; |
| 105 static const Pluralities IDS_DURATION_SHORT_MIN = { |
| 106 { IDS_TIME_MINS_DEFAULT, IDS_TIME_MIN_SINGULAR, IDS_TIME_MINS_ZERO, |
| 107 IDS_TIME_MINS_TWO, IDS_TIME_MINS_FEW, IDS_TIME_MINS_MANY }, |
| 108 "one{# min}", |
| 109 " other{# mins}" |
| 110 }; |
| 111 |
| 112 static const Pluralities IDS_LONG_SEC = { |
| 113 { IDS_TIME_LONG_SECS_DEFAULT, IDS_TIME_LONG_SEC_SINGULAR, |
| 114 IDS_TIME_LONG_SECS_ZERO, IDS_TIME_LONG_SECS_TWO, |
| 115 IDS_TIME_LONG_SECS_FEW, IDS_TIME_LONG_SECS_MANY }, |
| 116 "one{# second}", |
| 117 " other{# seconds}" |
| 118 }; |
| 119 static const Pluralities IDS_LONG_MIN = { |
| 120 { IDS_TIME_LONG_MINS_DEFAULT, IDS_TIME_LONG_MIN_SINGULAR, |
| 121 IDS_TIME_LONG_MINS_ZERO, IDS_TIME_LONG_MINS_TWO, |
| 122 IDS_TIME_LONG_MINS_FEW, IDS_TIME_LONG_MINS_MANY }, |
| 123 "one{# minute}", |
| 124 " other{# minutes}" |
| 125 }; |
| 126 static const Pluralities IDS_DURATION_HOUR = { |
| 127 { IDS_TIME_HOURS_DEFAULT, IDS_TIME_HOUR_SINGULAR, IDS_TIME_HOURS_ZERO, |
| 128 IDS_TIME_HOURS_TWO, IDS_TIME_HOURS_FEW, IDS_TIME_HOURS_MANY }, |
| 129 "one{# hour}", |
| 130 " other{# hours}" |
| 131 }; |
| 132 static const Pluralities IDS_DURATION_DAY = { |
| 133 { IDS_TIME_DAYS_DEFAULT, IDS_TIME_DAY_SINGULAR, IDS_TIME_DAYS_ZERO, |
| 134 IDS_TIME_DAYS_TWO, IDS_TIME_DAYS_FEW, IDS_TIME_DAYS_MANY }, |
| 135 "one{# day}", |
| 136 " other{# days}" |
| 137 }; |
| 138 |
| 139 static const Pluralities IDS_LONG_MIN_1ST = { |
| 140 { IDS_TIME_LONG_MINS_1ST_DEFAULT, IDS_TIME_LONG_MIN_1ST_SINGULAR, |
| 141 IDS_TIME_LONG_MINS_1ST_ZERO, IDS_TIME_LONG_MINS_1ST_TWO, |
| 142 IDS_TIME_LONG_MINS_1ST_FEW, IDS_TIME_LONG_MINS_1ST_MANY }, |
| 143 "one{# minute }", |
| 144 " other{# minutes }" |
| 145 }; |
| 146 static const Pluralities IDS_LONG_SEC_2ND = { |
| 147 { IDS_TIME_LONG_SECS_2ND_DEFAULT, IDS_TIME_LONG_SEC_2ND_SINGULAR, |
| 148 IDS_TIME_LONG_SECS_2ND_ZERO, IDS_TIME_LONG_SECS_2ND_TWO, |
| 149 IDS_TIME_LONG_SECS_2ND_FEW, IDS_TIME_LONG_SECS_2ND_MANY }, |
| 150 "one{# second}", |
| 151 " other{# seconds}" |
| 152 }; |
| 153 static const Pluralities IDS_DURATION_HOUR_1ST = { |
| 154 { IDS_TIME_HOURS_1ST_DEFAULT, IDS_TIME_HOUR_1ST_SINGULAR, |
| 155 IDS_TIME_HOURS_1ST_ZERO, IDS_TIME_HOURS_1ST_TWO, |
| 156 IDS_TIME_HOURS_1ST_FEW, IDS_TIME_HOURS_1ST_MANY }, |
| 157 "one{# hour }", |
| 158 " other{# hours }" |
| 159 }; |
| 160 static const Pluralities IDS_LONG_MIN_2ND = { |
| 161 { IDS_TIME_LONG_MINS_2ND_DEFAULT, IDS_TIME_LONG_MIN_2ND_SINGULAR, |
| 162 IDS_TIME_LONG_MINS_2ND_ZERO, IDS_TIME_LONG_MINS_2ND_TWO, |
| 163 IDS_TIME_LONG_MINS_2ND_FEW, IDS_TIME_LONG_MINS_2ND_MANY }, |
| 164 "one{# minute}", |
| 165 " other{# minutes}" |
| 166 }; |
| 167 static const Pluralities IDS_DURATION_DAY_1ST = { |
| 168 { IDS_TIME_DAYS_1ST_DEFAULT, IDS_TIME_DAY_1ST_SINGULAR, |
| 169 IDS_TIME_DAYS_1ST_ZERO, IDS_TIME_DAYS_1ST_TWO, |
| 170 IDS_TIME_DAYS_1ST_FEW, IDS_TIME_DAYS_1ST_MANY }, |
| 171 "one{# day }", |
| 172 " other{# days }" |
| 173 }; |
| 174 static const Pluralities IDS_DURATION_HOUR_2ND = { |
| 175 { IDS_TIME_HOURS_2ND_DEFAULT, IDS_TIME_HOUR_2ND_SINGULAR, |
| 176 IDS_TIME_HOURS_2ND_ZERO, IDS_TIME_HOURS_2ND_TWO, |
| 177 IDS_TIME_HOURS_2ND_FEW, IDS_TIME_HOURS_2ND_MANY }, |
| 178 "one{# hour}", |
| 179 " other{# hours}" |
| 180 }; |
| 181 |
| 182 Formatter::Formatter(const Pluralities& sec_pluralities, |
| 183 const Pluralities& min_pluralities, |
| 184 const Pluralities& hour_pluralities, |
| 185 const Pluralities& day_pluralities) { |
| 186 simple_format_[UNIT_SEC] = InitFormat(sec_pluralities); |
| 187 simple_format_[UNIT_MIN] = InitFormat(min_pluralities); |
| 188 simple_format_[UNIT_HOUR] = InitFormat(hour_pluralities); |
| 189 simple_format_[UNIT_DAY] = InitFormat(day_pluralities); |
| 190 } |
| 191 |
| 192 Formatter::Formatter(const Pluralities& sec_pluralities, |
| 193 const Pluralities& min_pluralities, |
| 194 const Pluralities& hour_pluralities, |
| 195 const Pluralities& day_pluralities, |
| 196 const Pluralities& min_sec_pluralities1, |
| 197 const Pluralities& min_sec_pluralities2, |
| 198 const Pluralities& hour_min_pluralities1, |
| 199 const Pluralities& hour_min_pluralities2, |
| 200 const Pluralities& day_hour_pluralities1, |
| 201 const Pluralities& day_hour_pluralities2) { |
| 202 simple_format_[UNIT_SEC] = InitFormat(sec_pluralities); |
| 203 simple_format_[UNIT_MIN] = InitFormat(min_pluralities); |
| 204 simple_format_[UNIT_HOUR] = InitFormat(hour_pluralities); |
| 205 simple_format_[UNIT_DAY] = InitFormat(day_pluralities); |
| 206 detailed_format_[TWO_UNITS_MIN_SEC][0] = InitFormat(min_sec_pluralities1); |
| 207 detailed_format_[TWO_UNITS_MIN_SEC][1] = InitFormat(min_sec_pluralities2); |
| 208 detailed_format_[TWO_UNITS_HOUR_MIN][0] = InitFormat(hour_min_pluralities1); |
| 209 detailed_format_[TWO_UNITS_HOUR_MIN][1] = InitFormat(hour_min_pluralities2); |
| 210 detailed_format_[TWO_UNITS_DAY_HOUR][0] = InitFormat(day_hour_pluralities1); |
| 211 detailed_format_[TWO_UNITS_DAY_HOUR][1] = InitFormat(day_hour_pluralities2); |
| 212 } |
| 213 |
| 214 void Formatter::Format(Unit unit, |
| 215 int value, |
| 216 icu::UnicodeString& formatted_string) const { |
| 217 DCHECK(simple_format_[unit]); |
| 218 UErrorCode error = U_ZERO_ERROR; |
| 219 formatted_string = simple_format_[unit]->format(value, error); |
| 220 DCHECK(U_SUCCESS(error)) << "Error in icu::PluralFormat::format()."; |
| 221 return; |
| 222 } |
| 223 |
| 224 void Formatter::Format(TwoUnits units, |
| 225 int value_1, |
| 226 int value_2, |
| 227 icu::UnicodeString& formatted_string) const { |
| 228 DCHECK(detailed_format_[units][0]) |
| 229 << "Detailed() not implemented for your (format, length) combination!"; |
| 230 DCHECK(detailed_format_[units][1]) |
| 231 << "Detailed() not implemented for your (format, length) combination!"; |
| 232 UErrorCode error = U_ZERO_ERROR; |
| 233 formatted_string = detailed_format_[units][0]->format(value_1, error); |
| 234 DCHECK(U_SUCCESS(error)); |
| 235 formatted_string += detailed_format_[units][1]->format(value_2, error); |
| 236 DCHECK(U_SUCCESS(error)); |
| 237 return; |
| 238 } |
| 239 |
| 240 scoped_ptr<icu::PluralFormat> Formatter::CreateFallbackFormat( |
| 241 const icu::PluralRules& rules, |
| 242 const Pluralities& pluralities) const { |
| 243 icu::UnicodeString pattern; |
| 244 if (rules.isKeyword(UNICODE_STRING_SIMPLE("one"))) |
| 245 pattern += icu::UnicodeString(pluralities.fallback_one); |
| 246 pattern += icu::UnicodeString(pluralities.fallback_other); |
| 247 |
| 248 UErrorCode error = U_ZERO_ERROR; |
| 249 scoped_ptr<icu::PluralFormat> format( |
| 250 new icu::PluralFormat(rules, pattern, error)); |
| 251 DCHECK(U_SUCCESS(error)); |
| 252 return format.Pass(); |
| 253 } |
| 254 |
| 255 scoped_ptr<icu::PluralFormat> Formatter::InitFormat( |
| 256 const Pluralities& pluralities) { |
| 257 if (!FormatterContainer::GetFallback()) { |
| 258 icu::UnicodeString pattern; |
| 259 std::vector<int> ids; |
| 260 for (size_t j = 0; j < kNumberPluralities; ++j) |
| 261 ids.push_back(pluralities.Ids[j]); |
| 262 scoped_ptr<icu::PluralFormat> format = l10n_util::BuildPluralFormat(ids); |
| 263 if (format.get()) |
| 264 return format.Pass(); |
| 265 } |
| 266 |
| 267 scoped_ptr<icu::PluralRules> rules(l10n_util::BuildPluralRules()); |
| 268 return CreateFallbackFormat(*rules, pluralities); |
| 269 } |
| 270 |
| 271 const Formatter* FormatterContainer::Get(TimeFormat::Format format, |
| 272 TimeFormat::Length length) const { |
| 273 DCHECK(formatter_[format][length]) |
| 274 << "Combination of FORMAT_ELAPSED and LENGTH_LONG is not implemented!"; |
| 275 return formatter_[format][length].get(); |
| 276 } |
| 277 |
| 278 FormatterContainer::FormatterContainer() { |
| 279 Initialize(); |
| 280 } |
| 281 |
| 282 FormatterContainer::~FormatterContainer() { |
| 283 } |
| 284 |
| 285 void FormatterContainer::Initialize() { |
| 286 formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_SHORT].reset( |
| 287 new Formatter(IDS_ELAPSED_SHORT_SEC, |
| 288 IDS_ELAPSED_SHORT_MIN, |
| 289 IDS_ELAPSED_HOUR, |
| 290 IDS_ELAPSED_DAY)); |
| 291 formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_LONG].reset(); |
| 292 formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_SHORT].reset( |
| 293 new Formatter(IDS_REMAINING_SHORT_SEC, |
| 294 IDS_REMAINING_SHORT_MIN, |
| 295 IDS_REMAINING_HOUR, |
| 296 IDS_REMAINING_DAY)); |
| 297 formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_LONG].reset( |
| 298 new Formatter(IDS_REMAINING_LONG_SEC, |
| 299 IDS_REMAINING_LONG_MIN, |
| 300 IDS_REMAINING_HOUR, |
| 301 IDS_REMAINING_DAY)); |
| 302 formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_SHORT].reset( |
| 303 new Formatter(IDS_DURATION_SHORT_SEC, |
| 304 IDS_DURATION_SHORT_MIN, |
| 305 IDS_DURATION_HOUR, |
| 306 IDS_DURATION_DAY)); |
| 307 formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_LONG].reset( |
| 308 new Formatter(IDS_LONG_SEC, |
| 309 IDS_LONG_MIN, |
| 310 IDS_DURATION_HOUR, |
| 311 IDS_DURATION_DAY, |
| 312 IDS_LONG_MIN_1ST, |
| 313 IDS_LONG_SEC_2ND, |
| 314 IDS_DURATION_HOUR_1ST, |
| 315 IDS_LONG_MIN_2ND, |
| 316 IDS_DURATION_DAY_1ST, |
| 317 IDS_DURATION_HOUR_2ND)); |
| 318 } |
| 319 |
| 320 void FormatterContainer::Shutdown() { |
| 321 for (int format = 0; format < TimeFormat::FORMAT_COUNT; ++format) { |
| 322 for (int length = 0; length < TimeFormat::LENGTH_COUNT; ++length) { |
| 323 formatter_[format][length].reset(); |
| 324 } |
| 325 } |
| 326 } |
| 327 |
| 328 } // namespace ui |
OLD | NEW |