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.
5 #include "ui/base/l10n/formatter.h"
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"
16 UI_BASE_EXPORT
bool formatter_force_fallback
= false;
18 static const size_t kNumberPluralities
= 6;
20 int ids
[kNumberPluralities
];
21 const char* fallback_one
;
22 const char* fallback_other
;
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
},
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
},
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
},
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
},
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
},
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
},
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
},
74 " other{# seconds left}"
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
},
81 " other{# minutes left}"
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
},
88 " other{# hours left}"
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
},
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
},
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
},
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
},
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
},
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
},
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
},
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
},
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
},
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
},
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
},
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
},
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
},
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
);
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
);
213 void Formatter::Format(Unit unit
,
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().";
223 void Formatter::Format(TwoUnits units
,
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
));
239 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
);
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();
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
);
263 return format
.Pass();
266 scoped_ptr
<icu::PluralRules
> rules(l10n_util::BuildPluralRules());
267 return CreateFallbackFormat(*rules
, pluralities
);
270 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();
277 FormatterContainer::FormatterContainer() {
281 FormatterContainer::~FormatterContainer() {
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
,
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
,
296 formatter_
[TimeFormat::FORMAT_REMAINING
][TimeFormat::LENGTH_LONG
].reset(
297 new Formatter(IDS_REMAINING_LONG_SEC
,
298 IDS_REMAINING_LONG_MIN
,
301 formatter_
[TimeFormat::FORMAT_DURATION
][TimeFormat::LENGTH_SHORT
].reset(
302 new Formatter(IDS_DURATION_SHORT_SEC
,
303 IDS_DURATION_SHORT_MIN
,
306 formatter_
[TimeFormat::FORMAT_DURATION
][TimeFormat::LENGTH_LONG
].reset(
307 new Formatter(IDS_LONG_SEC
,
313 IDS_DURATION_HOUR_1ST
,
315 IDS_DURATION_DAY_1ST
,
316 IDS_DURATION_HOUR_2ND
));
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();