From 98a1d4f734be2e1fd5d9e4f2daa0da290c96fd52 Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sat, 8 Nov 2025 22:14:23 +0000 Subject: [PATCH] better fallbacks --- src/i18n/I18nEngine.cpp | 31 +++++++++++++++++++++++++++++++ tests/i18n.cpp | 9 +++++++++ 2 files changed, 40 insertions(+) diff --git a/src/i18n/I18nEngine.cpp b/src/i18n/I18nEngine.cpp index 41f76a6..446710d 100644 --- a/src/i18n/I18nEngine.cpp +++ b/src/i18n/I18nEngine.cpp @@ -1,5 +1,7 @@ #include "I18nEngine.hpp" +#include +#include #include using namespace Hyprutils::I18n; @@ -42,6 +44,35 @@ std::string CI18nEngine::localizeEntry(const std::string& locale, uint64_t key, entry = &m_impl->entries[locale][key]; if (!entry || !entry->exists) { + // try to fall back to lang_LANG + if (locale.contains('_')) { + auto stem = locale.substr(0, locale.find('_')); + auto stemUpper = stem; + std::ranges::transform(stemUpper, stemUpper.begin(), ::toupper); + auto newLocale = std::format("{}_{}", stem, stemUpper); + if (m_impl->entries.contains(newLocale) && m_impl->entries[newLocale].size() > key) + entry = &m_impl->entries[newLocale][key]; + } + } + + if (!entry || !entry->exists) { + // try to fall back to any lang prefixed with our prefix + if (locale.contains('_')) { + auto stem = locale.substr(0, locale.find('_') + 1); + for (const auto& [k, v] : m_impl->entries) { + if (k.starts_with(stem)) { + if (m_impl->entries.contains(k) && m_impl->entries[k].size() > key) + entry = &m_impl->entries[k][key]; + + if (entry && entry->exists) + break; + } + } + } + } + + if (!entry || !entry->exists) { + // fall back to general fallback if (m_impl->entries.contains(m_impl->fallbackLocale) && m_impl->entries[m_impl->fallbackLocale].size() > key) entry = &m_impl->entries[m_impl->fallbackLocale][key]; } diff --git a/tests/i18n.cpp b/tests/i18n.cpp index 8e95a24..4c3c701 100644 --- a/tests/i18n.cpp +++ b/tests/i18n.cpp @@ -36,6 +36,9 @@ int main(int argc, char** argv, char** envp) { return "Mam {count} jabłek."; }); + engine.registerEntry("es_XX", TXT_KEY_FALLBACK, "I don't speak spanish"); + engine.registerEntry("es_ES", TXT_KEY_FALLBACK, "I don't speak spanish here either"); + EXPECT(engine.localizeEntry("en_US", TXT_KEY_HELLO, {}), "Hello World!"); EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_HELLO, {}), "Witaj świecie!"); EXPECT(engine.localizeEntry("de_DE", TXT_KEY_HELLO, {}), "Hello World!"); @@ -47,6 +50,12 @@ int main(int argc, char** argv, char** envp) { EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "Mam 2 jabłka."); EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek."); + EXPECT(engine.localizeEntry("pl_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "5"}}), "Mam 5 jabłek."); + EXPECT(engine.localizeEntry("en_XX", TXT_KEY_I_HAVE_APPLES, {{"count", "2"}}), "I have 2 apples."); + + EXPECT(engine.localizeEntry("es_YY", TXT_KEY_FALLBACK, {}), "I don't speak spanish here either"); + EXPECT(engine.localizeEntry("es_XX", TXT_KEY_FALLBACK, {}), "I don't speak spanish"); + EXPECT(engine.localizeEntry("pl_PL", TXT_KEY_FALLBACK, {}), "Fallback string!"); return ret;