mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-31 22:19:44 +01:00
Add LocalizationMaint.cpp/h (not compiled yet)
This commit is part of rewritten history of the localization branch. The original (unsquashed) commit history can be found here: https://github.com/Dav999-v/VVVVVV/tree/localization-orig
This commit is contained in:
parent
8e33815c97
commit
97d905e8bf
2 changed files with 415 additions and 0 deletions
398
desktop_version/src/LocalizationMaint.cpp
Normal file
398
desktop_version/src/LocalizationMaint.cpp
Normal file
|
@ -0,0 +1,398 @@
|
|||
#define LOCALIZATIONMAINT_CPP
|
||||
#include "Localization.h"
|
||||
#include "LocalizationStorage.h"
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#include "FileSystemUtils.h"
|
||||
#include "Graphics.h"
|
||||
#include "Vlogging.h"
|
||||
#include "XMLUtils.h"
|
||||
|
||||
|
||||
namespace loc
|
||||
{
|
||||
|
||||
static void sync_lang_file(const std::string& langcode)
|
||||
{
|
||||
/* Update translation files for the given language with new strings from templates.
|
||||
* This basically takes the (English) templates, fills in existing translations, and saves.
|
||||
* Any FILESYSTEM_saveTiXml2Document() writes to main lang dir */
|
||||
vlog_info("Syncing %s with templates...", langcode.c_str());
|
||||
|
||||
lang = langcode;
|
||||
loadtext(false);
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
tinyxml2::XMLHandle hDoc(&doc);
|
||||
tinyxml2::XMLElement* pElem;
|
||||
tinyxml2::XMLElement* subElem;
|
||||
|
||||
if (load_lang_doc("meta", doc, "en"))
|
||||
{
|
||||
FOR_EACH_XML_ELEMENT(hDoc, pElem)
|
||||
{
|
||||
const char* pKey = pElem->Value();
|
||||
|
||||
if (SDL_strcmp(pKey, "active") == 0)
|
||||
pElem->SetText((int) langmeta.active);
|
||||
else if (SDL_strcmp(pKey, "nativename") == 0)
|
||||
pElem->SetText(langmeta.nativename.c_str());
|
||||
else if (SDL_strcmp(pKey, "credit") == 0)
|
||||
pElem->SetText(langmeta.credit.c_str());
|
||||
else if (SDL_strcmp(pKey, "action_hint") == 0)
|
||||
pElem->SetText(langmeta.action_hint.c_str());
|
||||
else if (SDL_strcmp(pKey, "autowordwrap") == 0)
|
||||
pElem->SetText((int) langmeta.autowordwrap);
|
||||
else if (SDL_strcmp(pKey, "toupper") == 0)
|
||||
pElem->SetText((int) langmeta.toupper);
|
||||
else if (SDL_strcmp(pKey, "toupper_i_dot") == 0)
|
||||
pElem->SetText((int) langmeta.toupper_i_dot);
|
||||
else if (SDL_strcmp(pKey, "toupper_lower_escape_char") == 0)
|
||||
pElem->SetText((int) langmeta.toupper_lower_escape_char);
|
||||
else if (SDL_strcmp(pKey, "menu_select") == 0)
|
||||
pElem->SetText(langmeta.menu_select.c_str());
|
||||
else if (SDL_strcmp(pKey, "menu_select_tight") == 0)
|
||||
pElem->SetText(langmeta.menu_select_tight.c_str());
|
||||
}
|
||||
|
||||
/* This part exists because we want to preserve blank lines between the commented
|
||||
* options for clarity, so we have to take matters into our own hands. */
|
||||
for (
|
||||
tinyxml2::XMLNode* pNode = hDoc.FirstChildElement().FirstChild().ToNode();
|
||||
pNode != NULL;
|
||||
pNode = pNode->NextSibling()
|
||||
)
|
||||
{
|
||||
tinyxml2::XMLComment* pCom = pNode->ToComment();
|
||||
if (pCom != NULL)
|
||||
{
|
||||
tinyxml2::XMLNode* pPrevNode = pCom->PreviousSibling();
|
||||
if (pPrevNode != NULL)
|
||||
{
|
||||
doc.FirstChildElement()->InsertAfterChild(pPrevNode, doc.NewText("\n\n "));
|
||||
}
|
||||
doc.FirstChildElement()->InsertAfterChild(pCom, doc.NewText("\n "));
|
||||
}
|
||||
}
|
||||
|
||||
FILESYSTEM_saveTiXml2Document((langcode + "/meta.xml").c_str(), doc);
|
||||
}
|
||||
|
||||
if (load_lang_doc("strings", doc, "en"))
|
||||
{
|
||||
FOR_EACH_XML_ELEMENT(hDoc, pElem)
|
||||
{
|
||||
EXPECT_ELEM(pElem, "string");
|
||||
|
||||
const char* eng = pElem->Attribute("english");
|
||||
if (eng != NULL)
|
||||
{
|
||||
pElem->SetAttribute("translation", map_lookup_text(map_translation, eng, ""));
|
||||
}
|
||||
}
|
||||
|
||||
FILESYSTEM_saveTiXml2Document((langcode + "/strings.xml").c_str(), doc);
|
||||
}
|
||||
|
||||
if (load_lang_doc("strings_plural", doc, "en"))
|
||||
{
|
||||
/* Form 255 is technically invalid, but we have to account for it */
|
||||
bool form_id_used[256];
|
||||
SDL_zeroa(form_id_used);
|
||||
for (int num = 0; num < 200; num++)
|
||||
{
|
||||
form_id_used[number_plural_form[num]] = true;
|
||||
}
|
||||
|
||||
FOR_EACH_XML_ELEMENT(hDoc, pElem)
|
||||
{
|
||||
EXPECT_ELEM(pElem, "string");
|
||||
|
||||
pElem->DeleteChildren();
|
||||
|
||||
const char* eng_plural = pElem->Attribute("english_plural");
|
||||
|
||||
for (int form_id = 0; form_id < 255; form_id++)
|
||||
{
|
||||
if (form_id_used[form_id] && eng_plural != NULL)
|
||||
{
|
||||
subElem = doc.NewElement("translation");
|
||||
pElem->LinkEndChild(subElem);
|
||||
|
||||
subElem->SetAttribute("form", form_id);
|
||||
|
||||
char* key = add_disambiguator(form_id+1, eng_plural, NULL);
|
||||
if (key == NULL)
|
||||
{
|
||||
/* Are we out of memory? Stop, don't blank our language files... */
|
||||
return;
|
||||
}
|
||||
|
||||
subElem->SetAttribute("translation", map_lookup_text(map_translation_plural, key, ""));
|
||||
|
||||
SDL_free(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FILESYSTEM_saveTiXml2Document((langcode + "/strings_plural.xml").c_str(), doc);
|
||||
}
|
||||
|
||||
if (load_lang_doc("cutscenes", doc, "en"))
|
||||
{
|
||||
FOR_EACH_XML_ELEMENT(hDoc, pElem)
|
||||
{
|
||||
EXPECT_ELEM(pElem, "cutscene");
|
||||
|
||||
const char* cutscene_id = pElem->Attribute("id");
|
||||
if (cutscene_id == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hashmap* map = map_translation_cutscene;
|
||||
|
||||
uintptr_t ptr_cutscene_map;
|
||||
bool found = hashmap_get(map, (void*) cutscene_id, SDL_strlen(cutscene_id), &ptr_cutscene_map);
|
||||
hashmap* cutscene_map = (hashmap*) ptr_cutscene_map;
|
||||
if (!found || cutscene_map == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FOR_EACH_XML_SUB_ELEMENT(pElem, subElem)
|
||||
{
|
||||
EXPECT_ELEM(subElem, "dialogue");
|
||||
|
||||
const char* eng = subElem->Attribute("english");
|
||||
if (eng == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t alloc_len;
|
||||
const std::string eng_unwrapped = graphics.string_unwordwrap(eng);
|
||||
char* eng_prefixed = add_disambiguator(subElem->UnsignedAttribute("case", 1), eng_unwrapped.c_str(), &alloc_len);
|
||||
if (eng_prefixed == NULL)
|
||||
{
|
||||
/* Out of memory or something, stop */
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t ptr_format;
|
||||
found = hashmap_get(cutscene_map, (void*) eng_prefixed, alloc_len-1, &ptr_format);
|
||||
const TextboxFormat* format = (TextboxFormat*) ptr_format;
|
||||
|
||||
SDL_free(eng_prefixed);
|
||||
|
||||
if (!found || format == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
subElem->DeleteAttribute("tt");
|
||||
subElem->DeleteAttribute("wraplimit");
|
||||
subElem->DeleteAttribute("centertext");
|
||||
subElem->DeleteAttribute("pad");
|
||||
subElem->DeleteAttribute("pad_left");
|
||||
subElem->DeleteAttribute("pad_right");
|
||||
subElem->DeleteAttribute("padtowidth");
|
||||
|
||||
if (format->text != NULL)
|
||||
subElem->SetAttribute("translation", format->text);
|
||||
if (format->tt)
|
||||
subElem->SetAttribute("tt", 1);
|
||||
if (format->wraplimit_raw != 0)
|
||||
subElem->SetAttribute("wraplimit", format->wraplimit_raw);
|
||||
if (format->centertext)
|
||||
subElem->SetAttribute("centertext", 1);
|
||||
if (format->pad_left == format->pad_right && format->pad_left != 0)
|
||||
{
|
||||
subElem->SetAttribute("pad", format->pad_left);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (format->pad_left != 0)
|
||||
subElem->SetAttribute("pad_left", format->pad_left);
|
||||
if (format->pad_right != 0)
|
||||
subElem->SetAttribute("pad_right", format->pad_right);
|
||||
}
|
||||
if (format->padtowidth != 0)
|
||||
subElem->SetAttribute("padtowidth", format->padtowidth);
|
||||
}
|
||||
}
|
||||
|
||||
FILESYSTEM_saveTiXml2Document((langcode + "/cutscenes.xml").c_str(), doc);
|
||||
}
|
||||
|
||||
if (load_lang_doc("roomnames", doc, "en"))
|
||||
{
|
||||
FOR_EACH_XML_ELEMENT(hDoc, pElem)
|
||||
{
|
||||
EXPECT_ELEM(pElem, "roomname");
|
||||
|
||||
pElem->SetAttribute("translation",
|
||||
get_roomname_translation(false, pElem->UnsignedAttribute("x"), pElem->UnsignedAttribute("y"))
|
||||
);
|
||||
}
|
||||
|
||||
FILESYSTEM_saveTiXml2Document((langcode + "/roomnames.xml").c_str(), doc);
|
||||
}
|
||||
|
||||
if (load_lang_doc("roomnames_special", doc, "en"))
|
||||
{
|
||||
FOR_EACH_XML_ELEMENT(hDoc, pElem)
|
||||
{
|
||||
EXPECT_ELEM(pElem, "roomname");
|
||||
|
||||
const char* eng = pElem->Attribute("english");
|
||||
if (eng != NULL)
|
||||
{
|
||||
pElem->SetAttribute("translation", map_lookup_text(map_translation_roomnames_special, eng, ""));
|
||||
}
|
||||
}
|
||||
|
||||
FILESYSTEM_saveTiXml2Document((langcode + "/roomnames_special.xml").c_str(), doc);
|
||||
}
|
||||
}
|
||||
|
||||
bool sync_lang_files(void)
|
||||
{
|
||||
/* Returns false if we can't set the lang write dir, true otherwise.
|
||||
* This could maybe be extended with better error reporting,
|
||||
* problem is getting across which files failed in which languages. */
|
||||
std::string oldlang = lang;
|
||||
if (!FILESYSTEM_setLangWriteDir())
|
||||
{
|
||||
vlog_error("Cannot set write dir to lang dir, not syncing language files");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < languagelist.size(); i++)
|
||||
{
|
||||
if (languagelist[i].code != "en")
|
||||
sync_lang_file(languagelist[i].code);
|
||||
}
|
||||
|
||||
FILESYSTEM_restoreWriteDir();
|
||||
lang = oldlang;
|
||||
loadtext(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool save_roomname_to_file(const std::string& langcode, bool custom_level, int roomx, int roomy, const char* tra, const char* explanation)
|
||||
{
|
||||
if (custom_level)
|
||||
{
|
||||
vlog_error("Saving custom level room names not implemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fix_room_coords(custom_level, &roomx, &roomy))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
tinyxml2::XMLHandle hDoc(&doc);
|
||||
tinyxml2::XMLElement* pElem;
|
||||
|
||||
if (!load_lang_doc("roomnames", doc, langcode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
FOR_EACH_XML_ELEMENT(hDoc, pElem)
|
||||
{
|
||||
EXPECT_ELEM(pElem, "roomname");
|
||||
|
||||
int x = pElem->IntAttribute("x", -1);
|
||||
int y = pElem->IntAttribute("y", -1);
|
||||
|
||||
if (x == roomx && y == roomy)
|
||||
{
|
||||
if (explanation != NULL)
|
||||
{
|
||||
pElem->SetAttribute("explanation", explanation);
|
||||
}
|
||||
if (tra != NULL)
|
||||
{
|
||||
pElem->SetAttribute("translation", tra);
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
vlog_error("Could not find room %d,%d in language file to replace!", roomx, roomy);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FILESYSTEM_setLangWriteDir())
|
||||
{
|
||||
vlog_error("Cannot set write dir to lang dir, so room name can't be saved");
|
||||
return false;
|
||||
}
|
||||
bool save_success = FILESYSTEM_saveTiXml2Document((langcode + "/roomnames.xml").c_str(), doc);
|
||||
FILESYSTEM_restoreWriteDir();
|
||||
if (!save_success)
|
||||
{
|
||||
vlog_error("Could not write roomnames document!");
|
||||
return false;
|
||||
}
|
||||
return store_roomname_translation(custom_level, roomx, roomy, tra, explanation);
|
||||
}
|
||||
|
||||
bool save_roomname_explanation_to_files(bool custom_level, int roomx, int roomy, const char* explanation)
|
||||
{
|
||||
bool success = true;
|
||||
for (size_t i = 0; i < languagelist.size(); i++)
|
||||
{
|
||||
if (!save_roomname_to_file(languagelist[i].code, custom_level, roomx, roomy, NULL, explanation))
|
||||
{
|
||||
success = false;
|
||||
vlog_warn("Could not save room name explanation to language %s", languagelist[i].code.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return !languagelist.empty() && success;
|
||||
}
|
||||
|
||||
void local_limits_check(void)
|
||||
{
|
||||
text_overflows.clear();
|
||||
loadtext(true);
|
||||
limitscheck_current_overflow = 0;
|
||||
}
|
||||
|
||||
void global_limits_check(void)
|
||||
{
|
||||
text_overflows.clear();
|
||||
|
||||
std::string oldlang = lang;
|
||||
|
||||
textbook_clear(&textbook_main);
|
||||
textbook_set_protected(&textbook_main, true);
|
||||
|
||||
for (size_t i = 0; i < languagelist.size(); i++)
|
||||
{
|
||||
if (languagelist[i].code != "en")
|
||||
{
|
||||
lang = languagelist[i].code;
|
||||
loadtext(true);
|
||||
}
|
||||
}
|
||||
|
||||
lang = oldlang;
|
||||
loadtext(false);
|
||||
|
||||
textbook_set_protected(&textbook_main, false);
|
||||
|
||||
limitscheck_current_overflow = 0;
|
||||
}
|
||||
|
||||
} /* namespace loc */
|
17
desktop_version/src/LocalizationMaint.h
Normal file
17
desktop_version/src/LocalizationMaint.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef LOCALIZATIONMAINT_H
|
||||
#define LOCALIZATIONMAINT_H
|
||||
|
||||
namespace loc
|
||||
{
|
||||
|
||||
bool sync_lang_files(void);
|
||||
|
||||
bool save_roomname_to_file(const std::string& langcode, bool custom_level, int roomx, int roomy, const char* tra, const char* explanation);
|
||||
bool save_roomname_explanation_to_files(bool custom_level, int roomx, int roomy, const char* explanation);
|
||||
|
||||
void local_limits_check(void);
|
||||
void global_limits_check(void);
|
||||
|
||||
} /* namespace loc */
|
||||
|
||||
#endif /* LOCALIZATIONMAINT_H */
|
Loading…
Reference in a new issue