1 #include "Common/Data/Text/I18n.h"
2 #include "Common/Data/Format/IniFile.h"
3 #include "Common/File/VFS/VFS.h"
4 
5 #include "Common/StringUtils.h"
6 
7 I18NRepo i18nrepo;
8 
~I18NRepo()9 I18NRepo::~I18NRepo() {
10 	Clear();
11 }
12 
LanguageID()13 std::string I18NRepo::LanguageID() {
14 	return languageID_;
15 }
16 
Clear()17 void I18NRepo::Clear() {
18 	std::lock_guard<std::mutex> guard(catsLock_);
19 	for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
20 		iter->second.reset();
21 	}
22 	cats_.clear();
23 }
24 
T(const char * key,const char * def)25 const char *I18NCategory::T(const char *key, const char *def) {
26 	if (!key) {
27 		return "ERROR";
28 	}
29 	// Replace the \n's with \\n's so that key values with newlines will be found correctly.
30 	std::string modifiedKey = key;
31 	modifiedKey = ReplaceAll(modifiedKey, "\n", "\\n");
32 
33 	auto iter = map_.find(modifiedKey);
34 	if (iter != map_.end()) {
35 //		INFO_LOG(SYSTEM, "translation key found in %s: %s", name_.c_str(), key);
36 		return iter->second.text.c_str();
37 	} else {
38 		std::lock_guard<std::mutex> guard(missedKeyLock_);
39 		if (def)
40 			missedKeyLog_[key] = def;
41 		else
42 			missedKeyLog_[key] = modifiedKey.c_str();
43 //		INFO_LOG(SYSTEM, "Missed translation key in %s: %s", name_.c_str(), key);
44 		return def ? def : key;
45 	}
46 }
47 
SetMap(const std::map<std::string,std::string> & m)48 void I18NCategory::SetMap(const std::map<std::string, std::string> &m) {
49 	for (auto iter = m.begin(); iter != m.end(); ++iter) {
50 		if (map_.find(iter->first) == map_.end()) {
51 			std::string text = ReplaceAll(iter->second, "\\n", "\n");
52 			map_[iter->first] = I18NEntry(text);
53 //			INFO_LOG(SYSTEM, "Language entry: %s -> %s", iter->first.c_str(), text.c_str());
54 		}
55 	}
56 }
57 
GetCategory(const char * category)58 std::shared_ptr<I18NCategory> I18NRepo::GetCategory(const char *category) {
59 	std::lock_guard<std::mutex> guard(catsLock_);
60 	auto iter = cats_.find(category);
61 	if (iter != cats_.end()) {
62 		return iter->second;
63 	} else {
64 		I18NCategory *c = new I18NCategory(this, category);
65 		cats_[category].reset(c);
66 		return cats_[category];
67 	}
68 }
69 
GetIniPath(const std::string & languageID) const70 std::string I18NRepo::GetIniPath(const std::string &languageID) const {
71 	return "lang/" + languageID + ".ini";
72 }
73 
IniExists(const std::string & languageID) const74 bool I18NRepo::IniExists(const std::string &languageID) const {
75 	File::FileInfo info;
76 	if (!VFSGetFileInfo(GetIniPath(languageID).c_str(), &info))
77 		return false;
78 	if (!info.exists)
79 		return false;
80 	return true;
81 }
82 
LoadIni(const std::string & languageID,const Path & overridePath)83 bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath) {
84 	IniFile ini;
85 	Path iniPath;
86 
87 //	INFO_LOG(SYSTEM, "Loading lang ini %s", iniPath.c_str());
88 	if (!overridePath.empty()) {
89 		iniPath = overridePath / (languageID + ".ini");
90 	} else {
91 		iniPath = Path(GetIniPath(languageID));
92 	}
93 
94 	if (!ini.LoadFromVFS(iniPath.ToString()))
95 		return false;
96 
97 	Clear();
98 
99 	const std::vector<Section> &sections = ini.Sections();
100 
101 	std::lock_guard<std::mutex> guard(catsLock_);
102 	for (auto iter = sections.begin(); iter != sections.end(); ++iter) {
103 		if (iter->name() != "") {
104 			cats_[iter->name()].reset(LoadSection(&(*iter), iter->name().c_str()));
105 		}
106 	}
107 
108 	languageID_ = languageID;
109 	return true;
110 }
111 
LoadSection(const Section * section,const char * name)112 I18NCategory *I18NRepo::LoadSection(const Section *section, const char *name) {
113 	I18NCategory *cat = new I18NCategory(this, name);
114 	std::map<std::string, std::string> sectionMap = section->ToMap();
115 	cat->SetMap(sectionMap);
116 	return cat;
117 }
118 
119 // This is a very light touched save variant - it won't overwrite
120 // anything, only create new entries.
SaveIni(const std::string & languageID)121 void I18NRepo::SaveIni(const std::string &languageID) {
122 	IniFile ini;
123 	ini.Load(GetIniPath(languageID));
124 	std::lock_guard<std::mutex> guard(catsLock_);
125 	for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
126 		std::string categoryName = iter->first;
127 		Section *section = ini.GetOrCreateSection(categoryName.c_str());
128 		SaveSection(ini, section, iter->second);
129 	}
130 	ini.Save(GetIniPath(languageID));
131 }
132 
SaveSection(IniFile & ini,Section * section,std::shared_ptr<I18NCategory> cat)133 void I18NRepo::SaveSection(IniFile &ini, Section *section, std::shared_ptr<I18NCategory> cat) {
134 	const std::map<std::string, std::string> &missed = cat->Missed();
135 
136 	for (auto iter = missed.begin(); iter != missed.end(); ++iter) {
137 		if (!section->Exists(iter->first.c_str())) {
138 			std::string text = ReplaceAll(iter->second, "\n", "\\n");
139 			section->Set(iter->first, text);
140 		}
141 	}
142 
143 	const std::map<std::string, I18NEntry> &entries = cat->GetMap();
144 	for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
145 		std::string text = ReplaceAll(iter->second.text, "\n", "\\n");
146 		section->Set(iter->first, text);
147 	}
148 
149 	cat->ClearMissed();
150 }
151