44 return 'A' <= c && c <=
'Z';
49 return 'a' <= c && c <=
'z';
72 return '0' <= c && c <=
'9';
86 std::string locale_name(locale_full_name);
89#if defined(__APPLE__) && !defined(WITH_HEADLESS) && !defined(WITH_GHOST_SDL)
90 if (locale_name.empty()) {
95 if (locale_name.empty()) {
101 if (locale_name.empty()) {
109 if (locale_name.empty()) {
111 if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, buf,
sizeof(buf)) != 0) {
113 if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, buf,
sizeof(buf)) != 0) {
114 std::string region = buf;
115 if (locale_name ==
"zh") {
116 if (region ==
"TW" || region ==
"HK" || region ==
"MO") {
118 locale_name +=
"_HANT";
122 locale_name +=
"_HANS";
126 locale_name +=
"_" + region;
133 parse_from_lang(locale_name);
153 bool parse_from_variant(
const std::string_view
input)
166 bool parse_from_encoding(
const std::string_view
input)
169 std::string tmp(
input.substr(0, end));
174 if (end >=
input.size()) {
178 return parse_from_variant(
input.substr(end + 1));
181 bool parse_from_country(
const std::string_view
input)
188 std::string tmp(
input.substr(0, end));
193 for (
char &c : tmp) {
198 if (std::find_if_not(tmp.begin(), tmp.end(),
is_upper_ascii) != tmp.end()) {
202 if (
language ==
"en" && tmp ==
"US_POSIX") {
206 else if (tmp.size() != 3u ||
214 if (end >=
input.size()) {
217 if (
input[end] ==
'.') {
218 return parse_from_encoding(
input.substr(end + 1));
221 return parse_from_variant(
input.substr(end + 1));
224 bool parse_from_script(
const std::string_view
input)
227 std::string tmp(
input.substr(0, end));
229 if (tmp.length() != 4) {
230 return parse_from_country(
input);
233 for (
char &c : tmp) {
235 return parse_from_country(
input);
241 if (end >=
input.size()) {
245 return parse_from_country(
input.substr(end + 1));
247 if (
input[end] ==
'.') {
248 return parse_from_encoding(
input.substr(end + 1));
251 return parse_from_variant(
input.substr(end + 1));
254 bool parse_from_lang(
const std::string_view
input)
257 std::string tmp(
input.substr(0, end));
261 for (
char &c : tmp) {
266 if (!
ELEM(tmp,
"c",
"posix")) {
270 if (end >=
input.size()) {
274 return parse_from_script(
input.substr(end + 1));
276 if (
input[end] ==
'.') {
277 return parse_from_encoding(
input.substr(end + 1));
280 return parse_from_variant(
input.substr(end + 1));
287 uint32_t keys_offset_ = 0;
288 uint32_t translations_offset_ = 0;
291 bool native_byteorder_ =
false;
292 size_t size_ =
false;
299 FILE *file =
BLI_fopen(filepath.c_str(),
"rb");
304 fseek(file, 0, SEEK_END);
307 fseek(file, 0, SEEK_SET);
309 if (fread(data_.data(), 1,
len, file) !=
len) {
311 error_ =
"Failed to read file";
315 error_ =
"Wrong file object";
320 if (error_.empty()) {
327 const uint32_t off = get(keys_offset_ +
id * 8 + 4);
328 return data_.data() + off;
333 const uint32_t
len = get(translations_offset_ +
id * 8);
334 const uint32_t off = get(translations_offset_ +
id * 8 + 4);
335 if (
len > data_.size() || off > data_.size() -
len) {
336 error_ =
"Bad mo-file format";
360 if (data_.
size() < 4) {
361 error_ =
"Invalid 'mo' file format - the file is too short";
367 if (
magic == 0x950412de) {
368 native_byteorder_ =
true;
370 else if (
magic == 0xde120495) {
371 native_byteorder_ =
false;
374 error_ =
"Invalid file format - invalid magic number";
380 keys_offset_ = get(12);
381 translations_offset_ = get(16);
386 if (offset > data_.size() - 4) {
387 error_ =
"Bad mo-file format";
391 memcpy(&
v, &data_[offset], 4);
392 if (!native_byteorder_) {
393 v = ((
v & 0xFF) << 24) | ((
v & 0xFF00) << 8) | ((
v & 0xFF0000) >> 8) |
394 ((
v & 0xFF000000) >> 24);
419 const size_t pos = c.
find(
char(4));
463 for (
size_t i = 0;
i < domains.
size();
i++) {
464 const std::string &domain_name = domains[
i];
465 const std::string filename = domain_name +
".mo";
467 for (
const std::string &path : catalog_paths) {
468 if (load_file(path +
"/" + filename, catalog)) {
472 catalogs_.
append(std::move(catalog));
476 std::optional<StringRefNull>
translate(
const int domain,
480 if (domain < 0 || domain >= catalogs_.size()) {
484 const std::string *
result = catalogs_[domain].lookup_ptr_as(key);
507 if (!info.
script.empty()) {
508 std::string script_uppercase = info.
script;
509 for (
char &c : script_uppercase) {
512 scripts.
append(script_uppercase);
515 for (
const std::string &script : scripts) {
516 std::string language = info.
language;
517 if (!script.empty()) {
518 language +=
"_" + script;
529 lang_folders.
append(language);
533 Vector<std::string>
result;
535 for (
const std::string &lang_folder : lang_folders) {
536 for (
const std::string &search_path : paths) {
537 result.append(search_path +
"/" + lang_folder +
"/LC_MESSAGES");
543 bool load_file(
const std::string &filepath, Catalog &catalog)
546 if (!mo.error().empty()) {
555 const std::string mo_encoding = extract(mo.value(0),
"charset=",
" \r\n;");
556 if (mo_encoding.empty()) {
557 error_ =
"Invalid mo-format, encoding is not specified";
560 if (mo_encoding !=
"UTF-8") {
561 error_ =
"supported mo-format, encoding must be UTF-8";
565 CLOG_INFO(&
LOG,
"Load messages from \"%s\"", filepath.c_str());
568 for (
size_t i = 0;
i < mo.size();
i++) {
569 const MessageKey key(mo.key(
i));
570 catalog.add(std::move(key), std::string(mo.value(
i)));
576 static std::string extract(StringRef meta,
const std::string &key,
const StringRef separators)
578 const size_t pos = meta.
find(key);
584 return std::string(meta.
substr(0, end_pos));
597 Info info(locale_full_name);
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
int64_t BLI_ftell(FILE *stream) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
Compatibility-like things for windows.
#define CLOG_ERROR(clg_ref,...)
#define CLOG_INFO(clg_ref,...)
ATTR_WARN_UNUSED_RESULT const BMVert * v
unsigned long long int uint64_t
void append(const T &value)
void reserve(const int64_t min_capacity)
static constexpr int64_t not_found
constexpr int64_t find(char c, int64_t pos=0) const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr int64_t find_first_of(StringRef chars, int64_t pos=0) const
void append(const T &value)
Info(const StringRef locale_full_name)
std::string to_full_name() const
MOFile(const std::string &filepath)
const std::string & error() const
MOMessages(const Info &info, const Vector< std::string > &domains, const Vector< std::string > &paths)
std::optional< StringRefNull > translate(const int domain, const StringRef context, const StringRef str) const
const std::string & error()
static constexpr bool is_upper_ascii(const char c)
static std::unique_ptr< MOMessages > global_messages
std::optional< StringRefNull > translate(const int domain, const StringRef context, const StringRef key)
static bool make_upper_ascii(char &c)
static bool make_lower_ascii(char &c)
bool operator==(const MessageKey &a, const MessageKey &b)
static constexpr bool is_lower_ascii(const char c)
static std::string global_full_name
static constexpr bool is_numeric_ascii(const char c)
std::string macos_user_locale()
uint64_t get_default_hash(const T &v, const Args &...args)
static uint64_t hash_as(const MessageKeyRef &key)
MessageKey(const StringRef c)
static int magic(const Tex *tex, const float texvec[3], TexResult *texres)