• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Formatter(const Pluralities & sec_pluralities,const Pluralities & min_pluralities,const Pluralities & hour_pluralities,const Pluralities & day_pluralities)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 
Formatter(const Pluralities & sec_pluralities,const Pluralities & min_pluralities,const Pluralities & hour_pluralities,const Pluralities & day_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)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 
Format(Unit unit,int value,icu::UnicodeString & formatted_string) const213 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 
Format(TwoUnits units,int value_1,int value_2,icu::UnicodeString & formatted_string) const223 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 
CreateFallbackFormat(const icu::PluralRules & rules,const Pluralities & pluralities) const239 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 
InitFormat(const Pluralities & pluralities)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 
Get(TimeFormat::Format format,TimeFormat::Length length) const270 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 
FormatterContainer()277 FormatterContainer::FormatterContainer() {
278   Initialize();
279 }
280 
~FormatterContainer()281 FormatterContainer::~FormatterContainer() {
282 }
283 
Initialize()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 
Shutdown()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