Compare commits

...

1 Commits

Author SHA1 Message Date
sindoring 44542be5e2 Fix Russian character deletion confirmation casing
Fold Cyrillic only in the ruRU glue layer so the stock delete confirmation accepts typed mixed case while preserving its button and Enter guards.

Verified on Windows with actual stock GlueXML and the ruRU MPQ: reproduced the rejected title-case confirmation before the fix; engine-free offline checks pass for 9 Russian and 7 English inputs, button/Enter dispatch, and dialog reset. The new application regression body also passes through the engine-free loader adapter. cargo fmt --all -- --check and git diff --check pass. Full workspace/app gates and production build were not run for this change; no player account or server deletion used.
2026-10-18 00:42:17 +04:00
2 changed files with 93 additions and 0 deletions
@@ -5,6 +5,27 @@
BENILLA_REALMLIST and BENILLA_REALMLIST_PINNED (set while $WOW_HOST fixes the address),
and reopens the dialog with the typed text when it is not an address. -->
<Script>
-- CharacterSelect.xml:679 compares the confirmation through strupper. Native 1.12 upper
-- (0x7fc180) leaves Cyrillic unchanged; the community ruRU pack needs its own case fold.
if GetLocale() == "ruRU" then
local function upper(text)
local result = strupper(text)
local lower = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
local capitals = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"
for i = 1, string.len(lower), 2 do
result = string.gsub(result, string.sub(lower, i, i + 1), string.sub(capitals, i, i + 1))
end
return result
end
CharacterDeleteEditBox:SetScript("OnTextChanged", function()
if upper(this:GetText()) == upper(DELETE_CONFIRM_STRING) then
CharacterDeleteButton1:Enable()
else
CharacterDeleteButton1:Disable()
end
end)
end
GlueDialogTypes["BENILLA_REALMLIST"] = {
-- The realmList CVar's registered help text.
text = "Address of realm list server",
@@ -38,6 +38,78 @@ fn ruru_stock_glue_loads_and_keeps_cyrillic_character_names() {
assert!(script.take_errors().is_empty());
}
#[test]
fn ruru_delete_confirmation_accepts_typed_cyrillic_without_case_sensitivity() {
let data = benilla_formats::wow_data_or_skip!();
let locale = benilla_formats::ClientLocale::RuRu;
if locale.pack_folder(&data).is_none() {
benilla_formats::skipped_optional("no ruRU language pack", &[data.join("ruRU")]);
return;
}
let chain = benilla_formats::Chain::open_localized(&data, locale).unwrap();
let _selected = super::reference_ui::fixture::use_chain(chain);
let mut script = UiScript::new_glue().unwrap();
script.set_russian_language_pack(true);
script.set_screen_size(1024.0, 768.0);
assert!(load_glue(&mut script).is_empty());
// Offline only: exercise the stock buttons and Enter without requesting a real deletion.
script
.run(
"GetCharacterInfo = function() return 'Probe', 'Human', 'Warrior', 1 end; \
DELETE_REQUESTS = 0; \
DeleteCharacter = function() DELETE_REQUESTS = DELETE_REQUESTS + 1 end; \
CharacterSelect.selectedIndex = 1; GlueParent:Show()",
)
.unwrap();
for (typed, accepted) in [
("Удалить", true),
("удалить", true),
("УдАлИтЬ", true),
("УДАЛИТЬ", true),
("Удали", false),
("Удалить!", false),
(" Удалить", false),
("DELETE", false),
("", false),
] {
for enter in [false, true] {
script
.run(
"CharacterDeleteDialog:Hide(); CharacterDeleteDialog:Show(); \
CharacterDeleteEditBox:SetFocus()",
)
.unwrap();
script.tick(0.01);
assert_eq!(text(&script, "CharacterDeleteEditBox"), "");
for c in typed.chars() {
script.char_input(&c.to_string());
}
script.tick(0.01);
assert_eq!(text(&script, "CharacterDeleteEditBox"), typed);
assert_eq!(
script
.eval::<bool>("return CharacterDeleteButton1:IsEnabled() == 1")
.unwrap(),
accepted,
"{typed:?}"
);
let before: i64 = script.eval("return DELETE_REQUESTS").unwrap();
if enter {
script.frame_key_input("ENTER");
script.key_up("ENTER");
} else {
script.run("CharacterDeleteButton1:Click()").unwrap();
}
assert_eq!(
script.eval::<i64>("return DELETE_REQUESTS").unwrap() - before,
i64::from(accepted),
"{typed:?}, enter={enter}"
);
}
}
assert!(script.take_errors().is_empty());
}
/// The stock glue VM on `screen` with both agreements accepted, as a returning player's is.
fn glue_on(screen: GlueScreen) -> UiScript {
let mut s = UiScript::new_glue().unwrap();