Merge pull request #2207 from rixnobis/i18n-ctx

i18n: msgctxt support, fewer untranslatable strings, fuzzy fix
This commit is contained in:
Rix
2026-10-04 13:17:53 -07:00
committed by GitHub
24 changed files with 272 additions and 115 deletions
+1 -1
View File
@@ -393,7 +393,7 @@ endef
regen-i18n:
find src -name *.cc -or -name *.c -or -name *.h | sort -u > pcsx-src-list.txt
xgettext --from-code=utf-8 --keyword=_ --keyword=f_ --keyword=l_ --language=C++ --add-comments --sort-by-file -o i18n/pcsx-redux.pot -f pcsx-src-list.txt
xgettext --from-code=utf-8 --keyword=_ --keyword=f_ --keyword=l_ --keyword=C_:1c,2 --keyword=lC_:1c,2 --language=C++ --add-comments --sort-by-file -o i18n/pcsx-redux.pot -f pcsx-src-list.txt
find src -name *.lua | sort -u > pcsx-src-list.txt
xgettext --from-code=utf-8 --keyword=t_ --language=Lua --join-existing --sort-by-file -o i18n/pcsx-redux.pot -f pcsx-src-list.txt
sed '/POT-Creation-Date/d' -i i18n/pcsx-redux.pot
+1 -1
View File
@@ -478,7 +478,7 @@ bool PCSX::OpenGL_GPU::configure() {
int msaaSampleCount = g_emulator->settings.get<Emulator::SettingMSAA>();
const auto msaaString = msaaSampleCount == 1 ? _("No MSAA") : fmt::format(f_("{}x MSAA"), msaaSampleCount);
if (ImGui::BeginCombo(_("MSAA"), msaaString.c_str())) {
if (ImGui::BeginCombo("MSAA", msaaString.c_str())) {
const int maxMSAA = OpenGL::maxSamples();
if (ImGui::Selectable(_("No MSAA"))) {
+2 -1
View File
@@ -35,7 +35,8 @@ class Debug {
Debug();
static uint32_t normalizeAddress(uint32_t address);
static bool isInKernel(uint32_t address, bool biosIsKernel = true);
static inline std::function<const char*()> s_breakpoint_type_names[] = {l_("Exec"), l_("Read"), l_("Write")};
static inline std::function<const char*()> s_breakpoint_type_names[] = {
lC_("Breakpoint type", "Exec"), lC_("Breakpoint type", "Read"), lC_("Breakpoint type", "Write")};
enum class BreakpointType { Exec, Read, Write };
enum class BreakpointCondition { Always, Change, Greater, Less, Equal };
+2 -1
View File
@@ -434,7 +434,8 @@ void PCSX::GPU::DrawingAreaEnd::drawLogNode(unsigned, const DrawLogSettings&) {
void PCSX::GPU::DrawingOffset::drawLogNode(unsigned, const DrawLogSettings&) { ImGui::Text(" X: %i, Y: %i", x, y); }
void PCSX::GPU::MaskBit::drawLogNode(unsigned, const DrawLogSettings&) {
ImGui::Text(_(" Set: %s, Check: %s"), set ? _("Yes") : _("No"), check ? _("Yes") : _("No"));
ImGui::Text(" %s",
fmt::format(f_("Set: {}, Check: {}"), set ? _("Yes") : _("No"), check ? _("Yes") : _("No")).c_str());
}
void PCSX::GPU::CtrlReset::drawLogNode(unsigned, const DrawLogSettings&) {}
+17 -6
View File
@@ -1035,14 +1035,25 @@ bool PadsImpl::Pad::configure() {
l_("Keyboard"),
};
static std::function<const char*()> const c_buttonNames[] = {
l_("╳"), l_("□"), l_("△"), l_("◯"), l_("Select"), l_("Start"), l_("L1"),
l_("R1"), l_("L2"), l_("R2"), l_("L3"), l_("R3"), l_("Analog Mode"),
[]() { return "╳"; },
[]() { return "□"; },
[]() { return "△"; },
[]() { return "◯"; },
lC_("Controller button", "Select"),
lC_("Controller button", "Start"),
[]() { return "L1"; },
[]() { return "R1"; },
[]() { return "L2"; },
[]() { return "R2"; },
[]() { return "L3"; },
[]() { return "R3"; },
l_("Analog Mode"),
};
static std::function<const char*()> const c_dpadDirections[] = {
l_("↑"),
l_("→"),
l_("↓"),
l_("←"),
[]() { return "↑"; },
[]() { return "→"; },
[]() { return "↓"; },
[]() { return "←"; },
};
static std::function<const char*()> const c_controllerTypes[] = {
l_("Digital"),
+5 -6
View File
@@ -215,12 +215,11 @@ void PCSX::Memory::reset() {
m_bios[index++] = w & 0xff;
w >>= 8;
}
strcpy((char *)m_bios + index, _(R"(
No BIOS loaded, emulation halted.
Set a BIOS file into the configuration, and do a hard reset of the emulator.
The distributed OpenBIOS.bin file can be an appropriate BIOS replacement.
)"));
std::string nobiosMessage = fmt::format("\n{:19}{}\n\n{}\n", "", _("No BIOS loaded, emulation halted."),
_("Set a BIOS file into the configuration, and do a hard reset of the "
"emulator.\nThe distributed OpenBIOS.bin file can be an appropriate "
"BIOS replacement."));
strcpy((char *)m_bios + index, nobiosMessage.c_str());
uint32_t nobioscrc = crc32(0L, Z_NULL, 0);
nobioscrc = crc32(nobioscrc, m_bios, bios_size);
+54 -21
View File
@@ -119,25 +119,31 @@ bool PCSX::System::loadLocale(const std::string& name, const std::filesystem::pa
unsigned inlineIndex = 0;
unsigned shift = 0;
uint8_t inlined = 0;
enum : int {
enum {
WAITING_MSGIDTOKEN,
WAITING_MSGCTXT,
WAITING_MSGIDTOKEN_AFTER_CTXT,
WAITING_MSGID,
WAITING_MSGSTRTOKEN,
WAITING_MSGSTR,
STATE_MAX
} state = WAITING_MSGIDTOKEN;
uint64_t hashValue;
std::string context;
bool hasContext = false;
std::map<uint64_t, std::string> locale;
if (in->failed()) return false;
std::string comment;
// The flags comment comes before the entry it applies to, which is only stored
// once the next entry starts, so it is kept per entry.
bool fuzzy = false;
bool entryFuzzy = false;
while ((c = in->getc()) >= 0) {
if (c == '\n' || c == '\r') {
if (inString) return false;
if (inComment) fuzzy = comment.find(", fuzzy") != std::string::npos;
if (inComment && comment.find(", fuzzy") != std::string::npos) fuzzy = true;
inComment = false;
newLine = true;
comment.clear();
@@ -154,7 +160,10 @@ bool PCSX::System::loadLocale(const std::string& name, const std::filesystem::pa
continue;
}
if (c == '\"') {
if (state != WAITING_MSGIDTOKEN && state != WAITING_MSGSTRTOKEN) return false;
if (state != WAITING_MSGIDTOKEN && state != WAITING_MSGIDTOKEN_AFTER_CTXT &&
state != WAITING_MSGSTRTOKEN) {
return false;
}
inString = true;
continue;
}
@@ -229,25 +238,42 @@ bool PCSX::System::loadLocale(const std::string& name, const std::filesystem::pa
} else {
if (c == ' ' || c == '\t') continue;
switch (state) {
case WAITING_MSGCTXT:
case WAITING_MSGID:
case WAITING_MSGSTR:
if (c == '\"') {
inString = true;
(*reinterpret_cast<int*>(&state))++;
if (state == STATE_MAX) state = WAITING_MSGIDTOKEN;
} else {
return false;
if (c != '\"') return false;
inString = true;
switch (state) {
case WAITING_MSGCTXT:
state = WAITING_MSGIDTOKEN_AFTER_CTXT;
break;
case WAITING_MSGID:
state = WAITING_MSGSTRTOKEN;
break;
default:
state = WAITING_MSGIDTOKEN;
break;
}
break;
case WAITING_MSGIDTOKEN:
case WAITING_MSGIDTOKEN_AFTER_CTXT:
case WAITING_MSGSTRTOKEN:
if (token.empty()) {
switch (state) {
case WAITING_MSGIDTOKEN:
if (!currentString.empty() && !fuzzy) locale[hashValue] = currentString;
if (!currentString.empty() && !entryFuzzy) locale[hashValue] = currentString;
entryFuzzy = fuzzy;
fuzzy = false;
break;
case WAITING_MSGSTRTOKEN:
hashValue = djb::hash(currentString);
case WAITING_MSGIDTOKEN_AFTER_CTXT:
context = currentString;
hasContext = true;
break;
default:
// A string with a context is looked up as "context\004string",
// the same key gettext uses for msgctxt entries.
hashValue = djb::hash(hasContext ? context + '\004' + currentString : currentString);
hasContext = false;
break;
}
currentString = "";
@@ -255,19 +281,26 @@ bool PCSX::System::loadLocale(const std::string& name, const std::filesystem::pa
token += c;
switch (state) {
case WAITING_MSGIDTOKEN:
if (token == "msgid") {
token = "";
state = WAITING_MSGID;
} else if (token == "msgctxt") {
token = "";
state = WAITING_MSGCTXT;
} else if (token.length() >= 7) {
return false;
}
break;
case WAITING_MSGIDTOKEN_AFTER_CTXT:
if (token.length() == 5) {
if (token != "msgid") {
return false;
}
if (token != "msgid") return false;
token = "";
state = WAITING_MSGID;
}
break;
case WAITING_MSGSTRTOKEN:
default:
if (token.length() == 6) {
if (token != "msgstr") {
return false;
}
if (token != "msgstr") return false;
token = "";
state = WAITING_MSGSTR;
}
@@ -280,7 +313,7 @@ bool PCSX::System::loadLocale(const std::string& name, const std::filesystem::pa
if (inString || (state != WAITING_MSGIDTOKEN)) return false;
if (!currentString.empty() && !fuzzy) locale[hashValue] = currentString;
if (!currentString.empty() && !entryFuzzy) locale[hashValue] = currentString;
m_locales[name] = locale;
return true;
}
+5
View File
@@ -308,3 +308,8 @@ extern System *g_system;
#define f_(str) fmt::runtime(PCSX::g_system->getStr(PCSX::djb::ctHash(str), str))
// Lambda string lookup to use with static arrays of strings
#define l_(str) []() { return PCSX::g_system->getStr(PCSX::djb::ctHash(str), str); }
// Same as _() and l_(), with a translation context for strings whose meaning
// depends on where they're used, e.g. C_("Menu", "Update"). The context shows up
// as msgctxt in the .pot file, and both arguments need to be string literals.
#define C_(ctx, str) PCSX::g_system->getStr(PCSX::djb::ctHash(ctx "\004" str), str)
#define lC_(ctx, str) []() { return PCSX::g_system->getStr(PCSX::djb::ctHash(ctx "\004" str), str); }
+6 -6
View File
@@ -804,7 +804,7 @@ void PCSX::GUI::init(std::function<void()> applyArguments) {
m_parallelPortEditor.title = l_("Parallel Port");
m_scratchPadEditor.title = l_("Scratch Pad");
m_hwrEditor.title = l_("Hardware Registers");
m_biosEditor.title = l_("BIOS");
m_biosEditor.title = []() { return "BIOS"; };
m_vramEditor.title = l_("VRAM");
auto makeExportFn = [this](MemoryEditorWrapper& wrapper, std::string postfixName) {
return [this, &wrapper, postfixName](size_t len, size_t base_addr) {
@@ -1490,7 +1490,7 @@ void PCSX::GUI::endFrame() {
ImGui::Separator();
if (ImGui::BeginMenu(_("Debug"))) {
ImGui::MenuItem(_("Show Logs"), nullptr, &m_log.m_show);
if (ImGui::BeginMenu(_("Lua"))) {
if (ImGui::BeginMenu("Lua")) {
ImGui::MenuItem(_("Show Lua Console"), nullptr, &m_luaConsole.m_show);
ImGui::MenuItem(_("Show Lua Inspector"), nullptr, &m_luaInspector.m_show);
ImGui::MenuItem(_("Show Lua editor"), nullptr, &m_luaEditor.m_show);
@@ -1620,7 +1620,7 @@ in Configuration->Emulation, restart PCSX-Redux, then try again.)"));
uint32_t frameCount = g_emulator->m_spu->getFrameCount();
ImGui::Text(_("%.2f ms audio buffer (%i frames)"), 1000.0f * frameCount / 44100.0f, frameCount);
} else {
ImGui::TextUnformatted(_("Idle"));
ImGui::TextUnformatted(C_("Audio status", "Idle"));
}
ImGui::EndMainMenuBar();
@@ -2010,7 +2010,7 @@ the update and manually apply it.)")));
}
ImGui::ProgressBar(m_update.progress());
if (!m_updateDownloading) {
if (ImGui::Button(_("Update"))) {
if (ImGui::Button(C_("Update dialog button", "Update"))) {
m_updateDownloading = true;
bool started = m_update.downloadAndApplyUpdate(
g_system->getVersion(),
@@ -2033,7 +2033,7 @@ the update and manually apply it.)")));
}
}
ImGui::SameLine();
if (ImGui::Button(_("Download"))) {
if (ImGui::Button(C_("Update dialog button", "Download"))) {
m_updateDownloading = true;
bool started = m_update.getDownloadUrl(
g_system->getVersion(),
@@ -2563,7 +2563,7 @@ bool PCSX::GUI::about() {
if (version.buildId.has_value()) {
ImGui::Text(_("Build: %i"), version.buildId.value());
}
ImGui::TextUnformatted(_("Changeset: "));
ImGui::TextUnformatted(_("Changeset:"));
ImGui::SameLine();
if (ImGui::SmallButton(version.changeset.c_str())) {
openUrl(fmt::format("https://github.com/grumpycoders/pcsx-redux/commit/{}", version.changeset));
+4 -4
View File
@@ -498,8 +498,8 @@ bool PCSX::Widgets::Assembly::draw(GUI* gui, psxRegisters* registers, Memory* me
ImGui::EndMenu();
}
if (ImGui::BeginMenu(_("Debug"))) {
if (ImGui::MenuItem(_("Pause"), "F6", nullptr, g_system->running())) g_system->pause();
if (ImGui::MenuItem(_("Resume"), "F5", nullptr, !g_system->running())) g_system->resume();
if (ImGui::MenuItem(C_("Emulation", "Pause"), "F6", nullptr, g_system->running())) g_system->pause();
if (ImGui::MenuItem(C_("Emulation", "Resume"), "F5", nullptr, !g_system->running())) g_system->resume();
ImGui::Separator();
if (ImGui::MenuItem(_("Step In"), "F11", nullptr, !g_system->running())) g_emulator->m_debug->stepIn();
if (ImGui::MenuItem(_("Step Over"), "F10", nullptr, !g_system->running())) g_emulator->m_debug->stepOver();
@@ -574,9 +574,9 @@ settings, otherwise debugging features may not work.)");
if (ImGui::Button(_("Jump to PC"))) {
m_jumpToPC = m_registers->pc;
}
DButton(_("Pause"), g_system->running(), [&]() mutable { g_system->pause(); });
DButton(C_("Emulation", "Pause"), g_system->running(), [&]() mutable { g_system->pause(); });
ImGui::SameLine();
DButton(_("Resume"), !g_system->running(), [&]() mutable { g_system->resume(); });
DButton(C_("Emulation", "Resume"), !g_system->running(), [&]() mutable { g_system->resume(); });
ImGui::SameLine();
DButton(_("Step In"), !g_system->running(), [&]() mutable { g_emulator->m_debug->stepIn(); });
ImGui::SameLine();
+38 -28
View File
@@ -37,15 +37,15 @@ static uint32_t getValueAboutToWrite(PCSX::R3000Acpu& cpu) {
static const char* getBreakpointConditionName(PCSX::Debug::BreakpointCondition condition) {
switch (condition) {
case PCSX::Debug::BreakpointCondition::Always:
return _("Always");
return C_("Breakpoint condition", "Always");
case PCSX::Debug::BreakpointCondition::Greater:
return _("Greater");
return C_("Breakpoint condition", "Greater");
case PCSX::Debug::BreakpointCondition::Less:
return _("Less");
return C_("Breakpoint condition", "Less");
case PCSX::Debug::BreakpointCondition::Change:
return _("Change");
return C_("Breakpoint condition", "Change");
case PCSX::Debug::BreakpointCondition::Equal:
return _("Equal");
return C_("Breakpoint condition", "Equal");
}
return _("Unknown");
}
@@ -192,9 +192,9 @@ void PCSX::Widgets::Breakpoints::draw(const char* title) {
static int range = 8;
static int width = 1;
if (m_breakpointType != (int)Debug::BreakpointType::Exec) {
ImGui::RadioButton(_("Byte"), &width, 1);
ImGui::RadioButton(_("Half"), &width, 2);
ImGui::RadioButton(_("Word"), &width, 4);
ImGui::RadioButton(C_("Memory access width", "Byte"), &width, 1);
ImGui::RadioButton(C_("Memory access width", "Half"), &width, 2);
ImGui::RadioButton(C_("Memory access width", "Word"), &width, 4);
ImGui::RadioButton(_("Range"), &width, 0);
if (width == 0) {
@@ -388,28 +388,38 @@ void PCSX::Widgets::Breakpoints::draw(const char* title) {
_("The mapping feature is a simple concept, but requires some amount of explanation. See the documentation "
"website for more details, in the Misc Features subsection of the Debugging section."));
ImGui::Checkbox(_("Map execution"), &debugger->m_mapping_e);
ImGui::Checkbox(_("Map byte reads "), &debugger->m_mapping_r8);
ImGui::SameLine();
ImGui::Checkbox(_("Map half reads "), &debugger->m_mapping_r16);
ImGui::SameLine();
ImGui::Checkbox(_("Map word reads "), &debugger->m_mapping_r32);
ImGui::Checkbox(_("Map byte writes "), &debugger->m_mapping_w8);
ImGui::SameLine();
ImGui::Checkbox(_("Map half writes "), &debugger->m_mapping_w16);
ImGui::SameLine();
ImGui::Checkbox(_("Map word writes "), &debugger->m_mapping_w32);
if (ImGui::BeginTable("MapFlags", 3, ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableNextColumn();
ImGui::Checkbox(_("Map byte reads"), &debugger->m_mapping_r8);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Map half reads"), &debugger->m_mapping_r16);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Map word reads"), &debugger->m_mapping_r32);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Map byte writes"), &debugger->m_mapping_w8);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Map half writes"), &debugger->m_mapping_w16);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Map word writes"), &debugger->m_mapping_w32);
ImGui::EndTable();
}
ImGui::Separator();
ImGui::Checkbox(_("Break on execution map"), &debugger->m_breakmp_e);
ImGui::Checkbox(_("Break on byte read map "), &debugger->m_breakmp_r8);
ImGui::SameLine();
ImGui::Checkbox(_("Break on half read map "), &debugger->m_breakmp_r16);
ImGui::SameLine();
ImGui::Checkbox(_("Break on word read map "), &debugger->m_breakmp_r32);
ImGui::Checkbox(_("Break on byte write map"), &debugger->m_breakmp_w8);
ImGui::SameLine();
ImGui::Checkbox(_("Break on half write map"), &debugger->m_breakmp_w16);
ImGui::SameLine();
ImGui::Checkbox(_("Break on word write map"), &debugger->m_breakmp_w32);
if (ImGui::BeginTable("BreakFlags", 3, ImGuiTableFlags_SizingFixedFit)) {
ImGui::TableNextColumn();
ImGui::Checkbox(_("Break on byte read map"), &debugger->m_breakmp_r8);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Break on half read map"), &debugger->m_breakmp_r16);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Break on word read map"), &debugger->m_breakmp_r32);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Break on byte write map"), &debugger->m_breakmp_w8);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Break on half write map"), &debugger->m_breakmp_w16);
ImGui::TableNextColumn();
ImGui::Checkbox(_("Break on word write map"), &debugger->m_breakmp_w32);
ImGui::EndTable();
}
ImGui::TreePop();
}
+2 -2
View File
@@ -35,12 +35,12 @@ void PCSX::Widgets::Events::draw(IO<File> memFile, const char* title) {
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;
auto events = Kernel::Events::getAllEvents(memFile);
if (ImGui::BeginTable("Events", 6, flags)) {
ImGui::TableSetupColumn(_("ID"));
ImGui::TableSetupColumn("ID");
ImGui::TableSetupColumn(_("Core"));
ImGui::TableSetupColumn(_("Spec"));
ImGui::TableSetupColumn(_("Mode"));
ImGui::TableSetupColumn(_("Flag"));
ImGui::TableSetupColumn(_("CB"));
ImGui::TableSetupColumn("CB");
ImGui::TableHeadersRow();
for (auto& ev : events) {
ImGui::TableNextRow();
+2 -2
View File
@@ -107,10 +107,10 @@ void PCSX::Widgets::GPUDump::drawPlayer() {
if (!player->comment().empty()) ImGui::Text(_("Comment: %s"), player->comment().c_str());
ImGui::Text(_("Frame %llu"), static_cast<unsigned long long>(player->frame()));
if (ImGui::Button(m_playing ? _("Pause") : _("Play"))) m_playing = !m_playing;
if (ImGui::Button(m_playing ? C_("Playback", "Pause") : C_("Playback", "Play"))) m_playing = !m_playing;
ImGui::SameLine();
ImGui::BeginDisabled(m_playing);
if (ImGui::Button(_("Step"))) player->step();
if (ImGui::Button(C_("Playback", "Step"))) player->step();
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button(_("Rewind"))) player->rewind();
+3 -4
View File
@@ -57,8 +57,7 @@ void PCSX::Widgets::GPULogger::draw(PCSX::GPULogger* logger, const char* title)
colorFormat == GPU::Logged::DrawLogSettings::ColorFormat::Expanded)) {
colorFormat = GPU::Logged::DrawLogSettings::ColorFormat::Expanded;
}
if (ImGui::MenuItem(_("HTML"), nullptr,
colorFormat == GPU::Logged::DrawLogSettings::ColorFormat::HTML)) {
if (ImGui::MenuItem("HTML", nullptr, colorFormat == GPU::Logged::DrawLogSettings::ColorFormat::HTML)) {
colorFormat = GPU::Logged::DrawLogSettings::ColorFormat::HTML;
}
ImGui::EndMenu();
@@ -81,7 +80,7 @@ void PCSX::Widgets::GPULogger::draw(PCSX::GPULogger* logger, const char* title)
"and memory."));
ImGui::Checkbox(_("Breakpoint on vsync"), &logger->m_breakOnVSync);
ImGui::SameLine();
if (ImGui::Button(_("Resume"))) {
if (ImGui::Button(C_("Emulation", "Resume"))) {
g_system->resume();
}
ImGui::Checkbox(_("Replay frame"), &m_replay);
@@ -245,7 +244,7 @@ void PCSX::Widgets::GPULogger::draw(PCSX::GPULogger* logger, const char* title)
break;
}
ImGui::SameLine();
ImGui::TextUnformatted(_("at PC = "));
ImGui::TextUnformatted(_("at PC ="));
ImGui::SameLine();
label = fmt::format("{:08x}", logged.pc);
if (ImGui::Button(label.c_str())) {
+3 -3
View File
@@ -58,20 +58,20 @@ void PCSX::Widgets::Handlers::draw(const uint32_t* psxMemory, const char* title)
}
while (infoAddr) {
std::string buttonStr;
ImGui::TextUnformatted(_("Handler data at "));
ImGui::TextUnformatted(_("Handler data at"));
ImGui::SameLine();
buttonStr = fmt::format("{:08x}##{}", infoAddr, counter++);
if (ImGui::Button(buttonStr.c_str())) {
g_system->m_eventBus->signal(Events::GUI::JumpToMemory{infoAddr, 16});
}
ImGui::TextUnformatted(_(" verifier: "));
ImGui::Text(" %s", _("verifier:"));
ImGui::SameLine();
uint32_t verifierAddr = memFile->readAt<uint32_t>(infoAddr + 8);
buttonStr = fmt::format("{:08x}##{}", verifierAddr, counter++);
if (ImGui::Button(buttonStr.c_str())) {
g_system->m_eventBus->signal(Events::GUI::JumpToPC{verifierAddr});
}
ImGui::TextUnformatted(_(" handler: "));
ImGui::Text(" %s", _("handler:"));
ImGui::SameLine();
uint32_t handlerAddr = memFile->readAt<uint32_t>(infoAddr + 4);
buttonStr = fmt::format("{:08x}##{}", handlerAddr, counter++);
+6 -6
View File
@@ -401,7 +401,7 @@ headers and subheader file boundary markers.)"));
ImVec2(0, ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() -
ImGui::GetStyle().ItemSpacing.y))) {
ImGui::TableSetupColumn(_("Path"), ImGuiTableColumnFlags_NoHide);
ImGui::TableSetupColumn(_("LBA"), ImGuiTableColumnFlags_WidthFixed, 80.0f);
ImGui::TableSetupColumn("LBA", ImGuiTableColumnFlags_WidthFixed, 80.0f);
ImGui::TableSetupColumn(_("Size"), ImGuiTableColumnFlags_WidthFixed, 100.0f);
ImGui::TableSetupColumn(_("Type"), ImGuiTableColumnFlags_WidthFixed, 60.0f);
ImGui::TableHeadersRow();
@@ -508,13 +508,13 @@ headers and subheader file boundary markers.)"));
typeStr = _("Gap");
break;
case FlatEntry::HiddenM1:
typeStr = _("M1");
typeStr = "M1";
break;
case FlatEntry::HiddenM2F1:
typeStr = _("M2F1");
typeStr = "M2F1";
break;
case FlatEntry::HiddenM2F2:
typeStr = _("M2F2");
typeStr = "M2F2";
break;
case FlatEntry::System:
typeStr = _("System");
@@ -618,7 +618,7 @@ significantly by caching the files beforehand.)"));
ImGui::TextUnformatted(str.c_str());
if (ImGui::BeginTable("Tracks", 5, ImGuiTableFlags_Resizable)) {
ImGui::TableSetupColumn(_("Track"));
ImGui::TableSetupColumn(_("Start"));
ImGui::TableSetupColumn(C_("Track position", "Start"));
ImGui::TableSetupColumn(_("Length"));
ImGui::TableSetupColumn(_("Pregap"));
ImGui::TableSetupColumn("CRC32");
@@ -809,7 +809,7 @@ significantly by caching the files beforehand.)"));
ImVec2(0, ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() -
ImGui::GetStyle().ItemSpacing.y))) {
ImGui::TableSetupColumn(_("Name"), ImGuiTableColumnFlags_NoHide);
ImGui::TableSetupColumn(_("LBA"), ImGuiTableColumnFlags_WidthFixed, 80.0f);
ImGui::TableSetupColumn("LBA", ImGuiTableColumnFlags_WidthFixed, 80.0f);
ImGui::TableSetupColumn(_("Size"), ImGuiTableColumnFlags_WidthFixed, 100.0f);
ImGui::TableHeadersRow();
std::unordered_set<uint32_t> visitedDirs;
+2 -2
View File
@@ -138,7 +138,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
g_emulator->m_sio->saveMcd(1);
g_emulator->m_sio->saveMcd(2);
}
ImGui::TextUnformatted(_("Undo version: "));
ImGui::TextUnformatted(_("Undo version:"));
ImGui::SameLine();
if (isLatest) {
ImGui::TextUnformatted(_("Latest"));
@@ -197,7 +197,7 @@ bool PCSX::Widgets::MemcardManager::draw(GUI* gui, const char* title) {
ImGui::TableSetupColumn(_("Block number"));
ImGui::TableSetupColumn(_("Icon"));
ImGui::TableSetupColumn(_("Title"));
ImGui::TableSetupColumn(_("ID"));
ImGui::TableSetupColumn("ID");
ImGui::TableSetupColumn(_("Filename"));
ImGui::TableSetupColumn(_("Action"));
ImGui::TableHeadersRow();
+2 -2
View File
@@ -397,7 +397,7 @@ void PCSX::Widgets::MemoryObserver::draw(const char* title) {
if (ImGui::BeginTabItem(_("Pattern search"))) {
if (m_useSIMD) {
ImGui::TextUnformatted(_("Sequence size: "));
ImGui::TextUnformatted(_("Sequence size:"));
ImGui::SameLine();
ImGui::RadioButton(_("8 bytes (fast)"), &m_sequenceSize, 8);
ImGui::SameLine();
@@ -408,7 +408,7 @@ void PCSX::Widgets::MemoryObserver::draw(const char* title) {
ImGui::InputText(_("Sequence"), m_sequence, m_sequenceSize + 1);
ImGui::InputInt(_("Step"), &m_step);
ImGui::InputInt(C_("Search increment", "Step"), &m_step);
if (m_step >= 1 && ImGui::Button(_("Search"))) {
if (m_useSIMD && m_sequenceSize == 8) {
+1 -1
View File
@@ -78,7 +78,7 @@ void PCSX::Widgets::NamedSaveStates::draw(GUI* gui, const char* title) {
float posY = ImGui::GetCursorPosY();
ImGui::SetCursorPosY(posY + verticalAlignAdjust);
ImGui::TextUnformatted(_("Filename: "));
ImGui::TextUnformatted(_("Filename:"));
ImGui::SameLine();
ImGui::TextUnformatted(gui->getSaveStatePrefix(true).c_str());
ImGui::SameLine(0.0f, 0.0f);
+2 -1
View File
@@ -85,7 +85,8 @@ void PCSX::Widgets::Patches::draw(const char* title) {
ImGui::PopID();
ImGui::TableSetColumnIndex(3);
ImGui::TextUnformatted(patch.type == PCSX::PatchManager::Patch::Type::Return ? _("Return") : _("NOP"));
ImGui::TextUnformatted(patch.type == PCSX::PatchManager::Patch::Type::Return ? C_("Patch type", "Return")
: "NOP");
}
ImGui::EndTable();
+1 -1
View File
@@ -33,7 +33,7 @@ bool PCSX::Widgets::PIOCart::draw(const char* title) {
return false;
} else {
auto pio_rom = settings.get<Emulator::SettingEXP1Filepath>().string();
ImGui::InputText(_("ROM"), const_cast<char*>(reinterpret_cast<const char*>(pio_rom.c_str())), pio_rom.length(),
ImGui::InputText("ROM", const_cast<char*>(reinterpret_cast<const char*>(pio_rom.c_str())), pio_rom.length(),
ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
selectEXP1Dialog = ImGui::Button("...");
+15 -15
View File
@@ -357,7 +357,7 @@ void PCSX::Widgets::TypedDebugger::displayNode(WatchTreeNode* node, const uint32
bool addressOfPointer, uint32_t extraImGuiId) {
ImGui::TableNextRow();
ImGui::TableNextColumn(); // Name.
std::string nameColumnString = fmt::format(f_("{}\t@ {:#x}##{}"), node->name, currentAddress, extraImGuiId);
std::string nameColumnString = fmt::format("{}\t@ {:#x}##{}", node->name, currentAddress, extraImGuiId);
IO<File> memFile = g_emulator->m_mem->getMemoryAsFile();
const char* nodeType = node->type.c_str();
@@ -560,16 +560,16 @@ void PCSX::Widgets::TypedDebugger::displayNewValueInput(const char* type, size_t
case 1:
if (equals(type, "char")) {
m_newValues[address] = *value.data<int8_t>();
if (ImGui::InputScalar(fmt::format(f_("##{}"), address).c_str(), ImGuiDataType_S8,
&m_newValues[address], &step, &stepFast, signedFormat, inputFlags)) {
if (ImGui::InputScalar(fmt::format("##{}", address).c_str(), ImGuiDataType_S8, &m_newValues[address],
&step, &stepFast, signedFormat, inputFlags)) {
memFile->write<int8_t>(m_newValues[address]);
}
} else {
// We have a uchar or something of size 1.
m_newValues[address] = *value.data<uint8_t>();
if (ImGui::InputScalar(fmt::format(f_("##{}"), address).c_str(), ImGuiDataType_U8,
&m_newValues[address], &step, &stepFast, unsignedFormat, inputFlags)) {
if (ImGui::InputScalar(fmt::format("##{}", address).c_str(), ImGuiDataType_U8, &m_newValues[address],
&step, &stepFast, unsignedFormat, inputFlags)) {
memFile->write<uint8_t>(m_newValues[address]);
}
}
@@ -577,15 +577,15 @@ void PCSX::Widgets::TypedDebugger::displayNewValueInput(const char* type, size_t
case 2:
if (equals(type, "short")) {
m_newValues[address] = *value.data<int16_t>();
if (ImGui::InputScalar(fmt::format(f_("##{}"), address).c_str(), ImGuiDataType_S16,
&m_newValues[address], &step, &stepFast, signedFormat, inputFlags)) {
if (ImGui::InputScalar(fmt::format("##{}", address).c_str(), ImGuiDataType_S16, &m_newValues[address],
&step, &stepFast, signedFormat, inputFlags)) {
memFile->write<int16_t>(m_newValues[address]);
}
} else {
// We have a ushort or something of size 2.
m_newValues[address] = *value.data<uint16_t>();
if (ImGui::InputScalar(fmt::format(f_("##{}"), address).c_str(), ImGuiDataType_U16,
&m_newValues[address], &step, &stepFast, unsignedFormat, inputFlags)) {
if (ImGui::InputScalar(fmt::format("##{}", address).c_str(), ImGuiDataType_U16, &m_newValues[address],
&step, &stepFast, unsignedFormat, inputFlags)) {
memFile->write<uint16_t>(m_newValues[address]);
}
}
@@ -593,15 +593,15 @@ void PCSX::Widgets::TypedDebugger::displayNewValueInput(const char* type, size_t
case 4:
if (equals(type, "int") || equals(type, "long")) {
m_newValues[address] = *value.data<int32_t>();
if (ImGui::InputScalar(fmt::format(f_("##{}"), address).c_str(), ImGuiDataType_S32,
&m_newValues[address], &step, &stepFast, signedFormat, inputFlags)) {
if (ImGui::InputScalar(fmt::format("##{}", address).c_str(), ImGuiDataType_S32, &m_newValues[address],
&step, &stepFast, signedFormat, inputFlags)) {
memFile->write<int32_t>(m_newValues[address]);
}
} else {
// We have uint or something of size 4.
m_newValues[address] = *value.data<uint32_t>();
if (ImGui::InputScalar(fmt::format(f_("##{}"), address).c_str(), ImGuiDataType_U32,
&m_newValues[address], &step, &stepFast, unsignedFormat, inputFlags)) {
if (ImGui::InputScalar(fmt::format("##{}", address).c_str(), ImGuiDataType_U32, &m_newValues[address],
&step, &stepFast, unsignedFormat, inputFlags)) {
memFile->write<uint32_t>(m_newValues[address]);
}
}
@@ -781,7 +781,7 @@ Arrays and pointers are specified as for data types)"));
ImGui::SameLine();
if (numberToCreate > 0 && ImGui::Button("Add") && m_structs.contains(type)) {
WatchTreeNode rootNode;
const auto inputType = createArray ? fmt::format(f_("{}[{}]"), type, numberToCreate) : type;
const auto inputType = createArray ? fmt::format("{}[{}]", type, numberToCreate) : type;
rootNode.type = inputType;
rootNode.name = inputType;
for (const auto& field : m_structs[type]) {
@@ -823,7 +823,7 @@ Arrays and pointers are specified as for data types)"));
ImGui::Checkbox("Input in hexadecimal", &m_hex);
gui->useMonoFont();
if (ImGui::BeginTable(_("WatchTable"), 6, treeTableFlags)) {
if (ImGui::BeginTable("WatchTable", 6, treeTableFlags)) {
ImGui::TableSetupColumn(_("Name"));
ImGui::TableSetupColumn(_("Type"));
ImGui::TableSetupColumn(_("Size"));
+1 -1
View File
@@ -77,7 +77,7 @@ XA audio and audio tracks.)"));
_("Cubic interpolation - better treble")};
changed |= ImGui::Combo(_("Interpolation"), &settings.get<Interpolation>().value, interpolationValues,
IM_ARRAYSIZE(interpolationValues));
changed |= ImGui::Checkbox(_("Mono"), &settings.get<Mono>().value);
changed |= ImGui::Checkbox(C_("Audio channels", "Mono"), &settings.get<Mono>().value);
ImGuiHelpers::ShowHelpMarker(_("Downmixes stereo to mono."));
changed |= ImGui::InputInt(_("Speed multiplier"), &settings.get<Speed>().value);
ImGuiHelpers::ShowHelpMarker(_(R"(Emulation speed, applied at the audio sink (the master clock).
+97
View File
@@ -0,0 +1,97 @@
/***************************************************************************
* Copyright (C) 2026 PCSX-Redux authors *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include "core/system.h"
#include "gtest/gtest.h"
namespace {
class TestSystem final : public PCSX::System {
public:
void softReset() override {}
void hardReset() override {}
void biosPutc(int) override {}
const PCSX::Arguments& getArgs() const override { throw std::logic_error("unused"); }
void printf(std::string&&) override {}
void log(PCSX::LogClass, std::string&&) override {}
void message(std::string&&) override {}
void luaMessage(const std::string&, bool) override {}
void update(bool) override {}
void close() override {}
void purgeAllEvents() override {}
void testQuit(int) override {}
};
const char* const c_poFile = R"(msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
#: src/a.cc:1
msgid "Mono"
msgstr "Mono-fr"
#: src/b.cc:1
msgctxt "Audio channels"
msgid "Mono"
msgstr "Mono-audio"
msgctxt ""
"Multi"
"line"
msgid ""
"Split "
"string"
msgstr "Split-ctx"
#, fuzzy
msgctxt "Playback"
msgid "Pause"
msgstr "Pause-fuzzy"
msgid "Last"
msgstr "Last-fr"
)";
} // namespace
TEST(I18n, Context) {
auto path = std::filesystem::temp_directory_path() / "pcsx-redux-i18n-test.po";
{
std::ofstream out(path, std::ios::binary);
out << c_poFile;
}
TestSystem system;
ASSERT_TRUE(system.loadLocale("Test", path));
system.activateLocale("Test");
auto* oldSystem = PCSX::g_system;
PCSX::g_system = &system;
EXPECT_STREQ(_("Mono"), "Mono-fr");
EXPECT_STREQ(C_("Audio channels", "Mono"), "Mono-audio");
EXPECT_STREQ(C_("Multiline", "Split string"), "Split-ctx");
EXPECT_STREQ(C_("Playback", "Pause"), "Pause");
EXPECT_STREQ(C_("Other", "Mono"), "Mono");
EXPECT_STREQ(lC_("Audio channels", "Mono")(), "Mono-audio");
EXPECT_STREQ(_("Last"), "Last-fr");
PCSX::g_system = oldSystem;
std::filesystem::remove(path);
}