i18n: one Red/Green/Blue row per language, Title/Action Safe keeps After Effects' label, no process spawn to read the locale (#490, #571 follow-up)

- #571's Red/Green/Blue rows duplicated #490's; keep 红色/绿色/蓝色 and give
  Traditional Chinese 紅色/綠色/藍色 instead of the English copies.
- The viewer's grid menu says Title/Action Safe again (as in After
  Effects and the View menu), with its own catalog rows.
- Half is 二分之一, as in After Effects' Simplified Chinese resolution menu.
- Drop the macOS 'defaults read' fallback for a C locale: sys-locale never
  reports one on Apple, so it never ran.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f
This commit is contained in:
Claude committed 2026-10-10 12:54:24 +00:00
1 parent c7b02cc9a3
commit 8f43e69cf1
3 files changed
+10 -52

No files matched your search

+1 -39
View File
@@ -54,51 +54,13 @@ fn system_language() -> &'static str {
#[cfg(not(target_arch = "wasm32"))]
{
static LANGUAGE: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();
LANGUAGE.get_or_init(|| {
let locale = sys_locale::get_locale();
// `C` / `POSIX` (and variants like `C.UTF-8`) mean "no particular locale": don't force
// English, ask the macOS preferred-languages list instead.
if locale.as_deref().is_some_and(is_c_locale) {
#[cfg(target_os = "macos")]
if let Some(l) = macos_preferred_language() {
return l;
}
return "en";
}
supported(locale.as_deref())
})
LANGUAGE.get_or_init(|| supported(sys_locale::get_locale().as_deref()))
}
// No system locale in the browser build: English.
#[cfg(target_arch = "wasm32")]
supported(None)
}
/// True when `locale` names the generic `C` / `POSIX` locale (or a variant like `C.UTF-8`).
fn is_c_locale(locale: &str) -> bool {
let base = locale.split(['.', '@']).next().unwrap_or("").to_ascii_lowercase();
matches!(base.as_str(), "c" | "posix")
}
/// The first supported language in the macOS `defaults read -g AppleLanguages` list.
#[cfg(target_os = "macos")]
fn macos_preferred_language() -> Option<&'static str> {
let out = std::process::Command::new("/usr/bin/defaults")
.args(["read", "-g", "AppleLanguages"])
.output()
.ok()?;
if !out.status.success() {
return None;
}
String::from_utf8_lossy(&out.stdout)
.split(['(', ')', ',', '"', '\n'])
.map(str::trim)
.filter(|s| !s.is_empty())
.find_map(|tag| {
let lang = supported(Some(tag));
(lang != "en").then_some(lang)
})
}
/// The language EffectCraft shows for a BCP 47 locale (`ja-JP` → `ja`, `zh-Hans-CN` → `zh-hans`,
/// `zh-TW` → `zh-hant`, `uk-UA` → `uk`).
fn supported(locale: Option<&str>) -> &'static str {
+8 -12
View File
@@ -464,6 +464,7 @@ const JAPANESE: &[(&str, &str)] = &[
("Time Stretch…", "時間伸縮…"),
("Time:", "時間:"),
("Time: {}", "時間:{}"),
("Title/Action Safe", "タイトル/アクションセーフ"),
("Title/action safe", "タイトル/アクションセーフ"),
("Toggle Switches / Modes", "スイッチ/モードを切り替え"),
("Track Name:", "トラック名:"),
@@ -554,7 +555,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Background Color:", "背景颜色:"),
("Blend Mode:", "混合模式:"),
("Blending Mode", "混合模式"),
("Blue", "蓝"),
("Blur", "模糊"),
("Blur Level", "模糊级别"),
("Boundary", "边界"),
@@ -670,7 +670,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Frame Target:", "帧目标:"),
("Freeze", "冻结"),
("Grab bag", "列表"),
("Green", "绿"),
("Grid", "网格"),
("Guide Layers: {}", "参考图层:{}"),
("Guides", "参考线"),
@@ -808,7 +807,6 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Rate Control: {}", "比特率控制:{}"),
("Recent", "最近使用"),
("Recording", "录制中"),
("Red", "红"),
("Remove", "移除"),
("Rename", "重命名"),
("Render: {} ms • UI {} fps{}", "渲染:{} 毫秒 • 界面 {} fps{}"),
@@ -901,6 +899,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Time Stretch…", "时间伸缩…"),
("Time:", "时间:"),
("Time: {}", "时间:{}"),
("Title/Action Safe", "字幕/动作安全框"),
("Title/action safe", "字幕/动作安全框"),
("Toggle Switches / Modes", "切换开关/模式"),
("Track Name:", "跟踪名称:"),
@@ -951,7 +950,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
("Fit", "适合"),
("Fit up to 100%", "适合(最大 100%)"),
("Full", "完整"),
("Half", "一半"),
("Half", "二分之一"),
("Only this layer uses this solid.", "仅此图层使用该纯色。"),
("Proportional Grid", "比例网格"),
("Quarter", "四分之一"),
@@ -1047,7 +1046,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Background Color:", "背景顏色:"),
("Blend Mode:", "混合模式:"),
("Blending Mode", "混合模式"),
("Blue", "藍"),
("Blur", "模糊"),
("Blur Level", "模糊級別"),
("Boundary", "邊界"),
@@ -1163,7 +1161,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Frame Target:", "幀目標:"),
("Freeze", "凍結"),
("Grab bag", "清單"),
("Green", "綠"),
("Grid", "格點"),
("Guide Layers: {}", "參考圖層:{}"),
("Guides", "參考線"),
@@ -1301,7 +1298,6 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Rate Control: {}", "位元速率控制:{}"),
("Recent", "最近使用"),
("Recording", "錄製中"),
("Red", "紅"),
("Remove", "移除"),
("Rename", "重命名"),
("Render: {} ms • UI {} fps{}", "算圖:{} 毫秒 • 介面 {} fps{}"),
@@ -1394,6 +1390,7 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Time Stretch…", "時間伸縮…"),
("Time:", "時間:"),
("Time: {}", "時間:{}"),
("Title/Action Safe", "字幕/動作安全框"),
("Title/action safe", "字幕/動作安全框"),
("Toggle Switches / Modes", "切換開關/模式"),
("Track Name:", "追蹤名稱:"),
@@ -1455,21 +1452,19 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("Third", "Third"),
("Toggle Mask and Shape Path Visibility", "Toggle Mask and Shape Path Visibility"),
("Toggle Transparency Grid", "Toggle Transparency Grid"),
// The same rows as the Simplified catalog (the tests require both lists to match). The
// Traditional wording is the translator's call and stays the English source until then.
("16 bits per channel", "16 bits per channel"),
("32 bits per channel (float)", "32 bits per channel (float)"),
("8 bits per channel", "8 bits per channel"),
("Adaptive Resolution", "Adaptive Resolution"),
("Adobe-style built-in", "Adobe-style built-in"),
("Blue", "Blue"),
("Blue", "藍色"),
("Current Settings", "Current Settings"),
("Disable Per-character 3D", "Disable Per-character 3D"),
("Draft", "Draft"),
("Enable Per-character 3D", "Enable Per-character 3D"),
("Fast Draft", "Fast Draft"),
("Fast Previews (Draft 3D, Faster Effects)", "Fast Previews (Draft 3D, Faster Effects)"),
("Green", "Green"),
("Green", "綠色"),
("Import Image Sequence", "Import Image Sequence"),
("Import PDF / Illustrator File", "Import PDF / Illustrator File"),
("Import Photoshop File", "Import Photoshop File"),
@@ -1482,7 +1477,7 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
("OpenGL (compatibility)", "OpenGL (compatibility)"),
("Opus Bitrate: {} kbps", "Opus Bitrate: {} kbps"),
("RGB Straight", "RGB Straight"),
("Red", "Red"),
("Red", "紅色"),
("Tone map", "Tone map"),
("Use In-Window Menu Bar on macOS", "Use In-Window Menu Bar on macOS"),
("Wireframe", "Wireframe"),
@@ -1878,6 +1873,7 @@ const UKRAINIAN: &[(&str, &str)] = &[
("Time Stretch…", "Розтягнення часу…"),
("Time:", "Час:"),
("Time: {}", "Час: {}"),
("Title/Action Safe", "Безпечні області титрів і дії"),
("Title/action safe", "Безпечні області титрів і дії"),
("Toggle Switches / Modes", "Перемкнути перемикачі / режими"),
("Track Name:", "Назва відстеження:"),
+1 -1
View File
@@ -636,7 +636,7 @@ pub(crate) fn bottom_bar(app: &mut EffectcraftApp, ui: &mut egui::Ui, bar: Rect,
}
let v = &app.ui.viewer;
let items = vec![
("Title/action safe".to_string(), v.safe_margins),
("Title/Action Safe".to_string(), v.safe_margins),
("Proportional Grid".to_string(), v.proportional_grid),
("Grid".to_string(), v.grid),
("Guides".to_string(), v.guides),