/******************************************************************** * gnc-icu-locale.cpp -- Localization with ICU. * * * * Copyright (C) 2025 John Ralls #include #include #include #include "gnc-locale-utils.h" #include constexpr const char *logdomain{"gnc.locale"}; enum class CompareStrength { PRIMARY, SECONDARY, TERTIARY, QUATERNARY, IDENTICAL }; static void collator_set_strength(icu::Collator* collator, CompareStrength strength) { switch (strength) { case CompareStrength::PRIMARY: collator->setStrength(icu::Collator::PRIMARY); break; case CompareStrength::SECONDARY: collator->setStrength(icu::Collator::SECONDARY); break; case CompareStrength::TERTIARY: collator->setStrength(icu::Collator::TERTIARY); break; case CompareStrength::QUATERNARY: collator->setStrength(icu::Collator::QUATERNARY); break; case CompareStrength::IDENTICAL: collator->setStrength(icu::Collator::IDENTICAL); break; } } static bool unicode_has_substring_internal(const char* needle, const char* haystack, int* position, int* length, CompareStrength strength) { UErrorCode status{U_ZERO_ERROR}; auto locale{gnc_locale_name()}; auto u_needle{icu::UnicodeString::fromUTF8(needle)}; auto u_haystack{icu::UnicodeString::fromUTF8(haystack)}; icu::StringSearch search(u_needle, u_haystack, locale, nullptr, status); g_free(locale); if (U_SUCCESS(status)) { auto collator = search.getCollator(); collator_set_strength(collator, strength); search.reset(); } if (U_FAILURE(status)) { g_log(logdomain, G_LOG_LEVEL_ERROR, "StringSearch creation failed for %s", haystack); return false; } auto pos{search.first(status)}; if (U_FAILURE(status)) { g_log(logdomain, G_LOG_LEVEL_ERROR, "StringSearch encountered an error finding %s in %s", needle, haystack); return false; } if (pos == USEARCH_DONE) { g_log(logdomain, G_LOG_LEVEL_DEBUG, "%s not found in %s", needle, haystack); return false; } if (position && length) { *position = pos; *length = search.getMatchedLength(); } g_log(logdomain, G_LOG_LEVEL_DEBUG, "%s found in %s at index %d", needle, haystack, pos); return true; } bool gnc_unicode_has_substring_base_chars(const char* needle, const char* haystack, int* position, int* length) { return unicode_has_substring_internal(needle, haystack, position, length, CompareStrength::PRIMARY); } bool gnc_unicode_has_substring_accented_chars(const char* needle, const char* haystack, int* position, int* length) { return unicode_has_substring_internal(needle, haystack, position, length, CompareStrength::SECONDARY); } bool gnc_unicode_has_substring_accented_case_sensitive(const char* needle, const char* haystack, int* position, int* length) { return unicode_has_substring_internal(needle, haystack, position, length, CompareStrength::TERTIARY); } bool gnc_unicode_has_substring_identical(const char* needle, const char*haystack, int* position, int* length) { auto location = strstr(haystack, needle); if (location && location != haystack) { *position = static_cast(location - haystack); *length = strlen(needle); return true; } return false; } static int unicode_compare_internal(const char* one, const char* two, CompareStrength strength) { UErrorCode status{U_ZERO_ERROR}; auto locale{gnc_locale_name()}; std::unique_ptr coll( icu::Collator::createInstance(icu::Locale(locale), status)); if (U_SUCCESS(status)) collator_set_strength(coll.get(), strength); if (U_FAILURE(status)) { g_log(logdomain, G_LOG_LEVEL_ERROR, "Failed to create collator for locale %s: %s", locale, u_errorName(status)); g_free(locale); return -99; } auto result = coll->compare(one, two, status); if (U_FAILURE(status)) { g_log(logdomain, G_LOG_LEVEL_ERROR, "Comparison of %s and %s in locale %s failed: %s", one, two, locale, u_errorName(status)); g_free(locale); return -99; } g_free(locale); return result == UCOL_LESS ? -1 : result == UCOL_EQUAL ? 0 : 1; } int gnc_unicode_compare_base_chars(const char* one, const char* two) { return unicode_compare_internal(one, two, CompareStrength::PRIMARY); } int gnc_unicode_compare_accented_chars(const char* one, const char* two) { return unicode_compare_internal(one, two, CompareStrength::SECONDARY); } int gnc_unicode_compare_accented_case_sensitive(const char* one, const char* two) { return unicode_compare_internal(one, two, CompareStrength::TERTIARY); } int gnc_unicode_compare_identical(const char* one, const char* two) { return unicode_compare_internal(one, two, CompareStrength::IDENTICAL); }