Skip to content

Instantly share code, notes, and snippets.

@gregtatum
Created August 13, 2021 19:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregtatum/d21c427268f10619b60d71c282b4fb9b to your computer and use it in GitHub Desktop.
Save gregtatum/d21c427268f10619b60d71c282b4fb9b to your computer and use it in GitHub Desktop.
changeset: 591812:1d345bc79fcb
parent: 591800:9c9e3a7a8337
user: Greg Tatum <gtatum@mozilla.com>
date: Tue Aug 10 11:59:03 2021 -0500
files: intl/components/gtest/TestBuffer.h intl/components/gtest/TestDisplayNames.cpp intl/components/gtest/moz.build intl/components/moz.build intl/components/src/DisplayNames.cpp intl/components/src/DisplayNames.h intl/components/src/ICU4CGlue.cpp intl/components/src/ICU4CGlue.h
description:
WIP Bug 1719735 - Unify DisplayNames smozilla::intl
diff --git a/intl/components/gtest/TestBuffer.h b/intl/components/gtest/TestBuffer.h
--- a/intl/components/gtest/TestBuffer.h
+++ b/intl/components/gtest/TestBuffer.h
@@ -42,16 +42,21 @@ class TestBuffer {
size_t length() const { return mBuffer.length(); }
/**
* Returns the buffer's overall capacity.
*/
size_t capacity() const { return mBuffer.capacity(); }
/**
+ * Clear the buffer.
+ */
+ void clear() { mBuffer.clear(); }
+
+ /**
* Resizes the buffer to the given amount of written elements.
* This is necessary because the buffer gets written to across
* FFI boundaries, so this needs to happen in a separate step.
*/
void written(size_t aAmount) {
MOZ_ASSERT(aAmount <= mBuffer.capacity());
mozilla::DebugOnly<bool> result = mBuffer.resizeUninitialized(aAmount);
MOZ_ASSERT(result);
diff --git a/intl/components/gtest/TestDisplayNames.cpp b/intl/components/gtest/TestDisplayNames.cpp
new file mode 100644
--- /dev/null
+++ b/intl/components/gtest/TestDisplayNames.cpp
@@ -0,0 +1,153 @@
+/* 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 "gtest/gtest.h"
+
+#include "TestBuffer.h"
+#include "mozilla/intl/DisplayNames.h"
+
+namespace mozilla::intl {
+
+TEST(IntlDisplayNames, Script)
+{
+ TestBuffer<char16_t> buffer;
+
+ DisplayNames::Options options(DisplayNames::Type::Script);
+ options.style = DisplayNames::Style::Long;
+
+ {
+ auto result = DisplayNames::TryCreate("en-US", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+ ASSERT_TRUE(displayNames->Of(buffer, "Hans").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"Simplified Han");
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "ThisIsTooLong").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"");
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "✋🏽 non-ascii input").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"");
+ }
+
+ {
+ auto result = DisplayNames::TryCreate("es-ES", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "Hans").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"han simplificado");
+ }
+
+ options.style = DisplayNames::Style::Short;
+ {
+ auto result = DisplayNames::TryCreate("en-US", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "Hans").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"Simplified");
+ }
+
+ {
+ auto result = DisplayNames::TryCreate("es-ES", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "Hans").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"simplificado");
+ }
+}
+
+TEST(IntlDisplayNames, Language)
+{
+ TestBuffer<char16_t> buffer;
+ DisplayNames::Options options(DisplayNames::Type::Language);
+
+ {
+ auto result = DisplayNames::TryCreate("en-US", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+
+ ASSERT_TRUE(displayNames->Of(buffer, "es-ES").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"Spanish (Spain)");
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "zh-Hant").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"Chinese (Traditional)");
+
+ // The undefined locale returns an empty string.
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "und").isOk());
+ ASSERT_TRUE(buffer.get_string_view().empty());
+
+ // Invalid locales return an empty string.
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "asdf").isOk());
+ ASSERT_TRUE(buffer.get_string_view().empty());
+ }
+ {
+ auto result = DisplayNames::TryCreate("es-ES", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "es-ES").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"español (España)");
+
+ buffer.clear();
+ ASSERT_TRUE(displayNames->Of(buffer, "zh-Hant").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"chino (tradicional)");
+ }
+}
+
+TEST(IntlDisplayNames, Region)
+{
+ TestBuffer<char16_t> buffer;
+
+ DisplayNames::Options options(DisplayNames::Type::Region);
+ options.style = DisplayNames::Style::Long;
+
+ {
+ auto result = DisplayNames::TryCreate("en-US", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+
+ ASSERT_TRUE(displayNames->Of(buffer, "US").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"United States");
+
+ ASSERT_TRUE(displayNames->Of(buffer, "ES").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"Spain");
+ }
+ {
+ auto result = DisplayNames::TryCreate("es-ES", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+
+ ASSERT_TRUE(displayNames->Of(buffer, "US").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"Estados Unidos");
+
+ ASSERT_TRUE(displayNames->Of(buffer, "ES").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"España");
+ }
+}
+
+TEST(IntlDisplayNames, Currency)
+{
+ TestBuffer<char16_t> buffer;
+
+ DisplayNames::Options options(DisplayNames::Type::Currency);
+ options.style = DisplayNames::Style::Long;
+
+ auto result = DisplayNames::TryCreate("en-US", options);
+ ASSERT_TRUE(result.isOk());
+ auto displayNames = result.unwrap();
+ ASSERT_TRUE(displayNames->Of(buffer, u"EUR").isOk());
+ ASSERT_EQ(buffer.get_string_view(), u"Euro");
+}
+
+} // namespace mozilla::intl
diff --git a/intl/components/gtest/moz.build b/intl/components/gtest/moz.build
--- a/intl/components/gtest/moz.build
+++ b/intl/components/gtest/moz.build
@@ -3,13 +3,14 @@
# 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/.
UNIFIED_SOURCES += [
"TestCalendar.cpp",
"TestCollator.cpp",
"TestDateTimeFormat.cpp",
+ "TestDisplayNames.cpp",
"TestNumberFormat.cpp",
"TestPluralRules.cpp",
]
FINAL_LIBRARY = "xul-gtest"
diff --git a/intl/components/moz.build b/intl/components/moz.build
--- a/intl/components/moz.build
+++ b/intl/components/moz.build
@@ -3,26 +3,28 @@
# 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/.
EXPORTS.mozilla.intl = [
"src/Calendar.h",
"src/Collator.h",
"src/DateTimeFormat.h",
"src/DateTimePatternGenerator.h",
+ "src/DisplayNames.h",
"src/ICU4CGlue.h",
"src/NumberFormat.h",
"src/PluralRules.h",
]
UNIFIED_SOURCES += [
"src/Calendar.cpp",
"src/Collator.cpp",
"src/DateTimeFormat.cpp",
"src/DateTimePatternGenerator.cpp",
+ "src/DisplayNames.cpp",
"src/ICU4CGlue.cpp",
"src/NumberFormat.cpp",
"src/NumberFormatFields.cpp",
"src/NumberFormatterSkeleton.cpp",
"src/PluralRules.cpp",
]
if not CONFIG["JS_STANDALONE"]:
diff --git a/intl/components/src/DisplayNames.cpp b/intl/components/src/DisplayNames.cpp
new file mode 100644
--- /dev/null
+++ b/intl/components/src/DisplayNames.cpp
@@ -0,0 +1,78 @@
+/* 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"
+
+namespace mozilla::intl {
+
+DisplayNames::~DisplayNames() {
+ // The mDisplayNames will not exist when the DisplayNames is being
+ // moved.
+ if (mULocaleDisplayNames.GetMut()) {
+ uldn_close(mULocaleDisplayNames.GetMut());
+ }
+}
+
+/* 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,
+ };
+
+ ULocaleDisplayNames* uLocaleDisplayNames =
+ uldn_openForContext(aLocale, contexts, std::size(contexts), &status);
+
+ if (U_FAILURE(status)) {
+ return Err(ToICUError(status));
+ }
+ return MakeUnique<DisplayNames>(uLocaleDisplayNames, MakeStringSpan(aLocale),
+ aOptions);
+};
+
+/* static */
+bool DisplayNames::ConvertScriptToLocale(ScriptLocaleVector& aLocale,
+ const char* aScript) {
+ const char* localeText = "und-";
+ for (size_t i = 0; i < 4; i++) {
+ mozilla::DebugOnly<bool> result = aLocale.append(localeText[i]);
+ // The allocation would only fail due to a logic error in the
+ // implementation, as the buffer is already stack-allocated.
+ MOZ_ASSERT(result);
+ }
+
+ // Script tags are 4 ascii characters followed by a null terminator.
+ // https://unicode-org.github.io/cldr-staging/charts/37/supplemental/languages_and_scripts.html
+ size_t scriptLength = strlen(aScript);
+ if (scriptLength != 4) {
+ // The script was not the correct size, return early without writing to
+ // the buffer.
+ return false;
+ }
+
+ for (size_t i = 0; i <= scriptLength; i++) {
+ mozilla::DebugOnly<bool> result = aLocale.append(aScript[i]);
+ // The allocation would only fail due to a logic error in the
+ // implementation, as the buffer is already stack-allocated.
+ MOZ_ASSERT(result);
+ }
+ return true;
+}
+
+} // namespace mozilla::intl
diff --git a/intl/components/src/DisplayNames.h b/intl/components/src/DisplayNames.h
new file mode 100644
--- /dev/null
+++ b/intl/components/src/DisplayNames.h
@@ -0,0 +1,330 @@
+/* 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/. */
+#ifndef intl_components_DisplayNames_h_
+#define intl_components_DisplayNames_h_
+
+#include "unicode/uldnames.h"
+#include "unicode/uloc.h"
+#include "unicode/ucurr.h"
+#include "mozilla/Casting.h"
+#include "mozilla/Result.h"
+#include "mozilla/ResultVariant.h"
+#include "mozilla/Span.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/intl/ICU4CGlue.h"
+
+namespace mozilla::intl {
+
+class DisplayNames final {
+ public:
+ enum class LocaleMatcher {
+ Lookup,
+ BestFit,
+ };
+
+ enum class Style {
+ Narrow,
+ Short,
+ Long,
+ };
+
+ enum class Type {
+ // Return the localized name of a language.
+ //
+ // Accepts:
+ // languageCode ["-" scriptCode] ["-" regionCode ] *("-" variant )
+ // Where the language code is:
+ // 1. A two letters ISO 639-1 language code
+ // https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+ // 2. A three letters ISO 639-2 language code
+ // https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
+ //
+ // Examples:
+ // "es-ES" => "European Spanish" (en-US), "español de España" (es-ES)
+ // "zh-Hant" => "Traditional Chinese" (en-US), "chino tradicional" (es-ES)
+ //
+ Language,
+ // Return the localized name of a region,
+ //
+ // Accepts:
+ // 1. an ISO-3166 two letters:
+ // https://www.iso.org/iso-3166-country-codes.html
+ // 2. region code, or a three digits UN M49 Geographic Regions.
+ // https://unstats.un.org/unsd/methodology/m49/
+ //
+ // Examples
+ // "US" => "United States" (en-US), "Estados Unidos", (es-ES)
+ // "158" => "Taiwan" (en-US), "Taiwán", (es-ES)
+ Region,
+ // Returns the localized name of a script.
+ //
+ // Accepts:
+ // ECMA-402 expects the ISO-15924 four letters script code.
+ // https://unicode.org/iso15924/iso15924-codes.html
+ // e.g. "Latn"
+ //
+ // Examples:
+ // "Cher" => "Cherokee" (en-US), "cherokee" (es-ES)
+ // "Latn" => "Latin" (en-US), "latino" (es-ES)
+ Script,
+ // Return the localized name of a currency.
+ //
+ // Accepts:
+ // A 3-letter ISO 4217 currency code.
+ // https://en.wikipedia.org/wiki/ISO_4217
+ //
+ // Examples:
+ // "EUR" => "Euro" (en-US), "euro" (es_ES), "欧元", (zh)
+ // "JPY" => "Japanese Yen" (en-US), "yen" (es_ES), "日元", (zh)
+ Currency,
+ };
+
+ enum class LanguageDisplay {
+ Standard,
+ Dialect,
+ };
+
+ /**
+ * These options map to ECMA 402 DisplayNames options. Make sure the defaults
+ * map to the default initialized values of ECMA 402.
+ *
+ * Note that fallbacking is not currently implemented here in the unified API,
+ * but is still handled in SpiderMonkey. This is due to support for the
+ * LanguageTag. See Bug 1719746.
+ *
+ * https://tc39.es/ecma402/#intl-displaynames-objects
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames
+ */
+ struct Options {
+ // The constructor ensures the required Type option is created.
+ explicit Options(Type aType) : type(aType){};
+ Options() = delete;
+
+ // Required:
+ Type type;
+
+ // Optional:
+ LocaleMatcher localeMatcher = LocaleMatcher::BestFit;
+ Style style = Style::Long;
+ LanguageDisplay languageDisplay = LanguageDisplay::Standard;
+ };
+
+ DisplayNames(ULocaleDisplayNames* aDisplayNames, Span<const char> aLocale,
+ Options aOptions)
+ : mOptions(aOptions),
+ mLocale(aLocale.data(), aLocale.size()),
+ mULocaleDisplayNames(aDisplayNames) {
+ MOZ_ASSERT(aDisplayNames);
+ };
+
+ static Result<UniquePtr<DisplayNames>, ICUError> TryCreate(
+ const char* aLocale, Options aOptions);
+
+ // Not copyable or movable
+ DisplayNames(const DisplayNames&) = delete;
+ DisplayNames& operator=(const DisplayNames&) = delete;
+
+ ~DisplayNames();
+
+ /**
+ * Get the results for a display name.
+ *
+ * https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype.of
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames/of
+ *
+ * Note that this function can take either a `char` or `char16_t` CharType.
+ * This was done to ensure there is a single public DisplayName::Of method
+ * exposed for computing everything. However, the underlying ICU4C calls may
+ * require one type or another. Rather than introduce extraneous string
+ * copying, this method enforces the correct CharType through a template.
+ *
+ * In the future, if we switch from ICU4C to ICU4X, this will most likely
+ * always require a `char` type.
+ */
+ template <typename B, typename CharType>
+ ICUResult Of(B& aBuffer, const CharType* aCode) const {
+ if constexpr (std::is_same<CharType, char>::value) {
+ switch (mOptions.type) {
+ case Type::Language:
+ return this->GetLanguage(aBuffer, aCode);
+ case Type::Region:
+ return this->GetRegion(aBuffer, aCode);
+ case Type::Script:
+ return this->GetScript(aBuffer, aCode);
+ case Type::Currency:
+ MOZ_ASSERT_UNREACHABLE("Type requires a char CharType.");
+ }
+ }
+ if constexpr (std::is_same<CharType, char16_t>::value) {
+ switch (mOptions.type) {
+ case Type::Currency:
+ return this->GetCurrency(aBuffer, aCode);
+ case Type::Language:
+ case Type::Region:
+ case Type::Script:
+ MOZ_ASSERT_UNREACHABLE("Type requires a char16_t CharType.");
+ }
+ }
+ MOZ_ASSERT_UNREACHABLE();
+ }
+
+ private:
+ /**
+ * This is a specialized form of the FillBufferWithICUCall for DisplayNames.
+ *
+ * The display name APIs such as `uldn_scriptDisplayName`,
+ * `uloc_getDisplayScript`, and `uldn_regionDisplayName` report
+ * U_ILLEGAL_ARGUMENT_ERROR when no display name was found. In order to
+ * accomodate fallbacking, return an empty string in this case.
+ */
+ template <typename B, typename F>
+ static ICUResult FillBufferWithICUDisplayNames(B& aBuffer, F aCallback) {
+ return FillBufferWithICUCall(
+ aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
+ int32_t res = aCallback(target, length, status);
+
+ if (*status == U_ILLEGAL_ARGUMENT_ERROR) {
+ *status = U_ZERO_ERROR;
+ }
+ return res;
+ });
+ }
+
+ /**
+ * Get the display names for Apply the DisplayNames::Type::Language.
+ */
+ template <typename B>
+ ICUResult GetLanguage(B& aBuffer, const char* aLanguage) const {
+ static_assert(std::is_same<typename B::CharType, char16_t>::value);
+ return FillBufferWithICUDisplayNames(
+ aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
+ return uldn_localeDisplayName(mULocaleDisplayNames.GetConst(),
+ aLanguage, target, length, status);
+ });
+ };
+
+ /**
+ * Get the display names for Apply the DisplayNames::Type::Region.
+ */
+ template <typename B>
+ ICUResult GetRegion(B& aBuffer, const char* aRegion) const {
+ static_assert(std::is_same<typename B::CharType, char16_t>::value);
+
+ return FillBufferWithICUDisplayNames(
+ aBuffer, [&](UChar* chars, uint32_t size, UErrorCode* status) {
+ return uldn_regionDisplayName(
+ mULocaleDisplayNames.GetConst(), aRegion, chars,
+ AssertedCast<int32_t, uint32_t>(size), status);
+ });
+ }
+
+ /**
+ * Get the display names for Apply the DisplayNames::Type::Currency.
+ *
+ * Note that this function requires a `const char16_t` for the currency. This
+ * is done to match the underlying ICU4C call. This may change if we move to
+ * ICU4X.
+ */
+ template <typename B>
+ ICUResult GetCurrency(B& aBuffer, const char16_t* aCurrency) const {
+ static_assert(std::is_same<typename B::CharType, char16_t>::value);
+ UCurrNameStyle style;
+ switch (mOptions.style) {
+ case Style::Long:
+ style = UCURR_LONG_NAME;
+ break;
+ case Style::Short:
+ style = UCURR_SYMBOL_NAME;
+ break;
+ case Style::Narrow:
+ style = UCURR_NARROW_SYMBOL_NAME;
+ break;
+ }
+
+ int32_t length = 0;
+ UErrorCode status = U_ZERO_ERROR;
+ const char16_t* name = ucurr_getName(aCurrency, mLocale.data(), style,
+ nullptr, &length, &status);
+ if (U_FAILURE(status)) {
+ return Err(ICUError::InternalError);
+ }
+
+ if (status == U_USING_DEFAULT_WARNING) {
+ // A resource bundle lookup returned a result from the root locale.
+ // Do not write to the buffer.
+ if (aBuffer.length() != 0) {
+ // Ensure an empty string is in the buffer.
+ aBuffer.written(0);
+ }
+ return Ok();
+ }
+
+ // Write the name, which is a static string, out to the buffer. This has a
+ // small performance cost compared to just returning the reference to the
+ // static string, but ensures a consistent DisplayNames::Of API.
+ size_t amount = length;
+ aBuffer.reserve(amount);
+ for (size_t i = 0; i < amount; i++) {
+ aBuffer.data()[i] = name[i];
+ }
+ aBuffer.written(amount);
+
+ return Ok();
+ }
+
+ // Vector to hold a "und-Latn" style language code + script.
+ // Total length: 9 ("und-" 4) + ("Latn" 4) + ("\0" 1)
+ using ScriptLocaleVector = Vector<char, 9>;
+
+ /**
+ * Get the display names for Apply the DisplayNames::Type::Script.
+ */
+ template <typename B>
+ ICUResult GetScript(B& aBuffer, const char* aScript) const {
+ static_assert(std::is_same<typename B::CharType, char16_t>::value);
+
+ if (mOptions.style == DisplayNames::Style::Long) {
+ // |uldn_scriptDisplayName| doesn't use the stand-alone form for script
+ // subtags, so we're using |uloc_getDisplayScript| instead. (This only
+ // applies to the long form.)
+ //
+ // ICU bug: https://unicode-org.atlassian.net/browse/ICU-9301
+ //
+ // |uloc_getDisplayScript| expects a full locale identifier as its input.
+ // Manually append the script. This could be handled more gracefully with
+ // full language tag support. See Bug
+ //
+ // Total length: 9 ("und-" 4) + ("Latn" 4) + ("\0" 1)
+ ScriptLocaleVector locale{};
+ if (!ConvertScriptToLocale(locale, aScript)) {
+ // In case the locale is not valid, do not write to the buffer to allow
+ // for fallbacking.
+ return Ok();
+ }
+
+ return FillBufferWithICUDisplayNames(
+ aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
+ return uloc_getDisplayScript(locale.begin(), mLocale.data(), target,
+ length, status);
+ });
+ }
+
+ return FillBufferWithICUDisplayNames(
+ aBuffer, [&](UChar* target, int32_t length, UErrorCode* status) {
+ return uldn_scriptDisplayName(mULocaleDisplayNames.GetConst(),
+ aScript, target, length, status);
+ });
+ };
+
+ [[nodiscard]] static bool ConvertScriptToLocale(ScriptLocaleVector& aLocale,
+ const char* aScript);
+
+ Options mOptions;
+ std::string mLocale;
+ ICUPointer<ULocaleDisplayNames> mULocaleDisplayNames =
+ ICUPointer<ULocaleDisplayNames>(nullptr);
+};
+
+} // namespace mozilla::intl
+#endif
diff --git a/intl/components/src/ICU4CGlue.cpp b/intl/components/src/ICU4CGlue.cpp
--- a/intl/components/src/ICU4CGlue.cpp
+++ b/intl/components/src/ICU4CGlue.cpp
@@ -1,19 +1,23 @@
/* 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/ICU4CGlue.h"
namespace mozilla::intl {
+ICUError ToICUError(UErrorCode status) {
+ if (status == U_MEMORY_ALLOCATION_ERROR) {
+ return ICUError::OutOfMemory;
+ }
+ return ICUError::InternalError;
+}
+
ICUResult ToICUResult(UErrorCode status) {
if (U_SUCCESS(status)) {
return Ok();
}
- if (status == U_MEMORY_ALLOCATION_ERROR) {
- return Err(ICUError::OutOfMemory);
- }
- return Err(ICUError::InternalError);
+ return Err(ToICUError(status));
}
} // namespace mozilla::intl
diff --git a/intl/components/src/ICU4CGlue.h b/intl/components/src/ICU4CGlue.h
--- a/intl/components/src/ICU4CGlue.h
+++ b/intl/components/src/ICU4CGlue.h
@@ -23,16 +23,21 @@ enum class ICUError : uint8_t {
/**
* Error type when a method call can only result in an internal ICU error.
*/
struct InternalError {};
using ICUResult = Result<Ok, ICUError>;
/**
+ * Convert a UErrorCode to ICUError.
+ */
+ICUError ToICUError(UErrorCode status);
+
+/**
* Convert a UErrorCode to ICUResult.
*/
ICUResult ToICUResult(UErrorCode status);
/**
* The ICU status can complain about a string not being terminated, but this
* is fine for this API, as it deals with the mozilla::Span that has a pointer
* and a length.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment