Merge remote-tracking branch 'origin/main' into fix-549
This commit is contained in:
commit
4181a50c58
|
|
@ -9,6 +9,7 @@ iconography, images or other Adobe assets; open licences only; every asset attri
|
|||
|
||||
| Asset | Author | Source | Licence |
|
||||
|---|---|---|---|
|
||||
| `crates/ui-egui/src/i18n/uk.tsv` | FilmCraft contributors | Original work: clean-room Ukrainian interface translations | MIT OR Apache-2.0 |
|
||||
| `assets/fonts/Inter-Regular.ttf` | The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) |
|
||||
| `assets/fonts/Inter-Medium.ttf` | The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) |
|
||||
| `assets/fonts/Inter-SemiBold.ttf` | The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) |
|
||||
|
|
|
|||
|
|
@ -211,4 +211,4 @@ Status: ✅ done · 🟡 in progress · ⬜ not started. Estimates are remaining
|
|||
|
||||
### Interface localization
|
||||
|
||||
Edit > Language switches English/Japanese/Spanish/Portuguese (Brazilian) and persists across restarts through `general.interfaceLanguage` (also available in Settings > General). Until a language is chosen, System Language (the default) follows the operating system's or browser's preferred languages and falls back to English. Commands `app.language.english`, `app.language.japanese`, `app.language.spanish` and `app.language.portuguese` are reachable through the control channel. Translations live in one catalog per language (`crates/ui-egui/src/i18n/<code>.tsv`); UI code wraps its strings in `tl!("…")` / `tlf!("…{name}", name)`, and names that come from registries (effects and their parameters, settings, commands, panels, workspaces, presets) are translated where they are drawn. **Spanish and Japanese cover the whole interface**: menus, panels, dialogs, the Settings pages, the Effects/Effect Controls registry, the Keyboard Shortcuts dialog and status messages; tests fail when a `tl!` literal, a menu label or a registry name has no Spanish entry (`ja.tsv` has the same entries, but the tests do not require them yet). Searches match the displayed labels as well as English sources. Brazilian Portuguese covers the core menus; untranslated labels use English. Engine error messages, the control channel, the CLI and MCP stay in English. Japanese text uses the Japanese fonts from [craft-fonts](https://github.com/storytold/craft-fonts) when FilmCraft is built with them (`CRAFT_FONTS_DIR`; all official releases, including the web build), otherwise a Japanese font already installed on the system; with neither, switching to Japanese is refused with a message. Existing vertical text support is preserved.
|
||||
Edit > Language switches English/Japanese/Spanish/Portuguese (Brazilian)/Ukrainian and persists across restarts through `general.interfaceLanguage` (also available in Settings > General). Until a language is chosen, System Language (the default) follows the operating system's or browser's preferred languages and falls back to English. Commands `app.language.english`, `app.language.japanese`, `app.language.spanish`, `app.language.portuguese` and `app.language.ukrainian` are reachable through the control channel. Translations live in one catalog per language (`crates/ui-egui/src/i18n/<code>.tsv`); UI code wraps its strings in `tl!("…")` / `tlf!("…{name}", name)`, and names that come from registries (effects and their parameters, settings, commands, panels, workspaces, presets) are translated where they are drawn. **Spanish and Japanese cover the whole interface**: menus, panels, dialogs, the Settings pages, the Effects/Effect Controls registry, the Keyboard Shortcuts dialog and status messages; tests fail when a `tl!` literal, a menu label or a registry name has no Spanish entry (`ja.tsv` has the same entries, but the tests do not require them yet). Searches match the displayed labels as well as English sources. Brazilian Portuguese and Ukrainian cover the core menus; untranslated labels use English. Ukrainian uses the bundled Cyrillic interface fonts. Engine error messages, the control channel, the CLI and MCP stay in English. Japanese text uses the Japanese fonts from [craft-fonts](https://github.com/storytold/craft-fonts) when FilmCraft is built with them (`CRAFT_FONTS_DIR`; all official releases, including the web build), otherwise a Japanese font already installed on the system; with neither, switching to Japanese is refused with a message. Existing vertical text support is preserved.
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ pub fn install(app: &FilmcraftApp, ctx: egui::Context) -> (Receiver<String>, Sho
|
|||
filmcraft_ui_egui::i18n::Language::Es
|
||||
} else if checked("app.language.portuguese") {
|
||||
filmcraft_ui_egui::i18n::Language::PtBr
|
||||
} else if checked("app.language.ukrainian") {
|
||||
filmcraft_ui_egui::i18n::Language::Uk
|
||||
} else {
|
||||
filmcraft_ui_egui::i18n::Language::En
|
||||
};
|
||||
|
|
|
|||
|
|
@ -118,8 +118,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
fn session(files: Arc<Files>) -> Session {
|
||||
let mut s = Session::default();
|
||||
s.services = files;
|
||||
let mut s = Session { services: files, ..Default::default() };
|
||||
s.execute("file.newSequence", json!({"name":"Review, 日本語", "fps":24})).unwrap();
|
||||
s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -680,7 +680,14 @@ static CATEGORIES: &[Category] = &[
|
|||
f(
|
||||
"general.interfaceLanguage",
|
||||
"Interface Language",
|
||||
Kind::Choice(&[("system", "System Language"), ("en", "English"), ("ja", "日本語"), ("es", "Español"), ("pt-br", "Português (Brasil)")]),
|
||||
Kind::Choice(&[
|
||||
("system", "System Language"),
|
||||
("en", "English"),
|
||||
("ja", "日本語"),
|
||||
("es", "Español"),
|
||||
("pt-br", "Português (Brasil)"),
|
||||
("uk", "Українська"),
|
||||
]),
|
||||
true,
|
||||
),
|
||||
f("general.atStartup", "At Startup", Kind::Choice(STARTUP), true),
|
||||
|
|
|
|||
|
|
@ -1033,7 +1033,6 @@
|
|||
Corner Radius Radio de esquina
|
||||
Polygon Sides Lados del polígono
|
||||
Fill Relleno
|
||||
Gradient Degradado
|
||||
Stroke Trazo
|
||||
Stroke 2 Trazo 2
|
||||
Background Fondo
|
||||
|
|
@ -3490,3 +3489,8 @@
|
|||
tracks pistas
|
||||
In Entrada
|
||||
Out Salida
|
||||
Export Markers as CSV… Exportar marcadores como CSV…
|
||||
Fill Type Tipo de relleno
|
||||
Gradient Angle Ángulo del degradado
|
||||
Linear Gradient Degradado lineal
|
||||
Solid Sólido
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 3 and column 95.
|
|
|
@ -3482,3 +3482,8 @@
|
|||
New Sequence 新規シーケンス
|
||||
Tracks トラック
|
||||
tracks トラック
|
||||
Export Markers as CSV… マーカーを CSV として書き出し…
|
||||
Fill Type 塗りの種類
|
||||
Gradient Angle グラデーションの角度
|
||||
Linear Gradient 線形グラデーション
|
||||
Solid べた塗り
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 11 and column 59.
|
|
|
@ -34,14 +34,16 @@ pub enum Language {
|
|||
/// Persisted as `pt-br` (the blanket `rename_all` would produce `ptbr`).
|
||||
#[serde(rename = "pt-br")]
|
||||
PtBr,
|
||||
Uk,
|
||||
}
|
||||
|
||||
static JAPANESE: OnceLock<Catalog> = OnceLock::new();
|
||||
static SPANISH: OnceLock<Catalog> = OnceLock::new();
|
||||
static PORTUGUESE: OnceLock<Catalog> = OnceLock::new();
|
||||
static UKRAINIAN: OnceLock<Catalog> = OnceLock::new();
|
||||
|
||||
impl Language {
|
||||
pub const ALL: [Self; 4] = [Self::En, Self::Ja, Self::Es, Self::PtBr];
|
||||
pub const ALL: [Self; 5] = [Self::En, Self::Ja, Self::Es, Self::PtBr, Self::Uk];
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
|
|
@ -49,6 +51,7 @@ impl Language {
|
|||
Self::Ja => "日本語",
|
||||
Self::Es => "Español",
|
||||
Self::PtBr => "Português (Brasil)",
|
||||
Self::Uk => "Українська",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,6 +61,7 @@ impl Language {
|
|||
"ja" => Some(Self::Ja),
|
||||
"es" => Some(Self::Es),
|
||||
"pt-br" => Some(Self::PtBr),
|
||||
"uk" => Some(Self::Uk),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +70,7 @@ impl Language {
|
|||
/// (BCP 47 or POSIX locale tags such as `es-419`, `pt_BR.UTF-8`, most preferred first) that the
|
||||
/// interface has, else English. Any Portuguese gets the Brazilian catalog, the only one there is.
|
||||
pub fn from_locales(tags: &[String]) -> Self {
|
||||
const PRIMARY: [(&str, Language); 4] = [("en", Language::En), ("ja", Language::Ja), ("es", Language::Es), ("pt", Language::PtBr)];
|
||||
const PRIMARY: [(&str, Language); 5] = [("en", Language::En), ("ja", Language::Ja), ("es", Language::Es), ("pt", Language::PtBr), ("uk", Language::Uk)];
|
||||
tags.iter()
|
||||
.find_map(|tag| {
|
||||
let primary = tag.split(['-', '_', '.', '@']).next().unwrap_or_default();
|
||||
|
|
@ -82,6 +86,7 @@ impl Language {
|
|||
Self::Ja => "ja",
|
||||
Self::Es => "es",
|
||||
Self::PtBr => "pt-br",
|
||||
Self::Uk => "uk",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +97,7 @@ impl Language {
|
|||
Self::Ja => Some(JAPANESE.get_or_init(|| Catalog::parse(include_str!("ja.tsv")))),
|
||||
Self::Es => Some(SPANISH.get_or_init(|| Catalog::parse(include_str!("es.tsv")))),
|
||||
Self::PtBr => Some(PORTUGUESE.get_or_init(|| Catalog::parse(include_str!("pt-br.tsv")))),
|
||||
Self::Uk => Some(UKRAINIAN.get_or_init(|| Catalog::parse(include_str!("uk.tsv")))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -246,7 +252,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn catalogs_are_well_formed() {
|
||||
for (code, text) in [("es", include_str!("es.tsv")), ("ja", include_str!("ja.tsv")), ("pt-br", include_str!("pt-br.tsv"))] {
|
||||
for (code, text) in
|
||||
[("es", include_str!("es.tsv")), ("ja", include_str!("ja.tsv")), ("pt-br", include_str!("pt-br.tsv")), ("uk", include_str!("uk.tsv"))]
|
||||
{
|
||||
let (entries, errors) = catalog::parse_entries(text);
|
||||
assert!(errors.is_empty(), "{code}: {errors:?}");
|
||||
for (i, (ctx, en, tr)) in entries.iter().enumerate() {
|
||||
|
|
@ -268,6 +276,10 @@ mod tests {
|
|||
assert_eq!(Language::PtBr.tr("File"), "Arquivo");
|
||||
assert_eq!(Language::PtBr.tr("meu video.mp4"), "meu video.mp4");
|
||||
assert_eq!(Language::PtBr.name(), "Português (Brasil)");
|
||||
assert_eq!(Language::Uk.name(), "Українська");
|
||||
assert_eq!(Language::Uk.tr("File"), "Файл");
|
||||
assert_eq!(Language::Uk.tr("мій кліп.mp4"), "мій кліп.mp4");
|
||||
assert_eq!(Language::Uk.tr("An untranslated label"), "An untranslated label");
|
||||
for l in Language::ALL {
|
||||
assert_eq!(Language::parse(l.code()), Some(l));
|
||||
let json = serde_json::to_string(&l).unwrap();
|
||||
|
|
@ -626,6 +638,21 @@ mod tests {
|
|||
assert_eq!(app.ui.language, Language::PtBr);
|
||||
assert_eq!(app.session.prefs.general.interface_language, "pt-br");
|
||||
assert!(crate::menus::menu_items(&app).iter().any(|it| it.id == "app.language.portuguese" && it.checked == Some(true)));
|
||||
let result = crate::menus::invoke(&mut app, &ctx, "app.language.ukrainian", serde_json::json!({})).unwrap();
|
||||
assert_eq!(result, serde_json::json!("uk"));
|
||||
assert_eq!(app.ui.language, Language::Uk);
|
||||
for item in crate::menus::menu_items(&app).iter().filter(|it| it.id.starts_with("app.language.")) {
|
||||
assert_eq!(item.checked, Some(item.id == "app.language.ukrainian"), "{}", item.id);
|
||||
}
|
||||
let saved = serde_json::to_string(&app.ui).unwrap();
|
||||
assert!(saved.contains("\"language\":\"uk\""), "{saved}");
|
||||
let restored: crate::state::UiState = serde_json::from_str(&saved).unwrap();
|
||||
assert_eq!(restored.language, Language::Uk);
|
||||
assert_eq!(app.session.prefs.general.interface_language, "uk");
|
||||
let prefs = serde_json::to_string(&app.session.prefs).unwrap();
|
||||
let mut restarted = filmcraft_engine::Session::default();
|
||||
restarted.prefs = serde_json::from_str(&prefs).unwrap();
|
||||
assert_eq!(crate::FilmcraftApp::new(restarted).ui.language, Language::Uk);
|
||||
crate::menus::invoke(&mut app, &ctx, "app.language.english", serde_json::json!({})).unwrap();
|
||||
assert_eq!(app.ui.language, Language::En);
|
||||
set_current(Language::En);
|
||||
|
|
@ -640,6 +667,9 @@ mod tests {
|
|||
assert_eq!(l(&["ES"]), Language::Es);
|
||||
assert_eq!(l(&["ja-JP"]), Language::Ja);
|
||||
assert_eq!(l(&["pt-BR"]), Language::PtBr);
|
||||
for tag in ["uk", "uk-UA", "uk_UA.UTF-8", "UK-ua"] {
|
||||
assert_eq!(l(&[tag]), Language::Uk);
|
||||
}
|
||||
assert_eq!(l(&["pt_PT.UTF-8@euro"]), Language::PtBr);
|
||||
assert_eq!(l(&["fr-FR", "de", "es-MX", "ja"]), Language::Es, "the first one the interface has");
|
||||
assert_eq!(l(&["en-GB", "es"]), Language::En);
|
||||
|
|
@ -684,6 +714,57 @@ mod tests {
|
|||
set_current(Language::En);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ukrainian_entries_cover_the_original_menu_catalog() {
|
||||
let app = crate::FilmcraftApp::new(filmcraft_engine::Session::default());
|
||||
let items = crate::menus::menu_items(&app);
|
||||
let known = |text: &str| crate::menus::MENUS.contains(&text) || items.iter().any(|it| it.label == text || it.path.iter().any(|p| p == text));
|
||||
let (entries, _) = catalog::parse_entries(include_str!("uk.tsv"));
|
||||
for (_, en, _) in entries {
|
||||
assert!(known(&en) || en == "Settings", "not a menu label: {en}");
|
||||
}
|
||||
let (portuguese, _) = catalog::parse_entries(include_str!("pt-br.tsv"));
|
||||
for (_, en, _) in portuguese {
|
||||
assert!(Language::Uk.has(&en), "missing Ukrainian menu label: {en}");
|
||||
}
|
||||
for en in crate::menus::MENUS.into_iter().chain(["Audio", "Settings"]) {
|
||||
assert_ne!(Language::Uk.tr(en), en, "untranslated Ukrainian menu: {en}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ukrainian_renders_with_bundled_fonts() {
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install(&ctx, &crate::theme::Tokens::for_kind(crate::theme::ThemeKind::default()));
|
||||
let mut output = ctx.run_ui(egui::RawInput::default(), |_| {});
|
||||
output.textures_delta.clear();
|
||||
let (entries, _) = catalog::parse_entries(include_str!("uk.tsv"));
|
||||
ctx.fonts_mut(|fonts| {
|
||||
// Include the entire alphabet, even letters not yet used by a translated menu label.
|
||||
let alphabet = "АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯабвгґдеєжзиіїйклмнопрстуфхцчшщьюя";
|
||||
let chars: std::collections::BTreeSet<_> = alphabet
|
||||
.chars()
|
||||
.chain(Language::Uk.name().chars())
|
||||
.chain(entries.iter().flat_map(|(_, _, uk)| uk.chars()))
|
||||
.filter(|ch| !ch.is_ascii())
|
||||
.collect();
|
||||
for family in crate::theme::font_families() {
|
||||
let font = egui::FontId::new(13.0, family);
|
||||
// has_glyph compares font faces, giving false negatives when Inter also supplies
|
||||
// the replacement character (the named medium/semibold stacks). Compare the
|
||||
// actual glyph texture regions instead; the noncharacter U+10FFFF is missing.
|
||||
let text: String = chars.iter().copied().chain(['\u{10ffff}']).collect();
|
||||
let galley = fonts.layout_no_wrap(text, font.clone(), egui::Color32::WHITE);
|
||||
let glyphs = &galley.rows[0].glyphs;
|
||||
assert_eq!(glyphs.len(), chars.len() + 1);
|
||||
let replacement = glyphs.last().unwrap().uv_rect;
|
||||
for glyph in glyphs.iter().take(chars.len()) {
|
||||
assert_ne!(glyph.uv_rect, replacement, "missing {} in {font:?}", glyph.chr);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn japanese_needs_an_installed_font() {
|
||||
let mut app = crate::FilmcraftApp::new(filmcraft_engine::Session::default());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,356 @@
|
|||
# Ukrainian (uk) catalog. Columns separated by TAB: context <TAB> source <TAB> translation
|
||||
# Empty context is a plain UI string. Escapes: \n, \t, \\; preserve placeholders and trailing ….
|
||||
# Original clean-room translations from the meaning of FilmCraft's English menu labels,
|
||||
# using Ukrainian video-editing vocabulary. No proprietary localization resources were used.
|
||||
# Core menu coverage; other interface strings fall back to English.
|
||||
|
||||
About FilmCraft Про FilmCraft
|
||||
Add Audio Submix Track Додати доріжку підміксу аудіо
|
||||
Add Caption at Playhead Додати субтитр у позиції курсора відтворення
|
||||
Add Chapter Marker… Додати маркер розділу…
|
||||
Add Edit Додати розріз
|
||||
Add Edit to All Tracks Додати розріз на всіх доріжках
|
||||
Add Flash Cue Marker… Додати маркер сигналу Flash…
|
||||
Add Frame Hold Додати стоп-кадр
|
||||
Add Guide… Додати напрямну…
|
||||
Add Marker Додати маркер
|
||||
Add New Caption Track… Додати нову доріжку субтитрів…
|
||||
Add Range Marker Додати маркер діапазону
|
||||
Add Range Marker to In and Out Додати маркер діапазону між початковою та кінцевою позначками
|
||||
Add Tracks… Додати доріжки…
|
||||
Adjustment Layer… Коригувальний шар…
|
||||
Align to Selection Вирівняти за виділенням
|
||||
Align to Video Frame Вирівняти за відеокадром
|
||||
Align to Video Frame as Group Вирівняти за відеокадром як групу
|
||||
All Panels Усі панелі
|
||||
Alpha Альфа
|
||||
Appearance Вигляд
|
||||
Appearance… Вигляд…
|
||||
Apply Audio Transition Застосувати аудіоперехід
|
||||
Apply Default Transitions to Selection Застосувати типові переходи до виділення
|
||||
Apply Video Transition Застосувати відеоперехід
|
||||
Arrange Упорядкувати
|
||||
ArtCraft Website Вебсайт ArtCraft
|
||||
Assembly Компонування
|
||||
Attach Proxies… Приєднати проксі…
|
||||
Audio Аудіо
|
||||
Audio Channels… Аудіоканали…
|
||||
Audio Clip Mixer Мікшер аудіокліпів
|
||||
Audio Gain… Підсилення аудіо…
|
||||
Audio Hardware… Аудіообладнання…
|
||||
Audio In Початок аудіо
|
||||
Audio Meters Індикатори рівня аудіо
|
||||
Audio Options Параметри аудіо
|
||||
Audio Out Кінець аудіо
|
||||
Audio Track Mixer Мікшер аудіодоріжок
|
||||
Audio Waveform Осцилограма аудіо
|
||||
Auto Save… Автозбереження…
|
||||
Automate to Sequence… Автоматично розмістити в секвенції…
|
||||
Bars and Tone… Кольорові смуги й тон…
|
||||
Bin Контейнер
|
||||
Bin From Selection Контейнер із виділення
|
||||
Black Video… Чорне відео…
|
||||
Blue Синій
|
||||
Bottom Низ
|
||||
Breakout to Mono Розділити на моноканали
|
||||
Bring Forward Перемістити вперед
|
||||
Bring to Front На передній план
|
||||
Brown Коричневий
|
||||
Captions Субтитри
|
||||
Captions and Graphics Субтитри й графіка
|
||||
Captions… Субтитри…
|
||||
Caribbean Карибський
|
||||
Center Horizontally По центру горизонтально
|
||||
Center Vertically По центру вертикально
|
||||
Cerulean Лазурний
|
||||
Clear Видалити
|
||||
Clear Guides Видалити напрямні
|
||||
Clear In Прибрати початкову позначку
|
||||
Clear In and Out Прибрати початкову та кінцеву позначки
|
||||
Clear Markers Видалити маркери
|
||||
Clear Out Прибрати кінцеву позначку
|
||||
Clear Selected Marker Видалити вибраний маркер
|
||||
Clip Кліп
|
||||
Close Закрити
|
||||
Close All Other Projects Закрити всі інші проєкти
|
||||
Close All Projects Закрити всі проєкти
|
||||
Close Gap Прибрати проміжок
|
||||
Close Project Закрити проєкт
|
||||
Color Management… Керування кольором…
|
||||
Color Matte… Кольорова підкладка…
|
||||
Comparison View Режим порівняння
|
||||
Composite Video Композитне відео
|
||||
Consolidate Duplicates Об’єднати дублікати
|
||||
Copy Копіювати
|
||||
Copy Paste Includes Sequence Markers Копіювати й вставляти разом із маркерами секвенції
|
||||
Create Captions from Transcript… Створити субтитри з транскрипції…
|
||||
Create Multi-Camera Source Sequence… Створити вихідну мультикамерну секвенцію…
|
||||
Create Proxies… Створити проксі…
|
||||
Cut Вирізати
|
||||
Dark Темний
|
||||
Darkest Найтемніший
|
||||
Delete Render Files Видалити файли рендерингу
|
||||
Delete Render Files In to Out Видалити файли рендерингу між початковою та кінцевою позначками
|
||||
Delete Tracks… Видалити доріжки…
|
||||
Delete Transcript Видалити транскрипцію
|
||||
Demo Footage Демонстраційні матеріали
|
||||
Demo Project Демонстраційний проєкт
|
||||
Deselect All Зняти все виділення
|
||||
Detach Proxies Від’єднати проксі
|
||||
Display Mode Режим відображення
|
||||
Distribute Розподілити
|
||||
Distribute Horizontally Розподілити горизонтально
|
||||
Distribute Space Horizontally Рівні проміжки по горизонталі
|
||||
Distribute Space Vertically Рівні проміжки по вертикалі
|
||||
Distribute Vertically Розподілити вертикально
|
||||
Duplicate Дублювати
|
||||
Dynamic Audio Waveforms Динамічні осцилограми аудіо
|
||||
Edit Редагування
|
||||
Edit Offline… Редагувати автономно…
|
||||
Edit Original Редагувати оригінал
|
||||
Edit Subclip… Редагувати підкліп…
|
||||
Editing Монтаж
|
||||
Effect Controls Керування ефектами
|
||||
Effects Ефекти
|
||||
Ellipse Еліпс
|
||||
Enable Увімкнути
|
||||
Enable Remix Увімкнути ремікс
|
||||
Essential Graphics Основна графіка
|
||||
Essential Sound Основний звук
|
||||
Events Події
|
||||
Export Експорт
|
||||
Export As Motion Graphics Template… Експортувати як шаблон анімаційної графіки…
|
||||
Extract Вилучити зі зсувом
|
||||
Extract Audio Видобути аудіо
|
||||
Field Options… Параметри полів…
|
||||
File Файл
|
||||
File… Файл…
|
||||
Fill frame Заповнити кадр
|
||||
FilmCraft Help… Довідка FilmCraft…
|
||||
FilmCraft on GitHub FilmCraft на GitHub
|
||||
FilmCraft on getartcraft.com FilmCraft на getartcraft.com
|
||||
Find Next Знайти далі
|
||||
Find… Знайти…
|
||||
Fit Умістити
|
||||
Fit to frame Умістити в кадр
|
||||
Flatten Звести
|
||||
Forest Лісовий
|
||||
Frame Blending Змішування кадрів
|
||||
Frame Hold Options… Параметри стоп-кадру…
|
||||
Frame Sampling Вибірка кадрів
|
||||
From Bin Із контейнера
|
||||
From Source Monitor Із монітора джерела
|
||||
From Source Monitor, Match Frame Із монітора джерела, відповідний кадр
|
||||
From file… Із файлу…
|
||||
Full Повна
|
||||
Generate Audio Waveform Створити осцилограму аудіо
|
||||
Get Media File Properties for Показати властивості медіафайлу для
|
||||
Go to Gap Перейти до проміжку
|
||||
Go to In Перейти до початкової позначки
|
||||
Go to Next Caption Segment Перейти до наступного сегмента субтитрів
|
||||
Go to Next Marker Перейти до наступного маркера
|
||||
Go to Out Перейти до кінцевої позначки
|
||||
Go to Previous Caption Segment Перейти до попереднього сегмента субтитрів
|
||||
Go to Previous Marker Перейти до попереднього маркера
|
||||
Go to Split Перейти до розділеної позначки
|
||||
Graphics and Titles Графіка й титри
|
||||
Graphics… Графіка…
|
||||
Green Зелений
|
||||
Group Згрупувати
|
||||
Guide Templates Шаблони напрямних
|
||||
Help Довідка
|
||||
Hide All Caption Tracks Приховати всі доріжки субтитрів
|
||||
High Quality Playback Відтворення у високій якості
|
||||
History Історія
|
||||
Import From Імпортувати з
|
||||
Import Image Sequence… Імпортувати послідовність зображень…
|
||||
Import from Media Browser Імпортувати з медіабраузера
|
||||
Import… Імпортувати…
|
||||
Info Інформація
|
||||
Ingest Settings… Налаштування завантаження медіа…
|
||||
Insert Вставити
|
||||
Insert Frame Hold Segment Вставити сегмент стоп-кадру
|
||||
Install Motion Graphics Template… Установити шаблон анімаційної графіки…
|
||||
Interpret Footage… Інтерпретувати матеріал…
|
||||
Join the ArtCraft Discord… Приєднатися до Discord ArtCraft…
|
||||
Keyboard Shortcuts… Клавіатурні скорочення…
|
||||
Label Мітка
|
||||
Labels… Мітки…
|
||||
Language Мова
|
||||
Lavender Лавандовий
|
||||
Learning Навчання
|
||||
Left Ліворуч
|
||||
Libraries Бібліотеки
|
||||
Lift Вилучити без зсуву
|
||||
Light Світлий
|
||||
Link Зв’язати
|
||||
Link Media… Прив’язати медіа…
|
||||
Linked Selection Пов’язане виділення
|
||||
Lock Guides Заблокувати напрямні
|
||||
Lumetri Color Колір Lumetri
|
||||
Lumetri Scopes Відеоскопи Lumetri
|
||||
Magnification Масштаб
|
||||
Make Offline… Зробити автономним…
|
||||
Make Subclip… Створити підкліп…
|
||||
Make Subsequence Створити підсеквенцію
|
||||
Manage Guides… Керувати напрямними…
|
||||
Mark Clip Позначити кліп
|
||||
Mark In Установити початкову позначку
|
||||
Mark Out Установити кінцеву позначку
|
||||
Mark Selection Позначити виділення
|
||||
Mark Split Установити розділену позначку
|
||||
Markers Маркери
|
||||
Match Frame Знайти відповідний кадр
|
||||
Media Analysis & Transcription… Аналіз медіа й транскрибування…
|
||||
Media Browser Медіабраузер
|
||||
Media Cache… Медіакеш…
|
||||
Media… Медіа…
|
||||
Memory… Пам’ять…
|
||||
Merge Clips… Об’єднати кліпи…
|
||||
Metadata Метадані
|
||||
Modify Змінити
|
||||
Motion Graphics Template… Шаблон анімаційної графіки…
|
||||
Multi-Camera Мультикамера
|
||||
Multi-Camera View Мультикамерний режим
|
||||
Nest… Вкласти в секвенцію…
|
||||
New Створити
|
||||
New Layer Новий шар
|
||||
New Project Panel Нова панель проєкту
|
||||
Next in Sequence Наступний у секвенції
|
||||
Next in Track Наступний на доріжці
|
||||
Normalize Mix Track… Нормалізувати доріжку міксу…
|
||||
Offline File… Автономний файл…
|
||||
Open Project… Відкрити проєкт…
|
||||
Optical Flow Оптичний потік
|
||||
Overwrite Перезаписати
|
||||
Paste Вставити
|
||||
Paste Attributes… Вставити атрибути…
|
||||
Paste Insert Вставити зі зсувом
|
||||
Paused Resolution Роздільність під час паузи
|
||||
Playback Resolution Роздільність відтворення
|
||||
Playback… Відтворення…
|
||||
Plugins… Плагіни…
|
||||
Polygon Багатокутник
|
||||
Preferences Налаштування
|
||||
Previous in Sequence Попередній у секвенції
|
||||
Previous in Track Попередній на доріжці
|
||||
Program Програма
|
||||
Progress Перебіг
|
||||
Project Проєкт
|
||||
Project Manager… Менеджер проєкту…
|
||||
Project Settings Налаштування проєкту
|
||||
Project… Проєкт…
|
||||
Properties Властивості
|
||||
Purple Пурпуровий
|
||||
Reconnect Full Resolution Media… Повторно під’єднати медіа повної роздільності…
|
||||
Recover Unsaved Changes… Відновити незбережені зміни…
|
||||
Rectangle Прямокутник
|
||||
Red Червоний
|
||||
Redo Повторити
|
||||
Reference Monitor Еталонний монітор
|
||||
Remix Ремікс
|
||||
Remix Properties… Властивості реміксу…
|
||||
Remove Attributes… Видалити атрибути…
|
||||
Remove Filler Words Видалити слова-паразити
|
||||
Remove Pauses Видалити паузи
|
||||
Remove Unused Видалити невикористане
|
||||
Rename… Перейменувати…
|
||||
Render Audio Рендерити аудіо
|
||||
Render Effects In to Out Рендерити ефекти між початковою та кінцевою позначками
|
||||
Render In to Out Рендерити між початковою та кінцевою позначками
|
||||
Render Selection Рендерити виділення
|
||||
Replace Fonts in Projects… Замінити шрифти в проєктах…
|
||||
Replace With Clip Замінити кліпом
|
||||
Report an Issue… Повідомити про проблему…
|
||||
Reset All Parameters Скинути всі параметри
|
||||
Reset Duration Скинути тривалість
|
||||
Reset to Saved Layout Відновити збережене компонування
|
||||
Restore Captions from Source Clip Відновити субтитри з вихідного кліпу
|
||||
Reveal Log Files… Показати файли журналу…
|
||||
Reverse Match Frame Знайти відповідний кадр у секвенції
|
||||
Revert Повернути до збереженого
|
||||
Revert Remix Скасувати ремікс
|
||||
Review Перегляд
|
||||
Right Праворуч
|
||||
Ripple Delete Видалити зі зсувом
|
||||
Ripple Sequence Markers Зсувати маркери секвенції
|
||||
Rose Рожевий
|
||||
Safe Margins Безпечні поля
|
||||
Save Зберегти
|
||||
Save All Зберегти все
|
||||
Save As… Зберегти як…
|
||||
Save Guides as Template… Зберегти напрямні як шаблон…
|
||||
Save a Copy… Зберегти копію…
|
||||
Save as Template… Зберегти як шаблон…
|
||||
Scale to Frame Size Масштабувати до розміру кадру
|
||||
Scene Edit Detection… Виявлення монтажних склейок…
|
||||
Scratch Disks… Робочі диски…
|
||||
Search Bin Пошуковий контейнер
|
||||
Select Вибрати
|
||||
Select All Виділити все
|
||||
Select All Matching Виділити всі відповідні
|
||||
Select Label Group Виділити групу міток
|
||||
Select Next Graphic Вибрати наступний графічний елемент
|
||||
Select Next Layer Вибрати наступний шар
|
||||
Select Previous Graphic Вибрати попередній графічний елемент
|
||||
Select Previous Layer Вибрати попередній шар
|
||||
Selection Follows Playhead Виділення слідує за курсором відтворення
|
||||
Selection as FilmCraft Project… Виділення як проєкт FilmCraft…
|
||||
Selection… Виділення…
|
||||
Send Backward Перемістити назад
|
||||
Send to Back На задній план
|
||||
Sequence Секвенція
|
||||
Sequence From Clip Секвенція з кліпу
|
||||
Sequence Settings… Налаштування секвенції…
|
||||
Sequence… Секвенція…
|
||||
Settings Налаштування
|
||||
Show Active Caption Tracks Only Показати лише активні доріжки субтитрів
|
||||
Show All Caption Tracks Показати всі доріжки субтитрів
|
||||
Show All Marker Colors Показати всі кольори маркерів
|
||||
Show Guides Показати напрямні
|
||||
Show Rulers Показати лінійки
|
||||
Show Through Edits Показати розрізи без зміни кадру
|
||||
Simplify Sequence… Спростити секвенцію…
|
||||
Snap in Program Monitor Прив’язка в моніторі програми
|
||||
Snap in Timeline Прив’язка на часовій шкалі
|
||||
Source Джерело
|
||||
Source Settings… Налаштування джерела…
|
||||
Speed/Duration… Швидкість/тривалість…
|
||||
Synchronize… Синхронізувати…
|
||||
System Compatibility Report… Звіт про сумісність системи…
|
||||
Tan Світло-коричневий
|
||||
Teal Бірюзовий
|
||||
Text Текст
|
||||
Time Interpolation Часова інтерполяція
|
||||
Timecode Таймкод
|
||||
Timecode… Таймкод…
|
||||
Timeline Часова шкала
|
||||
Timeline… Часова шкала…
|
||||
Toggle Proxies Перемкнути проксі
|
||||
Tools Інструменти
|
||||
Top Верх
|
||||
Transcribe Sequence… Транскрибувати секвенцію…
|
||||
Transcribe… Транскрибувати…
|
||||
Transcript Транскрипція
|
||||
Transparent Video… Прозоре відео…
|
||||
Trim Edit Підрізати склейку
|
||||
Trim… Підрізання…
|
||||
Undo Скасувати
|
||||
Ungroup Розгрупувати
|
||||
Universal Counting Leader… Універсальний зворотний відлік…
|
||||
Update Metadata… Оновити метадані…
|
||||
Upgrade Caption to Graphic Перетворити субтитр на графіку
|
||||
Upgrade to Source Graphic Перетворити на вихідну графіку
|
||||
Vertical Text Вертикальний текст
|
||||
Video In Початок відео
|
||||
Video Options Параметри відео
|
||||
Video Out Кінець відео
|
||||
Video and Audio Waveform Split Розділений вигляд відео й осцилограми аудіо
|
||||
View Вигляд
|
||||
Violet Фіолетовий
|
||||
Window Вікно
|
||||
Workspaces Робочі середовища
|
||||
Yellow Жовтий
|
||||
Zoom In Збільшити
|
||||
Zoom Out Зменшити
|
||||
Zoom to Sequence Умістити секвенцію
|
||||
|
Can't render this file because it has a wrong number of fields in line 7.
|
|
|
@ -0,0 +1,8 @@
|
|||
asset: uk.tsv
|
||||
title: Ukrainian interface translation catalog
|
||||
author: FilmCraft contributors
|
||||
source: original work
|
||||
license: MIT OR Apache-2.0
|
||||
license-file: LICENSE-MIT and LICENSE-APACHE
|
||||
added: 2026-10-09 by dmatviichuk
|
||||
notes: Original English-to-Ukrainian menu translations migrated to the TSV catalog format.
|
||||
|
|
@ -26,6 +26,7 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
|||
uic!("app.language.japanese", "日本語", ["Edit", "Language"], None),
|
||||
uic!("app.language.spanish", "Español", ["Edit", "Language"], None),
|
||||
uic!("app.language.portuguese", "Português (Brasil)", ["Edit", "Language"], None),
|
||||
uic!("app.language.ukrainian", "Українська", ["Edit", "Language"], None),
|
||||
uic!("source.playback.toggle", "Source Play/Stop", [], None),
|
||||
uic!("source.playback.play", "Play Source", [], None),
|
||||
uic!("source.playback.stop", "Stop Source", [], None),
|
||||
|
|
@ -169,7 +170,7 @@ pub fn invoke(app: &mut FilmcraftApp, ctx: &egui::Context, id: &str, mut params:
|
|||
let object = params.as_object_mut().ok_or("command parameters must be an object")?;
|
||||
object.insert("target".into(), json!("source"));
|
||||
}
|
||||
if matches!(id, "app.language.english" | "app.language.japanese" | "app.language.spanish" | "app.language.portuguese") {
|
||||
if matches!(id, "app.language.english" | "app.language.japanese" | "app.language.spanish" | "app.language.portuguese" | "app.language.ukrainian") {
|
||||
// Japanese needs the craft-fonts (built with CRAFT_FONTS_DIR) or a font installed on the system
|
||||
if id == "app.language.japanese" && !crate::i18n::install_japanese_font(ctx) {
|
||||
return Err("no Japanese font is installed on this system (for example Noto Sans CJK JP); the interface stays in English".into());
|
||||
|
|
@ -178,6 +179,7 @@ pub fn invoke(app: &mut FilmcraftApp, ctx: &egui::Context, id: &str, mut params:
|
|||
"app.language.japanese" => crate::i18n::Language::Ja,
|
||||
"app.language.spanish" => crate::i18n::Language::Es,
|
||||
"app.language.portuguese" => crate::i18n::Language::PtBr,
|
||||
"app.language.ukrainian" => crate::i18n::Language::Uk,
|
||||
_ => crate::i18n::Language::En,
|
||||
};
|
||||
// The preference is updated in memory before it is written, so a failed write (read-only
|
||||
|
|
@ -548,6 +550,7 @@ pub fn menu_items(app: &FilmcraftApp) -> Vec<MenuItem> {
|
|||
"app.language.japanese" => it.checked = Some(app.ui.language == crate::i18n::Language::Ja),
|
||||
"app.language.spanish" => it.checked = Some(app.ui.language == crate::i18n::Language::Es),
|
||||
"app.language.portuguese" => it.checked = Some(app.ui.language == crate::i18n::Language::PtBr),
|
||||
"app.language.ukrainian" => it.checked = Some(app.ui.language == crate::i18n::Language::Uk),
|
||||
_ => {}
|
||||
}
|
||||
if it.id.starts_with("view.") {
|
||||
|
|
|
|||
Loading…
Reference in New Issue