From 08686c3b95055cdfd6d00b2106b74d76f4d3d138 Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Sat, 16 May 2026 14:13:38 +0800 Subject: [PATCH] refactor(i18n): extract op-i18n crate (with Locale) from openpencil-shell-core --- Cargo.lock | 5 ++ crates/op-i18n/Cargo.toml | 9 +++ .../src/i18n/de.rs | 0 .../src/i18n/en.rs | 0 .../src/i18n/es.rs | 0 .../src/i18n/fr.rs | 0 .../src/i18n/hi.rs | 0 .../src/i18n/id.rs | 0 .../src/i18n/ja.rs | 0 .../src/i18n/ko.rs | 0 .../src/i18n/mod.rs | 2 +- .../src/i18n/pt.rs | 0 .../src/i18n/ru.rs | 0 .../src/i18n/th.rs | 0 .../src/i18n/tr.rs | 0 .../src/i18n/vi.rs | 0 .../src/i18n/zh_cn.rs | 0 .../src/i18n/zh_tw.rs | 0 crates/op-i18n/src/lib.rs | 5 ++ crates/op-i18n/src/locale.rs | 70 ++++++++++++++++++ crates/openpencil-shell-core/Cargo.toml | 5 ++ crates/openpencil-shell-core/src/document.rs | 74 +------------------ crates/openpencil-shell-core/src/lib.rs | 4 +- 23 files changed, 102 insertions(+), 72 deletions(-) create mode 100644 crates/op-i18n/Cargo.toml rename crates/{openpencil-shell-core => op-i18n}/src/i18n/de.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/en.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/es.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/fr.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/hi.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/id.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/ja.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/ko.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/mod.rs (98%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/pt.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/ru.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/th.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/tr.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/vi.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/zh_cn.rs (100%) rename crates/{openpencil-shell-core => op-i18n}/src/i18n/zh_tw.rs (100%) create mode 100644 crates/op-i18n/src/lib.rs create mode 100644 crates/op-i18n/src/locale.rs diff --git a/Cargo.lock b/Cargo.lock index 38cab071e..99bbd38e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2274,6 +2274,10 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "op-i18n" +version = "0.1.0" + [[package]] name = "openpencil-desktop" version = "0.1.0" @@ -2307,6 +2311,7 @@ dependencies = [ "bitflags 2.11.1", "glam", "jian-core", + "op-i18n", "serde", "thiserror 1.0.69", "tracing", diff --git a/crates/op-i18n/Cargo.toml b/crates/op-i18n/Cargo.toml new file mode 100644 index 000000000..6ba89770c --- /dev/null +++ b/crates/op-i18n/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "op-i18n" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +description = "OpenPencil i18n — Locale enum + 15-locale string tables" + +[dependencies] diff --git a/crates/openpencil-shell-core/src/i18n/de.rs b/crates/op-i18n/src/i18n/de.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/de.rs rename to crates/op-i18n/src/i18n/de.rs diff --git a/crates/openpencil-shell-core/src/i18n/en.rs b/crates/op-i18n/src/i18n/en.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/en.rs rename to crates/op-i18n/src/i18n/en.rs diff --git a/crates/openpencil-shell-core/src/i18n/es.rs b/crates/op-i18n/src/i18n/es.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/es.rs rename to crates/op-i18n/src/i18n/es.rs diff --git a/crates/openpencil-shell-core/src/i18n/fr.rs b/crates/op-i18n/src/i18n/fr.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/fr.rs rename to crates/op-i18n/src/i18n/fr.rs diff --git a/crates/openpencil-shell-core/src/i18n/hi.rs b/crates/op-i18n/src/i18n/hi.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/hi.rs rename to crates/op-i18n/src/i18n/hi.rs diff --git a/crates/openpencil-shell-core/src/i18n/id.rs b/crates/op-i18n/src/i18n/id.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/id.rs rename to crates/op-i18n/src/i18n/id.rs diff --git a/crates/openpencil-shell-core/src/i18n/ja.rs b/crates/op-i18n/src/i18n/ja.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/ja.rs rename to crates/op-i18n/src/i18n/ja.rs diff --git a/crates/openpencil-shell-core/src/i18n/ko.rs b/crates/op-i18n/src/i18n/ko.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/ko.rs rename to crates/op-i18n/src/i18n/ko.rs diff --git a/crates/openpencil-shell-core/src/i18n/mod.rs b/crates/op-i18n/src/i18n/mod.rs similarity index 98% rename from crates/openpencil-shell-core/src/i18n/mod.rs rename to crates/op-i18n/src/i18n/mod.rs index f34a76aaa..e428a6f8d 100644 --- a/crates/openpencil-shell-core/src/i18n/mod.rs +++ b/crates/op-i18n/src/i18n/mod.rs @@ -11,7 +11,7 @@ //! (`common.untitled`, `rightPanel.design`, `layout.flexLayout`, //! …) so cross-walking strings between TS and Rust is mechanical. -use crate::document::Locale; +use crate::Locale; mod de; mod en; diff --git a/crates/openpencil-shell-core/src/i18n/pt.rs b/crates/op-i18n/src/i18n/pt.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/pt.rs rename to crates/op-i18n/src/i18n/pt.rs diff --git a/crates/openpencil-shell-core/src/i18n/ru.rs b/crates/op-i18n/src/i18n/ru.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/ru.rs rename to crates/op-i18n/src/i18n/ru.rs diff --git a/crates/openpencil-shell-core/src/i18n/th.rs b/crates/op-i18n/src/i18n/th.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/th.rs rename to crates/op-i18n/src/i18n/th.rs diff --git a/crates/openpencil-shell-core/src/i18n/tr.rs b/crates/op-i18n/src/i18n/tr.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/tr.rs rename to crates/op-i18n/src/i18n/tr.rs diff --git a/crates/openpencil-shell-core/src/i18n/vi.rs b/crates/op-i18n/src/i18n/vi.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/vi.rs rename to crates/op-i18n/src/i18n/vi.rs diff --git a/crates/openpencil-shell-core/src/i18n/zh_cn.rs b/crates/op-i18n/src/i18n/zh_cn.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/zh_cn.rs rename to crates/op-i18n/src/i18n/zh_cn.rs diff --git a/crates/openpencil-shell-core/src/i18n/zh_tw.rs b/crates/op-i18n/src/i18n/zh_tw.rs similarity index 100% rename from crates/openpencil-shell-core/src/i18n/zh_tw.rs rename to crates/op-i18n/src/i18n/zh_tw.rs diff --git a/crates/op-i18n/src/lib.rs b/crates/op-i18n/src/lib.rs new file mode 100644 index 000000000..289a3e41c --- /dev/null +++ b/crates/op-i18n/src/lib.rs @@ -0,0 +1,5 @@ +//! OpenPencil i18n — Locale + 15-locale string tables. +mod i18n; +mod locale; +pub use i18n::*; +pub use locale::Locale; diff --git a/crates/op-i18n/src/locale.rs b/crates/op-i18n/src/locale.rs new file mode 100644 index 000000000..afc407044 --- /dev/null +++ b/crates/op-i18n/src/locale.rs @@ -0,0 +1,70 @@ +/// UI locale — full set of 15 mirrored from +/// `apps/web/src/i18n/locales/`. ZhCn + EnUs ship with complete +/// chrome translation tables; the rest fall through to EnUs +/// (visually obvious that translation is pending). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Locale { + EnUs, + ZhCn, + ZhTw, + Ja, + Ko, + Fr, + Es, + De, + Pt, + Ru, + Hi, + Tr, + Th, + Vi, + Id, +} + +impl Locale { + /// All locales in TopBar dropdown order — matches TS app. + pub const ALL: [Locale; 15] = [ + Locale::EnUs, + Locale::ZhCn, + Locale::ZhTw, + Locale::Ja, + Locale::Ko, + Locale::Fr, + Locale::Es, + Locale::De, + Locale::Pt, + Locale::Ru, + Locale::Hi, + Locale::Tr, + Locale::Th, + Locale::Vi, + Locale::Id, + ]; + + /// Cycle to the next locale (round-trips through `ALL`). + pub fn next(self) -> Self { + let i = Self::ALL.iter().position(|&l| l == self).unwrap_or(0); + Self::ALL[(i + 1) % Self::ALL.len()] + } + + /// Native-script display name (matches the TS dropdown). + pub fn display_name(self) -> &'static str { + match self { + Locale::EnUs => "English", + Locale::ZhCn => "简体中文", + Locale::ZhTw => "繁體中文", + Locale::Ja => "日本語", + Locale::Ko => "한국어", + Locale::Fr => "Français", + Locale::Es => "Español", + Locale::De => "Deutsch", + Locale::Pt => "Português", + Locale::Ru => "Русский", + Locale::Hi => "हिन्दी", + Locale::Tr => "Türkçe", + Locale::Th => "ไทย", + Locale::Vi => "Tiếng Việt", + Locale::Id => "Bahasa Indonesia", + } + } +} diff --git a/crates/openpencil-shell-core/Cargo.toml b/crates/openpencil-shell-core/Cargo.toml index 8e2b074d9..711f28682 100644 --- a/crates/openpencil-shell-core/Cargo.toml +++ b/crates/openpencil-shell-core/Cargo.toml @@ -31,6 +31,11 @@ accesskit = "0.24" # cargo silently picking a wrong version under a different workspace context. jian-core = { path = "../../vendor/jian/crates/jian-core", version = "0.0.1" } +# Phase 3 strangler reorg: i18n (Locale + 15-locale string tables) extracted +# into op-i18n; shell-core re-exports it as `i18n` so downstream callers keep +# resolving `crate::i18n::*` / `crate::document::Locale` unchanged. +op-i18n = { path = "../op-i18n" } + [features] default = [] serde = ["dep:serde"] diff --git a/crates/openpencil-shell-core/src/document.rs b/crates/openpencil-shell-core/src/document.rs index e1f8ca15f..caa9911c5 100644 --- a/crates/openpencil-shell-core/src/document.rs +++ b/crates/openpencil-shell-core/src/document.rs @@ -677,76 +677,10 @@ impl ThemeMode { } } -/// UI locale — full set of 15 mirrored from -/// `apps/web/src/i18n/locales/`. ZhCn + EnUs ship with complete -/// chrome translation tables; the rest fall through to EnUs -/// (visually obvious that translation is pending). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Locale { - EnUs, - ZhCn, - ZhTw, - Ja, - Ko, - Fr, - Es, - De, - Pt, - Ru, - Hi, - Tr, - Th, - Vi, - Id, -} - -impl Locale { - /// All locales in TopBar dropdown order — matches TS app. - pub const ALL: [Locale; 15] = [ - Locale::EnUs, - Locale::ZhCn, - Locale::ZhTw, - Locale::Ja, - Locale::Ko, - Locale::Fr, - Locale::Es, - Locale::De, - Locale::Pt, - Locale::Ru, - Locale::Hi, - Locale::Tr, - Locale::Th, - Locale::Vi, - Locale::Id, - ]; - - /// Cycle to the next locale (round-trips through `ALL`). - pub fn next(self) -> Self { - let i = Self::ALL.iter().position(|&l| l == self).unwrap_or(0); - Self::ALL[(i + 1) % Self::ALL.len()] - } - - /// Native-script display name (matches the TS dropdown). - pub fn display_name(self) -> &'static str { - match self { - Locale::EnUs => "English", - Locale::ZhCn => "简体中文", - Locale::ZhTw => "繁體中文", - Locale::Ja => "日本語", - Locale::Ko => "한국어", - Locale::Fr => "Français", - Locale::Es => "Español", - Locale::De => "Deutsch", - Locale::Pt => "Português", - Locale::Ru => "Русский", - Locale::Hi => "हिन्दी", - Locale::Tr => "Türkçe", - Locale::Th => "ไทย", - Locale::Vi => "Tiếng Việt", - Locale::Id => "Bahasa Indonesia", - } - } -} +// Phase 3 strangler reorg: the `Locale` enum + its impls moved into the +// `op-i18n` crate (alongside the 15-locale string tables). Re-exported here +// so existing `document::Locale` / `crate::document::Locale` paths resolve. +pub use op_i18n::Locale; /// Identifier for a property-panel input row. #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/openpencil-shell-core/src/lib.rs b/crates/openpencil-shell-core/src/lib.rs index 1330c320c..e15373ab2 100644 --- a/crates/openpencil-shell-core/src/lib.rs +++ b/crates/openpencil-shell-core/src/lib.rs @@ -22,7 +22,9 @@ pub mod chat_provider; pub mod codegen; pub mod document; pub mod figma; -pub mod i18n; +// Phase 3 strangler reorg: i18n extracted into the op-i18n crate. Re-exported +// as `i18n` so `crate::i18n::translate` / `crate::i18n::Locale` paths still resolve. +pub use op_i18n as i18n; pub mod jian; pub mod mcp; #[cfg(test)] mod mcp_tests;