feat(locale): add Fluent translations

Default to the platform language with persisted overrides across native and web builds.

Refs #630
This commit is contained in:
Hakan Seven 2026-08-02 18:53:38 +03:00
commit b90b3e3ecf
26 changed files with 1581 additions and 112 deletions

403
Cargo.lock generated
View file

@ -30,6 +30,8 @@ dependencies = [
"fontdb",
"getrandom 0.3.4",
"glam",
"i18n-embed",
"i18n-embed-fl",
"iced",
"iced_aw",
"iced_core",
@ -45,6 +47,7 @@ dependencies = [
"printpdf",
"rayon",
"rfd",
"rust-embed",
"rustc-hash 2.1.3",
"semver",
"serde",
@ -116,7 +119,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"cpufeatures 0.2.17",
]
[[package]]
@ -371,6 +374,15 @@ dependencies = [
"x11rb",
]
[[package]]
name = "arc-swap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
dependencies = [
"rustversion",
]
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
@ -661,6 +673,15 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "basic-toml"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
dependencies = [
"serde",
]
[[package]]
name = "bincode"
version = "1.3.3"
@ -760,6 +781,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-buffer"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
dependencies = [
"hybrid-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
@ -1002,7 +1032,7 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"crypto-common 0.1.7",
"inout",
]
@ -1116,6 +1146,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "convert_case"
version = "0.4.0"
@ -1238,6 +1274,15 @@ dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
@ -1300,6 +1345,15 @@ dependencies = [
"typenum",
]
[[package]]
name = "crypto-common"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
"hybrid-array",
]
[[package]]
name = "ctor"
version = "0.10.1"
@ -1391,8 +1445,19 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"block-buffer 0.10.4",
"crypto-common 0.1.7",
]
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.1",
"const-oid",
"crypto-common 0.2.2",
]
[[package]]
@ -1413,6 +1478,17 @@ dependencies = [
"objc2 0.6.4",
]
[[package]]
name = "displaydoc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "dlib"
version = "0.5.3"
@ -1664,6 +1740,15 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "find-crate"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2"
dependencies = [
"toml 0.5.11",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@ -1698,6 +1783,51 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
[[package]]
name = "fluent"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477"
dependencies = [
"fluent-bundle",
"unic-langid",
]
[[package]]
name = "fluent-bundle"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4"
dependencies = [
"fluent-langneg",
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash 2.1.3",
"self_cell",
"smallvec",
"unic-langid",
]
[[package]]
name = "fluent-langneg"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eebbe59450baee8282d71676f3bfed5689aeab00b27545e83e5f14b1195e8b0"
dependencies = [
"unic-langid",
]
[[package]]
name = "fluent-syntax"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
dependencies = [
"memchr 2.8.3",
"thiserror 2.0.18",
]
[[package]]
name = "foldhash"
version = "0.1.5"
@ -2172,6 +2302,83 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "hybrid-array"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
dependencies = [
"typenum",
]
[[package]]
name = "i18n-config"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef"
dependencies = [
"basic-toml",
"log",
"serde",
"serde_derive",
"thiserror 1.0.69",
"unic-langid",
]
[[package]]
name = "i18n-embed"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a217bbb075dcaefb292efa78897fc0678245ca67f265d12c351e42268fcb0305"
dependencies = [
"arc-swap",
"fluent",
"fluent-langneg",
"fluent-syntax",
"i18n-embed-impl",
"intl-memoizer",
"log",
"parking_lot",
"rust-embed",
"sys-locale",
"thiserror 1.0.69",
"unic-langid",
"walkdir",
"web-sys",
]
[[package]]
name = "i18n-embed-fl"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602e6bd30c3db2749e13e38b363a3d98d9d41de1d8de7a79c31bb69e45b47cda"
dependencies = [
"find-crate",
"fluent",
"fluent-syntax",
"i18n-config",
"i18n-embed",
"proc-macro-error3",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.119",
"unic-langid",
]
[[package]]
name = "i18n-embed-impl"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2"
dependencies = [
"find-crate",
"i18n-config",
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "iana-time-zone"
version = "0.1.65"
@ -2494,6 +2701,25 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "intl-memoizer"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f"
dependencies = [
"type-map",
"unic-langid",
]
[[package]]
name = "intl_pluralrules"
version = "7.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"
dependencies = [
"unic-langid",
]
[[package]]
name = "inventory"
version = "0.3.24"
@ -2872,7 +3098,7 @@ dependencies = [
"nom_locate",
"rand 0.9.5",
"rangemap",
"sha2",
"sha2 0.10.9",
"stringprep",
"thiserror 2.0.18",
"time",
@ -2993,7 +3219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest",
"digest 0.10.7",
]
[[package]]
@ -3029,6 +3255,22 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -4127,6 +4369,28 @@ dependencies = [
"version_check",
]
[[package]]
name = "proc-macro-error-attr3"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0084e6206a967a2dad822180626b2f6b07a3b379325e8f1ec0438e33a469ba7"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error3"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cf066225f2373bc711684792b69bdeac0356019b007e721090c24d92d5d5a50"
dependencies = [
"proc-macro-error-attr3",
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@ -4687,6 +4951,41 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rust-embed"
version = "8.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e7760e252aaba7b09f4be00e36476cf585bdb68a53552ac954cdf504ab4bc9"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcfc4d6f53af43755f7a723e4b6b8794fcce052a178dd8c6c1dadc5f5343097"
dependencies = [
"mime_guess",
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.119",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ffa149f6aa81b58a5b3011d01a857c4ed12c7a732d2c51947a4c7c692185f0"
dependencies = [
"sha2 0.11.0",
"walkdir",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -5013,8 +5312,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"cpufeatures 0.2.17",
"digest 0.10.7",
]
[[package]]
name = "sha2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
"digest 0.11.3",
]
[[package]]
@ -5354,6 +5664,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sys-locale"
version = "0.3.2"
@ -5514,6 +5835,17 @@ dependencies = [
"tracing",
]
[[package]]
name = "tinystr"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [
"displaydoc",
"serde_core",
"zerovec",
]
[[package]]
name = "tinyvec"
version = "1.12.0"
@ -5529,6 +5861,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "1.1.3+spec-1.1.0"
@ -5800,6 +6141,15 @@ dependencies = [
"core_maths",
]
[[package]]
name = "type-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
"rustc-hash 2.1.3",
]
[[package]]
name = "typenum"
version = "1.20.1"
@ -5823,6 +6173,25 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "unic-langid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
dependencies = [
"serde",
"tinystr",
]
[[package]]
name = "unicase"
version = "2.9.0"
@ -6885,7 +7254,7 @@ version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
dependencies = [
"toml",
"toml 1.1.3+spec-1.1.0",
"version_check",
]
@ -7099,12 +7468,28 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "zerofrom"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
[[package]]
name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
[[package]]
name = "zerovec"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [
"serde",
"zerofrom",
]
[[package]]
name = "zmij"
version = "1.0.23"

View file

@ -39,6 +39,9 @@ acadrust = { version = "0.4", features = ["serde"] }
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
flate2 = "1"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
i18n-embed = { version = "0.16", features = ["fluent-system"] }
i18n-embed-fl = "0.10"
rust-embed = "8"
inventory = "0.3"
truck-shapeops = { version = "0.4", optional = true }
rustc-hash = "2"
@ -64,6 +67,7 @@ iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] }
i18n-embed = { version = "0.16", features = ["desktop-requester"] }
rayon = "1"
open = "5"
printpdf = { version = "0.9.1", default-features = false }
@ -71,6 +75,7 @@ ureq = { version = "3", default-features = false, features = ["rustls", "platfor
[target.'cfg(target_arch = "wasm32")'.dependencies]
ocs_plugin_api = { path = "crates/ocs_plugin_api" }
i18n-embed = { version = "0.16", features = ["web-sys-requester"] }
console_error_panic_hook = "0.1"
log = "0.4"
getrandom = { version = "0.3", features = ["wasm_js"] }

5
i18n.toml Normal file
View file

@ -0,0 +1,5 @@
fallback_language = "en-US"
[fluent]
assets_dir = "locales"
domain = "opencadstudio"

View file

@ -0,0 +1,113 @@
language-system = Systemsprache
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = 简体中文
action-close = Schließen
action-options = Optionen
action-plugins = Plug-ins
ribbon-tab-draw = Zeichnen
ribbon-tab-annotate = Beschriften
ribbon-tab-insert = Einfügen
ribbon-tab-model = Modell
ribbon-tab-layout = Layout
ribbon-tab-manage = Verwalten
ribbon-tab-view = Ansicht
options-language-section = Sprache
options-language-label = Oberflächensprache:
options-open-save-section = Öffnen und Speichern
options-default-save-format-label = Standardspeicherformat:
options-default-save-format-help = Wird beim ersten Speichern einer neuen Zeichnung verwendet. Vorhandene Zeichnungen behalten Dateityp und Version bei.
options-theme-section = Design
options-theme-label = Anwendungsdesign:
options-theme-help = Das Ändern einer Grundfarbe aktiviert das benutzerdefinierte Design. Alle Komponentenfarben werden aus diesen sechs Farben erzeugt.
options-color-background = Hintergrund
options-color-text = Text
options-color-primary = Primär
options-color-success = Erfolg
options-color-warning = Warnung
options-color-danger = Gefahr
command-line-ready = Open CAD Studio ist bereit.
command-line-hint = Geben Sie einen Befehl ein oder verwenden Sie das Menüband. Öffnen Sie OBJ über die Registerkarte Einfügen.
command-line-label = Befehl:
command-line-literal-spaces = Wörtliche Leerzeichen: Das Leerzeichen bleibt in der Zeile, statt den Befehl auszuführen. Bleibt bis zum Ausschalten aktiv.
start-new-drawing = Neue Zeichnung
start-open-file = Datei öffnen…
start-donate = Spenden
start-send-feedback = Feedback senden
start-sponsors = Sponsoren
start-tutorials = Lernprogramme
start-loading-videos = Videos werden geladen…
start-videos-online = Videos werden aus dem Internet geladen.
start-open-playlist = Wiedergabeliste auf YouTube öffnen
start-discussions = Diskussionen
start-pinned = Angeheftet
start-loading-discussions = Diskussionen werden geladen…
start-discussions-online = Diskussionen werden von GitHub geladen.
start-open-discussions = Diskussionen auf GitHub öffnen
start-supporters = Unterstützer
start-support-on-patreon = Auf Patreon unterstützen
start-recent-files = Zuletzt verwendete Dateien
start-videos = Videos
start-welcome = Willkommen
start-recent-documents = Zuletzt verwendete Dokumente
start-no-recent-files = Geöffnete Dateien werden hier angezeigt.
start-browser-storage = Browserspeicher
start-keep-recent-files = Zuletzt verwendete Dateien behalten
modal-about = Info
modal-keyboard-shortcuts = Tastenkombinationen
modal-command-aliases = Befehlsaliase
modal-find-replace = Suchen und Ersetzen
modal-plugin-manager = Plug-in-Manager
modal-update-available = Aktualisierung verfügbar
modal-layer-manager = Layer-Manager
modal-layer-state-manager = Layerstatus-Manager
modal-edit-layer-state = Layerstatus bearbeiten
modal-plot = Plotten
modal-layout-manager = Layout-Manager
modal-scale-manager = Maßstabs-Manager
modal-annotation-object-scale = Maßstab des Beschriftungsobjekts
modal-plot-style-editor = Plotstileditor
modal-text-style-manager = Textstil-Manager
modal-multiline-style-manager = Mehrfachlinienstil-Manager
modal-table-style-manager = Tabellenstil-Manager
modal-multileader-style-manager = Multi-Führungslinienstil-Manager
modal-dimension-style-manager = Bemaßungsstil-Manager
modal-default-application = Standardanwendung
modal-save-warning = Speicherwarnung
modal-unable-save = Zeichnung kann nicht gespeichert werden
modal-drawing-changed = Zeichnung auf dem Datenträger geändert
modal-delete-layer = Layer löschen
modal-unsaved-changes = Nicht gespeicherte Änderungen
modal-point-style = Punktstil
modal-attribute-editor = Attribut-Editor
modal-save-drawing-as = Zeichnung speichern unter
command-move-base =
MOVE Basispunkt angeben [{ $count ->
[one] { $count } Objekt
*[other] { $count } Objekte
}]:
command-move-target = MOVE Zielpunkt angeben [Basis { $x },{ $y }]:
command-copy-array-count = COPY Anzahl der Elemente im Array eingeben:
command-copy-base =
COPY Basispunkt angeben [{ $count ->
[one] { $count } Objekt
*[other] { $count } Objekte
}]:
command-copy-array-target = COPY Zweiten Punkt für das Array mit { $count } Elementen angeben [Basis { $x },{ $y }]:
command-copy-target =
COPY Zielpunkt angeben [{ $count ->
[one] bisher { $count } Kopie
*[other] bisher { $count } Kopien
} | Array | Eingabe=fertig | Basis { $x },{ $y }]:

View file

@ -0,0 +1,113 @@
language-system = System language
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = Simplified Chinese
action-close = Close
action-options = Options
action-plugins = Plugins
ribbon-tab-draw = Draw
ribbon-tab-annotate = Annotate
ribbon-tab-insert = Insert
ribbon-tab-model = Model
ribbon-tab-layout = Layout
ribbon-tab-manage = Manage
ribbon-tab-view = View
options-language-section = Language
options-language-label = Interface language:
options-open-save-section = Open and Save
options-default-save-format-label = Default save format:
options-default-save-format-help = Used for the first save of a new drawing. Existing drawings keep their file type and version.
options-theme-section = Theme
options-theme-label = Application theme:
options-theme-help = Changing a base colour switches to Custom. The theme generates every component shade from these six colours.
options-color-background = Background
options-color-text = Text
options-color-primary = Primary
options-color-success = Success
options-color-warning = Warning
options-color-danger = Danger
command-line-ready = Open CAD Studio ready.
command-line-hint = Type a command or use the ribbon. Open OBJ from the Insert tab.
command-line-label = Command:
command-line-literal-spaces = Literal spaces: Space stays in the line instead of running the command. Stays on until toggled off.
start-new-drawing = New Drawing
start-open-file = Open File…
start-donate = Donate
start-send-feedback = Send Feedback
start-sponsors = Sponsors
start-tutorials = Tutorials
start-loading-videos = Loading videos…
start-videos-online = Videos load from the internet.
start-open-playlist = Open playlist on YouTube
start-discussions = Discussions
start-pinned = Pinned
start-loading-discussions = Loading discussions…
start-discussions-online = Discussions load from GitHub.
start-open-discussions = Open Discussions on GitHub
start-supporters = Supporters
start-support-on-patreon = Support on Patreon
start-recent-files = Recent Files
start-videos = Videos
start-welcome = Welcome
start-recent-documents = Recent Documents
start-no-recent-files = Files you open will show up here.
start-browser-storage = Browser storage
start-keep-recent-files = Keep recent files
modal-about = About
modal-keyboard-shortcuts = Keyboard Shortcuts
modal-command-aliases = Command Aliases
modal-find-replace = Find and Replace
modal-plugin-manager = Plugin Manager
modal-update-available = Update Available
modal-layer-manager = Layer Manager
modal-layer-state-manager = Layer State Manager
modal-edit-layer-state = Edit Layer State
modal-plot = Plot
modal-layout-manager = Layout Manager
modal-scale-manager = Scale Manager
modal-annotation-object-scale = Annotation Object Scale
modal-plot-style-editor = Plot Style Editor
modal-text-style-manager = Text Style Manager
modal-multiline-style-manager = Multiline Style Manager
modal-table-style-manager = Table Style Manager
modal-multileader-style-manager = Multileader Style Manager
modal-dimension-style-manager = Dimension Style Manager
modal-default-application = Default Application
modal-save-warning = Save Warning
modal-unable-save = Unable to Save Drawing
modal-drawing-changed = Drawing Changed on Disk
modal-delete-layer = Delete Layer
modal-unsaved-changes = Unsaved Changes
modal-point-style = Point Style
modal-attribute-editor = Attribute Editor
modal-save-drawing-as = Save Drawing As
command-move-base =
MOVE Specify base point [{ $count ->
[one] { $count } object
*[other] { $count } objects
}]:
command-move-target = MOVE Specify destination [base { $x },{ $y }]:
command-copy-array-count = COPY Enter number of items to array:
command-copy-base =
COPY Specify base point [{ $count ->
[one] { $count } object
*[other] { $count } objects
}]:
command-copy-array-target = COPY Specify second point for { $count }-item array [base { $x },{ $y }]:
command-copy-target =
COPY Specify destination [{ $count ->
[one] { $count } copy so far
*[other] { $count } copies so far
} | Array | Enter=done | base { $x },{ $y }]:

View file

@ -0,0 +1,113 @@
language-system = Langue du système
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = 简体中文
action-close = Fermer
action-options = Options
action-plugins = Extensions
ribbon-tab-draw = Dessin
ribbon-tab-annotate = Annoter
ribbon-tab-insert = Insérer
ribbon-tab-model = Modèle
ribbon-tab-layout = Présentation
ribbon-tab-manage = Gérer
ribbon-tab-view = Affichage
options-language-section = Langue
options-language-label = Langue de l'interface :
options-open-save-section = Ouvrir et enregistrer
options-default-save-format-label = Format d'enregistrement par défaut :
options-default-save-format-help = Utilisé lors du premier enregistrement d'un nouveau dessin. Les dessins existants conservent leur type de fichier et leur version.
options-theme-section = Thème
options-theme-label = Thème de l'application :
options-theme-help = La modification d'une couleur de base active le thème personnalisé. Le thème génère toutes les nuances des composants à partir de ces six couleurs.
options-color-background = Arrière-plan
options-color-text = Texte
options-color-primary = Principale
options-color-success = Succès
options-color-warning = Avertissement
options-color-danger = Danger
command-line-ready = Open CAD Studio est prêt.
command-line-hint = Saisissez une commande ou utilisez le ruban. Ouvrez un fichier OBJ depuis l'onglet Insérer.
command-line-label = Commande :
command-line-literal-spaces = Espaces littéraux : l'espace reste dans la ligne au lieu d'exécuter la commande. Ce mode reste actif jusqu'à sa désactivation.
start-new-drawing = Nouveau dessin
start-open-file = Ouvrir un fichier…
start-donate = Faire un don
start-send-feedback = Envoyer des commentaires
start-sponsors = Sponsors
start-tutorials = Tutoriels
start-loading-videos = Chargement des vidéos…
start-videos-online = Les vidéos sont chargées depuis Internet.
start-open-playlist = Ouvrir la playlist sur YouTube
start-discussions = Discussions
start-pinned = Épinglé
start-loading-discussions = Chargement des discussions…
start-discussions-online = Les discussions sont chargées depuis GitHub.
start-open-discussions = Ouvrir les discussions sur GitHub
start-supporters = Contributeurs
start-support-on-patreon = Soutenir sur Patreon
start-recent-files = Fichiers récents
start-videos = Vidéos
start-welcome = Bienvenue
start-recent-documents = Documents récents
start-no-recent-files = Les fichiers que vous ouvrez apparaîtront ici.
start-browser-storage = Stockage du navigateur
start-keep-recent-files = Conserver les fichiers récents
modal-about = À propos
modal-keyboard-shortcuts = Raccourcis clavier
modal-command-aliases = Alias de commandes
modal-find-replace = Rechercher et remplacer
modal-plugin-manager = Gestionnaire d'extensions
modal-update-available = Mise à jour disponible
modal-layer-manager = Gestionnaire de calques
modal-layer-state-manager = Gestionnaire d'états de calque
modal-edit-layer-state = Modifier l'état du calque
modal-plot = Tracer
modal-layout-manager = Gestionnaire de présentations
modal-scale-manager = Gestionnaire d'échelles
modal-annotation-object-scale = Échelle de l'objet annotatif
modal-plot-style-editor = Éditeur de styles de tracé
modal-text-style-manager = Gestionnaire de styles de texte
modal-multiline-style-manager = Gestionnaire de styles de multilignes
modal-table-style-manager = Gestionnaire de styles de tableau
modal-multileader-style-manager = Gestionnaire de styles de lignes de repère multiples
modal-dimension-style-manager = Gestionnaire de styles de cote
modal-default-application = Application par défaut
modal-save-warning = Avertissement d'enregistrement
modal-unable-save = Impossible d'enregistrer le dessin
modal-drawing-changed = Dessin modifié sur le disque
modal-delete-layer = Supprimer le calque
modal-unsaved-changes = Modifications non enregistrées
modal-point-style = Style de point
modal-attribute-editor = Éditeur d'attributs
modal-save-drawing-as = Enregistrer le dessin sous
command-move-base =
MOVE Spécifiez le point de base [{ $count ->
[one] { $count } objet
*[other] { $count } objets
}] :
command-move-target = MOVE Spécifiez la destination [base { $x },{ $y }] :
command-copy-array-count = COPY Saisissez le nombre d'éléments du réseau :
command-copy-base =
COPY Spécifiez le point de base [{ $count ->
[one] { $count } objet
*[other] { $count } objets
}] :
command-copy-array-target = COPY Spécifiez le second point du réseau de { $count } éléments [base { $x },{ $y }] :
command-copy-target =
COPY Spécifiez la destination [{ $count ->
[one] { $count } copie jusqu'ici
*[other] { $count } copies jusqu'ici
} | Réseau | Entrée=terminer | base { $x },{ $y }] :

View file

@ -0,0 +1,101 @@
language-system = सिस्टम भाषा
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = 简体中文
action-close = बंद करें
action-options = विकल्प
action-plugins = प्लग-इन
ribbon-tab-draw = ड्रॉ
ribbon-tab-annotate = एनोटेट
ribbon-tab-insert = इन्सर्ट
ribbon-tab-model = मॉडल
ribbon-tab-layout = लेआउट
ribbon-tab-manage = प्रबंधित करें
ribbon-tab-view = दृश्य
options-language-section = भाषा
options-language-label = इंटरफ़ेस भाषा:
options-open-save-section = खोलना और सहेजना
options-default-save-format-label = डिफ़ॉल्ट सहेजने का प्रारूप:
options-default-save-format-help = नई ड्रॉइंग को पहली बार सहेजते समय उपयोग होता है। मौजूदा ड्रॉइंग अपना फ़ाइल प्रकार और संस्करण बनाए रखती हैं।
options-theme-section = थीम
options-theme-label = अनुप्रयोग थीम:
options-theme-help = किसी मूल रंग को बदलने पर कस्टम थीम सक्रिय हो जाती है। थीम इन छह रंगों से सभी घटक शेड बनाती है।
options-color-background = पृष्ठभूमि
options-color-text = पाठ
options-color-primary = प्राथमिक
options-color-success = सफलता
options-color-warning = चेतावनी
options-color-danger = खतरा
command-line-ready = Open CAD Studio तैयार है।
command-line-hint = कोई कमांड लिखें या रिबन का उपयोग करें। OBJ को इन्सर्ट टैब से खोलें।
command-line-label = कमांड:
command-line-literal-spaces = वास्तविक स्पेस: स्पेस कमांड चलाने के बजाय पंक्ति में रहता है। दोबारा बंद करने तक यह चालू रहता है।
start-new-drawing = नई ड्रॉइंग
start-open-file = फ़ाइल खोलें…
start-donate = दान करें
start-send-feedback = प्रतिक्रिया भेजें
start-sponsors = प्रायोजक
start-tutorials = ट्यूटोरियल
start-loading-videos = वीडियो लोड हो रहे हैं…
start-videos-online = वीडियो इंटरनेट से लोड होते हैं।
start-open-playlist = YouTube पर प्लेलिस्ट खोलें
start-discussions = चर्चाएँ
start-pinned = पिन किया गया
start-loading-discussions = चर्चाएँ लोड हो रही हैं…
start-discussions-online = चर्चाएँ GitHub से लोड होती हैं।
start-open-discussions = GitHub पर चर्चाएँ खोलें
start-supporters = समर्थक
start-support-on-patreon = Patreon पर समर्थन करें
start-recent-files = हाल की फ़ाइलें
start-videos = वीडियो
start-welcome = स्वागत है
start-recent-documents = हाल के दस्तावेज़
start-no-recent-files = आपके द्वारा खोली गई फ़ाइलें यहाँ दिखाई देंगी।
start-browser-storage = ब्राउज़र संग्रहण
start-keep-recent-files = हाल की फ़ाइलें रखें
modal-about = परिचय
modal-keyboard-shortcuts = कीबोर्ड शॉर्टकट
modal-command-aliases = कमांड उपनाम
modal-find-replace = खोजें और बदलें
modal-plugin-manager = प्लग-इन प्रबंधक
modal-update-available = अपडेट उपलब्ध है
modal-layer-manager = लेयर प्रबंधक
modal-layer-state-manager = लेयर स्थिति प्रबंधक
modal-edit-layer-state = लेयर स्थिति संपादित करें
modal-plot = प्लॉट
modal-layout-manager = लेआउट प्रबंधक
modal-scale-manager = स्केल प्रबंधक
modal-annotation-object-scale = एनोटेशन ऑब्जेक्ट स्केल
modal-plot-style-editor = प्लॉट शैली संपादक
modal-text-style-manager = टेक्स्ट शैली प्रबंधक
modal-multiline-style-manager = मल्टीलाइन शैली प्रबंधक
modal-table-style-manager = तालिका शैली प्रबंधक
modal-multileader-style-manager = मल्टीलीडर शैली प्रबंधक
modal-dimension-style-manager = डाइमेंशन शैली प्रबंधक
modal-default-application = डिफ़ॉल्ट अनुप्रयोग
modal-save-warning = सहेजने की चेतावनी
modal-unable-save = ड्रॉइंग सहेजी नहीं जा सकती
modal-drawing-changed = डिस्क पर ड्रॉइंग बदल गई
modal-delete-layer = लेयर हटाएँ
modal-unsaved-changes = न सहेजे गए बदलाव
modal-point-style = बिंदु शैली
modal-attribute-editor = विशेषता संपादक
modal-save-drawing-as = ड्रॉइंग इस रूप में सहेजें
command-move-base = MOVE आधार बिंदु निर्दिष्ट करें [{ $count } ऑब्जेक्ट]:
command-move-target = MOVE गंतव्य निर्दिष्ट करें [आधार { $x },{ $y }]:
command-copy-array-count = COPY ऐरे में आइटमों की संख्या दर्ज करें:
command-copy-base = COPY आधार बिंदु निर्दिष्ट करें [{ $count } ऑब्जेक्ट]:
command-copy-array-target = COPY { $count }-आइटम ऐरे के लिए दूसरा बिंदु निर्दिष्ट करें [आधार { $x },{ $y }]:
command-copy-target = COPY गंतव्य निर्दिष्ट करें [अब तक { $count } प्रतियाँ | ऐरे | Enter=पूर्ण | आधार { $x },{ $y }]:

View file

@ -0,0 +1,113 @@
language-system = Systeemtaal
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = 简体中文
action-close = Sluiten
action-options = Opties
action-plugins = Plug-ins
ribbon-tab-draw = Tekenen
ribbon-tab-annotate = Annoteren
ribbon-tab-insert = Invoegen
ribbon-tab-model = Model
ribbon-tab-layout = Lay-out
ribbon-tab-manage = Beheren
ribbon-tab-view = Beeld
options-language-section = Taal
options-language-label = Interfacetaal:
options-open-save-section = Openen en opslaan
options-default-save-format-label = Standaard opslagformaat:
options-default-save-format-help = Wordt gebruikt wanneer een nieuwe tekening voor het eerst wordt opgeslagen. Bestaande tekeningen behouden hun bestandstype en versie.
options-theme-section = Thema
options-theme-label = Toepassingsthema:
options-theme-help = Als u een basiskleur wijzigt, wordt het aangepaste thema geactiveerd. Het thema maakt alle componenttinten op basis van deze zes kleuren.
options-color-background = Achtergrond
options-color-text = Tekst
options-color-primary = Primair
options-color-success = Geslaagd
options-color-warning = Waarschuwing
options-color-danger = Gevaar
command-line-ready = Open CAD Studio is gereed.
command-line-hint = Typ een opdracht of gebruik het lint. Open OBJ via het tabblad Invoegen.
command-line-label = Opdracht:
command-line-literal-spaces = Letterlijke spaties: een spatie blijft in de regel staan in plaats van de opdracht uit te voeren. Blijft actief tot u dit uitschakelt.
start-new-drawing = Nieuwe tekening
start-open-file = Bestand openen…
start-donate = Doneren
start-send-feedback = Feedback verzenden
start-sponsors = Sponsors
start-tutorials = Zelfstudies
start-loading-videos = Video's laden…
start-videos-online = Video's worden via internet geladen.
start-open-playlist = Afspeellijst openen op YouTube
start-discussions = Discussies
start-pinned = Vastgezet
start-loading-discussions = Discussies laden…
start-discussions-online = Discussies worden geladen vanaf GitHub.
start-open-discussions = Discussies openen op GitHub
start-supporters = Ondersteuners
start-support-on-patreon = Ondersteunen op Patreon
start-recent-files = Recente bestanden
start-videos = Video's
start-welcome = Welkom
start-recent-documents = Recente documenten
start-no-recent-files = Bestanden die u opent, verschijnen hier.
start-browser-storage = Browseropslag
start-keep-recent-files = Recente bestanden bewaren
modal-about = Info
modal-keyboard-shortcuts = Sneltoetsen
modal-command-aliases = Opdrachtaliassen
modal-find-replace = Zoeken en vervangen
modal-plugin-manager = Plug-inbeheer
modal-update-available = Update beschikbaar
modal-layer-manager = Lagenbeheer
modal-layer-state-manager = Laagstatusbeheer
modal-edit-layer-state = Laagstatus bewerken
modal-plot = Afdrukken
modal-layout-manager = Lay-outbeheer
modal-scale-manager = Schaalbeheer
modal-annotation-object-scale = Schaal van annotatieobject
modal-plot-style-editor = Afdrukstijleditor
modal-text-style-manager = Tekststijlbeheer
modal-multiline-style-manager = Meervoudige-lijnstijlbeheer
modal-table-style-manager = Tabelstijlbeheer
modal-multileader-style-manager = Multileaderstijlbeheer
modal-dimension-style-manager = Maatstijlbeheer
modal-default-application = Standaardtoepassing
modal-save-warning = Opslagwaarschuwing
modal-unable-save = Tekening kan niet worden opgeslagen
modal-drawing-changed = Tekening gewijzigd op schijf
modal-delete-layer = Laag verwijderen
modal-unsaved-changes = Niet-opgeslagen wijzigingen
modal-point-style = Puntstijl
modal-attribute-editor = Kenmerkeditor
modal-save-drawing-as = Tekening opslaan als
command-move-base =
MOVE Geef het basispunt op [{ $count ->
[one] { $count } object
*[other] { $count } objecten
}]:
command-move-target = MOVE Geef de bestemming op [basis { $x },{ $y }]:
command-copy-array-count = COPY Voer het aantal items in de matrix in:
command-copy-base =
COPY Geef het basispunt op [{ $count ->
[one] { $count } object
*[other] { $count } objecten
}]:
command-copy-array-target = COPY Geef het tweede punt voor de matrix met { $count } items op [basis { $x },{ $y }]:
command-copy-target =
COPY Geef de bestemming op [{ $count ->
[one] { $count } kopie tot nu toe
*[other] { $count } kopieën tot nu toe
} | Matrix | Enter=gereed | basis { $x },{ $y }]:

View file

@ -0,0 +1,116 @@
language-system = Язык системы
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = 简体中文
action-close = Закрыть
action-options = Параметры
action-plugins = Плагины
ribbon-tab-draw = Рисование
ribbon-tab-annotate = Аннотации
ribbon-tab-insert = Вставка
ribbon-tab-model = Модель
ribbon-tab-layout = Лист
ribbon-tab-manage = Управление
ribbon-tab-view = Вид
options-language-section = Язык
options-language-label = Язык интерфейса:
options-open-save-section = Открытие и сохранение
options-default-save-format-label = Формат сохранения по умолчанию:
options-default-save-format-help = Используется при первом сохранении нового чертежа. Существующие чертежи сохраняют тип и версию файла.
options-theme-section = Тема
options-theme-label = Тема приложения:
options-theme-help = Изменение базового цвета включает пользовательскую тему. Все оттенки компонентов создаются из этих шести цветов.
options-color-background = Фон
options-color-text = Текст
options-color-primary = Основной
options-color-success = Успех
options-color-warning = Предупреждение
options-color-danger = Опасность
command-line-ready = Open CAD Studio готов.
command-line-hint = Введите команду или используйте ленту. Откройте OBJ на вкладке «Вставка».
command-line-label = Команда:
command-line-literal-spaces = Буквальные пробелы: пробел остаётся в строке, а не запускает команду. Режим действует до отключения.
start-new-drawing = Новый чертёж
start-open-file = Открыть файл…
start-donate = Поддержать
start-send-feedback = Отправить отзыв
start-sponsors = Спонсоры
start-tutorials = Учебные материалы
start-loading-videos = Загрузка видео…
start-videos-online = Видео загружаются из Интернета.
start-open-playlist = Открыть плейлист на YouTube
start-discussions = Обсуждения
start-pinned = Закреплено
start-loading-discussions = Загрузка обсуждений…
start-discussions-online = Обсуждения загружаются с GitHub.
start-open-discussions = Открыть обсуждения на GitHub
start-supporters = Сторонники
start-support-on-patreon = Поддержать на Patreon
start-recent-files = Недавние файлы
start-videos = Видео
start-welcome = Добро пожаловать
start-recent-documents = Недавние документы
start-no-recent-files = Открытые вами файлы появятся здесь.
start-browser-storage = Хранилище браузера
start-keep-recent-files = Хранить недавние файлы
modal-about = О программе
modal-keyboard-shortcuts = Сочетания клавиш
modal-command-aliases = Псевдонимы команд
modal-find-replace = Найти и заменить
modal-plugin-manager = Диспетчер плагинов
modal-update-available = Доступно обновление
modal-layer-manager = Диспетчер слоёв
modal-layer-state-manager = Диспетчер состояний слоёв
modal-edit-layer-state = Изменить состояние слоя
modal-plot = Печать
modal-layout-manager = Диспетчер листов
modal-scale-manager = Диспетчер масштабов
modal-annotation-object-scale = Масштаб аннотативного объекта
modal-plot-style-editor = Редактор стилей печати
modal-text-style-manager = Диспетчер стилей текста
modal-multiline-style-manager = Диспетчер стилей мультилиний
modal-table-style-manager = Диспетчер стилей таблиц
modal-multileader-style-manager = Диспетчер стилей мультивыносок
modal-dimension-style-manager = Диспетчер стилей размеров
modal-default-application = Приложение по умолчанию
modal-save-warning = Предупреждение о сохранении
modal-unable-save = Не удалось сохранить чертёж
modal-drawing-changed = Чертёж изменён на диске
modal-delete-layer = Удалить слой
modal-unsaved-changes = Несохранённые изменения
modal-point-style = Стиль точек
modal-attribute-editor = Редактор атрибутов
modal-save-drawing-as = Сохранить чертёж как
command-move-base =
MOVE Укажите базовую точку [{ $count ->
[one] { $count } объект
[few] { $count } объекта
*[many] { $count } объектов
}]:
command-move-target = MOVE Укажите точку назначения [база { $x },{ $y }]:
command-copy-array-count = COPY Введите число элементов массива:
command-copy-base =
COPY Укажите базовую точку [{ $count ->
[one] { $count } объект
[few] { $count } объекта
*[many] { $count } объектов
}]:
command-copy-array-target = COPY Укажите вторую точку массива из { $count } элементов [база { $x },{ $y }]:
command-copy-target =
COPY Укажите точку назначения [{ $count ->
[one] создана { $count } копия
[few] создано { $count } копии
*[many] создано { $count } копий
} | Массив | Enter=готово | база { $x },{ $y }]:

View file

@ -0,0 +1,101 @@
language-system = Sistem dili
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = 简体中文
action-close = Kapat
action-options = Seçenekler
action-plugins = Eklentiler
ribbon-tab-draw = Çizim
ribbon-tab-annotate = Açıklama
ribbon-tab-insert = Ekle
ribbon-tab-model = Model
ribbon-tab-layout = Yerleşim
ribbon-tab-manage = Yönet
ribbon-tab-view = Görünüm
options-language-section = Dil
options-language-label = Arayüz dili:
options-open-save-section = Açma ve Kaydetme
options-default-save-format-label = Varsayılan kayıt biçimi:
options-default-save-format-help = Yeni bir çizimin ilk kaydında kullanılır. Mevcut çizimler dosya türünü ve sürümünü korur.
options-theme-section = Tema
options-theme-label = Uygulama teması:
options-theme-help = Temel renklerden birini değiştirmek Özel temaya geçer. Tema, tüm bileşen tonlarını bu altı renkten üretir.
options-color-background = Arka plan
options-color-text = Metin
options-color-primary = Ana renk
options-color-success = Başarı
options-color-warning = Uyarı
options-color-danger = Tehlike
command-line-ready = Open CAD Studio hazır.
command-line-hint = Bir komut yazın veya şeridi kullanın. OBJ dosyasını Ekle sekmesinden açın.
command-line-label = Komut:
command-line-literal-spaces = Değişmez boşluklar: Boşluk, komutu çalıştırmak yerine satırda kalır. Yeniden kapatılana kadar etkin kalır.
start-new-drawing = Yeni Çizim
start-open-file = Dosya Aç…
start-donate = Bağış Yap
start-send-feedback = Geri Bildirim Gönder
start-sponsors = Sponsorlar
start-tutorials = Eğitimler
start-loading-videos = Videolar yükleniyor…
start-videos-online = Videolar internetten yüklenir.
start-open-playlist = Oynatma listesini YouTube'da aç
start-discussions = Tartışmalar
start-pinned = Sabitlenmiş
start-loading-discussions = Tartışmalar yükleniyor…
start-discussions-online = Tartışmalar GitHub'dan yüklenir.
start-open-discussions = Tartışmaları GitHub'da Aç
start-supporters = Destekçiler
start-support-on-patreon = Patreon'da Destekle
start-recent-files = Son Dosyalar
start-videos = Videolar
start-welcome = Hoş Geldiniz
start-recent-documents = Son Belgeler
start-no-recent-files = Açtığınız dosyalar burada görünür.
start-browser-storage = Tarayıcı depolama alanı
start-keep-recent-files = Son dosyaları sakla
modal-about = Hakkında
modal-keyboard-shortcuts = Klavye Kısayolları
modal-command-aliases = Komut Kısaltmaları
modal-find-replace = Bul ve Değiştir
modal-plugin-manager = Eklenti Yöneticisi
modal-update-available = Güncelleme Var
modal-layer-manager = Katman Yöneticisi
modal-layer-state-manager = Katman Durumu Yöneticisi
modal-edit-layer-state = Katman Durumunu Düzenle
modal-plot = Yazdır
modal-layout-manager = Yerleşim Yöneticisi
modal-scale-manager = Ölçek Yöneticisi
modal-annotation-object-scale = Açıklama Nesnesi Ölçeği
modal-plot-style-editor = Yazdırma Stili Düzenleyicisi
modal-text-style-manager = Metin Stili Yöneticisi
modal-multiline-style-manager = Çoklu Çizgi Stili Yöneticisi
modal-table-style-manager = Tablo Stili Yöneticisi
modal-multileader-style-manager = Çoklu Kılavuz Stili Yöneticisi
modal-dimension-style-manager = Ölçülendirme Stili Yöneticisi
modal-default-application = Varsayılan Uygulama
modal-save-warning = Kayıt Uyarısı
modal-unable-save = Çizim Kaydedilemiyor
modal-drawing-changed = Çizim Diskte Değişti
modal-delete-layer = Katmanı Sil
modal-unsaved-changes = Kaydedilmemiş Değişiklikler
modal-point-style = Nokta Stili
modal-attribute-editor = Öznitelik Düzenleyicisi
modal-save-drawing-as = Çizimi Farklı Kaydet
command-move-base = MOVE Temel noktayı belirtin [{ $count } nesne]:
command-move-target = MOVE Hedef noktayı belirtin [temel { $x },{ $y }]:
command-copy-array-count = COPY Dizideki öğe sayısını girin:
command-copy-base = COPY Temel noktayı belirtin [{ $count } nesne]:
command-copy-array-target = COPY { $count } öğeli dizi için ikinci noktayı belirtin [temel { $x },{ $y }]:
command-copy-target = COPY Hedef noktayı belirtin [şimdiye kadar { $count } kopya | Dizi | Enter=tamam | temel { $x },{ $y }]:

View file

@ -0,0 +1,101 @@
language-system = 跟随系统
language-english = English
language-turkish = Türkçe
language-dutch = Nederlands
language-french = Français
language-german = Deutsch
language-hindi = हिन्दी
language-russian = Русский
language-chinese-simplified = 简体中文
action-close = 关闭
action-options = 选项
action-plugins = 插件
ribbon-tab-draw = 绘图
ribbon-tab-annotate = 注释
ribbon-tab-insert = 插入
ribbon-tab-model = 模型
ribbon-tab-layout = 布局
ribbon-tab-manage = 管理
ribbon-tab-view = 视图
options-language-section = 语言
options-language-label = 界面语言:
options-open-save-section = 打开和保存
options-default-save-format-label = 默认保存格式:
options-default-save-format-help = 用于新图形的首次保存。现有图形保留其文件类型和版本。
options-theme-section = 主题
options-theme-label = 应用程序主题:
options-theme-help = 更改基础颜色会切换到自定义主题。主题将从这六种颜色生成所有组件色调。
options-color-background = 背景
options-color-text = 文字
options-color-primary = 主要
options-color-success = 成功
options-color-warning = 警告
options-color-danger = 危险
command-line-ready = Open CAD Studio 已就绪。
command-line-hint = 输入命令或使用功能区。可从“插入”选项卡打开 OBJ。
command-line-label = 命令:
command-line-literal-spaces = 保留空格:空格会留在输入行中而不执行命令,直到再次切换此模式。
start-new-drawing = 新建图形
start-open-file = 打开文件…
start-donate = 捐赠
start-send-feedback = 发送反馈
start-sponsors = 赞助商
start-tutorials = 教程
start-loading-videos = 正在加载视频…
start-videos-online = 视频通过互联网加载。
start-open-playlist = 在 YouTube 上打开播放列表
start-discussions = 讨论
start-pinned = 已置顶
start-loading-discussions = 正在加载讨论…
start-discussions-online = 讨论从 GitHub 加载。
start-open-discussions = 在 GitHub 上打开讨论
start-supporters = 支持者
start-support-on-patreon = 在 Patreon 上支持
start-recent-files = 最近文件
start-videos = 视频
start-welcome = 欢迎
start-recent-documents = 最近使用的文档
start-no-recent-files = 您打开的文件将显示在此处。
start-browser-storage = 浏览器存储
start-keep-recent-files = 保留最近文件
modal-about = 关于
modal-keyboard-shortcuts = 键盘快捷键
modal-command-aliases = 命令别名
modal-find-replace = 查找和替换
modal-plugin-manager = 插件管理器
modal-update-available = 有可用更新
modal-layer-manager = 图层管理器
modal-layer-state-manager = 图层状态管理器
modal-edit-layer-state = 编辑图层状态
modal-plot = 打印
modal-layout-manager = 布局管理器
modal-scale-manager = 比例管理器
modal-annotation-object-scale = 注释对象比例
modal-plot-style-editor = 打印样式编辑器
modal-text-style-manager = 文字样式管理器
modal-multiline-style-manager = 多线样式管理器
modal-table-style-manager = 表格样式管理器
modal-multileader-style-manager = 多重引线样式管理器
modal-dimension-style-manager = 标注样式管理器
modal-default-application = 默认应用程序
modal-save-warning = 保存警告
modal-unable-save = 无法保存图形
modal-drawing-changed = 磁盘上的图形已更改
modal-delete-layer = 删除图层
modal-unsaved-changes = 未保存的更改
modal-point-style = 点样式
modal-attribute-editor = 属性编辑器
modal-save-drawing-as = 图形另存为
command-move-base = MOVE 指定基点 [{ $count } 个对象]
command-move-target = MOVE 指定目标点 [基点 { $x },{ $y }]
command-copy-array-count = COPY 输入阵列项目数:
command-copy-base = COPY 指定基点 [{ $count } 个对象]
command-copy-array-target = COPY 为 { $count } 项阵列指定第二点 [基点 { $x },{ $y }]
command-copy-target = COPY 指定目标点 [已复制 { $count } 个 | 阵列 | 回车=完成 | 基点 { $x },{ $y }]

View file

@ -855,6 +855,8 @@ pub(super) struct OpenCADStudio {
save_dialog_for_unsaved: bool,
/// User preference for the first save of a new/unsaved drawing.
default_save_format: String,
/// Persisted interface language selection.
language: crate::i18n::Language,
// ── DimStyle Dialog ───────────────────────────────────────────────────
/// Name of the style currently shown in the dialog.
@ -1602,6 +1604,8 @@ pub enum Message {
OptionsThemeChanged(String),
/// Edit one of Custom theme's six base colours as #RRGGBB.
OptionsThemeColorChanged(usize, String),
/// Switch the interface language and redraw localized views.
LanguageChanged(crate::i18n::Language),
ClearScene,
SetWireframe(bool),
/// Set the active tab's render mode (one of acadrust's seven visual
@ -2648,6 +2652,10 @@ impl OpenCADStudio {
}
fn new() -> Self {
let config = config::AppConfig::load();
if let Err(error) = crate::i18n::set_language(config.settings.language) {
eprintln!("Unable to apply saved UI language: {error}");
}
// Boot with only the Welcome/Start tab. The user creates drawings
// explicitly (File → New); we never auto-spawn Drawing1.
let start_tab = DocumentTab::new_start();
@ -2827,6 +2835,7 @@ impl OpenCADStudio {
save_dialog_filename: "drawing.dwg".to_string(),
save_dialog_for_unsaved: false,
default_save_format: crate::io::DEFAULT_SAVE_FORMAT.to_string(),
language: crate::i18n::Language::default(),
// Plot style
active_plot_style: crate::io::plot_style::PlotStyleTable::load_named(
crate::io::plot_style::DEFAULT_PLOT_STYLE,
@ -2997,7 +3006,7 @@ impl OpenCADStudio {
// so preferences, recents, status-bar layout, ribbon density and print
// options survive across sessions (issue #68). `last_saved_config` is
// seeded below so the first change — not the boot — triggers a write.
app.apply_config(config::AppConfig::load());
app.apply_config(config);
// Load command aliases from ocad.pgp (writes the shipped default file
// on first launch). The hide-set keeps aliases out of autocomplete while
// their target command still shows.

View file

@ -151,6 +151,9 @@ pub struct UserSettings {
/// (#188).
pub bg_color: Option<[u8; 3]>,
pub paper_bg_color: Option<[u8; 3]>,
/// Interface language preference. `System` negotiates against the
/// platform locale on every launch.
pub language: crate::i18n::Language,
}
impl Default for UserSettings {
@ -177,6 +180,7 @@ impl Default for UserSettings {
pick_drag_rect: false,
bg_color: None,
paper_bg_color: None,
language: crate::i18n::Language::default(),
}
}
}

View file

@ -288,6 +288,7 @@ impl OpenCADStudio {
pick_drag_rect: self.pick_drag_rect,
bg_color: self.default_bg_color.map(f4_to_u3),
paper_bg_color: self.default_paper_bg_color.map(f4_to_u3),
language: self.language,
}
}
@ -317,6 +318,9 @@ impl OpenCADStudio {
self.pick_drag_rect = s.pick_drag_rect;
self.default_bg_color = s.bg_color.map(u3_to_f4);
self.default_paper_bg_color = s.paper_bg_color.map(u3_to_f4);
if crate::i18n::set_language(s.language).is_ok() {
self.language = s.language;
}
// Push the restored background onto every drawing tab that exists now
// (the start tab and any initial drawing). Tabs created later pick it
// up via `apply_bg_default` at their construction site.

View file

@ -4323,6 +4323,22 @@ impl OpenCADStudio {
Task::none()
}
Message::LanguageChanged(language) => {
if self.language == language {
return Task::none();
}
match crate::i18n::set_language(language) {
Ok(()) => {
self.language = language;
self.persist_settings_if_changed();
}
Err(error) => self
.command_line
.push_error(&format!("Unable to change UI language: {error}")),
}
Task::none()
}
Message::AboutOpen => {
self.active_modal = Some(super::ModalKind::About);
Task::none()

View file

@ -2318,7 +2318,7 @@ fn start_page_content<'a>(
let headline = text("Open CAD Studio").size(40).style(start_primary_style);
// Plain outlined button (Open / New / Help / Contribute).
let outline_btn = |label: &'static str, msg: Message| {
let outline_btn = |label: String, msg: Message| {
button(text(label).size(14))
.on_press(msg)
.padding([10, 22])
@ -2346,7 +2346,7 @@ fn start_page_content<'a>(
button(
row![
crate::ui::icons::themed_danger_text(crate::ui::icons::HEART, 14.0),
text("Donate").size(14),
text(crate::tr!("start-donate")).size(14),
]
.spacing(5)
.align_y(iced::Center),
@ -2360,8 +2360,8 @@ fn start_page_content<'a>(
};
let primary_row = WrapFlow::new(vec![
outline_btn("New Drawing", Message::TabNew).into(),
outline_btn("Open File…", Message::OpenFile).into(),
outline_btn(crate::tr!("start-new-drawing"), Message::TabNew).into(),
outline_btn(crate::tr!("start-open-file"), Message::OpenFile).into(),
donate_btn.into(),
])
.spacing_x(12.0)
@ -2371,16 +2371,16 @@ fn start_page_content<'a>(
#[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
let mut secondary_items: Vec<Element<'a, Message>> = vec![
outline_btn(
"Send Feedback",
crate::tr!("start-send-feedback"),
Message::RibbonToolClick {
tool_id: "REPORT".to_string(),
event: crate::modules::ModuleEvent::Command("REPORT".to_string()),
},
)
.into(),
outline_btn("Options", Message::OptionsOpen).into(),
outline_btn(crate::tr!("action-options"), Message::OptionsOpen).into(),
];
secondary_items.push(outline_btn("Plugins", Message::PluginManagerOpen).into());
secondary_items.push(outline_btn(crate::tr!("action-plugins"), Message::PluginManagerOpen).into());
// The web build is already in the browser, so only the desktop offers a
// link to the web version.
#[cfg(not(target_arch = "wasm32"))]
@ -2403,7 +2403,7 @@ fn start_page_content<'a>(
.report_natural_width(action_width_out.clone());
let sponsors = column![
text("Sponsors").size(15),
text(crate::tr!("start-sponsors")).size(15),
mouse_area(
container(
iced::widget::svg(iced::widget::svg::Handle::from_memory(include_bytes!(
@ -2505,7 +2505,7 @@ fn start_page_content<'a>(
// whole thumbnail remains visible when that width changes.
let thumb_h =
(panel_w - VIDEO_PANEL_PADDING * 2.0 - VIDEO_SCROLL_GUTTER) * 9.0 / 16.0;
let mut list = column![text("Tutorials").size(15)]
let mut list = column![text(crate::tr!("start-tutorials")).size(15)]
.spacing(10)
.width(Fill)
// Keep the scrollbar off the thumbnails.
@ -2545,14 +2545,14 @@ fn start_page_content<'a>(
}
if videos.is_empty() {
let note = if videos_loading {
"Loading videos…"
crate::tr!("start-loading-videos")
} else {
"Videos load from the internet."
crate::tr!("start-videos-online")
};
list = list.push(text(note).size(12).style(start_muted_style));
}
let playlist_btn = mouse_area(
container(text("Open playlist on YouTube").size(12))
container(text(crate::tr!("start-open-playlist")).size(12))
.padding([6, 10])
.width(Fill)
.center_x(Fill)
@ -2605,7 +2605,7 @@ fn start_page_content<'a>(
// web builds read the CI-generated snapshot. Both sources mark pinned
// discussions and sort them before the rest of the list.
let discussions_panel: Element<'a, Message> = {
let mut list = column![text("Discussions").size(15)]
let mut list = column![text(crate::tr!("start-discussions")).size(15)]
.spacing(8)
.width(Fill);
for discussion in discussions {
@ -2618,7 +2618,7 @@ fn start_page_content<'a>(
.align_y(iced::Center);
if discussion.pinned {
meta = meta.push(
text("Pinned")
text(crate::tr!("start-pinned"))
.size(10)
.style(start_primary_style),
);
@ -2661,14 +2661,14 @@ fn start_page_content<'a>(
}
if discussions.is_empty() {
let note = if discussions_loading {
"Loading discussions…"
crate::tr!("start-loading-discussions")
} else {
"Discussions load from GitHub."
crate::tr!("start-discussions-online")
};
list = list.push(text(note).size(12).style(start_muted_style));
}
let open_btn = mouse_area(
container(text("Open Discussions on GitHub").size(12))
container(text(crate::tr!("start-open-discussions")).size(12))
.padding([6, 10])
.width(Fill)
.center_x(Fill)
@ -2725,7 +2725,7 @@ fn start_page_content<'a>(
// shows, so the rail always invites support.
let supporters: Element<'a, Message> = {
let mut list = column![
text("Supporters").size(15),
text(crate::tr!("start-supporters")).size(15),
Space::new().height(iced::Length::Fixed(12.0)),
]
.spacing(6)
@ -2746,7 +2746,7 @@ fn start_page_content<'a>(
container(
iced::widget::row![
crate::ui::icons::themed_danger_text(crate::ui::icons::HEART, 13.0),
text("Support on Patreon").size(12),
text(crate::tr!("start-support-on-patreon")).size(12),
]
.spacing(6)
.align_y(iced::Center),
@ -2831,7 +2831,7 @@ fn start_page_content<'a>(
.height(Fill)
.into(),
StartLayout::Compact => {
let tab_btn = |label: &'static str, section: super::StartSection| {
let tab_btn = |label: String, section: super::StartSection| {
let is_active = active == section;
button(text(label).size(14))
.on_press(Message::StartSectionSelect(section))
@ -2864,11 +2864,11 @@ fn start_page_content<'a>(
})
};
let tab_bar = Row::with_children(vec![
tab_btn("Recent Files", super::StartSection::Recent).into(),
tab_btn("Videos", super::StartSection::Videos).into(),
tab_btn("Welcome", super::StartSection::Welcome).into(),
tab_btn("Discussions", super::StartSection::Discussions).into(),
tab_btn("Supporters", super::StartSection::Supporters).into(),
tab_btn(crate::tr!("start-recent-files"), super::StartSection::Recent).into(),
tab_btn(crate::tr!("start-videos"), super::StartSection::Videos).into(),
tab_btn(crate::tr!("start-welcome"), super::StartSection::Welcome).into(),
tab_btn(crate::tr!("start-discussions"), super::StartSection::Discussions).into(),
tab_btn(crate::tr!("start-supporters"), super::StartSection::Supporters).into(),
])
.spacing(6.0)
.align_y(iced::Center)
@ -2944,11 +2944,11 @@ pub(super) fn recent_files_panel<'a>(
) -> Element<'a, Message> {
// Title mirrors the Supporters rail: size 15 in the bright text colour,
// followed by a 12px gap before the content.
let title = text("Recent Documents").size(15);
let title = text(crate::tr!("start-recent-documents")).size(15);
let body: Element<'a, Message> = if recents.is_empty() {
container(
text("Files you open will show up here.")
text(crate::tr!("start-no-recent-files"))
.size(12)
.style(start_muted_style)
)
@ -2976,7 +2976,7 @@ pub(super) fn recent_files_panel<'a>(
// directory line.
#[cfg(target_arch = "wasm32")]
let dir = if dir.is_empty() {
"Browser storage".to_string()
crate::tr!("start-browser-storage")
} else {
dir
};
@ -3087,7 +3087,7 @@ pub(super) fn recent_files_panel<'a>(
.padding([2, 6])
.width(iced::Length::Fixed(46.0));
let limit_row = row![
text("Keep recent files").size(11).style(start_muted_style).width(Fill),
text(crate::tr!("start-keep-recent-files")).size(11).style(start_muted_style).width(Fill),
button(crate::ui::icons::themed(crate::ui::icons::MINUS, 11.0))
.on_press(Message::SetRecentLimit(shown.saturating_sub(STEP)))
.padding([3, 6])

View file

@ -5,41 +5,41 @@ use iced::{Background, Element, Fill, Fit, Theme};
impl OpenCADStudio {
/// Title shown in the active modal's title bar. Keep in sync with the
/// [`Self::modal_content`] dispatch.
pub(super) fn modal_title(&self) -> &'static str {
pub(super) fn modal_title(&self) -> String {
use super::super::ModalKind as K;
match self.active_modal {
Some(K::About) => "About",
Some(K::Shortcuts) => "Keyboard Shortcuts",
Some(K::Aliases) => "Command Aliases",
Some(K::Options) => "Options",
Some(K::FindReplace) => "Find and Replace",
Some(K::PluginManager) => "Plugin Manager",
Some(K::UpdateNotice) => "Update Available",
Some(K::Layers) => "Layer Manager",
Some(K::LayerStateManager) => "Layer State Manager",
Some(K::LayerStateEditor) => "Edit Layer State",
Some(K::Plot) => "Plot",
Some(K::LayoutManager) => "Layout Manager",
Some(K::ScaleManager) => "Scale Manager",
Some(K::AnnoObjectScale) => "Annotation Object Scale",
Some(K::Plotstyle) => "Plot Style Editor",
Some(K::TextStyle) => "Text Style Manager",
Some(K::MlStyle) => "Multiline Style Manager",
Some(K::TableStyle) => "Table Style Manager",
Some(K::MLeaderStyle) => "Multileader Style Manager",
Some(K::DimStyle) => "Dimension Style Manager",
Some(K::AssocPrompt) => "Default Application",
Some(K::AecDropWarning) => "Save Warning",
Some(K::About) => crate::tr!("modal-about"),
Some(K::Shortcuts) => crate::tr!("modal-keyboard-shortcuts"),
Some(K::Aliases) => crate::tr!("modal-command-aliases"),
Some(K::Options) => crate::tr!("action-options"),
Some(K::FindReplace) => crate::tr!("modal-find-replace"),
Some(K::PluginManager) => crate::tr!("modal-plugin-manager"),
Some(K::UpdateNotice) => crate::tr!("modal-update-available"),
Some(K::Layers) => crate::tr!("modal-layer-manager"),
Some(K::LayerStateManager) => crate::tr!("modal-layer-state-manager"),
Some(K::LayerStateEditor) => crate::tr!("modal-edit-layer-state"),
Some(K::Plot) => crate::tr!("modal-plot"),
Some(K::LayoutManager) => crate::tr!("modal-layout-manager"),
Some(K::ScaleManager) => crate::tr!("modal-scale-manager"),
Some(K::AnnoObjectScale) => crate::tr!("modal-annotation-object-scale"),
Some(K::Plotstyle) => crate::tr!("modal-plot-style-editor"),
Some(K::TextStyle) => crate::tr!("modal-text-style-manager"),
Some(K::MlStyle) => crate::tr!("modal-multiline-style-manager"),
Some(K::TableStyle) => crate::tr!("modal-table-style-manager"),
Some(K::MLeaderStyle) => crate::tr!("modal-multileader-style-manager"),
Some(K::DimStyle) => crate::tr!("modal-dimension-style-manager"),
Some(K::AssocPrompt) => crate::tr!("modal-default-application"),
Some(K::AecDropWarning) => crate::tr!("modal-save-warning"),
#[cfg(not(target_arch = "wasm32"))]
Some(K::FileInUse) => "Unable to Save Drawing",
Some(K::FileInUse) => crate::tr!("modal-unable-save"),
#[cfg(not(target_arch = "wasm32"))]
Some(K::ExternalChange) => "Drawing Changed on Disk",
Some(K::LayerDeleteWarning) => "Delete Layer",
Some(K::Unsaved) => "Unsaved Changes",
Some(K::PointStyle) => "Point Style",
Some(K::AttributeEditor) => "Attribute Editor",
Some(K::SaveDialog) => "Save Drawing As",
None => "",
Some(K::ExternalChange) => crate::tr!("modal-drawing-changed"),
Some(K::LayerDeleteWarning) => crate::tr!("modal-delete-layer"),
Some(K::Unsaved) => crate::tr!("modal-unsaved-changes"),
Some(K::PointStyle) => crate::tr!("modal-point-style"),
Some(K::AttributeEditor) => crate::tr!("modal-attribute-editor"),
Some(K::SaveDialog) => crate::tr!("modal-save-drawing-as"),
None => String::new(),
}
}
@ -90,6 +90,7 @@ impl OpenCADStudio {
&self.default_save_format,
&self.ui_theme,
&self.theme_color_inputs,
self.language,
flow,
)
},

153
src/i18n.rs Normal file
View file

@ -0,0 +1,153 @@
//! Application language selection and embedded Fluent resources.
use i18n_embed::fluent::{fluent_language_loader, FluentLanguageLoader};
use i18n_embed::LanguageLoader;
#[cfg(not(target_arch = "wasm32"))]
use i18n_embed::DesktopLanguageRequester;
#[cfg(target_arch = "wasm32")]
use i18n_embed::WebLanguageRequester;
use rust_embed::RustEmbed;
use serde::{Deserialize, Serialize};
use std::sync::OnceLock;
#[derive(RustEmbed)]
#[folder = "locales/"]
struct Localizations;
/// User-selectable UI language. `System` keeps following the platform's
/// preferred locale while explicit choices remain stable across restarts.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum Language {
#[default]
#[serde(rename = "system")]
System,
#[serde(rename = "en-US")]
EnUs,
#[serde(rename = "tr-TR")]
TrTr,
#[serde(rename = "nl-NL")]
NlNl,
#[serde(rename = "fr-FR")]
FrFr,
#[serde(rename = "de-DE")]
DeDe,
#[serde(rename = "hi-IN")]
HiIn,
#[serde(rename = "ru-RU")]
RuRu,
#[serde(rename = "zh-CN")]
ZhCn,
}
impl Language {
pub const ALL: [Language; 9] = [
Language::System,
Language::EnUs,
Language::TrTr,
Language::NlNl,
Language::FrFr,
Language::DeDe,
Language::HiIn,
Language::RuRu,
Language::ZhCn,
];
fn requested(self) -> Vec<i18n_embed::unic_langid::LanguageIdentifier> {
match self {
Language::System => system_languages(),
Language::EnUs => vec!["en-US".parse().expect("valid locale")],
Language::TrTr => vec!["tr-TR".parse().expect("valid locale")],
Language::NlNl => vec!["nl-NL".parse().expect("valid locale")],
Language::FrFr => vec!["fr-FR".parse().expect("valid locale")],
Language::DeDe => vec!["de-DE".parse().expect("valid locale")],
Language::HiIn => vec!["hi-IN".parse().expect("valid locale")],
Language::RuRu => vec!["ru-RU".parse().expect("valid locale")],
Language::ZhCn => vec!["zh-CN".parse().expect("valid locale")],
}
}
}
impl std::fmt::Display for Language {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let label = match self {
Language::System => crate::tr!("language-system"),
Language::EnUs => crate::tr!("language-english"),
Language::TrTr => crate::tr!("language-turkish"),
Language::NlNl => crate::tr!("language-dutch"),
Language::FrFr => crate::tr!("language-french"),
Language::DeDe => crate::tr!("language-german"),
Language::HiIn => crate::tr!("language-hindi"),
Language::RuRu => crate::tr!("language-russian"),
Language::ZhCn => crate::tr!("language-chinese-simplified"),
};
f.write_str(&label)
}
}
fn system_languages() -> Vec<i18n_embed::unic_langid::LanguageIdentifier> {
#[cfg(target_arch = "wasm32")]
let requested = WebLanguageRequester::requested_languages();
#[cfg(not(target_arch = "wasm32"))]
let requested = DesktopLanguageRequester::requested_languages();
requested
}
fn load_language(
loader: &FluentLanguageLoader,
language: Language,
) -> Result<(), i18n_embed::I18nEmbedError> {
let mut requested = language.requested();
if requested.is_empty() {
requested.push(loader.fallback_language().clone());
}
i18n_embed::select(loader, &Localizations, &requested).map(|_| ())
}
pub fn loader() -> &'static FluentLanguageLoader {
static LOADER: OnceLock<FluentLanguageLoader> = OnceLock::new();
LOADER.get_or_init(|| {
let loader = fluent_language_loader!();
if let Err(error) = load_language(&loader, Language::System) {
eprintln!("Unable to load system UI language: {error}");
loader
.load_languages(&Localizations, &[loader.fallback_language().clone()])
.expect("fallback UI language must be embedded");
}
loader
})
}
/// Apply a user preference process-wide. The loader swaps resources atomically,
/// so the next Iced view pass immediately receives the new language.
pub fn set_language(language: Language) -> Result<(), i18n_embed::I18nEmbedError> {
load_language(loader(), language)
}
/// Built-in ribbon modules have stable ids; plug-ins keep their supplied title
/// until they provide their own localization bundle.
pub fn ribbon_module_title(id: &str, fallback: &str) -> String {
match id {
"draw" => crate::tr!("ribbon-tab-draw"),
"annotate" => crate::tr!("ribbon-tab-annotate"),
"insert" => crate::tr!("ribbon-tab-insert"),
"model" => crate::tr!("ribbon-tab-model"),
"layout" => crate::tr!("ribbon-tab-layout"),
"manage" => crate::tr!("ribbon-tab-manage"),
"view" => crate::tr!("ribbon-tab-view"),
_ => fallback.to_string(),
}
}
#[macro_export]
macro_rules! tr {
($message_id:literal $(,)?) => {
i18n_embed_fl::fl!($crate::i18n::loader(), $message_id)
};
($message_id:literal, $($name:ident = $value:expr),+ $(,)?) => {
i18n_embed_fl::fl!(
$crate::i18n::loader(),
$message_id,
$($name = $value),+
)
};
}

View file

@ -6,6 +6,7 @@ pub mod config;
pub mod cli;
pub mod command;
pub mod entities;
pub mod i18n;
pub mod io;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) mod network;

View file

@ -10,6 +10,7 @@ mod config;
mod cli;
mod command;
mod entities;
mod i18n;
mod io;
#[cfg(not(target_arch = "wasm32"))]
mod network;

View file

@ -61,23 +61,27 @@ impl CadCommand for CopyCommand {
fn prompt(&self) -> String {
if self.awaiting_count {
return "COPY Enter number of items to array:".to_string();
return crate::tr!("command-copy-array-count");
}
match &self.step {
Step::Base => format!(
"COPY Specify base point [{} objects]:",
self.handles.len()
Step::Base => crate::tr!(
"command-copy-base",
count = (self.handles.len() as i64),
),
Step::Placing(base) => {
if let Some(n) = self.array_count {
format!(
"COPY Specify second point for {n}-item array [base {:.3},{:.3}]:",
base.x, base.y
crate::tr!(
"command-copy-array-target",
count = (n as i64),
x = format!("{:.3}", base.x),
y = format!("{:.3}", base.y),
)
} else {
format!(
"COPY Specify destination [{} copies so far | Array | Enter=done | base {:.3},{:.3}]:",
self.count, base.x, base.y
crate::tr!(
"command-copy-target",
count = (self.count as i64),
x = format!("{:.3}", base.x),
y = format!("{:.3}", base.y),
)
}
}

View file

@ -53,13 +53,14 @@ impl CadCommand for MoveCommand {
fn prompt(&self) -> String {
match &self.step {
Step::Base => format!(
"MOVE Specify base point [{} objects]:",
self.handles.len()
Step::Base => crate::tr!(
"command-move-base",
count = (self.handles.len() as i64),
),
Step::Target(base) => format!(
"MOVE Specify destination [base {:.3},{:.3}]:",
base.x, base.y
Step::Target(base) => crate::tr!(
"command-move-target",
x = format!("{:.3}", base.x),
y = format!("{:.3}", base.y),
),
}
}

View file

@ -111,8 +111,8 @@ pub enum EntryKind {
impl CommandLine {
pub fn new() -> Self {
let mut cl = Self::default();
cl.push_info("Open CAD Studio ready.");
cl.push_info("Type a command or use the ribbon. Open OBJ: INSERT tab.");
cl.push_info(&crate::tr!("command-line-ready"));
cl.push_info(&crate::tr!("command-line-hint"));
cl
}
@ -430,7 +430,7 @@ impl CommandLine {
}
});
let prompt = container(
text("Command:").size(11).style(|theme: &Theme| iced::widget::text::Style {
text(crate::tr!("command-line-label")).size(11).style(|theme: &Theme| iced::widget::text::Style {
color: Some(theme.palette().success.base.color),
}),
)
@ -467,10 +467,7 @@ impl CommandLine {
..Default::default()
}
});
let literal_tip = container(
text("Literal spaces: Space stays in the line instead of running the command (same as typing a leading '>'). Stays on until toggled off.")
.size(11),
)
let literal_tip = container(text(crate::tr!("command-line-literal-spaces")).size(11))
.padding([3, 6])
.style(container::bordered_box);
let literal_btn = tooltip(literal_btn, literal_tip, tooltip::Position::Top).gap(4);

View file

@ -253,7 +253,7 @@ pub fn backdrop<'a>(
/// title bar; pass `Vector::ZERO` to keep it centred.
pub fn modal<'a>(
base: impl Into<Element<'a, Message>>,
title: &'a str,
title: impl iced::widget::text::IntoFragment<'a>,
content: impl Into<Element<'a, Message>>,
on_close: Message,
offset: Vector,

View file

@ -422,7 +422,7 @@ impl Ribbon {
let is_active = i == self.active;
let is_contextual = module.id() == "layout";
let btn = container(
button(text(module.title()).size(12))
button(text(crate::i18n::ribbon_module_title(module.id(), module.title())).size(12))
.on_press(Message::RibbonSelectTab(i))
.style(move |theme: &Theme, status| {
let palette = theme.palette();

View file

@ -9,6 +9,7 @@ pub fn view_window<'a>(
default_save_format: &'a str,
ui_theme: &'a UiThemeConfig,
theme_color_inputs: &'a [String; 6],
language: crate::i18n::Language,
sizing: crate::ui::modal::ModalSizing,
) -> Element<'a, Message> {
let selected_format = crate::io::SAVE_FORMAT_OPTIONS
@ -25,12 +26,12 @@ pub fn view_window<'a>(
let palette = ui_theme.palette.to_iced();
let colors = [
("Background", palette.background),
("Text", palette.text),
("Primary", palette.primary),
("Success", palette.success),
("Warning", palette.warning),
("Danger", palette.danger),
(crate::tr!("options-color-background"), palette.background),
(crate::tr!("options-color-text"), palette.text),
(crate::tr!("options-color-primary"), palette.primary),
(crate::tr!("options-color-success"), palette.success),
(crate::tr!("options-color-warning"), palette.warning),
(crate::tr!("options-color-danger"), palette.danger),
];
let mut color_controls = column![].spacing(8);
@ -60,16 +61,31 @@ pub fn view_window<'a>(
);
}
let close = button(text("Close").size(12))
let close = button(text(crate::tr!("action-close")).size(12))
.on_press(Message::CloseModal)
.padding([6, 18])
.style(button::secondary);
let content = column![
text("Open and Save").size(15),
text(crate::tr!("options-language-section")).size(15),
Space::new().height(10),
row![
text("Default save format:").size(12).width(150),
text(crate::tr!("options-language-label")).size(12).width(150),
iced::widget::pick_list(
Some(language),
crate::i18n::Language::ALL,
|value| value.to_string(),
)
.on_select(Message::LanguageChanged)
.width(sizing.width),
]
.spacing(12)
.align_y(iced::Center),
Space::new().height(22),
text(crate::tr!("options-open-save-section")).size(15),
Space::new().height(10),
row![
text(crate::tr!("options-default-save-format-label")).size(12).width(150),
iced::widget::pick_list(
selected_format,
crate::io::SAVE_FORMAT_OPTIONS,
@ -81,16 +97,14 @@ pub fn view_window<'a>(
.spacing(12)
.align_y(iced::Center),
Space::new().height(8),
text(
"Used for the first save of a new drawing. Existing drawings keep their file type and version."
)
text(crate::tr!("options-default-save-format-help"))
.size(11)
.width(sizing.width),
Space::new().height(22),
text("Theme").size(15),
text(crate::tr!("options-theme-section")).size(15),
Space::new().height(10),
row![
text("Iced theme:").size(12).width(150),
text(crate::tr!("options-theme-label")).size(12).width(150),
iced::widget::pick_list(
selected_theme,
theme_options,
@ -102,9 +116,7 @@ pub fn view_window<'a>(
.spacing(12)
.align_y(iced::Center),
Space::new().height(8),
text(
"Changing a base colour switches to Custom. Iced generates every component shade from these six colours."
)
text(crate::tr!("options-theme-help"))
.size(11)
.width(sizing.width),
Space::new().height(12),