#include "pch.h" #include "utils.h" namespace Localization { using Key = uint16_t; // Maps localization keys to their string identifiers. This is used for text elements to look up the correct localized string. inline std::unordered_map localization_key_map; using LanguageCode = uint32_t; // 32-bit integer to store the language code (e.g., "en", "fr", "es") inline LanguageCode current_language = 'e' << 24 | 'n' << 16; using JoinedKey = uint64_t; // 64-bit integer to store the joined language code and key inline JoinedKey join_keys(LanguageCode lang_code, Key loc_key) { return (static_cast(lang_code) << 16) | loc_key; } struct Segment { bool is_var; std::string text; }; inline std::unordered_map> localized_strings; inline std::unordered_map var_sym_cache; inline mrb_sym get_var_sym(mrb_state *mrb, const std::string &name) { auto it = var_sym_cache.find(name); if (it != var_sym_cache.end()) return it->second; mrb_sym sym = mrb_intern_cstr(mrb, name.c_str()); var_sym_cache[name] = sym; return sym; } inline std::vector parse_localized(const std::string &input) { std::vector out; size_t i = 0; while (i < input.size()) { size_t start = input.find("%{", i); if (start == std::string::npos) { out.push_back({false, input.substr(i)}); break; } if (start > i) { out.push_back({false, input.substr(i, start - i)}); } size_t end = input.find("}", start); if (end == std::string::npos) { // malformed fallback out.push_back({false, input.substr(start)}); break; } std::string var = input.substr(start + 2, end - (start + 2)); out.push_back({true, var}); i = end + 1; } return out; } inline void set(LanguageCode lang_code, Key key, const std::string &text) { localized_strings[join_keys(lang_code, key)] = parse_localized(text); } inline std::string get(Key key, const std::unordered_map ¶ms) { auto it = localized_strings.find(join_keys(current_language, key)); if (it == localized_strings.end()) return "Localization not set!"; const auto &entry = it->second; std::string result; result.reserve(10); for (const auto &seg : entry) { if (!seg.is_var) { result += seg.text; } else { auto param_it = params.find(seg.text); if (param_it == params.end()) { result += "%{" + seg.text + "}"; continue; } result += param_it->second; } } return result; } inline LanguageCode lang_from_sym(mrb_state *mrb, mrb_sym sym) { const char *s = mrb_sym2name(mrb, sym); size_t len = strlen(s); if (len == 0 || len > 4) return 0; LanguageCode code = 0; for (size_t i = 0; i < len; ++i) { code |= (static_cast(s[i]) << (8 * (3 - i))); } return code; } inline mrb_sym lang_to_sym(mrb_state *mrb, LanguageCode code) { char s[5] = {0}; for (int i = 0; i < 4; ++i) s[i] = (code >> (8 * (3 - i))) & 0xFF; return mrb_intern_cstr(mrb, s); } inline Key key_from_sym(mrb_state *mrb, mrb_sym sym) { const char *key_name = mrb_sym2name(mrb, sym); auto it = localization_key_map.find(key_name); if (it != localization_key_map.end()) return it->second; Key new_id = (Key)localization_key_map.size(); localization_key_map[key_name] = new_id; return new_id; } // This is only used for debugging and inspect output, so performance is not a concern inline std::string get_key_name(Key key) { for (const auto &[str, k] : localization_key_map) if (k == key) return str; return "Unknown Key"; } } // namespace Localization