TextLocalizationContainer.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * TextLocalizationContainer.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "TextLocalizationContainer.h"
  12. #include "texts/CGeneralTextHandler.h"
  13. #include "Languages.h"
  14. #include "TextOperations.h"
  15. #include "../VCMI_Lib.h"
  16. #include "../json/JsonNode.h"
  17. #include "../modding/CModHandler.h"
  18. VCMI_LIB_NAMESPACE_BEGIN
  19. std::recursive_mutex TextLocalizationContainer::globalTextMutex;
  20. void TextLocalizationContainer::registerStringOverride(const std::string & modContext, const std::string & language, const TextIdentifier & UID, const std::string & localized)
  21. {
  22. std::lock_guard globalLock(globalTextMutex);
  23. assert(!modContext.empty());
  24. assert(!language.empty());
  25. // NOTE: implicitly creates entry, intended - strings added by maps, campaigns, vcmi and potentially - UI mods are not registered anywhere at the moment
  26. auto & entry = stringsLocalizations[UID.get()];
  27. entry.overrideLanguage = language;
  28. entry.overrideValue = localized;
  29. if (entry.modContext.empty())
  30. entry.modContext = modContext;
  31. }
  32. void TextLocalizationContainer::addSubContainer(const TextLocalizationContainer & container)
  33. {
  34. std::lock_guard globalLock(globalTextMutex);
  35. assert(!vstd::contains(subContainers, &container));
  36. subContainers.push_back(&container);
  37. }
  38. void TextLocalizationContainer::removeSubContainer(const TextLocalizationContainer & container)
  39. {
  40. std::lock_guard globalLock(globalTextMutex);
  41. assert(vstd::contains(subContainers, &container));
  42. subContainers.erase(std::remove(subContainers.begin(), subContainers.end(), &container), subContainers.end());
  43. }
  44. const std::string & TextLocalizationContainer::deserialize(const TextIdentifier & identifier) const
  45. {
  46. std::lock_guard globalLock(globalTextMutex);
  47. if(stringsLocalizations.count(identifier.get()) == 0)
  48. {
  49. for(auto containerIter = subContainers.rbegin(); containerIter != subContainers.rend(); ++containerIter)
  50. if((*containerIter)->identifierExists(identifier))
  51. return (*containerIter)->deserialize(identifier);
  52. logGlobal->error("Unable to find localization for string '%s'", identifier.get());
  53. return identifier.get();
  54. }
  55. const auto & entry = stringsLocalizations.at(identifier.get());
  56. if (!entry.overrideValue.empty())
  57. return entry.overrideValue;
  58. return entry.baseValue;
  59. }
  60. void TextLocalizationContainer::registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized, const std::string & language)
  61. {
  62. std::lock_guard globalLock(globalTextMutex);
  63. assert(!modContext.empty());
  64. assert(!Languages::getLanguageOptions(language).identifier.empty());
  65. assert(UID.get().find("..") == std::string::npos); // invalid identifier - there is section that was evaluated to empty string
  66. //assert(stringsLocalizations.count(UID.get()) == 0); // registering already registered string?
  67. if(stringsLocalizations.count(UID.get()) > 0)
  68. {
  69. auto & value = stringsLocalizations[UID.get()];
  70. value.baseLanguage = language;
  71. value.baseValue = localized;
  72. }
  73. else
  74. {
  75. StringState value;
  76. value.baseLanguage = language;
  77. value.baseValue = localized;
  78. value.modContext = modContext;
  79. stringsLocalizations[UID.get()] = value;
  80. }
  81. }
  82. void TextLocalizationContainer::registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized)
  83. {
  84. assert(!getModLanguage(modContext).empty());
  85. registerString(modContext, UID, localized, getModLanguage(modContext));
  86. }
  87. bool TextLocalizationContainer::validateTranslation(const std::string & language, const std::string & modContext, const JsonNode & config) const
  88. {
  89. std::lock_guard globalLock(globalTextMutex);
  90. bool allPresent = true;
  91. for(const auto & string : stringsLocalizations)
  92. {
  93. if (string.second.modContext != modContext)
  94. continue; // Not our mod
  95. if (string.second.overrideLanguage == language)
  96. continue; // Already translated
  97. if (string.second.baseLanguage == language && !string.second.baseValue.empty())
  98. continue; // Base string already uses our language
  99. if (string.second.baseLanguage.empty())
  100. continue; // String added in localization, not present in base language (e.g. maps/campaigns)
  101. if (config.Struct().count(string.first) > 0)
  102. continue;
  103. if (allPresent)
  104. logMod->warn("Translation into language '%s' in mod '%s' is incomplete! Missing lines:", language, modContext);
  105. std::string currentText;
  106. if (string.second.overrideValue.empty())
  107. currentText = string.second.baseValue;
  108. else
  109. currentText = string.second.overrideValue;
  110. logMod->warn(R"( "%s" : "%s",)", string.first, TextOperations::escapeString(currentText));
  111. allPresent = false;
  112. }
  113. bool allFound = true;
  114. // for(const auto & string : config.Struct())
  115. // {
  116. // if (stringsLocalizations.count(string.first) > 0)
  117. // continue;
  118. //
  119. // if (allFound)
  120. // logMod->warn("Translation into language '%s' in mod '%s' has unused lines:", language, modContext);
  121. //
  122. // logMod->warn(R"( "%s" : "%s",)", string.first, TextOperations::escapeString(string.second.String()));
  123. // allFound = false;
  124. // }
  125. return allPresent && allFound;
  126. }
  127. void TextLocalizationContainer::loadTranslationOverrides(const std::string & language, const std::string & modContext, const JsonNode & config)
  128. {
  129. for(const auto & node : config.Struct())
  130. registerStringOverride(modContext, language, node.first, node.second.String());
  131. }
  132. bool TextLocalizationContainer::identifierExists(const TextIdentifier & UID) const
  133. {
  134. std::lock_guard globalLock(globalTextMutex);
  135. return stringsLocalizations.count(UID.get());
  136. }
  137. void TextLocalizationContainer::exportAllTexts(std::map<std::string, std::map<std::string, std::string>> & storage) const
  138. {
  139. std::lock_guard globalLock(globalTextMutex);
  140. for (auto const & subContainer : subContainers)
  141. subContainer->exportAllTexts(storage);
  142. for (auto const & entry : stringsLocalizations)
  143. {
  144. std::string textToWrite;
  145. std::string modName = entry.second.modContext;
  146. if (modName.find('.') != std::string::npos)
  147. modName = modName.substr(0, modName.find('.'));
  148. if (!entry.second.overrideValue.empty())
  149. textToWrite = entry.second.overrideValue;
  150. else
  151. textToWrite = entry.second.baseValue;
  152. storage[modName][entry.first] = textToWrite;
  153. }
  154. }
  155. std::string TextLocalizationContainer::getModLanguage(const std::string & modContext)
  156. {
  157. if (modContext == "core")
  158. return CGeneralTextHandler::getInstalledLanguage();
  159. return VLC->modh->getModLanguage(modContext);
  160. }
  161. void TextLocalizationContainer::jsonSerialize(JsonNode & dest) const
  162. {
  163. std::lock_guard globalLock(globalTextMutex);
  164. for(auto & s : stringsLocalizations)
  165. {
  166. dest.Struct()[s.first].String() = s.second.baseValue;
  167. if(!s.second.overrideValue.empty())
  168. dest.Struct()[s.first].String() = s.second.overrideValue;
  169. }
  170. }
  171. TextContainerRegistrable::TextContainerRegistrable()
  172. {
  173. VLC->generaltexth->addSubContainer(*this);
  174. }
  175. TextContainerRegistrable::~TextContainerRegistrable()
  176. {
  177. VLC->generaltexth->removeSubContainer(*this);
  178. }
  179. TextContainerRegistrable::TextContainerRegistrable(const TextContainerRegistrable & other)
  180. : TextLocalizationContainer(other)
  181. {
  182. VLC->generaltexth->addSubContainer(*this);
  183. }
  184. TextContainerRegistrable::TextContainerRegistrable(TextContainerRegistrable && other) noexcept
  185. :TextLocalizationContainer(other)
  186. {
  187. VLC->generaltexth->addSubContainer(*this);
  188. }
  189. VCMI_LIB_NAMESPACE_END