i18n, a11y, IME · palette copy reads i18n with builtin en/zh; dialog and palette Enter run on the field's key-down action, so an IME-kept Enter never submits; Button, IconButton, TextInput, Tabs, EditorTabs, StatusBarItem and palette rows name their role and label
This commit is contained in:
1 parent
8fd7b8ab87
commit
4580e3b075
15 files changed
+257
-75
No files matched your search
@@ -0,0 +1,87 @@
|
||||
//! Component copy in English and Chinese; `text` reads it when the app's catalogs lack the key.
|
||||
|
||||
/// English, the canonical set; zh-CN holds the same keys, a test keeps parity.
|
||||
const EN: &[(&str, &str)] = &[
|
||||
("palette.command.placeholder", "Type a command"),
|
||||
("palette.command.empty", "No matching commands"),
|
||||
("palette.recent", "Recent"),
|
||||
("palette.open.placeholder", "Go to file"),
|
||||
("palette.open.idle", "Type part of a file name"),
|
||||
("palette.open.empty", "No matching files"),
|
||||
("palette.switch.placeholder", "Switch to"),
|
||||
("palette.switch.empty", "Nothing open by that name"),
|
||||
("palette.search.placeholder", "Search everything"),
|
||||
("palette.search.idle", "Type to search"),
|
||||
("palette.search.empty", "Nothing found for “{query}”"),
|
||||
("palette.symbol.placeholder", "Go to a symbol"),
|
||||
("palette.symbol.empty", "No matching symbols"),
|
||||
("palette.project.placeholder", "Switch to a project"),
|
||||
("palette.project.empty", "No matching projects"),
|
||||
("palette.project.pin", "Pin"),
|
||||
("palette.project.unpin", "Unpin"),
|
||||
("palette.project.remove", "Remove from recent"),
|
||||
(
|
||||
"palette.branch.placeholder",
|
||||
"Switch to a branch, or name a new one",
|
||||
),
|
||||
("palette.branch.local", "Local"),
|
||||
("palette.branch.remote", "Remote"),
|
||||
("palette.branch.create", "Create branch “{query}”"),
|
||||
("palette.branch.empty", "No branch fits"),
|
||||
("palette.branch.switch", "Switch to it"),
|
||||
("palette.branch.delete", "Delete"),
|
||||
("palette.history.placeholder", "Search past commands"),
|
||||
("palette.history.empty", "No past command fits"),
|
||||
];
|
||||
|
||||
const ZH_CN: &[(&str, &str)] = &[
|
||||
("palette.command.placeholder", "输入命令"),
|
||||
("palette.command.empty", "没有匹配的命令"),
|
||||
("palette.recent", "最近"),
|
||||
("palette.open.placeholder", "转到文件"),
|
||||
("palette.open.idle", "输入文件名的一部分"),
|
||||
("palette.open.empty", "没有匹配的文件"),
|
||||
("palette.switch.placeholder", "切换到"),
|
||||
("palette.switch.empty", "没有以此名字打开的"),
|
||||
("palette.search.placeholder", "搜索全部"),
|
||||
("palette.search.idle", "输入以搜索"),
|
||||
("palette.search.empty", "没有找到“{query}”"),
|
||||
("palette.symbol.placeholder", "转到符号"),
|
||||
("palette.symbol.empty", "没有匹配的符号"),
|
||||
("palette.project.placeholder", "切换项目"),
|
||||
("palette.project.empty", "没有匹配的项目"),
|
||||
("palette.project.pin", "置顶"),
|
||||
("palette.project.unpin", "取消置顶"),
|
||||
("palette.project.remove", "从最近中移除"),
|
||||
("palette.branch.placeholder", "切换分支,或输入新分支名"),
|
||||
("palette.branch.local", "本地"),
|
||||
("palette.branch.remote", "远程"),
|
||||
("palette.branch.create", "创建分支“{query}”"),
|
||||
("palette.branch.empty", "没有匹配的分支"),
|
||||
("palette.branch.switch", "切换过去"),
|
||||
("palette.branch.delete", "删除"),
|
||||
("palette.history.placeholder", "搜索历史命令"),
|
||||
("palette.history.empty", "没有匹配的历史命令"),
|
||||
];
|
||||
|
||||
/// Ely's own message for `key` in `locale`, English for a locale Ely does not ship; fails on an unknown key.
|
||||
pub(crate) fn builtin(locale: &str, key: &str) -> &'static str {
|
||||
let table = if locale == "zh-CN" { ZH_CN } else { EN };
|
||||
table
|
||||
.iter()
|
||||
.find_map(|(k, v)| (*k == key).then_some(*v))
|
||||
.or_else(|| EN.iter().find_map(|(k, v)| (*k == key).then_some(*v)))
|
||||
.unwrap_or_else(|| panic!("no builtin message {key:?}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{EN, ZH_CN};
|
||||
|
||||
#[test]
|
||||
fn zh_cn_holds_every_english_key() {
|
||||
let zh: std::collections::HashSet<_> = ZH_CN.iter().map(|(key, _)| key).collect();
|
||||
assert_eq!(EN.len(), ZH_CN.len());
|
||||
assert!(EN.iter().all(|(key, _)| zh.contains(key)));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
mod builtin;
|
||||
mod locale;
|
||||
mod skip;
|
||||
#[cfg(test)]
|
||||
@@ -73,6 +74,14 @@ impl I18n {
|
||||
cx.refresh_windows();
|
||||
}
|
||||
|
||||
/// The locale's own message for `key`, nothing when its catalog lacks it.
|
||||
pub(crate) fn find(&self, key: &str) -> Option<&'static str> {
|
||||
self.catalogs
|
||||
.iter()
|
||||
.find(|(tag, _)| *tag == self.locale.tag)
|
||||
.and_then(|(_, catalog)| catalog.get(key).copied())
|
||||
}
|
||||
|
||||
/// The shown locale's message for `key`, each `{name}` filled from `args`; fails on a missing key or argument. gpui sets no paragraph direction, so a right-to-left message leads with a right-to-left mark: CoreText would lay out one that starts with Latin letters or a signed number left to right.
|
||||
pub fn text(&self, key: &str, args: &[(&str, &str)]) -> SharedString {
|
||||
let (_, catalog) = self
|
||||
@@ -92,6 +101,24 @@ impl I18n {
|
||||
}
|
||||
}
|
||||
|
||||
/// A component's message: the app's catalog first, Ely's builtin copy after. Without an `I18n` global it reads the English builtin.
|
||||
pub fn text(cx: &App, key: &str, args: &[(&str, &str)]) -> SharedString {
|
||||
let (message, direction) = if cx.has_global::<I18n>() {
|
||||
let i18n = cx.global::<I18n>();
|
||||
let message = i18n
|
||||
.find(key)
|
||||
.unwrap_or_else(|| builtin::builtin(i18n.locale().tag, key));
|
||||
(message, i18n.locale().direction)
|
||||
} else {
|
||||
(builtin::builtin("en-US", key), Direction::Ltr)
|
||||
};
|
||||
let text = fill(message, args).unwrap_or_else(|error| panic!("{key:?}: {error}"));
|
||||
match direction {
|
||||
Direction::Ltr => text.into(),
|
||||
Direction::Rtl => format!("\u{200f}{text}").into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// `message` with each `{name}` replaced by its argument.
|
||||
fn fill(message: &str, args: &[(&str, &str)]) -> Result<String, String> {
|
||||
let mut out = String::with_capacity(message.len());
|
||||
|
||||
Reference in new issue
Block a user