diff --git a/CMakeLists.txt b/CMakeLists.txt index d63b50510..8ff1d5909 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -355,6 +355,10 @@ endif() add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) +# Generate header for embedded translations +add_subdirectory(translations) +include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations") + add_subdirectory(external) # Final setup for miniupnpc diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index 4a76e76fc..28a186bf0 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -35,6 +35,7 @@ #include "file_io_utils.h" #include "common/util.h" #include "common/i18n.h" +#include "translation_files.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "i18n" @@ -62,6 +63,7 @@ std::string i18n_get_language() e = "en"; std::string language = e; + language = language.substr(0, language.find(".")); std::transform(language.begin(), language.end(), language.begin(), tolower); return language; } @@ -137,25 +139,40 @@ int i18n_set_language(const char *directory, const char *base, std::string langu i18n_log("Loading translations for language " << language); boost::system::error_code ignored_ec; - if (!boost::filesystem::exists(filename, ignored_ec)) { + if (boost::filesystem::exists(filename, ignored_ec)) { + if (!epee::file_io_utils::load_file_to_string(filename, contents)) { + i18n_log("Failed to load translations file: " << filename); + return -1; + } + } else { i18n_log("Translations file not found: " << filename); - const char *underscore = strchr(language.c_str(), '_'); - if (underscore) { - std::string fallback_language = std::string(language, 0, underscore - language.c_str()); - filename = std::string(directory) + "/" + base + "_" + fallback_language + ".qm"; - i18n_log("Not found, loading translations for language " << fallback_language); - if (!boost::filesystem::exists(filename, ignored_ec)) { - i18n_log("Translations file not found: " << filename); + filename = std::string(base) + "_" + language + ".qm"; + if (!find_embedded_file(filename, contents)) { + i18n_log("Embedded translations file not found: " << filename); + const char *underscore = strchr(language.c_str(), '_'); + if (underscore) { + std::string fallback_language = std::string(language, 0, underscore - language.c_str()); + filename = std::string(directory) + "/" + base + "_" + fallback_language + ".qm"; + i18n_log("Loading translations for language " << fallback_language); + if (boost::filesystem::exists(filename, ignored_ec)) { + if (!epee::file_io_utils::load_file_to_string(filename, contents)) { + i18n_log("Failed to load translations file: " << filename); + return -1; + } + } else { + i18n_log("Translations file not found: " << filename); + filename = std::string(base) + "_" + fallback_language + ".qm"; + if (!find_embedded_file(filename, contents)) { + i18n_log("Embedded translations file not found: " << filename); + return -1; + } + } + } else { return -1; } } } - if (!epee::file_io_utils::load_file_to_string(filename, contents)) { - i18n_log("Failed to load translations file: " << filename); - return -1; - } - data = (const unsigned char*)contents.c_str(); datalen = contents.size(); idx = 0; diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt new file mode 100644 index 000000000..36b72d68a --- /dev/null +++ b/translations/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2017, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cmake_minimum_required(VERSION 2.8.7) + +add_executable(generate_translations_header generate_translations_header.c) + +find_program(LRELEASE lrelease) +if(LRELEASE STREQUAL "LRELEASE-NOTFOUND") + set(ts_files "") + message(WARNING "lrelease program not found, translation files not built") +else() + file(GLOB ts_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" *.ts) + foreach(ts_file ${ts_files}) + string(REPLACE ".ts" ".qm" qm_file "${ts_file}") + add_custom_command(TARGET generate_translations_header + PRE_BUILD + COMMAND ${LRELEASE} "${CMAKE_CURRENT_SOURCE_DIR}/${ts_file}" -qm "${qm_file}" + WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}") + endforeach() +endif() + +string(REPLACE ".ts" ".qm" qm_files "${ts_files}") + +add_custom_command(TARGET generate_translations_header + POST_BUILD + COMMAND generate_translations_header ${qm_files} + WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}" + COMMENT "Generating embedded translations header") diff --git a/translations/generate_translations_header.c b/translations/generate_translations_header.c new file mode 100644 index 000000000..69671913e --- /dev/null +++ b/translations/generate_translations_header.c @@ -0,0 +1,86 @@ +// Copyright (c) 2013, Sergey Lyubka +// Copyright (c) 2017, The Monero Project +// All rights reserved. +// Released under the MIT license. + +// This program takes a list of files as an input, and produces C++ code that +// contains the contents of all these files as a collection of strings. +// +// Usage: +// 1. Compile this file: +// cc -o generate-translations-header generate-translations-header.c +// +// 2. Convert list of files into single header: +// ./generate-translations-header monero_fr.qm monero_it.qm > translations_files.h +// +// 3. In your application code, include translations_files.h, then you can +// access the files using this function: +// static bool find_embedded_file(const std::string &file_name, std::string &data); +// std::string data; +// find_embedded_file("monero_fr.qm", data); + +#include +#include + +static const char *code = + "static bool find_embedded_file(const std::string &name, std::string &data) {\n" + " const struct embedded_file *p;\n" + " for (p = embedded_files; p->name != NULL; p++) {\n" + " if (*p->name == name) {\n" + " data = *p->data;\n" + " return true;\n" + " }\n" + " }\n" + " return false;\n" + "}\n"; + +int main(int argc, char *argv[]) { + FILE *fp, *foutput; + int i, j, ch; + + if((foutput = fopen("translation_files.h", "w")) == NULL) { + exit(EXIT_FAILURE); + } + + fprintf(foutput, "#ifndef TRANSLATION_FILES_H\n"); + fprintf(foutput, "#define TRANSLATION_FILES_H\n\n"); + fprintf(foutput, "#include \n\n"); + + for (i = 1; i < argc; i++) { + if ((fp = fopen(argv[i], "rb")) == NULL) { + exit(EXIT_FAILURE); + } else { + fprintf(foutput, "static const std::string translation_file_name_%d = \"%s\";\n", i, argv[i]); + fprintf(foutput, "static const std::string translation_file_data_%d = std::string(", i); + for (j = 0; (ch = fgetc(fp)) != EOF; j++) { + if ((j % 16) == 0) { + if (j > 0) { + fprintf(foutput, "%s", "\""); + } + fprintf(foutput, "%s", "\n \""); + } + fprintf(foutput, "\\x%02x", ch); + } + fprintf(foutput, "\",\n %d);\n\n", j); + fclose(fp); + } + } + + fprintf(foutput, "%s", "static const struct embedded_file {\n"); + fprintf(foutput, "%s", " const std::string *name;\n"); + fprintf(foutput, "%s", " const std::string *data;\n"); + fprintf(foutput, "%s", "} embedded_files[] = {\n"); + + for (i = 1; i < argc; i++) { + fprintf(foutput, " {&translation_file_name_%d, &translation_file_data_%d},\n", i, i); + } + fprintf(foutput, "%s", " {NULL, NULL}\n"); + fprintf(foutput, "%s", "};\n\n"); + fprintf(foutput, "%s\n", code); + + fprintf(foutput, "#endif /* TRANSLATION_FILES_H */\n"); + + fclose(foutput); + + return EXIT_SUCCESS; +}