summaryrefslogtreecommitdiffstats
path: root/intl/components/src/DisplayNames.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/components/src/DisplayNames.cpp')
-rw-r--r--intl/components/src/DisplayNames.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/intl/components/src/DisplayNames.cpp b/intl/components/src/DisplayNames.cpp
new file mode 100644
index 0000000000..252969ccbb
--- /dev/null
+++ b/intl/components/src/DisplayNames.cpp
@@ -0,0 +1,234 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "mozilla/intl/DisplayNames.h"
+#include "ScopedICUObject.h"
+
+namespace mozilla::intl {
+
+DisplayNames::~DisplayNames() {
+ // The mDisplayNames will not exist when the DisplayNames is being
+ // moved.
+ if (auto* uldn = mULocaleDisplayNames.GetMut()) {
+ uldn_close(uldn);
+ }
+}
+
+DisplayNamesError DisplayNames::ToError(ICUError aError) const {
+ switch (aError) {
+ case ICUError::InternalError:
+ case ICUError::OverflowError:
+ return DisplayNamesError::InternalError;
+ case ICUError::OutOfMemory:
+ return DisplayNamesError::OutOfMemory;
+ }
+ MOZ_ASSERT_UNREACHABLE();
+ return DisplayNamesError::InternalError;
+}
+
+DisplayNamesError DisplayNames::ToError(
+ Locale::CanonicalizationError aError) const {
+ switch (aError) {
+ case Locale::CanonicalizationError::DuplicateVariant:
+ return DisplayNamesError::DuplicateVariantSubtag;
+ case Locale::CanonicalizationError::InternalError:
+ return DisplayNamesError::InternalError;
+ case Locale::CanonicalizationError::OutOfMemory:
+ return DisplayNamesError::OutOfMemory;
+ }
+ MOZ_ASSERT_UNREACHABLE();
+ return DisplayNamesError::InternalError;
+}
+
+/* static */
+Result<UniquePtr<DisplayNames>, ICUError> DisplayNames::TryCreate(
+ const char* aLocale, Options aOptions) {
+ UErrorCode status = U_ZERO_ERROR;
+ UDisplayContext contexts[] = {
+ // Use either standard or dialect names.
+ // For example either "English (GB)" or "British English".
+ aOptions.languageDisplay == DisplayNames::LanguageDisplay::Standard
+ ? UDISPCTX_STANDARD_NAMES
+ : UDISPCTX_DIALECT_NAMES,
+
+ // Assume the display names are used in a stand-alone context.
+ UDISPCTX_CAPITALIZATION_FOR_STANDALONE,
+
+ // Select either the long or short form. There's no separate narrow form
+ // available in ICU, therefore we equate "narrow"/"short" styles here.
+ aOptions.style == DisplayNames::Style::Long ? UDISPCTX_LENGTH_FULL
+ : UDISPCTX_LENGTH_SHORT,
+
+ // Don't apply substitutes, because we need to apply our own fallbacks.
+ UDISPCTX_NO_SUBSTITUTE,
+ };
+
+ const char* locale = IcuLocale(aLocale);
+
+ ULocaleDisplayNames* uLocaleDisplayNames =
+ uldn_openForContext(locale, contexts, std::size(contexts), &status);
+
+ if (U_FAILURE(status)) {
+ return Err(ToICUError(status));
+ }
+ return MakeUnique<DisplayNames>(uLocaleDisplayNames, MakeStringSpan(locale),
+ aOptions);
+};
+
+#ifdef DEBUG
+static bool IsStandaloneMonth(UDateFormatSymbolType symbolType) {
+ switch (symbolType) {
+ case UDAT_STANDALONE_MONTHS:
+ case UDAT_STANDALONE_SHORT_MONTHS:
+ case UDAT_STANDALONE_NARROW_MONTHS:
+ return true;
+
+ case UDAT_ERAS:
+ case UDAT_MONTHS:
+ case UDAT_SHORT_MONTHS:
+ case UDAT_WEEKDAYS:
+ case UDAT_SHORT_WEEKDAYS:
+ case UDAT_AM_PMS:
+ case UDAT_LOCALIZED_CHARS:
+ case UDAT_ERA_NAMES:
+ case UDAT_NARROW_MONTHS:
+ case UDAT_NARROW_WEEKDAYS:
+ case UDAT_STANDALONE_WEEKDAYS:
+ case UDAT_STANDALONE_SHORT_WEEKDAYS:
+ case UDAT_STANDALONE_NARROW_WEEKDAYS:
+ case UDAT_QUARTERS:
+ case UDAT_SHORT_QUARTERS:
+ case UDAT_STANDALONE_QUARTERS:
+ case UDAT_STANDALONE_SHORT_QUARTERS:
+ case UDAT_SHORTER_WEEKDAYS:
+ case UDAT_STANDALONE_SHORTER_WEEKDAYS:
+ case UDAT_CYCLIC_YEARS_WIDE:
+ case UDAT_CYCLIC_YEARS_ABBREVIATED:
+ case UDAT_CYCLIC_YEARS_NARROW:
+ case UDAT_ZODIAC_NAMES_WIDE:
+ case UDAT_ZODIAC_NAMES_ABBREVIATED:
+ case UDAT_ZODIAC_NAMES_NARROW:
+ case UDAT_NARROW_QUARTERS:
+ case UDAT_STANDALONE_NARROW_QUARTERS:
+ return false;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("unenumerated, undocumented symbol type");
+ return false;
+}
+#endif
+
+Result<Ok, DisplayNamesError> DisplayNames::ComputeDateTimeDisplayNames(
+ UDateFormatSymbolType symbolType, mozilla::Span<const int32_t> indices,
+ Span<const char> aCalendar) {
+ if (!mDateTimeDisplayNames.empty()) {
+ // No need to re-compute the display names.
+ return Ok();
+ }
+ mozilla::intl::Locale tag;
+ // Do not use mLocale.AsSpan() as it includes the null terminator inside the
+ // span.
+ if (LocaleParser::TryParse(Span(mLocale.Elements(), mLocale.Length() - 1),
+ tag)
+ .isErr()) {
+ return Err(DisplayNamesError::InvalidLanguageTag);
+ }
+
+ if (!aCalendar.empty()) {
+ // Add the calendar extension to the locale. This is only available via
+ // the MozExtension.
+ Vector<char, 32> extension;
+ Span<const char> prefix = MakeStringSpan("u-ca-");
+ if (!extension.append(prefix.data(), prefix.size()) ||
+ !extension.append(aCalendar.data(), aCalendar.size())) {
+ return Err(DisplayNamesError::OutOfMemory);
+ }
+ // This overwrites any other Unicode extensions, but should be okay to do
+ // here.
+ if (auto result = tag.SetUnicodeExtension(extension); result.isErr()) {
+ return Err(ToError(result.unwrapErr()));
+ }
+ }
+
+ constexpr char16_t* timeZone = nullptr;
+ constexpr int32_t timeZoneLength = 0;
+
+ constexpr char16_t* pattern = nullptr;
+ constexpr int32_t patternLength = 0;
+
+ Vector<char, DisplayNames::LocaleVecLength> localeWithCalendar;
+ VectorToBufferAdaptor buffer(localeWithCalendar);
+ if (auto result = tag.ToString(buffer); result.isErr()) {
+ return Err(ToError(result.unwrapErr()));
+ }
+ if (!localeWithCalendar.append('\0')) {
+ return Err(DisplayNamesError::OutOfMemory);
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ UDateFormat* fmt = udat_open(
+ UDAT_DEFAULT, UDAT_DEFAULT,
+ IcuLocale(
+ // IcuLocale takes a Span that does not include the null terminator.
+ Span(localeWithCalendar.begin(), localeWithCalendar.length() - 1)),
+ timeZone, timeZoneLength, pattern, patternLength, &status);
+ if (U_FAILURE(status)) {
+ return Err(DisplayNamesError::InternalError);
+ }
+ ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
+
+ Vector<char16_t, DisplayNames::LocaleVecLength> name;
+ for (int32_t index : indices) {
+ auto result = FillBufferWithICUCall(name, [&](UChar* target, int32_t length,
+ UErrorCode* status) {
+ return udat_getSymbols(fmt, symbolType, index, target, length, status);
+ });
+ if (result.isErr()) {
+ return Err(ToError(result.unwrapErr()));
+ }
+
+ // Everything except Undecimber should always have a non-empty name.
+ MOZ_ASSERT_IF(!IsStandaloneMonth(symbolType) || index != UCAL_UNDECIMBER,
+ !name.empty());
+
+ if (!mDateTimeDisplayNames.emplaceBack(Span(name.begin(), name.length()))) {
+ return Err(DisplayNamesError::OutOfMemory);
+ }
+ }
+ return Ok();
+}
+
+Span<const char> DisplayNames::ToCodeString(Month aMonth) {
+ switch (aMonth) {
+ case Month::January:
+ return MakeStringSpan("1");
+ case Month::February:
+ return MakeStringSpan("2");
+ case Month::March:
+ return MakeStringSpan("3");
+ case Month::April:
+ return MakeStringSpan("4");
+ case Month::May:
+ return MakeStringSpan("5");
+ case Month::June:
+ return MakeStringSpan("6");
+ case Month::July:
+ return MakeStringSpan("7");
+ case Month::August:
+ return MakeStringSpan("8");
+ case Month::September:
+ return MakeStringSpan("9");
+ case Month::October:
+ return MakeStringSpan("10");
+ case Month::November:
+ return MakeStringSpan("11");
+ case Month::December:
+ return MakeStringSpan("12");
+ case Month::Undecimber:
+ return MakeStringSpan("13");
+ }
+ MOZ_ASSERT_UNREACHABLE();
+ return MakeStringSpan("1");
+};
+
+} // namespace mozilla::intl