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

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' comments 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 "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 Formatter::force_fallback_ = false;
18
19 static const size_t kNPluralities = 6;
bartfab (slow) 2014/02/20 16:31:23 Nit: Avoid abbreviations like "N" whenever possibl
Thiemo Nagel 2014/02/22 21:44:10 Done.
20 struct Pluralities {
21 int Ids[kNPluralities];
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_[TWOUNITS_MIN_SEC][0] = InitFormat(min_sec_pluralities1);
207 detailed_format_[TWOUNITS_MIN_SEC][1] = InitFormat(min_sec_pluralities2);
208 detailed_format_[TWOUNITS_HOUR_MIN][0] = InitFormat(hour_min_pluralities1);
209 detailed_format_[TWOUNITS_HOUR_MIN][1] = InitFormat(hour_min_pluralities2);
210 detailed_format_[TWOUNITS_DAY_HOUR][0] = InitFormat(day_hour_pluralities1);
211 detailed_format_[TWOUNITS_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 value1,
226 int value2,
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(value1, error);
234 DCHECK(U_SUCCESS(error));
235 formatted_string += detailed_format_[units][1]->format(value2, 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 }
247 pattern += icu::UnicodeString(pluralities.fallback_other);
248
249 UErrorCode error = U_ZERO_ERROR;
250 scoped_ptr<icu::PluralFormat> format(
251 new icu::PluralFormat(rules, pattern, error));
252 DCHECK(U_SUCCESS(error));
253 return format.Pass();
254 }
255
256 scoped_ptr<icu::PluralFormat> Formatter::InitFormat(
257 const Pluralities& pluralities) {
258 if (!Formatter::force_fallback_) {
259 icu::UnicodeString pattern;
260 std::vector<int> ids;
261 for (size_t j = 0; j < kNPluralities; ++j) {
262 ids.push_back(pluralities.Ids[j]);
263 }
264 scoped_ptr<icu::PluralFormat> format = l10n_util::BuildPluralFormat(ids);
265 if (format.get())
266 return format.Pass();
267 }
268
269 scoped_ptr<icu::PluralRules> rules(l10n_util::BuildPluralRules());
270 return CreateFallbackFormat(*rules, pluralities);
271 }
272
273 const Formatter* FormatterContainer::Get(TimeFormat::Format format,
274 TimeFormat::Length length) const {
275 DCHECK(formatter_[format][length])
276 << "Combination of FORMAT_ELAPSED and LENGTH_LONG is not implemented!";
277 return formatter_[format][length].get();
278 }
279
280 FormatterContainer::FormatterContainer() {
281 Initialize();
282 }
283
284 FormatterContainer::~FormatterContainer() {
285 }
286
287 void FormatterContainer::Initialize() {
288 formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_SHORT].reset(
289 new Formatter(IDS_ELAPSED_SHORT_SEC,
290 IDS_ELAPSED_SHORT_MIN,
291 IDS_ELAPSED_HOUR,
292 IDS_ELAPSED_DAY));
293 formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_LONG].reset();
294 formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_SHORT].reset(
295 new Formatter(IDS_REMAINING_SHORT_SEC,
296 IDS_REMAINING_SHORT_MIN,
297 IDS_REMAINING_HOUR,
298 IDS_REMAINING_DAY));
299 formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_LONG].reset(
300 new Formatter(IDS_REMAINING_LONG_SEC,
301 IDS_REMAINING_LONG_MIN,
302 IDS_REMAINING_HOUR,
303 IDS_REMAINING_DAY));
304 formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_SHORT].reset(
305 new Formatter(IDS_DURATION_SHORT_SEC,
306 IDS_DURATION_SHORT_MIN,
307 IDS_DURATION_HOUR,
308 IDS_DURATION_DAY));
309 formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_LONG].reset(
310 new Formatter(IDS_LONG_SEC,
311 IDS_LONG_MIN,
312 IDS_DURATION_HOUR,
313 IDS_DURATION_DAY,
314 IDS_LONG_MIN_1ST,
315 IDS_LONG_SEC_2ND,
316 IDS_DURATION_HOUR_1ST,
317 IDS_LONG_MIN_2ND,
318 IDS_DURATION_DAY_1ST,
319 IDS_DURATION_HOUR_2ND));
320 }
321
322 void FormatterContainer::Shutdown() {
323 for (int format = 0; format < TimeFormat::FORMAT_COUNT; ++format) {
324 for (int length = 0; length < TimeFormat::LENGTH_COUNT; ++length) {
325 formatter_[format][length].reset();
326 }
327 }
328 }
329
330 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698