diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 185acaf179..882be2d950 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -34,6 +34,8 @@ #include +#include + #include #include @@ -391,6 +393,7 @@ OMW::Engine::~Engine() mStateManager = nullptr; mLuaWorker = nullptr; mLuaManager = nullptr; + mL10nManager = nullptr; mScriptContext = nullptr; @@ -682,6 +685,10 @@ void OMW::Engine::prepareEngine() mViewer->addEventHandler(mScreenCaptureHandler); + mL10nManager = std::make_unique(mVFS.get()); + mL10nManager->setPreferredLocales(Settings::Manager::getStringArray("preferred locales", "General")); + mEnvironment.setL10nManager(*mL10nManager); + mLuaManager = std::make_unique(mVFS.get(), mResDir / "lua_libs"); mEnvironment.setLuaManager(*mLuaManager); @@ -767,7 +774,6 @@ void OMW::Engine::prepareEngine() mEnvironment.setWorld(*mWorld); mWindowManager->setStore(mWorld->getStore()); - mLuaManager->initL10n(); mWindowManager->initUI(); // Load translation data diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 021dccdf34..81c3bd009b 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -111,6 +111,11 @@ namespace MWDialogue class Journal; } +namespace l10n +{ + class Manager; +} + struct SDL_Window; namespace OMW @@ -134,6 +139,7 @@ namespace OMW std::unique_ptr mStateManager; std::unique_ptr mLuaManager; std::unique_ptr mLuaWorker; + std::unique_ptr mL10nManager; MWBase::Environment mEnvironment; ToUTF8::FromType mEncoding; std::unique_ptr mEncoder; diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index 8044c385a8..0c159d27cd 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -10,6 +10,11 @@ namespace Resource class ResourceSystem; } +namespace l10n +{ + class Manager; +} + namespace MWBase { class World; @@ -42,6 +47,7 @@ namespace MWBase StateManager* mStateManager = nullptr; LuaManager* mLuaManager = nullptr; Resource::ResourceSystem* mResourceSystem = nullptr; + l10n::Manager* mL10nManager = nullptr; float mFrameRateLimit = 0; float mFrameDuration = 0; @@ -76,6 +82,8 @@ namespace MWBase void setResourceSystem(Resource::ResourceSystem& value) { mResourceSystem = &value; } + void setL10nManager(l10n::Manager& value) { mL10nManager = &value; } + Misc::NotNullPtr getWorld() const { return mWorld; } Misc::NotNullPtr getSoundManager() const { return mSoundManager; } @@ -98,6 +106,8 @@ namespace MWBase Misc::NotNullPtr getResourceSystem() const { return mResourceSystem; } + Misc::NotNullPtr getL10nManager() const { return mL10nManager; } + float getFrameRateLimit() const { return mFrameRateLimit; } void setFrameRateLimit(float value) { mFrameRateLimit = value; } diff --git a/apps/openmw/mwbase/luamanager.hpp b/apps/openmw/mwbase/luamanager.hpp index bcd30420ba..12abd91909 100644 --- a/apps/openmw/mwbase/luamanager.hpp +++ b/apps/openmw/mwbase/luamanager.hpp @@ -34,7 +34,6 @@ namespace MWBase public: virtual ~LuaManager() = default; - virtual std::string translate(const std::string& contextName, const std::string& key) = 0; virtual void newGameStarted() = 0; virtual void gameLoaded() = 0; virtual void registerObject(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 3cd18f97bd..fd46d2fff7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -48,10 +48,11 @@ #include +#include + #include #include "../mwbase/inputmanager.hpp" -#include "../mwbase/luamanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/world.hpp" @@ -1079,13 +1080,12 @@ namespace MWGui std::vector split; Misc::StringUtils::split(tag, split, ":"); - // TODO: LocalizationManager should not be a part of lua - const auto& luaManager = MWBase::Environment::get().getLuaManager(); + l10n::Manager& l10nManager = *MWBase::Environment::get().getL10nManager(); // If a key has a "Context:KeyName" format, use YAML to translate data - if (split.size() == 2 && luaManager != nullptr) + if (split.size() == 2) { - _result = luaManager->translate(split[0], split[1]); + _result = l10nManager.getContext(split[0])->formatMessage(split[1], {}, {}); return; } diff --git a/apps/openmw/mwlua/context.hpp b/apps/openmw/mwlua/context.hpp index 124e7f06be..b3e3703a46 100644 --- a/apps/openmw/mwlua/context.hpp +++ b/apps/openmw/mwlua/context.hpp @@ -7,7 +7,6 @@ namespace LuaUtil { class LuaState; class UserdataSerializer; - class L10nManager; } namespace MWLua @@ -21,7 +20,6 @@ namespace MWLua LuaManager* mLuaManager; LuaUtil::LuaState* mLua; LuaUtil::UserdataSerializer* mSerializer; - LuaUtil::L10nManager* mL10n; WorldView* mWorldView; LocalEventQueue* mLocalEventQueue; GlobalEventQueue* mGlobalEventQueue; diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 2abc094a76..a5600f6f16 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -58,12 +58,7 @@ namespace MWLua { std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) }); }; addTimeBindings(api, context, false); - api["l10n"] = [l10n = context.mL10n](const std::string& context, const sol::object& fallbackLocale) { - if (fallbackLocale == sol::nil) - return l10n->getContext(context); - else - return l10n->getContext(context, fallbackLocale.as()); - }; + api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager()); const MWWorld::Store* gmst = &MWBase::Environment::get().getWorld()->getStore().get(); api["getGMST"] = [lua = context.mLua, gmst](const std::string& setting) -> sol::object { diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 25a4c7f876..a5d605a84c 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -14,6 +14,8 @@ #include +#include + #include #include @@ -38,7 +40,6 @@ namespace MWLua LuaManager::LuaManager(const VFS::Manager* vfs, const std::filesystem::path& libsDir) : mLua(vfs, &mConfiguration) , mUiResourceManager(vfs) - , mL10n(vfs, &mLua) { Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion(); mLua.addInternalLibSearchPath(libsDir); @@ -60,19 +61,12 @@ namespace MWLua mGlobalScripts.setAutoStartConf(mConfiguration.getGlobalConf()); } - void LuaManager::initL10n() - { - mL10n.init(); - mL10n.setPreferredLocales(Settings::Manager::getStringArray("preferred locales", "General")); - } - void LuaManager::init() { Context context; context.mIsGlobal = true; context.mLuaManager = this; context.mLua = &mLua; - context.mL10n = &mL10n; context.mWorldView = &mWorldView; context.mLocalEventQueue = &mLocalEvents; context.mGlobalEventQueue = &mGlobalEvents; @@ -109,11 +103,6 @@ namespace MWLua mInitialized = true; } - std::string LuaManager::translate(const std::string& contextName, const std::string& key) - { - return mL10n.translate(contextName, key); - } - void LuaManager::loadPermanentStorage(const std::filesystem::path& userConfigPath) { const auto globalPath = userConfigPath / "global_storage.bin"; @@ -516,9 +505,9 @@ namespace MWLua LuaUi::clearUserInterface(); MWBase::Environment::get().getWindowManager()->setConsoleMode(""); + MWBase::Environment::get().getL10nManager()->dropCache(); mUiResourceManager.clear(); mLua.dropScriptCache(); - mL10n.clear(); initConfiguration(); { // Reload global scripts diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index 1d56c31af8..1d88d97957 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -29,9 +28,6 @@ namespace MWLua public: LuaManager(const VFS::Manager* vfs, const std::filesystem::path& libsDir); - // Called by engine.cpp before UI setup. - void initL10n(); - // Called by engine.cpp when the environment is fully initialized. void init(); @@ -42,8 +38,6 @@ namespace MWLua // thread (in parallel with osg Cull). Can not use scene graph. void update(); - std::string translate(const std::string& contextName, const std::string& key) override; - // Called by engine.cpp from the main thread. Can use scene graph. void synchronizedUpdate(); @@ -140,7 +134,6 @@ namespace MWLua LuaUtil::ScriptsConfiguration mConfiguration; LuaUtil::LuaState mLua; LuaUi::ResourceManager mUiResourceManager; - LuaUtil::L10nManager mL10n; sol::table mNearbyPackage; sol::table mUserInterfacePackage; sol::table mCameraPackage; diff --git a/apps/openmw_test_suite/lua/test_l10n.cpp b/apps/openmw_test_suite/lua/test_l10n.cpp index 7a44dc9af2..67415ee947 100644 --- a/apps/openmw_test_suite/lua/test_l10n.cpp +++ b/apps/openmw_test_suite/lua/test_l10n.cpp @@ -3,6 +3,7 @@ #include +#include #include #include @@ -84,20 +85,21 @@ you_have_arrows: "Arrows count: {count}" internal::CaptureStdout(); LuaUtil::LuaState lua{ mVFS.get(), &mCfg }; sol::state& l = lua.sol(); - LuaUtil::L10nManager l10n(mVFS.get(), &lua); - l10n.init(); - l10n.setPreferredLocales({ "de", "en" }); + l10n::Manager l10nManager(mVFS.get()); + l10nManager.setPreferredLocales({ "de", "en" }); EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: de en\n"); + l["l10n"] = LuaUtil::initL10nLoader(l, &l10nManager); + internal::CaptureStdout(); - l["t1"] = l10n.getContext("Test1"); + l.safe_script("t1 = l10n('Test1')"); EXPECT_THAT(internal::GetCapturedStdout(), "Fallback locale: en\n" "Language file \"l10n/Test1/de.yaml\" is enabled\n" "Language file \"l10n/Test1/en.yaml\" is enabled\n"); internal::CaptureStdout(); - l["t2"] = l10n.getContext("Test2"); + l.safe_script("t2 = l10n('Test2')"); { std::string output = internal::GetCapturedStdout(); EXPECT_THAT(output, HasSubstr("Language file \"l10n/Test2/en.yaml\" is enabled")); @@ -111,7 +113,7 @@ you_have_arrows: "Arrows count: {count}" EXPECT_EQ(get(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3"); internal::CaptureStdout(); - l10n.setPreferredLocales({ "en", "de" }); + l10nManager.setPreferredLocales({ "en", "de" }); EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: en de\n" "Language file \"l10n/Test1/en.yaml\" is enabled\n" @@ -140,7 +142,7 @@ you_have_arrows: "Arrows count: {count}" EXPECT_EQ(get(l, "t1('{unknown_arg}')"), "{unknown_arg}"); EXPECT_EQ(get(l, "t1('{num, integer}', {num=1})"), "{num, integer}"); // Doesn't give a valid currency symbol with `en`. Not that openmw is designed for real world currency. - l10n.setPreferredLocales({ "en-US", "de" }); + l10nManager.setPreferredLocales({ "en-US", "de" }); EXPECT_EQ(get(l, "t1('currency', {money=10000.10})"), "You have $10,000.10"); // Note: Not defined in English localisation file, so we fall back to the German before falling back to the key EXPECT_EQ(get(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!"); @@ -149,7 +151,7 @@ you_have_arrows: "Arrows count: {count}" // Test that locales with variants and country codes fall back to more generic locales internal::CaptureStdout(); - l10n.setPreferredLocales({ "en-GB-oed", "de" }); + l10nManager.setPreferredLocales({ "en-GB-oed", "de" }); EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: en_GB_OED de\n" "Language file \"l10n/Test1/en.yaml\" is enabled\n" @@ -158,8 +160,8 @@ you_have_arrows: "Arrows count: {count}" EXPECT_EQ(get(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3"); // Test setting fallback language - l["t3"] = l10n.getContext("Test3", "de"); - l10n.setPreferredLocales({ "en" }); + l.safe_script("t3 = l10n('Test3', 'de')"); + l10nManager.setPreferredLocales({ "en" }); EXPECT_EQ(get(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!"); } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index dba90d0189..55b5c3a81a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -33,7 +33,7 @@ add_component_dir (lua ) add_component_dir (l10n - messagebundles + messagebundles manager ) add_component_dir (settings diff --git a/components/l10n/manager.cpp b/components/l10n/manager.cpp new file mode 100644 index 0000000000..688d3c8a00 --- /dev/null +++ b/components/l10n/manager.cpp @@ -0,0 +1,95 @@ +#include "manager.hpp" + +#include + +#include +#include + +namespace l10n +{ + + void Manager::setPreferredLocales(const std::vector& langs) + { + mPreferredLocales.clear(); + for (const auto& lang : langs) + mPreferredLocales.push_back(icu::Locale(lang.c_str())); + { + Log msg(Debug::Info); + msg << "Preferred locales:"; + for (const icu::Locale& l : mPreferredLocales) + msg << " " << l.getName(); + } + for (auto& [key, context] : mCache) + updateContext(key.first, *context); + } + + void Manager::readLangData(const std::string& name, MessageBundles& ctx, const icu::Locale& lang) + { + std::string path = "l10n/"; + path.append(name); + path.append("/"); + path.append(lang.getName()); + path.append(".yaml"); + if (!mVFS->exists(path)) + return; + + ctx.load(*mVFS->get(path), lang, path); + } + + void Manager::updateContext(const std::string& name, MessageBundles& ctx) + { + icu::Locale fallbackLocale = ctx.getFallbackLocale(); + ctx.setPreferredLocales(mPreferredLocales); + int localeCount = 0; + bool fallbackLocaleInPreferred = false; + for (const icu::Locale& loc : ctx.getPreferredLocales()) + { + if (!ctx.isLoaded(loc)) + readLangData(name, ctx, loc); + if (ctx.isLoaded(loc)) + { + localeCount++; + Log(Debug::Verbose) << "Language file \"l10n/" << name << "/" << loc.getName() << ".yaml\" is enabled"; + if (loc == ctx.getFallbackLocale()) + fallbackLocaleInPreferred = true; + } + } + if (!ctx.isLoaded(ctx.getFallbackLocale())) + readLangData(name, ctx, ctx.getFallbackLocale()); + if (ctx.isLoaded(ctx.getFallbackLocale()) && !fallbackLocaleInPreferred) + Log(Debug::Verbose) << "Fallback language file \"l10n/" << name << "/" << ctx.getFallbackLocale().getName() + << ".yaml\" is enabled"; + + if (localeCount == 0) + { + Log(Debug::Warning) << "No language files for the preferred languages found in \"l10n/" << name << "\""; + } + } + + std::shared_ptr Manager::getContext( + const std::string& contextName, const std::string& fallbackLocaleName) + { + std::pair key(contextName, fallbackLocaleName); + auto it = mCache.find(key); + if (it != mCache.end()) + return it->second; + auto allowedChar = [](char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'; + }; + bool valid = !contextName.empty(); + for (char c : contextName) + valid = valid && allowedChar(c); + if (!valid) + throw std::runtime_error(std::string("Invalid l10n context name: ") + contextName); + icu::Locale fallbackLocale(fallbackLocaleName.c_str()); + std::shared_ptr ctx = std::make_shared(mPreferredLocales, fallbackLocale); + { + Log msg(Debug::Verbose); + msg << "Fallback locale: " << fallbackLocale.getName(); + } + updateContext(contextName, *ctx); + mCache.emplace(key, ctx); + return ctx; + } + +} diff --git a/components/l10n/manager.hpp b/components/l10n/manager.hpp new file mode 100644 index 0000000000..37ea45b274 --- /dev/null +++ b/components/l10n/manager.hpp @@ -0,0 +1,40 @@ +#ifndef COMPONENTS_L10N_MANAGER_H +#define COMPONENTS_L10N_MANAGER_H + +#include + +namespace VFS +{ + class Manager; +} + +namespace l10n +{ + + class Manager + { + public: + Manager(const VFS::Manager* vfs) + : mVFS(vfs) + { + } + + void dropCache() { mCache.clear(); } + void setPreferredLocales(const std::vector& locales); + const std::vector& getPreferredLocales() const { return mPreferredLocales; } + + std::shared_ptr getContext( + const std::string& contextName, const std::string& fallbackLocale = "en"); + + private: + void readLangData(const std::string& name, MessageBundles& ctx, const icu::Locale& lang); + void updateContext(const std::string& name, MessageBundles& ctx); + + const VFS::Manager* mVFS; + std::vector mPreferredLocales; + std::map, std::shared_ptr> mCache; + }; + +} + +#endif // COMPONENTS_L10N_MANAGER_H diff --git a/components/lua/l10n.cpp b/components/lua/l10n.cpp index 0b9e7a1e6b..d402cf9346 100644 --- a/components/lua/l10n.cpp +++ b/components/lua/l10n.cpp @@ -1,65 +1,18 @@ #include "l10n.hpp" -#include - #include -#include +#include -namespace sol +namespace { - template <> - struct is_automagical : std::false_type + struct L10nContext { + std::shared_ptr mData; }; -} -namespace LuaUtil -{ - void L10nManager::init() + void getICUArgs(std::string_view messageId, const sol::table& table, std::vector& argNames, + std::vector& args) { - sol::usertype ctx = mLua->sol().new_usertype("L10nContext"); - ctx[sol::meta_function::call] = &Context::translate; - } - - std::string L10nManager::translate(const std::string& contextName, const std::string& key) - { - Context& ctx = getContext(contextName).as(); - return ctx.translate(key, sol::nil); - } - - void L10nManager::setPreferredLocales(const std::vector& langs) - { - mPreferredLocales.clear(); - for (const auto& lang : langs) - mPreferredLocales.push_back(icu::Locale(lang.c_str())); - { - Log msg(Debug::Info); - msg << "Preferred locales:"; - for (const icu::Locale& l : mPreferredLocales) - msg << " " << l.getName(); - } - for (auto& [_, context] : mContexts) - context.updateLang(this); - } - - void L10nManager::Context::readLangData(L10nManager* manager, const icu::Locale& lang) - { - std::string path = "l10n/"; - path.append(mName); - path.append("/"); - path.append(lang.getName()); - path.append(".yaml"); - if (!manager->mVFS->exists(path)) - return; - - mMessageBundles->load(*manager->mVFS->get(path), lang, path); - } - - std::pair, std::vector> getICUArgs( - std::string_view messageId, const sol::table& table) - { - std::vector args; - std::vector argNames; for (auto& [key, value] : table) { // Argument values @@ -80,77 +33,37 @@ namespace LuaUtil const auto str = key.as(); argNames.push_back(icu::UnicodeString::fromUTF8(icu::StringPiece(str.data(), str.size()))); } - return std::make_pair(args, argNames); } - - std::string L10nManager::Context::translate(std::string_view key, const sol::object& data) - { - std::vector args; - std::vector argNames; - - if (data.is()) - { - sol::table dataTable = data.as(); - auto argData = getICUArgs(key, dataTable); - args = argData.first; - argNames = argData.second; - } - - return mMessageBundles->formatMessage(key, argNames, args); - } - - void L10nManager::Context::updateLang(L10nManager* manager) - { - icu::Locale fallbackLocale = mMessageBundles->getFallbackLocale(); - mMessageBundles->setPreferredLocales(manager->mPreferredLocales); - int localeCount = 0; - bool fallbackLocaleInPreferred = false; - for (const icu::Locale& loc : mMessageBundles->getPreferredLocales()) - { - if (!mMessageBundles->isLoaded(loc)) - readLangData(manager, loc); - if (mMessageBundles->isLoaded(loc)) - { - localeCount++; - Log(Debug::Verbose) << "Language file \"l10n/" << mName << "/" << loc.getName() << ".yaml\" is enabled"; - if (loc == fallbackLocale) - fallbackLocaleInPreferred = true; - } - } - if (!mMessageBundles->isLoaded(fallbackLocale)) - readLangData(manager, fallbackLocale); - if (mMessageBundles->isLoaded(fallbackLocale) && !fallbackLocaleInPreferred) - Log(Debug::Verbose) << "Fallback language file \"l10n/" << mName << "/" << fallbackLocale.getName() - << ".yaml\" is enabled"; - - if (localeCount == 0) - { - Log(Debug::Warning) << "No language files for the preferred languages found in \"l10n/" << mName << "\""; - } - } - - sol::object L10nManager::getContext(const std::string& contextName, const std::string& fallbackLocaleName) - { - auto it = mContexts.find(contextName); - if (it != mContexts.end()) - return sol::make_object(mLua->sol(), it->second); - auto allowedChar = [](char c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'; - }; - bool valid = !contextName.empty(); - for (char c : contextName) - valid = valid && allowedChar(c); - if (!valid) - throw std::runtime_error(std::string("Invalid l10n context name: ") + contextName); - icu::Locale fallbackLocale(fallbackLocaleName.c_str()); - Context ctx{ contextName, std::make_shared(mPreferredLocales, fallbackLocale) }; - { - Log msg(Debug::Verbose); - msg << "Fallback locale: " << fallbackLocale.getName(); - } - ctx.updateLang(this); - mContexts.emplace(contextName, ctx); - return sol::make_object(mLua->sol(), ctx); - } - +} + +namespace sol +{ + template <> + struct is_automagical : std::false_type + { + }; +} + +namespace LuaUtil +{ + sol::function initL10nLoader(sol::state& lua, l10n::Manager* manager) + { + sol::usertype ctxDef = lua.new_usertype("L10nContext"); + ctxDef[sol::meta_function::call] + = [](const L10nContext& ctx, std::string_view key, sol::optional args) { + std::vector argValues; + std::vector argNames; + if (args) + getICUArgs(key, *args, argNames, argValues); + return ctx.mData->formatMessage(key, argNames, argValues); + }; + + return sol::make_object( + lua, [manager](const std::string& contextName, sol::optional fallbackLocale) { + if (fallbackLocale) + return L10nContext{ manager->getContext(contextName, *fallbackLocale) }; + else + return L10nContext{ manager->getContext(contextName) }; + }); + } } diff --git a/components/lua/l10n.hpp b/components/lua/l10n.hpp index 5de5be8e59..68cb86050c 100644 --- a/components/lua/l10n.hpp +++ b/components/lua/l10n.hpp @@ -1,53 +1,16 @@ -#ifndef COMPONENTS_LUA_I18N_H -#define COMPONENTS_LUA_I18N_H +#ifndef COMPONENTS_LUA_L10N_H +#define COMPONENTS_LUA_L10N_H -#include "luastate.hpp" +#include -#include - -namespace VFS +namespace l10n { class Manager; } namespace LuaUtil { - - class L10nManager - { - public: - L10nManager(const VFS::Manager* vfs, LuaState* lua) - : mVFS(vfs) - , mLua(lua) - { - } - void init(); - void clear() { mContexts.clear(); } - - void setPreferredLocales(const std::vector& locales); - const std::vector& getPreferredLocales() const { return mPreferredLocales; } - - sol::object getContext(const std::string& contextName, const std::string& fallbackLocale = "en"); - std::string translate(const std::string& contextName, const std::string& key); - - private: - struct Context - { - const std::string mName; - // Must be a shared pointer so that sol::make_object copies the pointer, not the data structure. - std::shared_ptr mMessageBundles; - - void updateLang(L10nManager* manager); - void readLangData(L10nManager* manager, const icu::Locale& lang); - std::string translate(std::string_view key, const sol::object& data); - }; - - const VFS::Manager* mVFS; - LuaState* mLua; - std::vector mPreferredLocales; - std::map mContexts; - }; - + sol::function initL10nLoader(sol::state& lua, l10n::Manager* manager); } -#endif // COMPONENTS_LUA_I18N_H +#endif // COMPONENTS_LUA_L10N_H