From d31b88ceb00871ccfe000e605497cc07d6dd23df Mon Sep 17 00:00:00 2001 From: Wouter Wijsman Date: Thu, 18 Jul 2024 14:45:35 +0200 Subject: [PATCH] Initial support for translation in code and build system --- .gitignore | 1 + CMakeLists.txt | 27 ++++++++++++++++--- assets/languages/nl.po | 55 ++++++++++++++++++++++++++++++++++++++ src/TranslationManager.cpp | 53 +++++++++++++++++++++++++++++------- src/TranslationManager.hpp | 6 ++++- src/main.cpp | 6 +++++ src/states/MenuState.cpp | 19 ++++++------- src/utils.hpp | 9 +++++++ 8 files changed, 152 insertions(+), 24 deletions(-) create mode 100644 assets/languages/nl.po diff --git a/.gitignore b/.gitignore index 3042647..fe0213a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.lo *.o *.obj +*.mo # Precompiled Headers *.gch diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a02f40..c2053e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.20) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) message(FATAL_ERROR "Please create a build directory. Building inside the oceanpop directory directly would prevent the asset directory from being build correctly.") @@ -31,7 +31,6 @@ pkg_search_module(SDL2_IMAGE REQUIRED SDL2_image) pkg_search_module(SDL2_MIXER REQUIRED SDL2_mixer) pkg_search_module(SDL2_TTF REQUIRED SDL2_ttf) pkg_search_module(JSONCPP REQUIRED jsoncpp) -pkg_search_module(TINYGETTEXT REQUIRED tinygettext) # Link libraries @@ -43,7 +42,6 @@ if(BUILD_STATIC) ${SDL2_MIXER_STATIC_LIBRARIES} ${SDL2_TTF_STATIC_LIBRARIES} ${JSONCPP_STATIC_LIBRARIES} - ${TINYGETTEXT_STATIC_LIBRARIES} ) else() target_link_libraries(${PROJECT_NAME} PRIVATE @@ -52,7 +50,6 @@ else() ${SDL2_MIXER_LIBRARIES} ${SDL2_TTF_LIBRARIES} ${JSONCPP_LIBRARIES} - ${TINYGETTEXT_LIBRARIES} ) endif() @@ -65,6 +62,20 @@ include_directories( ${JSONCPP_INCLUDE_DIRS} ) +option(TRANSLATION_SUPPORT "Add support for loading translations, requires tinygettext" OFF) + +# Create translations +if(TRANSLATION_SUPPORT) + pkg_search_module(TINYGETTEXT REQUIRED tinygettext) + if(BUILD_STATIC) + target_link_libraries(${PROJECT_NAME} PRIVATE ${TINYGETTEXT_STATIC_LIBRARIES}) + else() + target_link_libraries(${PROJECT_NAME} PRIVATE ${TINYGETTEXT_LIBRARIES}) + endif() + + add_compile_definitions(TRANSLATION_SUPPORT=1) +endif() + # Set asset paths set(ASSETS_FONTS "${CMAKE_SOURCE_DIR}/assets/fonts") set(ASSETS_IMAGES "${CMAKE_SOURCE_DIR}/assets/images") @@ -72,6 +83,9 @@ set(ASSETS_LEVELS "${CMAKE_SOURCE_DIR}/assets/levels") set(ASSETS_SOUNDS "${CMAKE_SOURCE_DIR}/assets/sounds") set(ASSETS_MUSIC "${CMAKE_SOURCE_DIR}/assets/music") set(ASSETS_BACKGROUNDS "${CMAKE_SOURCE_DIR}/assets/backgrounds") +if(TRANSLATION_SUPPORT) + set(ASSETS_LANGUAGES "${CMAKE_SOURCE_DIR}/assets/languages") +endif() if(PSP) set(ASSETS_BACKGROUNDS "${CMAKE_CURRENT_SOURCE_DIR}/platform/psp/assets/backgrounds") @@ -102,6 +116,11 @@ if(NOT VITA) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${ASSETS_SOUNDS} ${CMAKE_CURRENT_BINARY_DIR}/assets/sounds) + if(TRANSLATION_SUPPORT) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${ASSETS_LANGUAGES} ${CMAKE_CURRENT_BINARY_DIR}/assets/languages) + endif() endif() # Platform specific install diff --git a/assets/languages/nl.po b/assets/languages/nl.po new file mode 100644 index 0000000..63c1e1f --- /dev/null +++ b/assets/languages/nl.po @@ -0,0 +1,55 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-07-18 11:01+0200\n" +"PO-Revision-Date: 2024-07-18 11:07+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.4\n" + +#: src/states/MenuState.cpp:22 +msgid "challenge mode" +msgstr "uitdagingsmodus" + +#: src/states/MenuState.cpp:23 +msgid "ends only when lives run out" +msgstr "eindigt alleen als je levens op zijn" + +#: src/states/MenuState.cpp:36 +msgid "exit" +msgstr "stoppen" + +#: src/states/MenuState.cpp:30 +msgid "high scores" +msgstr "resultaten" + +#: src/states/MenuState.cpp:27 +msgid "match without consequences" +msgstr "match zonder consequenties" + +#: src/states/MenuState.cpp:33 +msgid "options" +msgstr "opties" + +#: src/states/MenuState.cpp:19 +msgid "play pre-made levels" +msgstr "speel voorgeprogrammeerde levels" + +#: src/states/MenuState.cpp:26 +msgid "relaxed mode" +msgstr "relaxmodus" + +#: src/states/MenuState.cpp:18 +msgid "standard mode" +msgstr "standardmodus" diff --git a/src/TranslationManager.cpp b/src/TranslationManager.cpp index 3352643..63f139b 100644 --- a/src/TranslationManager.cpp +++ b/src/TranslationManager.cpp @@ -1,3 +1,5 @@ +#ifdef TRANSLATION_SUPPORT + #include "TranslationManager.hpp" #include @@ -21,22 +23,53 @@ TranslationManager::~TranslationManager() { } void TranslationManager::loadTranslations() { - std::string system_language = getSystemLanguage(); std::string language_dir = getResourcePath("assets/languages/"); - dictionary_manager.add_directory(language_dir); - tinygettext::Language language = tinygettext::Language::from_name(system_language); - if (language) { - dictionary_manager.set_language(language); - } else { - SDL_Log("Couldn't load language %s", system_language.c_str()); + + + for(std::string system_language : getSystemLanguageList()) { + tinygettext::Language language = tinygettext::Language::from_name(system_language); + if (language) { + for (tinygettext::Language l: dictionary_manager.get_languages()) { + if (l == language) { + dictionary_manager.set_language(language); + SDL_Log("Using language %s", language.get_language().c_str()); + language_set = true; + return; + } + } + } } + SDL_Log("Using default language English");; } -std::string TranslationManager::getSystemLanguage() { - return ""; +std::vector TranslationManager::getSystemLanguageList() { + std::vector locales; + + #include + char * locale_c_str = setlocale(LC_ALL, ""); + if (locale_c_str != NULL){ + std::string locale(locale_c_str); + free(locale_c_str); + + locales.push_back(locale); + if (locale.find(".") != std::string::npos) { + locales.push_back(locale.substr(0, locale.find("."))); + } + if (locale.find("_") != std::string::npos) { + locales.push_back(locale.substr(0, locale.find("_"))); + } + } + + return locales; } std::string TranslationManager::translate(std::string input) { - return dictionary_manager.get_dictionary().translate(input); + if (language_set) { + return dictionary_manager.get_dictionary().translate(input); + } else { + return input; + } } + +#endif // TRANSLATION_SUPPORT \ No newline at end of file diff --git a/src/TranslationManager.hpp b/src/TranslationManager.hpp index 626aa62..6228d2b 100644 --- a/src/TranslationManager.hpp +++ b/src/TranslationManager.hpp @@ -1,6 +1,9 @@ #ifndef TRANSLATIONMANAGER_H #define TRANSLATIONMANAGER_H +#ifdef TRANSLATION_SUPPORT +#include + #include class TranslationManager { @@ -8,7 +11,7 @@ class TranslationManager { tinygettext::DictionaryManager dictionary_manager; bool language_set = false; - std::string getSystemLanguage(); + std::vector getSystemLanguageList(); void loadTranslations(); public: @@ -17,5 +20,6 @@ class TranslationManager { std::string translate(std::string input); }; +#endif #endif // TRANSLATIONMANAGER_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5a26dae..3c7557a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,10 +11,16 @@ #include "StateManager.hpp" #include "EventManager.hpp" #include "OptionManager.hpp" +#include "TranslationManager.hpp" #include "utils.hpp" #include "constants.hpp" #include "states/MenuState.hpp" +#ifdef TRANSLATION_SUPPORT + // Create global TranslationManager object set in utils.h + TranslationManager translation_manager; +#endif + void run() { OptionManager option_manager; Window window("OceanPop", &option_manager); diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index 2d4a1f6..668c3e1 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -3,6 +3,7 @@ #include "../constants.hpp" #include "../colors.hpp" #include "../FontType.hpp" +#include "../utils.hpp" #include "GameState.hpp" MenuState::MenuState(SDL_Renderer * renderer, FontManager * fonts, SoundManager * sounds, OptionManager * options) : renderer(renderer), fonts(fonts), sounds(sounds), options(options), @@ -15,25 +16,25 @@ MenuState::MenuState(SDL_Renderer * renderer, FontManager * fonts, SoundManager std::string option_sub_text; switch ((State) i) { case State::STANDARD: - option_text = "standard mode"; - option_sub_text = "play pre-made levels"; + option_text = _("standard mode"); + option_sub_text = _("play pre-made levels"); break; case State::CHALLENGE: - option_text = "challenge mode"; - option_sub_text = "ends only when lives run out"; + option_text = _("challenge mode"); + option_sub_text = _("ends only when lives run out"); break; case State::RELAXED: - option_text = "relaxed mode"; - option_sub_text = "match without consequences"; + option_text = _("relaxed mode"); + option_sub_text = _("match without consequences"); break; case State::HIGHSCORES: - option_text = "high scores"; + option_text = _("high scores"); break; case State::OPTIONS: - option_text = "options"; + option_text = _("options"); break; case State::EXIT: - option_text = "exit"; + option_text = _("exit"); break; default: option_text = "?????????"; diff --git a/src/utils.hpp b/src/utils.hpp index 62b198c..49d365c 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -6,6 +6,8 @@ #include #include +#include "TranslationManager.hpp" + struct DisplayMode { int width; @@ -24,4 +26,11 @@ std::string getResourcePath(std::string file); SDL_DisplayMode getStandardDisplayMode(); std::vector getDisplayModes(); +#ifdef TRANSLATION_SUPPORT + extern TranslationManager translation_manager; + #define _(x) translation_manager.translate(x) +#else + #define _(x) x +#endif + #endif // UTILS_HPP