TextLocalizationContainer.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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 TextIdentifier & UID, const std::string & localized)
  21. {
  22. std::lock_guard globalLock(globalTextMutex);
  23. assert(!modContext.empty());
  24. // NOTE: implicitly creates entry, intended - strings added by maps, campaigns, vcmi and potentially - UI mods are not registered anywhere at the moment
  25. auto & entry = stringsLocalizations[UID.get()];
  26. // load string override only in following cases:
  27. // a) string was not modified in another mod (e.g. rebalance mod gave skill new description)
  28. // b) this string override is defined in the same mod as one that provided modified version of this string
  29. if (entry.identifierModContext == entry.baseStringModContext || modContext == entry.baseStringModContext)
  30. {
  31. entry.translatedText = localized;
  32. if (entry.identifierModContext.empty())
  33. {
  34. entry.identifierModContext = modContext;
  35. entry.baseStringModContext = modContext;
  36. }
  37. }
  38. else
  39. {
  40. logGlobal->debug("Skipping translation override for string %s: changed in a different mod", UID.get());
  41. }
  42. }
  43. void TextLocalizationContainer::addSubContainer(const TextLocalizationContainer & container)
  44. {
  45. std::lock_guard globalLock(globalTextMutex);
  46. assert(!vstd::contains(subContainers, &container));
  47. subContainers.push_back(&container);
  48. }
  49. void TextLocalizationContainer::removeSubContainer(const TextLocalizationContainer & container)
  50. {
  51. std::lock_guard globalLock(globalTextMutex);
  52. assert(vstd::contains(subContainers, &container));
  53. subContainers.erase(std::remove(subContainers.begin(), subContainers.end(), &container), subContainers.end());
  54. }
  55. const std::string & TextLocalizationContainer::translateString(const TextIdentifier & identifier) const
  56. {
  57. std::lock_guard globalLock(globalTextMutex);
  58. if(stringsLocalizations.count(identifier.get()) == 0)
  59. {
  60. for(auto containerIter = subContainers.rbegin(); containerIter != subContainers.rend(); ++containerIter)
  61. if((*containerIter)->identifierExists(identifier))
  62. return (*containerIter)->translateString(identifier);
  63. logGlobal->error("Unable to find localization for string '%s'", identifier.get());
  64. return identifier.get();
  65. }
  66. const auto & entry = stringsLocalizations.at(identifier.get());
  67. return entry.translatedText;
  68. }
  69. void TextLocalizationContainer::registerString(const std::string & modContext, const TextIdentifier & inputUID, const JsonNode & localized)
  70. {
  71. assert(localized.isNull() || !localized.getModScope().empty());
  72. assert(localized.isNull() || !getModLanguage(localized.getModScope()).empty());
  73. if (localized.isNull())
  74. registerString(modContext, modContext, inputUID, localized.String());
  75. else
  76. registerString(modContext, localized.getModScope(), inputUID, localized.String());
  77. }
  78. void TextLocalizationContainer::registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized)
  79. {
  80. registerString(modContext, modContext, UID, localized);
  81. }
  82. void TextLocalizationContainer::registerString(const std::string & identifierModContext, const std::string & localizedStringModContext, const TextIdentifier & UID, const std::string & localized)
  83. {
  84. std::lock_guard globalLock(globalTextMutex);
  85. assert(!identifierModContext.empty());
  86. assert(!localizedStringModContext.empty());
  87. assert(UID.get().find("..") == std::string::npos); // invalid identifier - there is section that was evaluated to empty string
  88. assert(stringsLocalizations.count(UID.get()) == 0 || boost::algorithm::starts_with(UID.get(), "map")); // registering already registered string?
  89. if(stringsLocalizations.count(UID.get()) > 0)
  90. {
  91. auto & value = stringsLocalizations[UID.get()];
  92. value.translatedText = localized;
  93. value.identifierModContext = identifierModContext;
  94. value.baseStringModContext = localizedStringModContext;
  95. }
  96. else
  97. {
  98. StringState value;
  99. value.translatedText = localized;
  100. value.identifierModContext = identifierModContext;
  101. value.baseStringModContext = localizedStringModContext;
  102. stringsLocalizations[UID.get()] = value;
  103. }
  104. }
  105. void TextLocalizationContainer::loadTranslationOverrides(const std::string & modContext, const JsonNode & config)
  106. {
  107. for(const auto & node : config.Struct())
  108. registerStringOverride(modContext, node.first, node.second.String());
  109. }
  110. bool TextLocalizationContainer::identifierExists(const TextIdentifier & UID) const
  111. {
  112. std::lock_guard globalLock(globalTextMutex);
  113. return stringsLocalizations.count(UID.get());
  114. }
  115. void TextLocalizationContainer::exportAllTexts(std::map<std::string, std::map<std::string, std::string>> & storage) const
  116. {
  117. std::lock_guard globalLock(globalTextMutex);
  118. for (auto const & subContainer : subContainers)
  119. subContainer->exportAllTexts(storage);
  120. for (auto const & entry : stringsLocalizations)
  121. {
  122. std::string textToWrite;
  123. std::string modName = entry.second.baseStringModContext;
  124. if (entry.second.baseStringModContext == entry.second.identifierModContext)
  125. {
  126. if (modName.find('.') != std::string::npos)
  127. modName = modName.substr(0, modName.find('.'));
  128. }
  129. boost::range::replace(modName, '.', '_');
  130. textToWrite = entry.second.translatedText;
  131. if (!textToWrite.empty())
  132. storage[modName][entry.first] = textToWrite;
  133. }
  134. }
  135. std::string TextLocalizationContainer::getModLanguage(const std::string & modContext)
  136. {
  137. if (modContext == "core")
  138. return CGeneralTextHandler::getInstalledLanguage();
  139. return VLC->modh->getModLanguage(modContext);
  140. }
  141. void TextLocalizationContainer::jsonSerialize(JsonNode & dest) const
  142. {
  143. std::lock_guard globalLock(globalTextMutex);
  144. for(auto & s : stringsLocalizations)
  145. dest.Struct()[s.first].String() = s.second.translatedText;
  146. }
  147. TextContainerRegistrable::TextContainerRegistrable()
  148. {
  149. VLC->generaltexth->addSubContainer(*this);
  150. }
  151. TextContainerRegistrable::~TextContainerRegistrable()
  152. {
  153. VLC->generaltexth->removeSubContainer(*this);
  154. }
  155. TextContainerRegistrable::TextContainerRegistrable(const TextContainerRegistrable & other)
  156. : TextLocalizationContainer(other)
  157. {
  158. VLC->generaltexth->addSubContainer(*this);
  159. }
  160. TextContainerRegistrable::TextContainerRegistrable(TextContainerRegistrable && other) noexcept
  161. :TextLocalizationContainer(other)
  162. {
  163. VLC->generaltexth->addSubContainer(*this);
  164. }
  165. VCMI_LIB_NAMESPACE_END