Working on translations

This commit is contained in:
Vladislav Khorev committed 2026-07-07 11:24:21 +03:00
1 parent 9d8ca865c7
commit e2d9105b02
18 files changed
+271 -88

No files matched your search

+1 -1
View File
@@ -316,7 +316,7 @@ namespace ZL
gameState.chatPreviewMsg[i].clear();
gameState.chatHistory[i].clear();
}
gameState.inventory.items.clear();
gameState.inventory.clear();
gameState.questJournal.loadFromFile("resources/config/quests.json", CONST_ZIP_FILE);
createLocations();
};
+33
View File
@@ -21,6 +21,39 @@ Language languageFromCode(const std::string& code) {
return Language::Russian;
}
std::string localizedConfigPath(const std::string& basePath, Language lang) {
static const std::string marker = "/config/";
const size_t pos = basePath.find(marker);
if (pos == std::string::npos) return basePath;
return basePath.substr(0, pos) + "/config_" + languageToCode(lang) + "/" + basePath.substr(pos + marker.size());
}
std::string readLocalizedConfigFile(const std::string& basePath, const std::string& zipFile) {
const std::string localizedPath = localizedConfigPath(basePath, g_currentLanguage);
if (localizedPath != basePath) {
std::string content;
if (zipFile.empty()) {
content = readTextFile(localizedPath);
} else {
try {
const auto buf = readFileFromZIP(localizedPath, zipFile);
content.assign(buf.begin(), buf.end());
} catch (const std::exception&) {
content.clear();
}
}
if (!content.empty()) return content;
}
if (zipFile.empty()) return readTextFile(basePath);
try {
const auto buf = readFileFromZIP(basePath, zipFile);
return std::string(buf.begin(), buf.end());
} catch (const std::exception&) {
return "";
}
}
void loadLanguageSetting() {
const std::string content = readTextFile("settings.json");
if (content.empty()) return;
+9
View File
@@ -14,6 +14,15 @@ extern Language g_currentLanguage;
std::string languageToCode(Language lang);
Language languageFromCode(const std::string& code);
// "resources/config/items.json" + English -> "resources/config_en/items.json".
// Returns basePath unchanged if it doesn't contain a "/config/" segment.
std::string localizedConfigPath(const std::string& basePath, Language lang);
// Reads basePath, preferring the config_<lang> variant for g_currentLanguage;
// falls back to basePath if the localized file/zip-entry is missing or empty.
// zipFile empty => loose files from disk; non-empty => entries inside that zip.
std::string readLocalizedConfigFile(const std::string& basePath, const std::string& zipFile);
// Reads the "language" field from settings.json (if present) and updates
// g_currentLanguage. Safe to call even if settings.json doesn't exist yet.
// Must run before any location/dialogue content is loaded, since some
+8
View File
@@ -1,6 +1,7 @@
#include "MenuManager.h"
#include "Game.h"
#include "Localization.h"
#include "items/ItemRegistry.h"
#include "render/TextRenderer.h"
#include "utils/Utils.h"
#include <iostream>
@@ -414,12 +415,19 @@ namespace ZL {
uiManager.setTextButtonCallback("languageRussianButton", [this](const std::string&) {
ZL::g_currentLanguage = ZL::Language::Russian;
reloadLocalizedGameContent();
});
uiManager.setTextButtonCallback("languageEnglishButton", [this](const std::string&) {
ZL::g_currentLanguage = ZL::Language::English;
reloadLocalizedGameContent();
});
}
void MenuManager::reloadLocalizedGameContent() {
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
gameState_.questJournal.reloadDefinitionsPreservingState("resources/config/quests.json", CONST_ZIP_FILE);
}
void MenuManager::showLoadGameScreen()
{
uiManager.pushMenuFromSavedRoot(loadSavedGameScreenRoot);
+1
View File
@@ -118,6 +118,7 @@ namespace ZL {
void enterGameplay(bool isFromLoad = false);
void applyHudForCurrentState();
void refreshQuestJournalUi();
void reloadLocalizedGameContent();
void selectQuestByIndex(int index);
void refreshItemPickupHud();
void setupStep5Callbacks();
+1 -1
View File
@@ -124,7 +124,7 @@ namespace ZL {
api.set_function("pickup_item", [inventory](const std::string& itemId) {
const Item* item = ItemRegistry::instance().findById(itemId);
if (item) {
inventory->addItem(*item);
inventory->addItem(item->id);
std::cout << "[script] pickup_item: " << item->name << std::endl;
} else {
std::cerr << "[script] pickup_item: item '" << itemId << "' not found in ItemRegistry\n";
+29 -30
View File
@@ -1,56 +1,55 @@
#include "Item.h"
#include "ItemRegistry.h"
#include "external/nlohmann/json.hpp"
#include <algorithm>
#include <iostream>
namespace ZL {
void Inventory::addItem(const Item& item) {
items.push_back(item);
std::cout << "Item added to inventory: " << item.name << std::endl;
if (onItemAdded) onItemAdded(item.id);
void Inventory::addItem(const std::string& itemId) {
itemIds.push_back(itemId);
std::cout << "Item added to inventory: " << itemId << std::endl;
if (onItemAdded) onItemAdded(itemId);
}
void Inventory::removeItem(const std::string& itemId) {
auto it = std::find_if(items.begin(), items.end(),
[&itemId](const Item& item) { return item.id == itemId; });
if (it != items.end()) {
std::cout << "Item removed from inventory: " << it->name << std::endl;
auto it = std::find(itemIds.begin(), itemIds.end(), itemId);
if (it != itemIds.end()) {
std::cout << "Item removed from inventory: " << itemId << std::endl;
if (onItemRemoved) onItemRemoved(itemId);
items.erase(it);
itemIds.erase(it);
}
}
bool Inventory::hasItem(const std::string& itemId) const {
return std::find_if(items.begin(), items.end(),
[&itemId](const Item& item) { return item.id == itemId; }) != items.end();
return std::find(itemIds.begin(), itemIds.end(), itemId) != itemIds.end();
}
std::vector<Item> Inventory::getItems() const {
std::vector<Item> result;
result.reserve(itemIds.size());
for (const auto& id : itemIds) {
const Item* def = ItemRegistry::instance().findById(id);
if (def) result.push_back(*def);
}
return result;
}
void Inventory::save(nlohmann::json& out) const {
nlohmann::json arr = nlohmann::json::array();
for (const auto& item : items) {
arr.push_back({
{"id", item.id},
{"name", item.name},
{"description", item.description},
{"icon", item.icon},
{"selectedIcon", item.selectedIcon}
});
}
out["items"] = std::move(arr);
out["items"] = itemIds;
}
void Inventory::load(const nlohmann::json& in) {
items.clear();
itemIds.clear();
if (!in.contains("items") || !in["items"].is_array()) return;
for (const auto& j : in["items"]) {
Item item;
item.id = j.value("id", "");
item.name = j.value("name", "");
item.description = j.value("description", "");
item.icon = j.value("icon", "");
item.selectedIcon = j.value("selectedIcon", "");
items.push_back(std::move(item));
if (j.is_string()) {
itemIds.push_back(j.get<std::string>());
} else if (j.is_object()) {
// Backward-compat with old save files that stored full item structs.
const std::string id = j.value("id", "");
if (!id.empty()) itemIds.push_back(id);
}
}
}
+9 -5
View File
@@ -22,13 +22,14 @@ namespace ZL {
class Inventory : public ISaveable {
public:
std::vector<Item> items;
void addItem(const Item& item);
void addItem(const std::string& itemId);
void removeItem(const std::string& itemId);
const std::vector<Item>& getItems() const { return items; }
// Resolves current items from ItemRegistry by id, so returned data
// always reflects the currently loaded (localized) definitions.
std::vector<Item> getItems() const;
bool hasItem(const std::string& itemId) const;
void clear() { items.clear(); }
size_t getCount() const { return items.size(); }
void clear() { itemIds.clear(); }
size_t getCount() const { return itemIds.size(); }
void save(nlohmann::json& out) const override;
void load(const nlohmann::json& in) override;
@@ -36,6 +37,9 @@ namespace ZL {
// Called whenever an item is added/removed; receives the item id.
std::function<void(const std::string&)> onItemAdded;
std::function<void(const std::string&)> onItemRemoved;
private:
std::vector<std::string> itemIds;
};
} // namespace ZL
+4 -14
View File
@@ -1,5 +1,6 @@
#include "ItemRegistry.h"
#include "utils/Utils.h"
#include "Localization.h"
#include "external/nlohmann/json.hpp"
#include <iostream>
@@ -11,20 +12,9 @@ ItemRegistry& ItemRegistry::instance() {
}
void ItemRegistry::loadFromJson(const std::string& jsonPath, const std::string& zipPath) {
std::string content;
try {
if (zipPath.empty()) {
content = readTextFile(jsonPath);
} else {
auto buf = readFileFromZIP(jsonPath, zipPath);
if (buf.empty()) {
std::cerr << "[ItemRegistry] Failed to read " << jsonPath << " from zip\n";
return;
}
content.assign(buf.begin(), buf.end());
}
} catch (const std::exception& e) {
std::cerr << "[ItemRegistry] Failed to open " << jsonPath << ": " << e.what() << "\n";
const std::string content = readLocalizedConfigFile(jsonPath, zipPath);
if (content.empty()) {
std::cerr << "[ItemRegistry] Failed to read " << jsonPath << "\n";
return;
}
+17 -19
View File
@@ -1,6 +1,7 @@
#include "quest/QuestJournal.h"
#include "external/nlohmann/json.hpp"
#include "utils/Utils.h"
#include "Localization.h"
#include <algorithm>
#include <iostream>
@@ -26,26 +27,9 @@ static QuestStatus parseQuestStatus(const std::string& value) {
}
bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipFile) {
quests.clear();
questOrder.clear();
std::string content;
try {
if (zipFile.empty()) {
content = ZL::readTextFile(path);
}
else {
auto data = ZL::readFileFromZIP(path, zipFile);
content.assign(data.begin(), data.end());
}
}
catch (const std::exception& e) {
std::cerr << "[quest] Failed to read " << path << ": " << e.what() << std::endl;
return false;
}
const std::string content = ZL::readLocalizedConfigFile(path, zipFile);
if (content.empty()) {
std::cerr << "[quest] Empty quest file: " << path << std::endl;
std::cerr << "[quest] Failed to read " << path << std::endl;
return false;
}
@@ -63,6 +47,12 @@ bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipF
return false;
}
// Only clear existing definitions/state once the new file is confirmed
// valid, so a failed reload (e.g. a broken localized file) doesn't wipe
// the currently loaded quest journal.
quests.clear();
questOrder.clear();
for (const auto& item : root["quests"]) {
QuestDefinition def;
def.id = item.value("id", "");
@@ -101,6 +91,14 @@ bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipF
return true;
}
bool QuestJournal::reloadDefinitionsPreservingState(const std::string& path, const std::string& zipFile) {
json snapshot;
save(snapshot);
if (!loadFromFile(path, zipFile)) return false;
load(snapshot);
return true;
}
bool QuestJournal::setStatus(const std::string& questId, QuestStatus status) {
QuestState* quest = findQuest(questId);
if (!quest) return false;
+5
View File
@@ -14,6 +14,11 @@ class QuestJournal : public ZL::ISaveable {
public:
bool loadFromFile(const std::string& path, const std::string& zipFile = "");
// Reloads quest definitions (e.g. after a language switch) while
// preserving current progress: snapshots status/objective state, reloads
// definitions from path, then re-applies the snapshot.
bool reloadDefinitionsPreservingState(const std::string& path, const std::string& zipFile = "");
void save(nlohmann::json& out) const override;
void load(const nlohmann::json& in) override;