1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-04 07:39:40 +00:00
openmw/components/lua/l10n.cpp
psi29a d444b1b350 Merge branch 'coverity' into 'master'
Do not copy data when it is not needed

See merge request 

(cherry picked from commit f42ab6a3e7)

e3ad30a5 Do not copy data when it is not needed
2022-08-16 07:27:29 +00:00

153 lines
5.7 KiB
C++

#include "l10n.hpp"
#include <unicode/errorcode.h>
#include <components/debug/debuglog.hpp>
#include <components/vfs/manager.hpp>
namespace sol
{
template <>
struct is_automagical<LuaUtil::L10nManager::Context> : std::false_type {};
}
namespace LuaUtil
{
void L10nManager::init()
{
sol::usertype<Context> ctx = mLua->sol().new_usertype<Context>("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<Context>();
return ctx.translate(key, sol::nil);
}
void L10nManager::setPreferredLocales(const std::vector<std::string>& 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<icu::Formattable>, std::vector<icu::UnicodeString>> getICUArgs(std::string_view messageId, const sol::table &table)
{
std::vector<icu::Formattable> args;
std::vector<icu::UnicodeString> argNames;
for (auto& [key, value] : table)
{
// Argument values
if (value.is<std::string>())
args.push_back(icu::Formattable(value.as<std::string>().c_str()));
// Note: While we pass all numbers as doubles, they still seem to be handled appropriately.
// Numbers can be forced to be integers using the argType number and argStyle integer
// E.g. {var, number, integer}
else if (value.is<double>())
args.push_back(icu::Formattable(value.as<double>()));
else
{
Log(Debug::Error) << "Unrecognized argument type for key \"" << key.as<std::string>()
<< "\" when formatting message \"" << messageId << "\"";
}
// Argument names
const auto str = key.as<std::string>();
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<icu::Formattable> args;
std::vector<icu::UnicodeString> argNames;
if (data.is<sol::table>()) {
sol::table dataTable = data.as<sol::table>();
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<l10n::MessageBundles>(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);
}
}