/// <summary> /// Get a display name for a transliterator. This reimplements the logic from the C++ /// Transliterator::getDisplayName method, since the ICU C API doesn't expose a /// utrans_getDisplayName() call. (Unfortunately). /// Note that if no text is found for the given locale, ICU will (by default) fallback to /// the root locale. However, the root locale's strings for transliterator display names /// are ugly and not suitable for displaying to the user. Therefore, if we have to /// fallback, we fallback to the "en" locale instead of the root locale. /// </summary> /// <param name="transId">The translator's system ID in ICU.</param> /// <param name="localeName">The ICU name of the locale in which to calculate the display /// name.</param> /// <returns>A name suitable for displaying to the user in the given locale, or in English /// if no translated text is present in the given locale.</returns> public static string GetDisplayName(string transId, string localeName) { const string translitDisplayNameRBKeyPrefix = "%Translit%%"; // See RB_DISPLAY_NAME_PREFIX in translit.cpp in ICU source code const string scriptDisplayNameRBKeyPrefix = "%Translit%"; // See RB_SCRIPT_DISPLAY_NAME_PREFIX in translit.cpp in ICU source code const string translitResourceBundleName = "ICUDATA-translit"; const string translitDisplayNamePatternKey = "TransliteratorNamePattern"; ParseTransliteratorID(transId, out var source, out var target, out var variant); if (target.Length < 1) { return(transId); // Malformed ID? Give up } using (var bundle = new ResourceBundle(translitResourceBundleName, localeName)) using (var bundleFallback = new ResourceBundle(translitResourceBundleName, "en")) { var pattern = bundle.GetStringByKey(translitDisplayNamePatternKey); // If we don't find a MessageFormat pattern in our locale, try the English fallback if (string.IsNullOrEmpty(pattern)) { pattern = bundleFallback.GetStringByKey(translitDisplayNamePatternKey); } // Still can't find a pattern? Then we won't be able to format the ID, so just return it if (string.IsNullOrEmpty(pattern)) { return(transId); } // First check if there is a specific localized name for this transliterator, and if so, just return it. // Note that we need to check whether the string we got still starts with the "%Translit%%" prefix, because // if so, it means that we got a value from the root locale's bundle, which isn't actually localized. var translitLocalizedName = bundle.GetStringByKey(translitDisplayNameRBKeyPrefix + transId); if (!string.IsNullOrEmpty(translitLocalizedName) && !translitLocalizedName.StartsWith(translitDisplayNameRBKeyPrefix)) { return(translitLocalizedName); } // There was no specific localized name for this transliterator (which will be true of most cases). Build one. // Try getting localized display names for the source and target, if possible. var localizedSource = bundle.GetStringByKey(scriptDisplayNameRBKeyPrefix + source); if (string.IsNullOrEmpty(localizedSource)) { localizedSource = source; // Can't localize } else { // As with the transliterator name, we need to check that the string we got didn't come from the root bundle // (which just returns a string that still contains the ugly %Translit% prefix). If it did, fall back to English. if (localizedSource.StartsWith(scriptDisplayNameRBKeyPrefix)) { localizedSource = bundleFallback.GetStringByKey(scriptDisplayNameRBKeyPrefix + source); } if (string.IsNullOrEmpty(localizedSource) || localizedSource.StartsWith(scriptDisplayNameRBKeyPrefix)) { localizedSource = source; } } // Same thing for target var localizedTarget = bundle.GetStringByKey(scriptDisplayNameRBKeyPrefix + target); if (string.IsNullOrEmpty(localizedTarget)) { localizedTarget = target; // Can't localize } else { if (localizedTarget.StartsWith(scriptDisplayNameRBKeyPrefix)) { localizedTarget = bundleFallback.GetStringByKey(scriptDisplayNameRBKeyPrefix + target); } if (string.IsNullOrEmpty(localizedTarget) || localizedTarget.StartsWith(scriptDisplayNameRBKeyPrefix)) { localizedTarget = target; } } var displayName = MessageFormatter.Format(pattern, localeName, out var status, 2.0, localizedSource, localizedTarget); if (status.IsSuccess()) { return(displayName + variant); // Variant is either empty string or starts with "/" } return(transId); // If formatting fails, the transliterator's ID is still our final fallback } }