ppsspp/Common/Data/Text/I18n.cpp
2024-11-30 00:39:38 +01:00

187 lines
4.3 KiB
C++

#include <cstring>
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Format/IniFile.h"
#include "Common/File/VFS/VFS.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
static const char * const g_categoryNames[(size_t)I18NCat::CATEGORY_COUNT] = {
"Audio",
"Controls",
"CwCheats",
"DesktopUI",
"Developer",
"Dialog",
"Error",
"Game",
"Graphics",
"InstallZip",
"KeyMapping",
"MainMenu",
"MainSettings",
"MappableControls",
"Networking",
"Pause",
"PostShaders",
"PSPCredits",
"MemStick",
"RemoteISO",
"Reporting",
"Savedata",
"Screen",
"Search",
"Store",
"SysInfo",
"System",
"TextureShaders",
"Themes",
"UI Elements",
"Upgrade",
"VR",
"Achievements",
"PSPSettings",
};
I18NRepo g_i18nrepo;
std::string I18NRepo::LanguageID() {
return languageID_;
}
I18NRepo::I18NRepo() {
Clear();
}
void I18NRepo::Clear() {
std::lock_guard<std::mutex> guard(catsLock_);
for (auto &iter : cats_) {
// Initialize with empty categories, so that early lookups don't crash.
iter = std::make_shared<I18NCategory>();
}
}
I18NCategory::I18NCategory(const Section &section) {
std::map<std::string, std::string> sectionMap = section.ToMap();
SetMap(sectionMap);
}
void I18NCategory::Clear() {
map_.clear();
missedKeyLog_.clear();
}
std::string_view I18NCategory::T(std::string_view key, std::string_view def) {
auto iter = map_.find(key);
if (iter != map_.end()) {
return iter->second.text.c_str();
} else {
std::lock_guard<std::mutex> guard(missedKeyLock_);
std::string missedKey(key);
if (!def.empty())
missedKeyLog_[missedKey] = def;
else
missedKeyLog_[missedKey] = std::string(key);
return !def.empty() ? def : key;
}
}
const char *I18NCategory::T_cstr(const char *key, const char *def) {
auto iter = map_.find(key);
if (iter != map_.end()) {
return iter->second.text.c_str();
} else {
std::lock_guard<std::mutex> guard(missedKeyLock_);
std::string missedKey(key);
if (def)
missedKeyLog_[missedKey] = def;
else
missedKeyLog_[missedKey] = std::string(key);
return def ? def : key;
}
}
void I18NCategory::SetMap(const std::map<std::string, std::string> &m) {
for (const auto &[key, value] : m) {
if (map_.find(key) == map_.end()) {
std::string text = ReplaceAll(value, "\\n", "\n");
_dbg_assert_(key.find('\n') == std::string::npos);
map_[key] = I18NEntry(text);
}
}
}
std::map<std::string, std::string> I18NCategory::Missed() const {
std::lock_guard<std::mutex> guard(missedKeyLock_);
return missedKeyLog_;
}
std::shared_ptr<I18NCategory> I18NRepo::GetCategory(I18NCat category) {
std::lock_guard<std::mutex> guard(catsLock_);
if (category != I18NCat::NONE)
return cats_[(size_t)category];
else
return nullptr;
}
Path I18NRepo::GetIniPath(const std::string &languageID) const {
return Path("lang") / (languageID + ".ini");
}
bool I18NRepo::IniExists(const std::string &languageID) const {
File::FileInfo info;
if (!g_VFS.Exists(GetIniPath(languageID).ToString().c_str()))
return false;
return true;
}
bool I18NRepo::LoadIni(const std::string &languageID, const Path &overridePath) {
IniFile ini;
Path iniPath;
// INFO_LOG(Log::System, "Loading lang ini %s", iniPath.c_str());
if (!overridePath.empty()) {
iniPath = overridePath / (languageID + ".ini");
} else {
iniPath = GetIniPath(languageID);
}
if (!ini.LoadFromVFS(g_VFS, iniPath.ToString()))
return false;
Clear();
const std::vector<std::unique_ptr<Section>> &sections = ini.Sections();
std::lock_guard<std::mutex> guard(catsLock_);
for (auto &section : sections) {
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) {
if (!strcmp(section->name().c_str(), g_categoryNames[i])) {
cats_[i].reset(new I18NCategory(*section.get()));
}
}
}
languageID_ = languageID;
return true;
}
void I18NRepo::LogMissingKeys() const {
std::lock_guard<std::mutex> guard(catsLock_);
for (size_t i = 0; i < (size_t)I18NCat::CATEGORY_COUNT; i++) {
auto &cat = cats_[i];
for (auto &key : cat->Missed()) {
INFO_LOG(Log::System, "Missing translation [%s]: %s (%s)", g_categoryNames[i], key.first.c_str(), key.second.c_str());
}
}
}
std::shared_ptr<I18NCategory> GetI18NCategory(I18NCat category) {
if (category == I18NCat::NONE) {
return std::shared_ptr<I18NCategory>();
}
std::shared_ptr<I18NCategory> cat = g_i18nrepo.GetCategory(category);
_dbg_assert_(cat);
return cat;
}