mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 08:02:24 +00:00
fix(i18n): a number in a search keyword list must survive translation
Searching Settings for `16` found ANSI colours in English and Japanese
and nothing at all in Chinese. Both other languages carry "16" in that
row's keyword list; the Chinese one did not, and neither does the Chinese
title ("ANSI 颜色"), so the row was unreachable by the number people
actually type for it.
Keywords are match data rather than prose. A digit is the part of them
that does not translate — "16" is "16" in every language — so dropping
one silently narrows what a speaker of that language can find.
The guard holds every keyword list to carrying each number English does.
A translation may add its own; it may not lose one. Only keyword lists:
digits in ordinary copy are phrasing, and the Japanese for "one per line"
carries a 1 the English has no reason to.
Found by comparing numeric literals across the three tables, which is a
check nothing else does — the existing i18n tests hold placeholders,
plural branches and vocabulary, all of which this passes.
This commit is contained in:
@@ -1471,6 +1471,13 @@ fn current_locale_index() -> usize {
|
||||
CURRENT.load(Ordering::Relaxed) as usize
|
||||
}
|
||||
|
||||
/// [`translate`] for one named locale, so a test can compare the languages
|
||||
/// against each other without steering the process-wide one.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn translate_for_test(locale_idx: usize, key: L10nKey) -> &'static str {
|
||||
translate(locale_idx, key)
|
||||
}
|
||||
|
||||
fn translate(locale_idx: usize, key: L10nKey) -> &'static str {
|
||||
if let Some(lang) = SUPPORTED_LANGUAGES.get(locale_idx)
|
||||
&& let Some(text) = (lang.translate_fn)(key)
|
||||
|
||||
+1
-1
@@ -678,7 +678,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> {
|
||||
"代理 proxy http https socks socks5 clash v2ray 网络 下载 更新"
|
||||
}
|
||||
L10nKey::SettingsSearchAnsiColorsKeywords => {
|
||||
"ANSI颜色 调色板 终端颜色 主题 ansi colors palette terminal theme"
|
||||
"ANSI颜色 调色板 终端颜色 16色 主题 ansi colors palette terminal theme"
|
||||
}
|
||||
L10nKey::SettingsSearchBackgroundImageKeywords => {
|
||||
"背景图片 背景图 壁纸 图片 照片 主题 background image wallpaper picture theme"
|
||||
|
||||
@@ -8507,3 +8507,69 @@ mod forward_bind_gpui_tests {
|
||||
assert_eq!(panel.bind_host, rule.bind.host, "the two forms agree");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod search_keyword_tests {
|
||||
use super::*;
|
||||
|
||||
/// A number in a search keyword list must survive translation.
|
||||
///
|
||||
/// Keywords are match data, not prose: they exist so someone typing what
|
||||
/// they would call the thing lands on its row. A digit is the part of that
|
||||
/// which does not translate — "16" is "16" in every language — so a
|
||||
/// translation that drops one silently narrows what its speakers can find.
|
||||
///
|
||||
/// `SettingsSearchAnsiColorsKeywords` was the case. English and Japanese
|
||||
/// both carried "16"; Chinese did not, so searching `16` under `zh-CN`
|
||||
/// matched nothing at all while the other two found ANSI colours.
|
||||
///
|
||||
/// Only the keyword strings. Digits in ordinary copy are phrasing — the
|
||||
/// Japanese for "one per line" contains a 1 the English has no reason to —
|
||||
/// and holding those to the English would be nonsense.
|
||||
#[test]
|
||||
fn a_number_in_a_keyword_list_survives_translation() {
|
||||
use crate::ui::i18n::{SUPPORTED_LANGUAGES, translate_for_test};
|
||||
|
||||
fn digits(text: &str) -> std::collections::BTreeSet<String> {
|
||||
let mut out = std::collections::BTreeSet::new();
|
||||
let mut cur = String::new();
|
||||
for c in text.chars() {
|
||||
if c.is_ascii_digit() {
|
||||
cur.push(c);
|
||||
} else if !cur.is_empty() {
|
||||
out.insert(std::mem::take(&mut cur));
|
||||
}
|
||||
}
|
||||
if !cur.is_empty() {
|
||||
out.insert(cur);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
let mut checked = 0usize;
|
||||
for entry in settings_search_entries() {
|
||||
let want = digits(translate_for_test(0, entry.keywords));
|
||||
if want.is_empty() {
|
||||
continue;
|
||||
}
|
||||
checked += 1;
|
||||
for (idx, lang) in SUPPORTED_LANGUAGES.iter().enumerate().skip(1) {
|
||||
let got = digits(translate_for_test(idx, entry.keywords));
|
||||
let dropped: Vec<&String> = want.difference(&got).collect();
|
||||
// A translation may add numbers of its own; it may not lose one.
|
||||
assert!(
|
||||
dropped.is_empty(),
|
||||
"{:?}: the {} keywords drop {:?}, which English carries, so a search \
|
||||
for it finds nothing in that language",
|
||||
entry.title,
|
||||
lang.code,
|
||||
dropped
|
||||
);
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
checked > 0,
|
||||
"no keyword list carries a number, so this guard is checking nothing"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user