From ec8a49623a34d0b8009166e540f624e1a02836bd Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 29 Jun 2024 21:24:28 +0200 Subject: [PATCH] Added initial localization support --- .gitignore | 1 + CMake/Packages/FindIntl.cmake | 60 +++++++++++++++++ CMakeLists.txt | 1 + es-app/src/guis/GuiMenu.cpp | 3 +- es-app/src/main.cpp | 2 + es-core/CMakeLists.txt | 2 + es-core/src/utils/LocalizationUtil.cpp | 92 ++++++++++++++++++++++++++ es-core/src/utils/LocalizationUtil.h | 32 +++++++++ 8 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 CMake/Packages/FindIntl.cmake create mode 100644 es-core/src/utils/LocalizationUtil.cpp create mode 100644 es-core/src/utils/LocalizationUtil.h diff --git a/.gitignore b/.gitignore index 48b3a92b6..8bce4676f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Object files *.o *.lo +*.mo *.slo # Shared libraries diff --git a/CMake/Packages/FindIntl.cmake b/CMake/Packages/FindIntl.cmake new file mode 100644 index 000000000..7640397de --- /dev/null +++ b/CMake/Packages/FindIntl.cmake @@ -0,0 +1,60 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindIntl +-------- + +Find the Gettext libintl headers and libraries. + +This module reports information about the Gettext libintl +installation in several variables. General variables:: + + Intl_FOUND - true if the libintl headers and libraries were found + Intl_INCLUDE_DIRS - the directory containing the libintl headers + Intl_LIBRARIES - libintl libraries to be linked + +The following cache variables may also be set:: + + Intl_INCLUDE_DIR - the directory containing the libintl headers + Intl_LIBRARY - the libintl library (if any) + +.. note:: + On some platforms, such as Linux with GNU libc, the gettext + functions are present in the C standard library and libintl + is not required. ``Intl_LIBRARIES`` will be empty in this + case. + +.. note:: + If you wish to use the Gettext tools (``msgmerge``, + ``msgfmt``, etc.), use :module:`FindGettext`. +#]=======================================================================] + + +# Written by Roger Leigh + +# Find include directory +find_path(Intl_INCLUDE_DIR + NAMES "libintl.h" + DOC "libintl include directory") +mark_as_advanced(Intl_INCLUDE_DIR) + +# Find all Intl libraries +find_library(Intl_LIBRARY "intl" + DOC "libintl libraries (if not in the C library)") +mark_as_advanced(Intl_LIBRARY) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Intl + FOUND_VAR Intl_FOUND + REQUIRED_VARS Intl_INCLUDE_DIR + FAIL_MESSAGE "Failed to find Gettext libintl") + +if(Intl_FOUND) + set(Intl_INCLUDE_DIRS "${Intl_INCLUDE_DIR}") + if(Intl_LIBRARY) + set(Intl_LIBRARIES "${Intl_LIBRARY}") + else() + unset(Intl_LIBRARIES) + endif() +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d5aed398..7ff11a875 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ elseif(NOT EMSCRIPTEN AND NOT ANDROID) find_package(FFmpeg REQUIRED) find_package(FreeImage REQUIRED) find_package(Freetype REQUIRED) + find_package(Intl REQUIRED) find_package(Libgit2 REQUIRED) find_package(Pugixml REQUIRED) find_package(SDL2 REQUIRED) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 805f11a8a..e55c8b9e7 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -36,6 +36,7 @@ #include "guis/GuiTextEditKeyboardPopup.h" #include "guis/GuiTextEditPopup.h" #include "guis/GuiThemeDownloader.h" +#include "utils/LocalizationUtil.h" #include "utils/PlatformUtil.h" #if defined(__ANDROID__) @@ -48,7 +49,7 @@ GuiMenu::GuiMenu() : mRenderer {Renderer::getInstance()} - , mMenu {"MAIN MENU"} + , mMenu {_("MAIN MENU")} , mThemeDownloaderReloadCounter {0} { const bool isFullUI {UIModeController::getInstance()->isUIModeFull()}; diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index 207fa25cc..63b31e4bb 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -29,6 +29,7 @@ #include "guis/GuiDetectDevice.h" #include "guis/GuiLaunchScreen.h" #include "utils/FileSystemUtil.h" +#include "utils/LocalizationUtil.h" #include "utils/PlatformUtil.h" #include "utils/StringUtil.h" #include "views/ViewController.h" @@ -732,6 +733,7 @@ int main(int argc, char* argv[]) return 0; } + Utils::Localization::setLanguage(Utils::Localization::getLocale()); Scripting::fireEvent("startup"); #if defined(__EMSCRIPTEN__) diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index 6222898fb..848c3a785 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -90,6 +90,7 @@ set(CORE_HEADERS # Utils ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/CImgUtil.h ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/FileSystemUtil.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/LocalizationUtil.h ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/MathUtil.h ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtil.h ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/StringUtil.h @@ -164,6 +165,7 @@ set(CORE_SOURCES # Utils ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/CImgUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/FileSystemUtil.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/LocalizationUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/MathUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/StringUtil.cpp diff --git a/es-core/src/utils/LocalizationUtil.cpp b/es-core/src/utils/LocalizationUtil.cpp new file mode 100644 index 000000000..40c5a0013 --- /dev/null +++ b/es-core/src/utils/LocalizationUtil.cpp @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +// +// ES-DE Frontend +// LocalizationUtil.cpp +// +// Localization functions. +// Provides support for translations using gettext/libintl. +// + +#include "utils/LocalizationUtil.h" + +#include "Log.h" +#include "resources/ResourceManager.h" +#include "utils/StringUtil.h" + +#include +#include + +namespace Utils +{ + namespace Localization + { + std::string getLocale() + { + std::string language; + + // The LANGUAGE environment variable takes precedence over LANG. + if (getenv("LANGUAGE") != nullptr) + language = getenv("LANGUAGE"); + + const std::vector languageValues { + Utils::String::delimitedStringToVector(language, ":")}; + + for (auto value : languageValues) { + if (std::find(sSupportedLanguages.cbegin(), sSupportedLanguages.cend(), value) != + sSupportedLanguages.cend()) { + return value; + } + } + + if (getenv("LANG") != nullptr) + language = getenv("LANG"); + + if (language.empty()) + return "en_US"; + + return language.substr(0, language.find(".")); + } + + void setLanguage(const std::string& locale) + { + if (std::find(sSupportedLanguages.cbegin(), sSupportedLanguages.cend(), locale) == + sSupportedLanguages.cend()) { + LOG(LogInfo) << "No support for language \"" << locale + << "\", reverting to default language \"en_US\""; + return; + } + else { + LOG(LogInfo) << "Setting application language to \"" << locale << "\""; + } + + // No need to perform translations if we're using the default language. + if (locale == "en_US") + return; + + std::string localePath; + localePath.append("/") + .append(locale) + .append("/LC_MESSAGES/") + .append(locale) + .append(".mo"); + + // If the message catalog file is not found then an emergency shutdown will be + // initiated by ResourceManager. + std::string objectPath { + ResourceManager::getInstance().getResourcePath(":/locale" + localePath)}; + + // This makes it possible to override the message catalog with a file in the + // application data directory. + if (objectPath.length() > localePath.length()) + objectPath = objectPath.substr(0, objectPath.length() - localePath.length()); + + setenv("LANGUAGE", locale.c_str(), 1); + setlocale(LC_MESSAGES, ""); + textdomain(locale.c_str()); + bindtextdomain(locale.c_str(), objectPath.c_str()); + bind_textdomain_codeset(locale.c_str(), "UTF-8"); + } + + } // namespace Localization + +} // namespace Utils diff --git a/es-core/src/utils/LocalizationUtil.h b/es-core/src/utils/LocalizationUtil.h new file mode 100644 index 000000000..ec0c2e621 --- /dev/null +++ b/es-core/src/utils/LocalizationUtil.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// +// ES-DE Frontend +// LocalizationUtil.h +// +// Localization functions. +// Provides support for translations using gettext/libintl. +// + +#ifndef ES_CORE_UTILS_LOCALIZATION_UTIL_H +#define ES_CORE_UTILS_LOCALIZATION_UTIL_H + +#include +#include +#include + +#define _(STR) std::string(gettext(STR)) + +namespace Utils +{ + namespace Localization + { + static inline std::vector sSupportedLanguages {"en_US", "sv_SE"}; + + std::string getLocale(); + void setLanguage(const std::string& locale); + + } // namespace Localization + +} // namespace Utils + +#endif // ES_CORE_UTILS_LOCALIZATION_UTIL_H