Home | History | Annotate | Download | only in l10n
      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 "third_party/icu/source/common/unicode/unistr.h"
     11 #include "ui/base/l10n/l10n_util_plurals.h"
     12 #include "ui/strings/grit/ui_strings.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