From 220e36f0f54dcb0342ba32b6325e9add2f47347c Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Wed, 29 Jul 2020 15:07:07 +0200 Subject: Add embedded gettext support from resource. --- src/Makefile.am | 10 +- src/translation.cc | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/translation.h | 60 +++++++++++ 3 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 src/translation.cc create mode 100644 src/translation.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index f2a392d..b656f48 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,11 @@ -noinst_LTLIBRARIES = libdg.la libzr.la libpugi.la +noinst_LTLIBRARIES = libdg.la libzr.la libpugi.la libnls.la + +# shared nls component + +libnls_la_CPPFLAGS = +libnls_la_LIBADD = +libnls_la_SOURCES = \ + translation.cc # libzita-resampler @@ -129,6 +136,7 @@ EXTRA_DIST = \ staminafilter.h \ syncedsettings.h \ thread.h \ + translation.h \ velocityfilter.h \ versionstr.h \ zrwrapper.h diff --git a/src/translation.cc b/src/translation.cc new file mode 100644 index 0000000..18763e4 --- /dev/null +++ b/src/translation.cc @@ -0,0 +1,287 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * translation.cc + * + * Sun Sep 8 14:13:22 CEST 2019 + * Copyright 2019 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "translation.h" + +#ifdef WITH_NLS + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +namespace +{ +using Text = std::pair; +using Texts = std::vector; + +struct +{ + std::mutex mutex; + int refcnt{0}; + Texts texts; +} singleton; + + +bool comparator(const Text& a, const Text& b) +{ + return a.first < b.first; +} +} + +Translation::Translation() +{ + std::lock_guard(singleton.mutex); + ++singleton.refcnt; +} + +Translation::~Translation() +{ + std::lock_guard(singleton.mutex); + + --singleton.refcnt; + + if(singleton.refcnt == 0) + { + singleton.texts.clear(); + } +} + +bool Translation::load(const char* catalog, std::size_t size) +{ + Texts texts; + + // https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html + + // byte + // +------------------------------------------+ + // 0 | magic number = 0x950412de | + // | | + // 4 | file format revision = 0 | + // | | + // 8 | number of strings | == N + // | | + // 12 | offset of table with original strings | == O + // | | + // 16 | offset of table with translation strings | == T + // | | + // 20 | size of hashing table | == S + // | | + // 24 | offset of hashing table | == H + // | | + // . . + // . (possibly more entries later) . + // . . + // | | + // O | length & offset 0th string ----------------. + // O + 8 | length & offset 1st string ------------------. + // ... ... | | + // O + ((N-1)*8)| length & offset (N-1)th string | | | + // | | | | + // T | length & offset 0th translation ---------------. + // T + 8 | length & offset 1st translation -----------------. + // ... ... | | | | + // T + ((N-1)*8)| length & offset (N-1)th translation | | | | | + // | | | | | | + // H | start hash table | | | | | + // ... ... | | | | + // H + S * 4 | end hash table | | | | | + // | | | | | | + // | NUL terminated 0th string <----------------' | | | + // | | | | | + // | NUL terminated 1st string <------------------' | | + // | | | | + // ... ... | | + // | | | | + // | NUL terminated 0th translation <---------------' | + // | | | + // | NUL terminated 1st translation <-----------------' + // | | + // ... ... + // | | + // +------------------------------------------+ + constexpr std::uint32_t magic_number_le = 0x950412de; + constexpr std::uint32_t magic_number_be = 0xde120495; + + const char* ptr = catalog; + + // Verify magic number + std::uint32_t magic_number = *reinterpret_cast(ptr); + if(magic_number != magic_number_le && magic_number != magic_number_be) + { + // Error - bad magic number + return false; + } + ptr += sizeof(magic_number); + + // Verify file format revision + std::uint32_t file_format_revision = + *reinterpret_cast(ptr); + if(file_format_revision != 0) + { + // Error - bad file format revision + return false; + } + ptr += sizeof(file_format_revision); + + // Read number of string in the catalog + std::uint32_t number_of_strings = + *reinterpret_cast(ptr); + ptr += sizeof(number_of_strings); + + // Read orig_table_offset + std::uint32_t orig_table_offset = + *reinterpret_cast(ptr); + ptr += sizeof(orig_table_offset); + + // Read transl_table_offset + std::uint32_t transl_table_offset = + *reinterpret_cast(ptr); + ptr += sizeof(transl_table_offset); + + // Read hash_table_size + std::uint32_t hash_table_size = + *reinterpret_cast(ptr); + ptr += sizeof(hash_table_size); + + // Read hash_table_offset + std::uint32_t hash_table_offset = + *reinterpret_cast(ptr); + ptr += sizeof(hash_table_offset); + + + // Read original/translated string tables: + const char* orig_ptr = catalog + orig_table_offset; + const char* transl_ptr = catalog + transl_table_offset; + for(std::uint32_t string_index = 0; + string_index < number_of_strings; ++string_index) + { + // Read string_length + std::uint32_t orig_string_length = + *reinterpret_cast(orig_ptr); + orig_ptr += sizeof(orig_string_length); + + // Read string_offset + std::uint32_t orig_string_offset = + *reinterpret_cast(orig_ptr); + orig_ptr += sizeof(orig_string_offset); + + std::string orig; + orig.append(catalog + orig_string_offset, orig_string_length); + + // Read string_length + std::uint32_t transl_string_length = + *reinterpret_cast(transl_ptr); + transl_ptr += sizeof(transl_string_length); + + // Read string_offset + std::uint32_t transl_string_offset = + *reinterpret_cast(transl_ptr); + transl_ptr += sizeof(transl_string_offset); + + std::string transl; + transl.append(catalog + transl_string_offset, transl_string_length); + + texts.push_back(make_pair(const_hash(orig.data()), transl)); + } + + std::sort(texts.begin(), texts.end(), comparator); + + { + std::lock_guard(singleton.mutex); + std::swap(singleton.texts, texts); + } + + return true; +} + +const char* Translation::gettext(std::uint64_t msgid, const char* original) +{ + std::lock_guard(singleton.mutex); + if(singleton.refcnt == 0) + { + return original; + } + + auto it = std::lower_bound(singleton.texts.begin(), + singleton.texts.end(), + make_pair(msgid, std::string()), comparator); + if(it == singleton.texts.end() || + it->first != msgid) + { + return original; + } + + return it->second.data(); +} + +std::string Translation::getISO639LanguageName() +{ + std::string lang; + +#ifdef _WIN32 + LCID lcid = GetUserDefaultLCID(); + char name[LOCALE_NAME_MAX_LENGTH]; + GetLocaleInfo(lcid, LOCALE_SISO639LANGNAME, name, LOCALE_NAME_MAX_LENGTH); + lang = name; +#else + try + { + auto langstr = setlocale(LC_ALL, ""); + if(langstr != nullptr) + { + lang = langstr; + } + } + catch(...) + { + // Bad locale: don't load anything - use default + printf("Bad locale: don't load anything - use default\n"); + return ""; + } + + if(lang == "C") + { + printf("Don't load anything - use default\n"); + return ""; // Don't load anything - use default + } + + auto _ = lang.find('_'); + lang = lang.substr(0, _); +#endif + + return lang; +} + +#endif // WITH_NLS diff --git a/src/translation.h b/src/translation.h new file mode 100644 index 0000000..7f506a6 --- /dev/null +++ b/src/translation.h @@ -0,0 +1,60 @@ +/* -*- Mode: c++ -*- */ +/*************************************************************************** + * translation.h + * + * Sun Sep 8 14:13:22 CEST 2019 + * Copyright 2019 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * DrumGizmo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +#include + +#ifdef WITH_NLS + +#include + +std::uint64_t constexpr const_hash(const char* input) +{ + return *input ? + static_cast(*input) + 33 * const_hash(input + 1) : + 5381; +} + +#define _(msg) Translation::gettext(const_hash(msg), msg) + +class Translation +{ +public: + Translation(); + virtual ~Translation(); + + bool load(const char* catalog, std::size_t size); + + static const char* gettext(std::uint64_t msgid, const char* original); + static std::string getISO639LanguageName(); +}; + +#else + +#define _(msg) msg + +#endif -- cgit v1.2.3