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 "base/memory/scoped_ptr.h"
11 #include "third_party/icu/source/common/unicode/unistr.h"
12 #include "third_party/icu/source/i18n/unicode/msgfmt.h"
13 #include "ui/base/l10n/l10n_util.h"
14 #include "ui/strings/grit/ui_strings.h"
18 UI_BASE_EXPORT
bool formatter_force_fallback
= false;
22 const char* const fallback_one
;
23 const char* const fallback_other
;
26 static const Pluralities IDS_ELAPSED_SHORT_SEC
= {
27 IDS_TIME_ELAPSED_SECS
,
31 static const Pluralities IDS_ELAPSED_SHORT_MIN
= {
32 IDS_TIME_ELAPSED_MINS
,
36 static const Pluralities IDS_ELAPSED_HOUR
= {
37 IDS_TIME_ELAPSED_HOURS
,
41 static const Pluralities IDS_ELAPSED_DAY
= {
42 IDS_TIME_ELAPSED_DAYS
,
47 static const Pluralities IDS_REMAINING_SHORT_SEC
= {
48 IDS_TIME_REMAINING_SECS
,
52 static const Pluralities IDS_REMAINING_SHORT_MIN
= {
53 IDS_TIME_REMAINING_MINS
,
58 static const Pluralities IDS_REMAINING_LONG_SEC
= {
59 IDS_TIME_REMAINING_LONG_SECS
,
61 " other{# seconds left}"
63 static const Pluralities IDS_REMAINING_LONG_MIN
= {
64 IDS_TIME_REMAINING_LONG_MINS
,
66 " other{# minutes left}"
68 static const Pluralities IDS_REMAINING_HOUR
= {
69 IDS_TIME_REMAINING_HOURS
,
71 " other{# hours left}"
73 static const Pluralities IDS_REMAINING_DAY
= {
74 IDS_TIME_REMAINING_DAYS
,
79 static const Pluralities IDS_DURATION_SHORT_SEC
= {
84 static const Pluralities IDS_DURATION_SHORT_MIN
= {
90 static const Pluralities IDS_LONG_SEC
= {
95 static const Pluralities IDS_LONG_MIN
= {
100 static const Pluralities IDS_DURATION_HOUR
= {
105 static const Pluralities IDS_DURATION_DAY
= {
111 static const Pluralities IDS_LONG_MIN_1ST
= {
112 IDS_TIME_LONG_MINS_1ST
,
113 "one{# minute and }",
114 " other{# minutes and }"
116 static const Pluralities IDS_LONG_SEC_2ND
= {
117 IDS_TIME_LONG_SECS_2ND
,
121 static const Pluralities IDS_DURATION_HOUR_1ST
= {
124 " other{# hours and }"
126 static const Pluralities IDS_LONG_MIN_2ND
= {
127 IDS_TIME_LONG_MINS_2ND
,
131 static const Pluralities IDS_DURATION_DAY_1ST
= {
134 " other{# days and }"
136 static const Pluralities IDS_DURATION_HOUR_2ND
= {
144 scoped_ptr
<icu::PluralRules
> BuildPluralRules() {
145 UErrorCode err
= U_ZERO_ERROR
;
146 scoped_ptr
<icu::PluralRules
> rules(
147 icu::PluralRules::forLocale(icu::Locale::getDefault(), err
));
148 if (U_FAILURE(err
)) {
150 icu::UnicodeString
fallback_rules("one: n is 1", -1, US_INV
);
151 rules
.reset(icu::PluralRules::createRules(fallback_rules
, err
));
152 DCHECK(U_SUCCESS(err
));
157 void FormatNumberInPlural(const icu::MessageFormat
& format
, int number
,
158 icu::UnicodeString
* result
, UErrorCode
* err
) {
159 if (U_FAILURE(*err
)) return;
160 icu::Formattable
formattable(number
);
161 icu::FieldPosition
ignore(icu::FieldPosition::DONT_CARE
);
162 format
.format(&formattable
, 1, *result
, ignore
, *err
);
163 DCHECK(U_SUCCESS(*err
));
169 Formatter::Formatter(const Pluralities
& sec_pluralities
,
170 const Pluralities
& min_pluralities
,
171 const Pluralities
& hour_pluralities
,
172 const Pluralities
& day_pluralities
) {
173 simple_format_
[UNIT_SEC
] = InitFormat(sec_pluralities
);
174 simple_format_
[UNIT_MIN
] = InitFormat(min_pluralities
);
175 simple_format_
[UNIT_HOUR
] = InitFormat(hour_pluralities
);
176 simple_format_
[UNIT_DAY
] = InitFormat(day_pluralities
);
179 Formatter::Formatter(const Pluralities
& sec_pluralities
,
180 const Pluralities
& min_pluralities
,
181 const Pluralities
& hour_pluralities
,
182 const Pluralities
& day_pluralities
,
183 const Pluralities
& min_sec_pluralities1
,
184 const Pluralities
& min_sec_pluralities2
,
185 const Pluralities
& hour_min_pluralities1
,
186 const Pluralities
& hour_min_pluralities2
,
187 const Pluralities
& day_hour_pluralities1
,
188 const Pluralities
& day_hour_pluralities2
) {
189 simple_format_
[UNIT_SEC
] = InitFormat(sec_pluralities
);
190 simple_format_
[UNIT_MIN
] = InitFormat(min_pluralities
);
191 simple_format_
[UNIT_HOUR
] = InitFormat(hour_pluralities
);
192 simple_format_
[UNIT_DAY
] = InitFormat(day_pluralities
);
193 detailed_format_
[TWO_UNITS_MIN_SEC
][0] = InitFormat(min_sec_pluralities1
);
194 detailed_format_
[TWO_UNITS_MIN_SEC
][1] = InitFormat(min_sec_pluralities2
);
195 detailed_format_
[TWO_UNITS_HOUR_MIN
][0] = InitFormat(hour_min_pluralities1
);
196 detailed_format_
[TWO_UNITS_HOUR_MIN
][1] = InitFormat(hour_min_pluralities2
);
197 detailed_format_
[TWO_UNITS_DAY_HOUR
][0] = InitFormat(day_hour_pluralities1
);
198 detailed_format_
[TWO_UNITS_DAY_HOUR
][1] = InitFormat(day_hour_pluralities2
);
201 void Formatter::Format(Unit unit
,
203 icu::UnicodeString
* formatted_string
) const {
204 DCHECK(simple_format_
[unit
]);
205 DCHECK(formatted_string
->isEmpty() == TRUE
);
206 UErrorCode error
= U_ZERO_ERROR
;
207 FormatNumberInPlural(*simple_format_
[unit
],
208 value
, formatted_string
, &error
);
209 DCHECK(U_SUCCESS(error
)) << "Error in icu::PluralFormat::format().";
213 void Formatter::Format(TwoUnits units
,
216 icu::UnicodeString
* formatted_string
) const {
217 DCHECK(detailed_format_
[units
][0])
218 << "Detailed() not implemented for your (format, length) combination!";
219 DCHECK(detailed_format_
[units
][1])
220 << "Detailed() not implemented for your (format, length) combination!";
221 DCHECK(formatted_string
->isEmpty() == TRUE
);
222 UErrorCode error
= U_ZERO_ERROR
;
223 FormatNumberInPlural(*detailed_format_
[units
][0], value_1
,
224 formatted_string
, &error
);
225 DCHECK(U_SUCCESS(error
));
226 FormatNumberInPlural(*detailed_format_
[units
][1], value_2
,
227 formatted_string
, &error
);
228 DCHECK(U_SUCCESS(error
));
232 scoped_ptr
<icu::MessageFormat
> Formatter::CreateFallbackFormat(
233 const icu::PluralRules
& rules
,
234 const Pluralities
& pluralities
) const {
235 icu::UnicodeString
pattern("{NUMBER, plural, ");
236 if (rules
.isKeyword(UNICODE_STRING_SIMPLE("one")))
237 pattern
+= icu::UnicodeString(pluralities
.fallback_one
);
238 pattern
+= icu::UnicodeString(pluralities
.fallback_other
);
239 pattern
.append(UChar(0x7du
)); // "}" = U+007D
241 UErrorCode error
= U_ZERO_ERROR
;
242 scoped_ptr
<icu::MessageFormat
> format(
243 new icu::MessageFormat(pattern
, error
));
244 DCHECK(U_SUCCESS(error
));
245 return format
.Pass();
248 scoped_ptr
<icu::MessageFormat
> Formatter::InitFormat(
249 const Pluralities
& pluralities
) {
250 if (!formatter_force_fallback
) {
251 base::string16 pattern
= l10n_util::GetStringUTF16(pluralities
.id
);
252 UErrorCode error
= U_ZERO_ERROR
;
253 scoped_ptr
<icu::MessageFormat
> format(new icu::MessageFormat(
254 icu::UnicodeString(FALSE
, pattern
.data(), pattern
.length()), error
));
255 DCHECK(U_SUCCESS(error
));
257 return format
.Pass();
260 scoped_ptr
<icu::PluralRules
> rules(BuildPluralRules());
261 return CreateFallbackFormat(*rules
, pluralities
);
264 const Formatter
* FormatterContainer::Get(TimeFormat::Format format
,
265 TimeFormat::Length length
) const {
266 DCHECK(formatter_
[format
][length
])
267 << "Combination of FORMAT_ELAPSED and LENGTH_LONG is not implemented!";
268 return formatter_
[format
][length
].get();
271 FormatterContainer::FormatterContainer() {
275 FormatterContainer::~FormatterContainer() {
278 void FormatterContainer::Initialize() {
279 formatter_
[TimeFormat::FORMAT_ELAPSED
][TimeFormat::LENGTH_SHORT
].reset(
280 new Formatter(IDS_ELAPSED_SHORT_SEC
,
281 IDS_ELAPSED_SHORT_MIN
,
284 formatter_
[TimeFormat::FORMAT_ELAPSED
][TimeFormat::LENGTH_LONG
].reset();
285 formatter_
[TimeFormat::FORMAT_REMAINING
][TimeFormat::LENGTH_SHORT
].reset(
286 new Formatter(IDS_REMAINING_SHORT_SEC
,
287 IDS_REMAINING_SHORT_MIN
,
290 formatter_
[TimeFormat::FORMAT_REMAINING
][TimeFormat::LENGTH_LONG
].reset(
291 new Formatter(IDS_REMAINING_LONG_SEC
,
292 IDS_REMAINING_LONG_MIN
,
295 formatter_
[TimeFormat::FORMAT_DURATION
][TimeFormat::LENGTH_SHORT
].reset(
296 new Formatter(IDS_DURATION_SHORT_SEC
,
297 IDS_DURATION_SHORT_MIN
,
300 formatter_
[TimeFormat::FORMAT_DURATION
][TimeFormat::LENGTH_LONG
].reset(
301 new Formatter(IDS_LONG_SEC
,
307 IDS_DURATION_HOUR_1ST
,
309 IDS_DURATION_DAY_1ST
,
310 IDS_DURATION_HOUR_2ND
));
313 void FormatterContainer::Shutdown() {
314 for (int format
= 0; format
< TimeFormat::FORMAT_COUNT
; ++format
) {
315 for (int length
= 0; length
< TimeFormat::LENGTH_COUNT
; ++length
) {
316 formatter_
[format
][length
].reset();