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