Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ endif()

if(WITH_TRANSLATIONS)
# define common command to generate the pot file from sources
list(APPEND XGETTEXT_COMMAND xgettext -F --from-code=UTF-8 --keyword=_ --keyword=M_ --keyword=P_:1,2 --keyword=MP_:1,2 --keyword=C_:1c,2 --keyword=MC_:1c,2 --keyword=CP_:1c,2,3 --keyword=MCP_:1c,2,3 -c)
list(APPEND XGETTEXT_COMMAND xgettext -F --from-code=UTF-8 --keyword=_ --keyword=M_ --keyword=P_:1,2 --keyword=MP_:1,2 --keyword=C_:1c,2 --keyword=MC_:1c,2 --keyword=CP_:1c,2,3 --keyword=MCP_:1c,2,3 --keyword=dgettext:2 -c)
add_custom_target(gettext-potfiles)
endif()

Expand Down
9 changes: 9 additions & 0 deletions cmake/Translations.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ find_package(Gettext)
set(COMPONENT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/..)
file(GLOB_RECURSE POT_SOURCES RELATIVE ${COMPONENT_PATH} ${COMPONENT_PATH}/*.cpp ${COMPONENT_PATH}/*.hpp)

# For libdnf5-cli, also include the userconfirm header file from the global include directory
if(GETTEXT_DOMAIN STREQUAL "libdnf5-cli")
set(USERCONFIRM_HEADER "${CMAKE_SOURCE_DIR}/include/libdnf5-cli/utils/userconfirm.hpp")
if(EXISTS ${USERCONFIRM_HEADER})
file(RELATIVE_PATH REL_USERCONFIRM ${COMPONENT_PATH} ${USERCONFIRM_HEADER})
list(APPEND POT_SOURCES ${REL_USERCONFIRM})
endif()
endif()

# target to refresh pot file from current sources
add_custom_target(${GETTEXT_DOMAIN}-pot
COMMENT "Generating fresh ${GETTEXT_DOMAIN}.pot file from sources"
Expand Down
61 changes: 51 additions & 10 deletions include/libdnf5-cli/utils/userconfirm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@
#ifndef LIBDNF5_CLI_UTILS_USERCONFIRM_HPP
#define LIBDNF5_CLI_UTILS_USERCONFIRM_HPP

#include "libdnf5-cli/defs.h"

#include <langinfo.h>
#include <libdnf5/conf/config_main.hpp>
#include <libdnf5/utils/bgettext/bgettext-lib.h>
#include <regex.h>

#include <iostream>
#include <string>
Expand All @@ -38,27 +43,63 @@ bool userconfirm(Config & config) {
if (config.get_assumeyes_option().get_value()) {
return true;
}
std::string msg;
if (config.get_defaultyes_option().get_value()) {
msg = "Is this ok [Y/n]: ";

auto default_yes = config.get_defaultyes_option().get_value();

const char * msg_id;
const char * msg_translated;

if (default_yes) {
msg_id = "Is this ok [Y/n]: ";
msg_translated = dgettext("libdnf5-cli", "Is this ok [Y/n]: ");
} else {
msg = "Is this ok [y/N]: ";
msg_id = "Is this ok [y/N]: ";
msg_translated = dgettext("libdnf5-cli", "Is this ok [y/N]: ");
}

// If the prompt was translated, we assume the system locale (nl_langinfo) matches the language of the prompt.
// If the prompt fell back to English (no translation found), we MUST use English regexes to avoid
// dangerous mismatches (e.g. Swahili system locale where 'n' means Yes, but displaying English [y/N]).
std::string yes_pattern;
std::string no_pattern;

if (std::string(msg_translated) != msg_id) {
yes_pattern = nl_langinfo(YESEXPR);
no_pattern = nl_langinfo(NOEXPR);
} else {
yes_pattern = "^[yY]";
no_pattern = "^[nN]";
}

while (true) {
std::cerr << msg;
std::cerr << msg_translated;

std::string choice;
std::getline(std::cin, choice);

if (choice.empty()) {
return config.get_defaultyes_option().get_value();
return default_yes;
}
if (choice == "y" || choice == "Y") {
return true;

// Match user input against translator-provided patterns
// These patterns stay in sync with the translated prompt
regex_t regex;
if (regcomp(&regex, yes_pattern.c_str(), REG_EXTENDED | REG_NOSUB) == 0) {
int result = regexec(&regex, choice.c_str(), 0, nullptr, 0);
regfree(&regex);
if (result == 0) {
return true;
}
}
if (choice == "n" || choice == "N") {
return false;

if (regcomp(&regex, no_pattern.c_str(), REG_EXTENDED | REG_NOSUB) == 0) {
int result = regexec(&regex, choice.c_str(), 0, nullptr, 0);
regfree(&regex);
if (result == 0) {
return false;
}
}
// If patterns didn't match, the input is invalid - loop and ask again
}
}

Expand Down