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

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: More cleanup and documentation Created 6 years, 10 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 "grit/ui_strings.h"
10 #include "third_party/icu/source/common/unicode/unistr.h"
11 #include "ui/base/l10n/l10n_util_plurals.h"
12
13 namespace ui {
14
15 // static
16 bool FormatterContainer::ForceFallback_ = false;
17
18 static const size_t kNPluralities = 6;
19 struct Pluralities {
20 int Ids[kNPluralities];
bartfab (slow) 2014/02/18 12:04:04 Nit: User hacker_style, not CamelCaps for member n
Thiemo Nagel 2014/02/19 17:08:44 Done.
21 const char *FallbackOne;
bartfab (slow) 2014/02/18 12:04:04 Nit: Put * on the type, not the member.
Thiemo Nagel 2014/02/19 17:08:44 Done.
22 const char *FallbackOther;
23 };
24
25 static const Pluralities IDS_ELAPSED_SHORT_SEC = {
26 { IDS_TIME_ELAPSED_SECS_DEFAULT, IDS_TIME_ELAPSED_SEC_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_MIN_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_HOUR_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_DAY_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_SEC_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_MIN_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_SEC_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_MIN_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_HOUR_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_DAY_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_SEC_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_MIN_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_SEC_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_MIN_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_HOUR_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_DAY_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_MIN_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_SEC_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_HOUR_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_MIN_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_DAY_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_HOUR_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 format_[SEC].reset(InitFormat(sec_pluralities));
186 format_[MIN].reset(InitFormat(min_pluralities));
187 format_[HOUR].reset(InitFormat(hour_pluralities));
188 format_[DAY].reset(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 format_[SEC].reset(InitFormat(sec_pluralities));
202 format_[MIN].reset(InitFormat(min_pluralities));
203 format_[HOUR].reset(InitFormat(hour_pluralities));
204 format_[DAY].reset(InitFormat(day_pluralities));
205 detailed_format_[MIN_SEC][0].reset(InitFormat(min_sec_pluralities1));
206 detailed_format_[MIN_SEC][1].reset(InitFormat(min_sec_pluralities2));
207 detailed_format_[HOUR_MIN][0].reset(InitFormat(hour_min_pluralities1));
208 detailed_format_[HOUR_MIN][1].reset(InitFormat(hour_min_pluralities2));
209 detailed_format_[DAY_HOUR][0].reset(InitFormat(day_hour_pluralities1));
210 detailed_format_[DAY_HOUR][1].reset(InitFormat(day_hour_pluralities2));
211 }
212
213 void Formatter::Format(Unit unit, int value,
bartfab (slow) 2014/02/18 12:04:04 Nit: Per the style guide, a declaration/definition
Thiemo Nagel 2014/02/19 17:08:44 Done.
214 icu::UnicodeString& formatted_string) const {
215 UErrorCode error = U_ZERO_ERROR;
bartfab (slow) 2014/02/18 12:04:04 Nit: Why not move down the definition of |error| d
Thiemo Nagel 2014/02/19 17:08:44 Done.
216 DCHECK(format_[unit].get()) << "This can never happen.";
bartfab (slow) 2014/02/18 12:04:04 Nit 1: #include "base/logging.h" Nit 2: format_[un
Thiemo Nagel 2014/02/19 17:08:44 Done.
217 formatted_string = format_[unit].get()->format(value, error);
bartfab (slow) 2014/02/18 12:04:04 Nit: No need for get() before ->
Thiemo Nagel 2014/02/19 17:08:44 Done.
218 DCHECK(U_SUCCESS(error)) << "Formatter error.";
219 return;
220 }
221
222 void Formatter::Format(TwoUnits units, int value1, int value2,
bartfab (slow) 2014/02/18 12:04:04 Nit: Per the style guide, a declaration/definition
Thiemo Nagel 2014/02/19 17:08:44 Done.
223 icu::UnicodeString& formatted_string) const {
224 UErrorCode error = U_ZERO_ERROR;
bartfab (slow) 2014/02/18 12:04:04 Nit: Why not move down the definition of |error| d
Thiemo Nagel 2014/02/19 17:08:44 Done.
225 DCHECK(detailed_format_[units][0].get())
bartfab (slow) 2014/02/18 12:04:04 Nit: detailed_format_[unit][0] can be truth-checke
Thiemo Nagel 2014/02/19 17:08:44 Done.
226 << "Detailed() not implemented for your (type,length) combination!";
227 DCHECK(detailed_format_[units][1].get())
bartfab (slow) 2014/02/18 12:04:04 Nit: detailed_format_[unit][1] can be truth-checke
Thiemo Nagel 2014/02/19 17:08:44 Done.
228 << "Detailed() not implemented for your (type,length) combination!";
229 formatted_string = detailed_format_[units][0].get()->format(value1, error);
bartfab (slow) 2014/02/18 12:04:04 Nit: No need for get() before ->
Thiemo Nagel 2014/02/19 17:08:44 Done.
230 DCHECK(U_SUCCESS(error));
231 formatted_string += detailed_format_[units][1].get()->format(value2, error);
bartfab (slow) 2014/02/18 12:04:04 Nit: No need for get() before ->
Thiemo Nagel 2014/02/19 17:08:44 Done.
232 DCHECK(U_SUCCESS(error));
233 return;
234 }
235
236 icu::PluralFormat* Formatter::CreateFallbackFormat(
bartfab (slow) 2014/02/18 12:04:04 Nit: Per the style guide, a declaration/definition
Thiemo Nagel 2014/02/19 17:08:44 Done.
237 const icu::PluralRules& rules, const Pluralities& pluralities) const {
238 icu::UnicodeString pattern;
239 if (rules.isKeyword(UNICODE_STRING_SIMPLE("one"))) {
bartfab (slow) 2014/02/18 12:04:04 Nit: No curly braces necessary for single-line if.
Thiemo Nagel 2014/02/19 17:08:44 Not necessary but optional, if I read the style gu
bartfab (slow) 2014/02/20 16:31:22 Yes, it is a matter of choice. But if you look at
Thiemo Nagel 2014/02/22 21:44:09 Done, though I'm not certain the code is more read
240 pattern += icu::UnicodeString(pluralities.FallbackOne);
241 }
242 pattern += icu::UnicodeString(pluralities.FallbackOther);
243
244 UErrorCode err = U_ZERO_ERROR;
245 icu::PluralFormat* format = new icu::PluralFormat(rules, pattern, err);
246 DCHECK(U_SUCCESS(err));
247 return format;
248 }
249
250 icu::PluralFormat* Formatter::InitFormat(const Pluralities& pluralities) {
251 icu::UnicodeString pattern;
252 std::vector<int> ids;
253 for (size_t j = 0; j < kNPluralities; ++j) {
bartfab (slow) 2014/02/18 12:04:04 Nit: No need for curly braces when the body of a l
Thiemo Nagel 2014/02/19 17:08:44 I can't find a rule for that.
bartfab (slow) 2014/02/20 16:31:22 It's hiding in the rule for conditionals: "In gen
Thiemo Nagel 2014/02/22 21:44:09 Done.
Thiemo Nagel 2014/02/22 21:44:09 Oh well...
254 ids.push_back(pluralities.Ids[j]);
255 }
256 scoped_ptr<icu::PluralFormat> format = l10n_util::BuildPluralFormat(ids);
257 if (format.get() && !FormatterContainer::GetFallbackForTesting())
bartfab (slow) 2014/02/18 12:04:04 Nit 1: scoped_ptr supports truth testing out of th
Thiemo Nagel 2014/02/19 17:08:44 Done.
258 return format.release();
259
260 scoped_ptr<icu::PluralRules> rules(l10n_util::BuildPluralRules());
261 return CreateFallbackFormat(*rules, pluralities);
262 }
263
264 const Formatter* FormatterContainer::Get(TimeFormat::Type type,
265 TimeFormat::Length length) const {
266 DCHECK(formatter_[type][length])
267 << "Combination of kElapsed and kLong is not implemented!";
268 return formatter_[type][length];
269 }
270
271 FormatterContainer::FormatterContainer() {
bartfab (slow) 2014/02/18 12:04:04 Nit: Can you not initialize formatter_ in the init
Thiemo Nagel 2014/02/19 17:08:44 I'd love to, but that's C++11.
bartfab (slow) 2014/02/20 16:31:22 That's too bad...
272 formatter_[TimeFormat::kElapsed][TimeFormat::kShort] =
273 new Formatter(IDS_ELAPSED_SHORT_SEC,
274 IDS_ELAPSED_SHORT_MIN,
275 IDS_ELAPSED_HOUR,
276 IDS_ELAPSED_DAY);
277 formatter_[TimeFormat::kElapsed][TimeFormat::kLong] = NULL;
278 formatter_[TimeFormat::kRemaining][TimeFormat::kShort] =
279 new Formatter(IDS_REMAINING_SHORT_SEC,
280 IDS_REMAINING_SHORT_MIN,
281 IDS_REMAINING_HOUR,
282 IDS_REMAINING_DAY);
283 formatter_[TimeFormat::kRemaining][TimeFormat::kLong] =
284 new Formatter(IDS_REMAINING_LONG_SEC,
285 IDS_REMAINING_LONG_MIN,
286 IDS_REMAINING_HOUR,
287 IDS_REMAINING_DAY);
288 formatter_[TimeFormat::kDuration][TimeFormat::kShort] =
289 new Formatter(IDS_DURATION_SHORT_SEC,
290 IDS_DURATION_SHORT_MIN,
291 IDS_DURATION_HOUR,
292 IDS_DURATION_DAY);
293 formatter_[TimeFormat::kDuration][TimeFormat::kLong] =
294 new Formatter(IDS_LONG_SEC,
295 IDS_LONG_MIN,
296 IDS_DURATION_HOUR,
297 IDS_DURATION_DAY,
298 IDS_LONG_MIN_1ST,
299 IDS_LONG_SEC_2ND,
300 IDS_DURATION_HOUR_1ST,
301 IDS_LONG_MIN_2ND,
302 IDS_DURATION_DAY_1ST,
303 IDS_DURATION_HOUR_2ND);
304 }
305
306 FormatterContainer::~FormatterContainer() {
307 for (int type = 0; type < TimeFormat::kNType; ++type)
bartfab (slow) 2014/02/18 12:04:04 Nit: This for loop needs curly braces because its
Thiemo Nagel 2014/02/19 17:08:44 Done.
308 for (int length = 0; length < TimeFormat::kNLength; ++length)
309 delete formatter_[type][length];
bartfab (slow) 2014/02/18 12:04:04 Can you make formatter_ be an array of scoped_ptrs
Thiemo Nagel 2014/02/19 17:08:44 Done.
310 }
311
312 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698