diff options
Diffstat (limited to 'core/src/Utils/I18n.cpp')
-rw-r--r-- | core/src/Utils/I18n.cpp | 300 |
1 files changed, 0 insertions, 300 deletions
diff --git a/core/src/Utils/I18n.cpp b/core/src/Utils/I18n.cpp deleted file mode 100644 index e5131cc..0000000 --- a/core/src/Utils/I18n.cpp +++ /dev/null @@ -1,300 +0,0 @@ -#include "I18n.hpp" - -#include <json/reader.h> -#include <json/value.h> -#include <tsl/array_map.h> -#include <filesystem> -#include <fstream> -#include <stdexcept> -#include <string_view> -#include <utility> - -namespace fs = std::filesystem; -using namespace std::literals::string_view_literals; - -namespace { - -struct LanguageInfo -{ - std::string CodeName; - std::string LocaleName; - fs::path File; -}; - -class I18nState -{ -public: - static I18nState& Get() - { - static I18nState instance; - return instance; - } - -public: - tsl::array_map<char, LanguageInfo> LocaleInfos; - tsl::array_map<char, std::string> CurrentEntries; - LanguageInfo* CurrentLanguage = nullptr; - bool Unloaded = false; - - void Unload() - { - Unloaded = true; - CurrentEntries = {}; - } - - void EnsureLoaded() - { - if (Unloaded) { - Unloaded = false; - Reload(); - } - } - - void Reload() - { - if (!CurrentLanguage) return; - - std::ifstream ifs(CurrentLanguage->File); - Json::Value root; - ifs >> root; - - for (auto name : root.getMemberNames()) { - if (name == "$localized_name") { - continue; - } - - auto& value = root[name]; - if (value.isString()) { - CurrentEntries.insert(name, value.asCString()); - } - } - } -}; - -std::string FindLocalizedName(const fs::path& localeFile) -{ - std::ifstream ifs(localeFile); - if (!ifs) { - throw std::runtime_error("Failed to open locale file."); - } - - Json::Value root; - ifs >> root; - if (auto& name = root["$localized_name"]; name.isString()) { - return std::string(name.asCString()); - } else { - throw std::runtime_error("Failed to find $localized_name in language file."); - } -} - -} // namespace - -void I18n::Init() -{ - auto& state = I18nState::Get(); - - auto dir = fs::current_path() / "locale"; - if (!fs::exists(dir)) { - throw std::runtime_error("Failed to find locale directory."); - } - - for (auto& elm : fs::directory_iterator{ dir }) { - if (!elm.is_regular_file()) continue; - - auto& path = elm.path(); - auto codeName = path.stem().string(); - - state.LocaleInfos.emplace( - codeName, - LanguageInfo{ - .CodeName = codeName, - .LocaleName = FindLocalizedName(path), - .File = path, - }); - } -} - -void I18n::Shutdown() -{ - auto& state = I18nState::Get(); - state.LocaleInfos.clear(); - state.CurrentEntries.clear(); - state.CurrentLanguage = nullptr; - state.Unloaded = false; -} - -void I18n::Unload() -{ - auto& state = I18nState::Get(); - state.Unload(); - OnUnload(); -} - -std::string_view I18n::GetLanguage() -{ - auto& state = I18nState::Get(); - return state.CurrentLanguage->CodeName; -} - -bool I18n::SetLanguage(std::string_view lang) -{ - auto& state = I18nState::Get(); - if (state.CurrentLanguage && - state.CurrentLanguage->CodeName == lang) - { - return false; - } - - if (auto iter = state.LocaleInfos.find(lang); iter != state.LocaleInfos.end()) { - state.CurrentLanguage = &iter.value(); - state.Reload(); - } - - OnLanguageChange(); - return true; -} - -std::optional<std::string_view> I18n::Lookup(std::string_view key) -{ - auto& state = I18nState::Get(); - state.EnsureLoaded(); - - auto iter = state.CurrentEntries.find(key); - if (iter != state.CurrentEntries.end()) { - return iter.value(); - } else { - return std::nullopt; - } -} - -std::string_view I18n::LookupUnwrap(std::string_view key) -{ - auto o = Lookup(key); - if (!o) { - std::string msg; - msg.append("Unable to find locale for '"); - msg.append(key); - msg.append("'."); - throw std::runtime_error(std::move(msg)); - }; - return o.value(); -} - -std::string_view I18n::LookupLanguage(std::string_view lang) -{ - auto& state = I18nState::Get(); - auto iter = state.LocaleInfos.find(lang); - if (iter != state.LocaleInfos.end()) { - return iter.value().LocaleName; - } else { - return ""sv; - } -} - -BasicTranslation::BasicTranslation(std::string_view key) - : mContent{ I18n::LookupUnwrap(key) } -{ -} - -const std::string& BasicTranslation::GetString() const -{ - return mContent; -} - -const char* BasicTranslation::Get() const -{ - return mContent.c_str(); -} - -FormattedTranslation::FormattedTranslation(std::string_view key) -{ - auto src = I18n::LookupUnwrap(key); - - mMinimumResultLen = 0; - - bool escape = false; - bool matchingCloseBrace = false; - std::string buf; - for (char c : src) { - switch (c) { - case '\\': { - // Disallow double (or more) escaping - if (escape) { - buf += '\\'; - escape = false; - break; - } - - escape = true; - } break; - - case '{': { - // Escaping an opening brace cause the whole "argument" (if any) gets parsed as a part of the previous literal - if (escape) { - buf += '{'; - escape = false; - break; - } - - // Generate literal - mMinimumResultLen += buf.size(); - mParsedElements.push_back(Element{ std::move(buf) }); // Should also clear buf - - matchingCloseBrace = true; - } break; - case '}': { - if (escape) { - throw std::runtime_error("Cannot escape '}', put \\ before the '{' if intended to escape braces."); - } - - // If there is no pairing '{', simply treat this as a normal character - // (escaping for closing braces) - if (!matchingCloseBrace) { - buf += '}'; - break; - } - - // Generate argument - if (buf.empty()) { - // No index given, default to use current argument's index - auto currArgIdx = (int)mNumArguments; - mParsedElements.push_back(Element{ currArgIdx }); - } else { - // Use provided index - int argIdx = std::stoi(buf); - mParsedElements.push_back(Element{ argIdx }); - buf.clear(); - } - } break; - - default: { - if (escape) { - throw std::runtime_error("Cannot escape normal character '" + std::to_string(c) + "'."); - } - - buf += c; - } break; - } - } -} - -std::string FormattedTranslation::Format(std::span<Argument> args) -{ - if (args.size() != mNumArguments) { - throw std::runtime_error("Invalid number of arguments for FormattedTranslation::Format, expected " + std::to_string(mNumArguments) + " but found " + std::to_string(args.size()) + "."); - } - - std::string result; - result.reserve(mMinimumResultLen); - - for (auto& elm : mParsedElements) { - if (auto literal = std::get_if<std::string>(&elm)) { - result.append(*literal); - } - if (auto idx = std::get_if<int>(&elm)) { - result.append(args[*idx]); - } - } - - return result; -} |