Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Data/Text/I18n.cpp
3187 views
1
#include <cstring>
2
3
#include "Common/Data/Text/I18n.h"
4
#include "Common/Data/Format/IniFile.h"
5
#include "Common/File/VFS/VFS.h"
6
#include "Common/Log.h"
7
8
#include "Common/StringUtils.h"
9
10
// Don't forget to update the constants in the header file if you change this.
11
static const char * const g_categoryNames[(size_t)I18NCat::CATEGORY_COUNT] = {
12
"Audio",
13
"Controls",
14
"CwCheats",
15
"DesktopUI",
16
"Developer",
17
"Dialog",
18
"Error",
19
"Game",
20
"Graphics",
21
"InstallZip",
22
"KeyMapping",
23
"MainMenu",
24
"MainSettings",
25
"MappableControls",
26
"Networking",
27
"Pause",
28
"PostShaders",
29
"PSPCredits",
30
"MemStick",
31
"RemoteISO",
32
"Reporting",
33
"Savedata",
34
"Screen",
35
"Search",
36
"Store",
37
"SysInfo",
38
"System",
39
"TextureShaders",
40
"Themes",
41
"UI Elements",
42
"VR",
43
"Achievements",
44
"PSPSettings",
45
};
46
47
I18NRepo g_i18nrepo;
48
49
std::string I18NRepo::LanguageID() {
50
return languageID_;
51
}
52
53
I18NRepo::I18NRepo() {
54
Clear();
55
}
56
57
void I18NRepo::Clear() {
58
std::lock_guard<std::mutex> guard(catsLock_);
59
for (auto &iter : cats_) {
60
// Initialize with empty categories, so that early lookups don't crash.
61
iter = std::make_shared<I18NCategory>();
62
}
63
}
64
65
I18NCategory::I18NCategory(const Section &section) {
66
std::map<std::string, std::string> sectionMap = section.ToMap();
67
SetMap(sectionMap);
68
}
69
70
void I18NCategory::Clear() {
71
map_.clear();
72
missedKeyLog_.clear();
73
}
74
75
std::string_view I18NCategory::T(std::string_view key, std::string_view def) {
76
auto iter = map_.find(key);
77
if (iter != map_.end()) {
78
return iter->second.text.c_str();
79
} else {
80
std::lock_guard<std::mutex> guard(missedKeyLock_);
81
std::string missedKey(key);
82
if (!def.empty())
83
missedKeyLog_[missedKey] = def;
84
else
85
missedKeyLog_[missedKey] = std::string(key);
86
return !def.empty() ? def : key;
87
}
88
}
89
90
const char *I18NCategory::T_cstr(const char *key, const char *def) {
91
auto iter = map_.find(key);
92
if (iter != map_.end()) {
93
return iter->second.text.c_str();
94
} else {
95
std::lock_guard<std::mutex> guard(missedKeyLock_);
96
std::string missedKey(key);
97
if (def)
98
missedKeyLog_[missedKey] = def;
99
else
100
missedKeyLog_[missedKey] = std::string(key);
101
return def ? def : key;
102
}
103
}
104
105
void I18NCategory::SetMap(const std::map<std::string, std::string> &m) {
106
for (const auto &[key, value] : m) {
107
if (map_.find(key) == map_.end()) {
108
std::string text = ReplaceAll(value, "\\n", "\n");
109
_dbg_assert_(key.find('\n') == std::string::npos);
110
map_[key] = I18NEntry(text);
111
}
112
}
113
}
114
115
std::map<std::string, std::string> I18NCategory::Missed() const {
116
std::lock_guard<std::mutex> guard(missedKeyLock_);
117
return missedKeyLog_;
118
}
119
120
std::shared_ptr<I18NCategory> I18NRepo::GetCategory(I18NCat category) {
121
std::lock_guard<std::mutex> guard(catsLock_);
122
if (category != I18NCat::NONE)
123
return cats_[(size_t)category];
124
else
125
return nullptr;
126
}
127
128
Path I18NRepo::GetIniPath(const std::string &languageID) const {
129
return Path("lang") / (languageID + ".ini");
130
}
131
132
bool I18NRepo::IniExists(const std::string &languageID) const {
133
File::FileInfo info;
134
if (!g_VFS.Exists(GetIniPath(languageID).ToString().c_str()))
135
return false;
136
return true;
137
}
138
139
bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath) {
140
IniFile ini;
141
Path iniPath;
142
143
// INFO_LOG(Log::System, "Loading lang ini %s", iniPath.c_str());
144
if (!overridePath.empty()) {
145
iniPath = overridePath / (languageID + ".ini");
146
} else {
147
iniPath = GetIniPath(languageID);
148
}
149
150
if (!ini.LoadFromVFS(g_VFS, iniPath.ToString()))
151
return false;
152
153
Clear();
154
155
const std::vector<std::unique_ptr<Section>> &sections = ini.Sections();
156
157
std::lock_guard<std::mutex> guard(catsLock_);
158
for (auto &section : sections) {
159
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) {
160
if (!strcmp(section->name().c_str(), g_categoryNames[i])) {
161
cats_[i].reset(new I18NCategory(*section.get()));
162
}
163
}
164
}
165
166
languageID_ = languageID;
167
return true;
168
}
169
170
void I18NRepo::LogMissingKeys() const {
171
std::lock_guard<std::mutex> guard(catsLock_);
172
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) {
173
auto &cat = cats_[i];
174
for (auto &key : cat->Missed()) {
175
INFO_LOG(Log::System, "Missing translation [%s]: %s (%s)", g_categoryNames[i], key.first.c_str(), key.second.c_str());
176
}
177
}
178
}
179
180
std::shared_ptr<I18NCategory> GetI18NCategory(I18NCat category) {
181
if (category == I18NCat::NONE) {
182
return std::shared_ptr<I18NCategory>();
183
}
184
std::shared_ptr<I18NCategory> cat = g_i18nrepo.GetCategory(category);
185
_dbg_assert_(cat);
186
return cat;
187
}
188
189