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..bb67ecc362292e5020ffbf54ac41c5b64102dbc4 |
--- /dev/null |
+++ b/chrome/common/plural_formatter.cc |
@@ -0,0 +1,179 @@ |
+// 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 "base/scoped_ptr.h" |
+#include "base/stl_util-inl.h" |
+#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. |
+PluralFormatter::StringSourceFunction g_string_source = |
+ 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. |
+const char* g_override_locale = NULL; |
+ |
+} // namespace |
+ |
+// 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 PluralFormatter::PluralFormatterImpl { |
+ public: |
+ PluralFormatterImpl(); |
+ bool Init(const int message_ids[]); |
+ string16 GetPluralString(int number) const; |
+ private: |
+ scoped_ptr<icu::PluralFormat> formatter_; |
+}; |
+ |
+PluralFormatter::PluralFormatterImpl::PluralFormatterImpl() |
+ : formatter_(NULL) {} |
+ |
+bool PluralFormatter::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 error = 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, error)); |
+ } else { |
+ rules.reset(icu::PluralRules::forLocale(icu::Locale::getDefault(), error)); |
+ } |
+ |
+ // If we fail to create the rules for the given locale, we use |
+ // fallback rules. |
+ if (U_FAILURE(error)) { |
+ error = U_ZERO_ERROR; |
+ icu::UnicodeString fallback_rules("one: n is 1", -1, US_INV); |
+ rules.reset(icu::PluralRules::createRules(fallback_rules, error)); |
+ if (U_FAILURE(error)) { |
+ NOTREACHED() << "Failed to create fallback rule."; |
+ return false; |
+ } |
+ } |
+ |
+ 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, error)); |
+ |
+ // If format creation fails, we fail. |
+ if (U_FAILURE(error)) { |
+ formatter_.reset(NULL); |
+ return false; |
+ } |
+ |
+ formatter_.swap(format); |
+ return true; |
+} |
+ |
+string16 PluralFormatter::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); |
+ |
+ string16 result; |
+ if (U_FAILURE(error)) { |
+ NOTREACHED() << "Failed to format plural string."; |
+ result.clear(); |
+ return result; |
+ } |
+ |
+ int capacity = plural_string.length() + 1; |
+ plural_string.extract(static_cast<UChar*>(WriteInto(&result, capacity)), |
+ capacity, error); |
+ |
+ // On error, result will be the empty string. |
+ if (U_FAILURE(error)) { |
+ NOTREACHED() << "Failed to extract plural string."; |
+ result.clear(); |
+ } |
+ |
+ return result; |
+} |
+ |
+///////////////////////////////////////////////////// |
+// Implementation of the main class PluralFormatter. |
+ |
+PluralFormatter::PluralFormatter() : impl_(NULL) {} |
+ |
+PluralFormatter::~PluralFormatter() { |
+ delete impl_; |
+} |
+ |
+bool PluralFormatter::Init(const int message_ids[]) { |
+ impl_ = new PluralFormatter::PluralFormatterImpl; |
+ return impl_->Init(message_ids); |
+} |
+ |
+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); |
+} |
+ |
+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; |
+} |