fix: correct flag emoji for ISO alpha-3 region code (#5557)

* fix: correct flag emoji for ISO alpha-3 region code

* fix: use rust_iso3166 to convert ISO3 region code

* fix: validate ISO country code when generating flag emoji

* fix: correct icon encoding for unlock test in specific regions

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
This commit is contained in:
AetherWing
2025-11-24 20:40:32 +08:00
committed by GitHub
parent 38b306a438
commit 6b3f5eea16
4 changed files with 167 additions and 7 deletions

103
Cargo.lock generated
View File

@@ -1144,6 +1144,7 @@ dependencies = [
"reqwest_dav", "reqwest_dav",
"runas", "runas",
"rust-i18n", "rust-i18n",
"rust_iso3166",
"scopeguard", "scopeguard",
"serde", "serde",
"serde_json", "serde_json",
@@ -1664,6 +1665,27 @@ dependencies = [
"syn 2.0.111", "syn 2.0.111",
] ]
[[package]]
name = "csv"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde_core",
]
[[package]]
name = "csv-core"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "0.2.9" version = "0.2.9"
@@ -1935,6 +1957,16 @@ dependencies = [
"dirs-sys 0.5.0", "dirs-sys 0.5.0",
] ]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]] [[package]]
name = "dirs-sys" name = "dirs-sys"
version = "0.3.7" version = "0.3.7"
@@ -1958,6 +1990,17 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users 0.4.6",
"winapi",
]
[[package]] [[package]]
name = "dispatch" name = "dispatch"
version = "0.2.0" version = "0.2.0"
@@ -2131,6 +2174,12 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.35" version = "0.8.35"
@@ -3719,6 +3768,17 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "is-terminal"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi 0.5.2",
"libc",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "is-wsl" name = "is-wsl"
version = "0.4.0" version = "0.4.0"
@@ -5561,6 +5621,20 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "prettytable-rs"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a"
dependencies = [
"csv",
"encode_unicode",
"is-terminal",
"lazy_static",
"term",
"unicode-width",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "1.3.1" version = "1.3.1"
@@ -6303,6 +6377,18 @@ dependencies = [
"ordered-multimap", "ordered-multimap",
] ]
[[package]]
name = "rust_iso3166"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0d3a0089ee08071ea1baaba83f2265c97f7646c53c3f92b205eb2cdaab72b1"
dependencies = [
"js-sys",
"phf 0.11.3",
"prettytable-rs",
"wasm-bindgen",
]
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "2.1.1" version = "2.1.1"
@@ -7896,6 +7982,17 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "term"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
dependencies = [
"dirs-next",
"rustversion",
"winapi",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@@ -8742,6 +8839,12 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]] [[package]]
name = "universal-hash" name = "universal-hash"
version = "0.5.1" version = "0.5.1"

View File

@@ -18,6 +18,7 @@
- windows 端监听关机信号失败 - windows 端监听关机信号失败
- 修复代理按钮和高亮状态不同步 - 修复代理按钮和高亮状态不同步
- 修复侧边栏可能的未能正确跳转 - 修复侧边栏可能的未能正确跳转
- 修复解锁测试部分地区图标编码不正确
<details> <details>
<summary><strong> ✨ 新增功能 </strong></summary> <summary><strong> ✨ 新增功能 </strong></summary>

View File

@@ -98,6 +98,7 @@ clash_verge_service_ipc = { version = "2.0.21", features = [
], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" } ], git = "https://github.com/clash-verge-rev/clash-verge-service-ipc" }
arc-swap = "1.7.1" arc-swap = "1.7.1"
rust-i18n = "3.1.5" rust-i18n = "3.1.5"
rust_iso3166 = "0.1.14"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
deelevate = { workspace = true } deelevate = { workspace = true }

View File

@@ -1,4 +1,5 @@
use chrono::Local; use chrono::Local;
use rust_iso3166;
pub fn get_local_date_string() -> String { pub fn get_local_date_string() -> String {
let now = Local::now(); let now = Local::now();
@@ -6,16 +7,70 @@ pub fn get_local_date_string() -> String {
} }
pub fn country_code_to_emoji(country_code: &str) -> String { pub fn country_code_to_emoji(country_code: &str) -> String {
let country_code = country_code.to_uppercase(); let uc = country_code.to_ascii_uppercase();
if country_code.len() < 2 {
return String::new();
}
let bytes = country_code.as_bytes(); // 长度校验:仅允许 2 或 3
match uc.len() {
2 => {
// 校验是否是合法 alpha2
if rust_iso3166::from_alpha2(&uc).is_none() {
return String::new();
}
alpha2_to_emoji(&uc)
}
3 => {
// 转换并校验 alpha3
match rust_iso3166::from_alpha3(&uc) {
Some(c) => {
let alpha2 = c.alpha2.to_ascii_uppercase();
alpha2_to_emoji(&alpha2)
}
None => String::new(),
}
}
_ => String::new(),
}
}
fn alpha2_to_emoji(alpha2: &str) -> String {
let bytes = alpha2.as_bytes();
let c1 = 0x1F1E6 + (bytes[0] as u32) - ('A' as u32); let c1 = 0x1F1E6 + (bytes[0] as u32) - ('A' as u32);
let c2 = 0x1F1E6 + (bytes[1] as u32) - ('A' as u32); let c2 = 0x1F1E6 + (bytes[1] as u32) - ('A' as u32);
char::from_u32(c1) char::from_u32(c1)
.and_then(|c1| char::from_u32(c2).map(|c2| format!("{c1}{c2}"))) .and_then(|x| char::from_u32(c2).map(|y| format!("{x}{y}")))
.unwrap_or_default() .unwrap_or_default()
} }
#[cfg(test)]
mod tests {
use super::country_code_to_emoji;
#[test]
fn country_code_to_emoji_iso2() {
assert_eq!(country_code_to_emoji("CN"), "🇨🇳");
assert_eq!(country_code_to_emoji("us"), "🇺🇸");
}
#[test]
fn country_code_to_emoji_iso3() {
assert_eq!(country_code_to_emoji("CHN"), "🇨🇳");
assert_eq!(country_code_to_emoji("USA"), "🇺🇸");
}
#[test]
fn country_code_to_emoji_invalid() {
assert_eq!(country_code_to_emoji("XXX"), "");
assert_eq!(country_code_to_emoji("ZZ"), "");
}
#[test]
fn country_code_to_emoji_short() {
assert_eq!(country_code_to_emoji("C"), "");
assert_eq!(country_code_to_emoji(""), "");
}
#[test]
fn country_code_to_emoji_long() {
assert_eq!(country_code_to_emoji("CNAAA"), "");
}
}