diff --git a/cmake_modules/VCMI_lib.cmake b/cmake_modules/VCMI_lib.cmake index 15aeb9892..8f3f44bed 100644 --- a/cmake_modules/VCMI_lib.cmake +++ b/cmake_modules/VCMI_lib.cmake @@ -434,6 +434,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE) ${MAIN_LIB_DIR}/Interprocess.h ${MAIN_LIB_DIR}/JsonDetail.h ${MAIN_LIB_DIR}/JsonNode.h + ${MAIN_LIB_DIR}/Languages.h ${MAIN_LIB_DIR}/LoadProgress.h ${MAIN_LIB_DIR}/LogicalExpression.h ${MAIN_LIB_DIR}/NetPacksBase.h diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index a86d7dba4..fbd02ddd2 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -18,6 +18,7 @@ #include "GameConstants.h" #include "mapObjects/CQuest.h" #include "VCMI_Lib.h" +#include "Languages.h" VCMI_LIB_NAMESPACE_BEGIN @@ -106,22 +107,27 @@ bool Unicode::isValidString(const char * data, size_t size) /// Detects language and encoding of H3 text files based on matching against pregenerated footprints of H3 file void CGeneralTextHandler::detectInstallParameters() { - struct LanguageFootprint - { - std::string language; - std::string encoding; - std::array footprint; - }; + using LanguageFootprint = std::array; - static const std::vector knownFootprints = - { - { "english", "CP1252", { { 0.0559, 0.0000, 0.1983, 0.0051, 0.0222, 0.0183, 0.4596, 0.2405, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000 } } }, - { "french", "CP1252", { { 0.0493, 0.0000, 0.1926, 0.0047, 0.0230, 0.0121, 0.4133, 0.2780, 0.0002, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0259, 0.0008 } } }, - { "german", "CP1252", { { 0.0534, 0.0000, 0.1705, 0.0047, 0.0418, 0.0208, 0.4775, 0.2191, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0005, 0.0036, 0.0080 } } }, - { "polish", "CP1250", { { 0.0534, 0.0000, 0.1701, 0.0067, 0.0157, 0.0133, 0.4328, 0.2540, 0.0001, 0.0043, 0.0000, 0.0244, 0.0000, 0.0000, 0.0181, 0.0071 } } }, - { "russian", "CP1251", { { 0.0548, 0.0000, 0.1744, 0.0061, 0.0031, 0.0009, 0.0046, 0.0136, 0.0000, 0.0004, 0.0000, 0.0000, 0.0227, 0.0061, 0.4882, 0.2252 } } }, - { "ukrainian", "CP1251", { { 0.0559, 0.0000, 0.1807, 0.0059, 0.0036, 0.0013, 0.0046, 0.0134, 0.0000, 0.0004, 0.0000, 0.0487, 0.0209, 0.0060, 0.4615, 0.1972 } } }, - }; + static const std::array knownFootprints = + { { + { { 0.0559, 0.0000, 0.1983, 0.0051, 0.0222, 0.0183, 0.4596, 0.2405, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000 } }, + { { 0.0493, 0.0000, 0.1926, 0.0047, 0.0230, 0.0121, 0.4133, 0.2780, 0.0002, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0259, 0.0008 } }, + { { 0.0534, 0.0000, 0.1705, 0.0047, 0.0418, 0.0208, 0.4775, 0.2191, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0005, 0.0036, 0.0080 } }, + { { 0.0534, 0.0000, 0.1701, 0.0067, 0.0157, 0.0133, 0.4328, 0.2540, 0.0001, 0.0043, 0.0000, 0.0244, 0.0000, 0.0000, 0.0181, 0.0071 } }, + { { 0.0548, 0.0000, 0.1744, 0.0061, 0.0031, 0.0009, 0.0046, 0.0136, 0.0000, 0.0004, 0.0000, 0.0000, 0.0227, 0.0061, 0.4882, 0.2252 } }, + { { 0.0559, 0.0000, 0.1807, 0.0059, 0.0036, 0.0013, 0.0046, 0.0134, 0.0000, 0.0004, 0.0000, 0.0487, 0.0209, 0.0060, 0.4615, 0.1972 } }, + } }; + + static const std::array knownLanguages = + { { + "english", + "french", + "german", + "polish", + "russian", + "ukrainian" + } }; // load file that will be used for footprint generation // this is one of the most text-heavy files in game and consists solely from translated texts @@ -129,7 +135,7 @@ void CGeneralTextHandler::detectInstallParameters() std::array charCount{}; std::array footprint{}; - std::vector deviations(knownFootprints.size(), 0.0); + std::array deviations{}; auto data = resource->readAll(); @@ -152,19 +158,19 @@ void CGeneralTextHandler::detectInstallParameters() for (size_t i = 0; i < deviations.size(); ++i) { for (size_t j = 0; j < footprint.size(); ++j) - deviations[i] += std::abs((footprint[j] - knownFootprints[i].footprint[j])); + deviations[i] += std::abs((footprint[j] - knownFootprints[i][j])); } size_t bestIndex = boost::range::min_element(deviations) - deviations.begin(); for (size_t i = 0; i < deviations.size(); ++i) - logGlobal->debug("Comparing to %s: %f", knownFootprints[i].language, deviations[i]); + logGlobal->debug("Comparing to %s: %f", knownLanguages[i], deviations[i]); Settings language = settings.write["session"]["language"]; - language->String() = knownFootprints[bestIndex].language; + language->String() = knownLanguages[bestIndex]; Settings encoding = settings.write["session"]["encoding"]; - encoding->String() = knownFootprints[bestIndex].encoding; + encoding->String() = Languages::getLanguageOptions(knownLanguages[bestIndex]).encoding; } std::string Unicode::toUnicode(const std::string &text) diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 6c943f282..2e079c077 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -25,6 +25,7 @@ #include "spells/CSpellHandler.h" #include "CSkillHandler.h" #include "CGeneralTextHandler.h" +#include "Languages.h" #include "ScriptHandler.h" #include "RoadHandler.h" #include "RiverHandler.h" @@ -1129,18 +1130,17 @@ bool CModHandler::validateTranslations(TModID modName) const 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 languagesList = - { "english", "german", "polish", "russian", "ukrainian" }; - - for (auto const & language : languagesList) + for (auto const & language : Languages::getLanguageList()) { - if (mod.config[language].isNull()) + if (!language.hasTranslation) continue; - auto fileList = mod.config[language]["translations"].convertTo >(); + if (mod.config[language.identifier].isNull()) + continue; + + auto fileList = mod.config[language.identifier]["translations"].convertTo >(); JsonNode json = JsonUtils::assembleFromFiles(fileList); - result |= VLC->generaltexth->validateTranslation(language, modName, json); + result |= VLC->generaltexth->validateTranslation(language.identifier, modName, json); } return result; diff --git a/lib/Languages.h b/lib/Languages.h new file mode 100644 index 000000000..98a4681e0 --- /dev/null +++ b/lib/Languages.h @@ -0,0 +1,81 @@ +/* + * Languages.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +namespace Languages +{ + +enum class ELanguages +{ + ENGLISH, + FRENCH, + GERMAN, + POLISH, + RUSSIAN, + UKRAINIAN, + + COUNT +}; + +struct Options +{ + /// string identifier (ascii, lower-case), e.g. "english" + std::string identifier; + + /// human-readable name of language in English + std::string nameEnglish; + + /// human-readable name of language in its own language + std::string nameNative; + + /// encoding that is used by H3 for this language + std::string encoding; + + /// VCMI is capable of detecting H3 install in this language + bool hasDetection = false; + + /// VCMI supports translations into this language + bool hasTranslation = false; +}; + +inline auto const & getLanguageList( ) +{ + static const std::array languages + { { + { "english", "English", "English", "CP1252", true, true }, + { "french", "French", "Français", "CP1252", true, false }, + { "german", "German", "Deutsch", "CP1252", true, true }, + { "polish", "Polish", "Polski", "CP1250", true, true }, + { "russian", "Russian", "Русский", "CP1251", true, true }, + { "ukrainian", "Ukrainian", "Українська", "CP1251", true, true } + } }; + static_assert (languages.size() == static_cast(ELanguages::COUNT), "Languages array is missing a value!" ); + + return languages; +} + +inline const Options & getLanguageOptions( ELanguages language ) +{ + assert(language < ELanguages::COUNT); + return getLanguageList()[static_cast(language)]; +} + +inline const Options & getLanguageOptions( std::string language ) +{ + for (auto const & entry : getLanguageList()) + if (entry.identifier == language) + return entry; + + static const Options emptyValue; + assert(0); + return emptyValue; +} + +}