Chromium Code Reviews| Index: chrome/common/plural_formatter.cc |
| diff --git a/chrome/common/plural_formatter.cc b/chrome/common/plural_formatter.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a92388676bb0c71a39d5056556876f33ada84e82 |
| --- /dev/null |
| +++ b/chrome/common/plural_formatter.cc |
| @@ -0,0 +1,168 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/common/plural_formatter.h" |
| + |
| +#include <vector> |
|
tfarina
2011/03/26 02:05:15
is this necessary/used in this source file?
Greg Spencer (Chromium)
2011/03/28 23:13:37
Nope, not any more. Removed.
|
| + |
| +#include "base/scoped_ptr.h" |
| +#include "base/stl_util-inl.h" |
| +#include "base/string16.h" |
|
tfarina
2011/03/26 02:05:15
you can remove this include.
Greg Spencer (Chromium)
2011/03/28 23:13:37
Done.
|
| +#include "base/utf_string_conversions.h" |
| +#include "grit/generated_resources.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "unicode/locid.h" |
| +#include "unicode/plurfmt.h" |
| +#include "unicode/plurrule.h" |
| + |
| +namespace { |
| + |
| +// This is the functor for mapping message IDs to strings. We reset |
| +// it for tests so that we can avoid using grit for the tests. |
| +static PluralFormatter::StringSourceFunction g_string_source = |
|
tfarina
2011/03/26 02:05:15
the static keyword is not necessary. Please, could
Greg Spencer (Chromium)
2011/03/28 23:13:37
Done.
|
| + l10n_util::GetStringUTF8; |
| + |
| +// This is used to override which locale is used for the translation. |
| +// It is used by the tests to test different locales. If it is set to |
| +// NULL, then the default locale is used. |
| +static const char* g_override_locale = NULL; |
| + |
| +} // anonymous namespace |
|
tfarina
2011/03/26 02:05:15
Although I see in many places, and it's misleading
Greg Spencer (Chromium)
2011/03/28 23:13:37
I hate that convention: it looks like someone forg
|
| + |
| +// An implementation class is necessary here because icu does some |
| +// funky things with namespaces: it declares its stuff under a |
| +// macro-constructed namespace name and then aliases it to 'icu', |
| +// which means you have to include the icu header before any forward |
| +// declaration (which defeats the purpose of forward declarations) in |
| +// order to use the namespace "icu" or the macro. |
| + |
| +class PluralFormatterImpl { |
| + public: |
| + PluralFormatterImpl(); |
| + bool Init(const int message_ids[]); |
| + string16 GetPluralString(int number) const; |
| + private: |
| + scoped_ptr<icu::PluralFormat> formatter_; |
| +}; |
| + |
| +PluralFormatterImpl::PluralFormatterImpl() : formatter_(NULL) {} |
| + |
| +bool PluralFormatterImpl::Init(const int message_ids[]) { |
| + static const icu::UnicodeString kKeywords[] = { |
| + UNICODE_STRING_SIMPLE("zero"), UNICODE_STRING_SIMPLE("one"), |
| + UNICODE_STRING_SIMPLE("two"), UNICODE_STRING_SIMPLE("few"), |
| + UNICODE_STRING_SIMPLE("many"), UNICODE_STRING_SIMPLE("other"), |
| + }; |
| + |
| + UErrorCode err = U_ZERO_ERROR; |
| + scoped_ptr<icu::PluralRules> rules; |
| + if (g_override_locale) { |
| + icu::Locale overridden_locale(g_override_locale); |
| + rules.reset(icu::PluralRules::forLocale(overridden_locale, err)); |
| + } else { |
| + rules.reset(icu::PluralRules::forLocale(icu::Locale::getDefault(), err)); |
| + } |
| + |
| + // If we fail to create the rules for the given locale, we use |
| + // fallback rules. |
| + if (U_FAILURE(err)) { |
| + err = U_ZERO_ERROR; |
| + icu::UnicodeString fallback_rules("one: n is 1", -1, US_INV); |
| + rules.reset(icu::PluralRules::createRules(fallback_rules, err)); |
| + DCHECK(U_SUCCESS(err)); |
| + } |
| + |
| + icu::UnicodeString pattern; |
| + for (size_t j = 0; j < arraysize(kKeywords); ++j) { |
| + int msg_id = message_ids[j]; |
| + std::string sub_pattern = (*g_string_source)(msg_id); |
| + // NA means this keyword is not used in the current locale. Even |
| + // if a translator translated for this keyword, we do not use it |
| + // unless it's 'other' or it's defined in the rules for the |
| + // current locale. Special-casing of 'other' (j = 5) will be |
| + // removed once ICU's isKeyword is fixed to return true for |
| + // isKeyword('other'). |
| + if (sub_pattern.compare("NA") != 0 && |
| + (j == 5 || rules->isKeyword(kKeywords[j]))) { |
| + pattern += kKeywords[j]; |
| + pattern += UNICODE_STRING_SIMPLE("{"); |
| + pattern += icu::UnicodeString(sub_pattern.c_str(), "UTF-8"); |
| + pattern += UNICODE_STRING_SIMPLE("}"); |
| + } |
| + } |
| + |
| + scoped_ptr<icu::PluralFormat> format( |
| + new icu::PluralFormat(*rules, pattern, err)); |
| + |
| + // If format creation fails, we fail. |
| + if (U_FAILURE(err)) { |
| + formatter_.reset(NULL); |
| + return false; |
| + } |
| + |
| + formatter_.swap(format); |
| + return true; |
| +} |
| + |
| +string16 PluralFormatterImpl::GetPluralString(int number) const { |
| + DCHECK(formatter_.get() != NULL) |
| + << "GetPluralString called before initializing formatter."; |
| + icu::UnicodeString plural_string; |
| + UErrorCode error = U_ZERO_ERROR; |
| + plural_string = formatter_->format(number, error); |
| + DCHECK(U_SUCCESS(error)); |
|
Paweł Hajdan Jr.
2011/03/28 18:52:29
Later case handles U_FAILURE, and this only DCHECK
Greg Spencer (Chromium)
2011/03/28 23:13:37
OK, I added handling for this to make it consisten
|
| + string16 result; |
| + int capacity = plural_string.length() + 1; |
| + plural_string.extract(static_cast<UChar*>(WriteInto(&result, capacity)), |
| + capacity, error); |
| + DCHECK(U_SUCCESS(error)); |
|
Paweł Hajdan Jr.
2011/03/28 18:52:29
nit: I'd suggest to make this a NOTREACHED() in th
Greg Spencer (Chromium)
2011/03/28 23:13:37
NOTREACHED is a good suggestion. I switched to th
|
| + // On error, result will be the empty string. |
| + if (U_FAILURE(error)) |
| + result.clear(); |
| + |
| + return result; |
| +} |
| + |
| +///////////////////////////////////////////////////// |
| +// Implementation of the main class PluralFormatter. |
| + |
| +PluralFormatter::PluralFormatter() : impl_(NULL) {} |
| + |
| +PluralFormatter::~PluralFormatter() { |
| + delete impl_; |
| +} |
| + |
| +bool PluralFormatter::Init(int zero_message_id, |
| + int one_message_id, |
| + int two_message_id, |
| + int few_message_id, |
| + int many_message_id, |
| + int default_message_id) { |
| + int message_ids[6]; |
| + message_ids[0] = zero_message_id; |
| + message_ids[1] = one_message_id; |
| + message_ids[2] = two_message_id; |
| + message_ids[3] = few_message_id; |
| + message_ids[4] = many_message_id; |
| + message_ids[5] = default_message_id; |
| + return Init(message_ids); |
| +} |
| + |
| +bool PluralFormatter::Init(const int message_ids[]) { |
|
tfarina
2011/03/26 02:05:15
this should be implemented above. The order of the
Greg Spencer (Chromium)
2011/03/28 23:13:37
Sorry, missed one. Done.
|
| + impl_ = new PluralFormatterImpl; |
| + return impl_->Init(message_ids); |
| +} |
| + |
| +string16 PluralFormatter::GetPluralString(int number) const { |
| + DCHECK(impl_ != NULL) << "GetPluralString called before Init"; |
| + return impl_->GetPluralString(number); |
| +} |
| + |
| +void PluralFormatter::SetStringSource(StringSourceFunction string_source) { |
| + g_string_source = string_source; |
| +} |
| + |
| +void PluralFormatter::SetOverrideLocale(const char* overrride_locale) { |
| + g_override_locale = overrride_locale; |
| +} |