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 <limits.h>
8 
9 #include <memory>
10 #include <vector>
11 
12 #include "base/logging.h"
13 #include "third_party/icu/source/common/unicode/unistr.h"
14 #include "third_party/icu/source/i18n/unicode/msgfmt.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/strings/grit/ui_strings.h"
17 
18 namespace ui {
19 
20 UI_BASE_EXPORT bool formatter_force_fallback = false;
21 
22 struct Pluralities {
23   int id;
24   const char* const fallback_one;
25   const char* const fallback_other;
26 };
27 
28 static const Pluralities IDS_ELAPSED_SHORT_SEC = {
29   IDS_TIME_ELAPSED_SECS,
30   "one{# sec ago}",
31   " other{# secs ago}"
32 };
33 
34 static const Pluralities IDS_ELAPSED_LONG_SEC = {
35     IDS_TIME_ELAPSED_LONG_SECS, "one{# second ago}", " other{# seconds ago}"};
36 
37 static const Pluralities IDS_ELAPSED_SHORT_MIN = {
38   IDS_TIME_ELAPSED_MINS,
39   "one{# min ago}",
40   " other{# mins ago}"
41 };
42 
43 static const Pluralities IDS_ELAPSED_LONG_MIN = {
44     IDS_TIME_ELAPSED_LONG_MINS, "one{# minute ago}", " other{# minutes ago}"};
45 
46 static const Pluralities IDS_ELAPSED_HOUR = {
47   IDS_TIME_ELAPSED_HOURS,
48   "one{# hour ago}",
49   " other{# hours ago}"
50 };
51 static const Pluralities IDS_ELAPSED_DAY = {
52   IDS_TIME_ELAPSED_DAYS,
53   "one{# day ago}",
54   " other{# days ago}"
55 };
56 static const Pluralities IDS_ELAPSED_MONTH = {
57     IDS_TIME_ELAPSED_MONTHS, "one{# month ago}", " other{# months ago}"};
58 static const Pluralities IDS_ELAPSED_YEAR = {
59     IDS_TIME_ELAPSED_YEARS, "one{# year ago}", " other{# years ago}"};
60 
61 static const Pluralities IDS_REMAINING_SHORT_SEC = {
62   IDS_TIME_REMAINING_SECS,
63   "one{# sec left}",
64   " other{# secs left}"
65 };
66 static const Pluralities IDS_REMAINING_SHORT_MIN = {
67   IDS_TIME_REMAINING_MINS,
68   "one{# min left}",
69   " other{# mins left}"
70 };
71 
72 static const Pluralities IDS_REMAINING_LONG_SEC = {
73   IDS_TIME_REMAINING_LONG_SECS,
74   "one{# second left}",
75   " other{# seconds left}"
76 };
77 static const Pluralities IDS_REMAINING_LONG_MIN = {
78   IDS_TIME_REMAINING_LONG_MINS,
79   "one{# minute left}",
80   " other{# minutes left}"
81 };
82 static const Pluralities IDS_REMAINING_HOUR = {
83   IDS_TIME_REMAINING_HOURS,
84   "one{# hour left}",
85   " other{# hours left}"
86 };
87 static const Pluralities IDS_REMAINING_DAY = {
88   IDS_TIME_REMAINING_DAYS,
89   "one{# day left}",
90   " other{# days left}"
91 };
92 static const Pluralities IDS_REMAINING_MONTH = {
93     IDS_TIME_REMAINING_MONTHS, "one{# month left}", " other{# months left}"};
94 static const Pluralities IDS_REMAINING_YEAR = {
95     IDS_TIME_REMAINING_YEARS, "one{# year left}", " other{# years left}"};
96 
97 static const Pluralities IDS_DURATION_SHORT_SEC = {
98   IDS_TIME_SECS,
99   "one{# sec}",
100   " other{# secs}"
101 };
102 static const Pluralities IDS_DURATION_SHORT_MIN = {
103   IDS_TIME_MINS,
104   "one{# min}",
105   " other{# mins}"
106 };
107 
108 static const Pluralities IDS_LONG_SEC = {
109   IDS_TIME_LONG_SECS,
110   "one{# second}",
111   " other{# seconds}"
112 };
113 static const Pluralities IDS_LONG_MIN = {
114   IDS_TIME_LONG_MINS,
115   "one{# minute}",
116   " other{# minutes}"
117 };
118 static const Pluralities IDS_DURATION_HOUR = {
119   IDS_TIME_HOURS,
120   "one{# hour}",
121   " other{# hours}"
122 };
123 static const Pluralities IDS_DURATION_DAY = {
124   IDS_TIME_DAYS,
125   "one{# day}",
126   " other{# days}"
127 };
128 static const Pluralities IDS_DURATION_MONTH = {IDS_TIME_MONTHS, "one{# month}",
129                                                " other{# months}"};
130 static const Pluralities IDS_DURATION_YEAR = {IDS_TIME_YEARS, "one{# year}",
131                                               " other{# years}"};
132 
133 static const Pluralities IDS_LONG_MIN_1ST = {
134   IDS_TIME_LONG_MINS_1ST,
135   "one{# minute and }",
136   " other{# minutes and }"
137 };
138 static const Pluralities IDS_LONG_SEC_2ND = {
139   IDS_TIME_LONG_SECS_2ND,
140   "one{# second}",
141   " other{# seconds}"
142 };
143 static const Pluralities IDS_DURATION_HOUR_1ST = {
144   IDS_TIME_HOURS_1ST,
145   "one{# hour and }",
146   " other{# hours and }"
147 };
148 static const Pluralities IDS_LONG_MIN_2ND = {
149   IDS_TIME_LONG_MINS_2ND,
150   "one{# minute}",
151   " other{# minutes}"
152 };
153 static const Pluralities IDS_DURATION_DAY_1ST = {
154   IDS_TIME_DAYS_1ST,
155   "one{# day and }",
156   " other{# days and }"
157 };
158 static const Pluralities IDS_DURATION_HOUR_2ND = {
159   IDS_TIME_HOURS_2ND,
160   "one{# hour}",
161   " other{# hours}"
162 };
163 
164 namespace {
165 
BuildPluralRules()166 std::unique_ptr<icu::PluralRules> BuildPluralRules() {
167   UErrorCode err = U_ZERO_ERROR;
168   std::unique_ptr<icu::PluralRules> rules(
169       icu::PluralRules::forLocale(icu::Locale::getDefault(), err));
170   if (U_FAILURE(err)) {
171     err = U_ZERO_ERROR;
172     icu::UnicodeString fallback_rules("one: n is 1", -1, US_INV);
173     rules.reset(icu::PluralRules::createRules(fallback_rules, err));
174     DCHECK(U_SUCCESS(err));
175   }
176   return rules;
177 }
178 
FormatNumberInPlural(const icu::MessageFormat & format,int number,icu::UnicodeString * result,UErrorCode * err)179 void FormatNumberInPlural(const icu::MessageFormat& format, int number,
180                           icu::UnicodeString* result, UErrorCode* err) {
181   if (U_FAILURE(*err)) return;
182   icu::Formattable formattable(number);
183   icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
184   format.format(&formattable, 1, *result, ignore, *err);
185   DCHECK(U_SUCCESS(*err));
186   return;
187 }
188 
189 }  // namespace
190 
Formatter(const Pluralities & sec_pluralities,const Pluralities & min_pluralities,const Pluralities & hour_pluralities,const Pluralities & day_pluralities,const Pluralities & month_pluralities,const Pluralities & year_pluralities)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& month_pluralities,
196                      const Pluralities& year_pluralities) {
197   simple_format_[UNIT_SEC] = InitFormat(sec_pluralities);
198   simple_format_[UNIT_MIN] = InitFormat(min_pluralities);
199   simple_format_[UNIT_HOUR] = InitFormat(hour_pluralities);
200   simple_format_[UNIT_DAY] = InitFormat(day_pluralities);
201   simple_format_[UNIT_MONTH] = InitFormat(month_pluralities);
202   simple_format_[UNIT_YEAR] = InitFormat(year_pluralities);
203 }
204 
Formatter(const Pluralities & sec_pluralities,const Pluralities & min_pluralities,const Pluralities & hour_pluralities,const Pluralities & day_pluralities,const Pluralities & month_pluralities,const Pluralities & year_pluralities,const Pluralities & min_sec_pluralities1,const Pluralities & min_sec_pluralities2,const Pluralities & hour_min_pluralities1,const Pluralities & hour_min_pluralities2,const Pluralities & day_hour_pluralities1,const Pluralities & day_hour_pluralities2)205 Formatter::Formatter(const Pluralities& sec_pluralities,
206                      const Pluralities& min_pluralities,
207                      const Pluralities& hour_pluralities,
208                      const Pluralities& day_pluralities,
209                      const Pluralities& month_pluralities,
210                      const Pluralities& year_pluralities,
211                      const Pluralities& min_sec_pluralities1,
212                      const Pluralities& min_sec_pluralities2,
213                      const Pluralities& hour_min_pluralities1,
214                      const Pluralities& hour_min_pluralities2,
215                      const Pluralities& day_hour_pluralities1,
216                      const Pluralities& day_hour_pluralities2) {
217   simple_format_[UNIT_SEC] = InitFormat(sec_pluralities);
218   simple_format_[UNIT_MIN] = InitFormat(min_pluralities);
219   simple_format_[UNIT_HOUR] = InitFormat(hour_pluralities);
220   simple_format_[UNIT_DAY] = InitFormat(day_pluralities);
221   simple_format_[UNIT_MONTH] = InitFormat(month_pluralities);
222   simple_format_[UNIT_YEAR] = InitFormat(year_pluralities);
223   detailed_format_[TWO_UNITS_MIN_SEC][0] = InitFormat(min_sec_pluralities1);
224   detailed_format_[TWO_UNITS_MIN_SEC][1] = InitFormat(min_sec_pluralities2);
225   detailed_format_[TWO_UNITS_HOUR_MIN][0] = InitFormat(hour_min_pluralities1);
226   detailed_format_[TWO_UNITS_HOUR_MIN][1] = InitFormat(hour_min_pluralities2);
227   detailed_format_[TWO_UNITS_DAY_HOUR][0] = InitFormat(day_hour_pluralities1);
228   detailed_format_[TWO_UNITS_DAY_HOUR][1] = InitFormat(day_hour_pluralities2);
229 }
230 
Format(Unit unit,int value,icu::UnicodeString * formatted_string) const231 void Formatter::Format(Unit unit,
232                        int value,
233                        icu::UnicodeString* formatted_string) const {
234   DCHECK(simple_format_[unit]);
235   DCHECK(formatted_string->isEmpty() == TRUE);
236   UErrorCode error = U_ZERO_ERROR;
237   FormatNumberInPlural(*simple_format_[unit],
238                         value, formatted_string, &error);
239   DCHECK(U_SUCCESS(error)) << "Error in icu::PluralFormat::format().";
240   return;
241 }
242 
Format(TwoUnits units,int value_1,int value_2,icu::UnicodeString * formatted_string) const243 void Formatter::Format(TwoUnits units,
244                        int value_1,
245                        int value_2,
246                        icu::UnicodeString* formatted_string) const {
247   DCHECK(detailed_format_[units][0])
248       << "Detailed() not implemented for your (format, length) combination!";
249   DCHECK(detailed_format_[units][1])
250       << "Detailed() not implemented for your (format, length) combination!";
251   DCHECK(formatted_string->isEmpty() == TRUE);
252   UErrorCode error = U_ZERO_ERROR;
253   FormatNumberInPlural(*detailed_format_[units][0], value_1,
254                        formatted_string, &error);
255   DCHECK(U_SUCCESS(error));
256   FormatNumberInPlural(*detailed_format_[units][1], value_2,
257                         formatted_string, &error);
258   DCHECK(U_SUCCESS(error));
259   return;
260 }
261 
CreateFallbackFormat(const icu::PluralRules & rules,const Pluralities & pluralities) const262 std::unique_ptr<icu::MessageFormat> Formatter::CreateFallbackFormat(
263     const icu::PluralRules& rules,
264     const Pluralities& pluralities) const {
265   icu::UnicodeString pattern("{NUMBER, plural, ");
266   if (rules.isKeyword(UNICODE_STRING_SIMPLE("one")))
267     pattern += icu::UnicodeString(pluralities.fallback_one);
268   pattern += icu::UnicodeString(pluralities.fallback_other);
269   pattern.append(UChar(0x7du));  // "}" = U+007D
270 
271   UErrorCode error = U_ZERO_ERROR;
272   std::unique_ptr<icu::MessageFormat> format(
273       new icu::MessageFormat(pattern, error));
274   DCHECK(U_SUCCESS(error));
275   return format;
276 }
277 
InitFormat(const Pluralities & pluralities)278 std::unique_ptr<icu::MessageFormat> Formatter::InitFormat(
279     const Pluralities& pluralities) {
280   if (!formatter_force_fallback) {
281     base::string16 pattern = l10n_util::GetStringUTF16(pluralities.id);
282     UErrorCode error = U_ZERO_ERROR;
283     std::unique_ptr<icu::MessageFormat> format(new icu::MessageFormat(
284         icu::UnicodeString(FALSE, pattern.data(), pattern.length()), error));
285     DCHECK(U_SUCCESS(error));
286     if (format.get())
287       return format;
288   }
289 
290   std::unique_ptr<icu::PluralRules> rules(BuildPluralRules());
291   return CreateFallbackFormat(*rules, pluralities);
292 }
293 
Get(TimeFormat::Format format,TimeFormat::Length length) const294 const Formatter* FormatterContainer::Get(TimeFormat::Format format,
295                                          TimeFormat::Length length) const {
296   return formatter_[format][length].get();
297 }
298 
FormatterContainer()299 FormatterContainer::FormatterContainer() {
300   Initialize();
301 }
302 
~FormatterContainer()303 FormatterContainer::~FormatterContainer() {
304 }
305 
Initialize()306 void FormatterContainer::Initialize() {
307   formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_SHORT].reset(
308       new Formatter(IDS_ELAPSED_SHORT_SEC, IDS_ELAPSED_SHORT_MIN,
309                     IDS_ELAPSED_HOUR, IDS_ELAPSED_DAY, IDS_ELAPSED_MONTH,
310                     IDS_ELAPSED_YEAR));
311   formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_LONG].reset(
312       new Formatter(IDS_ELAPSED_LONG_SEC, IDS_ELAPSED_LONG_MIN,
313                     IDS_ELAPSED_HOUR, IDS_ELAPSED_DAY, IDS_ELAPSED_MONTH,
314                     IDS_ELAPSED_YEAR));
315   formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_SHORT].reset(
316       new Formatter(IDS_REMAINING_SHORT_SEC, IDS_REMAINING_SHORT_MIN,
317                     IDS_REMAINING_HOUR, IDS_REMAINING_DAY, IDS_REMAINING_MONTH,
318                     IDS_REMAINING_YEAR));
319   formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_LONG].reset(
320       new Formatter(IDS_REMAINING_LONG_SEC, IDS_REMAINING_LONG_MIN,
321                     IDS_REMAINING_HOUR, IDS_REMAINING_DAY, IDS_REMAINING_MONTH,
322                     IDS_REMAINING_YEAR));
323   formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_SHORT].reset(
324       new Formatter(IDS_DURATION_SHORT_SEC, IDS_DURATION_SHORT_MIN,
325                     IDS_DURATION_HOUR, IDS_DURATION_DAY, IDS_DURATION_MONTH,
326                     IDS_DURATION_YEAR));
327   formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_LONG].reset(
328       new Formatter(IDS_LONG_SEC, IDS_LONG_MIN, IDS_DURATION_HOUR,
329                     IDS_DURATION_DAY, IDS_DURATION_MONTH, IDS_DURATION_YEAR,
330                     IDS_LONG_MIN_1ST, IDS_LONG_SEC_2ND, IDS_DURATION_HOUR_1ST,
331                     IDS_LONG_MIN_2ND, IDS_DURATION_DAY_1ST,
332                     IDS_DURATION_HOUR_2ND));
333 }
334 
Shutdown()335 void FormatterContainer::Shutdown() {
336   for (int format = 0; format < TimeFormat::FORMAT_COUNT; ++format) {
337     for (int length = 0; length < TimeFormat::LENGTH_COUNT; ++length) {
338       formatter_[format][length].reset();
339     }
340   }
341 }
342 
343 }  // namespace ui
344