Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(240)

Side by Side Diff: ui/base/l10n/formatter.cc

Issue 147443007: Add support for localized time strings with two units, eg. "2 hours 17 minutes" (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address Bartosz' 3rd round of comments Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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];
tony 2014/02/26 20:29:20 Nit: ids
Thiemo Nagel 2014/02/26 21:13:34 Done.
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698