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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ui/base/l10n/formatter.cc
diff --git a/ui/base/l10n/formatter.cc b/ui/base/l10n/formatter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a3064b65b4540add816c6f2a4dba50343ec2ca19
--- /dev/null
+++ b/ui/base/l10n/formatter.cc
@@ -0,0 +1,330 @@
+// Copyright 2014 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 "ui/base/l10n/formatter.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "grit/ui_strings.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "ui/base/l10n/l10n_util_plurals.h"
+
+namespace ui {
+
+// static
+bool Formatter::force_fallback_ = false;
+
+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.
+struct Pluralities {
+ int Ids[kNPluralities];
+ const char* fallback_one;
+ const char* fallback_other;
+};
+
+static const Pluralities IDS_ELAPSED_SHORT_SEC = {
+ { IDS_TIME_ELAPSED_SECS_DEFAULT, IDS_TIME_ELAPSED_SEC_SINGULAR,
+ IDS_TIME_ELAPSED_SECS_ZERO, IDS_TIME_ELAPSED_SECS_TWO,
+ IDS_TIME_ELAPSED_SECS_FEW, IDS_TIME_ELAPSED_SECS_MANY },
+ "one{# sec ago}",
+ " other{# secs ago}"
+};
+static const Pluralities IDS_ELAPSED_SHORT_MIN = {
+ { IDS_TIME_ELAPSED_MINS_DEFAULT, IDS_TIME_ELAPSED_MIN_SINGULAR,
+ IDS_TIME_ELAPSED_MINS_ZERO, IDS_TIME_ELAPSED_MINS_TWO,
+ IDS_TIME_ELAPSED_MINS_FEW, IDS_TIME_ELAPSED_MINS_MANY },
+ "one{# min ago}",
+ " other{# mins ago}"
+};
+static const Pluralities IDS_ELAPSED_HOUR = {
+ { IDS_TIME_ELAPSED_HOURS_DEFAULT, IDS_TIME_ELAPSED_HOUR_SINGULAR,
+ IDS_TIME_ELAPSED_HOURS_ZERO, IDS_TIME_ELAPSED_HOURS_TWO,
+ IDS_TIME_ELAPSED_HOURS_FEW, IDS_TIME_ELAPSED_HOURS_MANY },
+ "one{# hour ago}",
+ " other{# hours ago}"
+};
+static const Pluralities IDS_ELAPSED_DAY = {
+ { IDS_TIME_ELAPSED_DAYS_DEFAULT, IDS_TIME_ELAPSED_DAY_SINGULAR,
+ IDS_TIME_ELAPSED_DAYS_ZERO, IDS_TIME_ELAPSED_DAYS_TWO,
+ IDS_TIME_ELAPSED_DAYS_FEW, IDS_TIME_ELAPSED_DAYS_MANY },
+ "one{# day ago}",
+ " other{# days ago}"
+};
+
+static const Pluralities IDS_REMAINING_SHORT_SEC = {
+ { IDS_TIME_REMAINING_SECS_DEFAULT, IDS_TIME_REMAINING_SEC_SINGULAR,
+ IDS_TIME_REMAINING_SECS_ZERO, IDS_TIME_REMAINING_SECS_TWO,
+ IDS_TIME_REMAINING_SECS_FEW, IDS_TIME_REMAINING_SECS_MANY },
+ "one{# sec left}",
+ " other{# secs left}"
+};
+static const Pluralities IDS_REMAINING_SHORT_MIN = {
+ { IDS_TIME_REMAINING_MINS_DEFAULT, IDS_TIME_REMAINING_MIN_SINGULAR,
+ IDS_TIME_REMAINING_MINS_ZERO, IDS_TIME_REMAINING_MINS_TWO,
+ IDS_TIME_REMAINING_MINS_FEW, IDS_TIME_REMAINING_MINS_MANY },
+ "one{# min left}",
+ " other{# mins left}"
+};
+
+static const Pluralities IDS_REMAINING_LONG_SEC = {
+ { IDS_TIME_REMAINING_LONG_SECS_DEFAULT, IDS_TIME_REMAINING_LONG_SEC_SINGULAR,
+ IDS_TIME_REMAINING_LONG_SECS_ZERO, IDS_TIME_REMAINING_LONG_SECS_TWO,
+ IDS_TIME_REMAINING_LONG_SECS_FEW, IDS_TIME_REMAINING_LONG_SECS_MANY },
+ "one{# second left}",
+ " other{# seconds left}"
+};
+static const Pluralities IDS_REMAINING_LONG_MIN = {
+ { IDS_TIME_REMAINING_LONG_MINS_DEFAULT, IDS_TIME_REMAINING_LONG_MIN_SINGULAR,
+ IDS_TIME_REMAINING_LONG_MINS_ZERO, IDS_TIME_REMAINING_LONG_MINS_TWO,
+ IDS_TIME_REMAINING_LONG_MINS_FEW, IDS_TIME_REMAINING_LONG_MINS_MANY },
+ "one{# minute left}",
+ " other{# minutes left}"
+};
+static const Pluralities IDS_REMAINING_HOUR = {
+ { IDS_TIME_REMAINING_HOURS_DEFAULT, IDS_TIME_REMAINING_HOUR_SINGULAR,
+ IDS_TIME_REMAINING_HOURS_ZERO, IDS_TIME_REMAINING_HOURS_TWO,
+ IDS_TIME_REMAINING_HOURS_FEW, IDS_TIME_REMAINING_HOURS_MANY },
+ "one{# hour left}",
+ " other{# hours left}"
+};
+static const Pluralities IDS_REMAINING_DAY = {
+ { IDS_TIME_REMAINING_DAYS_DEFAULT, IDS_TIME_REMAINING_DAY_SINGULAR,
+ IDS_TIME_REMAINING_DAYS_ZERO, IDS_TIME_REMAINING_DAYS_TWO,
+ IDS_TIME_REMAINING_DAYS_FEW, IDS_TIME_REMAINING_DAYS_MANY },
+ "one{# day left}",
+ " other{# days left}"
+};
+
+static const Pluralities IDS_DURATION_SHORT_SEC = {
+ { IDS_TIME_SECS_DEFAULT, IDS_TIME_SEC_SINGULAR, IDS_TIME_SECS_ZERO,
+ IDS_TIME_SECS_TWO, IDS_TIME_SECS_FEW, IDS_TIME_SECS_MANY },
+ "one{# sec}",
+ " other{# secs}"
+};
+static const Pluralities IDS_DURATION_SHORT_MIN = {
+ { IDS_TIME_MINS_DEFAULT, IDS_TIME_MIN_SINGULAR, IDS_TIME_MINS_ZERO,
+ IDS_TIME_MINS_TWO, IDS_TIME_MINS_FEW, IDS_TIME_MINS_MANY },
+ "one{# min}",
+ " other{# mins}"
+};
+
+static const Pluralities IDS_LONG_SEC = {
+ { IDS_TIME_LONG_SECS_DEFAULT, IDS_TIME_LONG_SEC_SINGULAR,
+ IDS_TIME_LONG_SECS_ZERO, IDS_TIME_LONG_SECS_TWO,
+ IDS_TIME_LONG_SECS_FEW, IDS_TIME_LONG_SECS_MANY },
+ "one{# second}",
+ " other{# seconds}"
+};
+static const Pluralities IDS_LONG_MIN = {
+ { IDS_TIME_LONG_MINS_DEFAULT, IDS_TIME_LONG_MIN_SINGULAR,
+ IDS_TIME_LONG_MINS_ZERO, IDS_TIME_LONG_MINS_TWO,
+ IDS_TIME_LONG_MINS_FEW, IDS_TIME_LONG_MINS_MANY },
+ "one{# minute}",
+ " other{# minutes}"
+};
+static const Pluralities IDS_DURATION_HOUR = {
+ { IDS_TIME_HOURS_DEFAULT, IDS_TIME_HOUR_SINGULAR, IDS_TIME_HOURS_ZERO,
+ IDS_TIME_HOURS_TWO, IDS_TIME_HOURS_FEW, IDS_TIME_HOURS_MANY },
+ "one{# hour}",
+ " other{# hours}"
+};
+static const Pluralities IDS_DURATION_DAY = {
+ { IDS_TIME_DAYS_DEFAULT, IDS_TIME_DAY_SINGULAR, IDS_TIME_DAYS_ZERO,
+ IDS_TIME_DAYS_TWO, IDS_TIME_DAYS_FEW, IDS_TIME_DAYS_MANY },
+ "one{# day}",
+ " other{# days}"
+};
+
+static const Pluralities IDS_LONG_MIN_1ST = {
+ { IDS_TIME_LONG_MINS_1ST_DEFAULT, IDS_TIME_LONG_MIN_1ST_SINGULAR,
+ IDS_TIME_LONG_MINS_1ST_ZERO, IDS_TIME_LONG_MINS_1ST_TWO,
+ IDS_TIME_LONG_MINS_1ST_FEW, IDS_TIME_LONG_MINS_1ST_MANY },
+ "one{# minute }",
+ " other{# minutes }"
+};
+static const Pluralities IDS_LONG_SEC_2ND = {
+ { IDS_TIME_LONG_SECS_2ND_DEFAULT, IDS_TIME_LONG_SEC_2ND_SINGULAR,
+ IDS_TIME_LONG_SECS_2ND_ZERO, IDS_TIME_LONG_SECS_2ND_TWO,
+ IDS_TIME_LONG_SECS_2ND_FEW, IDS_TIME_LONG_SECS_2ND_MANY },
+ "one{# second}",
+ " other{# seconds}"
+};
+static const Pluralities IDS_DURATION_HOUR_1ST = {
+ { IDS_TIME_HOURS_1ST_DEFAULT, IDS_TIME_HOUR_1ST_SINGULAR,
+ IDS_TIME_HOURS_1ST_ZERO, IDS_TIME_HOURS_1ST_TWO,
+ IDS_TIME_HOURS_1ST_FEW, IDS_TIME_HOURS_1ST_MANY },
+ "one{# hour }",
+ " other{# hours }"
+};
+static const Pluralities IDS_LONG_MIN_2ND = {
+ { IDS_TIME_LONG_MINS_2ND_DEFAULT, IDS_TIME_LONG_MIN_2ND_SINGULAR,
+ IDS_TIME_LONG_MINS_2ND_ZERO, IDS_TIME_LONG_MINS_2ND_TWO,
+ IDS_TIME_LONG_MINS_2ND_FEW, IDS_TIME_LONG_MINS_2ND_MANY },
+ "one{# minute}",
+ " other{# minutes}"
+};
+static const Pluralities IDS_DURATION_DAY_1ST = {
+ { IDS_TIME_DAYS_1ST_DEFAULT, IDS_TIME_DAY_1ST_SINGULAR,
+ IDS_TIME_DAYS_1ST_ZERO, IDS_TIME_DAYS_1ST_TWO,
+ IDS_TIME_DAYS_1ST_FEW, IDS_TIME_DAYS_1ST_MANY },
+ "one{# day }",
+ " other{# days }"
+};
+static const Pluralities IDS_DURATION_HOUR_2ND = {
+ { IDS_TIME_HOURS_2ND_DEFAULT, IDS_TIME_HOUR_2ND_SINGULAR,
+ IDS_TIME_HOURS_2ND_ZERO, IDS_TIME_HOURS_2ND_TWO,
+ IDS_TIME_HOURS_2ND_FEW, IDS_TIME_HOURS_2ND_MANY },
+ "one{# hour}",
+ " other{# hours}"
+};
+
+Formatter::Formatter(const Pluralities& sec_pluralities,
+ const Pluralities& min_pluralities,
+ const Pluralities& hour_pluralities,
+ const Pluralities& day_pluralities) {
+ simple_format_[UNIT_SEC] = InitFormat(sec_pluralities);
+ simple_format_[UNIT_MIN] = InitFormat(min_pluralities);
+ simple_format_[UNIT_HOUR] = InitFormat(hour_pluralities);
+ simple_format_[UNIT_DAY] = InitFormat(day_pluralities);
+}
+
+Formatter::Formatter(const Pluralities& sec_pluralities,
+ const Pluralities& min_pluralities,
+ const Pluralities& hour_pluralities,
+ const Pluralities& day_pluralities,
+ const Pluralities& min_sec_pluralities1,
+ const Pluralities& min_sec_pluralities2,
+ const Pluralities& hour_min_pluralities1,
+ const Pluralities& hour_min_pluralities2,
+ const Pluralities& day_hour_pluralities1,
+ const Pluralities& day_hour_pluralities2) {
+ simple_format_[UNIT_SEC] = InitFormat(sec_pluralities);
+ simple_format_[UNIT_MIN] = InitFormat(min_pluralities);
+ simple_format_[UNIT_HOUR] = InitFormat(hour_pluralities);
+ simple_format_[UNIT_DAY] = InitFormat(day_pluralities);
+ detailed_format_[TWOUNITS_MIN_SEC][0] = InitFormat(min_sec_pluralities1);
+ detailed_format_[TWOUNITS_MIN_SEC][1] = InitFormat(min_sec_pluralities2);
+ detailed_format_[TWOUNITS_HOUR_MIN][0] = InitFormat(hour_min_pluralities1);
+ detailed_format_[TWOUNITS_HOUR_MIN][1] = InitFormat(hour_min_pluralities2);
+ detailed_format_[TWOUNITS_DAY_HOUR][0] = InitFormat(day_hour_pluralities1);
+ detailed_format_[TWOUNITS_DAY_HOUR][1] = InitFormat(day_hour_pluralities2);
+}
+
+void Formatter::Format(Unit unit,
+ int value,
+ icu::UnicodeString& formatted_string) const {
+ DCHECK(simple_format_[unit]);
+ UErrorCode error = U_ZERO_ERROR;
+ formatted_string = simple_format_[unit]->format(value, error);
+ DCHECK(U_SUCCESS(error)) << "Error in icu::PluralFormat::format().";
+ return;
+}
+
+void Formatter::Format(TwoUnits units,
+ int value1,
+ int value2,
+ icu::UnicodeString& formatted_string) const {
+ DCHECK(detailed_format_[units][0])
+ << "Detailed() not implemented for your (format, length) combination!";
+ DCHECK(detailed_format_[units][1])
+ << "Detailed() not implemented for your (format, length) combination!";
+ UErrorCode error = U_ZERO_ERROR;
+ formatted_string = detailed_format_[units][0]->format(value1, error);
+ DCHECK(U_SUCCESS(error));
+ formatted_string += detailed_format_[units][1]->format(value2, error);
+ DCHECK(U_SUCCESS(error));
+ return;
+}
+
+scoped_ptr<icu::PluralFormat> Formatter::CreateFallbackFormat(
+ const icu::PluralRules& rules,
+ const Pluralities& pluralities) const {
+ icu::UnicodeString pattern;
+ if (rules.isKeyword(UNICODE_STRING_SIMPLE("one"))) {
+ pattern += icu::UnicodeString(pluralities.fallback_one);
+ }
+ pattern += icu::UnicodeString(pluralities.fallback_other);
+
+ UErrorCode error = U_ZERO_ERROR;
+ scoped_ptr<icu::PluralFormat> format(
+ new icu::PluralFormat(rules, pattern, error));
+ DCHECK(U_SUCCESS(error));
+ return format.Pass();
+}
+
+scoped_ptr<icu::PluralFormat> Formatter::InitFormat(
+ const Pluralities& pluralities) {
+ if (!Formatter::force_fallback_) {
+ icu::UnicodeString pattern;
+ std::vector<int> ids;
+ for (size_t j = 0; j < kNPluralities; ++j) {
+ ids.push_back(pluralities.Ids[j]);
+ }
+ scoped_ptr<icu::PluralFormat> format = l10n_util::BuildPluralFormat(ids);
+ if (format.get())
+ return format.Pass();
+ }
+
+ scoped_ptr<icu::PluralRules> rules(l10n_util::BuildPluralRules());
+ return CreateFallbackFormat(*rules, pluralities);
+}
+
+const Formatter* FormatterContainer::Get(TimeFormat::Format format,
+ TimeFormat::Length length) const {
+ DCHECK(formatter_[format][length])
+ << "Combination of FORMAT_ELAPSED and LENGTH_LONG is not implemented!";
+ return formatter_[format][length].get();
+}
+
+FormatterContainer::FormatterContainer() {
+ Initialize();
+}
+
+FormatterContainer::~FormatterContainer() {
+}
+
+void FormatterContainer::Initialize() {
+ formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_SHORT].reset(
+ new Formatter(IDS_ELAPSED_SHORT_SEC,
+ IDS_ELAPSED_SHORT_MIN,
+ IDS_ELAPSED_HOUR,
+ IDS_ELAPSED_DAY));
+ formatter_[TimeFormat::FORMAT_ELAPSED][TimeFormat::LENGTH_LONG].reset();
+ formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_SHORT].reset(
+ new Formatter(IDS_REMAINING_SHORT_SEC,
+ IDS_REMAINING_SHORT_MIN,
+ IDS_REMAINING_HOUR,
+ IDS_REMAINING_DAY));
+ formatter_[TimeFormat::FORMAT_REMAINING][TimeFormat::LENGTH_LONG].reset(
+ new Formatter(IDS_REMAINING_LONG_SEC,
+ IDS_REMAINING_LONG_MIN,
+ IDS_REMAINING_HOUR,
+ IDS_REMAINING_DAY));
+ formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_SHORT].reset(
+ new Formatter(IDS_DURATION_SHORT_SEC,
+ IDS_DURATION_SHORT_MIN,
+ IDS_DURATION_HOUR,
+ IDS_DURATION_DAY));
+ formatter_[TimeFormat::FORMAT_DURATION][TimeFormat::LENGTH_LONG].reset(
+ new Formatter(IDS_LONG_SEC,
+ IDS_LONG_MIN,
+ IDS_DURATION_HOUR,
+ IDS_DURATION_DAY,
+ IDS_LONG_MIN_1ST,
+ IDS_LONG_SEC_2ND,
+ IDS_DURATION_HOUR_1ST,
+ IDS_LONG_MIN_2ND,
+ IDS_DURATION_DAY_1ST,
+ IDS_DURATION_HOUR_2ND));
+}
+
+void FormatterContainer::Shutdown() {
+ for (int format = 0; format < TimeFormat::FORMAT_COUNT; ++format) {
+ for (int length = 0; length < TimeFormat::LENGTH_COUNT; ++length) {
+ formatter_[format][length].reset();
+ }
+ }
+}
+
+} // namespace ui

Powered by Google App Engine
This is Rietveld 408576698