diff --git a/README.md b/README.md index 9b67078b..9fb4aa97 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ The desktop app also offers a loopback-only control channel (`figcraft --control - **Its own renderer:** tiny-skia on every core for the canvas and exports, with layer blending, effects and masks; booleans from linesweeper, geometry from kurbo, text from skrifa and HarfRust. - **Runs in the browser:** the whole engine and UI compile to WebAssembly. - **Accessible:** screen readers get names, roles, values and states for the tools, layer rows, fields, dropdowns, tabs and menus (AccessKit on desktop), F6 moves the keyboard between the toolbar, layers, canvas and properties (Tab within them, Escape back to the canvas), the system's reduce-motion setting is honoured, and panel text meets WCAG AA contrast in both themes. -- **Languages:** English and Spanish (a draft waiting for a native speaker's review), picked in Preferences or taken from the system or browser and switched live; adding a language is one catalog file ([`docs/translating.md`](docs/translating.md)). +- **Languages:** English, Russian (reviewed by VanTan), and Spanish, French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts, picked in Preferences or taken from the system or browser and switched live; adding a language is one catalog file ([`docs/translating.md`](docs/translating.md)). - **Plugins:** sandboxed WebAssembly plugins (a metered, pure-Rust interpreter with a memory cap and no file or network access, in the browser too) that act through the same commands, each run one undo step, within the permissions their manifest asks for. A Rust SDK and two example plugins are in [`plugins/`](plugins); see [`docs/plugins.md`](docs/plugins.md). - **Live sessions (preview):** share the open file from the app (Share ▸ Start sharing: online through a server, or on your local network), give the others the short join code it shows (or the invite link), and edit it together: everyone's changes sync live, with cursors, selections, avatars and follow, and undo takes back only your own changes. Others join from the desktop app (invite links open it) or from the browser build. See [`docs/collab-protocol.md`](docs/collab-protocol.md). - **A server of your own (preview):** `figcraft-server` keeps a team's files, publishes them from the app (File ▸ Publish to server…, or Share ▸ Online with your server in Preferences) and hands out share links and join codes that let people edit or only view; files survive restarts and apps reconnect where they were. No accounts yet: links are the access. Releases ship it for Linux, and the desktop app joins over `wss://`; [hosting it on a rented Linux server](docs/server.md#deploy-on-a-linux-server) takes a systemd unit and Caddy or nginx (or Docker). See [`docs/server.md`](docs/server.md). diff --git a/ROADMAP.md b/ROADMAP.md index 46c0fd29..9b2a7b8e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,7 +2,7 @@ **Stage: alpha** · next: beta, ~26 points and ~162–292 h away (one Opus 5.5 agent; ~33–67 h wall-clock with 5 agents) -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (recent files as cards with thumbnails on the home screen; community fixes: lost images reported on loading (#207), SVG import budgets (#190), Leptos code (#223), full screen (#227), server snapshots readable again (#188), owner-only preferences (#200), ruzstd 0.9.1 (#182), layer cycle repair (#205), batch rollback, Direct3D 12 on Windows, plugin metering (#192); vector edit mode regions: hover hatch, selection and drag; SVG/PDF exports refuse over-depth documents (#208); bounded MCP/control framing, connections and queues (#201); plugin log limits (#191); live-session revert guard (#198); attached MCP no longer replays after a lost reply (#194); repeat command recursion fix; the Layout panel's constrain-proportions link scales the other side, #220; core editor leftovers: component playground, auto gap handles, variables inside styles, inner shadows and background blur on groups, command-made text readable on dark pages, a one-step frame/group convert, delays on mouse triggers and manual overlay offsets, wrapped-line alignment; `.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; starter templates, a Templates tab and Getting started tips on the home screen; your online files; everyday editing partials: number fields, outline options, Hide when publishing, cursors, panel widths, dark pages; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; a public figcraft-server: open hosting, owner tokens, per-address limits, idle expiry, moderation, install.sh; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; Windows QA pass with eight fixes; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%) · **Target:** Figma Design (desktop app 126.10.7) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (Russian catalog and integer plural forms; recent files as cards with thumbnails on the home screen; community fixes: lost images reported on loading (#207), SVG import budgets (#190), Leptos code (#223), full screen (#227), server snapshots readable again (#188), owner-only preferences (#200), ruzstd 0.9.1 (#182), layer cycle repair (#205), batch rollback, Direct3D 12 on Windows, plugin metering (#192); vector edit mode regions: hover hatch, selection and drag; SVG/PDF exports refuse over-depth documents (#208); bounded MCP/control framing, connections and queues (#201); plugin log limits (#191); live-session revert guard (#198); attached MCP no longer replays after a lost reply (#194); repeat command recursion fix; the Layout panel's constrain-proportions link scales the other side, #220; core editor leftovers: component playground, auto gap handles, variables inside styles, inner shadows and background blur on groups, command-made text readable on dark pages, a one-step frame/group convert, delays on mouse triggers and manual overlay offsets, wrapped-line alignment; `.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; starter templates, a Templates tab and Getting started tips on the home screen; your online files; everyday editing partials: number fields, outline options, Hide when publishing, cursors, panel widths, dark pages; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; a public figcraft-server: open hosting, owner tokens, per-address limits, idle expiry, moderation, install.sh; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; Windows QA pass with eight fixes; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%) · **Target:** Figma Design (desktop app 126.10.7) FigCraft is a clean-room, open-source, pure-Rust reimplementation of the Figma design workflow, native on macOS, Windows, Linux and FreeBSD and in the browser, fully drivable by agents through @@ -63,7 +63,7 @@ native speakers' reviews of the six language drafts need people. | Stability | 55% | 20–40 | [gaps](docs/gaps.md) | | Performance | 60% | 30–60 | [hardware-parity](docs/hardware-parity.md) | | Hardware | 60% | 10–21 | [hardware-parity](docs/hardware-parity.md) | -| Localization | 39% (layer; English; Spanish with a Latin American overlay, French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese drafts; CJK UI fonts) | 29–51 | [localization-parity](docs/localization-parity.md) | +| Localization | 39% (layer; English; Spanish with a Latin American overlay, French, German, Brazilian Portuguese, Japanese, Korean, Simplified Chinese drafts; reviewed Russian; CJK UI fonts) | 29–51 | [localization-parity](docs/localization-parity.md) | | Platforms | 75% | 10–20 | [hardware-parity](docs/hardware-parity.md) | | Ecosystem / plugins | 38% (own WASM plugins with panels, settings and widgets; a self-hosted plugin directory) | 8–26 | [gaps](docs/gaps.md) | | AI features | 0% | 30–60 | [gaps](docs/gaps.md) | @@ -99,6 +99,7 @@ native speakers' reviews of the six language drafts need people. | Language | Code | Strings | Status | |---|---|---|---| | English | en | 100% | full | +| Russian | ru | 100% (2,165 entries) | wording reviewed by VanTan | | Simplified Chinese | zh-Hans | 100% | draft (needs native review) | | Spanish | es, es-419 | 100% (es-419: an overlay of 98 entries) | draft (needs native review) | | Hindi | hi | 0% | none | @@ -111,9 +112,9 @@ native speakers' reviews of the six language drafts need people. | Korean | ko | 100% | draft (needs native review) | | Vietnamese | vi | 0% | none | -No other languages. Figma ships English, Japanese, Korean, French, German, Spanish and Portuguese +Figma ships English, Japanese, Korean, French, German, Spanish and Portuguese (Brazil); FigCraft has the localization layer, English and Spanish (Spain and Latin America), -French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts (Preferences ▸ +French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts, and Russian reviewed by VanTan (Preferences ▸ Language; Traditional Chinese has no catalog; CJK UI text uses an installed CJK font, loaded only when needed; adding a language: [`docs/translating.md`](docs/translating.md)). Document text in any script (bidi, Arabic shaping, @@ -312,6 +313,7 @@ Ranked; detail and milestones in [`docs/roadmap.md`](docs/roadmap.md). | Date | Change | Summary | |---|---|---| +| 2026-10-14 | minor | Russian UI catalog (M9): 1,577 UI keys, 557 command labels and 31 menu wordings, reviewed and approved by VanTan. Preferences lists Русский; ru regional tags follow it. Russian integer plurals use one/few/many with other as fallback; existing languages keep their rules. Contributor display name VanTan added. Headline estimates unchanged. | | 2026-10-14 | minor | Home screen recent files as cards (M7): each card shows the picture its file carries on its first page's colour, the file's icon, name and "Edited ... ago"; sort by name, creation or last change either way; a table view; a click selects, a double-click or Enter opens, arrow keys move; Open, Show in folder and Remove from recents on right-click. Numbers unchanged. | | 2026-10-14 | minor | Lost images reported on loading (#207): a `.figcraft` file whose image member is damaged, too large or missing no longer loads in silence; each lost image (up to 20, then a count) is listed in `repairs` on `file.open` with its hash and the reason, and the app shows it as a toast. Saving still writes the file without the lost images. Numbers unchanged | | 2026-10-14 | minor | SVG import budgets (#190): gzipped SVG is unpacked by FigCraft within 64 MiB before parsing (it was unpacked without a limit), so the nesting check sees the real document; SVG documents embedded in `` data URIs share one 32 MiB budget and at most 4 levels, and one past a budget is left out. Linked files stay unread. Numbers unchanged | diff --git a/contributors/people.toml b/contributors/people.toml index e61cfd31..30482465 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -16,3 +16,6 @@ # # To remove your names, delete your entry (or set fields empty) in a PR. The next # contributors.py run, and the next build, drop them. + +[people.VanTan666] +display_name = "VanTan" diff --git a/crates/engine/src/cmd/view.rs b/crates/engine/src/cmd/view.rs index 6135e5df..4cd44e56 100644 --- a/crates/engine/src/cmd/view.rs +++ b/crates/engine/src/cmd/view.rs @@ -369,18 +369,24 @@ pub fn specs() -> Vec { Ok(json!({"url": s.prefs.publish_server, "default": crate::live::DEFAULT_SERVER})) } ), - spec!("pref.language", "Interface language", "{language: system|en|es|es-419|fr|de|pt-BR|ja|ko|zh-Hans|…; else English}", false, |s, p| { - let lang = req_string("pref.language", p, "language")?; - let lang = lang.trim(); - // A BCP 47-style tag ("es", "pt-BR", "zh_Hans") or "system". - let tag = lang.len() <= 35 - && lang.split(['-', '_']).all(|part| !part.is_empty() && part.len() <= 8 && part.chars().all(|c| c.is_ascii_alphanumeric())); - if !tag { - return Err(bad("pref.language", format!("`{lang}` is not a language code"))); + spec!( + "pref.language", + "Interface language", + "{language: system|en|ru|es|es-419|fr|de|pt-BR|ja|ko|zh-Hans|…; else English}", + false, + |s, p| { + let lang = req_string("pref.language", p, "language")?; + let lang = lang.trim(); + // A BCP 47-style tag ("es", "pt-BR", "zh_Hans") or "system". + let tag = lang.len() <= 35 + && lang.split(['-', '_']).all(|part| !part.is_empty() && part.len() <= 8 && part.chars().all(|c| c.is_ascii_alphanumeric())); + if !tag { + return Err(bad("pref.language", format!("`{lang}` is not a language code"))); + } + s.prefs.language = lang.to_string(); + Ok(json!({"language": s.prefs.language})) } - s.prefs.language = lang.to_string(); - Ok(json!({"language": s.prefs.language})) - }), + ), spec!("app.preferences", "Preferences", "{}", false, |s, _| ui(s, "dialog.preferences")), spec!("app.actions", "Actions", "{}", false, |s, _| ui(s, "palette.actions")), spec!("help.shortcuts", "Keyboard shortcuts", "{}", false, |s, _| ui(s, "panel.shortcuts")), diff --git a/crates/ui-egui/build.rs b/crates/ui-egui/build.rs index 368ea626..d5a4d3f0 100644 --- a/crates/ui-egui/build.rs +++ b/crates/ui-egui/build.rs @@ -71,7 +71,7 @@ fn locales() -> String { o } -/// The rows of one catalog table: `"key" = "text"` or `"key" = { one = "…", other = "…" }`. +/// The rows of one catalog table: strings or plural forms (`other` required). fn table(o: &mut String, path: &std::path::Path, t: Option<&toml::Value>) { let Some(t) = t.and_then(toml::Value::as_table) else { return }; // Sorted by key (byte order), as the lookup's binary search needs. @@ -81,8 +81,16 @@ fn table(o: &mut String, path: &std::path::Path, t: Option<&toml::Value>) { let s = |k: &str| v.get(k).and_then(toml::Value::as_str); match (v.as_str(), s("one"), s("other")) { (Some(text), ..) => _ = writeln!(o, " ({k:?}, Text::Plain({text:?})),"), - (None, one, Some(other)) => _ = writeln!(o, " ({k:?}, Text::Plural({}, {other:?})),", name(one)), - _ => println!("cargo::warning={}: `{k}` is neither a string nor {{ one, other }}; left out", path.display()), + (None, one, Some(other)) => { + _ = writeln!( + o, + " ({k:?}, Text::Plural {{ one: {}, few: {}, many: {}, other: {other:?} }}),", + name(one), + name(s("few")), + name(s("many")), + ); + } + _ => println!("cargo::warning={}: `{k}` is neither a string nor plural forms with `other`; left out", path.display()), } } } diff --git a/crates/ui-egui/locales/ru.toml b/crates/ui-egui/locales/ru.toml new file mode 100644 index 00000000..3ab40757 --- /dev/null +++ b/crates/ui-egui/locales/ru.toml @@ -0,0 +1,2178 @@ +# FigCraft interface strings: Russian. +# Wording reviewed and approved by VanTan. +# Keep Frame, Dev Mode and Smart animate as familiar tool names. +# See docs/translating.md; plural forms follow Russian integer rules. + +[meta] +name = "Русский" + +[ui] +"% of all commits" = "% всех коммитов" +"(no longer published)" = "(публикация снята)" +"({n} warnings: some entries were skipped)" = { one = "({n} предупреждение: некоторые записи пропущены)", few = "({n} предупреждения: некоторые записи пропущены)", many = "({n} предупреждений: некоторые записи пропущены)", other = "({n} предупреждения: некоторые записи пропущены)" } +"+ Create variable" = "+ Создать переменную" +".fig (experimental)" = ".fig (экспериментально)" +"1…9, 0" = "1…9, 0" +"@{login}: {prs} PRs, {commits} commits, +{added} / −{deleted} lines (Δ {delta}), +{bin_added} / −{bin_deleted} binary assets, {first} – {last}" = "@{login}: PR: {prs}, коммитов: {commits}, +{added} / −{deleted} строк (Δ {delta}), +{bin_added} / −{bin_deleted} бинарных ресурсов, {first} – {last}" +"A collection of this file with these modes and variables: override their values per mode and add variables of its own" = "Коллекция этого файла с этими режимами и переменными: переопределяйте значения для каждого режима и добавляйте собственные переменные" +"A responsive product page: hero, features, figures, a quote and a footer" = "Адаптивная страница продукта: главный блок, возможности, показатели, цитата и подвал" +"A spring plays until it settles: its mass, stiffness and damping set how long" = "Пружинная анимация длится до затухания: её время задают масса, жёсткость и демпфирование" +"A title, an agenda, a big figure, a chart and a closing slide" = "Заголовок, повестка, крупный показатель, диаграмма и заключительный слайд" +"A to Z" = "От А до Я" +"About" = "О программе" +"About FigCraft" = "О FigCraft" +"About this server" = "Об этом сервере" +"Action" = "Действие" +"Actions" = "Действия" +"Actions for {name}" = "Действия для {name}" +"Actions…" = "Действия…" +"Active" = "Активно" +"Actual size (100%)" = "Фактический размер (100%)" +"Add a category" = "Добавить категорию" +"Add a comment" = "Добавить комментарий" +"Add a description" = "Добавить описание" +"Add a frame (F) or a shape (R) to get started" = "Для начала добавьте Frame (F) или фигуру (R)" +"Add a layer or component to tile first" = "Сначала добавьте слой или компонент для повторения" +"Add a note for developers" = "Добавить заметку для разработчиков" +"Add action" = "Добавить действие" +"Add actions for when no condition holds" = "Добавить действия на случай, если ни одно условие не выполнено" +"Add an annotation for developers" = "Добавить аннотацию для разработчиков" +"Add another condition" = "Добавить ещё одно условие" +"Add auto layout" = "Добавить автолейаут" +"Add background behind overlay" = "Добавить фон за оверлеем" +"Add content to this slot" = "Добавить содержимое в этот слот" +"Add dev resource" = "Добавить ресурс для разработки" +"Add effect" = "Добавить эффект" +"Add export setting" = "Добавить настройку экспорта" +"Add fill" = "Добавить заливку" +"Add interaction" = "Добавить взаимодействие" +"Add layout grid" = "Добавить сетку макета" +"Add library" = "Добавить библиотеку" +"Add library file…" = "Добавить файл библиотеки…" +"Add max height" = "Добавить максимальную высоту" +"Add max width" = "Добавить максимальную ширину" +"Add min height" = "Добавить минимальную высоту" +"Add min width" = "Добавить минимальную ширину" +"Add mode" = "Добавить режим" +"Add new page" = "Добавить страницу" +"Add or update collections from a variables JSON" = "Добавить или обновить коллекции из JSON переменных" +"Add properties to make instances configurable" = "Добавьте свойства, чтобы настраивать экземпляры" +"Add reaction" = "Добавить реакцию" +"Add starting point" = "Добавить начальную точку" +"Add stroke" = "Добавить обводку" +"Add variant" = "Добавить вариант" +"Add variant with a new value" = "Добавить вариант с новым значением" +"Added" = "Добавлено" +"Added the library \"{name}\"" = "Добавлена библиотека «{name}»" +"Adjust" = "Коррекция" +"Adjustment presets" = "Пресеты коррекции" +"Advanced" = "Дополнительно" +"Advanced stroke" = "Расширенные настройки обводки" +"After delay" = "После задержки" +"After delay {ms}ms" = "После задержки {ms} мс" +"Align" = "Выравнивание" +"Align bottom" = "Выровнять по нижнему краю" +"Align center" = "Выровнять по центру" +"Align horizontal centers" = "Выровнять центры по горизонтали" +"Align left" = "Выровнять по левому краю" +"Align middle" = "Выровнять по середине" +"Align right" = "Выровнять по правому краю" +"Align text baselines" = "Выровнять базовые линии текста" +"Align top" = "Выровнять по верхнему краю" +"Align vertical centers" = "Выровнять центры по вертикали" +"Alignment" = "Выравнивание" +"All" = "Все" +"All categories" = "Все категории" +"All comments" = "Все комментарии" +"All components" = "Все компоненты" +"All fonts" = "Все шрифты" +"All supported properties" = "Все поддерживаемые свойства" +"All variables" = "Все переменные" +"Alpha" = "Альфа" +"Alphabetical" = "По алфавиту" +"Alt+drag" = "Alt+перетаскивание" +"An analytics screen on a grid: figures, charts drawn from vectors and a table" = "Экран аналитики на сетке: показатели, векторные диаграммы и таблица" +"An invite link that opens FigCraft and joins" = "Ссылка приглашения, которая открывает FigCraft и подключает к сессии" +"Anchor" = "Привязка" +"Android" = "Android" +"Angular" = "Угловой" +"Animation" = "Анимация" +"Annotations" = "Аннотации" +"Annotations shown on the canvas" = "Аннотации на холсте" +"Another server, or one that needs its key. The usual server is set in Preferences." = "Другой сервер или сервер, требующий ключ. Основной сервер задаётся в настройках." +"Any status" = "Любой статус" +"Anyone on your network with this code can edit the file, while this app stays open." = "Любой в вашей сети с этим кодом может редактировать файл, пока приложение открыто." +"Anyone with the edit link can change the file; the view link only lets people look. The links carry their secrets: share them like passwords. `figcraft-server links` makes more and revokes them." = "По ссылке для редактирования можно изменять файл, по ссылке для просмотра — только смотреть. Ссылки содержат секреты: передавайте их как пароли. `figcraft-server links` создаёт новые ссылки и отзывает старые." +"Anyone with this code can edit the file." = "Любой с этим кодом может редактировать файл." +"Anyone with this code can look at the file, not change it." = "Любой с этим кодом может просматривать файл без возможности редактирования." +"Appearance" = "Внешний вид" +"Apply a component property" = "Применить свойство компонента" +"Apply variable" = "Применить переменную" +"Apply variable to text" = "Применить переменную к тексту" +"Apply variable to visibility" = "Применить переменную к видимости" +"Apps" = "Приложения" +"Apr" = "апр." +"Arc" = "Дуга" +"Arc ratio" = "Соотношение дуги" +"Arc start" = "Начало дуги" +"Arc sweep" = "Угол дуги" +"Archive" = "Архивировать" +"Archived" = "В архиве" +"Arrange" = "Упорядочить" +"Arrow" = "Стрелка" +"As typed" = "Как введено" +"Asking the server…" = "Запрос к серверу…" +"Assets" = "Ресурсы" +"Aug" = "авг." +"Auto" = "Авто" +"Auto (space around)" = "Авто (отступы вокруг)" +"Auto (space between)" = "Авто (отступы между)" +"Auto (space evenly)" = "Авто (равные отступы)" +"Auto ({mode})" = "Авто ({mode})" +"Auto columns" = "Автостолбцы" +"Auto gap" = "Автоинтервал" +"Auto height" = "Автовысота" +"Auto layout" = "Автолейаут" +"Auto rows" = "Автостроки" +"Auto width" = "Автоширина" +"Back to the FigCraft server" = "Вернуться к серверу FigCraft" +"Back to the font's value" = "Вернуть значение шрифта" +"Back" = "Назад" +"Back to Ready for dev" = "Снова готово к разработке" +"Back to Ready for dev: the next change compares with how it is now" = "Снова готово к разработке: следующие изменения сравниваются с текущим состоянием" +"Back to assets" = "Назад к ресурсам" +"Back to current" = "Вернуться к текущему" +"Back to styles" = "Назад к стилям" +"Back to the canvas from a focused region" = "Вернуться из выбранной области к холсту" +"Back to the current version" = "Вернуться к текущей версии" +"Back to the values the font style implies" = "Вернуть значения стиля шрифта" +"Background" = "Фон" +"Background blur" = "Размытие фона" +"Basics" = "Основы" +"Before" = "До" +"Bend" = "Изгиб" +"Bevel" = "Скошенное" +"Binary assets added" = "Добавлено бинарных ресурсов" +"Binary assets removed" = "Удалено бинарных ресурсов" +"Black & white" = "Чёрно-белое" +"Bleed" = "Вылет за обрез" +"Bleed marks" = "Метки вылета" +"Bleed marks need a bleed" = "Для меток вылета нужно задать вылет" +"Blend" = "Смешивание" +"Blend mode" = "Режим наложения" +"Blue" = "Синий" +"Blur" = "Размытие" +"Boolean" = "Логическое" +"Boolean groups" = "Булевы группы" +"Both directions" = "Оба направления" +"Bottom" = "Снизу" +"Bottom left radius" = "Радиус нижнего левого угла" +"Bottom right radius" = "Радиус нижнего правого угла" +"Branch" = "Ветка" +"Branch archived" = "Ветка архивирована" +"Branch merged" = "Ветка объединена" +"Branch name" = "Имя ветки" +"Branch of {file}" = "Ветка файла {file}" +"Branch of {file} · merged or archived" = "Ветка файла {file} · объединена или архивирована" +"Branch updated from main" = "Ветка обновлена из основной" +"Branch {name}" = "Ветка {name}" +"Branches" = "Ветки" +"Bound to {name}. Click to detach the variable." = "Привязано к {name}. Нажмите, чтобы отвязать переменную." +"Bring forward" = "На уровень вперёд" +"Bring the main file's changes into the branch" = "Перенести изменения основного файла в ветку" +"Bring to front" = "На передний план" +"Bristle" = "Щетина" +"Browse" = "Обзор" +"Brush" = "Кисть" +"Brush rotation jitter" = "Колебание угла кисти" +"Brush scatter" = "Рассеивание кисти" +"Brush size jitter" = "Колебание размера кисти" +"Brush spacing" = "Интервал кисти" +"Bulleted list" = "Маркированный список" +"C or Shift+C" = "C или Shift+C" +"C or ⇧C" = "C или ⇧C" +"CMYK colour" = "Цвет CMYK" +"Can view" = "Может просматривать" +"Checking the server…" = "Проверка сервера…" +"Clear from this list" = "Убрать из этого списка" +"Code or invite link" = "Код или ссылка приглашения" +"Color profile: {name}" = "Цветовой профиль: {name}" +"Colour and text styles, Light and Dark variables, buttons, inputs and cards" = "Цветовые и текстовые стили, переменные светлой и тёмной тем, кнопки, поля ввода и карточки" +"CSS custom properties…" = "Пользовательские свойства CSS…" +"Can edit" = "Может редактировать" +"Can't read this library: {e}" = "Не удалось прочитать библиотеку: {e}" +"Cancel" = "Отмена" +"Canvas" = "Холст" +"Canvas stacking" = "Порядок наложения на холсте" +"Cap height to baseline" = "От высоты прописных до базовой линии" +"Case" = "Регистр" +"Case-sensitive forms" = "Формы с учётом регистра" +"Categories" = "Категории" +"Center" = "По центру" +"Change alias" = "Изменить алиас" +"Change resolved" = "Изменение разрешено" +"Change style" = "Изменить стиль" +"Change to" = "Переключить на" +"Change to needs a layer inside a variant" = "Для переключения нужен слой внутри варианта" +"Changed" = "Изменено" +"Changed on both sides: keep one" = "Изменено с обеих сторон: выберите один вариант" +"Changes on main" = "Изменения в основной ветке" +"Changes on the branch" = "Изменения в ветке" +"Changes unknown" = "Изменения неизвестны" +"Choose React, Leptos, SwiftUI or Compose above to connect this component to its code." = "Выберите выше React, Leptos, SwiftUI или Compose, чтобы связать компонент с его кодом." +"Choose another profile to change it." = "Для смены выберите другой профиль." +"Choose collection" = "Выбрать коллекцию" +"Choose image" = "Выбрать изображение" +"Choose mode" = "Выбрать режим" +"Choose variable" = "Выбрать переменную" +"Circle" = "Круг" +"Clear recent" = "Очистить недавние" +"Click + to replace mixed content" = "Нажмите +, чтобы заменить смешанное содержимое" +"Click anywhere on the canvas to leave one." = "Нажмите в любом месте холста, чтобы оставить комментарий." +"Click or drag to merge · {alt} removes" = "Нажмите или перетащите для объединения · {alt} удаляет" +"Click or drag to remove · {alt} merges" = "Нажмите или перетащите для удаления · {alt} объединяет" +"Click to record a key" = "Нажмите для записи клавиши" +"Clip content" = "Обрезать содержимое" +"Clip to shape" = "Обрезать по фигуре" +"Close" = "Закрыть" +"Close overlay" = "Закрыть оверлей" +"Close preview" = "Закрыть предпросмотр" +"Close tab" = "Закрыть вкладку" +"Close version history" = "Закрыть историю версий" +"Close when clicking outside" = "Закрывать при нажатии снаружи" +"Close without saving" = "Закрыть без сохранения" +"Code" = "Код" +"Code syntax" = "Синтаксис кода" +"Collapse" = "Свернуть" +"Collapse panel" = "Свернуть панель" +"Collection" = "Коллекция" +"Collections" = "Коллекции" +"Color" = "Цвет" +"Color burn" = "Затемнение основы" +"Color dodge" = "Осветление основы" +"Color format" = "Формат цвета" +"Color profile" = "Цветовой профиль" +"Color styles" = "Цветовые стили" +"Color values in this file are in this color space." = "Значения цветов в этом файле заданы в этом цветовом пространстве." +"Color variables" = "Цветовые переменные" +"Colour" = "Цвет" +"Column" = "Столбец" +"Column gap" = "Интервал между столбцами" +"Column sizes" = "Размеры столбцов" +"Column span" = "Объединение столбцов" +"Columns" = "Столбцы" +"Columns {count}" = "Столбцы: {count}" +"Comment" = "Комментарий" +"Comments" = "Комментарии" +"Commits" = "Коммиты" +"Company" = "Компания" +"Compare changes" = "Сравнить изменения" +"Completed" = "Завершено" +"Component" = "Компонент" +"Component name" = "Имя компонента" +"Component property" = "Свойство компонента" +"Component set" = "Набор компонентов" +"Component sets" = "Наборы компонентов" +"Components" = "Компоненты" +"Conditional" = "Условие" +"Confetti" = "Конфетти" +"Conflicts" = "Конфликты" +"Connect to code" = "Связать с кодом" +"Connected" = "Подключено" +"Connected to code: {from}" = "Связано с кодом: {from}" +"Connection lost, reconnecting (attempt {attempt})…" = "Соединение потеряно, переподключение (попытка {attempt})…" +"Constrain proportions" = "Сохранять пропорции" +"Constraints" = "Ограничения" +"Content" = "Содержимое" +"Contents hidden" = "Содержимое скрыто" +"Contents only" = "Только содержимое" +"Contextual alternates" = "Контекстные альтернативы" +"Contrast" = "Контраст" +"Contributors" = "Участники" +"Contributors: {people} · Commits: {commits}" = "Участники: {people} · Коммиты: {commits}" +"Controller" = "Контроллер" +"Cool" = "Холодный" +"Copied as CSS" = "Скопировано как CSS" +"Copied as PNG" = "Скопировано как PNG" +"Copied as SVG" = "Скопировано как SVG" +"Copied as text" = "Скопировано как текст" +"Copied {value}" = "Скопировано: {value}" +"Copy" = "Копировать" +"Copy as" = "Копировать как" +"Copy as CSS" = "Копировать как CSS" +"Copy as PNG" = "Копировать как PNG" +"Copy as SVG" = "Копировать как SVG" +"Copy code" = "Копировать код" +"Copy link" = "Копировать ссылку" +"Copy the {label}" = "Копировать: {label}" +"Copy {value}" = "Копировать {value}" +"Corner radius" = "Радиус углов" +"Corner smoothing" = "Сглаживание углов" +"Couldn't reach {server}: showing what this computer remembers." = "Не удалось связаться с {server}: показаны данные, сохранённые на этом компьютере." +"Count" = "Количество" +"Create" = "Создать" +"Create a variable collection first" = "Сначала создайте коллекцию переменных" +"Create a variable first" = "Сначала создайте переменную" +"Create alias" = "Создать алиас" +"Create branch" = "Создать ветку" +"Create collection" = "Создать коллекцию" +"Create component" = "Создать компонент" +"Create component property" = "Создать свойство компонента" +"Create components, styles or variables to publish them as a library." = "Создайте компоненты, стили или переменные, чтобы опубликовать их как библиотеку." +"Create property…" = "Создать свойство…" +"Create style" = "Создать стиль" +"Create style from the selected text" = "Создать стиль из выделенного текста" +"Created" = "Создано" +"Crop" = "Обрезка" +"Crop marks" = "Метки обреза" +"Ctrl+arrows" = "Ctrl+стрелки" +"Ctrl+click" = "Ctrl+нажатие" +"Ctrl+scroll" = "Ctrl+прокрутка" +"Current name" = "Текущее имя" +"Current version" = "Текущая версия" +"Cursor chat" = "Чат у курсора" +"Custom" = "Пользовательский" +"Cut" = "Вырезать" +"Dark" = "Тёмная" +"Darken" = "Затемнение" +"Dash" = "Штрих" +"Dashboard" = "Панель аналитики" +"Date created" = "Дата создания" +"Dec" = "дек." +"Decoration style" = "Стиль оформления" +"Decoration" = "Оформление" +"Deep select" = "Глубокое выделение" +"Default" = "По умолчанию" +"Default content" = "Содержимое по умолчанию" +"Default value (the top-left variant)" = "Значение по умолчанию (верхний левый вариант)" +"Delay" = "Задержка" +"Delete" = "Удалить" +"Delete category (its annotations stay)" = "Удалить категорию (аннотации сохранятся)" +"Delete collection" = "Удалить коллекцию" +"Delete group" = "Удалить группу" +"Delete mode" = "Удалить режим" +"Delete page" = "Удалить страницу" +"Delete preset" = "Удалить пресет" +"Delete property" = "Удалить свойство" +"Delete the style (layers keep their values)" = "Удалить стиль (значения слоёв сохранятся)" +"Delete thread" = "Удалить обсуждение" +"Delete variable" = "Удалить переменную" +"Delete “{name}”? This can't be undone." = "Удалить «{name}»? Это действие нельзя отменить." +"Density" = "Плотность" +"Depth" = "Глубина" +"Describe how and when to use this component" = "Опишите, как и когда использовать этот компонент" +"Describe what changed" = "Опишите изменения" +"Description" = "Описание" +"Description (optional)" = "Описание (необязательно)" +"Design" = "Дизайн" +"Design system starter" = "Основа дизайн-системы" +"Desktop" = "Компьютер" +"Destination" = "Назначение" +"Detach alias" = "Отвязать алиас" +"Detach instance" = "Отсоединить экземпляр" +"Detach property" = "Отвязать свойство" +"Detach style" = "Отвязать стиль" +"Detach styles" = "Отвязать стили" +"Detach variable" = "Отвязать переменную" +"Details" = "Подробности" +"Dev Mode" = "Dev Mode" +"Dev resources" = "Ресурсы для разработки" +"Development" = "Разработка" +"Device" = "Устройство" +"Device at 100%" = "Устройство в масштабе 100%" +"Diamond" = "Ромб" +"Difference" = "Разница" +"Dimensions" = "Размеры" +"Direction" = "Направление" +"Disconnect from code" = "Отвязать от кода" +"Dispersion" = "Дисперсия" +"Display name" = "Отображаемое имя" +"Dissolve" = "Растворение" +"Distribute and tidy" = "Распределить и упорядочить" +"Distribute horizontal spacing" = "Распределить по горизонтали" +"Distribute vertical spacing" = "Распределить по вертикали" +"Divider" = "Разделитель" +"Don't truncate" = "Не обрезать" +"Done" = "Готово" +"Done editing" = "Завершить редактирование" +"Dots" = "Точки" +"Dotted" = "Точечный" +"Down" = "Вниз" +"Drafts" = "Черновики" +"Drag the stroke to add a width point" = "Перетащите обводку, чтобы добавить точку ширины" +"Draw" = "Рисование" +"Draw a frame" = "Нарисовать Frame" +"Drawing tools" = "Инструменты рисования" +"Drop shadow" = "Внешняя тень" +"Dual-licensed MIT OR Apache-2.0. Made by the ArtCraft team and community." = "Двойная лицензия MIT OR Apache-2.0. Создано командой и сообществом ArtCraft." +"Duo" = "Дуо" +"Duplicate" = "Дублировать" +"Duplicate by dragging" = "Дублировать перетаскиванием" +"Duplicate mode" = "Дублировать режим" +"Duplicate page" = "Дублировать страницу" +"Edit" = "Правка" +"Edit as an expression" = "Редактировать как выражение" +"Edit colour" = "Изменить цвет" +"Edit comment" = "Изменить комментарий" +"Edit dev resource" = "Изменить ресурс для разработки" +"Edit link" = "Ссылка для редактирования" +"Edit link copied" = "Ссылка для редактирования скопирована" +"Edit style" = "Изменить стиль" +"Edit variable" = "Изменить переменную" +"Edit with the builder" = "Редактировать в конструкторе" +"Edited {when}" = "Изменено {when}" +"Effect settings" = "Настройки эффекта" +"Effect styles" = "Стили эффектов" +"Effects" = "Эффекты" +"Ellipse" = "Эллипс" +"Ellipses" = "Эллипсы" +"Else" = "Иначе" +"Else if" = "Иначе если" +"Embedded profile" = "Встроенный профиль" +"Empty" = "Пусто" +"End" = "Конец" +"End point" = "Конечная точка" +"Ends the session; everyone keeps their copy" = "Завершает сессию; у всех остаются свои копии" +"Enter" = "Enter" +"Enter the address of a figcraft-server and press Browse to see the plugins it offers." = "Введите адрес figcraft-server и нажмите «Обзор», чтобы увидеть доступные плагины." +"Enter to save · Esc to cancel" = "Enter — сохранить · Esc — отменить" +"Eraser" = "Ластик" +"Esc" = "Esc" +"Essential" = "Основные" +"Every conflict has a choice." = "Для каждого конфликта выбран вариант." +"Every top-level frame already starts a flow" = "Каждый Frame верхнего уровня уже начинает сценарий" +"Exact opacity (4 then 5: 45%)" = "Точная непрозрачность (4, затем 5: 45%)" +"Exclude" = "Исключение" +"Excluded" = "Исключено" +"Excluded from layout" = "Исключено из компоновки" +"Excludes" = "Исключения" +"Exclusion" = "Исключение" +"Exit focus view (Esc)" = "Выйти из режима фокуса (Esc)" +"Expand" = "Развернуть" +"Export" = "Экспорт" +"Export all" = "Экспортировать всё" +"Export frames and layers as PNG, JPG, SVG or PDF" = "Экспортировать Frame и слои в PNG, JPG, SVG или PDF" +"Export page as PNG" = "Экспортировать страницу в PNG" +"Export settings" = "Настройки экспорта" +"Export variables" = "Экспортировать переменные" +"Export {name}" = "Экспортировать {name}" +"Export {n} layers" = { one = "Экспортировать {n} слой", few = "Экспортировать {n} слоя", many = "Экспортировать {n} слоёв", other = "Экспортировать {n} слоя" } +"Exported {file}" = "Экспортирован файл {file}" +"Exported {name}" = "Экспортировано: {name}" +"Exported {name}; {n} kinds of content were left out" = { one = "Экспортировано: {name}; пропущен {n} тип содержимого", few = "Экспортировано: {name}; пропущены {n} типа содержимого", many = "Экспортировано: {name}; пропущено {n} типов содержимого", other = "Экспортировано: {name}; пропущено {n} типа содержимого" } +"Exported {n} files" = { one = "Экспортирован {n} файл", few = "Экспортированы {n} файла", many = "Экспортировано {n} файлов", other = "Экспортировано {n} файла" } +"Export…" = "Экспорт…" +"Expose properties to instances" = "Сделать свойства доступными в экземплярах" +"Exposure" = "Экспозиция" +"Expression" = "Выражение" +"Extend collection" = "Расширить коллекцию" +"Extends \"{name}\": overridden values are marked" = "Расширяет «{name}»: переопределённые значения отмечены" +"Eye" = "Глаз" +"F" = "F" +"Fade" = "Затухание" +"False" = "Ложь" +"Feb" = "февр." +"Fetching from the server…" = "Загрузка с сервера…" +"File" = "Файл" +"File name" = "Имя файла" +"File not found" = "Файл не найден" +"File published" = "Файл опубликован" +"Files aren't end-to-end encrypted: the server's operator can read them." = "Файлы не защищены сквозным шифрованием: оператор сервера может их прочитать." +"Files nobody opens for {n} days are deleted." = { one = "Файлы, которые никто не открывает {n} день, удаляются.", few = "Файлы, которые никто не открывает {n} дня, удаляются.", many = "Файлы, которые никто не открывает {n} дней, удаляются.", other = "Файлы, которые никто не открывает {n} дня, удаляются." } +"Fill" = "Заливка" +"Fill (fr)" = "Заполнение (fr)" +"Fill container" = "Заполнить контейнер" +"Fill window" = "Заполнить окно" +"Fills" = "Заливки" +"Fill|sizing" = "Заполнение" +"Filter and sort" = "Фильтрация и сортировка" +"Find" = "Найти" +"Find and replace" = "Найти и заменить" +"Find layers" = "Найти слои" +"First" = "Первый" +"First commit" = "Первый коммит" +"First on top" = "Первый сверху" +"Fit" = "Вписать" +"Fit device to window" = "Вписать устройство в окно" +"Fit to contents" = "По содержимому" +"Fit to window" = "Вписать в окно" +"Fit width" = "По ширине" +"Fixed" = "Фиксированный" +"Fixed gap" = "Фиксированный интервал" +"Fixed size" = "Фиксированный размер" +"Flatten" = "Свести" +"Flip" = "Отразить" +"Flip horizontal" = "Отразить по горизонтали" +"Flip text to the other side" = "Перенести текст на другую сторону" +"Flip vertical" = "Отразить по вертикали" +"Flip width profile" = "Отразить профиль ширины" +"Flow starting point" = "Начальная точка сценария" +"Flow starting points" = "Начальные точки сценариев" +"Flows" = "Сценарии" +"Focus view" = "Режим фокуса" +"Fold autosaves" = "Свернуть автосохранения" +"Folder: click to collapse" = "Папка: нажмите, чтобы свернуть" +"Folder: click to expand" = "Папка: нажмите, чтобы развернуть" +"Follow" = "Следить" +"Follow {name}" = "Следить за {name}" +"Following" = "Следование" +"Following {name} · {key} to stop" = "Следование за {name} · {key} — остановить" +"Font" = "Шрифт" +"Font not installed" = "Шрифт не установлен" +"Font size" = "Размер шрифта" +"Font style" = "Начертание шрифта" +"Forget on this computer" = "Забыть на этом компьютере" +"Four screens of a trail app with components and a prototype flow" = "Четыре экрана приложения для походов с компонентами и сценарием прототипа" +"Fractions" = "Дроби" +"Frame" = "Frame" +"Frame selection" = "Поместить выделение во Frame" +"Frame tools" = "Инструменты Frame" +"Frames" = "Frames" +"From the library \"{name}\": read-only" = "Из библиотеки «{name}»: только чтение" +"From {name}" = "Из {name}" +"From {server}" = "С {server}" +"Frost" = "Иней" +"Gap" = "Интервал" +"Gap between columns" = "Интервал между столбцами" +"Gap between items" = "Интервал между элементами" +"Gap between rows" = "Интервал между строками" +"Getting started" = "Начало работы" +"Give the address of a server open to the public, or a server's address with its key (`figcraft-server key` prints it)." = "Укажите адрес общедоступного сервера или адрес сервера с ключом (`figcraft-server key` выводит ключ)." +"Glass" = "Стекло" +"Go to main component" = "Перейти к основному компоненту" +"Go to {name}" = "Перейти к {name}" +"Grab bag" = "Список имён" +"Gradient" = "Градиент" +"Gray" = "Серый" +"Green" = "Зелёный" +"Greyscale components and two example screens to sketch a flow with" = "Монохромные компоненты и два примера экранов для наброска сценария" +"Grid" = "Сетка" +"Grid ({size})" = "Сетка ({size})" +"Grid styles" = "Стили сеток" +"Grid {size}" = "Сетка {size}" +"Group" = "Группа" +"Group selection" = "Сгруппировать выделение" +"Groups" = "Группы" +"Gutter" = "Межколонник" +"Hand tool" = "Инструмент «Рука»" +"Hanging list" = "Выступающий список" +"Hanging punctuation" = "Висячая пунктуация" +"Hard light" = "Жёсткий свет" +"Height" = "Высота" +"Help" = "Справка" +"Help and resources" = "Справка и ресурсы" +"Help page" = "Страница справки" +"Here: {names}" = "Здесь: {names}" +"Hidden" = "Скрыто" +"Hide contents" = "Скрыть содержимое" +"Hide from publishing" = "Исключить из публикации" +"Hide section contents" = "Скрыть содержимое секции" +"Hide these tips (Getting started in the Help menu shows them again)" = "Скрыть подсказки (их можно вернуть через «Начало работы» в меню справки)" +"Hide when publishing" = "Скрывать при публикации" +"Highlight layers on hover" = "Подсвечивать слои при наведении" +"Highlights" = "Света" +"Hold Alt" = "Удерживайте Alt" +"Hold Alt+Z" = "Удерживайте Alt+Z" +"Hold Ctrl" = "Удерживайте Ctrl" +"Hold Z" = "Удерживайте Z" +"Hold ⌃ or ⌘" = "Удерживайте ⌃ или ⌘" +"Hold ⌥" = "Удерживайте ⌥" +"Hold ⌥Z" = "Удерживайте ⌥Z" +"Home" = "Главная" +"Horizontal" = "Горизонтально" +"Horizontal align" = "Горизонтальное выравнивание" +"Horizontal hex" = "Горизонтальный шестиугольник" +"Horizontal padding" = "Горизонтальные внутренние отступы" +"Horizontal spacing" = "Горизонтальный интервал" +"Hosted by {operator}" = "Оператор: {operator}" +"Hover a layer to see its properties" = "Наведите на слой, чтобы увидеть его свойства" +"Hover other layers to measure the distance" = "Наведите на другие слои, чтобы измерить расстояние" +"Hue" = "Цветовой тон" +"Hug" = "По содержимому" +"Hug contents" = "По размеру содержимого" +"Icon components on a 24 px grid with keylines and a size preview" = "Компоненты иконок на сетке 24 px с направляющими и предпросмотром размеров" +"Icon set starter" = "Основа набора иконок" +"If" = "Если" +"If {condition}" = "Если {condition}" +"Ignore auto layout (absolute position)" = "Игнорировать автолейаут (абсолютная позиция)" +"Image" = "Изображение" +"Import" = "Импорт" +"Import path" = "Путь импорта" +"Import plugin from manifest" = "Импортировать плагин из манифеста" +"Import plugin from manifest…" = "Импортировать плагин из манифеста…" +"Import variables" = "Импортировать переменные" +"Imported {name}" = "Импортировано: {name}" +"Imported {n} new variables, updated {updated}" = { one = "Импортирована {n} новая переменная, обновлено: {updated}", few = "Импортированы {n} новые переменные, обновлено: {updated}", many = "Импортировано {n} новых переменных, обновлено: {updated}", other = "Импортировано {n} новой переменной, обновлено: {updated}" } +"Import…" = "Импорт…" +"In an instance, this comes from the main component" = "В экземпляре это значение берётся из основного компонента" +"In the browser you can join sessions the desktop app hosts and files on a FigCraft server. A page opened over https reaches other computers only through wss:// links (a server behind TLS)." = "В браузере можно подключаться к сессиям настольного приложения и файлам на сервере FigCraft. Страница, открытая по https, подключается к другим компьютерам только по ссылкам wss:// (сервер с TLS)." +"Include bounding box" = "Включить ограничивающий прямоугольник" +"Include id attribute" = "Включить атрибут id" +"Included" = "Включено" +"Included in layout" = "Участвует в компоновке" +"Individual corners" = "Углы по отдельности" +"Individual strokes" = "Обводки по отдельности" +"Ink" = "Чернила" +"Inner shadow" = "Внутренняя тень" +"Inside" = "Внутри" +"Inspect" = "Инспектор" +"Inspect the layer under the pointer: size, colours, typography" = "Свойства слоя под указателем: размер, цвета, типографика" +"Install plugin" = "Установить плагин" +"Install" = "Установить" +"Installed {name}" = "Установлено: {name}" +"Installed" = "Установлено" +"Installing…" = "Установка…" +"Instance" = "Экземпляр" +"Instance properties" = "Свойства экземпляра" +"Instance swap" = "Замена экземпляра" +"Instance swap (no other component)" = "Замена экземпляра (других компонентов нет)" +"Instances" = "Экземпляры" +"Instant" = "Мгновенно" +"Interaction" = "Взаимодействие" +"Interactions" = "Взаимодействия" +"Interface design and prototyping. A free, open-source, clean-room design tool written from scratch in Rust, part of the ArtCraft Crafting Apps." = "Дизайн интерфейсов и прототипирование. Бесплатный инструмент с открытым исходным кодом, независимо созданный с нуля на Rust, из семейства ArtCraft Crafting Apps." +"Intersect" = "Пересечение" +"Intersects" = "Пересечения" +"Invite link" = "Ссылка приглашения" +"Invite link copied" = "Ссылка приглашения скопирована" +"It asks permission to:" = "Запрашивает разрешение на:" +"Jan" = "янв." +"Join" = "Подключиться" +"Join code copied" = "Код подключения скопирован" +"Join code or invite link" = "Код подключения или ссылка приглашения" +"Join live session" = "Подключиться к совместной сессии" +"Join us on Discord" = "Присоединиться к нам в Discord" +"Join with a code" = "Подключиться по коду" +"Joined" = "Подключено" +"Joined {file}" = "Подключено к {file}" +"Joining…" = "Подключение…" +"Join|stroke" = "Соединение" +"Jul" = "июл." +"Jun" = "июн." +"Just now" = "Только что" +"Justify" = "По ширине" +"Keep appearance" = "Сохранить внешний вид" +"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "«Сохранить внешний вид» преобразует все значения цветов в {profile}, сохраняя их вид. «Сохранить значения» оставляет числа без изменений, поэтому цвета меняются." +"Keep tool selected after use" = "Оставлять инструмент выбранным после использования" +"Keep values" = "Сохранить значения" +"Key/Gamepad" = "Клавиша/геймпад" +"Key/Gamepad {key}" = "Клавиша/геймпад {key}" +"Keyboard shortcuts" = "Сочетания клавиш" +"Kits" = "Наборы" +"Landing page" = "Лендинг" +"Language" = "Язык" +"Lasso" = "Лассо" +"Last" = "Последний" +"Last change: {note}" = "Последнее изменение: {note}" +"Last commit" = "Последний коммит" +"Last modified" = "Последнее изменение" +"Last on top" = "Последний сверху" +"Layer blur" = "Размытие слоя" +"Layer name" = "Имя слоя" +"Layers" = "Слои" +"Layers in an instance take their position from the main component" = "Положение слоёв экземпляра задаётся основным компонентом" +"Layout" = "Компоновка" +"Layout grid" = "Сетка макета" +"Layout grid settings" = "Настройки сетки макета" +"Layout grids" = "Сетки макета" +"Leave session" = "Покинуть сессию" +"Leaves" = "Листья" +"Left" = "Слева" +"Left & right" = "Слева и справа" +"Left side" = "Левая сторона" +"Left side width" = "Ширина левой стороны" +"Letter case" = "Регистр букв" +"Letter spacing" = "Межбуквенный интервал" +"Libraries" = "Библиотеки" +"Libraries in this file" = "Библиотеки в этом файле" +"Library updates available" = "Доступны обновления библиотек" +"Library: click to collapse" = "Библиотека: нажмите, чтобы свернуть" +"Library: click to expand" = "Библиотека: нажмите, чтобы развернуть" +"Library: its variables are read-only here; edit them in the library file and publish it" = "Библиотека: здесь переменные доступны только для чтения; измените их в файле библиотеки и опубликуйте его" +"Light" = "Светлая" +"Lighten" = "Осветление" +"Light|glass" = "Свет" +"Line" = "Линия" +"Line arrow" = "Линейная стрелка" +"Line delta" = "Разница строк" +"Line height" = "Межстрочный интервал" +"Linear" = "Линейный" +"Lines" = "Линии" +"Lines +/−" = "Строки +/−" +"Lines added" = "Добавлено строк" +"Lines deleted" = "Удалено строк" +"Link" = "Ссылка" +"Link copied" = "Ссылка скопирована" +"Link docs, code or tickets for developers." = "Добавьте ссылки на документацию, код или задачи для разработчиков." +"Link frames on the Prototype tab, then present the flow" = "Свяжите Frame на вкладке «Прототип», затем запустите сценарий" +"Link this component to its code component: instances then show your real snippet with their values." = "Свяжите этот компонент с компонентом в коде: экземпляры будут показывать ваш код со своими значениями." +"List spacing" = "Интервал списка" +"List style" = "Стиль списка" +"Live session" = "Совместная сессия" +"Live · {people}" = "Онлайн · {people}" +"Local components" = "Локальные компоненты" +"Local network" = "Локальная сеть" +"Local styles" = "Локальные стили" +"Local variables" = "Локальные переменные" +"Lock/Unlock" = "Заблокировать/разблокировать" +"Locked" = "Заблокировано" +"Lowercase" = "Строчные" +"Luminance" = "Яркость" +"Luminosity" = "Свечение" +"Main" = "Основная" +"Main component deleted" = "Основной компонент удалён" +"Main menu" = "Главное меню" +"Make a component" = "Создать компонент" +"Make slot" = "Сделать слотом" +"Mar" = "мар." +"Margin" = "Внешний отступ" +"Mark all as read" = "Отметить всё как прочитанное" +"Mark as resolved" = "Отметить как решённое" +"Mark as unread" = "Отметить как непрочитанное" +"Mark as unresolved" = "Отметить как нерешённое" +"Mask" = "Маска" +"Mass · Stiffness · Damping" = "Масса · Жёсткость · Демпфирование" +"Match (empty: the whole name)" = "Совпадение (пусто: всё имя)" +"Max H" = "Макс. В" +"Max W" = "Макс. Ш" +"Max lines" = "Максимум строк" +"Maximize" = "Развернуть окно" +"May" = "май" +"Measure to the selection" = "Измерить до выделения" +"Merge into main" = "Объединить с основной" +"Merge regions" = "Объединить области" +"Merge the branch into the main file (one undo step) and archive it" = "Объединить ветку с основным файлом (один шаг отмены) и архивировать её" +"Merged" = "Объединено" +"Merged PRs" = "Объединённые PR" +"Min H" = "Мин. В" +"Min W" = "Мин. Ш" +"Minimize" = "Свернуть окно" +"Mirror angle" = "Отражать угол" +"Mirror angle and length" = "Отражать угол и длину" +"Mirroring" = "Зеркальность" +"Missing" = "Отсутствует" +"Missing layer" = "Слой отсутствует" +"Missing style" = "Стиль отсутствует" +"Missing variable" = "Переменная отсутствует" +"Miter" = "Острое" +"Miter angle" = "Угол острого соединения" +"Mixed" = "Смешанное" +"Mixed fonts" = "Разные шрифты" +"Mixed interactions: click + to replace them" = "Разные взаимодействия: нажмите + для замены" +"Mobile app" = "Мобильное приложение" +"Mode" = "Режим" +"Model" = "Модель" +"Models" = "Модели" +"Mono" = "Моно" +"More" = "Ещё" +"More instance actions" = "Другие действия с экземпляром" +"Mouse down" = "Нажатие кнопки мыши" +"Mouse enter" = "Вход указателя" +"Mouse leave" = "Выход указателя" +"Mouse up" = "Отпускание кнопки мыши" +"Move along the toolbar or the layer rows" = "Перемещение по панели инструментов или строкам слоёв" +"Move" = "Перемещение" +"Move column left" = "Переместить столбец влево" +"Move column right" = "Переместить столбец вправо" +"Move down" = "Переместить вниз" +"Move in" = "Сдвиг внутрь" +"Move out" = "Сдвиг наружу" +"Move tools" = "Инструменты перемещения" +"Move up" = "Переместить вверх" +"Moved to {name}" = "Перемещено в {name}" +"Multi" = "Мульти" +"Multiply" = "Умножение" +"Muted" = "Приглушённый" +"Name" = "Имя" +"Name (A–Z)" = "Имя (А–Я)" +"Named instance" = "Именованный экземпляр" +"Navigate to" = "Перейти к" +"Nested instance" = "Вложенный экземпляр" +"New design file" = "Новый файл дизайна" +"Newest first" = "Сначала новые" +"Next or previous control in the focused region" = "Следующий или предыдущий элемент в активной области" +"Next ready for dev" = "Следующий готовый к разработке" +"Next size option" = "Следующий вариант размера" +"No assets: no export settings, icons or images. Add export settings in the Design tab." = "Нет ресурсов: настроек экспорта, иконок или изображений. Добавьте настройки экспорта на вкладке «Дизайн»." +"No boolean variables fit here." = "Нет подходящих логических переменных." +"No boolean variables yet. Create them in Local variables." = "Логических переменных пока нет. Создайте их в разделе «Локальные переменные»." +"No branches. A branch is a copy of the file to try changes in, then review and merge." = "Веток нет. Ветка — копия файла, в которой можно попробовать изменения, затем проверить и объединить их." +"No category" = "Без категории" +"No changes" = "Нет изменений" +"No colour variables fit here." = "Нет подходящих цветовых переменных." +"No colour variables yet. Create them in Local variables." = "Цветовых переменных пока нет. Создайте их в разделе «Локальные переменные»." +"No comments yet" = "Комментариев пока нет" +"No components yet. Select layers and press {keys}." = "Компонентов пока нет. Выделите слои и нажмите {keys}." +"No conflicts." = "Конфликтов нет." +"No contributor data was built into this copy." = "В этой сборке нет данных об участниках." +"No fonts match" = "Подходящих шрифтов нет" +"No join code here (the server is older than join codes, or this address can't be put in one): share the link instead." = "Код подключения недоступен (сервер не поддерживает коды или адрес нельзя включить в код): поделитесь ссылкой." +"No layer changes to show." = "Нет изменений слоёв для показа." +"No libraries yet. Add a .figcraft file that was published as a library, or paste a team library's link." = "Библиотек пока нет. Добавьте файл .figcraft, опубликованный как библиотека, или вставьте ссылку на командную библиотеку." +"No list" = "Без списка" +"No matching comments" = "Подходящих комментариев нет" +"No matching components" = "Подходящих компонентов нет" +"No matching layers" = "Подходящих слоёв нет" +"No matching styles" = "Подходящих стилей нет" +"No matching variables" = "Подходящих переменных нет" +"No mirroring" = "Без зеркальности" +"No model credits were built into this copy." = "В этой сборке нет сведений об использованных моделях." +"No number variables fit here." = "Нет подходящих числовых переменных." +"No number variables yet. Create them in Local variables." = "Числовых переменных пока нет. Создайте их в разделе «Локальные переменные»." +"No paints" = "Заливок нет" +"No plugins on {server}." = "На {server} нет плагинов." +"No plugins yet. Import a development plugin from its figcraft-plugin.json." = "Плагинов пока нет. Импортируйте плагин для разработки из его figcraft-plugin.json." +"No preferred components" = "Предпочтительных компонентов нет" +"No preset" = "Без пресета" +"No saved versions yet. Save a version to mark a point in your work you can come back to." = "Сохранённых версий пока нет. Сохраните версию, чтобы можно было вернуться к этому этапу работы." +"No scrolling" = "Без прокрутки" +"No shortcuts match" = "Подходящих сочетаний нет" +"No status" = "Без статуса" +"No string variables fit here." = "Нет подходящих строковых переменных." +"No string variables yet. Create them in Local variables." = "Строковых переменных пока нет. Создайте их в разделе «Локальные переменные»." +"No styles yet." = "Стилей пока нет." +"No styles yet. Click + to create one from the selection." = "Стилей пока нет. Нажмите +, чтобы создать стиль из выделения." +"No templates match your search" = "По вашему запросу нет шаблонов" +"No text styles yet. Click + to create one from the selected text." = "Текстовых стилей пока нет. Нажмите +, чтобы создать стиль из выделенного текста." +"No variables fit here." = "Нет подходящих переменных." +"No variables yet. Create them in Local variables." = "Переменных пока нет. Создайте их в разделе «Локальные переменные»." +"Nobody else is here yet" = "Здесь пока больше никого нет" +"Noise" = "Шум" +"None" = "Нет" +"None." = "Нет." +"Normal" = "Обычный" +"Not bound" = "Не привязано" +"Not in a live session" = "Нет активной совместной сессии" +"Not in this font" = "Нет в этом шрифте" +"Not properties, kept as written: {names}" = "Не свойства, оставлены как есть: {names}" +"Not read yet" = "Ещё не прочитано" +"Not shared" = "Не опубликовано" +"Note {n}" = "Заметка {n}" +"Nothing changed since this was marked ready for dev." = "С момента отметки «Готово к разработке» изменений нет." +"Nothing here yet. Set a frame's status to list it." = "Здесь пока пусто. Задайте статус Frame, чтобы он появился в списке." +"Nothing online yet. Use Share on a file to put it online and it is listed here." = "Онлайн-файлов пока нет. Нажмите «Поделиться» в файле, чтобы опубликовать его и добавить сюда." +"Nothing to export" = "Нечего экспортировать" +"Nov" = "нояб." +"Now" = "Сейчас" +"Nudge (Shift: big nudge)" = "Сдвиг (Shift: большой шаг)" +"Nudge amount" = "Шаг сдвига" +"Number" = "Число" +"Number ↑" = "Число ↑" +"Number ↓" = "Число ↓" +"Numbered list" = "Нумерованный список" +"Object" = "Объект" +"Oct" = "окт." +"Off" = "Выкл." +"Offered first when filling the slot (any content is allowed)" = "Предлагается первым при заполнении слота (можно использовать любое содержимое)" +"Offered first when swapping the instance (any component is allowed)" = "Предлагается первым при замене экземпляра (можно использовать любой компонент)" +"Offset" = "Смещение" +"Oldest first" = "Сначала старые" +"On click" = "По нажатию" +"On drag" = "При перетаскивании" +"On this page" = "На этой странице" +"One announcement in five common sizes: square, portrait, story, link and banner" = "Одно объявление в пяти популярных размерах: квадрат, портрет, история, ссылка и баннер" +"Online" = "Онлайн" +"Online files" = "Онлайн-файлы" +"Online sharing is off: files are shared on the local network only." = "Онлайн-публикация отключена: файлы доступны только в локальной сети." +"Only the first changed layers are listed." = "Показаны только первые изменённые слои." +"Only the first {n} changed layers are listed." = "Показаны только первые {n} изменённых слоёв." +"Only your threads" = "Только ваши обсуждения" +"Opacity" = "Непрозрачность" +"Opacity & blend" = "Непрозрачность и наложение" +"Opacity 10%…90%, 0: 100%" = "Непрозрачность 10%…90%, 0: 100%" +"Open" = "Открыть" +"Open .figcraft, .svg or images" = "Открыть .figcraft, .svg или изображения" +"Open branch" = "Открыть ветку" +"Open file" = "Открыть файл" +"Open files" = "Открыть файлы" +"Open in focus view" = "Открыть в режиме фокуса" +"Open in new window" = "Открыть в новом окне" +"Open invite links in FigCraft" = "Открывать ссылки приглашения в FigCraft" +"Open link" = "Открыть ссылку" +"Open link {url}" = "Открыть ссылку {url}" +"Open overlay" = "Открыть оверлей" +"Open prototype preview" = "Открыть предпросмотр прототипа" +"Open variables ({count})" = "Открыть переменные ({count})" +"Opened “{name}” in a new tab" = "«{name}» открыт в новой вкладке" +"Open|comments" = "Открытые" +"Options" = "Параметры" +"Orange" = "Оранжевый" +"Order" = "Порядок" +"Other apps may not open every .fig file exported here." = "Другие приложения могут открыть не каждый экспортированный здесь файл .fig." +"Others lose access and keep their copy; the file stays on the server for you" = "Другие участники потеряют доступ, сохранив свои копии; файл останется на сервере для вас" +"Others type this under Share ▸ Join with a code" = "Другие участники вводят это в «Поделиться» ▸ «Подключиться по коду»" +"Outline stroke" = "Преобразовать обводку в контур" +"Outline text" = "Преобразовать текст в контуры" +"Outlines" = "Контуры" +"Outside" = "Снаружи" +"Overflow" = "Переполнение" +"Overlay" = "Оверлей" +"Overlay position" = "Положение оверлея" +"Overridden here (the collection it extends has another value)" = "Переопределено здесь (в исходной коллекции другое значение)" +"Overrides" = "Переопределения" +"Owner token" = "Токен владельца" +"Owner token copied" = "Токен владельца скопирован" +"PDF print options" = "Параметры печати PDF" +"PDF print settings" = "Настройки печати PDF" +"Padding" = "Внутренние отступы" +"Page" = "Страница" +"Pages" = "Страницы" +"Pages: {pages}" = "Страницы: {pages}" +"Paint bucket" = "Заливка области" +"Pan the canvas" = "Перемещение по холсту" +"Pan with nothing selected (Shift: farther)" = "Перемещение без выделения (Shift: дальше)" +"Panels" = "Панели" +"Paper" = "Бумага" +"Paragraph indent" = "Абзацный отступ" +"Paragraph indent applies to left-aligned text" = "Абзацный отступ применяется к тексту с выравниванием влево" +"Paragraph spacing" = "Интервал между абзацами" +"Pass through" = "Пропуск" +"Paste a link (https://…)" = "Вставьте ссылку (https://…)" +"Paste a team library link (a view link)" = "Вставьте ссылку на командную библиотеку (для просмотра)" +"Paste here" = "Вставить здесь" +"Paste to replace" = "Вставить с заменой" +"Pattern" = "Паттерн" +"Pattern alignment" = "Выравнивание паттерна" +"Pattern scale" = "Масштаб паттерна" +"Pattern source" = "Источник паттерна" +"Pen" = "Перо" +"Pencil" = "Карандаш" +"People in this file" = "Участники в этом файле" +"People join from anywhere with a code. The file is kept on {server} while you share it." = "Участники подключаются по коду из любой сети. Пока файл опубликован, он хранится на {server}." +"People on your network join with a code. Nothing goes through a server." = "Участники вашей сети подключаются по коду. Сервер не используется." +"Phone" = "Телефон" +"Pick color" = "Пипетка" +"Pick color (I)" = "Пипетка (I)" +"Pick one font family first" = "Сначала выберите одно семейство шрифта" +"Pick the Frame tool, then drag on the canvas or choose a device size" = "Выберите инструмент Frame, затем протяните область на холсте или выберите размер устройства" +"Pin a property" = "Закрепить свойство" +"Pinch" = "Сужение" +"Pixel grid" = "Пиксельная сетка" +"Pixel preview 1x" = "Пиксельный предпросмотр 1x" +"Pixel preview 2x" = "Пиксельный предпросмотр 2x" +"Place a shape or frame (its tool chosen)" = "Разместить фигуру или Frame (выбранным инструментом)" +"Place automatically" = "Разместить автоматически" +"Place image" = "Разместить изображение" +"Plugins run in FigCraft's sandbox, without files, network or clock. Install only plugins you trust." = "Плагины работают в песочнице FigCraft без доступа к файлам, сети и часам. Устанавливайте только плагины, которым доверяете." +"Plugins" = "Плагины" +"Plus darker" = "Линейное затемнение" +"Plus lighter" = "Линейное осветление" +"Point" = "Точка" +"Points" = "Точки" +"Polygon" = "Многоугольник" +"Polygons" = "Многоугольники" +"Port" = "Порт" +"Position" = "Положение" +"Post" = "Отправить" +"Preferences" = "Настройки" +"Preferred" = "Предпочтительные" +"Present" = "Презентация" +"Present or preview" = "Презентация или предпросмотр" +"Present this flow" = "Запустить этот сценарий" +"Presentation" = "Презентация" +"Presentation slides" = "Слайды презентации" +"Presenting: comments" = "Презентация: комментарии" +"Presenting: cycle the size options" = "Презентация: переключить размер" +"Presenting: full screen" = "Презентация: полный экран" +"Presenting: next frame" = "Презентация: следующий Frame" +"Presenting: previous frame" = "Презентация: предыдущий Frame" +"Presenting: restart" = "Презентация: начать заново" +"Preserve scroll position" = "Сохранять положение прокрутки" +"Presets" = "Пресеты" +"Press a key…" = "Нажмите клавишу…" +"Playground" = "Песочница" +"Preview" = "Предпросмотр" +"Preview this flow" = "Предпросмотр этого сценария" +"Preview this frame" = "Предпросмотр этого Frame" +"Previous ready for dev" = "Предыдущий готовый к разработке" +"Progressive" = "Прогрессивный" +"Properties" = "Свойства" +"Prototype" = "Прототип" +"Prototype preview" = "Предпросмотр прототипа" +"Prototype settings" = "Настройки прототипа" +"Publish" = "Опубликовать" +"Publish changes" = "Опубликовать изменения" +"Publish to another server…" = "Опубликовать на другом сервере…" +"Publish to server" = "Опубликовать на сервере" +"Published version {v}" = "Опубликована версия {v}" +"Published version {v} to the server" = "Версия {v} опубликована на сервере" +"Published version {v}: save the file so other files can use it" = "Опубликована версия {v}: сохраните файл, чтобы её можно было использовать в других файлах" +"Published · {people}" = "Опубликовано · {people}" +"Publishing to a server needs the desktop app; in the browser you can join a server's files" = "Для публикации на сервере нужно настольное приложение; в браузере можно подключаться к файлам сервера" +"Publishing with the server's key." = "Публикация с ключом сервера." +"Publishing…" = "Публикация…" +"Push" = "Вытеснение" +"Push changes to main component" = "Перенести изменения в основной компонент" +"Put text on path" = "Разместить текст на пути" +"R" = "R" +"Radial" = "Радиальный" +"Radius" = "Радиус" +"Ratio" = "Соотношение" +"Reading the library…" = "Чтение библиотеки…" +"Ready for dev" = "Готово к разработке" +"Real name" = "Настоящее имя" +"Recent" = "Недавние" +"Recent files" = "Недавние файлы" +"Recents" = "Недавние" +"Recovered {n} unsaved files after the app quit unexpectedly: save them to keep them" = { one = "После сбоя восстановлен {n} несохранённый файл: сохраните его", few = "После сбоя восстановлены {n} несохранённых файла: сохраните их", many = "После сбоя восстановлено {n} несохранённых файлов: сохраните их", other = "После сбоя восстановлено {n} несохранённого файла: сохраните их" } +"Rectangle" = "Прямоугольник" +"Rectangles" = "Прямоугольники" +"Red" = "Красный" +"Reduce motion" = "Уменьшить движение" +"Refraction" = "Преломление" +"Regular expression" = "Регулярное выражение" +"Remove" = "Удалить" +"Remove all" = "Удалить всё" +"Remove annotation" = "Удалить аннотацию" +"Remove auto layout" = "Убрать автолейаут" +"Remove dev resource" = "Удалить ресурс для разработки" +"Remove export setting" = "Удалить настройку экспорта" +"Remove from recents" = "Убрать из недавних" +"Remove from server" = "Удалить с сервера" +"Remove from server…" = "Удалить с сервера…" +"Remove guide" = "Удалить направляющую" +"Remove interaction" = "Удалить взаимодействие" +"Remove library from this file" = "Удалить библиотеку из этого файла" +"Remove min and max" = "Убрать минимум и максимум" +"Remove min and max height" = "Убрать минимальную и максимальную высоту" +"Remove min and max width" = "Убрать минимальную и максимальную ширину" +"Remove regions" = "Удалить области" +"Remove slot" = "Убрать слот" +"Remove starting point" = "Убрать начальную точку" +"Remove stop" = "Удалить точку градиента" +"Remove text from path" = "Снять текст с пути" +"Remove the else and its actions" = "Удалить «Иначе» и его действия" +"Remove this condition and its actions" = "Удалить это условие и его действия" +"Remove this file from the server? It can't be shared or opened from there again. Your copy stays open here." = "Удалить файл с сервера? Его больше нельзя будет открывать и публиковать оттуда. Ваша копия останется открытой здесь." +"Remove {name} from {server}?" = "Удалить {name} с {server}?" +"Removed" = "Удалено" +"Removed from the server" = "Удалено с сервера" +"Removed since it was marked" = "Удалено после отметки" +"Rename" = "Переименовать" +"Rename duplicated layers" = "Переименовывать дубликаты слоёв" +"Rename folder" = "Переименовать папку" +"Rename group" = "Переименовать группу" +"Rename layers" = "Переименовать слои" +"Rename mode" = "Переименовать режим" +"Rename on the server" = "Переименовать на сервере" +"Rename on the server…" = "Переименовать на сервере…" +"Rename page" = "Переименовать страницу" +"Rename to" = "Новое имя" +"Replace all" = "Заменить всё" +"Replace mixed export settings" = "Заменить разные настройки экспорта" +"Replace with" = "Заменить на" +"Replace with a new interaction" = "Заменить новым взаимодействием" +"Reply" = "Ответить" +"Report" = "Пожаловаться" +"Report this file…" = "Пожаловаться на этот файл…" +"Reset" = "Сбросить" +"Reset adjustments" = "Сбросить коррекцию" +"Reset all changes" = "Сбросить все изменения" +"Reset all overrides on this layer" = "Сбросить все переопределения слоя" +"Reset overrides" = "Сбросить переопределения" +"Reset slot" = "Сбросить слот" +"Reset this override" = "Сбросить переопределение" +"Reset to style" = "Сбросить к стилю" +"Reset to the inherited value" = "Вернуть унаследованное значение" +"Resize by the nudge amount" = "Изменить размер на шаг сдвига" +"Resize frames to the device's screen (or the window without a device): their constraints and auto layout respond" = "Менять размер Frame под экран устройства (или окно, если устройство не выбрано): ограничения и автолейаут подстраиваются" +"Resize to fit" = "Подогнать размер" +"Resolve" = "Решить" +"Resolve change" = "Разрешить изменение" +"Resolved" = "Решённые" +"Resources" = "Ресурсы" +"Responsive" = "Адаптивно" +"Restart" = "Начать заново" +"Restore" = "Восстановить" +"Restore main component" = "Восстановить основной компонент" +"Restore this version" = "Восстановить эту версию" +"Restored “{name}”" = "«{name}» восстановлено" +"Reverse the order" = "Обратить порядок" +"Reversed triangle" = "Обратный треугольник" +"Review and merge" = "Проверить и объединить" +"Ribbon" = "Лента" +"Right" = "Справа" +"Right side" = "Правая сторона" +"Right side width" = "Ширина правой стороны" +"Rotate 90°" = "Повернуть на 90°" +"Rotate 90° right" = "Повернуть на 90° вправо" +"Rotate to landscape" = "Повернуть в альбомную ориентацию" +"Rotate to portrait" = "Повернуть в портретную ориентацию" +"Rotation" = "Поворот" +"Rotation jitter" = "Колебание угла" +"Round" = "Круглое" +"Rounded" = "Скруглённый" +"Row" = "Строка" +"Row gap" = "Интервал между строками" +"Row sizes" = "Размеры строк" +"Row span" = "Объединение строк" +"Rows" = "Строки" +"Rows {count}" = "Строки: {count}" +"Rulers" = "Линейки" +"Run" = "Запустить" +"Run {label}" = "Запустить {label}" +"Search templates" = "Поиск шаблонов" +"See all templates" = "Все шаблоны" +"Select layers and they space themselves as their content changes" = "Выделите слои: интервалы между ними будут подстраиваться при изменении содержимого" +"SHA-256: {sha}…" = "SHA-256: {sha}…" +"Same as layer" = "Как у слоя" +"Saturation" = "Насыщенность" +"Save" = "Сохранить" +"Save as" = "Сохранить как" +"Save as preset…" = "Сохранить как пресет…" +"Save version {key}" = "Сохранить версию {key}" +"Saved" = "Сохранено" +"Saved {name}" = "Сохранено: {name}" +"Say something" = "Напишите сообщение" +"Scale" = "Масштаб" +"Scatter" = "Рассеивание" +"Scoping: where pickers offer it" = "Область применения: где предлагается переменная" +"Screen" = "Экран" +"Scroll behavior" = "Поведение при прокрутке" +"Scroll to" = "Прокрутить к" +"Scroll to layer" = "Прокрутить к слою" +"Scroll with parent" = "Прокручивать с родителем" +"Search plugins" = "Поиск плагинов" +"Search" = "Поиск" +"Search actions, layers and tools" = "Поиск действий, слоёв и инструментов" +"Search assets" = "Поиск ресурсов" +"Search comments" = "Поиск комментариев" +"Search fonts" = "Поиск шрифтов" +"Search layers" = "Поиск слоёв" +"Search shortcuts" = "Поиск сочетаний клавиш" +"Section" = "Секция" +"Section color" = "Цвет секции" +"Sections" = "Секции" +"Select a frame and add a starting point to present it as a flow." = "Выделите Frame и добавьте начальную точку, чтобы запустить его как сценарий." +"Select a frame to preview it" = "Выделите Frame для предпросмотра" +"Select a layer to see its properties and code" = "Выделите слой, чтобы увидеть его свойства и код" +"Select all" = "Выделить всё" +"Select all with same" = "Выделить всё с одинаковым" +"Select layer" = "Выделить слой" +"Select layers to apply the style to" = "Выделите слои для применения стиля" +"Select overlapping shapes" = "Выделите пересекающиеся фигуры" +"Select points to edit their position, radius and mirroring" = "Выделите точки для изменения положения, радиуса и зеркальности" +"Select slot" = "Выделить слот" +"Selection" = "Выделение" +"Selection box, {w} by {h} at {x}, {y}: arrows move it, Shift+arrows or {cmd}+arrows size it, Enter selects, Escape cancels" = "Область выделения {w} на {h} в {x}, {y}: стрелки — перемещение, Shift+стрелки или {cmd}+стрелки — размер, Enter — выделить, Escape — отменить" +"Selection box: arrows move it, Shift+arrows or {cmd}+arrows size it, Enter selects, Escape cancels" = "Область выделения: стрелки — перемещение, Shift+стрелки или {cmd}+стрелки — размер, Enter — выделить, Escape — отменить" +"Send backward" = "На уровень назад" +"Send report" = "Отправить жалобу" +"Send to back" = "На задний план" +"Sep" = "сент." +"Server address (ws://host:47900/)" = "Адрес сервера (ws://host:47900/)" +"Server address" = "Адрес сервера" +"Server for online sharing" = "Сервер для онлайн-публикации" +"Set as default" = "Задать по умолчанию" +"Set variable" = "Задать переменную" +"Set variable mode" = "Задать режим переменных" +"Set variable {name}" = "Задать переменную {name}" +"Set {name} to {value}" = "Задать {name}: {value}" +"Shadows" = "Тени" +"Shape" = "Фигура" +"Shape builder" = "Конструктор фигур" +"Shape tools" = "Инструменты фигур" +"Share" = "Поделиться" +"Share this file" = "Поделиться файлом" +"Share with a code" = "Поделиться по коду" +"Share: live session, export" = "Поделиться: совместная сессия, экспорт" +"Shared" = "Опубликовано" +"Shared: copy its code from the file's menu" = "Опубликовано: скопируйте код в меню файла" +"Sharing a file needs the desktop app; here you can join one." = "Для публикации файла нужно настольное приложение; здесь можно подключиться к опубликованному файлу." +"Sharing on the local network (online sharing is off: no server is set in Preferences)." = "Общий доступ в локальной сети (онлайн-публикация отключена: сервер не задан в настройках)." +"Sharing on your network · {people}" = "Общий доступ в вашей сети · {people}" +"Sharing online · {people}" = "Общий доступ онлайн · {people}" +"Sharing stopped: the file stays on the server, open only to you" = "Общий доступ остановлен: файл остаётся на сервере, доступен только вам" +"Show" = "Показать" +"Show all templates" = "Показать все шаблоны" +"Show as grid" = "Показать сеткой" +"Show as list" = "Показать списком" +"Show autosaves" = "Показать автосохранения" +"Show behind transparent areas" = "Показать за прозрачными областями" +"Show comments (Shift+C)" = "Показать комментарии (Shift+C)" +"Show device frame" = "Показать рамку устройства" +"Show dimensions on objects" = "Показать размеры объектов" +"Show hotspot hints" = "Показать интерактивные области" +"Show in Explorer" = "Показать в Проводнике" +"Show in Finder" = "Показать в Finder" +"Show in folder" = "Показать в папке" +"Show only this on the canvas" = "Показать на холсте только это" +"Show open" = "Показать открытые" +"Show section contents" = "Показать содержимое секции" +"Show this layer" = "Показать этот слой" +"Show usage" = "Показать использование" +"Show/Hide" = "Показать/скрыть" +"Show/Hide UI" = "Показать/скрыть интерфейс" +"Side by side" = "Рядом" +"Sidebar" = "Боковая панель" +"Simplify stroke" = "Упростить обводку" +"Size" = "Размер" +"Size jitter" = "Колебание размера" +"Skip ink" = "Пропускать пересечения с буквами" +"Slashed zero" = "Перечёркнутый ноль" +"Slice" = "Срез" +"Slices" = "Срезы" +"Slide in" = "Въезд" +"Slide out" = "Выезд" +"Slot" = "Слот" +"Slot content" = "Содержимое слота" +"Small capitals" = "Капительные буквы" +"Small caps" = "Капитель" +"Smart animate" = "Smart animate" +"Snap to pixel grid" = "Привязка к пиксельной сетке" +"Social and slides" = "Соцсети и слайды" +"Social media" = "Соцсети" +"Social post sizes" = "Размеры публикаций для соцсетей" +"Soft light" = "Мягкий свет" +"Solid" = "Сплошная" +"Some variants have the same property values: give each one a unique combination" = "Некоторые варианты имеют одинаковые значения свойств: задайте каждому уникальное сочетание" +"Sort" = "Сортировка" +"Sort by" = "Сортировать по" +"Sort by date" = "Сортировать по дате" +"Sort by page" = "Сортировать по странице" +"Sort by {column}" = "Сортировать по {column}" +"Space around" = "Отступы вокруг" +"Space between" = "Отступы между" +"Space drag" = "Пробел и перетаскивание" +"Space evenly" = "Равные отступы" +"Space+drag" = "Пробел+перетаскивание" +"Spacing" = "Интервал" +"Spam, stolen work, abuse…" = "Спам, украденные работы, оскорбления…" +"Splay" = "Расхождение" +"Spotlight me" = "Показывать мой экран всем" +"Spread" = "Размах" +"Square" = "Квадрат" +"Standard" = "Стандартный" +"Standard ligatures" = "Стандартные лигатуры" +"Star" = "Звезда" +"Stars" = "Звёзды" +"Start" = "Начало" +"Start ascending from / stop descending at" = "Начать увеличение с / закончить уменьшение на" +"Start from a blank canvas" = "Начать с пустого холста" +"Start from a template" = "Начать с шаблона" +"Start on path" = "Начало на пути" +"Start point" = "Начальная точка" +"Start sharing" = "Включить общий доступ" +"Start sharing with the Share button, then pass on the code it gives" = "Включите общий доступ кнопкой «Поделиться», затем передайте полученный код" +"Starting to share…" = "Включение общего доступа…" +"Status" = "Статус" +"Sticky" = "Закреплённый" +"Stop" = "Остановить" +"Stop following {name}" = "Перестать следить за {name}" +"Stop session" = "Завершить сессию" +"Stop sharing" = "Остановить общий доступ" +"Stop spotlighting" = "Прекратить показ всем" +"Stop {n} {hex}" = "Точка {n} {hex}" +"Stretch" = "Растянуть" +"Strikethrough" = "Зачёркивание" +"String" = "Строка" +"Stroke" = "Обводка" +"Stroke style" = "Стиль обводки" +"Stroke weight" = "Толщина обводки" +"Strokes" = "Обводки" +"Style" = "Стиль" +"Styles" = "Стили" +"Stylistic set 1" = "Стилистический набор 1" +"Stylistic set 2" = "Стилистический набор 2" +"Stylistic set 3" = "Стилистический набор 3" +"Stylistic set 4" = "Стилистический набор 4" +"Stylistic set 5" = "Стилистический набор 5" +"Subtract" = "Вычитание" +"Subtracts" = "Вычитания" +"Suffix" = "Суффикс" +"Suspend snapping while dragging" = "Временно отключить привязку при перетаскивании" +"Swap instance" = "Заменить экземпляр" +"Swap library: use another library's assets of the same names" = "Заменить библиотеку: использовать одноимённые ресурсы другой библиотеки" +"Swap orientation" = "Сменить ориентацию" +"Swap overlay" = "Заменить оверлей" +"Swap to {name}" = "Заменить на {name}" +"Swap width and height" = "Поменять ширину и высоту" +"Swapped instance" = "Экземпляр заменён" +"Swapped {n} assets" = { one = "Заменён {n} ресурс", few = "Заменены {n} ресурса", many = "Заменено {n} ресурсов", other = "Заменено {n} ресурса" } +"Swapped {n} assets; {missed} had no match and still use the old library" = { one = "Заменён {n} ресурс; без совпадений: {missed}, для них сохранена старая библиотека", few = "Заменены {n} ресурса; без совпадений: {missed}, для них сохранена старая библиотека", many = "Заменено {n} ресурсов; без совпадений: {missed}, для них сохранена старая библиотека", other = "Заменено {n} ресурса; без совпадений: {missed}, для них сохранена старая библиотека" } +"Sweep" = "Угол дуги" +"System" = "Системный" +"Tab / Shift+Tab" = "Tab / Shift+Tab" +"Table" = "Таблица" +"Tablet" = "Планшет" +"Tabular numbers" = "Табличные цифры" +"Take the values from the first selected layer" = "Взять значения первого выделенного слоя" +"Taper" = "Сужение к концу" +"Taper both ends" = "Сужение с обоих концов" +"Tell the server's operator what is wrong with it" = "Сообщить оператору сервера о проблеме" +"Temperature" = "Температура" +"Template" = "Шаблон" +"Templates" = "Шаблоны" +"Terms" = "Условия" +"Text" = "Текст" +"Text align" = "Выравнивание текста" +"Text content" = "Содержимое текста" +"Text style" = "Текстовый стиль" +"Text styles" = "Текстовые стили" +"Texts" = "Тексты" +"Texture" = "Текстура" +"The canvas previews colors in sRGB; exported PNG, JPG and WebP files are tagged Display P3." = "На холсте цвета отображаются в sRGB; экспортированные PNG, JPG и WebP помечены профилем Display P3." +"The file is deleted from the server: nobody can open or join it from there again, and this can't be undone. Copies that are open stay with whoever has them." = "Файл удаляется с сервера: его больше нельзя будет открыть или подключиться к нему оттуда. Отменить это нельзя. Уже открытые копии остаются у участников." +"The file now holds another library" = "Теперь файл содержит другую библиотеку" +"The file now holds another library (\"{name}\")" = "Теперь файл содержит другую библиотеку («{name}»)" +"The files you shared or published online from this computer. They stay on their server until you remove them." = "Файлы, которыми вы поделились или которые опубликовали онлайн с этого компьютера. Они остаются на сервере, пока вы их не удалите." +"The link carries the session's secret: share it only with people you invite." = "Ссылка содержит секрет сессии: передавайте её только приглашённым участникам." +"The name in your online files and on the server. The document inside keeps its own name." = "Имя в списке онлайн-файлов и на сервере. Документ внутри сохраняет собственное имя." +"The selection uses several text styles: click to apply one" = "В выделении несколько текстовых стилей: нажмите, чтобы применить один" +"The server deletes this file if nobody opens it before {date}." = "Сервер удалит файл, если никто не откроет его до {date}." +"The whole file" = "Весь файл" +"Theme" = "Тема" +"There's no destination for that action" = "Для этого действия не задано назначение" +"Thickness" = "Толщина" +"This branch can't be reviewed: {e}" = "Не удалось проверить ветку: {e}" +"This branch was merged or archived." = "Ветка объединена или архивирована." +"This computer only" = "Только этот компьютер" +"This computer remembers the owner token, so the file can be managed from the home screen's Online list. Copy it to manage the file from somewhere else: it is a password for this file and won't be shown again." = "Этот компьютер сохраняет токен владельца для управления файлом из списка «Онлайн» на главной. Скопируйте токен для управления с другого устройства: это пароль к файлу, он больше не будет показан." +"This condition is edited as an expression" = "Это условие редактируется как выражение" +"This file" = "Этот файл" +"This file has unsaved changes" = "В файле есть несохранённые изменения" +"This file is on {server}, open only to you. Share it to let others in with a new code." = "Файл находится на {server} и доступен только вам. Включите общий доступ, чтобы пригласить других по новому коду." +"This frame is new on the branch." = "Этот Frame добавлен в ветке." +"This frame isn't on the branch." = "Этого Frame нет в ветке." +"This server is open to the public: no key needed." = "Сервер общедоступен: ключ не нужен." +"This server needs its key: add ?key=… to the address (`figcraft-server key` prints it on the server's machine)." = "Сервер требует ключ: добавьте ?key=… к адресу (`figcraft-server key` выводит его на компьютере сервера)." +"This server takes files only with its key: see Advanced." = "Сервер принимает файлы только с ключом: см. «Дополнительно»." +"Tidy up" = "Упорядочить" +"Tile" = "Повторение" +"Tile type" = "Тип повторения" +"Tint" = "Оттенок" +"Title" = "Заголовок" +"Title case" = "Каждое Слово С Прописной" +"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Чтобы поделиться библиотекой с командой, опубликуйте файл на сервере («Поделиться» ▸ «Опубликовать на сервере…»), затем опубликуйте там библиотеку." +"Toggle" = "Переключить" +"Toggle light/dark theme" = "Переключить светлую/тёмную тему" +"Toggle visibility" = "Переключить видимость" +"Tools" = "Инструменты" +"Top" = "Сверху" +"Top & bottom" = "Сверху и снизу" +"Top left radius" = "Радиус верхнего левого угла" +"Top right radius" = "Радиус верхнего правого угла" +"Transform" = "Трансформация" +"Triangle arrow" = "Треугольная стрелка" +"Trigger" = "Триггер" +"True" = "Истина" +"Truncate text" = "Обрезать текст" +"Truncate with an ellipsis" = "Обрезать с многоточием" +"Try again" = "Повторить" +"Try another search or filter." = "Попробуйте другой запрос или фильтр." +"Try another word, or look at every kind." = "Попробуйте другое слово или посмотрите все категории." +"Turn a layer into a component, reuse it, and change every copy at once" = "Превратите слой в компонент, используйте повторно и изменяйте все копии сразу" +"Two digits" = "Две цифры" +"Type" = "Тип" +"Type settings" = "Настройки типографики" +"Type the join code you were given, or paste an invite link." = "Введите полученный код подключения или вставьте ссылку приглашения." +"Typography" = "Типографика" +"Underline" = "Подчёркивание" +"Undo takes back only your own changes; what others changed since stays." = "Отмена затрагивает только ваши изменения; последующие изменения других участников сохраняются." +"Ungroup" = "Разгруппировать" +"Uniform" = "Равномерный" +"Union" = "Объединение" +"Unions" = "Объединения" +"Unknown — treated as sRGB" = "Неизвестный — считается sRGB" +"Unread" = "Непрочитанные" +"Unresolve" = "Снова открыть" +"Unsaved changes" = "Несохранённые изменения" +"Unsaved changes in {names}. Close the window and lose them?" = "Несохранённые изменения в {names}. Закрыть окно и потерять их?" +"Unsaved changes: save first ({save}), or close the tab with {alt}-click to discard" = "Есть несохранённые изменения: сохраните ({save}) или закройте вкладку нажатием с {alt}, чтобы отбросить их" +"Unsaved changes: save first, or close again with {key} to discard" = "Есть несохранённые изменения: сохраните или закройте ещё раз с {key}, чтобы отбросить их" +"Up" = "Вверх" +"Update plugin" = "Обновить плагин" +"Update" = "Обновить" +"Update from main" = "Обновить из основной ветки" +"Update from selection" = "Обновить из выделения" +"Updated {components} and {styles}" = "Обновлено: {components} и {styles}" +"Updated {name}" = "Обновлено: {name}" +"Uploads this file to a FigCraft server and keeps this tab in sync with it. You get a link to edit and a link to view." = "Загружает файл на сервер FigCraft и синхронизирует эту вкладку с ним. Вы получите ссылки для редактирования и просмотра." +"Uppercase" = "Прописные" +"Use a fixed value" = "Использовать фиксированное значение" +"Use an expression, e.g. count + 1" = "Использовать выражение, например count + 1" +"Use as mask" = "Использовать как маску" +"Use auto layout" = "Использовать автолейаут" +"Use number keys for opacity" = "Задавать непрозрачность цифровыми клавишами" +"Use the layer's fill" = "Использовать заливку слоя" +"Used in {files} · {instances}" = "Используется в {files} · {instances}" +"Username" = "Имя пользователя" +"Value" = "Значение" +"Uses variables: each layer shows them in its own mode" = "Используются переменные: каждый слой отображает их в своём режиме" +"Variable" = "Переменная" +"Variable collections" = "Коллекции переменных" +"Variable modes" = "Режимы переменных" +"Variable width" = "Переменная ширина" +"Variables" = "Переменные" +"Variables JSON…" = "JSON переменных…" +"Variables store reusable values (colours, numbers, strings, booleans) with modes such as Light and Dark." = "Переменные хранят значения для повторного использования (цвета, числа, строки, логические значения) с режимами, например светлым и тёмным." +"Variant" = "Вариант" +"Vector" = "Вектор" +"Vectors" = "Векторы" +"Version" = "Версия" +"Version history" = "История версий" +"Version {version}" = "Версия {version}" +"Version {v} · {components} · {styles} · {variables}" = "Версия {v} · {components} · {styles} · {variables}" +"Vertical" = "Вертикально" +"Vertical align" = "Вертикальное выравнивание" +"Vertical hex" = "Вертикальный шестиугольник" +"Vertical padding" = "Вертикальные внутренние отступы" +"Vertical spacing" = "Вертикальный интервал" +"Vertical trim" = "Вертикальная обрезка" +"View" = "Вид" +"View link" = "Ссылка для просмотра" +"View link copied" = "Ссылка для просмотра скопирована" +"View only" = "Только просмотр" +"View only: you see every change as it happens but can't edit. Leave to keep a copy you can edit." = "Только просмотр: вы видите все изменения сразу, но не можете редактировать. Выйдите из сессии, чтобы сохранить редактируемую копию." +"Viewing “{name}”" = "Просмотр «{name}»" +"Violet" = "Фиолетовый" +"Visibility" = "Видимость" +"Visible" = "Видимый" +"Vivid" = "Яркий" +"Warm" = "Тёплый" +"Watch" = "Часы" +"Wavy" = "Волнистый" +"Web" = "Веб" +"Websites" = "Веб-сайты" +"WebSocket URL" = "URL WebSocket" +"WebSocket URL copied" = "URL WebSocket скопирован" +"Wedge" = "Клин" +"Weight" = "Толщина" +"What you try here changes this preview and the code below, not the file" = "Здесь меняются только предпросмотр и код ниже; файл остаётся без изменений" +"What changed since it was marked ready for dev" = "Изменения после отметки «Готово к разработке»" +"What is wrong with this file? The server's operator gets your report." = "Что не так с файлом? Оператор сервера получит вашу жалобу." +"What it's for" = "Назначение" +"What this changed from isn't known: it changed before FigCraft kept a copy of ready-for-dev frames. Resolve the change (or mark it ready again) to compare from now on." = "Исходное состояние неизвестно: изменение произошло до того, как FigCraft начал сохранять копии Frame, готовых к разработке. Разрешите изменение (или снова отметьте готовность), чтобы сравнивать последующие изменения." +"While hovering" = "При наведении" +"While pressing" = "При удержании" +"White" = "Белый" +"Who runs it, its terms and how long it keeps files" = "Оператор, условия и срок хранения файлов" +"Why it changed" = "Причина изменения" +"Widgets" = "Виджеты" +"Width" = "Ширина" +"Width point" = "Точка ширины" +"Width profile" = "Профиль ширины" +"Wireframe kit" = "Набор вайрфреймов" +"With the layers' export settings" = "С настройками экспорта слоёв" +"Wrap" = "Перенос" +"Wrap in new section" = "Поместить в новую секцию" +"Yellow" = "Жёлтый" +"Yesterday" = "Вчера" +"You" = "Вы" +"You'll get the shared file as a new tab and edit it together." = "Общий файл откроется в новой вкладке для совместного редактирования." +"You're spotlighting: everyone follows your view" = "Вы показываете экран всем: участники следуют за вашим видом" +"Your copy of the file stays open" = "Ваша копия файла останется открытой" +"Your copy stays open; the file stays on the server" = "Ваша копия останется открытой; файл останется на сервере" +"Your name" = "Ваше имя" +"Your name in comments" = "Ваше имя в комментариях" +"Your system asks for reduced motion: animations stay off." = "В системе включено уменьшение движения: анимации отключены." +"Z" = "Z" +"Z to A" = "От Я до А" +"Zoom" = "Масштаб" +"Zoom around the pointer" = "Масштабировать вокруг указателя" +"Zoom device to fill window" = "Масштабировать устройство до заполнения окна" +"Zoom in" = "Увеличить" +"Zoom out" = "Уменьшить" +"Zoom to 100%" = "Масштаб 100%" +"Zoom to 200%" = "Масштаб 200%" +"Zoom to 50%" = "Масштаб 50%" +"Zoom to fit" = "Вписать всё" +"Zoom to selection" = "Масштаб по выделению" +"Zoom tool: click to zoom in, drag an area" = "Инструмент масштаба: нажмите для увеличения или выделите область" +"Zoom tool: zoom out" = "Инструмент масштаба: уменьшение" +"and {n} more" = { one = "и ещё {n}", few = "и ещё {n}", many = "и ещё {n}", other = "и ещё {n}" } +"bleed marks" = "метки вылета" +"by {author}" = "автор: {author}" +"call any command its access allows" = "вызывать любые команды в рамках разрешений" +"call only: {commands}" = "вызывать только: {commands}" +"can edit the document" = "может редактировать документ" +"crop marks" = "метки обреза" +"development: {path}" = "разработка: {path}" +"e.g. count + 1" = "например, count + 1" +"e.g. count >= 3 and not done" = "например, count >= 3 and not done" +"figcraft:// links from a browser, chat or email open this app and join the session." = "Ссылки figcraft:// из браузера, чата или почты открывают это приложение и подключают к сессии." +"from {server}" = "с {server}" +"hidden" = "скрыто" +"iOS" = "iOS" +"just now" = "только что" +"locked" = "заблокировано" +"no bleed" = "без вылета" +"nobody else here yet" = "здесь пока больше никого нет" +"now" = "сейчас" +"or" = "или" +"put a widget on the canvas" = "размещать виджет на холсте" +"read and edit the document" = "читать и редактировать документ" +"read the document" = "читать документ" +"reads the document" = "читает документ" +"the plugin" = "плагин" +"this session only" = "только эта сессия" +"yesterday" = "вчера" +"{instances} in {files}" = "{instances} в {files}" +"{language}: draft translation, waiting for a native speaker's review." = "{language}: черновой перевод, ожидает проверки носителем языка." +"{length} bleed" = "вылет {length}" +"{month} {day}, {year}" = "{day} {month} {year}" +"{name} cleared from the list" = "{name} убран из списка" +"{name} is forgotten on this computer; the file stays on {server}" = "{name} забыт на этом компьютере; файл остаётся на {server}" +"{name} is on the server." = "{name} находится на сервере." +"{name} is spotlighting" = "{name} показывает экран всем" +"{name}\nClick to apply to the selection" = "{name}\nНажмите, чтобы применить к выделению" +"{name}\nDrag onto the canvas, or click to place in the middle of the view.\nHold Alt (Option) while dropping onto an instance to swap it." = "{name}\nПеретащите на холст или нажмите, чтобы разместить в центре вида.\nУдерживайте Alt (Option) при переносе на экземпляр, чтобы заменить его." +"{name} (from {library})" = "{name} (из {library})" +"{name} (you)" = "{name} (вы)" +"{name} ({layers} layers)" = "{name} (слоёв: {layers})" +"{name} ({tag}): {min} – {max}, default {default}. Double-click the slider to go back to the style's value; Escape while dragging cancels." = "{name} ({tag}): {min} – {max}, по умолчанию {default}. Двойное нажатие на ползунок возвращает значение стиля; Escape при перетаскивании отменяет изменение." +"{name} from {path}" = "{name} из {path}" +"{name} was removed from the server" = "{name} удалён с сервера" +"{name} · deleted on main" = "{name} · удалено в основной ветке" +"{name} · deleted on the branch" = "{name} · удалено в ветке" +"{name} · not published" = "{name} · не опубликовано" +"{name} · on a server" = "{name} · на сервере" +"{name} · published version {v}" = "{name} · опубликована версия {v}" +"{name} · version {v} on the server" = "{name} · версия {v} на сервере" +"{name}, in a live session" = "{name}, в совместной сессии" +"{name}, online, open only to you" = "{name}, онлайн, доступен только вам" +"{name}, {kind}, {w} by {h}, selected" = "{name}, {kind}, {w} на {h}, выделено" +"{n} added" = { one = "Добавлено: {n}", few = "Добавлено: {n}", many = "Добавлено: {n}", other = "Добавлено: {n}" } +"{n} autosave versions" = { one = "{n} автосохранённая версия", few = "{n} автосохранённые версии", many = "{n} автосохранённых версий", other = "{n} автосохранённой версии" } +"{n} changed" = { one = "Изменено: {n}", few = "Изменено: {n}", many = "Изменено: {n}", other = "Изменено: {n}" } +"{n} columns" = { one = "{n} столбец", few = "{n} столбца", many = "{n} столбцов", other = "{n} столбца" } +"{n} components" = { one = "{n} компонент", few = "{n} компонента", many = "{n} компонентов", other = "{n} компонента" } +"{n} components and styles have newer versions" = { one = "{n} компонент или стиль имеет новую версию", few = "{n} компонента или стиля имеют новые версии", many = "{n} компонентов и стилей имеют новые версии", other = "{n} компонента или стиля имеют новые версии" } +"{n} conflicts to resolve" = { one = "{n} конфликт для разрешения", few = "{n} конфликта для разрешения", many = "{n} конфликтов для разрешения", other = "{n} конфликта для разрешения" } +"{n} days ago" = { one = "{n} день назад", few = "{n} дня назад", many = "{n} дней назад", other = "{n} дня назад" } +"{n} files" = { one = "{n} файл", few = "{n} файла", many = "{n} файлов", other = "{n} файла" } +"{n} hours ago" = { one = "{n} час назад", few = "{n} часа назад", many = "{n} часов назад", other = "{n} часа назад" } +"{n} instances" = { one = "{n} экземпляр", few = "{n} экземпляра", many = "{n} экземпляров", other = "{n} экземпляра" } +"{n} layers" = { one = "{n} слой", few = "{n} слоя", many = "{n} слоёв", other = "{n} слоя" } +"{n} layers as one file" = { one = "{n} слой в одном файле", few = "{n} слоя в одном файле", many = "{n} слоёв в одном файле", other = "{n} слоя в одном файле" } +"{n} layers selected" = { one = "Выделен {n} слой", few = "Выделены {n} слоя", many = "Выделено {n} слоёв", other = "Выделено {n} слоя" } +"{n} layers · {parts}" = { one = "{n} слой · {parts}", few = "{n} слоя · {parts}", many = "{n} слоёв · {parts}", other = "{n} слоя · {parts}" } +"{n} matches" = { one = "{n} совпадение", few = "{n} совпадения", many = "{n} совпадений", other = "{n} совпадения" } +"{n} minutes ago" = { one = "{n} минуту назад", few = "{n} минуты назад", many = "{n} минут назад", other = "{n} минуты назад" } +"{n} months ago" = { one = "{n} месяц назад", few = "{n} месяца назад", many = "{n} месяцев назад", other = "{n} месяца назад" } +"{n} of {total}" = "{n} из {total}" +"{n} people joined" = { one = "Подключился {n} участник", few = "Подключились {n} участника", many = "Подключились {n} участников", other = "Подключилось {n} участника" } +"{n} preferred components" = { one = "{n} предпочтительный компонент", few = "{n} предпочтительных компонента", many = "{n} предпочтительных компонентов", other = "{n} предпочтительного компонента" } +"{n} ready" = { one = "Готово: {n}", few = "Готово: {n}", many = "Готово: {n}", other = "Готово: {n}" } +"{n} removed" = { one = "Удалено: {n}", few = "Удалено: {n}", many = "Удалено: {n}", other = "Удалено: {n}" } +"{n} replies" = { one = "{n} ответ", few = "{n} ответа", many = "{n} ответов", other = "{n} ответа" } +"{n} rows" = { one = "{n} строка", few = "{n} строки", many = "{n} строк", other = "{n} строки" } +"{n} styles" = { one = "{n} стиль", few = "{n} стиля", many = "{n} стилей", other = "{n} стиля" } +"{n} variables" = { one = "{n} переменная", few = "{n} переменные", many = "{n} переменных", other = "{n} переменной" } +"{n} weeks ago" = { one = "{n} неделю назад", few = "{n} недели назад", many = "{n} недель назад", other = "{n} недели назад" } +"{n} years ago" = { one = "{n} год назад", few = "{n} года назад", many = "{n} лет назад", other = "{n} года назад" } +"{n}d" = "{n} д" +"{n}h" = "{n} ч" +"{n}m" = "{n} мин" +"{n}w" = "{n} нед" +"{page}, nothing selected" = "{page}, ничего не выделено" +"{profile} (current)" = "{profile} (текущий)" +"{property} is its value; {property ? \"a\" : \"b\"} picks a string. Other {…} stays as written." = "{property} — значение свойства; {property ? \"a\" : \"b\"} выбирает строку. Другие {…} остаются как есть." +"{server} can't say what became of your files (it is older than this app)." = "{server} не может сообщить, что стало с вашими файлами (версия сервера старее приложения)." +"{server} · No longer on the server" = "{server} · Больше нет на сервере" +"{server} · {shared} · Opened {date}" = "{server} · {shared} · Открыто {date}" +"{tool} hold {key}" = "{tool} удерживайте {key}" +"{value} isn't a colour" = "{value} не является цветом" +"{when} · edited" = "{when} · изменено" +"← ↑ → ↓" = "← ↑ → ↓" +"← → ↑ ↓" = "← → ↑ ↓" +"←" = "←" +"→, Space or N" = "→, Пробел или N" +"↵" = "↵" +"⇥ / ⇧⇥" = "⇥ / ⇧⇥" +"⌘ arrows" = "⌘ стрелки" +"⌘ click" = "⌘ нажатие" +"⌘ scroll" = "⌘ прокрутка" +"⌥ drag" = "⌥ перетаскивание" + +[commands] +"align.bottom" = "Выровнять по нижнему краю" +"align.hcenter" = "Выровнять центры по горизонтали" +"align.left" = "Выровнять по левому краю" +"align.right" = "Выровнять по правому краю" +"align.top" = "Выровнять по верхнему краю" +"align.vcenter" = "Выровнять центры по вертикали" +"annotation.add" = "Добавить аннотацию" +"annotation.addCategory" = "Добавить категорию аннотаций" +"annotation.categories" = "Категории аннотаций" +"annotation.edit" = "Изменить аннотацию" +"annotation.editCategory" = "Изменить категорию аннотаций" +"annotation.list" = "Список аннотаций" +"annotation.remove" = "Удалить аннотацию" +"annotation.removeCategory" = "Удалить категорию аннотаций" +"app.actions" = "Действия" +"app.preferences" = "Настройки" +"arrange.back" = "На задний план" +"arrange.backward" = "На уровень назад" +"arrange.forward" = "На уровень вперёд" +"arrange.front" = "На передний план" +"arrange.packH" = "Уплотнить по горизонтали" +"arrange.packV" = "Уплотнить по вертикали" +"arrange.roundToPixels" = "Округлить до пикселей" +"arrange.tidy" = "Упорядочить" +"bool.exclude" = "Исключить выделение" +"bool.flatten" = "Свести булеву группу" +"bool.intersect" = "Пересечь выделение" +"bool.subtract" = "Вычесть выделение" +"bool.union" = "Объединить выделение" +"branch.archive" = "Архивировать ветку" +"branch.create" = "Создать ветку" +"branch.list" = "Список веток" +"branch.merge" = "Объединить ветку" +"branch.open" = "Открыть ветку" +"branch.review" = "Проверить ветку" +"branch.updateFromMain" = "Обновить из основной ветки" +"codeConnect.export" = "Экспортировать связи с кодом" +"codeConnect.import" = "Импортировать связи с кодом" +"codeConnect.remove" = "Отвязать от кода" +"codeConnect.set" = "Связать с кодом" +"collab.cursor" = "Показать курсор в точке" +"collab.cursorChat" = "Чат у курсора" +"collab.diff" = "Показать изменения синхронизации" +"collab.files" = "Ваши онлайн-файлы" +"collab.follow" = "Следить" +"collab.forgetFile" = "Забыть онлайн-файл" +"collab.host" = "Начать совместную сессию" +"collab.join" = "Подключиться к совместной сессии" +"collab.leave" = "Покинуть совместную сессию" +"collab.manageFile" = "Управлять опубликованным файлом" +"collab.openFile" = "Открыть онлайн-файл" +"collab.peers" = "Участники в этом файле" +"collab.publish" = "Опубликовать на сервере" +"collab.recoverFile" = "Восстановить онлайн-файл" +"collab.registerLinks" = "Открывать ссылки приглашения в FigCraft" +"collab.report" = "Пожаловаться на файл оператору сервера" +"collab.serverInfo" = "Информация о сервере" +"collab.share" = "Поделиться по коду подключения" +"collab.spotlight" = "Показывать мой экран всем" +"collab.stop" = "Завершить совместную сессию" +"command.batch" = "Выполнить несколько команд" +"comment.add" = "Добавить комментарий" +"comment.delete" = "Удалить комментарий" +"comment.edit" = "Изменить комментарий" +"comment.link" = "Копировать ссылку на комментарий" +"comment.list" = "Список комментариев" +"comment.markRead" = "Отметить комментарий как прочитанный" +"comment.move" = "Переместить комментарий" +"comment.next" = "Следующий комментарий" +"comment.open" = "Открыть комментарий" +"comment.people" = "Участники комментариев" +"comment.react" = "Добавить реакцию на комментарий" +"comment.reply" = "Ответить на комментарий" +"comment.resolve" = "Отметить комментарий как решённый" +"comment.zoom" = "Масштаб по комментариям" +"component.addProperty" = "Добавить свойство компонента" +"component.addVariant" = "Добавить вариант" +"component.bindProperty" = "Привязать слой к свойству" +"component.combineVariants" = "Объединить в варианты" +"component.create" = "Создать компонент" +"component.createMultiple" = "Создать несколько компонентов" +"component.deleteProperty" = "Удалить свойство компонента" +"component.detach" = "Отсоединить экземпляр" +"component.editProperty" = "Изменить свойство компонента" +"component.exposeInstance" = "Открыть доступ к вложенному экземпляру" +"component.goToMain" = "Перейти к основному компоненту" +"component.hideWhenPublishing" = "Скрывать при публикации" +"component.list" = "Список компонентов" +"component.makeSlot" = "Сделать слотом" +"component.playground" = "Песочница компонента" +"component.properties" = "Свойства компонента" +"component.pushChanges" = "Перенести изменения в основной компонент" +"component.removeSlot" = "Убрать слот" +"component.resetInstance" = "Сбросить все изменения" +"component.resetOverride" = "Сбросить переопределение" +"component.restoreMain" = "Восстановить основной компонент" +"dev.compare" = "Сравнить изменения" +"dev.focus" = "Режим фокуса" +"dev.resolveChange" = "Разрешить изменение" +"dev.unfocus" = "Выйти из режима фокуса" +"devResource.add" = "Добавить ресурс для разработки" +"devResource.edit" = "Изменить ресурс для разработки" +"devResource.list" = "Список ресурсов для разработки" +"devResource.remove" = "Удалить ресурс для разработки" +"distribute.horizontal" = "Распределить по горизонтали" +"distribute.vertical" = "Распределить по вертикали" +"doc.home" = "Главная" +"doc.list" = "Список документов" +"doc.nextTab" = "Следующая вкладка" +"doc.previousTab" = "Предыдущая вкладка" +"doc.switch" = "Переключить документ" +"edit.copy" = "Копировать" +"edit.copyAsCss" = "Копировать как CSS" +"edit.copyAsPng" = "Копировать как PNG" +"edit.copyAsSvg" = "Копировать как SVG" +"edit.copyAsText" = "Копировать как текст" +"edit.copyLink" = "Копировать ссылку на выделение" +"edit.copyProperties" = "Копировать свойства" +"edit.cut" = "Вырезать" +"edit.delete" = "Удалить" +"edit.duplicate" = "Дублировать" +"edit.findReplace" = "Найти и заменить" +"edit.paste" = "Вставить" +"edit.pasteOver" = "Вставить поверх выделения" +"edit.pasteProperties" = "Вставить свойства" +"edit.pasteReplace" = "Вставить с заменой" +"edit.pickColor" = "Пипетка" +"edit.redo" = "Повторить" +"edit.repeat" = "Повторить последнюю команду" +"edit.setDefaultProperties" = "Задать свойства по умолчанию" +"edit.undo" = "Отменить" +"effect.add" = "Добавить эффект" +"effect.move" = "Переместить эффект" +"effect.remove" = "Удалить эффект" +"effect.set" = "Задать эффект" +"export.add" = "Добавить настройку экспорта" +"export.remove" = "Удалить настройку экспорта" +"export.update" = "Изменить настройку экспорта" +"file.close" = "Закрыть вкладку" +"file.colorProfile" = "Цветовой профиль" +"file.duplicate" = "Дублировать файл" +"file.export" = "Экспорт" +"file.exportFramesPdf" = "Экспортировать Frame в PDF" +"file.exportLayers" = "Экспортировать слои" +"file.exportSvg" = "Экспортировать выделение в SVG" +"file.import" = "Импорт" +"file.new" = "Новый файл дизайна" +"file.newFromTemplate" = "Создать из шаблона" +"file.open" = "Открыть" +"file.openRecent" = "Открыть недавние" +"file.placeImage" = "Разместить изображение" +"file.recover" = "Восстановить файл" +"file.rename" = "Переименовать файл" +"file.revert" = "Вернуть сохранённое состояние" +"file.save" = "Сохранить" +"file.saveAs" = "Сохранить как" +"file.saveVersion" = "Сохранить в историю версий" +"file.versionHistory" = "Показать историю версий" +"fill.add" = "Добавить заливку" +"fill.move" = "Переместить заливку" +"fill.pattern" = "Заливка паттерном" +"fill.remove" = "Удалить заливку" +"fill.set" = "Задать заливку" +"frame.create" = "Создать Frame" +"frame.presets" = "Список пресетов Frame" +"frame.swapOrientation" = "Сменить ориентацию" +"grid.add" = "Добавить сетку макета" +"grid.move" = "Переместить сетку макета" +"grid.remove" = "Удалить сетку макета" +"guide.add" = "Добавить направляющую" +"guide.clear" = "Удалить все направляющие" +"guide.move" = "Переместить направляющую" +"guide.remove" = "Удалить направляющую" +"guide.select" = "Выделить направляющую" +"help.gettingStarted" = "Начало работы" +"help.about" = "О FigCraft" +"help.page" = "Страница справки" +"help.shortcuts" = "Сочетания клавиш" +"image.adjust" = "Коррекция изображения" +"image.deletePreset" = "Удалить пресет изображения" +"image.place" = "Разместить изображение" +"image.presets" = "Пресеты изображения" +"image.savePreset" = "Сохранить пресет изображения" +"inspect.assets" = "Инспектор ресурсов" +"inspect.code" = "Код слоя" +"inspect.componentCode" = "Код компонента" +"inspect.properties" = "Инспектор свойств" +"inspect.readyForDev" = "Готово к разработке" +"instance.create" = "Создать экземпляр" +"instance.exposed" = "Доступные вложенные экземпляры" +"instance.place" = "Разместить экземпляр" +"instance.resetSlot" = "Сбросить слот" +"instance.setProperties" = "Задать свойства экземпляра" +"instance.slotInsert" = "Вставить в слот" +"instance.swap" = "Заменить экземпляр" +"instance.swapAt" = "Заменить экземпляр в точке" +"layers.collapseAll" = "Свернуть слои" +"layout.add" = "Добавить автолейаут" +"layout.remove" = "Убрать автолейаут" +"layout.setTrack" = "Задать размер дорожки сетки" +"layout.suggest" = "Предложить автолейаут" +"libraries.open" = "Библиотеки" +"libraries.publish" = "Опубликовать библиотеку…" +"library.acceptUpdates" = "Принять обновления библиотеки" +"library.add" = "Добавить библиотеку" +"library.aliasVariable" = "Создать алиас переменной библиотеки" +"library.applyStyle" = "Применить стиль библиотеки" +"library.bindVariable" = "Привязать переменную библиотеки" +"library.extendCollection" = "Расширить коллекцию библиотеки" +"library.import" = "Импортировать компонент библиотеки" +"library.importVariable" = "Импортировать переменную библиотеки" +"library.list" = "Список библиотек" +"library.place" = "Разместить компонент библиотеки" +"library.publish" = "Опубликовать библиотеку" +"library.publishToServer" = "Опубликовать библиотеку на сервере" +"library.remove" = "Удалить библиотеку" +"library.setVariableMode" = "Задать режим переменных библиотеки" +"library.swap" = "Заменить библиотеку" +"library.swapAt" = "Заменить экземпляр компонентом библиотеки" +"library.updates" = "Обновления библиотеки" +"library.usage" = "Использование библиотеки" +"measure.distances" = "Измерить расстояния" +"measure.padding" = "Измерить внутренние отступы автолейаута" +"node.create" = "Создать слой" +"node.get" = "Получить слой" +"node.reorder" = "Изменить порядок слоя" +"node.reparent" = "Переместить слой в" +"node.set" = "Задать свойства" +"object.batchRename" = "Массовое переименование" +"object.convertToFrame" = "Преобразовать во Frame" +"object.convertToGroup" = "Преобразовать в группу" +"object.flipH" = "Отразить по горизонтали" +"object.flipV" = "Отразить по вертикали" +"object.frameSelection" = "Поместить выделение во Frame" +"object.group" = "Сгруппировать выделение" +"object.hideOthers" = "Скрыть другие слои" +"object.mask" = "Использовать как маску" +"object.moveToPage" = "Переместить на страницу" +"object.opacityKeys" = "Непрозрачность цифровыми клавишами" +"object.rasterize" = "Растрировать выделение" +"object.releaseMask" = "Снять маску" +"object.removeFill" = "Удалить заливку" +"object.removeFrame" = "Убрать Frame" +"object.removeStroke" = "Удалить обводку" +"object.rename" = "Переименовать" +"object.resizeToFit" = "Подогнать размер" +"object.rotate180" = "Повернуть на 180°" +"object.rotateLeft" = "Повернуть на 90° влево" +"object.rotateRight" = "Повернуть на 90° вправо" +"object.setThumbnail" = "Использовать как миниатюру" +"object.swapFillStroke" = "Поменять заливку и обводку" +"object.toggleLock" = "Заблокировать/разблокировать выделение" +"object.toggleVisible" = "Показать/скрыть выделение" +"object.ungroup" = "Разгруппировать выделение" +"object.wrapInSection" = "Поместить в новую секцию" +"page.add" = "Добавить страницу" +"page.delete" = "Удалить страницу" +"page.duplicate" = "Дублировать страницу" +"page.list" = "Список страниц" +"page.move" = "Переместить страницу" +"page.rename" = "Переименовать страницу" +"page.select" = "Перейти к странице" +"page.setBackground" = "Фон страницы" +"paint.documentColors" = "Цвета документа" +"plugin.close" = "Закрыть панель плагина" +"plugin.directory.install" = "Установить плагин из каталога" +"plugin.directory.list" = "Обзор каталога плагинов" +"plugin.directory.publish" = "Опубликовать плагин в каталоге" +"plugin.directory.unpublish" = "Снять плагин с публикации в каталоге" +"plugin.event" = "Использовать элемент панели плагина" +"plugin.import" = "Импортировать плагин из манифеста…" +"plugin.list" = "Список плагинов" +"plugin.remove" = "Удалить плагин" +"plugin.run" = "Запустить плагин" +"plugin.ui" = "Панель плагина" +"plugins.manage" = "Управление плагинами…" +"plugins.runLast" = "Запустить последний плагин" +"pref.highlightHover" = "Подсвечивать слои при наведении" +"pref.keepTool" = "Оставлять инструмент выбранным после использования" +"pref.language" = "Язык интерфейса" +"pref.nudge" = "Шаг сдвига" +"pref.numberKeysOpacity" = "Задавать непрозрачность цифровыми клавишами" +"pref.publishServer" = "Сервер для онлайн-публикации" +"pref.reduceMotion" = "Уменьшить движение" +"pref.renameDuplicates" = "Переименовывать дубликаты слоёв" +"pref.showDimensions" = "Показать размеры объектов" +"pref.userName" = "Ваше имя в комментариях" +"proto.action.add" = "Добавить действие прототипа" +"proto.action.move" = "Изменить порядок действия прототипа" +"proto.action.remove" = "Удалить действие прототипа" +"proto.action.set" = "Изменить действие прототипа" +"proto.closePreview" = "Закрыть предпросмотр прототипа" +"proto.conditional.addBlock" = "Добавить «Иначе если» / «Иначе»" +"proto.conditional.removeBlock" = "Удалить «Если» / «Иначе если» / «Иначе»" +"proto.conditional.setCondition" = "Изменить условие" +"proto.evaluate" = "Вычислить выражение прототипа" +"proto.flow.add" = "Добавить начальную точку сценария" +"proto.flow.remove" = "Удалить сценарий" +"proto.flow.rename" = "Переименовать сценарий" +"proto.interaction.add" = "Добавить взаимодействие" +"proto.interaction.clear" = "Удалить все взаимодействия" +"proto.interaction.remove" = "Удалить взаимодействие" +"proto.interaction.set" = "Изменить взаимодействие" +"proto.present" = "Презентация" +"proto.presentOptions" = "Параметры презентации" +"proto.preview" = "Открыть предпросмотр прототипа" +"proto.settings" = "Настройки прототипа" +"section.toggleContents" = "Скрыть/показать содержимое секции" +"select.add" = "Добавить к выделению" +"select.all" = "Выделить всё" +"select.area" = "Выделить область" +"select.byName" = "Выделить слои по имени" +"select.children" = "Выделить дочерние слои" +"select.inverse" = "Инвертировать выделение" +"select.layersAt" = "Слои в точке" +"select.matching" = "Выделить соответствующие слои" +"select.next" = "Выделить следующий соседний слой" +"select.none" = "Снять выделение" +"select.parent" = "Выделить родителя" +"select.previous" = "Выделить предыдущий соседний слой" +"select.remove" = "Убрать из выделения" +"select.sameEffect" = "Выделить с одинаковым эффектом" +"select.sameFill" = "Выделить с одинаковой заливкой" +"select.sameFont" = "Выделить с одинаковым шрифтом" +"select.sameInstance" = "Выделить одинаковые экземпляры" +"select.sameProperties" = "Выделить с одинаковыми свойствами" +"select.sameStroke" = "Выделить с одинаковой обводкой" +"select.sameText" = "Выделить с одинаковыми свойствами текста" +"select.set" = "Выделить" +"shape.arrow" = "Стрелка" +"shape.ellipse" = "Эллипс" +"shape.line" = "Линия" +"shape.polygon" = "Многоугольник" +"shape.rectangle" = "Прямоугольник" +"shape.star" = "Звезда" +"shapeBuilder.merge" = "Объединить области" +"shapeBuilder.mode" = "Режим конструктора фигур" +"shapeBuilder.regions" = "Области конструктора фигур" +"shapeBuilder.remove" = "Удалить области" +"stroke.add" = "Добавить обводку" +"stroke.brush" = "Кисть" +"stroke.move" = "Переместить обводку" +"stroke.remove" = "Удалить обводку" +"stroke.set" = "Задать обводку" +"stroke.widthProfile" = "Профиль ширины" +"style.apply" = "Применить стиль" +"style.bind" = "Привязать переменную в стиле" +"style.create" = "Создать стиль" +"style.delete" = "Удалить стиль" +"style.detach" = "Отвязать стиль" +"style.duplicate" = "Дублировать стиль" +"style.list" = "Список стилей" +"style.move" = "Переместить стили" +"style.renameFolder" = "Переименовать папку стилей" +"style.update" = "Изменить стиль" +"text.alignCenter" = "Выровнять по центру" +"text.alignJustify" = "По ширине" +"text.alignLeft" = "Выровнять по левому краю" +"text.alignRight" = "Выровнять по правому краю" +"text.bold" = "Полужирный" +"text.bulletList" = "Маркированный список" +"text.case" = "Регистр букв" +"text.caseLower" = "Строчные" +"text.caseOriginal" = "Исходный регистр" +"text.caseSmallCaps" = "Капитель" +"text.caseTitle" = "Каждое Слово С Прописной" +"text.caseUpper" = "Прописные" +"text.create" = "Текст" +"text.decoration" = "Оформление" +"text.edit.begin" = "Редактировать текст" +"text.edit.caret" = "Установить текстовый курсор" +"text.edit.end" = "Завершить редактирование текста" +"text.edit.insert" = "Ввести текст" +"text.edit.key" = "Клавиша текста" +"text.edit.preedit" = "Составить текст (IME)" +"text.edit.selectLine" = "Выделить строку" +"text.edit.selectWord" = "Выделить слово" +"text.edit.selection" = "Выделение текста" +"text.endPreview" = "Завершить предпросмотр текста" +"text.fonts" = "Список шрифтов" +"text.italic" = "Курсив" +"text.lineHeightDown" = "Уменьшить межстрочный интервал" +"text.lineHeightUp" = "Увеличить межстрочный интервал" +"text.link" = "Создать ссылку" +"text.listIndent" = "Увеличить отступ списка" +"text.listOutdent" = "Уменьшить отступ списка" +"text.listStyle" = "Стиль списка" +"text.maxLines" = "Максимум строк" +"text.numberList" = "Нумерованный список" +"text.onPath" = "Текст на пути" +"text.paragraphIndent" = "Абзацный отступ" +"text.paragraphSpacing" = "Интервал между абзацами" +"text.pathOptions" = "Параметры текста на пути" +"text.preview" = "Предпросмотр изменения текста" +"text.removeFromPath" = "Снять текст с пути" +"text.set" = "Задать текст" +"text.setFeatures" = "Функции OpenType" +"text.setVariations" = "Оси вариативного шрифта" +"text.sizeDown" = "Уменьшить размер шрифта" +"text.sizeUp" = "Увеличить размер шрифта" +"text.strikethrough" = "Зачёркивание" +"text.style" = "Оформить диапазон текста" +"text.trackingDown" = "Уменьшить межбуквенный интервал" +"text.trackingUp" = "Увеличить межбуквенный интервал" +"text.underline" = "Подчёркивание" +"text.weightDown" = "Уменьшить толщину шрифта" +"text.weightUp" = "Увеличить толщину шрифта" +"tool.arrow" = "Стрелка" +"tool.comment" = "Комментарий" +"tool.ellipse" = "Эллипс" +"tool.frame" = "Frame" +"tool.hand" = "Рука" +"tool.line" = "Линия" +"tool.move" = "Перемещение" +"tool.pen" = "Перо" +"tool.pencil" = "Карандаш" +"tool.pencilBrush" = "Кисть карандаша" +"tool.place" = "Разместить выбранным инструментом" +"tool.polygon" = "Многоугольник" +"tool.rectangle" = "Прямоугольник" +"tool.scale" = "Масштаб" +"tool.scaleAnchor" = "Центр масштабирования" +"tool.section" = "Секция" +"tool.set" = "Выбрать инструмент" +"tool.shapeBuilder" = "Конструктор фигур" +"tool.slice" = "Срез" +"tool.star" = "Звезда" +"tool.text" = "Текст" +"transform.move" = "Перемещение" +"transform.moveTo" = "Переместить в" +"transform.nudge" = "Сдвинуть" +"transform.nudgeSize" = "Изменить размер на шаг сдвига" +"transform.resize" = "Изменить размер" +"transform.rotate" = "Повернуть" +"transform.rotationOrigin" = "Переместить центр вращения" +"transform.scale" = "Масштаб" +"transform.setRotationOrigin" = "Задать центр вращения" +"variable.addMode" = "Добавить режим" +"variable.bind" = "Привязать переменную" +"variable.create" = "Создать переменную" +"variable.createCollection" = "Создать коллекцию переменных" +"variable.delete" = "Удалить переменную" +"variable.deleteCollection" = "Удалить коллекцию" +"variable.duplicate" = "Дублировать переменную" +"variable.export" = "Экспортировать переменные" +"variable.import" = "Импортировать переменные" +"variable.list" = "Список переменных" +"variable.moveMode" = "Переместить режим" +"variable.removeMode" = "Удалить режим" +"variable.renameCollection" = "Переименовать коллекцию" +"variable.renameMode" = "Переименовать режим" +"variable.resetOverride" = "Сбросить переопределение переменной" +"variable.set" = "Задать значение переменной" +"variable.setMode" = "Задать режим переменных" +"variable.update" = "Изменить переменную" +"vector.create" = "Вектор" +"vector.deleteHeal" = "Удалить выделение и восстановить путь" +"vector.edit.addVertex" = "Добавить точку" +"vector.edit.begin" = "Редактировать вектор" +"vector.edit.bend" = "Изогнуть сегмент" +"vector.edit.bounds" = "Границы выделенных точек" +"vector.edit.connect" = "Соединить точки" +"vector.edit.cornerRadius" = "Радиус угла точки" +"vector.edit.cut" = "Разрезать путь" +"vector.edit.delete" = "Удалить точки" +"vector.edit.end" = "Завершить редактирование" +"vector.edit.erase" = "Стереть" +"vector.edit.lasso" = "Выделить лассо" +"vector.edit.mirroring" = "Зеркальность направляющих" +"vector.edit.moveHandles" = "Переместить направляющие" +"vector.edit.moveVertices" = "Переместить точки" +"vector.edit.nudge" = "Сдвинуть точки" +"vector.edit.paint" = "Заливка области" +"vector.edit.pen" = "Точка пера" +"vector.edit.penEnd" = "Завершить путь пера" +"vector.edit.position" = "Положение точки" +"vector.edit.resize" = "Изменить размер выделенных точек" +"vector.edit.rotate" = "Повернуть точки" +"vector.edit.select" = "Выделить точки" +"vector.edit.selectAll" = "Выделить все точки" +"vector.edit.setHandle" = "Задать направляющую" +"vector.edit.smooth" = "Сгладить или заострить точки" +"vector.edit.state" = "Состояние редактирования вектора" +"vector.edit.strokeCap" = "Окончание обводки в точке" +"vector.edit.strokeJoin" = "Соединение обводки в точке" +"vector.edit.tool" = "Инструмент редактирования вектора" +"vector.edit.transform" = "Трансформировать точки" +"vector.edit.widthPoint" = "Точка ширины" +"vector.flatten" = "Свести" +"vector.join" = "Соединить выделение" +"vector.offset" = "Сместить путь" +"vector.outlineStroke" = "Преобразовать обводку в контур" +"vector.reverse" = "Обратить направление пути" +"vector.simplify" = "Упростить вектор" +"vector.smoothJoin" = "Плавно соединить выделение" +"version.autosave" = "Автосохранить версию" +"version.delete" = "Удалить версию" +"version.duplicate" = "Дублировать версию" +"version.exitPreview" = "Вернуться к текущей версии" +"version.list" = "Список версий" +"version.preview" = "Просмотреть версию" +"version.rename" = "Переименовать версию" +"version.restore" = "Восстановить версию" +"view.assets" = "Панель ресурсов" +"view.comments" = "Комментарии" +"view.contextMenu" = "Показать контекстное меню" +"view.design" = "Панель дизайна" +"view.designPrototype" = "Переключить дизайн/прототип" +"view.devMode" = "Dev Mode" +"view.frameOutlines" = "Контуры Frame" +"view.get" = "Получить вид" +"view.layers" = "Панель слоёв" +"view.layoutGrids" = "Сетки макета" +"view.leftSidebar" = "Показать/скрыть левую панель" +"view.maskOutlines" = "Контуры масок" +"view.multiplayerCursors" = "Курсоры участников" +"view.nextFrame" = "Масштаб по следующему Frame" +"view.nextPage" = "Следующая страница" +"view.nextRegion" = "Фокус на следующей области" +"view.outlines" = "Контуры" +"view.outlinesHiddenLayers" = "Включить скрытые слои в контуры" +"view.outlinesObjectBounds" = "Включить границы объектов в контуры" +"view.outlinesSelection" = "Контуры выделения" +"view.pan" = "Переместить вид" +"view.panelWidths" = "Сбросить ширину панелей" +"view.pixelGrid" = "Пиксельная сетка" +"view.pixelPreview" = "Пиксельный предпросмотр" +"view.prevFrame" = "Масштаб по предыдущему Frame" +"view.prevPage" = "Предыдущая страница" +"view.previousRegion" = "Фокус на предыдущей области" +"view.proofSrgb" = "Экранная цветопроба sRGB" +"view.fullscreen" = "Полный экран" +"view.prototype" = "Панель прототипа" +"view.rulers" = "Линейки" +"view.set" = "Задать вид" +"view.slices" = "Срезы" +"view.snapGeometry" = "Привязка к геометрии" +"view.snapObjects" = "Привязка к объектам" +"view.snapPixel" = "Привязка к пиксельной сетке" +"view.themeDark" = "Тёмная тема" +"view.themeLight" = "Светлая тема" +"view.themeSystem" = "Системная тема" +"view.toggleUi" = "Показать/скрыть интерфейс" +"view.zoom100" = "Масштаб 100%" +"view.zoom200" = "Масштаб 200%" +"view.zoom50" = "Масштаб 50%" +"view.zoomAt" = "Инструмент масштаба" +"view.zoomFit" = "Вписать всё" +"view.zoomIn" = "Увеличить" +"view.zoomOut" = "Уменьшить" +"view.zoomSelection" = "Масштаб по выделению" +"widget.click" = "Нажать виджет" +"widget.insert" = "Вставить виджет" +"widget.list" = "Список виджетов" +"window.focus" = "Вывести окно на передний план" +"window.new" = "Новое окно" +"template.list" = "Список шаблонов" + +[menu] +"bool.exclude" = "Исключение" +"bool.flatten" = "Свести" +"bool.intersect" = "Пересечение" +"bool.subtract" = "Вычитание" +"bool.union" = "Объединение" +"file.duplicate" = "Дублировать" +"file.import" = "Импортировать из файла…" +"file.rename" = "Переименовать" +"libraries.open" = "Открыть библиотеки" +"select.sameEffect" = "Эффект" +"select.sameFill" = "Заливка" +"select.sameFont" = "Шрифт" +"select.sameInstance" = "Экземпляр" +"select.sameProperties" = "Свойства" +"select.sameStroke" = "Обводка" +"select.sameText" = "Свойства текста" +"text.alignCenter" = "Выровнять текст по центру" +"text.alignJustify" = "Выровнять текст по ширине" +"text.alignLeft" = "Выровнять текст по левому краю" +"text.alignRight" = "Выровнять текст по правому краю" +"view.assets" = "Ресурсы" +"view.design" = "Дизайн" +"view.layers" = "Слои" +"view.outlines" = "Показать контуры" +"view.outlinesHiddenLayers" = "Включить скрытые слои" +"view.outlinesObjectBounds" = "Включить границы объектов" +"view.outlinesSelection" = "Показать контуры выделения" +"view.prototype" = "Прототип" +"view.themeDark" = "Тёмная" +"view.themeLight" = "Светлая" +"view.themeSystem" = "Системная" diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 3b8e1b1a..46ccd909 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -13,8 +13,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; /// One catalog entry. pub enum Text { Plain(&'static str), - /// Plural forms: `one` (when the language has it) and `other`. - Plural(Option<&'static str>, &'static str), + /// Optional language-specific plural forms, with `other` as the fallback. + Plural { + one: Option<&'static str>, + few: Option<&'static str>, + many: Option<&'static str>, + other: &'static str, + }, } /// A language's catalog (generated from `locales/.toml`). @@ -104,7 +109,7 @@ fn lookup(lang: usize, key: &str) -> Option<&'static Text> { /// two translations gets a context after `|` ("Fill|sizing"); the English catalog maps it back. pub fn tr(key: &str) -> &str { match lookup(CURRENT.load(Ordering::Relaxed), key) { - Some(Text::Plain(s) | Text::Plural(_, s)) => s, + Some(Text::Plain(s) | Text::Plural { other: s, .. }) => s, None => key, } } @@ -123,26 +128,59 @@ pub fn trf(key: &str, args: &[(&str, &dyn Display)]) -> String { /// The plural form of `key` (its English "other" form) for count `n`, with `{n}` and `args` filled. pub fn trn(key: &str, n: usize, args: &[(&str, &dyn Display)]) -> String { let lang = CURRENT.load(Ordering::Relaxed); - let text = match lookup(lang, key) { - Some(Text::Plural(one, other)) => one.filter(|_| plural_one(lang, n)).unwrap_or(other), - Some(Text::Plain(s)) => s, - None => key, - }; + let text = counted(lang, key, n); let mut all: Vec<(&str, &dyn Display)> = vec![("n", &n)]; all.extend_from_slice(args); fill(text, &all) } +/// Pick a plural using the catalog that supplies the text: an English fallback keeps English's +/// rule, even when the selected language is Russian. +fn counted(lang: usize, key: &str, n: usize) -> &str { + chain(lang) + .find_map(|l| { + find(l.ui, key).map(|text| match text { + Text::Plain(s) => *s, + Text::Plural { one, few, many, other } => match plural(l.code, n) { + Plural::One => one.unwrap_or(other), + Plural::Few => few.unwrap_or(other), + Plural::Many => many.unwrap_or(other), + Plural::Other => other, + }, + }) + }) + .unwrap_or(key) +} + +#[derive(Debug, PartialEq, Eq)] +enum Plural { + One, + Few, + Many, + Other, +} + +/// CLDR integer rules. Existing one/other catalogs may omit the extra forms. +fn plural(code: &str, n: usize) -> Plural { + match code { + "ru" if n % 10 == 1 && n % 100 != 11 => Plural::One, + "ru" if (2..=4).contains(&(n % 10)) && !(12..=14).contains(&(n % 100)) => Plural::Few, + "ru" => Plural::Many, + "ja" | "ko" | "zh-Hans" => Plural::Other, + "fr" | "pt-BR" if n <= 1 => Plural::One, + _ if n == 1 => Plural::One, + _ => Plural::Other, + } +} + +// CLDR's million-count "many" in French, Spanish and Portuguese still reads their existing +// "other" wording. Only Russian currently supplies separate few/many forms. + /// Whether count `n` takes the "one" form: 0 and 1 in French and Brazilian Portuguese, never in /// Japanese, Korean and Chinese (no plural forms: always "other"), 1 elsewhere (CLDR's integer rules). -// shortcut: CLDR's "many" (French, Spanish and Portuguese millions) reads as "other" here; add it -// when a language needs a third form. +#[cfg(test)] fn plural_one(lang: usize, n: usize) -> bool { - match LANGS.get(lang).map(|l| l.code) { - Some("ja" | "ko" | "zh-Hans") => false, - Some("fr" | "pt-BR") => n <= 1, - _ => n == 1, - } + plural(LANGS.get(lang).map_or("en", |l| l.code), n) == Plural::One } /// Replace each `{name}` in `text` with its argument, in one pass (an argument's own braces stay). @@ -186,8 +224,8 @@ pub fn menu_item<'a>(id: &str, english: &'a str) -> Cow<'a, str> { fn label<'a>(menu: bool, id: &str, english: &'a str) -> Cow<'a, str> { let text = chain(CURRENT.load(Ordering::Relaxed)).find_map(|l| menu.then(|| find(l.menu, id)).flatten().or_else(|| find(l.commands, id))); match text { - Some(Text::Plain(s) | Text::Plural(_, s)) if english.ends_with('…') && !s.ends_with('…') => Cow::Owned(format!("{s}…")), - Some(Text::Plain(s) | Text::Plural(_, s)) => Cow::Borrowed(s), + Some(Text::Plain(s) | Text::Plural { other: s, .. }) if english.ends_with('…') && !s.ends_with('…') => Cow::Owned(format!("{s}…")), + Some(Text::Plain(s) | Text::Plural { other: s, .. }) => Cow::Borrowed(s), None => Cow::Borrowed(english), } } diff --git a/crates/ui-egui/src/i18n_tests.rs b/crates/ui-egui/src/i18n_tests.rs index 768434e3..c8c73202 100644 --- a/crates/ui-egui/src/i18n_tests.rs +++ b/crates/ui-egui/src/i18n_tests.rs @@ -6,7 +6,7 @@ use std::collections::BTreeSet; use super::*; /// Languages that must translate every UI string and command label. -const COMPLETE: &[&str] = &["es"]; +const COMPLETE: &[&str] = &["es", "ru"]; /// Keys looked up from data rather than written as `tr("…")` in the code: the main menu's /// submenu names (the engine's menu catalog) and labels of engine and document enums. @@ -216,7 +216,7 @@ fn placeholders(s: &str) -> BTreeSet<&str> { fn forms(t: &Text) -> Vec<&'static str> { match t { Text::Plain(s) => vec![s], - Text::Plural(one, other) => one.iter().copied().chain([*other]).collect(), + Text::Plural { one, few, many, other } => one.iter().chain(few).chain(many).copied().chain([*other]).collect(), } } @@ -265,10 +265,10 @@ fn catalogs_have_known_keys_and_matching_placeholders() { for (i, form) in forms(t).iter().enumerate() { let got = placeholders(form); // A "one" form may spell the count out ("una capa"). - let ok = if matches!(t, Text::Plural(Some(_), _)) && i == 0 { got.is_subset(&want) } else { got == want }; + let ok = if matches!(t, Text::Plural { one: Some(_), .. }) && i == 0 { got.is_subset(&want) } else { got == want }; assert!(ok, "{}: {k:?} has placeholders {got:?}, English has {want:?}", lang.code); } - assert_eq!(matches!(t, Text::Plural(..)), matches!(src, Text::Plural(..)), "{}: {k:?} plural or not as in English", lang.code); + assert_eq!(matches!(t, Text::Plural { .. }), matches!(src, Text::Plural { .. }), "{}: {k:?} plural or not as in English", lang.code); } for (id, t) in lang.commands.iter().chain(lang.menu) { assert!(ids.contains(id), "{}: unknown command id {id:?}", lang.code); @@ -369,7 +369,7 @@ fn japanese_korean_and_chinese_have_no_plural_forms() { for code in ["ja", "ko", "zh-Hans"] { let lang = LANGS.iter().position(|l| l.code == code).unwrap(); assert!(!plural_one(lang, 0) && !plural_one(lang, 1) && !plural_one(lang, 2), "{code}"); - let ones: Vec<_> = LANGS[lang].ui.iter().filter(|(_, t)| matches!(t, Text::Plural(Some(_), _))).map(|(k, _)| k).collect(); + let ones: Vec<_> = LANGS[lang].ui.iter().filter(|(_, t)| matches!(t, Text::Plural { one: Some(_), .. })).map(|(k, _)| k).collect(); assert!(ones.is_empty(), "{code}: plural entries give only `other`: {ones:?}"); } } @@ -397,6 +397,50 @@ fn the_key_scanner_reads_rust_literals() { assert_eq!(literals(src), vec!["A \"b\"", "C {n}", "DE"]); } +#[test] +fn russian_language_tags_and_integer_plurals() { + let ru = LANGS.iter().position(|l| l.code == "ru").unwrap(); + assert_eq!(LANGS[ru].name, "Русский"); + for tag in ["ru", "ru-RU", "ru_RU.UTF-8", "RU", "ru-BY", "ru-Cyrl-RU"] { + assert_eq!(resolve(tag, None), ru, "{tag}"); + assert_eq!(resolve("system", Some(tag)), ru, "{tag}"); + } + assert_eq!(resolve("system", Some("nl,ru-RU,en-US")), ru); + for n in [1, 21, 31, 101, 1_001] { + assert_eq!(plural("ru", n), Plural::One, "{n}"); + assert_eq!(fill(counted(ru, "{n} layers", n), &[("n", &n)]), format!("{n} слой")); + } + for n in [2, 3, 4, 22, 23, 24, 102, 104, 1_002] { + assert_eq!(plural("ru", n), Plural::Few, "{n}"); + assert_eq!(fill(counted(ru, "{n} layers", n), &[("n", &n)]), format!("{n} слоя")); + } + for n in [0, 5, 10, 11, 12, 13, 14, 19, 20, 25, 100, 111, 112, 114, 1_000_000] { + assert_eq!(plural("ru", n), Plural::Many, "{n}"); + assert_eq!(fill(counted(ru, "{n} layers", n), &[("n", &n)]), format!("{n} слоёв")); + } + for (key, text) in LANGS[ru].ui { + if let Text::Plural { one, few, many, .. } = text { + assert!(one.is_some() && few.is_some() && many.is_some(), "{key}: missing Russian plural forms"); + } + } +} + +#[test] +fn plural_fallback_uses_the_language_of_its_catalog() { + let la = LANGS.iter().position(|l| l.code == "es-419").unwrap(); + assert_eq!(counted(la, "{n} layers", 1), "{n} capa"); + assert_eq!(counted(la, "{n} layers", 21), "{n} capas"); + // Unsupported optional categories use other; existing one/other languages are unchanged. + for code in ["en", "es", "de"] { + for n in [0, 2, 11, 21, 1_000_000] { + assert_eq!(plural(code, n), Plural::Other, "{code}: {n}"); + } + assert_eq!(plural(code, 1), Plural::One, "{code}"); + } + assert_eq!(counted(0, "{n} layers", 21), "{n} layers"); + assert_eq!(counted(0, "unknown {n}", 21), "unknown {n}"); +} + #[test] fn the_language_preference_takes_language_codes() { let mut s = figcraft_engine::Session::new(); diff --git a/crates/ui-egui/src/layers.rs b/crates/ui-egui/src/layers.rs index 956709f4..118d2d6f 100644 --- a/crates/ui-egui/src/layers.rs +++ b/crates/ui-egui/src/layers.rs @@ -111,7 +111,8 @@ pub fn rail(app: &mut FigApp, ui: &mut Ui) { ui.painter().rect_filled(ir, CornerRadius::same(6), t.hover); } icons::paint(ui.painter(), Rect::from_center_size(ir.center(), vec2(16.0, 16.0)), icon, if active { t.accent_text } else { t.icon }); - ui.painter().text(pos2(r.center().x, y + 38.0), Align2::CENTER_CENTER, label, regular(10.0), t.text); + let shown = crate::widgets::elide_font(ui, label, (r.width() - 6.0).max(0.0), regular(10.0)); + ui.painter().text(pos2(r.center().x, y + 38.0), Align2::CENTER_CENTER, shown, regular(10.0), t.text); if resp.clicked() { match tab { Some(tb) => { @@ -677,7 +678,7 @@ fn layers(app: &mut FigApp, ui: &mut Ui) { ui.horizontal(|ui| { ui.add_space(16.0); let hint = if filter.is_empty() { tr("Add a frame (F) or a shape (R) to get started") } else { tr("No matching layers") }; - crate::widgets::label(ui, hint, t.text_secondary); + ui.add(egui::Label::new(egui::RichText::new(hint).font(regular(11.0)).color(t.text_secondary)).wrap()); }); } // Dragging near the list's top or bottom edge scrolls it, faster closer to the edge. diff --git a/docs/gaps.md b/docs/gaps.md index 34c4f439..9a2153f8 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -1,6 +1,6 @@ # Where FigCraft falls short of Figma -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** minor (`.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail) · **Target:** Figma Design (desktop app 126.10.7) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (reviewed Russian catalog and integer plurals; `.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail) · **Target:** Figma Design (desktop app 126.10.7) Every known shortfall against Figma, one entry each, ranked by how much it stops a Figma user from doing real work in FigCraft. Agents: pick work from the top unless the owner says otherwise. The @@ -18,7 +18,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe | 2 | Multiplayer and sharing are a preview, without accounts (**beta blocker**) | Feature | 42–84 | [collaboration-parity](collaboration-parity.md) | | 3 | Nothing checked against real Figma files | Fidelity | 15–30 | [target-app-parity](target-app-parity.md) | | 4 | Interaction fidelity not compared tool by tool | UI/UX | 40–60 | [ui-parity](ui-parity.md) | -| 5 | Localization: English plus unreviewed Spanish (and Latin American Spanish), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts | Localization | 29–51 | [localization-parity](localization-parity.md) | +| 5 | Localization: English, reviewed Russian, plus unreviewed Spanish (and Latin American Spanish), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts | Localization | 29–51 | [localization-parity](localization-parity.md) | | 6 | Plugins: widgets take clicks only, a self-hosted directory but no public catalogue; Figma's plugins don't run | Ecosystem | 8–26 | [target-app-parity](target-app-parity.md) | | 7 | CPU canvas where Figma uses the GPU | Hardware/performance | 30–60 | [hardware-parity](hardware-parity.md) | | 8 | Platform QA: Linux and the browser left (Windows pass done 2026-10-11) | Stability/platforms | 10–20 | [hardware-parity](hardware-parity.md) | @@ -142,7 +142,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe - **Impact:** Figma users notice the feel within minutes; it decides whether they stay. - **Estimate:** 40–60 h. Detail in [`ui-parity.md`](ui-parity.md). -### 5. Localization: English plus unreviewed drafts +### 5. Localization: English, reviewed Russian, plus unreviewed drafts - **Missing:** native reviews of the Spanish, French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese drafts and the Latin American Spanish overlay, the 25 newest strings in five drafts, Hindi, Arabic, Indonesian and Vietnamese, localized engine messages and number @@ -152,8 +152,9 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe Preferences ▸ Language, `pref.language`; `en.toml` is complete, and `es.toml`, `fr.toml`, `de.toml`, `pt-BR.toml`, `ja.toml`, `ko.toml` and `zh-Hans.toml` (2026-10-12) are machine drafts (all but `zh-Hans.toml` lack the 25 newest strings), with `es-419.toml` a Latin American overlay - listing only what differs from Spanish (only Spanish is held to completeness by the check; the - others show English for strings added later). + listing only what differs from Spanish (Spanish and Russian are held to completeness by the check; the + others show English for strings added later). Russian adds all 2,165 current entries, + reviewed and approved by VanTan, with one/few/many integer plurals. CJK UI text uses an installed CJK face the desktop app loads when something on screen needs it (`crates/ui-egui/src/cjk.rs`). Engine error messages and Dev Mode values stay English. - **Impact:** non-English designers get machine drafts nobody has reviewed yet, and English @@ -394,6 +395,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe | Date | Change | Summary | |---|---|---| +| 2026-10-14 | minor | Russian catalog (M9), reviewed and approved by VanTan: all 2,165 current entries, Русский in Preferences and regional ru tags. Russian integer plurals use one/few/many; contributor display name VanTan added. Existing estimates unchanged. | | 2026-10-13 | minor | Gap 1: the experimental `.fig` export writes instance sizes (resized, filling an auto layout, scaled), variables with modes and bindings, shared styles and variable-width strokes, and the import reads them back. Checked by opening written files in other software: resized and scaled instances keep their size and lay their layers out for it, variables with their modes and colour bindings resolve there (a frame set to another mode shows that mode's colours, also inside instances), colour, text, effect and grid styles are listed, and variable-width strokes that are the same on both sides of the path draw with their width; number, string and boolean bindings and bindings on effects, layout guides and gradient stops come across as the values they give but did not show as bound there (their entries now also name the layer field they set, not yet checked there), and strokes wider on one side of the path are written as filled outlines, no longer editable strokes there (the stroke itself is kept as plugin data and our own import puts it back; not yet checked there). Hours and the ranking unchanged | | 2026-10-12 | minor | Gap 2: your online files landed (the app records the files it put on a server with their owner tokens, Home ▸ Online lists them and opens them again as their owner, with sharing, rename, removal and forget from the list; one request per server tells which are still there; tabs mark files in a live session). What is left is unchanged: accounts, teams and per-person permissions, shared comments and versions. Hours unchanged | | 2026-10-12 | minor | Gap 2: sharing with a join code (Share ▸ Online or Local network, one Start sharing button, a short code to give others, one join field for a code or a link; the server names links with codes and limits wrong ones) added to the landed list; usability, so the estimate and ranking are unchanged | diff --git a/docs/localization-parity.md b/docs/localization-parity.md index 99f0f1a7..04fc1b4f 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -1,10 +1,10 @@ # Localization parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog) · **Target:** Figma Design (desktop app 126.10.7) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (reviewed Russian catalog and integer plurals; draft catalogs catch up: every draft translates every string again; Simplified Chinese draft catalog) · **Target:** Figma Design (desktop app 126.10.7) FigCraft's interface has a localization layer with an English catalog and Spanish (with a Latin American overlay), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese -drafts (machine-drafted, waiting for native speakers' review). This page lists the twelve key +drafts (machine-drafted, waiting for native speakers' review), plus Russian reviewed and approved by VanTan. This page lists the twelve key languages of the craftrules standard, then the other languages Figma ships. How to add a language: [`translating.md`](translating.md). @@ -17,19 +17,19 @@ browser's page translation. The `.lproj` folders in `/Applications/Figma.app` (6 Electron/Chromium locale packs, not Figma's own UI languages. Text *content* in any script is supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no vertical text. -## What FigCraft has (measured 2026-10-12, from source) +## What FigCraft has (measured 2026-10-14, from source) - **Layer:** one TOML catalog per language in `crates/ui-egui/locales/`, baked into the binary by the UI crate's build script (desktop and web; nothing read at run time). Keys are the English text - (`tr("Fill")`, `trf` for `{named}` placeholders, `trn` for counts with one/other plural forms), so + (`tr("Fill")`, `trf` for `{named}` placeholders, `trn` for counts with one/few/many/other plural forms), so a missing translation shows English, never a raw key. One English text that needs two translations takes a context (`"Fill|sizing"`). -- **Strings:** 1,417 UI keys in `en.toml` (36 with plural forms; ~110 are labels from engine and - document data, such as tool names, blend modes and Dev Mode properties) plus 536 command labels - and 29 menu wordings by command id: 1,982 entries. Command ids, the CLI, MCP and the control +- **Strings:** 1,577 UI keys in `en.toml` (38 with plural forms; ~110 are labels from engine and + document data, such as tool names, blend modes and Dev Mode properties) plus 557 command labels + and 31 menu wordings by command id: 2,165 entries. Command ids, the CLI, MCP and the control channel stay English. Accessibility names come from the same strings. - **Language preference:** Preferences ▸ Language (System, English, Deutsch, Español, Español - (Latinoamérica), Français, 日本語, 한국어, Português (Brasil), 简体中文), applied at once without a + (Latinoamérica), Français, 日本語, 한국어, Português (Brasil), Русский, 简体中文), applied at once without a restart; System takes the first of the OS's preferred languages that has a catalog (Windows' language list, macOS AppleLanguages, `LANGUAGE` then the locale variables on Linux and BSD; a regional tag falls back to its language, pt-PT or pt to pt-BR, and es-419, es-MX, es-AR, es-CO and the other @@ -42,13 +42,13 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v overlay, then the base language, then English. - **Check:** `cargo test -p figcraft-ui-egui i18n` fails on keys the code uses that `en.toml` lacks, keys no code uses, unknown command ids, placeholders that differ from English, overlay - entries that read as in their base language and, for complete languages (Spanish), anything + entries that read as in their base language and, for complete languages (Spanish and Russian), anything untranslated. The other drafts are not held to it: a string added later shows English there until someone translates it. Every draft translates every string as of 2026-10-12 (checked by adding each to `COMPLETE` locally and running the check). - **Not yet localized:** messages the engine writes (errors, a few toasts, Dev Mode property values - such as "inside"), number formatting (decimal points stay `.`), and plural forms beyond one/other - (French and Brazilian Portuguese take the singular for 0 as well; Japanese, Korean and Chinese + such as "inside"), number formatting (decimal points stay `.`), and additional plural rules for languages other than Russian + (Russian has one/few/many integer forms; French and Brazilian Portuguese take the singular for 0 as well; Japanese, Korean and Chinese have one form; CLDR's "many" for millions reads as the plural). - **Script support in documents:** Unicode bidi with Arabic shaping and mirrored brackets in text layers; right-to-left list markers and indents; right-to-left names in the layers panel, tabs @@ -91,7 +91,13 @@ supported by both apps (CJK, Arabic and Devanagari shaping, IME); Figma has no v | Korean | ko | 2,076 / 2,076 (100%, draft) | yes | Hangul text and IME in documents; UI text in an installed Korean face (web: the `FIGCRAFT_EMBED_CJK` build) | no | draft | 1–2 (after native review) | | Vietnamese | vi | 0 (0%) | no | Latin with stacked diacritics | no | none | 3–5 | -Other languages: none shipped (Figma ships none beyond the eight above). +Russian (`ru`): all 2,165 entries translated, including dialogs, tooltips, commands and menu wording; +wording reviewed and approved by VanTan. The picker shows Русский, and `ru-RU`, `ru-BY` and +`ru_RU.UTF-8` use it. Russian uses egui's bundled Cyrillic glyphs; no font asset was added. +The catalog keeps Frame, Dev Mode and Smart animate untranslated. Existing languages keep their +one/other behavior; Russian counts use one/few/many. Other languages beyond these: none shipped. +The navigation rail elides long labels with the full wording in its tooltip; the empty-layer hint +wraps to the panel width. Both were checked in Russian in native macOS screenshots. The layer is done. Per-language hours above are agent hours for a catalog of ~1,800 entries (a machine draft, then a pass over the panels for overflow), plus each language's plural rule where @@ -107,6 +113,7 @@ Vietnamese and Arabic. Traditional Chinese is not planned (no catalog; its users | Date | Change | Summary | |---|---|---| +| 2026-10-14 | minor | Russian catalog: 1,577 UI keys (38 with plural forms), 557 command labels and 31 menu wordings (2,165 entries), reviewed by VanTan. Русский in Preferences; ru tags resolve to it. Catalog generation supports optional few/many forms; Russian integer rules distinguish 1/21, 2–4/22–24 and 0/5–20, including 11–14. The catalog supplying a fallback selects its own plural rule. Spanish and Russian are checked for completeness. Estimates unchanged. | | 2026-10-12 | minor | Drafts catch up again, and the counts with them: French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese translate the 71 UI strings and 5 command labels they lacked since sharing with a join code and open hosting (the Share popover, join codes, owner tokens, reports, the .fig export notes), and with Spanish the 26 strings and 3 command labels of your online files (Home ▸ Online, its menu and dialogs, the marks on tabs); machine-drafted in each file's conventions, still drafts. The Latin American overlay adds computadora and agrega where the new Spanish says ordenador and añade (98 entries). 2,076 entries per catalog (1,503 UI strings, 544 command labels, 29 menu wordings) | | 2026-10-12 | minor | Draft catalogs catch up: French, German, Brazilian Portuguese, Japanese and Korean translate the 66 UI strings and 17 command labels added since they were drafted (cursor chat and spotlight, the plugin directory, Rename layers, presentation and snapping keys, list levels, the rotation origin, placing with Enter…), Simplified Chinese the 46 and 12 it lacked; machine-drafted from the English in each file's conventions, still drafts (not in `COMPLETE`). The Latin American overlay adds Control for a gamepad controller and presiona in the plugin directory hint (90 entries, with Enter for the keycap). String counts re-measured: 1,417 UI keys, 536 command labels and 29 menu wordings (1,982 entries), every draft at 100%. Localization grade unchanged | | 2026-10-12 | minor | Simplified Chinese draft catalog (gap 5): `zh-Hans.toml` translates every UI string (1,371 keys; plurals give only `other`, and `plural_one` never picks "one"), command label (524) and menu wording (29), machine-drafted in a concise UI style with full-width punctuation and marked as a draft awaiting native review; Preferences ▸ Language lists 简体中文, System maps zh, zh-CN, zh-SG and zh-Hans to it, and Traditional Chinese (zh-Hant, zh-TW, zh-HK, zh-MO) has no catalog and stays English (or the next OS language), never Simplified. A Chinese UI draws Han in a Chinese face first (Microsoft YaHei, PingFang SC, Noto Sans CJK SC), ahead of the craft-fonts build's Japanese BIZ UDPGothic, and changing the UI language lays the CJK faces out again (switching from Japanese to Chinese no longer keeps the Japanese face in front). String counts re-measured: 1,371 UI keys, 524 command labels and 29 menu wordings (1,924 entries); the French, German, Brazilian Portuguese, Japanese and Korean drafts have 1,899 of them (the 25 newer strings read English). Localization 36% → 39%, 31–54 h → 29–51 h | diff --git a/docs/mcp.md b/docs/mcp.md index 2b66657c..6731090c 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -349,7 +349,7 @@ on; `key` resolves keys the way the canvas does. Commands behind keys: nested layers), Escape or a click drops it. - `pref.reduceMotion {on?}`: reduced motion (the interface's own animations off; the system's accessibility setting turns it on too). Prototype transitions still play. -- `pref.language {language: system|en|es|es-419|fr|de|pt-BR|ja|ko|zh-Hans|…}`: the app's interface language, applied live ("system": +- `pref.language {language: system|en|ru|es|es-419|fr|de|pt-BR|ja|ko|zh-Hans|…}`: the app's interface language, applied live ("system": the OS or browser language; a language without a catalog shows English). Command ids, labels and every result stay English. diff --git a/docs/roadmap.md b/docs/roadmap.md index 8e2780d6..ad1ae844 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # FigCraft roadmap: milestones and what's next -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (recent files as cards with thumbnails on the home screen; community fixes: lost images reported on loading, SVG import budgets, Leptos code, full screen, server snapshots, owner-only preferences, ruzstd 0.9.1, layer cycle repair, batch rollback, Direct3D 12 on Windows, plugin metering; vector edit mode regions: hover hatch, selection and drag; repeat command recursion fix; the Layout panel's constrain-proportions link scales the other side, #220; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; `.fig` import, best effort; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~79%) · **Target:** Figma Design (desktop app 126.10.7) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (reviewed Russian catalog and integer plurals; recent files as cards with thumbnails on the home screen; community fixes: lost images reported on loading, SVG import budgets, Leptos code, full screen, server snapshots, owner-only preferences, ruzstd 0.9.1, layer cycle repair, batch rollback, Direct3D 12 on Windows, plugin metering; vector edit mode regions: hover hatch, selection and drag; repeat command recursion fix; the Layout panel's constrain-proportions link scales the other side, #220; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; `.fig` import, best effort; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~79%) · **Target:** Figma Design (desktop app 126.10.7) Forward-looking plan. Where FigCraft stands today: [`ROADMAP.md`](../ROADMAP.md) and [`target-app-parity.md`](target-app-parity.md); the full ranked list: [`gaps.md`](gaps.md). Hours @@ -17,7 +17,7 @@ are one Opus 5.5 agent; most milestones split across 4–6 agents on disjoint cr 40–60 h, parallel by tool. 4. **Languages** on the landed localization layer: native reviews of the es, fr, de, pt-BR, ja, ko and zh-Hans drafts and the es-419 overlay (people), then the other key languages: 3–7 h per language - plus native review. + plus native review. Russian's 2,165-entry catalog was reviewed and approved by VanTan. 5. **Multiplayer M9**: accounts and teams on top of the live sessions and the standalone server (the WebSocket transport, hosting from the app, invite links that open the app, document sync, live cursors, selections, text carets, avatars and follow, per-user undo, reconnect with offline edits, collision-free ids for styles, variables, collections and comments, joining from the browser, and `figcraft-server` with persistence, publishing from the app and share links to edit or view landed), per [`collab-protocol.md`](collab-protocol.md) and [`server.md`](server.md): 45–90 h. ## Alpha gate @@ -73,6 +73,7 @@ blockers. | Date | Change | Summary | |---|---|---| +| 2026-10-14 | minor | Russian catalog (M9), reviewed and approved by VanTan: all 2,165 current entries, Русский in Preferences and regional ru tags. Russian integer plurals use one/few/many; contributor display name VanTan added. Existing estimates unchanged. | | 2026-10-14 | minor | M7: Home screen recent files as cards: each card shows the picture its file carries on its first page's colour, the file's icon, name and "Edited ... ago"; sort by name, creation or last change either way; a table view; a click selects, a double-click or Enter opens, arrow keys move; Open, Show in folder and Remove from recents on right-click. Numbers unchanged. | | 2026-10-14 | minor | Lost images reported on loading (#207): a `.figcraft` file whose image member is damaged, too large or missing no longer loads in silence; each lost image (up to 20, then a count) is listed in `repairs` on `file.open` with its hash and the reason, and the app shows it as a toast. Saving still writes the file without the lost images. Numbers unchanged | | 2026-10-14 | minor | SVG import budgets (#190): gzipped SVG is unpacked by FigCraft within 64 MiB before parsing (it was unpacked without a limit), so the nesting check sees the real document; SVG documents embedded in `` data URIs share one 32 MiB budget and at most 4 levels, and one past a budget is left out. Linked files stay unread. Numbers unchanged | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index c34d5916..e15ddaed 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1,6 +1,6 @@ # FigCraft parity with Figma -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** minor (vector edit mode regions: hover hatch, selection and drag; plugin log byte/count limits (#191); core editor leftovers: component playground, auto gap handles, variables inside styles, inner shadows and background blur on groups, command-made text readable on dark pages, a one-step frame/group convert, delays on mouse triggers and manual overlay offsets, wrapped-line alignment; starter templates, a Templates tab and Getting started tips on the home screen: essentials ~57% → ~59%; everyday editing partials: number fields, outline options, Hide when publishing, cursors, panel widths, dark pages, numbers unchanged but menu breadth 211/211; text box sizes measured against documents re-saved by other software: whole-pixel line heights, advances on a 1024-per-em grid, letter spacing between glyphs only, numbers unchanged; .fig export checked by opening written files in other software, numbers unchanged; a public figcraft-server: open hosting, owner tokens, per-address limits, idle expiry, moderation, install.sh; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%; last major: full re-measure against Figma desktop 126.10.7) · **Target:** Figma Design (desktop app 126.10.7, macOS) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (reviewed Russian catalog and integer plurals; vector edit mode regions: hover hatch, selection and drag; plugin log byte/count limits (#191); core editor leftovers: component playground, auto gap handles, variables inside styles, inner shadows and background blur on groups, command-made text readable on dark pages, a one-step frame/group convert, delays on mouse triggers and manual overlay offsets, wrapped-line alignment; starter templates, a Templates tab and Getting started tips on the home screen: essentials ~57% → ~59%; everyday editing partials: number fields, outline options, Hide when publishing, cursors, panel widths, dark pages, numbers unchanged but menu breadth 211/211; text box sizes measured against documents re-saved by other software: whole-pixel line heights, advances on a 1024-per-em grid, letter spacing between glyphs only, numbers unchanged; .fig export checked by opening written files in other software, numbers unchanged; a public figcraft-server: open hosting, owner tokens, per-address limits, idle expiry, moderation, install.sh; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; headline percentages recomputed from the area table: feature breadth ~84%, core ~94%, mainstream ~50%; text lists, hanging punctuation and decoration styles; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; export and inspect behaviour suite and fixes; .fig import: editable vector networks and more layer kinds; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; styles and variables behaviour suite and fixes; prototyping behaviour suite and fixes; components behaviour suite and fixes; embedded image colour profiles; layers panel keyboard: F2 rename, Shift+F10 context menu, keyboard box selection, 10k-layer QA; per-side stroke weight variables; text behaviour suite: CJK line breaking, metrics, vertical trim; selection and transform behaviour suite and fixes; `.fig` import, best effort; auto layout behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings; feature breadth ~80%; ready ~49%; last major: full re-measure against Figma desktop 126.10.7) · **Target:** Figma Design (desktop app 126.10.7, macOS) The authoritative assessment of how close FigCraft is to Figma Design. [`ROADMAP.md`](../ROADMAP.md) summarizes it; [`gaps.md`](gaps.md) is the ranked work list; the measured menu checklist is @@ -129,7 +129,7 @@ Breadth: does it exist. Ready: depth, correctness and fidelity for real projects | Stability | 8 | 55% | 20–40 | Never-crash lints, command guard with rollback, recovery copies, 963 tests incl. junk-parameter sweeps and a seeded retained-render walk; but two days old, one release (v0.2.0), no field reports, QA mostly on macOS | [gaps](gaps.md) | | Performance | 6 | 60% | 30–60 | `cargo xtask bench`: 50k-layer page ~8 ms zoomed out, 10k-layer edits ~0.2 ms, 2,000-instance design system edits 4–64 ms; CPU (tiny-skia) canvas where Figma renders on the GPU; nothing timed against Figma on the same file | [hardware-parity](hardware-parity.md) | | Hardware | 3 | 60% | 10–21 | Trackpad pinch and HiDPI yes; pen pressure on Windows and from browser styluses (none from macOS/Linux tablets); several windows (screen-reader tree in the main one only); no GPU canvas, no P3 on screen | [hardware-parity](hardware-parity.md) | -| Localization | 3 | 39% | 29–51 | The layer, an English catalog and machine drafts of Spanish (with a Latin American overlay), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese (none reviewed by a native speaker; every draft covers all current strings), CJK UI text in installed faces loaded on demand (web: only the craft-fonts CJK build); engine messages stay English | [localization-parity](localization-parity.md) | +| Localization | 3 | 39% | 29–51 | The layer, English, Russian reviewed by VanTan, and machine drafts of Spanish (with a Latin American overlay), French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese (none reviewed by a native speaker; every draft covers all current strings), CJK UI text in installed faces loaded on demand (web: only the craft-fonts CJK build); engine messages stay English | [localization-parity](localization-parity.md) | | Platforms | 3 | 75% | 10–20 | macOS, Windows (x64/x86/arm64), Linux, FreeBSD, web all packaged (v0.2.0); Figma also has iOS/Android apps; non-macOS QA thin | [hardware-parity](hardware-parity.md) | | Ecosystem/plugins | 3 | 38% | 8–26 | Own WASM plugins with a Rust SDK and three examples (#101), plugin panels and settings (ABI v2), widgets that take clicks (API 3), a self-hosted plugin directory on `figcraft-server`; no Figma plugin compatibility, no public catalogue or community files; the CLI, control channel and MCP server are ahead of Figma for agents | [gaps](gaps.md) | | AI features | 2 | 0% | 30–60 | None | [gaps](gaps.md) | @@ -628,6 +628,7 @@ Carried over from the ROADMAP's "Shipped so far" (2026-10-10). The test count th | Date | Change | Summary | |---|---|---| +| 2026-10-14 | minor | Russian catalog (M9), reviewed and approved by VanTan: all 2,165 current entries, Русский in Preferences and regional ru tags. Russian integer plurals use one/few/many; contributor display name VanTan added. Existing estimates unchanged. | | 2026-10-13 | minor | Vector edit mode regions: the filled area under the pointer is hatched (clear of points and segments, holes left out, spacing by octave folding fitted to measurements at several zoom levels, fixed to the shape); click, Shift-click and drag select and move areas with their points (drawn unselected); a click on an empty spot clears them and stays in edit mode (see [ui-parity](ui-parity.md)) | | 2026-10-13 | minor | Plugin log limits (#191): cap each UTF-8 line at 8 KiB, total log bytes at 1 MiB and lines at 1,000 per run/callback before copying guest memory or forwarding to the host. Over-budget runs fail and roll back document edits; callbacks get fresh budgets. Added sandbox boundary/forwarding tests and an engine rollback regression. Percentages and hours unchanged. | | 2026-10-13 | minor | Component playground (M3): with an instance, a component or a component set selected, the Inspect tab shows a Playground: a preview of the component and a control for each variant, boolean and text property. What is tried there changes the preview and the code in the Code section, never the file (no undo step, the canvas instance stays as it is); Reset goes back to the component's or the instance's own values. `component.playground {id?, properties?, language?, path?}` gives the same to scripts: the properties with their values, the code, and a PNG on request (`playground_tests`). Left: instance-swap and slot properties in the playground (the command takes an instance swap; the panel has no control for it), a code connect template's snippet with the tried values. Numbers unchanged. | diff --git a/docs/translating.md b/docs/translating.md index b7c26a40..4a2431c7 100644 --- a/docs/translating.md +++ b/docs/translating.md @@ -15,6 +15,8 @@ Simplified. The catalogs: English (`en`, the source), and Spanish (`es`, with the Latin American overlay `es-419`), French (`fr`), German (`de`), Brazilian Portuguese (`pt-BR`), Japanese (`ja`), Korean (`ko`) and Simplified Chinese (`zh-Hans`), machine-drafted and waiting for native speakers' review. +Russian (`ru`, Русский) was reviewed and approved by VanTan; its catalog keeps Frame, Dev Mode +and Smart animate as familiar tool names and uses « » quotes. ## The catalogs @@ -59,12 +61,15 @@ language also needs its script in `cjk::language`. 2. Translate every value; keep the keys. Keep `{placeholders}` exactly, "…" where English has it, brand names and keyboard glyphs. English labels of commands: `figcraft-cli commands`. 3. Plurals: give `one` only if the language has a singular form (Japanese, Korean and Chinese give - only `other`, and `plural_one` returns false for them). `trn` picks `one` for a count of 1, and - also for 0 in French and Brazilian Portuguese (keep `{n}` in their `one` forms); a language with other rules needs its rule in - `plural_one` in [`crates/ui-egui/src/i18n.rs`](../crates/ui-egui/src/i18n.rs). + only `other`). `other` is required; `one`, `few` and `many` are optional. `trn` picks `one` for + 1 (also 0 in French and Brazilian Portuguese). Russian uses `one` for 1, 21, 31… except 11; + `few` for 2–4, 22–24… except 12–14; and `many` for the other integers, including 0. Keep `{n}` + in all Russian forms. Add new rules to `plural` in + [`crates/ui-egui/src/i18n.rs`](../crates/ui-egui/src/i18n.rs). A missing form reads `other`; + a missing key uses the fallback catalog's own plural rule. 4. To require full coverage, add the code to `COMPLETE` in [`crates/ui-egui/src/i18n_tests.rs`](../crates/ui-egui/src/i18n_tests.rs). Every later PR that - adds a string then has to translate it into each complete language, so only Spanish is there: + adds a string then has to translate it into each complete language: Spanish and Russian are there. French, German, Brazilian Portuguese, Japanese, Korean and Simplified Chinese are registered as drafts, and a string added later shows English in them until it is translated. To list what a draft lacks, add its code to `COMPLETE` locally and run the check. @@ -86,7 +91,7 @@ Wrap every string a user sees or a screen reader hears: `tr` for plain text, `tr placeholders, `trn` for counts, `tr_noop` for literals in `const` tables that are looked up later with `tr(label)`. The key must be a string literal as the first argument (the check reads the source). Don't wrap ids, command ids, JSON, log messages, file extensions or document content. Then -add the key to `en.toml` and to each complete language (Spanish); the drafts may follow later. +add the key to `en.toml` and to each complete language (Spanish and Russian); the drafts may follow later. Labels from the engine and document crates (tool names, blend modes, easing curves…) are looked up at display with `tr(x.label())`; they are listed in `dynamic_labels` in `i18n_tests.rs`.