23 out->push_back(
static_cast<char>(cp));
24 }
else if (cp <= 0x7FF) {
25 out->push_back(
static_cast<char>(0xC0 | (cp >> 6)));
26 out->push_back(
static_cast<char>(0x80 | (cp & 0x3F)));
27 }
else if (cp <= 0xFFFF) {
28 out->push_back(
static_cast<char>(0xE0 | (cp >> 12)));
29 out->push_back(
static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
30 out->push_back(
static_cast<char>(0x80 | (cp & 0x3F)));
32 out->push_back(
static_cast<char>(0xF0 | (cp >> 18)));
33 out->push_back(
static_cast<char>(0x80 | ((cp >> 12) & 0x3F)));
34 out->push_back(
static_cast<char>(0x80 | ((cp >> 6) & 0x3F)));
35 out->push_back(
static_cast<char>(0x80 | (cp & 0x3F)));
40 if (c >=
'0' && c <=
'9')
42 if (c >=
'a' && c <=
'f')
44 if (c >=
'A' && c <=
'F')
58 bool Parse(std::unordered_map<std::string, std::string>* out) {
70 if (!ParseString(&key))
77 if (!ParseString(&value))
81 const char c = Peek();
95 char Peek()
const {
return i_ < s_.size() ? s_[i_] :
'\0'; }
103 while (i_ < s_.size()) {
104 const char c = s_[i_];
105 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r') {
116 while (i_ < s_.size()) {
117 const char c = s_[i_++];
126 const char e = s_[i_++];
132 out->push_back(
'\\');
138 out->push_back(
'\b');
141 out->push_back(
'\f');
144 out->push_back(
'\n');
147 out->push_back(
'\r');
150 out->push_back(
'\t');
156 if (cp >= 0xD800 && cp <= 0xDBFF) {
157 if (i_ + 1 < s_.size() && s_[i_] ==
'\\' && s_[i_ + 1] ==
'u') {
162 cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
176 if (i_ + 4 > s_.size())
179 for (
int k = 0; k < 4; ++k) {
180 const int h =
HexVal(s_[i_++]);
183 v = (v << 4) | static_cast<uint32_t>(h);
189 const std::string&
s_;
190 std::string::size_type i_ = 0;
198 return path->parent_path();
203 return path->parent_path();
222 std::vector<std::string> locales;
223 locales.push_back(kDefaultLocale);
224 const std::filesystem::path dir = ResolveI18nDir();
227 for (
const auto& entry : std::filesystem::directory_iterator(dir, ec)) {
230 if (!entry.is_regular_file())
232 const std::filesystem::path& p = entry.path();
233 if (p.extension() !=
".json")
235 std::string stem = p.stem().string();
236 if (stem == kDefaultLocale)
238 if (std::find(locales.begin(), locales.end(), stem) == locales.end()) {
239 locales.push_back(stem);
243 std::sort(locales.begin(), locales.end());
250 if (locale == kDefaultLocale) {
256 util::logf(
"i18n: catalog for '%s' not found", locale.c_str());
259 std::ifstream file(asset->string(), std::ios::binary);
261 util::logf(
"i18n: cannot open catalog %s", asset->string().c_str());
264 std::ostringstream ss;
266 const std::string text = ss.str();
267 std::unordered_map<std::string, std::string> catalog;
268 FlatJsonParser parser(text);
269 if (!parser.Parse(&catalog)) {
270 util::logf(
"i18n: failed to parse catalog %s", asset->string().c_str());
273 util::logf(
"i18n: loaded '%s' catalog (%d entries)", locale.c_str(),
274 static_cast<int>(catalog.size()));
280 std::string target = locale.empty() ? std::string(kDefaultLocale) : locale;
282 target = kDefaultLocale;
293 const std::string& locale,
const std::string& json_text) {
294 std::unordered_map<std::string, std::string> catalog;
295 FlatJsonParser parser(json_text);
296 if (!parser.Parse(&catalog)) {
307 auto it = cat_it->second.find(visible_key);
308 if (it == cat_it->second.end())
std::vector< std::string > GetAvailableLocales() const
const std::string * Find(const std::string &visible_key) const
void SetLanguage(const std::string &locale)
static LanguageManager & Get()
std::string current_locale_
LanguageChangedCallback on_changed_
bool LoadCatalog(const std::string &locale)
std::map< std::string, std::unordered_map< std::string, std::string > > catalogs_
bool LoadCatalogFromStringForTesting(const std::string &locale, const std::string &json_text)
bool ParseHex4(uint32_t *out)
FlatJsonParser(const std::string &text)
bool ParseString(std::string *out)
bool Parse(std::unordered_map< std::string, std::string > *out)
void AppendUtf8(uint32_t cp, std::string *out)
constexpr char kDefaultLocale[]
std::filesystem::path ResolveI18nDir()
void ClearTranslationCache()
void logf(const absl::FormatSpec< Args... > &format, Args &&... args)