1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

VCMI will now check whether translations are complete

This commit is contained in:
Ivan Savenko 2023-02-09 19:15:39 +02:00
parent 623cae7d47
commit f57a77c2fe
4 changed files with 99 additions and 26 deletions

View File

@ -410,10 +410,49 @@ void CGeneralTextHandler::registerStringOverride(const std::string & modContext,
entry.modContext = modContext; entry.modContext = modContext;
} }
void CGeneralTextHandler::loadTranslationOverrides(const std::string & language, const JsonNode & config) bool CGeneralTextHandler::validateTranslation(const std::string & language, const std::string & modContext, const JsonNode & config) const
{
bool allPresent = true;
for (auto const & string : stringsLocalizations)
{
if (string.second.modContext != modContext)
continue;
if (string.second.baseLanguage == language && !string.second.baseValue.empty())
continue;
if (config.Struct().count(string.first) > 0)
continue;
if (allPresent)
logMod->warn("Translation into language '%s' in mod '%s' is incomplete! Missing lines:", language, modContext);
logMod->warn(R"( "%s" : "",)", string.first);
allPresent = false;
}
bool allFound = true;
for (auto const & string : config.Struct())
{
if (stringsLocalizations.count(string.first) > 0)
continue;
if (allFound)
logMod->warn("Translation into language '%s' in mod '%s' has unused lines:", language, modContext);
logMod->warn(R"( "%s" : "",)", string.first);
allFound = false;
}
return allPresent && allFound;
}
void CGeneralTextHandler::loadTranslationOverrides(const std::string & language, const std::string & modContext, const JsonNode & config)
{ {
for ( auto const & node : config.Struct()) for ( auto const & node : config.Struct())
registerStringOverride(node.second.meta, language, node.first, node.second.String()); registerStringOverride(modContext, language, node.first, node.second.String());
} }
CGeneralTextHandler::CGeneralTextHandler(): CGeneralTextHandler::CGeneralTextHandler():
@ -619,7 +658,7 @@ CGeneralTextHandler::CGeneralTextHandler():
} }
} }
int32_t CGeneralTextHandler::pluralText(const int32_t textIndex, const int32_t count) const int32_t CGeneralTextHandler::pluralText(int32_t textIndex, int32_t count) const
{ {
if(textIndex == 0) if(textIndex == 0)
return 0; return 0;

View File

@ -41,7 +41,7 @@ namespace Unicode
std::string DLL_LINKAGE fromUnicode(const std::string & text, const std::string & encoding); std::string DLL_LINKAGE fromUnicode(const std::string & text, const std::string & encoding);
///delete (amount) UTF characters from right ///delete (amount) UTF characters from right
DLL_LINKAGE void trimRight(std::string & text, const size_t amount = 1); DLL_LINKAGE void trimRight(std::string & text, size_t amount = 1);
}; };
class CInputStream; class CInputStream;
@ -87,8 +87,8 @@ public:
/// end current line /// end current line
bool endLine(); bool endLine();
CLegacyConfigParser(std::string URI); explicit CLegacyConfigParser(std::string URI);
CLegacyConfigParser(const std::unique_ptr<CInputStream> & input); explicit CLegacyConfigParser(const std::unique_ptr<CInputStream> & input);
}; };
class CGeneralTextHandler; class CGeneralTextHandler;
@ -175,9 +175,14 @@ class DLL_LINKAGE CGeneralTextHandler
std::string getModLanguage(const std::string & modContext); std::string getModLanguage(const std::string & modContext);
public: public:
/// validates translation of specified language for specified mod
/// returns true if localization is valid and complete
/// any error messages will be written to log file
bool validateTranslation(const std::string & language, const std::string & modContext, JsonNode const & file) const;
/// Loads translation from provided json /// Loads translation from provided json
/// Any entries loaded by this will have priority over texts registered normally /// Any entries loaded by this will have priority over texts registered normally
void loadTranslationOverrides(const std::string & language, JsonNode const & file); void loadTranslationOverrides(const std::string & language, const std::string & modContext, JsonNode const & file);
/// add selected string to internal storage /// add selected string to internal storage
void registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized); void registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized);
@ -240,7 +245,7 @@ public:
std::vector<std::string> findStringsWithPrefix(std::string const & prefix); std::vector<std::string> findStringsWithPrefix(std::string const & prefix);
int32_t pluralText(const int32_t textIndex, const int32_t count) const; int32_t pluralText(int32_t textIndex, int32_t count) const;
size_t getCampaignLength(size_t campaignID) const; size_t getCampaignLength(size_t campaignID) const;

View File

@ -1118,32 +1118,55 @@ void CModHandler::initializeConfig()
loadConfigFromFile("defaultMods.json"); loadConfigFromFile("defaultMods.json");
} }
bool CModHandler::validateTranslations(TModID modName) const
{
bool result = true;
const auto & mod = allMods.at(modName);
{
auto fileList = mod.config["translations"].convertTo<std::vector<std::string> >();
JsonNode json = JsonUtils::assembleFromFiles(fileList);
result |= VLC->generaltexth->validateTranslation(mod.baseLanguage, modName, json);
}
// TODO: unify language lists in mod handler, general text handler, launcher and json validation
static const std::vector<std::string> languagesList =
{ "english", "german", "polish", "russian", "ukrainian" };
for (auto const & language : languagesList)
{
if (mod.config[language].isNull())
continue;
auto fileList = mod.config[language]["translations"].convertTo<std::vector<std::string> >();
JsonNode json = JsonUtils::assembleFromFiles(fileList);
result |= VLC->generaltexth->validateTranslation(language, modName, json);
}
return result;
}
void CModHandler::loadTranslation(TModID modName) void CModHandler::loadTranslation(TModID modName)
{ {
auto const & mod = allMods[modName]; auto & mod = allMods[modName];
std::string preferredLanguage = VLC->generaltexth->getPreferredLanguage(); std::string preferredLanguage = VLC->generaltexth->getPreferredLanguage();
std::string modBaseLanguage = allMods[modName].baseLanguage; std::string modBaseLanguage = allMods[modName].baseLanguage;
for (auto const & config : mod.config["translations"].Vector()) auto baseTranslationList = mod.config["translations"].convertTo<std::vector<std::string> >();
{ auto extraTranslationList = mod.config[preferredLanguage]["translations"].convertTo<std::vector<std::string> >();
JsonNode json(ResourceID(config.String(), EResType::TEXT));
json.setMeta(modName);
VLC->generaltexth->loadTranslationOverrides(modBaseLanguage, json); JsonNode baseTranslation = JsonUtils::assembleFromFiles(baseTranslationList);
} JsonNode extraTranslation = JsonUtils::assembleFromFiles(extraTranslationList);
for (auto const & config : mod.config[preferredLanguage]["translations"].Vector()) VLC->generaltexth->loadTranslationOverrides(modBaseLanguage, modName, baseTranslation);
{ VLC->generaltexth->loadTranslationOverrides(preferredLanguage, modName, extraTranslation);
JsonNode json(ResourceID(config.String(), EResType::TEXT));
json.setMeta(modName);
VLC->generaltexth->loadTranslationOverrides(preferredLanguage, json);
}
} }
void CModHandler::load() void CModHandler::load()
{ {
CStopWatch totalTime, timer; CStopWatch totalTime;
CStopWatch timer;
logMod->info("\tInitializing content handler: %d ms", timer.getDiff()); logMod->info("\tInitializing content handler: %d ms", timer.getDiff());
@ -1166,15 +1189,19 @@ void CModHandler::load()
for(const TModID & modName : activeMods) for(const TModID & modName : activeMods)
content->load(allMods[modName]); content->load(allMods[modName]);
for(const TModID & modName : activeMods)
loadTranslation(modName);
#if SCRIPTING_ENABLED #if SCRIPTING_ENABLED
VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load
#endif #endif
content->loadCustom(); content->loadCustom();
for(const TModID & modName : activeMods)
loadTranslation(modName);
for(const TModID & modName : activeMods)
if (!validateTranslations(modName))
allMods[modName].validation = CModInfo::FAILED;
logMod->info("\tLoading mod data: %d ms", timer.getDiff()); logMod->info("\tLoading mod data: %d ms", timer.getDiff());
VLC->creh->loadCrExpBon(); VLC->creh->loadCrExpBon();

View File

@ -286,6 +286,8 @@ class DLL_LINKAGE CModHandler
void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods); void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods);
void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods); void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods);
void loadTranslation(TModID modName); void loadTranslation(TModID modName);
bool validateTranslations(TModID modName) const;
public: public:
/// returns true if scope is reserved for internal use and can not be used by mods /// returns true if scope is reserved for internal use and can not be used by mods