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> §ions = 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