From 4b745036e02d7d1036c6993473ffd1225cc1a21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Fri, 13 Sep 2024 18:29:28 +0200 Subject: [PATCH] I18N: Set C++ locale and format sizes according to the locale Displaying a package size, an installation size, a download speed etc. formats decimal numbers to 1-digit precision after the decimal point (49.5 KiB). However, users expect the number to be formatted according their locale. E.g. in cs_CZ.UTF-8, it is "49,5 KiB". DNF5 formats these values with fmt::format() which utilizes C++ locale if "L" formatting option is used. C++ locale (std::locale::global()) and C locale (setlocale(), C++-wrapped as std::setlocale()) are two different things and DNF5 only has set the C locale up to now. This patch starts setting C++ locale, which also implicitly sets C locale. This patch also modifies libdnf5::cli::utils::units::format_size_aligned() to use the locale-dependent decimal seperator (available since fmt-8.0.0). I manually tested dnf5 and dnf5daemon-client and they work for me. Though there is a risk that the new C++ locale will affect some code uknown to me, like regular expression matching, or thread-specific locales. If the affected code was unfixable, we can resort to saving the desired C++ locale into a dedicated object accessible to format_size_aligned() and pass it explicitly for fmt::format. Thorough testing is welcome. Related: https://github.com/rpm-software-management/dnf5/issues/1687 --- dnf5/main.cpp | 10 +++++----- dnf5daemon-client/main.cpp | 8 ++++++-- libdnf5-cli/CMakeLists.txt | 3 +++ libdnf5-cli/utils/units.cpp | 9 ++++++++- test/libdnf5-cli/utils/test_utf8.cpp | 4 ++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/dnf5/main.cpp b/dnf5/main.cpp index 99083a5e2..d47eccc57 100644 --- a/dnf5/main.cpp +++ b/dnf5/main.cpp @@ -77,7 +77,6 @@ along with libdnf. If not, see . #include #include #include -#include #include #include @@ -85,6 +84,7 @@ along with libdnf. If not, see . #include #include #include +#include constexpr const char * DNF5_LOGGER_FILENAME = "dnf5.log"; @@ -940,11 +940,11 @@ static void print_new_leaves(Context & context) { } static void set_locale() { - auto * locale = setlocale(LC_ALL, ""); - if (locale) { - return; + try { + std::locale::global(std::locale("")); + } catch (std::runtime_error & ex) { + std::cerr << "Failed to set locale, defaulting to \"C\"" << std::endl; } - std::cerr << "Failed to set locale, defaulting to \"C\"" << std::endl; } } // namespace dnf5 diff --git a/dnf5daemon-client/main.cpp b/dnf5daemon-client/main.cpp index 7ac80bae5..cc102f826 100644 --- a/dnf5daemon-client/main.cpp +++ b/dnf5daemon-client/main.cpp @@ -42,13 +42,13 @@ along with libdnf. If not, see . #include #include #include -#include #include #include #include #include #include +#include namespace dnfdaemon::client { @@ -220,7 +220,11 @@ static void add_commands(Context & context) { int main(int argc, char * argv[]) { std::unique_ptr connection; - setlocale(LC_ALL, ""); + try { + std::locale::global(std::locale("")); + } catch (std::runtime_error & ext) { + } + dnfdaemon::client::Context context; diff --git a/libdnf5-cli/CMakeLists.txt b/libdnf5-cli/CMakeLists.txt index ec9163594..ff8c72d99 100644 --- a/libdnf5-cli/CMakeLists.txt +++ b/libdnf5-cli/CMakeLists.txt @@ -47,6 +47,9 @@ target_link_libraries(libdnf5-cli PRIVATE common) target_link_libraries(libdnf5-cli PUBLIC libdnf5) pkg_check_modules(LIBFMT REQUIRED fmt) +if (LIBFMT_VERSION VERSION_LESS 8.0.0) + add_definitions(-DFMT_PRE_8) +endif() list(APPEND LIBDNF5_CLI_PC_REQUIRES "${LIBFMT_MODULE_NAME}") target_link_libraries(libdnf5-cli PUBLIC ${LIBFMT_LIBRARIES}) diff --git a/libdnf5-cli/utils/units.cpp b/libdnf5-cli/utils/units.cpp index bf1d3c83d..92319882c 100644 --- a/libdnf5-cli/utils/units.cpp +++ b/libdnf5-cli/utils/units.cpp @@ -52,7 +52,14 @@ std::pair to_size(int64_t num) { std::string format_size_aligned(int64_t num) { auto [value, unit] = to_size(num); - return fmt::format("{0:.1f} {1:>3s}", value, unit); + return fmt::format( +#ifdef FMT_PRE_8 + "{0:.1f} {1:>3s}", +#else + "{0:.1Lf} {1:>3s}", +#endif + value, + unit); } diff --git a/test/libdnf5-cli/utils/test_utf8.cpp b/test/libdnf5-cli/utils/test_utf8.cpp index 097116be8..c776ee6b9 100644 --- a/test/libdnf5-cli/utils/test_utf8.cpp +++ b/test/libdnf5-cli/utils/test_utf8.cpp @@ -22,7 +22,7 @@ along with libdnf. If not, see . #include "utils/utf8.hpp" -#include +#include CPPUNIT_TEST_SUITE_REGISTRATION(UTF8Test); @@ -30,7 +30,7 @@ CPPUNIT_TEST_SUITE_REGISTRATION(UTF8Test); void UTF8Test::setUp() { // wide characters do not work at all until we set locales in the code - setlocale(LC_ALL, "C.UTF-8"); + std::locale::global(std::locale("C.UTF-8")); hello_world_en = "Hello world!"; hello_world_cs = "Ahoj světe!";