i18n: complete Simplified Chinese catalog, Chinese font fallback and a coverage xtask (#930)
Adds a complete Simplified Chinese (zh) catalog covering every English message, a font fallback for Chinese text, and `cargo xtask i18n --check`, which checks that the complete catalogs (ja, zh) carry every English message and that every command label is translatable. Co-authored-by: Noly1990 <[email protected]>
This commit is contained in:
1 parent
587cf865b8
commit
45f92277cb
17 files changed
+2485
-52
No files matched your search
+99
-13
@@ -10,12 +10,17 @@ use std::sync::Arc;
|
||||
/// Name of the CJK fallback entry in [`egui::FontDefinitions`].
|
||||
pub const CJK_FALLBACK: &str = "cjk_fallback";
|
||||
|
||||
/// Name of the Hangul fallback entry in [`egui::FontDefinitions`], after [`CJK_FALLBACK`].
|
||||
/// Name of the Chinese fallback entry in [`egui::FontDefinitions`], after [`CJK_FALLBACK`].
|
||||
/// A Japanese-only CJK font (Meiryo, Hiragino Gothic) lacks simplified characters, so
|
||||
/// Chinese falls through to this one per glyph.
|
||||
pub const CHINESE_FALLBACK: &str = "chinese_fallback";
|
||||
|
||||
/// Name of the Hangul fallback entry in [`egui::FontDefinitions`], last.
|
||||
pub const HANGUL_FALLBACK: &str = "hangul_fallback";
|
||||
|
||||
/// UI languages whose text needs the CJK font. The web app's bundled font covers Japanese only;
|
||||
/// add Chinese or Korean here together with a font that covers them.
|
||||
pub const CJK_LANGUAGES: &[&str] = &["ja"];
|
||||
pub const CJK_LANGUAGES: &[&str] = &["ja", "zh", "ko"];
|
||||
|
||||
/// Whether the UI language needs a CJK font (the web app fetches one only then).
|
||||
pub fn needs_cjk_font(language: &str) -> bool {
|
||||
@@ -113,6 +118,34 @@ pub fn is_cjk_font_name(file_name: &str) -> bool {
|
||||
CJK_FONT_KEYWORDS.iter().any(|k| name.contains(k))
|
||||
}
|
||||
|
||||
/// Lower-case file-name fragments that identify fonts covering simplified Chinese. Deliberately
|
||||
/// separate from [`CJK_FONT_KEYWORDS`]: many Japanese fonts (Hiragino Gothic, Meiryo, Yu Gothic)
|
||||
/// match those but lack simplified characters, so a Chinese fallback searches these instead.
|
||||
pub const CHINESE_FONT_KEYWORDS: &[&str] = &[
|
||||
"pingfang",
|
||||
"hiragino sans gb",
|
||||
"stheiti",
|
||||
"songti",
|
||||
"arial unicode",
|
||||
"msyh",
|
||||
"yahei",
|
||||
"simhei",
|
||||
"simsun",
|
||||
"wqy",
|
||||
"wenquanyi",
|
||||
"notosanscjk",
|
||||
"notosanssc",
|
||||
"droidsansfallback",
|
||||
"sourcehanss",
|
||||
"siyuan",
|
||||
];
|
||||
|
||||
/// Whether a font file name looks like a font covering simplified Chinese.
|
||||
pub fn is_chinese_font_name(file_name: &str) -> bool {
|
||||
let name = file_name.to_ascii_lowercase();
|
||||
CHINESE_FONT_KEYWORDS.iter().any(|k| name.contains(k))
|
||||
}
|
||||
|
||||
/// Session state of the web app's on-demand CJK font. Pure state so it can be tested off the web:
|
||||
/// the shell does the fetch when [`CjkFontLoader::tick`] asks for it.
|
||||
#[derive(Debug, Default)]
|
||||
@@ -256,6 +289,30 @@ const HANGUL_FONT_CANDIDATES: &[&str] = &[
|
||||
"/usr/share/fonts/nanum/NanumGothic.ttf",
|
||||
];
|
||||
|
||||
/// Fonts covering simplified Chinese, appended after the CJK fallback so Chinese renders even
|
||||
/// when the CJK font found is a Japanese one without simplified characters (Hiragino Gothic,
|
||||
/// Meiryo, Yu Gothic).
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
const CHINESE_FONT_CANDIDATES: &[&str] = &[
|
||||
// Windows: Microsoft YaHei
|
||||
"C:\\Windows\\Fonts\\msyh.ttc",
|
||||
"C:\\Windows\\Fonts\\msyh.ttf",
|
||||
// macOS: PingFang SC, Hiragino Sans GB, Songti SC, STHeiti, Arial Unicode MS
|
||||
"/System/Library/Fonts/PingFang.ttc",
|
||||
"/System/Library/Fonts/Hiragino Sans GB.ttc",
|
||||
"/System/Library/Fonts/STHeiti Light.ttc",
|
||||
"/System/Library/Fonts/STHeiti Medium.ttc",
|
||||
"/System/Library/Fonts/Supplemental/Songti.ttc",
|
||||
"/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
|
||||
"/Library/Fonts/Arial Unicode.ttf",
|
||||
// Linux: Noto Sans CJK SC
|
||||
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
|
||||
];
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn try_read_font(path: &std::path::Path) -> Option<Vec<u8>> {
|
||||
if let Ok(bytes) = std::fs::read(path)
|
||||
@@ -267,9 +324,9 @@ fn try_read_font(path: &std::path::Path) -> Option<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively search for matching CJK font files up to a bounded depth.
|
||||
/// Recursively search for matching font files up to a bounded depth, testing names with `matches`.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn scan_dir_for_font(dir: &std::path::Path, depth_remaining: usize) -> Option<Vec<u8>> {
|
||||
fn scan_dir_for_font(dir: &std::path::Path, depth_remaining: usize, matches: fn(&str) -> bool) -> Option<Vec<u8>> {
|
||||
if depth_remaining == 0 {
|
||||
return None;
|
||||
}
|
||||
@@ -282,7 +339,7 @@ fn scan_dir_for_font(dir: &std::path::Path, depth_remaining: usize) -> Option<Ve
|
||||
let name = p.file_name().map(|n| n.to_string_lossy().to_ascii_lowercase()).unwrap_or_default();
|
||||
let is_font = name.ends_with(".ttc") || name.ends_with(".ttf") || name.ends_with(".otf");
|
||||
if is_font
|
||||
&& is_cjk_font_name(&name)
|
||||
&& matches(&name)
|
||||
&& let Some(bytes) = try_read_font(&p)
|
||||
{
|
||||
return Some(bytes);
|
||||
@@ -293,7 +350,7 @@ fn scan_dir_for_font(dir: &std::path::Path, depth_remaining: usize) -> Option<Ve
|
||||
}
|
||||
|
||||
for sub in subdirs {
|
||||
if let Some(bytes) = scan_dir_for_font(&sub, depth_remaining.saturating_sub(1)) {
|
||||
if let Some(bytes) = scan_dir_for_font(&sub, depth_remaining.saturating_sub(1), matches) {
|
||||
return Some(bytes);
|
||||
}
|
||||
}
|
||||
@@ -336,7 +393,7 @@ fn find_system_cjk_font() -> Option<Vec<u8>> {
|
||||
}
|
||||
if let Ok(local) = std::env::var("LOCALAPPDATA") {
|
||||
let user_fonts = std::path::PathBuf::from(local).join("Microsoft").join("Windows").join("Fonts");
|
||||
if let Some(bytes) = scan_dir_for_font(&user_fonts, 2) {
|
||||
if let Some(bytes) = scan_dir_for_font(&user_fonts, 2, is_cjk_font_name) {
|
||||
return Some(bytes);
|
||||
}
|
||||
}
|
||||
@@ -346,7 +403,7 @@ fn find_system_cjk_font() -> Option<Vec<u8>> {
|
||||
{
|
||||
let dirs = ["/System/Library/Fonts", "/System/Library/Fonts/Supplemental", "/Library/Fonts"];
|
||||
for d in dirs {
|
||||
if let Some(bytes) = scan_dir_for_font(std::path::Path::new(d), 2) {
|
||||
if let Some(bytes) = scan_dir_for_font(std::path::Path::new(d), 2, is_cjk_font_name) {
|
||||
return Some(bytes);
|
||||
}
|
||||
}
|
||||
@@ -356,13 +413,13 @@ fn find_system_cjk_font() -> Option<Vec<u8>> {
|
||||
{
|
||||
let search_dirs = [std::path::PathBuf::from("/usr/share/fonts"), std::path::PathBuf::from("/usr/local/share/fonts")];
|
||||
for dir in &search_dirs {
|
||||
if let Some(bytes) = scan_dir_for_font(dir, 3) {
|
||||
if let Some(bytes) = scan_dir_for_font(dir, 3, is_cjk_font_name) {
|
||||
return Some(bytes);
|
||||
}
|
||||
}
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
let user_fonts = std::path::PathBuf::from(home).join(".local/share/fonts");
|
||||
if let Some(bytes) = scan_dir_for_font(&user_fonts, 2) {
|
||||
if let Some(bytes) = scan_dir_for_font(&user_fonts, 2, is_cjk_font_name) {
|
||||
return Some(bytes);
|
||||
}
|
||||
}
|
||||
@@ -371,13 +428,21 @@ fn find_system_cjk_font() -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Configure font definitions with system CJK and Hangul fallbacks where available.
|
||||
/// Configure font definitions with system CJK, Chinese and Hangul fallbacks where available.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn setup_fonts(ctx: &egui::Context) {
|
||||
let mut fonts = egui::FontDefinitions::default();
|
||||
let mut found = false;
|
||||
if let Some(cjk) = find_system_cjk_font().and_then(|bytes| with_cjk_fallback(fonts.clone(), bytes)) {
|
||||
fonts = cjk;
|
||||
let cjk = find_system_cjk_font();
|
||||
if let Some(bytes) = cjk.clone().and_then(|bytes| with_cjk_fallback(fonts.clone(), bytes)) {
|
||||
fonts = bytes;
|
||||
found = true;
|
||||
}
|
||||
// Chinese after the CJK fallback: a Japanese-only CJK font lacks simplified characters,
|
||||
// so Chinese falls through to this one per glyph.
|
||||
let chinese = find_system_chinese_font().filter(|bytes| cjk.as_ref() != Some(bytes));
|
||||
if let Some(with_chinese) = chinese.and_then(|bytes| with_fallback(fonts.clone(), CHINESE_FALLBACK, bytes)) {
|
||||
fonts = with_chinese;
|
||||
found = true;
|
||||
}
|
||||
let hangul = HANGUL_FONT_CANDIDATES.iter().find_map(|path| try_read_font(std::path::Path::new(path)));
|
||||
@@ -390,6 +455,27 @@ pub fn setup_fonts(ctx: &egui::Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find a font covering simplified Chinese in the known candidate paths or by scanning the
|
||||
/// system font directories for [`CHINESE_FONT_KEYWORDS`] names.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn find_system_chinese_font() -> Option<Vec<u8>> {
|
||||
for path in CHINESE_FONT_CANDIDATES {
|
||||
if let Some(bytes) = try_read_font(std::path::Path::new(path)) {
|
||||
return Some(bytes);
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let dirs = ["/System/Library/Fonts", "/System/Library/Fonts/Supplemental", "/Library/Fonts"];
|
||||
for d in dirs {
|
||||
if let Some(bytes) = scan_dir_for_font(std::path::Path::new(d), 2, is_chinese_font_name) {
|
||||
return Some(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// On web, setup_fonts does not perform synchronous disk reads.
|
||||
/// Use asynchronous loading through [`solidcraft_web`] or host scripts.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
||||
@@ -35,7 +35,8 @@ static ENGLISH: Catalog = Catalog::new("en", "English", include_str!("locales/en
|
||||
static JAPANESE: Catalog = Catalog::new("ja", "日本語", include_str!("locales/ja.json"));
|
||||
static GERMAN: Catalog = Catalog::new("de", "Deutsch", include_str!("locales/de.json"));
|
||||
static KOREAN: Catalog = Catalog::new("ko", "한국어", include_str!("locales/ko.json"));
|
||||
pub const LOCALES: &[&Catalog] = &[&ENGLISH, &JAPANESE, &GERMAN, &KOREAN];
|
||||
static CHINESE: Catalog = Catalog::new("zh", "中文", include_str!("locales/zh.json"));
|
||||
pub const LOCALES: &[&Catalog] = &[&ENGLISH, &JAPANESE, &GERMAN, &KOREAN, &CHINESE];
|
||||
|
||||
pub fn is_supported(id: &str) -> bool {
|
||||
LOCALES.iter().any(|catalog| catalog.id == id)
|
||||
@@ -180,6 +181,90 @@ mod tests {
|
||||
result
|
||||
}
|
||||
|
||||
/// Every English text `label()` can be asked to translate, read straight from the catalogs.
|
||||
fn english_texts() -> BTreeSet<&'static str> {
|
||||
ENGLISH.messages().values().map(String::as_str).collect()
|
||||
}
|
||||
|
||||
/// Catalogs delivered complete. English is the source catalog; Japanese was completed
|
||||
/// upstream and Chinese lands complete here. German and Korean are still partial (they
|
||||
/// predate the coverage guards), so their gaps are reported by `cargo xtask i18n`
|
||||
/// without failing the build, while nothing in the two complete catalogs may regress.
|
||||
const COMPLETE: &[&str] = &["ja", "zh"];
|
||||
|
||||
/// `text()` falls back to English silently, so a message a complete locale never received is
|
||||
/// shown in English inside an otherwise translated UI, with nothing in the build to say so.
|
||||
/// This is the guard against that: adding a key to `en.json` and forgetting a complete
|
||||
/// catalog fails here.
|
||||
#[test]
|
||||
fn every_complete_catalog_carries_every_english_message() {
|
||||
let english: Messages = serde_json::from_str(ENGLISH.source).unwrap();
|
||||
for catalog in LOCALES.iter().filter(|c| COMPLETE.contains(&c.id)) {
|
||||
let messages: Messages = serde_json::from_str(catalog.source).unwrap();
|
||||
let missing: Vec<&str> = english.keys().filter(|id| !messages.contains_key(*id)).map(String::as_str).collect();
|
||||
assert!(missing.is_empty(), "{} is missing {} English message(s): {}", catalog.id, missing.len(), missing.join(", "));
|
||||
}
|
||||
}
|
||||
|
||||
/// A toolbar tab or panel reaches the UI as a bare source string (`toolbar::TABS`,
|
||||
/// `CommandSpec::tab` / `.panel`) and is resolved only by `label()`'s English-text lookup, so
|
||||
/// a heading missing from `en.json` can never translate in any locale. There are a dozen of
|
||||
/// them, so all of them are required rather than ratcheted.
|
||||
#[test]
|
||||
fn every_toolbar_tab_and_panel_is_in_the_english_catalog() {
|
||||
let texts = english_texts();
|
||||
let mut wanted: Vec<&str> = crate::toolbar::TABS.to_vec();
|
||||
wanted.push(crate::toolbar_custom::WORKSPACE);
|
||||
for spec in solidcraft_engine::command_specs() {
|
||||
wanted.push(spec.tab);
|
||||
wanted.push(spec.panel);
|
||||
}
|
||||
let mut missing: Vec<&str> = wanted.into_iter().filter(|t| !t.is_empty() && !texts.contains(t)).collect();
|
||||
missing.sort_unstable();
|
||||
missing.dedup();
|
||||
assert!(missing.is_empty(), "toolbar tabs/panels absent from en.json: {missing:?}");
|
||||
}
|
||||
|
||||
/// Every command label the engine can show must exist as an English catalog message.
|
||||
///
|
||||
/// `label()` resolves source text coming from the engine by looking that exact English string
|
||||
/// up in `en.json`, so a label the catalog does not carry stays English in *every* locale —
|
||||
/// nothing fails, the row just looks untranslated. The whole registry now has entries (their
|
||||
/// ids are `cmd.<command id>`), so this is a hard requirement rather than a budget: a new
|
||||
/// command added without a catalog entry fails here.
|
||||
#[test]
|
||||
fn every_command_label_is_in_the_english_catalog() {
|
||||
let texts = english_texts();
|
||||
let mut untranslated: Vec<&str> = solidcraft_engine::command_specs().iter().map(|c| c.label).filter(|label| !texts.contains(label)).collect();
|
||||
untranslated.sort_unstable();
|
||||
untranslated.dedup();
|
||||
assert!(
|
||||
untranslated.is_empty(),
|
||||
"{} command label(s) have no English catalog entry, so they would stay English in every locale: {untranslated:?}",
|
||||
untranslated.len()
|
||||
);
|
||||
}
|
||||
|
||||
/// The whole engine registry now reaches the catalogs. Sample command labels from areas that
|
||||
/// had no entry at all before (sheet metal, inspect, joints, motion) must resolve to their
|
||||
/// translation rather than echo the English source text — `label()` returns its argument when
|
||||
/// it finds no English message, so a dropped entry looks like a working English UI.
|
||||
#[test]
|
||||
fn newly_catalogued_command_labels_translate() {
|
||||
let cases = [
|
||||
("zh", "法兰", "斑马纹分析", "运动研究"),
|
||||
("ja", "フランジ", "ゼブラ解析", "モーション スタディ"),
|
||||
("de", "Flansch", "Zebra-Analyse", "Bewegungsuntersuchung"),
|
||||
("ko", "플랜지", "얼룩말줄무늬 분석", "운동 연구"),
|
||||
];
|
||||
for (lang, flange, zebra, motion) in cases {
|
||||
let locale = Locale::for_language(lang);
|
||||
for (source, expected) in [("Flange", flange), ("Zebra Analysis", zebra), ("Motion Study", motion)] {
|
||||
assert_eq!(locale.label(source), expected, "{lang}: {source}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn catalogs_have_valid_ids_and_matching_placeholders() {
|
||||
let english: Messages = serde_json::from_str(ENGLISH.source).unwrap();
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
//! The context menu and the browser build `Item`s out of English literals, and
|
||||
//! `Locale::label()` can only translate a literal that exists as an English
|
||||
//! message. A label the catalog has never seen comes straight back as English
|
||||
//! and stays English in every locale, with nothing in the build to say so.
|
||||
//! This reads every menu the app can open and fails on the ones it cannot
|
||||
//! translate.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::browser;
|
||||
use crate::context_menu::{self, Item, Target};
|
||||
use crate::{Services, SolveApp};
|
||||
use serde_json::json;
|
||||
use solidcraft_engine::Session;
|
||||
|
||||
fn sample_app() -> SolveApp {
|
||||
let mut s = Session::default();
|
||||
s.run_script(&solidcraft_engine::sample::script()).expect("sample script");
|
||||
SolveApp::new(s, Services::default())
|
||||
}
|
||||
|
||||
/// Every English text `label()` can be asked to translate.
|
||||
fn english_texts() -> BTreeSet<String> {
|
||||
const EN: &str = include_str!("locales/en.json");
|
||||
let messages: serde_json::Map<String, serde_json::Value> = serde_json::from_str(EN).expect("en.json is valid JSON");
|
||||
messages.values().filter_map(|v| v.as_str().map(str::to_string)).collect()
|
||||
}
|
||||
|
||||
/// Separators carry no text; section headings are real text and are checked.
|
||||
fn translatable(items: &[Item]) -> impl Iterator<Item = &str> {
|
||||
items.iter().map(|i| i.label.as_str()).filter(|l| !l.is_empty())
|
||||
}
|
||||
|
||||
fn take(app: &SolveApp, out: &mut BTreeSet<String>) {
|
||||
let state = app.session.model.state();
|
||||
let bodies: Vec<String> = state.bodies.iter().map(|b| b.name.clone()).collect();
|
||||
let features: Vec<u64> = app.session.doc.features.iter().map(|f| f.id).collect();
|
||||
let components: Vec<u64> = app.session.doc.components.iter().map(|c| c.id).collect();
|
||||
let groups: Vec<u64> = app.session.doc.browser_groups.iter().map(|g| g.id).collect();
|
||||
|
||||
for target in [Target::Viewport, Target::Origin, Target::Units, Target::Toolbar, Target::Workspace, Target::NamedViews] {
|
||||
take_items(out, &context_menu::items(app, &target));
|
||||
}
|
||||
for name in &bodies {
|
||||
take_items(out, &context_menu::items(app, &Target::Body { name: name.clone() }));
|
||||
}
|
||||
for name in ["Named View1", "Front detail"] {
|
||||
take_items(out, &context_menu::items(app, &Target::NamedView { name: name.to_string() }));
|
||||
}
|
||||
for id in &features {
|
||||
take_items(out, &context_menu::items(app, &Target::Sketch { id: *id }));
|
||||
}
|
||||
for id in &components {
|
||||
take_items(out, &context_menu::items(app, &Target::Component { id: *id }));
|
||||
for folder in ["bodies", "sketches", "construction"] {
|
||||
take_items(out, &context_menu::items(app, &Target::Folder { component: *id, folder: folder.to_string() }));
|
||||
take_items(out, &browser::folder_items(app, *id, folder));
|
||||
}
|
||||
}
|
||||
for id in &groups {
|
||||
take_items(out, &context_menu::items(app, &Target::Group { id: *id }));
|
||||
take_items(out, &browser::group_items(app, *id));
|
||||
}
|
||||
for spec in solidcraft_engine::command_specs().iter() {
|
||||
let target = Target::ToolbarCommand { id: spec.id.to_string() };
|
||||
take_items(out, &context_menu::items(app, &target));
|
||||
}
|
||||
}
|
||||
|
||||
fn take_items(out: &mut BTreeSet<String>, items: &[Item]) {
|
||||
for label in translatable(items) {
|
||||
out.insert(label.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/// A label the catalog has never seen can never translate, and the item's own
|
||||
/// text is the only witness. Every menu label the app can draw must therefore
|
||||
/// exist as an English message. The sample design's names are data, not text,
|
||||
/// and are left alone.
|
||||
#[test]
|
||||
fn every_menu_label_is_in_the_english_catalog() {
|
||||
let mut app = sample_app();
|
||||
let mut seen = BTreeSet::new();
|
||||
take(&app, &mut seen);
|
||||
|
||||
// The labels that depend on state, so their other branch is seen too.
|
||||
let names: Vec<String> = app.session.model.state().bodies.iter().map(|b| b.name.clone()).collect();
|
||||
app.ui.show_origin = false;
|
||||
take(&app, &mut seen);
|
||||
for name in &names {
|
||||
app.ui.locked_bodies.push(name.clone());
|
||||
app.session.visibility.hidden_bodies.push(name.clone());
|
||||
}
|
||||
take(&app, &mut seen);
|
||||
app.ui.locked_bodies.clear();
|
||||
app.session.visibility.hidden_bodies.clear();
|
||||
take(&app, &mut seen);
|
||||
|
||||
// A menu that stops yielding labels would empty `seen` and turn the
|
||||
// whole test into a tautology, so the sweep has to stay broad.
|
||||
assert!(seen.len() > 40, "the sweep only reached {} label(s)", seen.len());
|
||||
|
||||
let texts = english_texts();
|
||||
let mut missing: Vec<&str> = seen
|
||||
.iter()
|
||||
// Unit symbols are the same word in every language: `mm` is `mm` in
|
||||
// Chinese and in German, so they are not prose to translate.
|
||||
.filter(|l| !texts.contains(l.as_str()) && !crate::prefs::UNITS.contains(&l.as_str()))
|
||||
.map(String::as_str)
|
||||
.collect();
|
||||
missing.sort_unstable();
|
||||
assert!(missing.is_empty(), "{} menu label(s) have no English catalog entry, so they stay English in every locale: {:?}", missing.len(), missing);
|
||||
}
|
||||
|
||||
/// The control channel and scripts call `run()` outside a render language scope, so the
|
||||
/// "command done" echo must follow the saved preference rather than the thread-local scope
|
||||
/// (otherwise its template falls back to English: "撤销 done" instead of "撤销 完成").
|
||||
#[test]
|
||||
fn command_done_echo_follows_the_language_preference_without_a_render_scope() {
|
||||
let mut app = SolveApp::new(Session::default(), Services::default());
|
||||
app.preferences.language = "zh".into();
|
||||
app.run("solid.box", json!({ "length": 5, "width": 5, "height": 5 })).expect("box");
|
||||
let echo = app.session.log.last().expect("the echo is logged");
|
||||
let zh = crate::i18n::Locale::for_language("zh");
|
||||
let expected = zh.format("ui.command_done", &[("command", zh.label(crate::command_label("solid.box")))]);
|
||||
assert_eq!(echo, &expected);
|
||||
assert!(echo.contains("完成"), "{echo}");
|
||||
assert!(!echo.ends_with("done"), "{echo}");
|
||||
}
|
||||
@@ -54,6 +54,45 @@ fn language_preference_cancel_restores_previous_locale() {
|
||||
assert_eq!(h.app.tr("ui.preferences"), "Preferences");
|
||||
}
|
||||
|
||||
/// The chrome a user sees before touching anything: the browser heading and the SELECT panel.
|
||||
/// Both were English literals in the drawing code, and a broken message id falls back to
|
||||
/// English silently, so check the word actually rendered in each language.
|
||||
#[test]
|
||||
fn window_chrome_renders_in_the_active_language() {
|
||||
for (lang, browser, filter, faces) in [
|
||||
("zh", "浏览器", "选择筛选", "面"),
|
||||
("ja", "ブラウザ", "選択フィルター", "面"),
|
||||
("de", "BROWSER", "Auswahlfilter", "Flächen"),
|
||||
("ko", "브라우저", "선택 필터", "면"),
|
||||
] {
|
||||
let mut h = Harness::new();
|
||||
h.app.preferences.language = lang.into();
|
||||
h.app.home.open = false;
|
||||
h.frames(2);
|
||||
// The browser heading is on screen from the start; the selection filter lives in the
|
||||
// SELECT panel, which is not the one shown by default.
|
||||
assert!(h.widget(browser).is_some(), "{lang}: the browser heading must show {browser:?}");
|
||||
let select = scenario::handle_at("panel:SOLID:SELECT").expect("the SELECT panel is on the SOLID tab");
|
||||
h.step(&json!({"click": [select.x, select.y]})).unwrap();
|
||||
h.frames(2);
|
||||
assert!(h.widget(filter).is_some(), "{lang}: the selection filter must show {filter:?}");
|
||||
assert!(h.widget(faces).is_some(), "{lang}: the Faces checkbox must show {faces:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// The command palette's hint embeds a JSON example, so its braces must not be mistaken for a
|
||||
/// placeholder name by the catalog's parser, and the example must survive interpolation whole.
|
||||
#[test]
|
||||
fn command_palette_hint_keeps_its_json_example_intact() {
|
||||
let mut h = Harness::new();
|
||||
h.app.preferences.language = "zh".into();
|
||||
h.app.home.open = false;
|
||||
h.frames(2);
|
||||
let hint = crate::i18n::format("ui.command_palette_hint", &[("example", "Extrude {\"distance\": 20}")]);
|
||||
assert!(hint.contains("Extrude {\"distance\": 20}"), "the example is inserted literally: {hint:?}");
|
||||
assert!(!hint.contains("{example}"), "the placeholder is substituted: {hint:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn translated_dialog_buttons_do_not_wrap() {
|
||||
fn cancel_rows(shape: &egui::Shape) -> Option<usize> {
|
||||
|
||||
@@ -40,6 +40,8 @@ mod hole_placement_ui_tests;
|
||||
pub mod home;
|
||||
pub mod i18n;
|
||||
#[cfg(test)]
|
||||
mod i18n_menu_tests;
|
||||
#[cfg(test)]
|
||||
mod i18n_ui_tests;
|
||||
pub mod icons;
|
||||
pub mod inference;
|
||||
@@ -494,7 +496,13 @@ impl SolveApp {
|
||||
}
|
||||
match &r {
|
||||
Ok(_) => {
|
||||
self.session.echo(format!("{} done", command_label(id)));
|
||||
// Programmatic callers (the control channel, scripts) run outside a render
|
||||
// language scope, so resolve the echo from the saved preference instead of the
|
||||
// thread-local scope. The command's own label is translated too, so the echo
|
||||
// reads the same way the toolbar button it came from does.
|
||||
let language = i18n::Locale::for_language(&self.preferences.language);
|
||||
let label = language.label(command_label(id));
|
||||
self.session.echo(language.format("ui.command_done", &[("command", label)]));
|
||||
if matches!(id, "file.new" | "doc.open" | "doc.recover") {
|
||||
self.sketch_choices = sketch_families::Choices::default();
|
||||
dim_view::cancel();
|
||||
|
||||
@@ -65,14 +65,15 @@
|
||||
"ui.combine": "Kombinieren",
|
||||
"ui.coming_later": "bald verfügbar",
|
||||
"ui.concentric": "Konzentrisch",
|
||||
"ui.configurations": "Konfigurationen",
|
||||
"ui.conic_curve": "Kegelschnitt",
|
||||
"ui.constant": "Konstant",
|
||||
"ui.constraints_conflict": "Bedingungen konfliktreich",
|
||||
"ui.construct": "Konstruktion",
|
||||
"ui.construction": "Konstruktionslinie",
|
||||
"ui.copy": "Kopieren",
|
||||
"ui.corner": "Ecke",
|
||||
"ui.corners": "Ecken",
|
||||
"ui.corner": "Ecke",
|
||||
"ui.counterbore": "Tiefenbohrung",
|
||||
"ui.countersink": "Senkung",
|
||||
"ui.create": "Erstellen",
|
||||
@@ -112,6 +113,7 @@
|
||||
"ui.extent_type": "Ausdehnungstyp",
|
||||
"ui.extrude": "Extrudieren",
|
||||
"ui.factor": "Faktor",
|
||||
"ui.feature_history": "Feature-Verlauf",
|
||||
"ui.feature_options": "Funktionsoptionen",
|
||||
"ui.features": "Funktionen",
|
||||
"ui.file": "Datei",
|
||||
@@ -249,8 +251,8 @@
|
||||
"ui.profile_plane": "Profilebene",
|
||||
"ui.profiles": "Profile",
|
||||
"ui.project": "Projizieren",
|
||||
"ui.quantity": "Anzahl",
|
||||
"ui.quantity_2": "Anzahl 2",
|
||||
"ui.quantity": "Anzahl",
|
||||
"ui.quit": "Beenden",
|
||||
"ui.radius": "Radius",
|
||||
"ui.radius_type": "Radius-Typ",
|
||||
@@ -273,7 +275,6 @@
|
||||
"ui.shell": "Hülle",
|
||||
"ui.show": "Einblenden",
|
||||
"ui.show_constraints": "Bedingungen",
|
||||
"ui.show_construction_geometries": "Konstruktionsgeometrien",
|
||||
"ui.show_dimensions": "Maße",
|
||||
"ui.show_points": "Punkte anzeigen",
|
||||
"ui.show_profile": "Profil anzeigen",
|
||||
@@ -291,8 +292,8 @@
|
||||
"ui.slot": "Langloch",
|
||||
"ui.smooth": "Glatt",
|
||||
"ui.snap": "Einrasten",
|
||||
"ui.spacing": "Abstand",
|
||||
"ui.spacing_2": "Abstand 2",
|
||||
"ui.spacing": "Abstand",
|
||||
"ui.sphere": "Sphäre",
|
||||
"ui.spline": "Spline",
|
||||
"ui.split_body": "Körper trennen",
|
||||
@@ -309,11 +310,13 @@
|
||||
"ui.theme": "Design",
|
||||
"ui.thickness": "Dicke",
|
||||
"ui.thread": "Gewinde",
|
||||
"ui.timeline_bottom": "Timeline unten",
|
||||
"ui.timeline_left_note": "Im linken Layout wird die Rücksetzmarke über dem Teile- und Szenenbrowser eingeblendet.",
|
||||
"ui.timeline_left_sidebar": "Linke Seitenleiste",
|
||||
"ui.tip_angle": "Spitzenwinkel",
|
||||
"ui.to_object": "Bis Objekt",
|
||||
"ui.top": "Oben",
|
||||
"ui.torus": "Torus",
|
||||
"ui.total_angle": "Gesamtwinkel",
|
||||
"ui.trim": "Kürzen",
|
||||
"ui.two_sides": "Beidseitig",
|
||||
"ui.ui_scale": "UI-Skalierung",
|
||||
@@ -325,9 +328,359 @@
|
||||
"ui.view_centre": "Ansichtszentrum",
|
||||
"ui.visual_style": "Ansichtsstil",
|
||||
"ui.workspace": "Arbeitsbereich",
|
||||
"ui.x_distance": "X-Abstand",
|
||||
"ui.y_distance": "Y-Abstand",
|
||||
"ui.x_distance": "X-Abstand",
|
||||
"ui.zoom_to_the_cursor": "Zum Cursor zoomen",
|
||||
"ui.animateJoint": "Gelenk animieren",
|
||||
"ui.calibrate": "Kalibrieren",
|
||||
"ui.customizeToolbar": "Symbolleiste anpassen…",
|
||||
"ui.editJointLimits": "Gelenkgrenzen bearbeiten",
|
||||
"ui.exportDxf": "Als DXF exportieren…",
|
||||
"ui.findInTimeline": "In der Zeitleiste suchen",
|
||||
"ui.findInWindow": "Im Fenster suchen",
|
||||
"ui.fit": "Einpassen",
|
||||
"ui.groupSelected": "Auswahl gruppieren",
|
||||
"ui.hideAxes": "Achsen ausblenden",
|
||||
"ui.hideDimension": "Bemaßung ausblenden",
|
||||
"ui.hideOrigin": "Ursprung ausblenden",
|
||||
"ui.hidePlanes": "Ebenen ausblenden",
|
||||
"ui.hideProfile": "Profil ausblenden",
|
||||
"ui.homeView": "Startansicht",
|
||||
"ui.isolate": "Isolieren",
|
||||
"ui.lock": "Sperren",
|
||||
"ui.newSketchGroup": "Neue Skizzengruppe",
|
||||
"ui.normalConstruction": "Normal / Konstruktion",
|
||||
"ui.orbit": "Drehen",
|
||||
"ui.heading_position": "POSITION",
|
||||
"ui.pan": "Verschieben",
|
||||
"ui.pinToShortcuts": "An Verknüpfungen anheften",
|
||||
"ui.pinToToolbar": "An Symbolleiste anheften",
|
||||
"ui.properties": "Eigenschaften",
|
||||
"ui.removeSection": "Schnitt entfernen",
|
||||
"ui.removeFromToolbar": "Aus der Symbolleiste entfernen",
|
||||
"ui.repeat": "Wiederholen",
|
||||
"ui.report_a_bug": "Fehler melden",
|
||||
"ui.restore": "Wiederherstellen",
|
||||
"ui.heading_setup": "EINRICHTEN",
|
||||
"ui.selectOther": "Andere auswählen…",
|
||||
"ui.selectTangentChain": "Tangentialkette auswählen",
|
||||
"ui.showAll": "Alle einblenden",
|
||||
"ui.showAllBodies": "Alle Volumenkörper einblenden",
|
||||
"ui.showAxes": "Achsen einblenden",
|
||||
"ui.showDimension": "Bemaßung einblenden",
|
||||
"ui.showOrigin": "Ursprung einblenden",
|
||||
"ui.showPlanes": "Ebenen einblenden",
|
||||
"ui.suppress": "Unterdrücken",
|
||||
"ui.command_palette_hint": "Befehl eingeben, z. B. {example} — oder S drücken, um zu suchen",
|
||||
"ui.unground": "Fixierung lösen",
|
||||
"ui.unisolate": "Isolierung aufheben",
|
||||
"ui.unlock": "Entsperren",
|
||||
"ui.unsuppress": "Unterdrückung aufheben",
|
||||
"ui.updateToCurrentView": "Auf aktuelle Ansicht aktualisieren",
|
||||
"ui.zoom": "Zoomen",
|
||||
"ui.command_done": "{command} fertig",
|
||||
"cmd.appearance_assign": "Erscheinungsbild",
|
||||
"cmd.appearance_library": "Erscheinungsbild-Bibliothek",
|
||||
"cmd.appearance_list": "Erscheinungsbilder auflisten",
|
||||
"cmd.body_rename": "Körper umbenennen",
|
||||
"cmd.browser_group": "Neue Gruppe",
|
||||
"cmd.browser_move": "Browser-Elemente verschieben",
|
||||
"cmd.browser_order": "Browser-Elemente ordnen",
|
||||
"cmd.browser_rename_group": "Gruppe umbenennen",
|
||||
"cmd.browser_ungroup": "Gruppierung aufheben",
|
||||
"cmd.browser_visibility": "Ein-/Ausblenden",
|
||||
"cmd.canvas_calibrate": "Zeichenfläche kalibrieren",
|
||||
"cmd.canvas_decal": "Dekal",
|
||||
"cmd.canvas_delete": "Zeichenfläche löschen",
|
||||
"cmd.canvas_edit": "Zeichenfläche bearbeiten",
|
||||
"cmd.canvas_insert": "Zeichenfläche",
|
||||
"cmd.component_activate": "Komponente aktivieren",
|
||||
"cmd.component_break_link": "Verknüpfung trennen",
|
||||
"cmd.component_capture_position": "Position erfassen",
|
||||
"cmd.component_create": "Neue Komponente",
|
||||
"cmd.component_delete": "Komponente löschen",
|
||||
"cmd.component_from_bodies": "Komponenten aus Körpern erstellen",
|
||||
"cmd.component_insert_design": "Konstruktion einfügen",
|
||||
"cmd.component_links": "Verknüpfungen auflisten",
|
||||
"cmd.component_list": "Komponenten auflisten",
|
||||
"cmd.component_move_bodies": "Körper in Komponente verschieben",
|
||||
"cmd.component_move_sketches": "Skizzen in Komponente verschieben",
|
||||
"cmd.component_paste_new": "Neu einfügen",
|
||||
"cmd.component_properties": "Physikalische Eigenschaften der Komponente",
|
||||
"cmd.component_rename": "Komponente umbenennen",
|
||||
"cmd.component_revert_position": "Position zurücksetzen",
|
||||
"cmd.component_update_links": "Verknüpfungen aktualisieren",
|
||||
"cmd.config_activate": "Konfiguration aktivieren",
|
||||
"cmd.config_column": "Konfigurationsspalte",
|
||||
"cmd.config_configure": "Konfigurieren",
|
||||
"cmd.config_preview": "Konfiguration vorschauen",
|
||||
"cmd.config_row": "Konfigurationszeile",
|
||||
"cmd.config_rule": "Konfigurationsregel",
|
||||
"cmd.config_rules": "Konfigurationsregeln",
|
||||
"cmd.config_table": "Konfigurationstabelle anzeigen",
|
||||
"cmd.construct_axis_edge": "Achse durch Kante",
|
||||
"cmd.construct_axis_normal_to_face": "Achse senkrecht zur Fläche",
|
||||
"cmd.construct_axis_through_cylinder": "Achse durch Zylinder/Kegel/Torus",
|
||||
"cmd.construct_axis_two_planes": "Achse durch zwei Ebenen",
|
||||
"cmd.construct_axis_two_points": "Achse durch zwei Punkte",
|
||||
"cmd.construct_plane_along_path": "Ebene entlang Pfad",
|
||||
"cmd.construct_plane_midplane": "Mittenebene",
|
||||
"cmd.construct_plane_perpendicular": "Rechtebene",
|
||||
"cmd.construct_plane_tangent": "Tangentialebene",
|
||||
"cmd.construct_plane_three_points": "Ebene durch drei Punkte",
|
||||
"cmd.construct_plane_two_edges": "Ebene durch zwei Kanten",
|
||||
"cmd.construct_point_along_path": "Punkt entlang Pfad",
|
||||
"cmd.construct_point_center": "Punkt im Mittelpunkt von Kreis/Kugel/Torus",
|
||||
"cmd.construct_point_edge_plane": "Punkt an Kante und Ebene",
|
||||
"cmd.construct_point_three_planes": "Punkt aus drei Ebenen",
|
||||
"cmd.construct_point_two_edges": "Punkt aus zwei Kanten",
|
||||
"cmd.construct_point_vertex": "Punkt an Scheitelpunkt",
|
||||
"cmd.contact_check": "Kontakt prüfen",
|
||||
"cmd.contact_create": "Neuer Kontaktsatz",
|
||||
"cmd.contact_disable_all": "Kontakt deaktivieren",
|
||||
"cmd.contact_edit": "Kontaktsatz bearbeiten",
|
||||
"cmd.contact_enable_all": "Alle Kontakte aktivieren",
|
||||
"cmd.contact_enable_sets": "Kontaktsätze aktivieren",
|
||||
"cmd.contact_list": "Kontaktsätze auflisten",
|
||||
"cmd.diagnostics_crash_report": "Absturzbericht",
|
||||
"cmd.diagnostics_crash_reports": "Absturzberichte",
|
||||
"cmd.diagnostics_crash_reports_clear": "Absturzberichte löschen",
|
||||
"cmd.diagnostics_crash_test": "Absturztest",
|
||||
"cmd.doc_recover": "Konstruktion wiederherstellen",
|
||||
"cmd.doc_recovery_discard": "Wiederhergestellte Konstruktion verwerfen",
|
||||
"cmd.doc_recovery_list": "Wiederherstellbare Konstruktionen",
|
||||
"cmd.document_inspect": "Konstruktion prüfen",
|
||||
"cmd.document_units": "Einheiten der Konstruktion ändern",
|
||||
"cmd.edit_checkpoint": "Prüfpunkt",
|
||||
"cmd.edit_restore_checkpoint": "Prüfpunkt wiederherstellen",
|
||||
"cmd.engine_commands": "Befehle auflisten",
|
||||
"cmd.explode_create": "Explosionsdarstellung",
|
||||
"cmd.explode_delete": "Explosionsdarstellung löschen",
|
||||
"cmd.explode_list": "Explosionsdarstellungen auflisten",
|
||||
"cmd.explode_show": "Explosionsdarstellung anzeigen",
|
||||
"cmd.expr_evaluate": "Ausdruck auswerten",
|
||||
"cmd.face_delete": "Fläche löschen",
|
||||
"cmd.file_export": "Exportieren",
|
||||
"cmd.file_insert_mesh": "Netz einfügen",
|
||||
"cmd.file_insert_step": "STEP einfügen",
|
||||
"cmd.file_save_mesh": "Als Netz speichern",
|
||||
"cmd.inspect_accessibility": "Zugänglichkeitsanalyse",
|
||||
"cmd.inspect_center_of_mass": "Massenschwerpunkt",
|
||||
"cmd.inspect_curvature_comb": "Krümmungskamm-Analyse",
|
||||
"cmd.inspect_curvature_map": "Krümmungskartenanalyse",
|
||||
"cmd.inspect_draft": "Auswinkelanalyse",
|
||||
"cmd.inspect_environment_map": "Umgebungskartenanalyse",
|
||||
"cmd.inspect_interference": "Eingriff",
|
||||
"cmd.inspect_isocurve": "Isokurvenanalyse",
|
||||
"cmd.inspect_minimum_radius": "Mindestradiusanalyse",
|
||||
"cmd.inspect_zebra": "Zebra-Analyse",
|
||||
"cmd.joint_as_built": "Fertigungsgelenk",
|
||||
"cmd.joint_create": "Gelenk",
|
||||
"cmd.joint_delete": "Gelenk löschen",
|
||||
"cmd.joint_drive": "Gelenke antreiben",
|
||||
"cmd.joint_edit": "Gelenk bearbeiten",
|
||||
"cmd.joint_limits": "Gelenkgrenzen",
|
||||
"cmd.joint_list": "Gelenke auflisten",
|
||||
"cmd.joint_motion_link": "Bewegungsverknüpfung",
|
||||
"cmd.joint_origin": "Gelenkursprung",
|
||||
"cmd.joint_rigid_group": "Starrgruppe",
|
||||
"cmd.joint_solve": "Gelenke lösen",
|
||||
"cmd.material_assign": "Physikalisches Material",
|
||||
"cmd.material_create": "Neues physikalisches Material",
|
||||
"cmd.material_delete": "Physisches Material löschen",
|
||||
"cmd.material_edit": "Physisches Material bearbeiten",
|
||||
"cmd.material_list": "Materialien auflisten",
|
||||
"cmd.mesh_convert": "Netz konvertieren",
|
||||
"cmd.model_edges": "Kanten auflisten",
|
||||
"cmd.model_faces": "Flächen auflisten",
|
||||
"cmd.model_threads": "Gewinde auflisten",
|
||||
"cmd.motion_delete": "Bewegungsuntersuchung löschen",
|
||||
"cmd.motion_export": "Bewegungsuntersuchung exportieren",
|
||||
"cmd.motion_list": "Bewegungsuntersuchungen auflisten",
|
||||
"cmd.motion_play": "Bewegungsuntersuchung abspielen",
|
||||
"cmd.motion_study": "Bewegungsuntersuchung",
|
||||
"cmd.occurrence_copy": "Einfügen (Instanz)",
|
||||
"cmd.occurrence_ground": "Festsetzen",
|
||||
"cmd.occurrence_move": "Instanz verschieben",
|
||||
"cmd.occurrence_properties": "Physikalische Eigenschaften der Instanz",
|
||||
"cmd.parameters_add": "Benutzerparameter hinzufügen",
|
||||
"cmd.parameters_change": "Parameter ändern",
|
||||
"cmd.parameters_comment": "Parameter kommentieren",
|
||||
"cmd.parameters_complete": "Ausdruck vervollständigen",
|
||||
"cmd.parameters_delete": "Parameter löschen",
|
||||
"cmd.parameters_export": "Parameter exportieren",
|
||||
"cmd.parameters_favorite": "Favorisierter Parameter",
|
||||
"cmd.parameters_graph": "Parametergraph",
|
||||
"cmd.parameters_import": "Parameter importieren",
|
||||
"cmd.parameters_list": "Parameter auflisten",
|
||||
"cmd.parameters_rename": "Parameter umbenennen",
|
||||
"cmd.parameters_users": "Parameterverwendungen",
|
||||
"cmd.parts_fastener": "Verbindungselement einfügen",
|
||||
"cmd.parts_insert": "Bauteil einfügen",
|
||||
"cmd.parts_library": "Standardbauteile",
|
||||
"cmd.parts_resize": "Bauteilgröße ändern",
|
||||
"cmd.plastic_assign_rule": "Kunststoffregel zuweisen",
|
||||
"cmd.plastic_boss": "Erhöhung",
|
||||
"cmd.plastic_lip": "Lippe",
|
||||
"cmd.plastic_manage_rules": "Kunststoffregeln verwalten",
|
||||
"cmd.plastic_rest": "Auflage",
|
||||
"cmd.plastic_rules": "Kunststoffregeln auflisten",
|
||||
"cmd.plastic_snap_fit": "Rastverbindung",
|
||||
"cmd.select_clear": "Auswahl aufheben",
|
||||
"cmd.sheet_convert": "In Blech umwandeln",
|
||||
"cmd.sheet_export_bend_table": "Biegungstabelle exportieren",
|
||||
"cmd.sheet_export_dxf": "Flachmuster als DXF exportieren",
|
||||
"cmd.sheet_flange": "Flansch",
|
||||
"cmd.sheet_flat_pattern": "Flachmuster erstellen",
|
||||
"cmd.sheet_fold": "Falten",
|
||||
"cmd.sheet_hem": "Umkantung",
|
||||
"cmd.sheet_manage_rules": "Blechregeln",
|
||||
"cmd.sheet_refold": "Wieder falten",
|
||||
"cmd.sheet_rules": "Blechregeln auflisten",
|
||||
"cmd.sheet_unfold": "Entfalten",
|
||||
"cmd.sketch_arc_center_point": "Mittelpunkt-Kreisbogen",
|
||||
"cmd.sketch_arc_tangent": "Tangentialbogen",
|
||||
"cmd.sketch_arc_three_point": "3-Punkt-Kreisbogen",
|
||||
"cmd.sketch_auto_constrain": "Auto-Bedingung",
|
||||
"cmd.sketch_auto_constrain_from_datum": "Auto-Bedingung von Bezug",
|
||||
"cmd.sketch_auto_project": "Kanten automatisch auf Referenz projizieren",
|
||||
"cmd.sketch_blend_curve": "Übergangskurve",
|
||||
"cmd.sketch_chain": "Kette",
|
||||
"cmd.sketch_chamfer_distance_angle": "Abschrägung mit Abstand und Winkel",
|
||||
"cmd.sketch_chamfer_equal_distance": "Abschrägung mit gleichem Abstand",
|
||||
"cmd.sketch_chamfer_two_distance": "Abschrägung mit zwei Abständen",
|
||||
"cmd.sketch_circle_center": "Kreis aus Mittelpunkt und Durchmesser",
|
||||
"cmd.sketch_circle_three_point": "3-Punkt-Kreis",
|
||||
"cmd.sketch_circle_three_tangent": "3-Tangenten-Kreis",
|
||||
"cmd.sketch_circle_two_point": "2-Punkt-Kreis",
|
||||
"cmd.sketch_circle_two_tangent": "2-Tangenten-Kreis",
|
||||
"cmd.sketch_constrainer": "Skizzen-Bedingung",
|
||||
"cmd.sketch_constraint_fix": "Fixieren/Lösen",
|
||||
"cmd.sketch_constraint_midpoint": "Mittelpunkt",
|
||||
"cmd.sketch_construction": "Normal/Konstruktion",
|
||||
"cmd.sketch_delete": "Skizzenentitäten löschen",
|
||||
"cmd.sketch_dimension_text": "Maßtext verschieben",
|
||||
"cmd.sketch_drag": "Skizzengeometrie ziehen",
|
||||
"cmd.sketch_drag_end": "Skizzen-Ziehen beenden",
|
||||
"cmd.sketch_draw_dimensioned": "Skizzengeometrie mit Maßen zeichnen",
|
||||
"cmd.sketch_edit": "Skizze bearbeiten",
|
||||
"cmd.sketch_edit_text": "Text bearbeiten",
|
||||
"cmd.sketch_export_dxf": "Als DXF speichern",
|
||||
"cmd.sketch_finish_auto_constrain": "Mit Auto-Bedingung fertigstellen",
|
||||
"cmd.sketch_fit_curves_to_section": "Kurven an Netzquerschnitt anpassen",
|
||||
"cmd.sketch_glyphs": "Bedingungssymbole",
|
||||
"cmd.sketch_include_3d": "3D-Geometrie einbeziehen",
|
||||
"cmd.sketch_insert_dxf": "DXF einfügen",
|
||||
"cmd.sketch_insert_svg": "SVG einfügen",
|
||||
"cmd.sketch_inspect": "Skizze prüfen",
|
||||
"cmd.sketch_intersection_curve": "Schnittkurve",
|
||||
"cmd.sketch_isoparametric_curve": "Isoparametrische Kurve",
|
||||
"cmd.sketch_line3d": "3D-Linie",
|
||||
"cmd.sketch_line_midpoint": "Linie über den Mittelpunkt",
|
||||
"cmd.sketch_move": "Skizzenentitäten verschieben/kopieren",
|
||||
"cmd.sketch_move3d": "3D-Punkt verschieben",
|
||||
"cmd.sketch_move_point": "Skizzenpunkt ziehen",
|
||||
"cmd.sketch_offset_break": "Versatzverknüpfung trennen",
|
||||
"cmd.sketch_options": "Skizzenpalettenoptionen",
|
||||
"cmd.sketch_point3d": "3D-Punkt",
|
||||
"cmd.sketch_polygon_circumscribed": "Umgeschriebenes Polygon",
|
||||
"cmd.sketch_polygon_edge": "Polygon über eine Kante",
|
||||
"cmd.sketch_polygon_inscribed": "Eingeschriebenes Polygon",
|
||||
"cmd.sketch_project_to_surface": "Auf Fläche projizieren",
|
||||
"cmd.sketch_rectangle_center": "Rechteck vom Mittelpunkt",
|
||||
"cmd.sketch_rectangle_three_point": "3-Punkt-Rechteck",
|
||||
"cmd.sketch_rectangle_two_point": "2-Punkt-Rechteck",
|
||||
"cmd.sketch_redefine": "Skizzierebene neu definieren",
|
||||
"cmd.sketch_slot_arc_center": "Mittelpunkt-Bogenschlitz",
|
||||
"cmd.sketch_slot_arc_three_point": "3-Punkt-Bogenschlitz",
|
||||
"cmd.sketch_slot_center_point": "Mittelpunktschlitz",
|
||||
"cmd.sketch_slot_center_to_center": "Schlitz von Mittelpunkt zu Mittelpunkt",
|
||||
"cmd.sketch_slot_overall": "Gesamtschlitz",
|
||||
"cmd.sketch_snap": "Einrastinferenz",
|
||||
"cmd.sketch_solve": "Skizze lösen",
|
||||
"cmd.sketch_spline3d": "3D-Spline",
|
||||
"cmd.sketch_spline_control_point": "Spline mit Kontrollpunkten",
|
||||
"cmd.sketch_spline_control_point_5": "Spline mit Kontrollpunkten (Grad 5)",
|
||||
"cmd.sketch_spline_fit_point": "Spline mit Fitpunkten",
|
||||
"cmd.sketch_spun_profile": "Gedrehtes Profil",
|
||||
"cmd.sketch_toggle_driven": "Abhängiges Maß umschalten",
|
||||
"cmd.solid_align": "Ausrichten",
|
||||
"cmd.solid_boundary_fill": "Begrenzungsfüllung",
|
||||
"cmd.solid_bounding_solid": "Begrenzender Körper",
|
||||
"cmd.solid_emboss": "Prägen",
|
||||
"cmd.solid_move": "Verschieben/Kopieren",
|
||||
"cmd.solid_offset_face": "Fläche versetzen",
|
||||
"cmd.solid_press_pull": "Drücken/Ziehen",
|
||||
"cmd.solid_remove": "Entfernen",
|
||||
"cmd.solid_replace_face": "Fläche ersetzen",
|
||||
"cmd.solid_rib": "Rippe",
|
||||
"cmd.solid_split_face": "Fläche teilen",
|
||||
"cmd.solid_web": "Gewebe",
|
||||
"cmd.surface_patch": "Patch",
|
||||
"cmd.surface_stitch": "Nähen",
|
||||
"cmd.surface_thicken": "Dicke vergeben",
|
||||
"cmd.surface_extrude": "Flächen-Extrusion",
|
||||
"cmd.surface_revolve": "Flächen-Rotation",
|
||||
"cmd.surface_sweep": "Flächen-Sweep",
|
||||
"cmd.surface_loft": "Flächen-Loft",
|
||||
"cmd.surface_ruled": "Regelfläche",
|
||||
"cmd.surface_boundary": "Begrenzungsfläche",
|
||||
"cmd.surface_offset": "Offsetfläche",
|
||||
"cmd.surface_blend": "Verbindungsfläche",
|
||||
"cmd.surface_fillet": "Flächenverrundung",
|
||||
"cmd.surface_untrim": "Trimmen aufheben",
|
||||
"cmd.surface_unstitch": "Verbund aufheben",
|
||||
"cmd.surface_reverse_normal": "Normale umkehren",
|
||||
"cmd.inspect_continuity": "Kontinuitätsprüfung",
|
||||
"cmd.timeline_compute_all": "Alles berechnen",
|
||||
"cmd.timeline_copy": "Merkmale kopieren",
|
||||
"cmd.timeline_dependents": "Merkmalsabhängigkeiten",
|
||||
"cmd.timeline_inspect": "Merkmal prüfen",
|
||||
"cmd.timeline_paste": "Merkmale einfügen",
|
||||
"cmd.timeline_redefine": "Merkmal bearbeiten",
|
||||
"cmd.timeline_rename": "Umbenennen",
|
||||
"cmd.timeline_reorder": "Merkmal neu ordnen",
|
||||
"cmd.timeline_roll_to": "Verlaufsmarkierung verschieben",
|
||||
"cmd.timeline_rollback": "Zeitleistenmarkierung verschieben",
|
||||
"cmd.timeline_suppress": "Merkmal unterdrücken",
|
||||
"cmd.view_apply": "Benannte Ansicht wiederherstellen",
|
||||
"cmd.view_delete": "Benannte Ansicht löschen",
|
||||
"cmd.view_list": "Benannte Ansichten auflisten",
|
||||
"cmd.view_rename": "Benannte Ansicht umbenennen",
|
||||
"cmd.view_save": "Neue benannte Ansicht",
|
||||
"ui.export_ellipsis_keymap": "Exportieren…",
|
||||
"ui.heading_configure": "KONFIGURIEREN",
|
||||
"ui.heading_constraints": "RANDBEDINGUNGEN",
|
||||
"ui.heading_utilities": "WERKZEUGE",
|
||||
"BROWSER": "BROWSER",
|
||||
"Drive Joint": "Gelenk antreiben",
|
||||
"Edit Profile Sketch": "Profile-Skizze bearbeiten",
|
||||
"Find in Browser": "Im Browser suchen",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "Fehler melden — Screenshot prüfen und einen GitHub-Entwurf öffnen",
|
||||
"Reset Toolbar": "Symbolleiste zurücksetzen",
|
||||
"Select bodies (a face picks its body)": "Körper auswählen (eine Fläche wählt ihren Körper)",
|
||||
"Selection filter": "Auswahlfilter",
|
||||
"Revert Position": "Position zurücksetzen",
|
||||
"Construction": "Konstruktionslinie",
|
||||
"Faces": "Flächen",
|
||||
"Edges": "Kanten",
|
||||
"Vertices": "Scheitelpunkte",
|
||||
"Sketch geometry and profiles": "Skizzengeometrie und Profile",
|
||||
"Sketch curves": "Skizzenkurven",
|
||||
"Sketch points": "Skizzenpunkte",
|
||||
"Sketch dimensions": "Skizzenmaße",
|
||||
"Sketch constraints": "Skizzenbedingungen",
|
||||
"Flat": "Flach",
|
||||
"cmd.render_backends": "Render-Backends",
|
||||
"cmd.view_navigate": "Ansicht navigieren",
|
||||
"timeline.group": "Neuen Merkmalsordner anlegen",
|
||||
"timeline.ungroup": "Merkmalsordner entfernen",
|
||||
"timeline.rename_group": "Merkmalsordner umbenennen",
|
||||
"timeline.move_to_group": "Merkmale in Ordner verschieben",
|
||||
"timeline.move_many": "Zeitleistenelemente verschieben",
|
||||
"timeline.reorder_many": "Merkmale neu ordnen",
|
||||
"ui.show_construction_geometries": "Konstruktionsgeometrien",
|
||||
"ui.total_angle": "Gesamtwinkel",
|
||||
"sketch_text.builtin_font": "{family} (integriert)",
|
||||
"sketch_text.font_search": "Schriftarten suchen",
|
||||
"sketch_text.loading_fonts": "Installierte Schriftarten werden gesucht…",
|
||||
|
||||
@@ -245,6 +245,19 @@
|
||||
"cmd.surface_patch": "Patch",
|
||||
"cmd.surface_stitch": "Stitch",
|
||||
"cmd.surface_thicken": "Thicken",
|
||||
"cmd.surface_extrude": "Surface Extrude",
|
||||
"cmd.surface_revolve": "Surface Revolve",
|
||||
"cmd.surface_sweep": "Surface Sweep",
|
||||
"cmd.surface_loft": "Surface Loft",
|
||||
"cmd.surface_ruled": "Ruled Surface",
|
||||
"cmd.surface_boundary": "Boundary Surface",
|
||||
"cmd.surface_offset": "Offset Surface",
|
||||
"cmd.surface_blend": "Blend Surface",
|
||||
"cmd.surface_fillet": "Surface Fillet",
|
||||
"cmd.surface_untrim": "Untrim",
|
||||
"cmd.surface_unstitch": "Unstitch",
|
||||
"cmd.surface_reverse_normal": "Reverse Normal",
|
||||
"cmd.inspect_continuity": "Continuity Analysis",
|
||||
"cmd.timeline_compute_all": "Compute All",
|
||||
"cmd.timeline_copy": "Copy Features",
|
||||
"cmd.timeline_dependents": "Feature Dependents",
|
||||
@@ -326,6 +339,7 @@
|
||||
"ui.combine": "Combine",
|
||||
"ui.coming_later": "coming later",
|
||||
"ui.concentric": "Concentric",
|
||||
"ui.configurations": "Configurations",
|
||||
"ui.conic_curve": "Conic Curve",
|
||||
"ui.constant": "Constant",
|
||||
"ui.constraints_conflict": "Constraints conflict",
|
||||
@@ -376,6 +390,7 @@
|
||||
"ui.extent_type": "Extent Type",
|
||||
"ui.extrude": "Extrude",
|
||||
"ui.factor": "Factor",
|
||||
"ui.feature_history": "Feature history",
|
||||
"ui.feature_options": "Feature Options",
|
||||
"ui.features": "Features",
|
||||
"ui.file": "File",
|
||||
@@ -556,7 +571,6 @@
|
||||
"ui.shell": "Shell",
|
||||
"ui.show": "Show",
|
||||
"ui.show_constraints": "Constraints",
|
||||
"ui.show_construction_geometries": "Construction Geometries",
|
||||
"ui.show_dimensions": "Dimensions",
|
||||
"ui.show_points": "Show Points",
|
||||
"ui.show_profile": "Show Profile",
|
||||
@@ -597,11 +611,13 @@
|
||||
"ui.theme": "Theme",
|
||||
"ui.thickness": "Thickness",
|
||||
"ui.thread": "Thread",
|
||||
"ui.timeline_bottom": "Bottom timeline",
|
||||
"ui.timeline_left_note": "The left layout shows the rollback bar above the parts and scene browser.",
|
||||
"ui.timeline_left_sidebar": "Left sidebar",
|
||||
"ui.tip_angle": "Tip Angle",
|
||||
"ui.to_object": "To Object",
|
||||
"ui.top": "Top",
|
||||
"ui.torus": "Torus",
|
||||
"ui.total_angle": "Total angle",
|
||||
"ui.trackpad_pan_tip": "Trackpad gestures pan (off: they zoom like the wheel); mouse wheel zooms, thumb wheel pans sideways, pinch always zooms",
|
||||
"ui.trim": "Trim",
|
||||
"ui.two_finger_scroll_pans": "Two-finger scroll pans",
|
||||
@@ -862,6 +878,62 @@
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).",
|
||||
"cmd.render_backends": "Render Backends",
|
||||
"cmd.view_navigate": "Navigate View",
|
||||
"ui.animateJoint": "Animate Joint",
|
||||
"ui.calibrate": "Calibrate",
|
||||
"ui.customizeToolbar": "Customize Toolbar…",
|
||||
"ui.editJointLimits": "Edit Joint Limits",
|
||||
"ui.exportDxf": "Export DXF…",
|
||||
"ui.findInTimeline": "Find in Timeline",
|
||||
"ui.findInWindow": "Find in Window",
|
||||
"ui.fit": "Fit",
|
||||
"ui.groupSelected": "Group Selected",
|
||||
"ui.hideAxes": "Hide Axes",
|
||||
"ui.hideDimension": "Hide Dimension",
|
||||
"ui.hideOrigin": "Hide Origin",
|
||||
"ui.hidePlanes": "Hide Planes",
|
||||
"ui.hideProfile": "Hide Profile",
|
||||
"ui.homeView": "Home View",
|
||||
"ui.isolate": "Isolate",
|
||||
"ui.lock": "Lock",
|
||||
"ui.newSketchGroup": "New Sketch Group",
|
||||
"ui.normalConstruction": "Normal / Construction",
|
||||
"ui.orbit": "Orbit",
|
||||
"ui.heading_position": "POSITION",
|
||||
"ui.pan": "Pan",
|
||||
"ui.pinToShortcuts": "Pin to Shortcuts",
|
||||
"ui.pinToToolbar": "Pin to Toolbar",
|
||||
"ui.properties": "Properties",
|
||||
"ui.removeSection": "Remove Section",
|
||||
"ui.removeFromToolbar": "Remove from Toolbar",
|
||||
"ui.repeat": "Repeat",
|
||||
"ui.report_a_bug": "Report a bug",
|
||||
"ui.restore": "Restore",
|
||||
"ui.heading_setup": "SETUP",
|
||||
"ui.selectOther": "Select Other…",
|
||||
"ui.selectTangentChain": "Select Tangent Chain",
|
||||
"ui.showAll": "Show All",
|
||||
"ui.showAllBodies": "Show All Bodies",
|
||||
"ui.showAxes": "Show Axes",
|
||||
"ui.showDimension": "Show Dimension",
|
||||
"ui.showOrigin": "Show Origin",
|
||||
"ui.showPlanes": "Show Planes",
|
||||
"ui.suppress": "Suppress",
|
||||
"ui.command_palette_hint": "Type a command, e.g. {example} — or press S to search",
|
||||
"ui.unground": "Unground",
|
||||
"ui.unisolate": "Unisolate",
|
||||
"ui.unlock": "Unlock",
|
||||
"ui.unsuppress": "Unsuppress",
|
||||
"ui.updateToCurrentView": "Update to Current View",
|
||||
"ui.zoom": "Zoom",
|
||||
"ui.command_done": "{command} done",
|
||||
"timeline.group": "New Feature Folder",
|
||||
"timeline.ungroup": "Remove Feature Folders",
|
||||
"timeline.rename_group": "Rename Feature Folder",
|
||||
"timeline.move_to_group": "Move Features to Folder",
|
||||
"timeline.move_many": "Move Timeline Items",
|
||||
"timeline.reorder_many": "Reorder Features",
|
||||
"ui.show_construction_geometries": "Construction Geometries",
|
||||
"ui.total_angle": "Total angle",
|
||||
"sketch_text.builtin_font": "{family} (built-in)",
|
||||
"sketch_text.font_search": "Search fonts",
|
||||
"sketch_text.loading_fonts": "Looking for installed fonts…",
|
||||
@@ -879,4 +951,4 @@
|
||||
"Line Spacing": "Line Spacing",
|
||||
"Path": "Path",
|
||||
"sketch_text.on_path": "The text follows its path: the curve places it"
|
||||
}
|
||||
}
|
||||
@@ -245,6 +245,19 @@
|
||||
"cmd.surface_patch": "パッチ",
|
||||
"cmd.surface_stitch": "ステッチ",
|
||||
"cmd.surface_thicken": "厚み付け",
|
||||
"cmd.surface_extrude": "サーフェス押し出し",
|
||||
"cmd.surface_revolve": "サーフェス回転",
|
||||
"cmd.surface_sweep": "サーフェススイープ",
|
||||
"cmd.surface_loft": "サーフェスロフト",
|
||||
"cmd.surface_ruled": "ルールドサーフェス",
|
||||
"cmd.surface_boundary": "境界サーフェス",
|
||||
"cmd.surface_offset": "オフセットサーフェス",
|
||||
"cmd.surface_blend": "ブレンドサーフェス",
|
||||
"cmd.surface_fillet": "サーフェスフィレット",
|
||||
"cmd.surface_untrim": "トリム解除",
|
||||
"cmd.surface_unstitch": "縫い合わせ解除",
|
||||
"cmd.surface_reverse_normal": "法線を反転",
|
||||
"cmd.inspect_continuity": "連続性解析",
|
||||
"cmd.timeline_compute_all": "すべて再計算",
|
||||
"cmd.timeline_copy": "フィーチャーをコピー",
|
||||
"cmd.timeline_dependents": "従属フィーチャー",
|
||||
@@ -326,6 +339,7 @@
|
||||
"ui.combine": "結合",
|
||||
"ui.coming_later": "準備中",
|
||||
"ui.concentric": "同心",
|
||||
"ui.configurations": "構成",
|
||||
"ui.conic_curve": "円錐曲線",
|
||||
"ui.constant": "一定",
|
||||
"ui.constraints_conflict": "拘束が競合しています",
|
||||
@@ -376,6 +390,7 @@
|
||||
"ui.extent_type": "範囲タイプ",
|
||||
"ui.extrude": "押し出し",
|
||||
"ui.factor": "倍率",
|
||||
"ui.feature_history": "フィーチャー履歴",
|
||||
"ui.feature_options": "フィーチャーオプション",
|
||||
"ui.features": "フィーチャー",
|
||||
"ui.file": "ファイル",
|
||||
@@ -556,7 +571,6 @@
|
||||
"ui.shell": "シェル",
|
||||
"ui.show": "表示",
|
||||
"ui.show_constraints": "拘束",
|
||||
"ui.show_construction_geometries": "構築ジオメトリ",
|
||||
"ui.show_dimensions": "寸法",
|
||||
"ui.show_points": "点を表示",
|
||||
"ui.show_profile": "プロファイルを表示",
|
||||
@@ -597,11 +611,13 @@
|
||||
"ui.theme": "テーマ",
|
||||
"ui.thickness": "厚さ",
|
||||
"ui.thread": "ねじ",
|
||||
"ui.timeline_bottom": "下部タイムライン",
|
||||
"ui.timeline_left_note": "左レイアウトでは、ロールバックバーがパーツとシーンブラウザーの上に表示されます。",
|
||||
"ui.timeline_left_sidebar": "左サイドバー",
|
||||
"ui.tip_angle": "先端角度",
|
||||
"ui.to_object": "オブジェクトまで",
|
||||
"ui.top": "上面",
|
||||
"ui.torus": "トーラス",
|
||||
"ui.total_angle": "合計角度",
|
||||
"ui.trackpad_pan_tip": "トラックパッドのジェスチャーでパンします(オフの場合はマウスホイールと同様にズームします)。マウスホイールはズーム、サイドホイールは横方向のパン、ピンチ操作は常にズームになります。",
|
||||
"ui.trim": "トリム",
|
||||
"ui.two_finger_scroll_pans": "2本指スクロールでパン",
|
||||
@@ -862,6 +878,62 @@
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "その他を選択: モデル上で左ボタンを長押し(または右クリック › その他を選択…)するとカーソル下の要素が一覧表示されます。同じ場所を再度クリックすると次の要素が選択されます。中ボタンをダブルクリックするとホームビューに戻ります(環境設定 › ナビゲーション)。",
|
||||
"cmd.render_backends": "レンダリング バックエンド",
|
||||
"cmd.view_navigate": "ビューナビゲーション",
|
||||
"ui.animateJoint": "ジョイントをアニメート",
|
||||
"ui.calibrate": "校正",
|
||||
"ui.customizeToolbar": "ツールバーをカスタマイズ…",
|
||||
"ui.editJointLimits": "ジョイント制限を編集",
|
||||
"ui.exportDxf": "DXF をエクスポート…",
|
||||
"ui.findInTimeline": "タイムラインで検索",
|
||||
"ui.findInWindow": "ウィンドウで検索",
|
||||
"ui.fit": "フィット",
|
||||
"ui.groupSelected": "選択をグループ化",
|
||||
"ui.hideAxes": "軸を非表示",
|
||||
"ui.hideDimension": "寸法を非表示",
|
||||
"ui.hideOrigin": "原点を非表示",
|
||||
"ui.hidePlanes": "平面を非表示",
|
||||
"ui.hideProfile": "プロファイルを非表示",
|
||||
"ui.homeView": "ホームビュー",
|
||||
"ui.isolate": "分離表示",
|
||||
"ui.lock": "ロック",
|
||||
"ui.newSketchGroup": "新しいスケッチグループ",
|
||||
"ui.normalConstruction": "通常 / コンストラクション",
|
||||
"ui.orbit": "回転",
|
||||
"ui.heading_position": "配置",
|
||||
"ui.pan": "パン",
|
||||
"ui.pinToShortcuts": "ショートカットに固定",
|
||||
"ui.pinToToolbar": "ツールバーに固定",
|
||||
"ui.properties": "プロパティ",
|
||||
"ui.removeSection": "セクションを解除",
|
||||
"ui.removeFromToolbar": "ツールバーから削除",
|
||||
"ui.repeat": "繰り返し",
|
||||
"ui.report_a_bug": "不具合を報告",
|
||||
"ui.restore": "復元",
|
||||
"ui.heading_setup": "セットアップ",
|
||||
"ui.selectOther": "その他を選択…",
|
||||
"ui.selectTangentChain": "接線チェーンを選択",
|
||||
"ui.showAll": "すべて表示",
|
||||
"ui.showAllBodies": "すべてのボディを表示",
|
||||
"ui.showAxes": "軸を表示",
|
||||
"ui.showDimension": "寸法を表示",
|
||||
"ui.showOrigin": "原点を表示",
|
||||
"ui.showPlanes": "平面を表示",
|
||||
"ui.suppress": "抑制",
|
||||
"ui.command_palette_hint": "コマンドを入力(例: {example})— または S キーで検索",
|
||||
"ui.unground": "固定を解除",
|
||||
"ui.unisolate": "分離表示を解除",
|
||||
"ui.unlock": "ロック解除",
|
||||
"ui.unsuppress": "抑制を解除",
|
||||
"ui.updateToCurrentView": "現在のビューに更新",
|
||||
"ui.zoom": "ズーム",
|
||||
"ui.command_done": "{command} 完了",
|
||||
"timeline.group": "フィーチャーフォルダを新規作成",
|
||||
"timeline.ungroup": "フィーチャーフォルダを削除",
|
||||
"timeline.rename_group": "フィーチャーフォルダの名前を変更",
|
||||
"timeline.move_to_group": "フィーチャーをフォルダへ移動",
|
||||
"timeline.move_many": "タイムライン項目を移動",
|
||||
"timeline.reorder_many": "フィーチャーの順序を変更",
|
||||
"ui.show_construction_geometries": "構築ジオメトリ",
|
||||
"ui.total_angle": "合計角度",
|
||||
"sketch_text.builtin_font": "{family}(内蔵)",
|
||||
"sketch_text.font_search": "フォントを検索",
|
||||
"sketch_text.loading_fonts": "インストール済みフォントを検索中…",
|
||||
@@ -879,4 +951,4 @@
|
||||
"Line Spacing": "行間",
|
||||
"Path": "パス",
|
||||
"sketch_text.on_path": "文字はパスに沿います: 位置は曲線で決まります"
|
||||
}
|
||||
}
|
||||
@@ -65,14 +65,15 @@
|
||||
"ui.combine": "결합",
|
||||
"ui.coming_later": "추후 지원 예정",
|
||||
"ui.concentric": "동심",
|
||||
"ui.configurations": "구성",
|
||||
"ui.conic_curve": "원뿔 곡선",
|
||||
"ui.constant": "상수",
|
||||
"ui.constraints_conflict": "구속 조건 충돌",
|
||||
"ui.construct": "구성",
|
||||
"ui.construction": "구성선",
|
||||
"ui.copy": "복사",
|
||||
"ui.corner": "코너",
|
||||
"ui.corners": "코너",
|
||||
"ui.corner": "코너",
|
||||
"ui.counterbore": "카운터보어",
|
||||
"ui.countersink": "카운터싱크",
|
||||
"ui.create": "만들기",
|
||||
@@ -112,6 +113,7 @@
|
||||
"ui.extent_type": "범위 유형",
|
||||
"ui.extrude": "돌출",
|
||||
"ui.factor": "배율",
|
||||
"ui.feature_history": "피처 기록",
|
||||
"ui.feature_options": "피처 옵션",
|
||||
"ui.features": "피처",
|
||||
"ui.file": "파일",
|
||||
@@ -249,8 +251,8 @@
|
||||
"ui.profile_plane": "프로파일 평면",
|
||||
"ui.profiles": "프로파일",
|
||||
"ui.project": "투영",
|
||||
"ui.quantity": "수량",
|
||||
"ui.quantity_2": "수량 2",
|
||||
"ui.quantity": "수량",
|
||||
"ui.quit": "종료",
|
||||
"ui.radius": "반지름",
|
||||
"ui.radius_type": "반지름 유형",
|
||||
@@ -273,7 +275,6 @@
|
||||
"ui.shell": "쉘",
|
||||
"ui.show": "표시",
|
||||
"ui.show_constraints": "구속 조건",
|
||||
"ui.show_construction_geometries": "구성 형상",
|
||||
"ui.show_dimensions": "치수",
|
||||
"ui.show_points": "점 표시",
|
||||
"ui.show_profile": "프로파일 표시",
|
||||
@@ -291,8 +292,8 @@
|
||||
"ui.slot": "슬롯",
|
||||
"ui.smooth": "부드럽게",
|
||||
"ui.snap": "스냅",
|
||||
"ui.spacing": "간각",
|
||||
"ui.spacing_2": "간각 2",
|
||||
"ui.spacing_2": "간격 2",
|
||||
"ui.spacing": "간격",
|
||||
"ui.sphere": "구",
|
||||
"ui.spline": "스플라인",
|
||||
"ui.split_body": "바디 분할",
|
||||
@@ -309,11 +310,13 @@
|
||||
"ui.theme": "테마",
|
||||
"ui.thickness": "두께",
|
||||
"ui.thread": "나사산",
|
||||
"ui.timeline_bottom": "하단 타임라인",
|
||||
"ui.timeline_left_note": "왼쪽 레이아웃에서는 롤백 바가 부품 및 장면 브라우저 위에 표시됩니다.",
|
||||
"ui.timeline_left_sidebar": "왼쪽 사이드바",
|
||||
"ui.tip_angle": "선단 각도",
|
||||
"ui.to_object": "객체까지",
|
||||
"ui.top": "상면",
|
||||
"ui.torus": "토러스",
|
||||
"ui.total_angle": "전진 각도",
|
||||
"ui.trim": "트림",
|
||||
"ui.two_sides": "양방향",
|
||||
"ui.ui_scale": "UI 배율",
|
||||
@@ -325,9 +328,359 @@
|
||||
"ui.view_centre": "뷰 중심",
|
||||
"ui.visual_style": "시각 스타일",
|
||||
"ui.workspace": "작업 공간",
|
||||
"ui.x_distance": "X 거리",
|
||||
"ui.y_distance": "Y 거리",
|
||||
"ui.x_distance": "X 거리",
|
||||
"ui.zoom_to_the_cursor": "커서 위치로 확대/축소",
|
||||
"ui.animateJoint": "조인트 애니메이션",
|
||||
"ui.calibrate": "캘리브레이션",
|
||||
"ui.customizeToolbar": "도구 모음 사용자 지정…",
|
||||
"ui.editJointLimits": "조인트 제한 편집",
|
||||
"ui.exportDxf": "DXF 내보내기…",
|
||||
"ui.findInTimeline": "타임라인에서 찾기",
|
||||
"ui.findInWindow": "창에서 찾기",
|
||||
"ui.fit": "맞춤",
|
||||
"ui.groupSelected": "선택 항목 그룹화",
|
||||
"ui.hideAxes": "축 숨기기",
|
||||
"ui.hideDimension": "치수 숨기기",
|
||||
"ui.hideOrigin": "원점 숨기기",
|
||||
"ui.hidePlanes": "평면 숨기기",
|
||||
"ui.hideProfile": "윤곽 숨기기",
|
||||
"ui.homeView": "홈 보기",
|
||||
"ui.isolate": "분리 표시",
|
||||
"ui.lock": "잠금",
|
||||
"ui.newSketchGroup": "새 스케치 그룹",
|
||||
"ui.normalConstruction": "일반 / 생성",
|
||||
"ui.orbit": "회전",
|
||||
"ui.heading_position": "배치",
|
||||
"ui.pan": "이동",
|
||||
"ui.pinToShortcuts": "바로 가기에 고정",
|
||||
"ui.pinToToolbar": "도구 모음에 고정",
|
||||
"ui.properties": "속성",
|
||||
"ui.removeSection": "단면 제거",
|
||||
"ui.removeFromToolbar": "도구 모음에서 제거",
|
||||
"ui.repeat": "반복",
|
||||
"ui.report_a_bug": "버그 신고",
|
||||
"ui.restore": "복원",
|
||||
"ui.heading_setup": "설정",
|
||||
"ui.selectOther": "기타 선택…",
|
||||
"ui.selectTangentChain": "접선 체인 선택",
|
||||
"ui.showAll": "모두 표시",
|
||||
"ui.showAllBodies": "모두 표시",
|
||||
"ui.showAxes": "축 표시",
|
||||
"ui.showDimension": "치수 표시",
|
||||
"ui.showOrigin": "원점 표시",
|
||||
"ui.showPlanes": "평면 표시",
|
||||
"ui.suppress": "비활성화",
|
||||
"ui.command_palette_hint": "명령 입력(예: {example}) — 또는 S 키를 눌러 검색",
|
||||
"ui.unground": "고정 해제",
|
||||
"ui.unisolate": "분리 표시 해제",
|
||||
"ui.unlock": "잠금 해제",
|
||||
"ui.unsuppress": "비활성화 해제",
|
||||
"ui.updateToCurrentView": "현재 보기로 업데이트",
|
||||
"ui.zoom": "확대/축소",
|
||||
"ui.command_done": "{command} 완료",
|
||||
"cmd.appearance_assign": "외관",
|
||||
"cmd.appearance_library": "외관 라이브러리",
|
||||
"cmd.appearance_list": "외관 목록",
|
||||
"cmd.body_rename": "바디 이름 변경",
|
||||
"cmd.browser_group": "새 그룹",
|
||||
"cmd.browser_move": "브라우저 항목 이동",
|
||||
"cmd.browser_order": "브라우저 항목 정렬",
|
||||
"cmd.browser_rename_group": "그룹 이름 변경",
|
||||
"cmd.browser_ungroup": "그룹 해제",
|
||||
"cmd.browser_visibility": "표시/숨기기",
|
||||
"cmd.canvas_calibrate": "캔버스 보정",
|
||||
"cmd.canvas_decal": "데칼",
|
||||
"cmd.canvas_delete": "캔버스 삭제",
|
||||
"cmd.canvas_edit": "캔버스 편집",
|
||||
"cmd.canvas_insert": "캔버스",
|
||||
"cmd.component_activate": "구성 요소 활성화",
|
||||
"cmd.component_break_link": "링크 끊기",
|
||||
"cmd.component_capture_position": "위치 캡처",
|
||||
"cmd.component_create": "새 구성 요소",
|
||||
"cmd.component_delete": "구성 요소 삭제",
|
||||
"cmd.component_from_bodies": "바디로 구성 요소 만들기",
|
||||
"cmd.component_insert_design": "설계 삽입",
|
||||
"cmd.component_links": "링크 목록",
|
||||
"cmd.component_list": "구성 요소 목록",
|
||||
"cmd.component_move_bodies": "바디를 구성 요소로 이동",
|
||||
"cmd.component_move_sketches": "스케치를 구성 요소로 이동",
|
||||
"cmd.component_paste_new": "새로 붙여넣기",
|
||||
"cmd.component_properties": "구성 요소 물리 속성",
|
||||
"cmd.component_rename": "구성 요소 이름 변경",
|
||||
"cmd.component_revert_position": "위치 복원",
|
||||
"cmd.component_update_links": "링크 업데이트",
|
||||
"cmd.config_activate": "구성 활성화",
|
||||
"cmd.config_column": "구성 열",
|
||||
"cmd.config_configure": "구성",
|
||||
"cmd.config_preview": "구성 미리 보기",
|
||||
"cmd.config_row": "구성 행",
|
||||
"cmd.config_rule": "구성 규칙",
|
||||
"cmd.config_rules": "구성 규칙",
|
||||
"cmd.config_table": "구성 표시",
|
||||
"cmd.construct_axis_edge": "모서리를 통과하는 축",
|
||||
"cmd.construct_axis_normal_to_face": "면에 수직인 축",
|
||||
"cmd.construct_axis_through_cylinder": "원기둥/원뿔/토러스를 통과하는 축",
|
||||
"cmd.construct_axis_two_planes": "두 평면을 통과하는 축",
|
||||
"cmd.construct_axis_two_points": "두 점을 통과하는 축",
|
||||
"cmd.construct_plane_along_path": "경로를 따르는 평면",
|
||||
"cmd.construct_plane_midplane": "중간 평면",
|
||||
"cmd.construct_plane_perpendicular": "수직 평면",
|
||||
"cmd.construct_plane_tangent": "접선 평면",
|
||||
"cmd.construct_plane_three_points": "세 점을 통과하는 평면",
|
||||
"cmd.construct_plane_two_edges": "두 모서리를 통과하는 평면",
|
||||
"cmd.construct_point_along_path": "경로를 따르는 점",
|
||||
"cmd.construct_point_center": "원/구/토러스 중심의 점",
|
||||
"cmd.construct_point_edge_plane": "모서리와 평면의 점",
|
||||
"cmd.construct_point_three_planes": "세 평면이 만나는 점",
|
||||
"cmd.construct_point_two_edges": "두 모서리가 만나는 점",
|
||||
"cmd.construct_point_vertex": "꼭짓점의 점",
|
||||
"cmd.contact_check": "접촉 확인",
|
||||
"cmd.contact_create": "새 접촉 집합",
|
||||
"cmd.contact_disable_all": "접촉 비활성화",
|
||||
"cmd.contact_edit": "접촉 집합 편집",
|
||||
"cmd.contact_enable_all": "모든 접촉 활성화",
|
||||
"cmd.contact_enable_sets": "접촉 집합 활성화",
|
||||
"cmd.contact_list": "접촉 집합 목록",
|
||||
"cmd.diagnostics_crash_report": "충돌 보고서",
|
||||
"cmd.diagnostics_crash_reports": "충돌 보고서",
|
||||
"cmd.diagnostics_crash_reports_clear": "충돌 보고서 삭제",
|
||||
"cmd.diagnostics_crash_test": "충돌 테스트",
|
||||
"cmd.doc_recover": "설계 복구",
|
||||
"cmd.doc_recovery_discard": "복구된 설계 폐기",
|
||||
"cmd.doc_recovery_list": "복구 가능한 설계",
|
||||
"cmd.document_inspect": "설계 검사",
|
||||
"cmd.document_units": "설계 단위 변경",
|
||||
"cmd.edit_checkpoint": "체크포인트",
|
||||
"cmd.edit_restore_checkpoint": "체크포인트 복원",
|
||||
"cmd.engine_commands": "명령 목록",
|
||||
"cmd.explode_create": "분해 보기",
|
||||
"cmd.explode_delete": "분해 보기 삭제",
|
||||
"cmd.explode_list": "분해 보기 목록",
|
||||
"cmd.explode_show": "분해 보기 표시",
|
||||
"cmd.expr_evaluate": "식 평가",
|
||||
"cmd.face_delete": "면 삭제",
|
||||
"cmd.file_export": "내보내기",
|
||||
"cmd.file_insert_mesh": "메쉬 삽입",
|
||||
"cmd.file_insert_step": "STEP 삽입",
|
||||
"cmd.file_save_mesh": "메쉬로 저장",
|
||||
"cmd.inspect_accessibility": "접근성 분석",
|
||||
"cmd.inspect_center_of_mass": "무게중심",
|
||||
"cmd.inspect_curvature_comb": "곡률빗 분석",
|
||||
"cmd.inspect_curvature_map": "곡률맵 분석",
|
||||
"cmd.inspect_draft": "블록각 분석",
|
||||
"cmd.inspect_environment_map": "환경 맵 분석",
|
||||
"cmd.inspect_interference": "간섭",
|
||||
"cmd.inspect_isocurve": "등가곡선 분석",
|
||||
"cmd.inspect_minimum_radius": "최소 반경 분석",
|
||||
"cmd.inspect_zebra": "얼룩말줄무늬 분석",
|
||||
"cmd.joint_as_built": "조립 상태 관절",
|
||||
"cmd.joint_create": "관절",
|
||||
"cmd.joint_delete": "관절 삭제",
|
||||
"cmd.joint_drive": "관절 구동",
|
||||
"cmd.joint_edit": "관절 편집",
|
||||
"cmd.joint_limits": "관절 제한",
|
||||
"cmd.joint_list": "관절 목록",
|
||||
"cmd.joint_motion_link": "모션 링크",
|
||||
"cmd.joint_origin": "관절 원점",
|
||||
"cmd.joint_rigid_group": "강체 그룹",
|
||||
"cmd.joint_solve": "관절 해석",
|
||||
"cmd.material_assign": "물리 재질",
|
||||
"cmd.material_create": "새 물리 재질",
|
||||
"cmd.material_delete": "물리 재질 삭제",
|
||||
"cmd.material_edit": "물리 재질 편집",
|
||||
"cmd.material_list": "재질 목록",
|
||||
"cmd.mesh_convert": "메쉬 변환",
|
||||
"cmd.model_edges": "모서리 목록",
|
||||
"cmd.model_faces": "면 목록",
|
||||
"cmd.model_threads": "나사산 목록",
|
||||
"cmd.motion_delete": "운동 연구 삭제",
|
||||
"cmd.motion_export": "운동 연구 내보내기",
|
||||
"cmd.motion_list": "운동 연구 목록",
|
||||
"cmd.motion_play": "운동 연구 재생",
|
||||
"cmd.motion_study": "운동 연구",
|
||||
"cmd.occurrence_copy": "붙여넣기(인스턴스)",
|
||||
"cmd.occurrence_ground": "고정",
|
||||
"cmd.occurrence_move": "인스턴스 이동",
|
||||
"cmd.occurrence_properties": "인스턴스 물리 속성",
|
||||
"cmd.parameters_add": "사용자 매개 변수 추가",
|
||||
"cmd.parameters_change": "매개 변수 변경",
|
||||
"cmd.parameters_comment": "매개 변수에 설명 추가",
|
||||
"cmd.parameters_complete": "식 완성",
|
||||
"cmd.parameters_delete": "매개 변수 삭제",
|
||||
"cmd.parameters_export": "매개 변수 내보내기",
|
||||
"cmd.parameters_favorite": "즐겨찾는 매개 변수",
|
||||
"cmd.parameters_graph": "매개 변수 그래프",
|
||||
"cmd.parameters_import": "매개 변수 가져오기",
|
||||
"cmd.parameters_list": "매개 변수 목록",
|
||||
"cmd.parameters_rename": "매개 변수 이름 변경",
|
||||
"cmd.parameters_users": "매개 변수 사용처",
|
||||
"cmd.parts_fastener": "체결 부품 삽입",
|
||||
"cmd.parts_insert": "부품 삽입",
|
||||
"cmd.parts_library": "표준 부품",
|
||||
"cmd.parts_resize": "부품 크기 변경",
|
||||
"cmd.plastic_assign_rule": "플라스틱 규칙 지정",
|
||||
"cmd.plastic_boss": "보스",
|
||||
"cmd.plastic_lip": "리프",
|
||||
"cmd.plastic_manage_rules": "플라스틱 규칙 관리",
|
||||
"cmd.plastic_rest": "레스트",
|
||||
"cmd.plastic_rules": "플라스틱 규칙 목록",
|
||||
"cmd.plastic_snap_fit": "스냅핏",
|
||||
"cmd.select_clear": "선택 지우기",
|
||||
"cmd.sheet_convert": "판금으로 변환",
|
||||
"cmd.sheet_export_bend_table": "벤딩 표 내보내기",
|
||||
"cmd.sheet_export_dxf": "전개도를 DXF로 내보내기",
|
||||
"cmd.sheet_flange": "플랜지",
|
||||
"cmd.sheet_flat_pattern": "전개도 만들기",
|
||||
"cmd.sheet_fold": "접기",
|
||||
"cmd.sheet_hem": "헴",
|
||||
"cmd.sheet_manage_rules": "판금 규칙",
|
||||
"cmd.sheet_refold": "다시 접기",
|
||||
"cmd.sheet_rules": "판금 규칙 목록",
|
||||
"cmd.sheet_unfold": "펼치기",
|
||||
"cmd.sketch_arc_center_point": "중심점 호",
|
||||
"cmd.sketch_arc_tangent": "접선 호",
|
||||
"cmd.sketch_arc_three_point": "3점 호",
|
||||
"cmd.sketch_auto_constrain": "자동 구속",
|
||||
"cmd.sketch_auto_constrain_from_datum": "기준으로 자동 구속",
|
||||
"cmd.sketch_auto_project": "참조에 모서리 자동 투영",
|
||||
"cmd.sketch_blend_curve": "블렌드 곡선",
|
||||
"cmd.sketch_chain": "연속",
|
||||
"cmd.sketch_chamfer_distance_angle": "거리와 각도 모따기",
|
||||
"cmd.sketch_chamfer_equal_distance": "등거리 모따기",
|
||||
"cmd.sketch_chamfer_two_distance": "두 거리 모따기",
|
||||
"cmd.sketch_circle_center": "중심 지름 원",
|
||||
"cmd.sketch_circle_three_point": "3점 원",
|
||||
"cmd.sketch_circle_three_tangent": "3접선 원",
|
||||
"cmd.sketch_circle_two_point": "2점 원",
|
||||
"cmd.sketch_circle_two_tangent": "2접선 원",
|
||||
"cmd.sketch_constrainer": "스케치 구속기",
|
||||
"cmd.sketch_constraint_fix": "고정/고정 해제",
|
||||
"cmd.sketch_constraint_midpoint": "중간점",
|
||||
"cmd.sketch_construction": "일반/구성선",
|
||||
"cmd.sketch_delete": "스케치 개체 삭제",
|
||||
"cmd.sketch_dimension_text": "치수 텍스트 이동",
|
||||
"cmd.sketch_drag": "스케치 기하 드래그",
|
||||
"cmd.sketch_drag_end": "스케치 드래그 종료",
|
||||
"cmd.sketch_draw_dimensioned": "치수가 있는 스케치 개체 그리기",
|
||||
"cmd.sketch_edit": "스케치 편집",
|
||||
"cmd.sketch_edit_text": "텍스트 편집",
|
||||
"cmd.sketch_export_dxf": "DXF로 저장",
|
||||
"cmd.sketch_finish_auto_constrain": "자동 구속으로 완료",
|
||||
"cmd.sketch_fit_curves_to_section": "메시 단면에 곡선 맞추기",
|
||||
"cmd.sketch_glyphs": "구속 기호",
|
||||
"cmd.sketch_include_3d": "3D 기하 포함",
|
||||
"cmd.sketch_insert_dxf": "DXF 삽입",
|
||||
"cmd.sketch_insert_svg": "SVG 삽입",
|
||||
"cmd.sketch_inspect": "스케치 검사",
|
||||
"cmd.sketch_intersection_curve": "교차 곡선",
|
||||
"cmd.sketch_isoparametric_curve": "등매개 곡선",
|
||||
"cmd.sketch_line3d": "3D 선",
|
||||
"cmd.sketch_line_midpoint": "중점 선",
|
||||
"cmd.sketch_move": "스케치 개체 이동/복사",
|
||||
"cmd.sketch_move3d": "3D 점 이동",
|
||||
"cmd.sketch_move_point": "스케치 점 드래그",
|
||||
"cmd.sketch_offset_break": "오프셋 연결 끊기",
|
||||
"cmd.sketch_options": "스케치 팔레트 옵션",
|
||||
"cmd.sketch_point3d": "3D 점",
|
||||
"cmd.sketch_polygon_circumscribed": "외접 다각형",
|
||||
"cmd.sketch_polygon_edge": "변 다각형",
|
||||
"cmd.sketch_polygon_inscribed": "내접 다각형",
|
||||
"cmd.sketch_project_to_surface": "곡면으로 투영",
|
||||
"cmd.sketch_rectangle_center": "중심 사각형",
|
||||
"cmd.sketch_rectangle_three_point": "3점 사각형",
|
||||
"cmd.sketch_rectangle_two_point": "2점 사각형",
|
||||
"cmd.sketch_redefine": "스케치 평면 재정의",
|
||||
"cmd.sketch_slot_arc_center": "중심점 호 슬롯",
|
||||
"cmd.sketch_slot_arc_three_point": "3점 호 슬롯",
|
||||
"cmd.sketch_slot_center_point": "중심점 슬롯",
|
||||
"cmd.sketch_slot_center_to_center": "중심에서 중심 슬롯",
|
||||
"cmd.sketch_slot_overall": "전장 슬롯",
|
||||
"cmd.sketch_snap": "스냅 추론",
|
||||
"cmd.sketch_solve": "스케치 해석",
|
||||
"cmd.sketch_spline3d": "3D 스플라인",
|
||||
"cmd.sketch_spline_control_point": "제어점 스플라인",
|
||||
"cmd.sketch_spline_control_point_5": "제어점 스플라인(5차)",
|
||||
"cmd.sketch_spline_fit_point": "맞춤점 스플라인",
|
||||
"cmd.sketch_spun_profile": "회전 프로파일",
|
||||
"cmd.sketch_toggle_driven": "종속 치수 전환",
|
||||
"cmd.solid_align": "정렬",
|
||||
"cmd.solid_boundary_fill": "경계 채우기",
|
||||
"cmd.solid_bounding_solid": "둘러싸는 솔리드",
|
||||
"cmd.solid_emboss": "엠보스",
|
||||
"cmd.solid_move": "이동/복사",
|
||||
"cmd.solid_offset_face": "면 오프셋",
|
||||
"cmd.solid_press_pull": "밀기/당기기",
|
||||
"cmd.solid_remove": "제거",
|
||||
"cmd.solid_replace_face": "면 교체",
|
||||
"cmd.solid_rib": "리브",
|
||||
"cmd.solid_split_face": "면 분할",
|
||||
"cmd.solid_web": "웹",
|
||||
"cmd.surface_patch": "패치",
|
||||
"cmd.surface_stitch": "재봉",
|
||||
"cmd.surface_thicken": "두께 주기",
|
||||
"cmd.surface_extrude": "서피스 돌출",
|
||||
"cmd.surface_revolve": "서피스 회전",
|
||||
"cmd.surface_sweep": "서피스 스윕",
|
||||
"cmd.surface_loft": "서피스 로프트",
|
||||
"cmd.surface_ruled": "룰드 서피스",
|
||||
"cmd.surface_boundary": "바운더리 서피스",
|
||||
"cmd.surface_offset": "오프셋 서피스",
|
||||
"cmd.surface_blend": "블렌드 서피스",
|
||||
"cmd.surface_fillet": "서피스 필렛",
|
||||
"cmd.surface_untrim": "트림 해제",
|
||||
"cmd.surface_unstitch": "스티치 해제",
|
||||
"cmd.surface_reverse_normal": "법선 반전",
|
||||
"cmd.inspect_continuity": "연속성 분석",
|
||||
"cmd.timeline_compute_all": "모두 다시 계산",
|
||||
"cmd.timeline_copy": "특성 복사",
|
||||
"cmd.timeline_dependents": "특성 종속 항목",
|
||||
"cmd.timeline_inspect": "특성 검사",
|
||||
"cmd.timeline_paste": "특성 붙여넣기",
|
||||
"cmd.timeline_redefine": "특성 편집",
|
||||
"cmd.timeline_rename": "이름 변경",
|
||||
"cmd.timeline_reorder": "특성 순서 변경",
|
||||
"cmd.timeline_roll_to": "이력 마커 이동",
|
||||
"cmd.timeline_rollback": "타임라인 마커 이동",
|
||||
"cmd.timeline_suppress": "특성 비활성화",
|
||||
"cmd.view_apply": "이름 있는 보기 복원",
|
||||
"cmd.view_delete": "이름 있는 보기 삭제",
|
||||
"cmd.view_list": "이름 있는 보기 목록",
|
||||
"cmd.view_rename": "이름 있는 보기 이름 변경",
|
||||
"cmd.view_save": "새 이름 있는 보기",
|
||||
"ui.export_ellipsis_keymap": "내보내기…",
|
||||
"ui.heading_configure": "구성",
|
||||
"ui.heading_constraints": "구속 조건",
|
||||
"ui.heading_utilities": "유틸리티",
|
||||
"BROWSER": "브라우저",
|
||||
"Drive Joint": "조인트 구동",
|
||||
"Edit Profile Sketch": "윤곽 스케치 편집",
|
||||
"Find in Browser": "브라우저에서 찾기",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "버그 신고 — 스크린샷을 검토하고 GitHub 초안 열기",
|
||||
"Reset Toolbar": "도구 모음 재설정",
|
||||
"Select bodies (a face picks its body)": "바디 선택(면을 클릭하면 해당 바디 선택)",
|
||||
"Selection filter": "선택 필터",
|
||||
"Revert Position": "위치 복원",
|
||||
"Construction": "구성선",
|
||||
"Faces": "면",
|
||||
"Edges": "모서리",
|
||||
"Vertices": "꼭짓점",
|
||||
"Sketch geometry and profiles": "스케치 기하 및 프로파일",
|
||||
"Sketch curves": "스케치 곡선",
|
||||
"Sketch points": "스케치 점",
|
||||
"Sketch dimensions": "스케치 치수",
|
||||
"Sketch constraints": "스케치 구속 조건",
|
||||
"Flat": "플랫",
|
||||
"cmd.render_backends": "렌더링 백엔드",
|
||||
"cmd.view_navigate": "보기 탐색",
|
||||
"timeline.group": "새 특성 폴더",
|
||||
"timeline.ungroup": "특성 폴더 제거",
|
||||
"timeline.rename_group": "특성 폴더 이름 변경",
|
||||
"timeline.move_to_group": "특성을 폴더로 이동",
|
||||
"timeline.move_many": "타임라인 항목 이동",
|
||||
"timeline.reorder_many": "특성 순서 변경",
|
||||
"ui.show_construction_geometries": "구성 형상",
|
||||
"ui.total_angle": "전진 각도",
|
||||
"sketch_text.builtin_font": "{family}(내장)",
|
||||
"sketch_text.font_search": "글꼴 검색",
|
||||
"sketch_text.loading_fonts": "설치된 글꼴을 찾는 중…",
|
||||
|
||||
@@ -0,0 +1,954 @@
|
||||
{
|
||||
"browser.body_name": "实体{number}",
|
||||
"browser.component_name": "组件{number}",
|
||||
"browser.parameters": "参数 ({count})",
|
||||
"browser.precision": "精度: {precision}",
|
||||
"browser.sketch_name": "草图{number}",
|
||||
"browser.units": "单位: {units}",
|
||||
"cmd.appearance_assign": "外观",
|
||||
"cmd.appearance_library": "外观库",
|
||||
"cmd.appearance_list": "列出外观",
|
||||
"cmd.body_rename": "重命名实体",
|
||||
"cmd.browser_group": "新建组",
|
||||
"cmd.browser_move": "移动浏览器项目",
|
||||
"cmd.browser_order": "排列浏览器项目",
|
||||
"cmd.browser_rename_group": "重命名组",
|
||||
"cmd.browser_ungroup": "取消编组",
|
||||
"cmd.browser_visibility": "显示/隐藏",
|
||||
"cmd.canvas_calibrate": "校准画布",
|
||||
"cmd.canvas_decal": "贴花",
|
||||
"cmd.canvas_delete": "删除画布",
|
||||
"cmd.canvas_edit": "编辑画布",
|
||||
"cmd.canvas_insert": "画布",
|
||||
"cmd.component_activate": "激活组件",
|
||||
"cmd.component_break_link": "断开链接",
|
||||
"cmd.component_capture_position": "捕获位置",
|
||||
"cmd.component_create": "新建组件",
|
||||
"cmd.component_delete": "删除组件",
|
||||
"cmd.component_from_bodies": "由实体创建组件",
|
||||
"cmd.component_insert_design": "插入设计",
|
||||
"cmd.component_links": "列出链接",
|
||||
"cmd.component_list": "列出组件",
|
||||
"cmd.component_move_bodies": "将实体移至组件",
|
||||
"cmd.component_move_sketches": "将草图移至组件",
|
||||
"cmd.component_paste_new": "粘贴为新对象",
|
||||
"cmd.component_properties": "组件物理属性",
|
||||
"cmd.component_rename": "重命名组件",
|
||||
"cmd.component_revert_position": "恢复位置",
|
||||
"cmd.component_update_links": "更新链接",
|
||||
"cmd.config_activate": "激活配置",
|
||||
"cmd.config_column": "配置列",
|
||||
"cmd.config_configure": "配置",
|
||||
"cmd.config_preview": "预览配置",
|
||||
"cmd.config_row": "配置行",
|
||||
"cmd.config_rule": "配置规则",
|
||||
"cmd.config_rules": "配置规则",
|
||||
"cmd.config_table": "显示配置表",
|
||||
"cmd.construct_axis_edge": "过边的轴",
|
||||
"cmd.construct_axis_normal_to_face": "垂直于面的轴",
|
||||
"cmd.construct_axis_through_cylinder": "过圆柱/圆锥/圆环的轴",
|
||||
"cmd.construct_axis_two_planes": "过两个平面的轴",
|
||||
"cmd.construct_axis_two_points": "过两点的轴",
|
||||
"cmd.construct_plane_along_path": "沿路径的平面",
|
||||
"cmd.construct_plane_midplane": "中平面",
|
||||
"cmd.construct_plane_perpendicular": "垂直平面",
|
||||
"cmd.construct_plane_tangent": "相切平面",
|
||||
"cmd.construct_plane_three_points": "过三点的平面",
|
||||
"cmd.construct_plane_two_edges": "过两条边的平面",
|
||||
"cmd.construct_point_along_path": "沿路径的点",
|
||||
"cmd.construct_point_center": "圆/球/圆环中心的点",
|
||||
"cmd.construct_point_edge_plane": "边与平面交点",
|
||||
"cmd.construct_point_three_planes": "过三个平面的点",
|
||||
"cmd.construct_point_two_edges": "过两条边的点",
|
||||
"cmd.construct_point_vertex": "顶点处的点",
|
||||
"cmd.contact_check": "检查接触",
|
||||
"cmd.contact_create": "新建接触集",
|
||||
"cmd.contact_disable_all": "禁用接触",
|
||||
"cmd.contact_edit": "编辑接触集",
|
||||
"cmd.contact_enable_all": "启用全部接触",
|
||||
"cmd.contact_enable_sets": "启用接触集",
|
||||
"cmd.contact_list": "列出接触集",
|
||||
"cmd.diagnostics_crash_report": "崩溃报告",
|
||||
"cmd.diagnostics_crash_reports": "崩溃报告",
|
||||
"cmd.diagnostics_crash_reports_clear": "删除崩溃报告",
|
||||
"cmd.diagnostics_crash_test": "崩溃测试",
|
||||
"cmd.doc_recover": "恢复设计",
|
||||
"cmd.doc_recovery_discard": "丢弃已恢复的设计",
|
||||
"cmd.doc_recovery_list": "可恢复的设计",
|
||||
"cmd.document_inspect": "检查设计",
|
||||
"cmd.document_units": "更改设计单位",
|
||||
"cmd.edit_checkpoint": "检查点",
|
||||
"cmd.edit_restore_checkpoint": "恢复检查点",
|
||||
"cmd.engine_commands": "列出命令",
|
||||
"cmd.explode_create": "爆炸图",
|
||||
"cmd.explode_delete": "删除爆炸图",
|
||||
"cmd.explode_list": "列出爆炸图",
|
||||
"cmd.explode_show": "显示爆炸图",
|
||||
"cmd.expr_evaluate": "计算表达式",
|
||||
"cmd.face_delete": "删除面",
|
||||
"cmd.file_export": "导出",
|
||||
"cmd.file_insert_mesh": "插入网格",
|
||||
"cmd.file_insert_step": "插入 STEP",
|
||||
"cmd.file_save_mesh": "另存为网格",
|
||||
"cmd.inspect_accessibility": "可达性分析",
|
||||
"cmd.inspect_center_of_mass": "质心",
|
||||
"cmd.inspect_curvature_comb": "曲率梳分析",
|
||||
"cmd.inspect_curvature_map": "曲率图分析",
|
||||
"cmd.inspect_draft": "拔模分析",
|
||||
"cmd.inspect_environment_map": "环境贴图分析",
|
||||
"cmd.inspect_interference": "干涉",
|
||||
"cmd.inspect_isocurve": "等参线分析",
|
||||
"cmd.inspect_minimum_radius": "最小半径分析",
|
||||
"cmd.inspect_zebra": "斑马纹分析",
|
||||
"cmd.joint_as_built": "现状接头",
|
||||
"cmd.joint_create": "接头",
|
||||
"cmd.joint_delete": "删除接头",
|
||||
"cmd.joint_drive": "驱动接头",
|
||||
"cmd.joint_edit": "编辑接头",
|
||||
"cmd.joint_limits": "接头限制",
|
||||
"cmd.joint_list": "列出接头",
|
||||
"cmd.joint_motion_link": "运动关联",
|
||||
"cmd.joint_origin": "接头原点",
|
||||
"cmd.joint_rigid_group": "刚性组",
|
||||
"cmd.joint_solve": "解算接头",
|
||||
"cmd.material_assign": "物理材质",
|
||||
"cmd.material_create": "新建物理材质",
|
||||
"cmd.material_delete": "删除物理材质",
|
||||
"cmd.material_edit": "编辑物理材质",
|
||||
"cmd.material_list": "列出材质",
|
||||
"cmd.mesh_convert": "转换网格",
|
||||
"cmd.model_edges": "列出边",
|
||||
"cmd.model_faces": "列出面",
|
||||
"cmd.model_threads": "列出螺纹",
|
||||
"cmd.motion_delete": "删除运动研究",
|
||||
"cmd.motion_export": "导出运动研究",
|
||||
"cmd.motion_list": "列出运动研究",
|
||||
"cmd.motion_play": "播放运动研究",
|
||||
"cmd.motion_study": "运动研究",
|
||||
"cmd.occurrence_copy": "粘贴(实例)",
|
||||
"cmd.occurrence_ground": "固定",
|
||||
"cmd.occurrence_move": "移动实例",
|
||||
"cmd.occurrence_properties": "实例物理属性",
|
||||
"cmd.parameters_add": "添加用户参数",
|
||||
"cmd.parameters_change": "更改参数",
|
||||
"cmd.parameters_comment": "注释参数",
|
||||
"cmd.parameters_complete": "补全表达式",
|
||||
"cmd.parameters_delete": "删除参数",
|
||||
"cmd.parameters_export": "导出参数",
|
||||
"cmd.parameters_favorite": "收藏参数",
|
||||
"cmd.parameters_graph": "参数关系图",
|
||||
"cmd.parameters_import": "导入参数",
|
||||
"cmd.parameters_list": "列出参数",
|
||||
"cmd.parameters_rename": "重命名参数",
|
||||
"cmd.parameters_users": "参数使用者",
|
||||
"cmd.parts_fastener": "插入紧固件",
|
||||
"cmd.parts_insert": "插入零件",
|
||||
"cmd.parts_library": "标准零件",
|
||||
"cmd.parts_resize": "更改零件尺寸",
|
||||
"cmd.plastic_assign_rule": "指定塑料规则",
|
||||
"cmd.plastic_boss": "凸台",
|
||||
"cmd.plastic_lip": "凸缘",
|
||||
"cmd.plastic_manage_rules": "管理塑料规则",
|
||||
"cmd.plastic_rest": "垫块",
|
||||
"cmd.plastic_rules": "列出塑料规则",
|
||||
"cmd.plastic_snap_fit": "卡扣",
|
||||
"cmd.select_clear": "清除选择",
|
||||
"cmd.sheet_convert": "转换为钣金",
|
||||
"cmd.sheet_export_bend_table": "导出折弯表",
|
||||
"cmd.sheet_export_dxf": "将展开图导出为 DXF",
|
||||
"cmd.sheet_flange": "法兰",
|
||||
"cmd.sheet_flat_pattern": "创建展开图",
|
||||
"cmd.sheet_fold": "折叠",
|
||||
"cmd.sheet_hem": "卷边",
|
||||
"cmd.sheet_manage_rules": "钣金规则",
|
||||
"cmd.sheet_refold": "重新折弯",
|
||||
"cmd.sheet_rules": "列出钣金规则",
|
||||
"cmd.sheet_unfold": "展开",
|
||||
"cmd.sketch_arc_center_point": "中心点圆弧",
|
||||
"cmd.sketch_arc_tangent": "相切圆弧",
|
||||
"cmd.sketch_arc_three_point": "三点圆弧",
|
||||
"cmd.sketch_auto_constrain": "自动约束",
|
||||
"cmd.sketch_auto_constrain_from_datum": "基于基准自动约束",
|
||||
"cmd.sketch_auto_project": "自动投影边到参考",
|
||||
"cmd.sketch_blend_curve": "混合曲线",
|
||||
"cmd.sketch_chain": "连续链",
|
||||
"cmd.sketch_chamfer_distance_angle": "距离和角度倒角",
|
||||
"cmd.sketch_chamfer_equal_distance": "等距倒角",
|
||||
"cmd.sketch_chamfer_two_distance": "双距离倒角",
|
||||
"cmd.sketch_circle_center": "圆心直径圆",
|
||||
"cmd.sketch_circle_three_point": "三点圆",
|
||||
"cmd.sketch_circle_three_tangent": "三切线圆",
|
||||
"cmd.sketch_circle_two_point": "两点圆",
|
||||
"cmd.sketch_circle_two_tangent": "两切线圆",
|
||||
"cmd.sketch_constrainer": "草图约束器",
|
||||
"cmd.sketch_constraint_fix": "固定/解除固定",
|
||||
"cmd.sketch_constraint_midpoint": "中点",
|
||||
"cmd.sketch_construction": "普通/构造",
|
||||
"cmd.sketch_delete": "删除草图图元",
|
||||
"cmd.sketch_dimension_text": "移动尺寸文字",
|
||||
"cmd.sketch_drag": "拖动草图几何",
|
||||
"cmd.sketch_drag_end": "结束草图拖动",
|
||||
"cmd.sketch_draw_dimensioned": "绘制带尺寸的草图图元",
|
||||
"cmd.sketch_edit": "编辑草图",
|
||||
"cmd.sketch_edit_text": "编辑文字",
|
||||
"cmd.sketch_export_dxf": "另存为 DXF",
|
||||
"cmd.sketch_finish_auto_constrain": "使用自动约束完成",
|
||||
"cmd.sketch_fit_curves_to_section": "将曲线拟合到网格截面",
|
||||
"cmd.sketch_glyphs": "约束符号",
|
||||
"cmd.sketch_include_3d": "包含三维几何",
|
||||
"cmd.sketch_insert_dxf": "插入 DXF",
|
||||
"cmd.sketch_insert_svg": "插入 SVG",
|
||||
"cmd.sketch_inspect": "检查草图",
|
||||
"cmd.sketch_intersection_curve": "相交曲线",
|
||||
"cmd.sketch_isoparametric_curve": "等参数曲线",
|
||||
"cmd.sketch_line3d": "三维直线",
|
||||
"cmd.sketch_line_midpoint": "中点线",
|
||||
"cmd.sketch_move": "移动/复制草图图元",
|
||||
"cmd.sketch_move3d": "移动三维点",
|
||||
"cmd.sketch_move_point": "拖动草图点",
|
||||
"cmd.sketch_offset_break": "断开偏移关联",
|
||||
"cmd.sketch_options": "草图选项板选项",
|
||||
"cmd.sketch_point3d": "三维点",
|
||||
"cmd.sketch_polygon_circumscribed": "外切多边形",
|
||||
"cmd.sketch_polygon_edge": "边长多边形",
|
||||
"cmd.sketch_polygon_inscribed": "内接多边形",
|
||||
"cmd.sketch_project_to_surface": "投影到曲面",
|
||||
"cmd.sketch_rectangle_center": "中心矩形",
|
||||
"cmd.sketch_rectangle_three_point": "三点矩形",
|
||||
"cmd.sketch_rectangle_two_point": "两点矩形",
|
||||
"cmd.sketch_redefine": "重定义草图平面",
|
||||
"cmd.sketch_slot_arc_center": "中心点圆弧槽",
|
||||
"cmd.sketch_slot_arc_three_point": "三点圆弧槽",
|
||||
"cmd.sketch_slot_center_point": "中心点槽",
|
||||
"cmd.sketch_slot_center_to_center": "中心距槽",
|
||||
"cmd.sketch_slot_overall": "全长槽",
|
||||
"cmd.sketch_snap": "捕捉推断",
|
||||
"cmd.sketch_solve": "解算草图",
|
||||
"cmd.sketch_spline3d": "三维样条曲线",
|
||||
"cmd.sketch_spline_control_point": "控制点样条曲线",
|
||||
"cmd.sketch_spline_control_point_5": "控制点样条曲线(5 次)",
|
||||
"cmd.sketch_spline_fit_point": "拟合点样条曲线",
|
||||
"cmd.sketch_spun_profile": "旋转轮廓",
|
||||
"cmd.sketch_toggle_driven": "切换被驱动尺寸",
|
||||
"cmd.solid_align": "对齐",
|
||||
"cmd.solid_boundary_fill": "边界填充",
|
||||
"cmd.solid_bounding_solid": "包围实体",
|
||||
"cmd.solid_emboss": "压印",
|
||||
"cmd.solid_move": "移动/复制",
|
||||
"cmd.solid_offset_face": "偏移面",
|
||||
"cmd.solid_press_pull": "按压拖动",
|
||||
"cmd.solid_remove": "移除",
|
||||
"cmd.solid_replace_face": "替换面",
|
||||
"cmd.solid_rib": "筋板",
|
||||
"cmd.solid_split_face": "拆分面",
|
||||
"cmd.solid_web": "网格筋",
|
||||
"cmd.surface_patch": "补面",
|
||||
"cmd.surface_stitch": "缝合",
|
||||
"cmd.surface_thicken": "加厚",
|
||||
"cmd.surface_extrude": "曲面拉伸",
|
||||
"cmd.surface_revolve": "曲面旋转",
|
||||
"cmd.surface_sweep": "曲面扫掠",
|
||||
"cmd.surface_loft": "曲面放样",
|
||||
"cmd.surface_ruled": "直纹曲面",
|
||||
"cmd.surface_boundary": "边界曲面",
|
||||
"cmd.surface_offset": "偏移曲面",
|
||||
"cmd.surface_blend": "混合曲面",
|
||||
"cmd.surface_fillet": "曲面圆角",
|
||||
"cmd.surface_untrim": "取消修剪",
|
||||
"cmd.surface_unstitch": "取消缝合",
|
||||
"cmd.surface_reverse_normal": "反转法向",
|
||||
"cmd.inspect_continuity": "连续性分析",
|
||||
"cmd.timeline_compute_all": "全部重新計算",
|
||||
"cmd.timeline_copy": "复制特征",
|
||||
"cmd.timeline_dependents": "特征依存项",
|
||||
"cmd.timeline_inspect": "检查特征",
|
||||
"cmd.timeline_paste": "粘贴特征",
|
||||
"cmd.timeline_redefine": "编辑特征",
|
||||
"cmd.timeline_rename": "重命名",
|
||||
"cmd.timeline_reorder": "重排特征",
|
||||
"cmd.timeline_roll_to": "滚动历史标记",
|
||||
"cmd.timeline_rollback": "移动时间线标记",
|
||||
"cmd.timeline_suppress": "抑制特征",
|
||||
"cmd.view_apply": "还原命名视图",
|
||||
"cmd.view_delete": "删除命名视图",
|
||||
"cmd.view_list": "列出命名视图",
|
||||
"cmd.view_rename": "重命名命名视图",
|
||||
"cmd.view_save": "新建命名视图",
|
||||
"dialog.custom_axis": "↖ 自定义轴",
|
||||
"dialog.edit_title": "编辑 {title}",
|
||||
"dialog.select": "↖ 选择",
|
||||
"dialog.selected_count": "↖ 已选择 {count} 个",
|
||||
"document.save_changes": "保存对 {name} 的更改?",
|
||||
"document.unsaved_changes": "{name} 有未保存的更改。如果不保存,将丢失这些更改。",
|
||||
"settings.msaa_applies_on_restart": "抗锯齿将在下次启动 SolidCraft 时生效",
|
||||
"settings.msaa_reset": "{setting}: {requested}× 不受此图形设备支持;已重置为 {reset}× (支持: {supported})。",
|
||||
"settings.msaa_unsupported": "此图形设备不支持",
|
||||
"settings.reset_intro": "这些设置在此计算机上无法正常工作,会导致 SolidCraft 无法启动,因此已更改:",
|
||||
"settings.reset_title": "设置已更改",
|
||||
"sketch.degrees_of_freedom": "{count} 个自由度",
|
||||
"sketch.draw_curves": "绘制为 {linetype}",
|
||||
"sketch.make_selection": "将选择转换为 {linetype}",
|
||||
"ui.1_degree_of_freedom": "1 个自由度",
|
||||
"ui.3d_mouse_speed": "3D 鼠标速度",
|
||||
"ui.3d_mouse_windows": "3D 鼠标(Windows)",
|
||||
"ui.3d_sketch": "3D 草图",
|
||||
"ui.about": "关于",
|
||||
"ui.activate": "激活",
|
||||
"ui.active_component": "活动组件",
|
||||
"ui.agent_cursor": "代理光标",
|
||||
"ui.agent_cursor_speed": "代理光标速度",
|
||||
"ui.all": "全部",
|
||||
"ui.angle": "角度",
|
||||
"ui.angle_decimals": "角度小数位",
|
||||
"ui.animate_view_changes": "视图变化动画",
|
||||
"ui.animation": "动画",
|
||||
"ui.animation_speed": "动画速度",
|
||||
"ui.anti_aliasing": "抗锯齿",
|
||||
"ui.apply": "应用",
|
||||
"ui.arc": "圆弧",
|
||||
"ui.assemble": "装配",
|
||||
"ui.autosave_every": "自动保存间隔",
|
||||
"ui.axes": "轴",
|
||||
"ui.axis": "轴",
|
||||
"ui.base_point": "基点",
|
||||
"ui.bodies": "实体",
|
||||
"ui.box": "长方体",
|
||||
"ui.break": "打断",
|
||||
"ui.cancel": "取消",
|
||||
"ui.canvases": "画布",
|
||||
"ui.center": "中心",
|
||||
"ui.centerline": "中心线",
|
||||
"ui.chain": "链",
|
||||
"ui.chamfer": "倒角",
|
||||
"ui.chamfer_type": "倒角类型",
|
||||
"ui.change_workspace": "更改工作区",
|
||||
"ui.chord_length": "弦长",
|
||||
"ui.circle": "圆",
|
||||
"ui.circular_pattern": "圆形阵列",
|
||||
"ui.clear": "清除",
|
||||
"ui.clear_recent": "清除最近",
|
||||
"ui.clear_the_selection": "清除选择",
|
||||
"ui.click_selects_whole_bodies": "单击选择整个实体",
|
||||
"ui.click_then_press_key": "单击,然后按新按键",
|
||||
"ui.close": "关闭",
|
||||
"ui.coil": "螺旋",
|
||||
"ui.coincident": "重合",
|
||||
"ui.collapse": "折叠",
|
||||
"ui.collinear": "共线",
|
||||
"ui.combine": "组合",
|
||||
"ui.coming_later": "即将推出",
|
||||
"ui.concentric": "同心",
|
||||
"ui.configurations": "配置",
|
||||
"ui.conic_curve": "圆锥曲线",
|
||||
"ui.constant": "恒定",
|
||||
"ui.constraints_conflict": "约束冲突",
|
||||
"ui.construct": "构造",
|
||||
"ui.construction": "构造",
|
||||
"ui.context_menu": "上下文菜单",
|
||||
"ui.copy": "复制",
|
||||
"ui.corners": "角点",
|
||||
"ui.corner": "角",
|
||||
"ui.counterbore": "沉头孔",
|
||||
"ui.countersink": "锥形沉孔",
|
||||
"ui.create": "创建",
|
||||
"ui.create_copy": "创建副本",
|
||||
"ui.create_sketch": "创建草图",
|
||||
"ui.current": "当前",
|
||||
"ui.curvature": "曲率",
|
||||
"ui.curves": "曲线",
|
||||
"ui.cut": "剪切",
|
||||
"ui.cylinder": "圆柱体",
|
||||
"ui.dark": "深色",
|
||||
"ui.default_modelling_orientation": "默认建模方向",
|
||||
"ui.degrees_of_freedom": "自由度",
|
||||
"ui.delete": "删除",
|
||||
"ui.depth": "深度",
|
||||
"ui.design": "设计",
|
||||
"ui.diameter": "直径",
|
||||
"ui.dimension": "尺寸",
|
||||
"ui.direction": "方向",
|
||||
"ui.distance": "距离",
|
||||
"ui.distance_1": "距离 1",
|
||||
"ui.distance_2": "距离 2",
|
||||
"ui.document_settings": "文档设置",
|
||||
"ui.don_t_save": "不保存",
|
||||
"ui.draft": "拔模",
|
||||
"ui.drawing": "绘图",
|
||||
"ui.drill_point": "钻尖",
|
||||
"ui.edit": "编辑",
|
||||
"ui.ellipse": "椭圆",
|
||||
"ui.end_radius": "结束半径",
|
||||
"ui.entities": "实体",
|
||||
"ui.equal": "相等",
|
||||
"ui.expand": "展开",
|
||||
"ui.export_ellipsis": "导出...",
|
||||
"ui.export_ellipsis_keymap": "导出…",
|
||||
"ui.export_keymap_tip": "将按键、S 框收藏和工具栏导出为文件",
|
||||
"ui.extend": "延伸",
|
||||
"ui.extent_type": "延伸类型",
|
||||
"ui.extrude": "拉伸",
|
||||
"ui.factor": "系数",
|
||||
"ui.feature_history": "特征历史",
|
||||
"ui.feature_options": "特征选项",
|
||||
"ui.features": "特征",
|
||||
"ui.file": "文件",
|
||||
"ui.fillet": "圆角",
|
||||
"ui.filter_commands_or_keys": "筛选命令或按键",
|
||||
"ui.finish_sketch": "完成草图",
|
||||
"ui.fix_unfix": "固定/取消固定",
|
||||
"ui.flat": "平面",
|
||||
"ui.flip": "翻转",
|
||||
"ui.front": "前视",
|
||||
"ui.fully_constrained": "完全约束",
|
||||
"ui.general": "常规",
|
||||
"ui.graphics": "图形",
|
||||
"ui.ground_shadow": "地面阴影",
|
||||
"ui.heading_add_ins": "加载项",
|
||||
"ui.heading_assemble": "装配",
|
||||
"ui.heading_box": "长方体",
|
||||
"ui.heading_chamfer": "倒角",
|
||||
"ui.heading_circular_pattern": "圆形阵列",
|
||||
"ui.heading_coil": "螺旋",
|
||||
"ui.heading_combine": "组合",
|
||||
"ui.heading_configure": "配置",
|
||||
"ui.heading_constraints": "约束",
|
||||
"ui.heading_construct": "构造",
|
||||
"ui.heading_create": "创建",
|
||||
"ui.heading_create_sketch": "创建草图",
|
||||
"ui.heading_cylinder": "圆柱体",
|
||||
"ui.heading_design": "设计",
|
||||
"ui.heading_draft": "拔模",
|
||||
"ui.heading_edit": "编辑",
|
||||
"ui.heading_extrude": "拉伸",
|
||||
"ui.heading_fillet": "圆角",
|
||||
"ui.heading_finish_sketch": "完成草图",
|
||||
"ui.heading_front": "前视",
|
||||
"ui.heading_hole": "孔",
|
||||
"ui.heading_home": "主页",
|
||||
"ui.heading_insert": "插入",
|
||||
"ui.heading_inspect": "检查",
|
||||
"ui.heading_loft": "放样",
|
||||
"ui.heading_make": "制作",
|
||||
"ui.heading_measure": "测量",
|
||||
"ui.heading_mesh": "网格",
|
||||
"ui.heading_mirror": "镜像",
|
||||
"ui.heading_modify": "修改",
|
||||
"ui.heading_move": "移动",
|
||||
"ui.heading_offset": "偏移",
|
||||
"ui.heading_move_copy": "移动/复制",
|
||||
"ui.heading_offset_plane": "偏移平面",
|
||||
"ui.heading_ok": "确定",
|
||||
"ui.heading_parameters": "参数",
|
||||
"ui.heading_pattern_on_path": "路径阵列",
|
||||
"ui.heading_pipe": "管道",
|
||||
"ui.heading_plane_at_angle": "角度平面",
|
||||
"ui.heading_plastic": "塑料",
|
||||
"ui.heading_prepare": "准备",
|
||||
"ui.heading_rectangular_pattern": "矩形阵列",
|
||||
"ui.heading_revolve": "旋转",
|
||||
"ui.heading_right": "右视",
|
||||
"ui.heading_scale": "缩放",
|
||||
"ui.heading_section_analysis": "截面分析",
|
||||
"ui.heading_select": "选择",
|
||||
"ui.heading_sheet_metal": "钣金",
|
||||
"ui.heading_shell": "抽壳",
|
||||
"ui.heading_sketch": "草图",
|
||||
"ui.heading_sketch_palette": "草图面板",
|
||||
"ui.heading_sketch_scale": "草图缩放",
|
||||
"ui.heading_solid": "实体",
|
||||
"ui.heading_sphere": "球体",
|
||||
"ui.heading_split_body": "分割实体",
|
||||
"ui.heading_surface": "曲面",
|
||||
"ui.heading_sweep": "扫掠",
|
||||
"ui.heading_thread": "螺纹",
|
||||
"ui.heading_top": "上视",
|
||||
"ui.heading_torus": "圆环体",
|
||||
"ui.heading_utilities": "工具",
|
||||
"ui.heading_utility": "实用工具",
|
||||
"ui.help": "帮助",
|
||||
"ui.hide": "隐藏",
|
||||
"ui.hole": "孔",
|
||||
"ui.hole_tap_type": "孔攻丝类型",
|
||||
"ui.hole_type": "孔类型",
|
||||
"ui.home": "主页",
|
||||
"ui.horizontal_vertical": "水平/垂直",
|
||||
"ui.import_ellipsis": "导入…",
|
||||
"ui.insert": "插入",
|
||||
"ui.inspect": "检查",
|
||||
"ui.intersect": "相交",
|
||||
"ui.join": "连接",
|
||||
"ui.joints": "关节",
|
||||
"ui.keyboard_shortcuts": "键盘快捷键",
|
||||
"ui.language": "语言",
|
||||
"ui.length_decimals": "长度小数位",
|
||||
"ui.light": "浅色",
|
||||
"ui.line": "直线",
|
||||
"ui.linetype": "线型",
|
||||
"ui.loft": "放样",
|
||||
"ui.look_at": "朝向",
|
||||
"ui.look_at_anim_desc": "轴视图、注视、主视图与适合窗口",
|
||||
"ui.manufacture": "制造",
|
||||
"ui.measure": "测量",
|
||||
"ui.midpoint": "中点",
|
||||
"ui.mirror": "镜像",
|
||||
"ui.mirror_line": "镜像线",
|
||||
"ui.modify": "修改",
|
||||
"ui.move": "移动",
|
||||
"ui.multiple": "多个",
|
||||
"ui.named_views": "命名视图",
|
||||
"ui.navigation": "导航",
|
||||
"ui.needs_design": "{command}: 请先打开一个设计",
|
||||
"ui.new_body": "新建实体",
|
||||
"ui.new_design": "新建设计",
|
||||
"ui.normal": "法向",
|
||||
"ui.object_type": "对象类型",
|
||||
"ui.objects": "对象",
|
||||
"ui.offer_to_stand_up_y_up_files": "提供站立 Y-up 文件",
|
||||
"ui.offset": "偏移",
|
||||
"ui.offset_plane": "偏移平面",
|
||||
"ui.one_side": "单侧",
|
||||
"ui.open": "打开",
|
||||
"ui.open_ellipsis": "打开...",
|
||||
"ui.open_preferences": "打开首选项",
|
||||
"ui.open_recent": "打开最近",
|
||||
"ui.operation": "操作",
|
||||
"ui.options": "选项",
|
||||
"ui.orbit_around": "环绕",
|
||||
"ui.origin": "原点",
|
||||
"ui.palette_points": "点",
|
||||
"ui.palette_profile": "轮廓",
|
||||
"ui.pan_zoom_orbit": "平移、缩放、环绕",
|
||||
"ui.parallel": "平行",
|
||||
"ui.parameters": "参数",
|
||||
"ui.paste": "粘贴",
|
||||
"ui.path": "路径",
|
||||
"ui.pattern_on_path": "路径阵列",
|
||||
"ui.perpendicular": "垂直",
|
||||
"ui.perspective_view": "透视图",
|
||||
"ui.pipe": "管道",
|
||||
"ui.placement": "放置",
|
||||
"ui.plane": "平面",
|
||||
"ui.plane_at_angle": "角度平面",
|
||||
"ui.point": "点",
|
||||
"ui.point_under_the_cursor": "光标下的点",
|
||||
"ui.points": "点",
|
||||
"ui.polygon": "多边形",
|
||||
"ui.preferences": "首选项",
|
||||
"ui.press_a_key": "按一个键…",
|
||||
"ui.profile": "轮廓",
|
||||
"ui.profile_plane": "轮廓平面",
|
||||
"ui.profiles": "轮廓",
|
||||
"ui.project": "投影",
|
||||
"ui.project_face_edges_sketch": "将面的边投影到新草图",
|
||||
"ui.quantity_2": "数量 2",
|
||||
"ui.quantity": "数量",
|
||||
"ui.quit": "退出",
|
||||
"ui.radial_context_menu": "环形上下文菜单",
|
||||
"ui.radius": "半径",
|
||||
"ui.radius_type": "半径类型",
|
||||
"ui.rectangle": "矩形",
|
||||
"ui.rectangular_pattern": "矩形阵列",
|
||||
"ui.redo": "重做",
|
||||
"ui.render": "渲染",
|
||||
"ui.renderer": "渲染器",
|
||||
"ui.reset": "重置",
|
||||
"ui.reset_all": "全部重置",
|
||||
"ui.reset_the_settings_on_this_page": "重置此页面上的设置",
|
||||
"ui.restore_defaults": "恢复默认值",
|
||||
"ui.reverse_zoom_direction": "反转缩放方向",
|
||||
"ui.revolve": "旋转",
|
||||
"ui.right": "右视",
|
||||
"ui.save": "保存",
|
||||
"ui.save_as": "另存为",
|
||||
"ui.save_as_ellipsis": "另存为...",
|
||||
"ui.scale": "缩放",
|
||||
"ui.section_analysis": "截面分析",
|
||||
"ui.select": "选择",
|
||||
"ui.select_other_hint": "“选择其他”:在模型上按住左键(或右键单击 › 选择其他…)列出光标下的所有对象;在同一位置再次单击可选择该处的下一个对象。",
|
||||
"ui.selection": "选择",
|
||||
"ui.shell": "抽壳",
|
||||
"ui.show": "显示",
|
||||
"ui.show_constraints": "约束",
|
||||
"ui.show_dimensions": "尺寸",
|
||||
"ui.show_points": "显示点",
|
||||
"ui.show_profile": "显示轮廓",
|
||||
"ui.show_projected_geometries": "投影几何",
|
||||
"ui.simple": "简单",
|
||||
"ui.simulation": "仿真",
|
||||
"ui.single": "单个",
|
||||
"ui.sketch": "草图",
|
||||
"ui.sketch_dimension": "草图尺寸",
|
||||
"ui.sketch_grid": "草图网格",
|
||||
"ui.sketch_scale": "草图比例",
|
||||
"ui.sketch_status": "草图状态",
|
||||
"ui.sketches": "草图",
|
||||
"ui.slice": "切片",
|
||||
"ui.slot": "槽",
|
||||
"ui.smooth": "平滑",
|
||||
"ui.smooth_wheel_zoom": "平滑滚轮缩放",
|
||||
"ui.smooth_wheel_zoom_tip": "关闭后每个滚轮事件立即生效,不做平滑延迟",
|
||||
"ui.snap": "捕捉",
|
||||
"ui.space_mouse_hint": "同时移动和旋转视图;草图中仅支持平移和缩放。",
|
||||
"ui.space_mouse_navigate": "使用 SpaceNavigator / SpaceMouse 导航",
|
||||
"ui.spacing_2": "间距 2",
|
||||
"ui.spacing": "间距",
|
||||
"ui.sphere": "球体",
|
||||
"ui.spline": "样条曲线",
|
||||
"ui.split_body": "分割实体",
|
||||
"ui.stand_up_tip": "当 Z 朝上的 STEP 或 IGES 文件打开后,提示绕 X 轴旋转四分之一周",
|
||||
"ui.start": "开始",
|
||||
"ui.start_radius": "起始半径",
|
||||
"ui.sweep": "扫掠",
|
||||
"ui.symmetric": "对称",
|
||||
"ui.symmetry": "对称",
|
||||
"ui.system": "系统",
|
||||
"ui.tangent": "相切",
|
||||
"ui.taper_angle": "锥角",
|
||||
"ui.tapped": "攻丝",
|
||||
"ui.text": "文本",
|
||||
"ui.theme": "主题",
|
||||
"ui.thickness": "厚度",
|
||||
"ui.thread": "螺纹",
|
||||
"ui.timeline_bottom": "底部时间轴",
|
||||
"ui.timeline_left_note": "左侧布局下,回退条显示在零件和场景浏览器的上方。",
|
||||
"ui.timeline_left_sidebar": "左侧边栏",
|
||||
"ui.tip_angle": "尖端角度",
|
||||
"ui.to_object": "到对象",
|
||||
"ui.top": "上视",
|
||||
"ui.torus": "圆环体",
|
||||
"ui.trackpad_pan_tip": "触控板手势用于平移(关闭:像滚轮一样缩放);鼠标滚轮缩放、拇指滚轮横向平移、双指捏合始终缩放",
|
||||
"ui.trim": "修剪",
|
||||
"ui.two_finger_scroll_pans": "双指滚动平移",
|
||||
"ui.two_sides": "双侧",
|
||||
"ui.ui_scale": "界面缩放",
|
||||
"ui.undo": "撤销",
|
||||
"ui.units": "单位",
|
||||
"ui.units_of_new_designs": "新设计的单位",
|
||||
"ui.untitled": "无标题",
|
||||
"ui.up_axis_note": "视图中朝上的轴(轴视图、主视图、注视)。设计本身不变。",
|
||||
"ui.variable": "变量",
|
||||
"ui.view_centre": "视图中心",
|
||||
"ui.visual_shaded_hidden_edges": "带隐藏边的着色",
|
||||
"ui.visual_shaded_with_edges": "带边着色",
|
||||
"ui.visual_style": "视觉样式",
|
||||
"ui.visual_wireframe": "线框",
|
||||
"ui.workspace": "工作区",
|
||||
"ui.y_distance": "Y 距离",
|
||||
"ui.x_distance": "X 距离",
|
||||
"ui.y_up": "Y 朝上",
|
||||
"ui.z_up": "Z 朝上",
|
||||
"ui.zoom_to_cursor_tip": "滚轮向光标所在点缩放(关闭:向视图中心缩放)",
|
||||
"ui.zoom_to_the_cursor": "缩放到光标",
|
||||
"A new configuration, a copy of the active one": "新建配置,为当前配置的副本",
|
||||
"About SolidCraft": "关于 SolidCraft",
|
||||
"Active": "活动",
|
||||
"Active rule": "活动规则",
|
||||
"Add": "添加",
|
||||
"Add Key": "添加按键",
|
||||
"Add Mid Radius": "添加中间半径",
|
||||
"Add Row": "添加行",
|
||||
"All Sketch Points": "所有草图点",
|
||||
"All open designs": "所有打开的设计",
|
||||
"Allowed expressions, separated by semicolons": "允许的表达式,以分号分隔",
|
||||
"Also deletes": "同时删除",
|
||||
"Angle 2": "角度 2",
|
||||
"Animate": "动画",
|
||||
"Appearance name": "外观名称",
|
||||
"Applied Radius": "应用半径",
|
||||
"Apply To": "应用于",
|
||||
"Area": "面积",
|
||||
"Attachment": "附着",
|
||||
"BROWSER": "浏览器",
|
||||
"Bend Line Position": "弯折线位置",
|
||||
"Bend Position": "折弯位置",
|
||||
"Bend Radius": "折弯半径",
|
||||
"Bends": "折弯",
|
||||
"Body": "实体",
|
||||
"Bounding box": "边界框",
|
||||
"CSV or JSON: updates existing parameters, adds new ones": "CSV 或 JSON:更新现有参数、添加新参数",
|
||||
"Capture All Positions": "捕获所有位置",
|
||||
"Catch Direction": "卡扣方向",
|
||||
"Cells": "区域",
|
||||
"Center X": "中心 X",
|
||||
"Center Y": "中心 Y",
|
||||
"Center of mass": "质心",
|
||||
"Chamfer Type": "倒角类型",
|
||||
"Check collisions": "检查碰撞",
|
||||
"Checked": "已检查",
|
||||
"Choose a new plane. Sketch geometry and dimensions stay unchanged in its two-dimensional coordinates.": "选择一个新平面。草图几何和尺寸在其二维坐标中保持不变。",
|
||||
"Close draft": "关闭草稿",
|
||||
"Collisions": "碰撞",
|
||||
"Colour": "颜色",
|
||||
"Component1": "组件1",
|
||||
"Configuration": "配置",
|
||||
"Configuration rules": "配置规则",
|
||||
"Contact": "接触",
|
||||
"Copy all licences": "复制所有许可证",
|
||||
"Copy diagnostic information": "复制诊断信息",
|
||||
"Copy report": "复制报告",
|
||||
"Corner Relief Shape": "拐角释放形状",
|
||||
"Counterbore depth": "沉孔深度",
|
||||
"Countersink angle": "沉头角度",
|
||||
"Create Issue — review draft": "创建 Issue——审阅草稿",
|
||||
"Customize Toolbar": "自定义工具栏",
|
||||
"Delete all": "全部删除",
|
||||
"Delete rule": "删除规则",
|
||||
"Delete the row": "删除此行",
|
||||
"Delete the set": "删除此集合",
|
||||
"Delete this parameter": "删除此参数",
|
||||
"Density": "密度",
|
||||
"Designs you open or save appear here.": "你打开或保存的设计会显示在此处。",
|
||||
"Determined by To Object. Choose Angle to edit the angular extent.": "由“到对象”确定。选择“角度”可编辑角度范围。",
|
||||
"Discard": "放弃",
|
||||
"Double-click to rename": "双击重命名",
|
||||
"Drive Joint": "驱动接头",
|
||||
"Edit Profile Sketch": "编辑轮廓草图",
|
||||
"Enable rule": "启用规则",
|
||||
"Enabled": "已启用",
|
||||
"Explode": "爆炸",
|
||||
"Extents": "范围",
|
||||
"Face attachment lost": "面附着丢失",
|
||||
"Faces / edges / vertices": "面 / 边 / 顶点",
|
||||
"Favourite": "收藏",
|
||||
"Files from many CAD programs have Y up, so they open lying on their back here (Z up).": "许多 CAD 程序的文件以 Y 为上轴,因此在此处打开时会平躺(Z 朝上)。",
|
||||
"Filter": "筛选",
|
||||
"Find in Browser": "在浏览器中查找",
|
||||
"Flat size": "平板尺寸",
|
||||
"Follow camera (Look At sketches)": "跟随相机(注视草图)",
|
||||
"Font": "字体",
|
||||
"Full Length": "全长",
|
||||
"Gap": "间隙",
|
||||
"Grid": "网格",
|
||||
"Height": "高度",
|
||||
"Home view": "主视图",
|
||||
"In This Design": "在此设计中",
|
||||
"Interferes": "发生干涉",
|
||||
"Issue title": "Issue 标题",
|
||||
"Joint Origin1": "接头原点1",
|
||||
"Keep Tools": "保留工具",
|
||||
"Later": "稍后",
|
||||
"Leave it": "保持原样",
|
||||
"Legacy face attachment. Rebinding to a certified face creates an exact attachment.": "旧版面附着。重新绑定到经验证的面可创建精确附着。",
|
||||
"Length": "长度",
|
||||
"Length units": "长度单位",
|
||||
"Library": "零件库",
|
||||
"Loop": "循环",
|
||||
"Margin": "余量",
|
||||
"Mass": "质量",
|
||||
"Material": "材质",
|
||||
"Mid Radius": "中间半径",
|
||||
"Modeled": "已建模",
|
||||
"More toolbar panels": "更多工具栏面板",
|
||||
"Motion": "运动",
|
||||
"Move Type": "移动类型",
|
||||
"Move earlier": "向前移动",
|
||||
"Move later": "向后移动",
|
||||
"Name": "名称",
|
||||
"No crash reports.": "没有崩溃报告。",
|
||||
"No matching appearances": "没有匹配的外观",
|
||||
"Not available yet": "暂不可用",
|
||||
"Opacity": "不透明度",
|
||||
"Open GitHub draft": "打开 GitHub 草稿",
|
||||
"Open-source parametric 3D CAD in Rust. Licensed MIT OR Apache-2.0.": "使用 Rust 编写的开源参数化 3D CAD。基于 MIT OR Apache-2.0 许可。",
|
||||
"Other side": "另一侧",
|
||||
"Parameter values": "参数值",
|
||||
"Per Edge": "每条边",
|
||||
"Pick the tool bodies": "选择工具实体",
|
||||
"Place at the origin instead": "改为放置在原点",
|
||||
"Playback": "播放",
|
||||
"Position": "位置",
|
||||
"Position (0 to 1)": "位置(0 到 1)",
|
||||
"Position along the edge, 0 at the start end, 1 at the other": "沿边的位置,起点端为 0,另一端为 1",
|
||||
"Preview leaves the current design unchanged.": "预览不会更改当前设计。",
|
||||
"RECENT": "最近使用",
|
||||
"Radius Fit": "半径拟合",
|
||||
"Ratio": "比率",
|
||||
"Recover": "恢复",
|
||||
"Recover unsaved design?": "恢复未保存的设计?",
|
||||
"Remove Tools": "移除工具",
|
||||
"Remove from list": "从列表移除",
|
||||
"Remove private design details or paths before sharing.": "共享前请移除私密设计细节或路径。",
|
||||
"Remove the column": "移除此列",
|
||||
"Remove the track": "移除轨迹",
|
||||
"Remove this point": "移除此点",
|
||||
"Report a bug — review a screenshot and open a GitHub draft": "报告缺陷 — 复核截图并开启 GitHub 草稿",
|
||||
"Reset Toolbar": "重置工具栏",
|
||||
"Restore the inherited component or physical material appearance": "还原继承的组件或物理材质外观",
|
||||
"Result": "结果",
|
||||
"Retake screenshot": "重新截图",
|
||||
"Reverse": "反向",
|
||||
"Reverse Direction": "反向",
|
||||
"Revert": "还原",
|
||||
"Revert All Positions": "还原所有位置",
|
||||
"Review the report and screenshot before opening GitHub.": "打开 GitHub 前请审阅报告和截图。",
|
||||
"Rim Edge": "轮缘边",
|
||||
"Roll History Marker Here": "将历史标记滚动到此处",
|
||||
"Rule": "规则",
|
||||
"Saved preview. Geometry is being checked before editing.": "已保存预览。编辑前正在检查几何。",
|
||||
"Scale factor": "比例因子",
|
||||
"Search": "搜索",
|
||||
"Search commands": "搜索命令",
|
||||
"Select bodies (a face picks its body)": "选择实体(单击面即选择其所属实体)",
|
||||
"Select bodies or faces instead": "改为选择实体或面",
|
||||
"Selection filter": "选择筛选",
|
||||
"Semantic faces share their appearance across instances.": "语义面在各实例间共享外观。",
|
||||
"Shape": "形状",
|
||||
"Show Agent Cursor": "显示代理光标",
|
||||
"Show in folder": "在文件夹中显示",
|
||||
"Size": "大小",
|
||||
"Sketch plane has no body-face attachment.": "草图平面没有实体面附着。",
|
||||
"SolidCraft closed before these designs were saved. Their last autosave can be opened.": "SolidCraft 在这些设计保存前已关闭。可以打开它们的上次自动保存。",
|
||||
"Speed": "速度",
|
||||
"Stand it up": "将其立起",
|
||||
"Stand it up: a quarter turn about X, as one Move feature you can undo.": "将其立起:绕 X 轴旋转四分之一周,作为一个可撤销的移动特征。",
|
||||
"Stand this part up?": "立起此零件?",
|
||||
"Step": "步骤",
|
||||
"Steps": "步数",
|
||||
"Suppressed": "已抑制",
|
||||
"Swap Ends": "交换端点",
|
||||
"Tangent Chain": "相切链",
|
||||
"The dashed sketch is a saved plane guide. Select a new face to repair the attachment.": "虚线草图是已保存的平面引导。选择一个新面以修复附着。",
|
||||
"The saved author occurrence is missing. Select a valid author placement before rebinding.": "保存的作者实例缺失。重新绑定前请选择有效的作者放置。",
|
||||
"Then": "操作",
|
||||
"These features will be deleted:": "以下特征将被删除:",
|
||||
"These features will fail:": "以下特征将失败:",
|
||||
"This child is shared. Deleting it removes it from every copy of its parent.": "此子项是共享的。删除它会从其父项的每个副本中移除。",
|
||||
"This child is shared. Position changes apply to every copy of its parent.": "此子项是共享的。位置更改会应用到其父项的每个副本。",
|
||||
"This face cannot provide a durable attachment. Choose another face or explicitly use a fixed plane.": "此面无法提供持久附着。请选择其他面或显式使用固定平面。",
|
||||
"To Offset": "偏移",
|
||||
"Tolerance": "公差",
|
||||
"Type": "类型",
|
||||
"Updating the preview": "正在更新预览",
|
||||
"Use fixed plane": "使用固定平面",
|
||||
"VIEW": "视图",
|
||||
"Value": "值",
|
||||
"Volume": "体积",
|
||||
"Wall": "壁厚",
|
||||
"When": "条件",
|
||||
"Width": "宽度",
|
||||
"Will fail": "将失败",
|
||||
"angle, height": "角度、高度",
|
||||
"command name": "命令名称",
|
||||
"expression, e.g. 25 mm": "表达式,例如 25 mm",
|
||||
"fits the face, e.g. M8": "适配该面,例如 M8",
|
||||
"from the rule": "来自规则",
|
||||
"needs two joints": "需要两个接头",
|
||||
"new configuration": "新配置",
|
||||
"new name": "新名称",
|
||||
"new parameter": "新参数",
|
||||
"no interference": "无干涉",
|
||||
"no joint that moves yet": "尚无运动接头",
|
||||
"no picture": "无图片",
|
||||
"pick a hole to seat it, or OK places it at the origin": "选择一个孔放置,或单击确定放置在原点",
|
||||
"pick one or two vertices, edges or faces": "选择一个或两个顶点、边或面",
|
||||
"the parts library is not available": "零件库不可用",
|
||||
"to the next face": "到下一个面",
|
||||
"Revert Position": "恢复位置",
|
||||
"Construction": "构造",
|
||||
"Faces": "面",
|
||||
"Edges": "边",
|
||||
"Vertices": "顶点",
|
||||
"Sketch geometry and profiles": "草图几何和轮廓",
|
||||
"Sketch curves": "草图曲线",
|
||||
"Sketch points": "草图点",
|
||||
"Sketch dimensions": "草图尺寸",
|
||||
"Sketch constraints": "草图约束",
|
||||
"0 turns it off": "0 表示关闭",
|
||||
"Show the pointer an AI agent moves (#35)": "显示 AI 代理移动的指针(#35)",
|
||||
"Follow the camera (Look At sketches)": "跟随相机(注视草图)",
|
||||
"Flat": "平面",
|
||||
"Save Rule": "保存规则",
|
||||
"Preview Row": "预览行",
|
||||
"Add condition": "添加条件",
|
||||
"Apply to selection": "应用到选择",
|
||||
"Remove override": "移除覆盖",
|
||||
"Copy screenshot": "复制截图",
|
||||
"Remove screenshot": "移除截图",
|
||||
"Use active author placement": "使用活动作者放置",
|
||||
"Reattach selected face": "重新附着所选面",
|
||||
"Use saved fixed plane": "使用已保存的固定平面",
|
||||
"Use selected face as fixed plane": "使用所选面作为固定平面",
|
||||
"View navigator": "视图导航器",
|
||||
"Signed axes": "带符号轴",
|
||||
"Cube": "立方体",
|
||||
"Middle double-click": "中键双击",
|
||||
"Double-clicking the middle button in the view: Home (the home view, framing what is shown), Fit (frame what is shown, keeping the orientation) or nothing": "在视图中双击中键:主视图(主视图,框住所显示的内容)、适合窗口(框住所显示的内容,保持方向)或无动作",
|
||||
"Select Other: hold the left button still on the model (or right-click › Select Other…) to list everything under the cursor; click again on the same spot to select the next thing there. Double-click the middle button to go Home (Preferences › Navigation).": "“选择其他”:在模型上按住左键(或右键单击 › 选择其他…)列出光标下的所有对象;在同一位置再次单击可选择该处的下一个对象。双击中键转到主视图(首选项 › 导航)。",
|
||||
"cmd.render_backends": "渲染后端",
|
||||
"cmd.view_navigate": "导航视图",
|
||||
"ui.animateJoint": "动画接头",
|
||||
"ui.calibrate": "校准",
|
||||
"ui.customizeToolbar": "自定义工具栏…",
|
||||
"ui.editJointLimits": "编辑接头限制",
|
||||
"ui.exportDxf": "导出 DXF…",
|
||||
"ui.findInTimeline": "在时间线中查找",
|
||||
"ui.findInWindow": "在窗口中查找",
|
||||
"ui.fit": "适合",
|
||||
"ui.groupSelected": "将所选编组",
|
||||
"ui.hideAxes": "隐藏轴",
|
||||
"ui.hideDimension": "隐藏尺寸",
|
||||
"ui.hideOrigin": "隐藏原点",
|
||||
"ui.hidePlanes": "隐藏平面",
|
||||
"ui.hideProfile": "隐藏轮廓",
|
||||
"ui.homeView": "主视图",
|
||||
"ui.isolate": "隔离",
|
||||
"ui.lock": "锁定",
|
||||
"ui.newSketchGroup": "新建草图组",
|
||||
"ui.normalConstruction": "普通 / 构造",
|
||||
"ui.orbit": "环绕",
|
||||
"ui.heading_position": "位置",
|
||||
"ui.pan": "平移",
|
||||
"ui.pinToShortcuts": "固定到快捷方式",
|
||||
"ui.pinToToolbar": "固定到工具栏",
|
||||
"ui.properties": "属性",
|
||||
"ui.removeSection": "移除剖面",
|
||||
"ui.removeFromToolbar": "从工具栏移除",
|
||||
"ui.repeat": "重复",
|
||||
"ui.report_a_bug": "报告缺陷",
|
||||
"ui.restore": "还原",
|
||||
"ui.heading_setup": "设置",
|
||||
"ui.selectOther": "选择其他…",
|
||||
"ui.selectTangentChain": "选择相切链",
|
||||
"ui.showAll": "显示全部",
|
||||
"ui.showAllBodies": "显示所有实体",
|
||||
"ui.showAxes": "显示轴",
|
||||
"ui.showDimension": "显示尺寸",
|
||||
"ui.showOrigin": "显示原点",
|
||||
"ui.showPlanes": "显示平面",
|
||||
"ui.suppress": "抑制",
|
||||
"ui.command_palette_hint": "输入命令,例如 {example} — 或按 S 搜索",
|
||||
"ui.unground": "取消固定",
|
||||
"ui.unisolate": "取消隔离",
|
||||
"ui.unlock": "解锁",
|
||||
"ui.unsuppress": "取消抑制",
|
||||
"ui.updateToCurrentView": "更新为当前视图",
|
||||
"ui.zoom": "缩放",
|
||||
"ui.command_done": "{command} 完成",
|
||||
"timeline.group": "新建特征文件夹",
|
||||
"timeline.ungroup": "移除特征文件夹",
|
||||
"timeline.rename_group": "重命名特征文件夹",
|
||||
"timeline.move_to_group": "将特征移至文件夹",
|
||||
"timeline.move_many": "移动时间线项目",
|
||||
"timeline.reorder_many": "特征重新排序",
|
||||
"ui.show_construction_geometries": "构造几何图元",
|
||||
"ui.total_angle": "总角度",
|
||||
"sketch_text.builtin_font": "{family}(内置)",
|
||||
"sketch_text.font_search": "搜索字体",
|
||||
"sketch_text.loading_fonts": "正在查找已安装字体…",
|
||||
"sketch_text.use_font_file": "使用字体文件 {path}",
|
||||
"sketch_text.bold": "粗体",
|
||||
"sketch_text.italic": "斜体",
|
||||
"sketch_text.align_left": "左对齐",
|
||||
"sketch_text.align_center": "居中",
|
||||
"sketch_text.align_right": "右对齐",
|
||||
"sketch_text.path_hint": "文字所跟随的草图曲线:先单击此处,再选择曲线",
|
||||
"sketch_text.path_clear": "将文字移离其曲线",
|
||||
"Style": "样式",
|
||||
"Alignment": "对齐",
|
||||
"Letter Spacing": "字间距",
|
||||
"Line Spacing": "行间距",
|
||||
"Path": "路径",
|
||||
"sketch_text.on_path": "文字沿路径排列:由曲线放置",
|
||||
"ui.analysis": "分析",
|
||||
"ui.edit_section_analysis": "编辑截面分析"
|
||||
}
|
||||
@@ -67,7 +67,9 @@ pub fn palette_bar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
ui.label(RichText::new("›").strong().color(t.accent));
|
||||
let r = ui.add(
|
||||
egui::TextEdit::singleline(&mut app.palette.text)
|
||||
.hint_text("Type a command, e.g. Extrude {\"distance\": 20} — or press S to search")
|
||||
// The JSON example keeps its braces here rather than in the catalog, so the
|
||||
// placeholder parser never has to treat JSON punctuation as a name.
|
||||
.hint_text(crate::i18n::format("ui.command_palette_hint", &[("example", "Extrude {\"distance\": 20}")]))
|
||||
.desired_width(520.0),
|
||||
);
|
||||
if app.palette.focus {
|
||||
|
||||
@@ -860,13 +860,15 @@ fn page(app: &mut SolveApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
);
|
||||
crate::scenario::publish_rect("prefs:scale", scale.rect);
|
||||
ui.end_row();
|
||||
ui.label("Feature history");
|
||||
ui.label(app.tr("ui.feature_history"));
|
||||
let timeline_bottom = app.tr("ui.timeline_bottom").to_string();
|
||||
let timeline_left = app.tr("ui.timeline_left_sidebar").to_string();
|
||||
ui.horizontal(|ui| {
|
||||
ui.radio_value(&mut app.preferences.timeline_left, false, "Bottom timeline");
|
||||
ui.radio_value(&mut app.preferences.timeline_left, true, "Left sidebar");
|
||||
ui.radio_value(&mut app.preferences.timeline_left, false, timeline_bottom);
|
||||
ui.radio_value(&mut app.preferences.timeline_left, true, timeline_left);
|
||||
});
|
||||
ui.end_row();
|
||||
note(ui, "The left layout shows the rollback bar above the parts and scene browser.");
|
||||
note(ui, app.tr("ui.timeline_left_note"));
|
||||
ui.label(app.tr("ui.units_of_new_designs"));
|
||||
egui::ComboBox::from_id_salt("pref_units").selected_text(app.preferences.default_units.clone()).show_ui(ui, |ui| {
|
||||
for u in UNITS {
|
||||
|
||||
@@ -296,7 +296,8 @@ pub fn app_bar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
tabs_ui.set_clip_rect(tabs_rect.intersect(ui.clip_rect()));
|
||||
crate::documents::tabs(app, &mut tabs_ui, r, tabs_start, tabs_end);
|
||||
let response = ui.interact(report, ui.id().with("sc_report_button"), Sense::click());
|
||||
response.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, ui.is_enabled(), "Report a bug"));
|
||||
let report_text = crate::i18n::tr("ui.report_a_bug");
|
||||
response.widget_info(|| egui::WidgetInfo::labeled(egui::WidgetType::Button, ui.is_enabled(), report_text));
|
||||
crate::scenario::publish_handle("titlebar:report", report.center());
|
||||
ui.painter().rect_filled(report, 4.0, if response.hovered() || response.has_focus() { t.hover } else { t.app_bar });
|
||||
ui.painter().rect_stroke(report, 4.0, Stroke::new(1.0, t.border), egui::StrokeKind::Inside);
|
||||
@@ -306,7 +307,7 @@ pub fn app_bar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
ui.painter().text(
|
||||
pos2(report.left() + 29.0, report.center().y),
|
||||
Align2::LEFT_CENTER,
|
||||
"Report a bug",
|
||||
report_text,
|
||||
FontId::proportional(13.0),
|
||||
t.app_bar_text,
|
||||
);
|
||||
|
||||
@@ -123,7 +123,7 @@ unit regardless of the design, pass an expression with one: `"value": "50 mm"` o
|
||||
| `ui.home` | `open?`: bool; `sample?`: index | the start page: show or hide it, open a built-in sample (once built); returns `open` and the recent designs |
|
||||
| `ui.shortcut` | `command`, `key?`, `replace?` | a command's key, or rebinds it (`""` clears; a key another command uses is refused unless `replace`) |
|
||||
| `ui.help` | `item?`: `about`, `shortcuts` (read only), `report` (copies diagnostics), `crashes` (the Crash Reports window) | the version, commit, build date and diagnostic text |
|
||||
| `ui.prefs` | any of `default_units`, `nav` (`fusion`, `solidworks`, `inventor`, `alias`, `tinkercad`, `openscad`), `zoom_reverse`, `zoom_to_cursor`, `smooth_wheel_zoom` (default true), `trackpad_pan` (default true: two-finger scroll pans, pinch zooms), `orbit_cursor`, `look_at_anim`, `look_at_speed`, `middle_double_click` (`home` (default), `fit`, `off`), `msaa`, `length_decimals`, `angle_decimals`, `theme` (`dark`, `light`, `system`), `language` (`en`, `ja`; see [localization](localization.md)), `ui_scale`, `up_axis` (`z`, `y`), `offer_stand_up`; `open` (a page: General, Navigation, Keyboard Shortcuts, Design, Graphics, Sketch, Selection) opens the Preferences dialog there; `button`: `ok`, `cancel`, `apply`, `defaults`; `stand_up: true` stands up a just-opened Y-up file | sets preferences (kept between runs), returns them all plus `dialog` (the open page or null), `dark` and `stand_up_offer` |
|
||||
| `ui.prefs` | any of `default_units`, `nav` (`fusion`, `solidworks`, `inventor`, `alias`, `tinkercad`, `openscad`), `zoom_reverse`, `zoom_to_cursor`, `smooth_wheel_zoom` (default true), `trackpad_pan` (default true: two-finger scroll pans, pinch zooms), `orbit_cursor`, `look_at_anim`, `look_at_speed`, `middle_double_click` (`home` (default), `fit`, `off`), `msaa`, `length_decimals`, `angle_decimals`, `theme` (`dark`, `light`, `system`), `language` (`en`, `ja`, `de`, `ko`, `zh`; see [localization](localization.md)), `ui_scale`, `up_axis` (`z`, `y`), `offer_stand_up`; `open` (a page: General, Navigation, Keyboard Shortcuts, Design, Graphics, Sketch, Selection) opens the Preferences dialog there; `button`: `ok`, `cancel`, `apply`, `defaults`; `stand_up: true` stands up a just-opened Y-up file | sets preferences (kept between runs), returns them all plus `dialog` (the open page or null), `dark` and `stand_up_offer` |
|
||||
| `ui.capture` | `action?`: `capture`, `revert`, `cancel` | answers the Capture Position question (asked when a command starts or the design is saved while components were moved without capturing); without `action`, what waits |
|
||||
| `ui.window` | `action`: minimize, maximize, restore, toggle, close | what the title bar's caption buttons do (on Windows and Linux the application bar is the title bar); close asks about unsaved designs first |
|
||||
| `ui.agent_cursor` | `show` (bool), `speed`: a number from 1 to 5 (times the normal speed) or `"instant"` (no animation: the command runs at once and the pointer jumps to where it acted), `follow_camera` (bool); all optional | the settings. While the cursor is shown, `engine.execute` first glides to the command's toolbar button and to each pick, then runs and replies; later requests wait their turn. `"animate": false` on a call skips it |
|
||||
|
||||
+47
-10
@@ -1,8 +1,9 @@
|
||||
# UI localization
|
||||
|
||||
SolidCraft embeds its translations and works offline. English and Japanese are currently
|
||||
registered in `crates/ui-egui/src/i18n.rs`. Adding a language requires an embedded catalog and
|
||||
one registration there; the preferences selector and validation use the same registry.
|
||||
SolidCraft embeds its translations and works offline. English, Japanese, German, Korean and
|
||||
Chinese are currently registered in `crates/ui-egui/src/i18n.rs`. Adding a language requires an
|
||||
embedded catalog and one registration there; the preferences selector and validation use the same
|
||||
registry.
|
||||
|
||||
## Messages
|
||||
|
||||
@@ -23,6 +24,37 @@ Catalogs are parsed once and looked up through ordered maps. This is offline loc
|
||||
not a claim of zero runtime overhead. The catalog tests check registered locale IDs,
|
||||
translation keys, and placeholder consistency. Run `cargo xtask ci` before submitting changes.
|
||||
|
||||
## Coverage
|
||||
|
||||
A message a locale never received falls back to English silently, so a gap shows up only as a row
|
||||
that looks untranslated. Two guards catch that in the build:
|
||||
|
||||
- `every_complete_catalog_carries_every_english_message` — adding a key to `en.json`
|
||||
without giving it to a complete catalog fails the tests. English is the source catalog,
|
||||
Japanese and Chinese are delivered complete; German and Korean are still partial — their
|
||||
gaps are reported by `cargo xtask i18n` without failing the build.
|
||||
- `every_toolbar_tab_and_panel_is_in_the_english_catalog` — tabs and panels reach the UI as bare
|
||||
source strings, resolved only by the English-label adapter, so a heading missing from `en.json`
|
||||
can never translate.
|
||||
|
||||
Command labels are the same case on a larger scale: every label in the engine's registry now has
|
||||
an entry under `cmd.<command id>`, and `every_command_label_is_in_the_english_catalog` fails if a
|
||||
command is added without one.
|
||||
|
||||
The context menu and the browser are the same case again, and they carry the most labels of all:
|
||||
`every_menu_label_is_in_the_english_catalog` opens every menu the app can reach, flips the state
|
||||
each conditional label branches on (origin shown, bodies locked, bodies hidden) so both branches
|
||||
are seen, and fails on any label the catalog has never seen. Unit symbols are left out of the
|
||||
sweep: `mm` is `mm` in every language, so they are not prose to translate. The sweep also asserts
|
||||
it keeps reaching a broad set of labels, so it cannot pass by finding nothing.
|
||||
|
||||
`cargo xtask i18n` prints the same picture across every locale — which messages each one is
|
||||
missing and which command labels English cannot translate. `--check` turns it into a non-zero exit
|
||||
for CI. `--add-missing` writes each uncovered label into `en.json` under `cmd.<command id>`, with
|
||||
the English label as its text, so the label adapter can find it and only the translations are left
|
||||
to do. One key per distinct label: two commands that share a label (`Trim`, `Extend`) must not both
|
||||
claim it, or the English-text lookup would silently pick one.
|
||||
|
||||
## Rendering and coverage
|
||||
|
||||
One immutable `Locale` type handles all translations. Table widgets without an app reference
|
||||
@@ -31,18 +63,23 @@ Standalone translated panels enter the same scope. Measure rendered text to size
|
||||
widgets; UTF-8 byte lengths do not describe their displayed width.
|
||||
|
||||
The desktop host tries system CJK fonts across Windows, macOS and Linux, as well as an optional
|
||||
`CRAFT_FONTS_DIR` font pack, and skips files that cannot be parsed. No fonts are redistributed
|
||||
in native desktop distribution packages; native builds rely entirely on existing operating system fonts
|
||||
or craft-fonts.
|
||||
`CRAFT_FONTS_DIR` font pack, and skips files that cannot be parsed. Chinese uses its own fallback
|
||||
chain after the general CJK font: many Japanese CJK fonts (Hiragino Gothic, Meiryo, Yu Gothic)
|
||||
match the CJK font search but lack simplified characters, so a second fallback found by the
|
||||
Chinese font-name list (`PingFang`, `Microsoft YaHei`, `Noto Sans CJK SC`, …) covers them per
|
||||
glyph. No fonts are redistributed in native desktop distribution packages; native builds rely
|
||||
entirely on existing operating system fonts or craft-fonts.
|
||||
|
||||
In the browser, `solidcraft-web` bundles a self-hosted Japanese font (`BIZUDPGothic-Regular.ttf`, licensed
|
||||
under SIL OFL 1.1) within the web distribution artifacts only. The font is loaded asynchronously from the same
|
||||
origin (`./fonts/BIZUDPGothic-Regular.ttf`) on-demand when the interface language is set to Japanese, or, with
|
||||
origin (`./fonts/BIZUDPGothic-Regular.ttf`) on-demand when the interface language is a CJK one, or, with
|
||||
any other interface language, the first time Japanese text (kana, kanji or full-width forms in a design name,
|
||||
file name, body name or typed text) is drawn, without contacting any external CDNs or third-party servers.
|
||||
A session that never shows such text downloads no font. If font loading fails with the Japanese interface,
|
||||
the web interface shows English for that session only to prevent missing glyphs (tofu); the saved language
|
||||
preference is kept, so the next visit tries again. This bundled font specifically targets Japanese; Korean (Hangul) and Chinese scripts are not covered.
|
||||
A session that never shows such text downloads no font. If font loading fails with a CJK interface, the web
|
||||
interface shows English for that session only to prevent missing glyphs (tofu); the saved language
|
||||
preference is kept, so the next visit tries again. The bundled font specifically targets Japanese; it also
|
||||
renders the kanji Chinese shares with Japanese, while Korean (Hangul) and simplified-only Chinese characters
|
||||
are not covered, so a self-hosted SC/Korean font bundle remains follow-up work for full web coverage.
|
||||
|
||||
The current messages support named substitution, not locale-specific plural selection,
|
||||
number/date formatting, or right-to-left layout and shaping. Add those capabilities when
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
//! `cargo xtask i18n`: the message catalogs' coverage report, and a way to repair it.
|
||||
//!
|
||||
//! The catalogs under `crates/ui-egui/src/locales` are plain JSON maps from a stable message id
|
||||
//! to one translation. The English catalog is also the lookup table for `Locale::label()`, which
|
||||
//! translates source strings coming from the engine (command labels, toolbar tabs and panels) by
|
||||
//! matching their English text. A label the English catalog does not carry therefore stays English
|
||||
//! in *every* locale, silently — nothing fails, the row just looks untranslated.
|
||||
//!
|
||||
//! That is the failure this command exists to make visible. `--check` turns the report into a
|
||||
//! non-zero exit for CI; `--add-missing` writes the uncovered command labels into the English
|
||||
//! catalog under `cmd.<command id>` so a translator only has to fill in the other catalogs.
|
||||
//!
|
||||
//! German and Korean predate the coverage guards and are still partial, so their gaps are
|
||||
//! reported but do not fail `--check`; the catalogs delivered complete (`COMPLETE`) may not
|
||||
//! regress.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
/// Where the catalogs live, and the file the English one must lead.
|
||||
const DIR: &str = "crates/ui-egui/src/locales";
|
||||
|
||||
/// Catalogs delivered complete: a missing message here fails `--check`. English is the source
|
||||
/// catalog and is checked implicitly; keep this in step with `COMPLETE` in `ui-egui::i18n`.
|
||||
const COMPLETE: &[&str] = &["ja", "zh"];
|
||||
|
||||
/// What `run` found.
|
||||
#[derive(Default)]
|
||||
struct Report {
|
||||
/// Locale id → the English message ids it has not translated.
|
||||
missing: BTreeMap<String, Vec<String>>,
|
||||
/// Command label → the command ids that use it, for labels English does not carry.
|
||||
untranslated: BTreeMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
impl Report {
|
||||
/// Gaps that must be repaired (complete catalogs only; partial catalogs are reported).
|
||||
fn problems(&self) -> usize {
|
||||
self.missing.iter().filter(|(id, _)| COMPLETE.contains(&id.as_str())).map(|(_, m)| m.len()).sum::<usize>() + self.untranslated.len()
|
||||
}
|
||||
|
||||
/// Gaps in catalogs that are still allowed to be partial.
|
||||
fn reported_only(&self) -> usize {
|
||||
self.missing.iter().filter(|(id, _)| !COMPLETE.contains(&id.as_str())).map(|(_, m)| m.len()).sum()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(root: &Path, args: &[&str]) -> Result<(), String> {
|
||||
let english = read(root, "en")?;
|
||||
let mut catalogs: Vec<(String, Map<String, Value>)> = Vec::new();
|
||||
for id in locales(root)? {
|
||||
let messages = read(root, &id)?;
|
||||
catalogs.push((id, messages));
|
||||
}
|
||||
|
||||
let mut report = Report::default();
|
||||
for (id, messages) in &catalogs {
|
||||
if id == "en" {
|
||||
continue;
|
||||
}
|
||||
let missing: Vec<String> = english.keys().filter(|k| !messages.contains_key(*k)).cloned().collect();
|
||||
if !missing.is_empty() {
|
||||
report.missing.insert(id.clone(), missing);
|
||||
}
|
||||
}
|
||||
|
||||
let known: Vec<&str> = english.values().filter_map(Value::as_str).collect();
|
||||
for (label, ids) in labels_needing_translation(&known) {
|
||||
report.untranslated.insert(label, ids);
|
||||
}
|
||||
|
||||
if args.contains(&"--add-missing") {
|
||||
let added = add_missing(root, &mut report)?;
|
||||
println!("i18n: wrote {added} new English message(s) into {DIR}/en.json");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
print_report(&report);
|
||||
|
||||
if args.contains(&"--check") {
|
||||
if report.problems() > 0 {
|
||||
return Err(format!(
|
||||
"{} message(s) missing from a complete catalog and {} command label(s) with no English entry \
|
||||
(see above); `cargo xtask i18n --add-missing` writes the labels into en.json",
|
||||
report.missing.iter().filter(|(id, _)| COMPLETE.contains(&id.as_str())).map(|(_, m)| m.len()).sum::<usize>(),
|
||||
report.untranslated.len()
|
||||
));
|
||||
}
|
||||
if report.reported_only() > 0 {
|
||||
println!(
|
||||
"i18n: every complete catalog ({}) carries every English message and every command label is translatable; \
|
||||
{} message(s) are still missing from the partial catalogs (de, ko)",
|
||||
COMPLETE.join(", "),
|
||||
report.reported_only()
|
||||
);
|
||||
} else {
|
||||
println!("i18n: every catalog carries every English message; every command label is translatable");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Locale ids other than English: every catalog file that sits beside `en.json`, so a newly
|
||||
/// added locale is picked up without editing this command.
|
||||
fn locales(root: &Path) -> Result<Vec<String>, String> {
|
||||
let dir = root.join(DIR);
|
||||
let entries = std::fs::read_dir(&dir).map_err(|e| format!("{}: {e}", dir.display()))?;
|
||||
let mut ids: Vec<String> = entries
|
||||
.filter_map(Result::ok)
|
||||
.map(|e| e.path())
|
||||
.filter(|p| p.extension().and_then(|x| x.to_str()) == Some("json"))
|
||||
.filter_map(|p| p.file_stem().map(|s| s.to_string_lossy().into_owned()))
|
||||
.filter(|id| id != "en")
|
||||
.collect();
|
||||
ids.sort();
|
||||
Ok(ids)
|
||||
}
|
||||
|
||||
fn read(root: &Path, locale: &str) -> Result<Map<String, Value>, String> {
|
||||
let path = root.join(DIR).join(format!("{locale}.json"));
|
||||
let text = std::fs::read_to_string(&path).map_err(|e| format!("{}: {e}", path.display()))?;
|
||||
match serde_json::from_str(&text) {
|
||||
Ok(Value::Object(map)) => Ok(map),
|
||||
Ok(_) => Err(format!("{}: a catalog must be a JSON object", path.display())),
|
||||
Err(e) => Err(format!("{}: {e}", path.display())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Command labels the English catalog cannot translate, mapped to the commands that use them.
|
||||
///
|
||||
/// The registry is read through `solidcraft-cli commands` so the report comes from the real
|
||||
/// command table, never from a copy of it.
|
||||
fn labels_needing_translation(known: &[&str]) -> BTreeMap<String, Vec<String>> {
|
||||
let mut out: BTreeMap<String, Vec<String>> = BTreeMap::new();
|
||||
let Ok(commands) = registry() else {
|
||||
return out;
|
||||
};
|
||||
for command in commands {
|
||||
let (Some(id), Some(label)) = (command["id"].as_str(), command["label"].as_str()) else {
|
||||
continue;
|
||||
};
|
||||
if known.contains(&label) {
|
||||
continue;
|
||||
}
|
||||
let ids = out.entry(label.to_string()).or_default();
|
||||
if !ids.iter().any(|x| x == id) {
|
||||
ids.push(id.to_string());
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn registry() -> Result<Vec<Value>, String> {
|
||||
let out = crate::cargo()
|
||||
.args(["run", "-q", "--release", "-p", "solidcraft-cli", "--", "commands"])
|
||||
.output()
|
||||
.map_err(|e| format!("solidcraft-cli: {e}"))?;
|
||||
if !out.status.success() {
|
||||
return Err(format!("solidcraft-cli commands failed:\n{}", String::from_utf8_lossy(&out.stderr)));
|
||||
}
|
||||
serde_json::from_slice(&out.stdout).map_err(|e| format!("commands JSON: {e}"))
|
||||
}
|
||||
|
||||
/// Write one `cmd.<command id>` entry per still-untranslated label into the English catalog and
|
||||
/// drop the newly covered labels from the report.
|
||||
fn add_missing(root: &Path, report: &mut Report) -> Result<usize, String> {
|
||||
if report.untranslated.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
let path = root.join(DIR).join("en.json");
|
||||
let mut english = read(root, "en")?;
|
||||
// One key per label, so two commands sharing a label cannot collide in `label()`'s
|
||||
// English-text → id table. The lowest command id wins, keeping the entry stable.
|
||||
let mut chosen: BTreeMap<String, String> = BTreeMap::new();
|
||||
for (label, ids) in &report.untranslated {
|
||||
if let Some(id) = ids.iter().min() {
|
||||
chosen.insert(label.clone(), format!("cmd.{id}"));
|
||||
}
|
||||
}
|
||||
for (label, key) in &chosen {
|
||||
if english.contains_key(key) {
|
||||
continue;
|
||||
}
|
||||
english.insert(key.clone(), Value::String(label.clone()));
|
||||
}
|
||||
english.sort_keys();
|
||||
std::fs::write(&path, serde_json::to_string_pretty(&Value::Object(english)).map_err(|e| e.to_string())? + "\n")
|
||||
.map_err(|e| format!("{}: {e}", path.display()))?;
|
||||
let added = chosen.len();
|
||||
report.untranslated.clear();
|
||||
Ok(added)
|
||||
}
|
||||
|
||||
fn print_report(report: &Report) {
|
||||
for (locale, missing) in &report.missing {
|
||||
println!("i18n: {locale} is missing {} English message(s):", missing.len());
|
||||
for id in missing {
|
||||
println!(" {id}");
|
||||
}
|
||||
}
|
||||
if !report.untranslated.is_empty() {
|
||||
println!("i18n: {} command label(s) have no English catalog entry, so they stay English in every locale:", report.untranslated.len());
|
||||
for (label, ids) in &report.untranslated {
|
||||
println!(" {label:<42} {}", ids.join(", "));
|
||||
}
|
||||
println!("i18n: run `cargo xtask i18n --add-missing` to add them to en.json, then translate them.");
|
||||
}
|
||||
if report.problems() == 0 {
|
||||
println!("i18n: catalogs complete; every command label is translatable");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn report_counts_both_kinds_of_gap() {
|
||||
let report = Report {
|
||||
missing: BTreeMap::from([("zh".into(), vec!["a".into(), "b".into()])]),
|
||||
untranslated: BTreeMap::from([("Flange".into(), vec!["sheet.flange".into()])]),
|
||||
};
|
||||
assert_eq!(report.problems(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_empty_report_has_no_problems() {
|
||||
assert_eq!(Report::default().problems(), 0);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
mod assets;
|
||||
mod book;
|
||||
mod features;
|
||||
mod i18n;
|
||||
mod layers;
|
||||
mod licences;
|
||||
mod test;
|
||||
@@ -20,6 +21,12 @@ commands:
|
||||
layers enforce the crate dependency layering (plan/architecture.md §3)
|
||||
assets check that every non-code asset file is attributed in ATTRIBUTION.md
|
||||
features list SolidCraft's commands by area in docs/features.md (from the registry)
|
||||
i18n [--check | --add-missing]
|
||||
report how complete the message catalogs in crates/ui-egui/src/locales are:
|
||||
which messages a locale has not translated and which command labels the English
|
||||
catalog cannot translate (so they stay English everywhere). --check fails when
|
||||
there is a gap, for CI; --add-missing writes the uncovered command labels into
|
||||
en.json under cmd.<command id> so only the translations are left to do
|
||||
oracle [--cli BIN]
|
||||
replay the reference tests (tests/oracle/*/recipe.json), compare with each
|
||||
case's expected.json and write docs/oracle.md (--cli: a solidcraft-cli
|
||||
@@ -49,6 +56,7 @@ fn main() -> ExitCode {
|
||||
Some("layers") => cmd_layers(),
|
||||
Some("assets") => assets::run(&root()),
|
||||
Some("features") => features::run(&root()),
|
||||
Some("i18n") => i18n::run(&root(), &rest),
|
||||
Some("oracle") => cmd_oracle(rest.iter().position(|a| *a == "--cli").and_then(|i| rest.get(i + 1)).copied()),
|
||||
Some("action-matrix") => {
|
||||
cmd_action_matrix(rest.contains(&"--bless"), rest.iter().position(|a| *a == "--results").and_then(|i| rest.get(i + 1)).copied())
|
||||
|
||||
Reference in new issue
Block a user