mirror of
https://github.com/storytold/filmcraft.git
synced 2026-11-02 18:09:21 +00:00
Merge PR #3714: bs: Add Bosnian (bs) to the interface languages
This commit is contained in:
@@ -18,6 +18,7 @@ iconography, images or other Adobe assets; open licences only; every asset attri
|
||||
| `crates/ui-egui/src/i18n/fr.tsv` | FilmCraft contributors | Original work: clean-room French interface translations | MIT OR Apache-2.0 |
|
||||
| `crates/ui-egui/src/i18n/pl.tsv` | FilmCraft contributors | Original work: clean-room Polish interface translations | MIT OR Apache-2.0 |
|
||||
| `crates/ui-egui/src/i18n/pt-br.tsv` | FilmCraft contributors | Original work: clean-room Brazilian Portuguese interface translations | MIT OR Apache-2.0 |
|
||||
| `crates/ui-egui/src/i18n/bs.tsv` | FilmCraft contributors | Original work: AI-assisted clean-room Bosnian interface translations, not yet native-reviewed | MIT OR Apache-2.0 |
|
||||
| `crates/ui-egui/src/i18n/ru.tsv` | FilmCraft contributors | Original work: clean-room Russian interface translation (full catalog) | MIT OR Apache-2.0 |
|
||||
| `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`) |
|
||||
|
||||
@@ -85,6 +85,7 @@ Premiere ships 10 interface languages. Measured from our catalogs (3,593 strings
|
||||
| Portuguese (Brazil) | partial (whole interface; native review welcome) | ~100% |
|
||||
| Indonesian | partial (whole interface; native review welcome) | ~100% |
|
||||
| Italian | partial (whole interface; native review welcome) | ~100% |
|
||||
| Bosnian | partial (whole interface; AI-assisted, native review welcome) | ~100% |
|
||||
| Korean | partial (whole interface; needs an installed Hangul font; native review welcome) | ~100% |
|
||||
| Czech | partial (whole interface; native review needed) | 100% |
|
||||
| Russian | partial (whole interface; native review welcome) | ~100% |
|
||||
|
||||
@@ -145,6 +145,8 @@ pub fn install(app: &FilmcraftApp, ctx: egui::Context) -> (Receiver<String>, Men
|
||||
filmcraft_ui_egui::i18n::Language::Id
|
||||
} else if checked("app.language.italian") {
|
||||
filmcraft_ui_egui::i18n::Language::It
|
||||
} else if checked("app.language.bosnian") {
|
||||
filmcraft_ui_egui::i18n::Language::Bs
|
||||
} else if checked("app.language.korean") {
|
||||
filmcraft_ui_egui::i18n::Language::Ko
|
||||
} else if checked("app.language.czech") {
|
||||
|
||||
@@ -216,6 +216,7 @@ pub(super) static CATEGORIES: &[Category] = &[
|
||||
// Alphabetical by the language's own name after English; Cyrillic, then
|
||||
// CJK names follow the Latin ones. Edit ▸ Language lists them the same way.
|
||||
("id", "Bahasa Indonesia"),
|
||||
("bs", "Bosanski"),
|
||||
("cs", "Čeština"),
|
||||
("de", "Deutsch"),
|
||||
("es", "Español"),
|
||||
|
||||
@@ -47,6 +47,8 @@ pub enum Language {
|
||||
Fr,
|
||||
Id,
|
||||
It,
|
||||
/// Bosnian, Latin script (Ijekavian); `bs-Cyrl` locales use it too.
|
||||
Bs,
|
||||
/// Korean; Hangul needs an installed system font ([`korean_font_available`]).
|
||||
Ko,
|
||||
/// Czech; Latin with háčky and čárky, covered by the bundled fonts.
|
||||
@@ -70,13 +72,14 @@ static KOREAN: OnceLock<Catalog> = OnceLock::new();
|
||||
static POLISH: OnceLock<Catalog> = OnceLock::new();
|
||||
static INDONESIAN: OnceLock<Catalog> = OnceLock::new();
|
||||
static ITALIAN: OnceLock<Catalog> = OnceLock::new();
|
||||
static BOSNIAN: OnceLock<Catalog> = OnceLock::new();
|
||||
static CZECH: OnceLock<Catalog> = OnceLock::new();
|
||||
static SWEDISH: OnceLock<Catalog> = OnceLock::new();
|
||||
static TURKISH: OnceLock<Catalog> = OnceLock::new();
|
||||
|
||||
impl Language {
|
||||
pub const ALL: [Self; 16] =
|
||||
[Self::En, Self::Ja, Self::Es, Self::PtBr, Self::Uk, Self::ZhCn, Self::De, Self::Ru, Self::Fr, Self::Id, Self::Ko, Self::It, Self::Cs, Self::Pl, Self::Sv, Self::Tr];
|
||||
pub const ALL: [Self; 17] =
|
||||
[Self::En, Self::Ja, Self::Es, Self::PtBr, Self::Uk, Self::ZhCn, Self::De, Self::Ru, Self::Fr, Self::Id, Self::Ko, Self::It, Self::Cs, Self::Pl, Self::Sv, Self::Tr, Self::Bs];
|
||||
|
||||
/// BCP 47 tag for AccessKit and the web IME's hidden input. Keep the persisted spelling
|
||||
/// (`pt-br`, `zh-cn`) out of the protocol-facing value.
|
||||
@@ -93,6 +96,7 @@ impl Language {
|
||||
Self::Fr => "fr",
|
||||
Self::Id => "id",
|
||||
Self::It => "it",
|
||||
Self::Bs => "bs",
|
||||
Self::Ko => "ko",
|
||||
Self::Cs => "cs",
|
||||
Self::Pl => "pl",
|
||||
@@ -114,6 +118,7 @@ impl Language {
|
||||
Self::Fr => "Français",
|
||||
Self::Id => "Bahasa Indonesia",
|
||||
Self::It => "Italiano",
|
||||
Self::Bs => "Bosanski",
|
||||
Self::Ko => "한국어",
|
||||
Self::Cs => "Čeština",
|
||||
Self::Pl => "Polski",
|
||||
@@ -135,6 +140,7 @@ impl Language {
|
||||
"fr" => Some(Self::Fr),
|
||||
"id" => Some(Self::Id),
|
||||
"it" => Some(Self::It),
|
||||
"bs" => Some(Self::Bs),
|
||||
"ko" => Some(Self::Ko),
|
||||
"cs" => Some(Self::Cs),
|
||||
"pl" => Some(Self::Pl),
|
||||
@@ -150,11 +156,11 @@ impl Language {
|
||||
/// any German (`de-AT`, `de_CH.UTF-8`…) gets the German one and any French (`fr-CA`, `fr_BE.UTF-8`…)
|
||||
/// the French one; any Indonesian (`id-ID`, `id_ID.UTF-8`, and the legacy `in-ID` that older Java
|
||||
/// and Android runtimes report) the Indonesian one; any Italian (`it-IT`, `it-CH`, `it_IT.UTF-8`…)
|
||||
/// the Italian one; any Swedish (`sv-SE`, `sv-FI`, `sv_SE.UTF-8`…) the Swedish one; any Turkish (`tr`, `tr-TR`, `tr_TR.UTF-8`…) the Turkish one. Chinese gets the Simplified catalog
|
||||
/// the Italian one; any Bosnian (`bs-BA`, `bs_BA.UTF-8`, `bs-Latn-BA`…) the Bosnian one; any Swedish (`sv-SE`, `sv-FI`, `sv_SE.UTF-8`…) the Swedish one; any Turkish (`tr`, `tr-TR`, `tr_TR.UTF-8`…) the Turkish one. Chinese gets the Simplified catalog
|
||||
/// only for Simplified locales (`zh`, `zh-CN`, `zh-SG`, `zh-Hans…`); Traditional ones (`zh-TW`,
|
||||
/// `zh-HK`, `zh-MO`, `zh-Hant…`) are skipped.
|
||||
pub fn from_locales(tags: &[String]) -> Self {
|
||||
const PRIMARY: [(&str, Language); 16] = [
|
||||
const PRIMARY: [(&str, Language); 17] = [
|
||||
("en", Language::En),
|
||||
("ja", Language::Ja),
|
||||
("es", Language::Es),
|
||||
@@ -166,6 +172,7 @@ impl Language {
|
||||
("id", Language::Id),
|
||||
("in", Language::Id),
|
||||
("it", Language::It),
|
||||
("bs", Language::Bs),
|
||||
("ko", Language::Ko),
|
||||
("cs", Language::Cs),
|
||||
("pl", Language::Pl),
|
||||
@@ -202,6 +209,7 @@ impl Language {
|
||||
Self::Fr => "fr",
|
||||
Self::Id => "id",
|
||||
Self::It => "it",
|
||||
Self::Bs => "bs",
|
||||
Self::Ko => "ko",
|
||||
Self::Cs => "cs",
|
||||
Self::Pl => "pl",
|
||||
@@ -224,6 +232,7 @@ impl Language {
|
||||
Self::Fr => Some(FRENCH.get_or_init(|| Catalog::parse(include_str!("i18n/fr.tsv")))),
|
||||
Self::Id => Some(INDONESIAN.get_or_init(|| Catalog::parse(include_str!("i18n/id.tsv")))),
|
||||
Self::It => Some(ITALIAN.get_or_init(|| Catalog::parse(include_str!("i18n/it.tsv")))),
|
||||
Self::Bs => Some(BOSNIAN.get_or_init(|| Catalog::parse(include_str!("i18n/bs.tsv")))),
|
||||
Self::Ko => Some(KOREAN.get_or_init(|| Catalog::parse(include_str!("i18n/ko.tsv")))),
|
||||
Self::Cs => Some(CZECH.get_or_init(|| Catalog::parse(include_str!("i18n/cs.tsv")))),
|
||||
Self::Pl => Some(POLISH.get_or_init(|| Catalog::parse(include_str!("i18n/pl.tsv")))),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
asset: bs.tsv
|
||||
title: Bosnian 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-30 by nine-codes
|
||||
notes: AI-assisted clean-room English-to-Bosnian (Latin, Ijekavian) translations, shared with storytold/craft-i18n locales/bs/; not yet reviewed by a native speaker.
|
||||
@@ -14,6 +14,7 @@ fn catalog_text(l: Language) -> Option<&'static str> {
|
||||
Language::Fr => Some(include_str!("fr.tsv")),
|
||||
Language::Id => Some(include_str!("id.tsv")),
|
||||
Language::It => Some(include_str!("it.tsv")),
|
||||
Language::Bs => Some(include_str!("bs.tsv")),
|
||||
Language::Ko => Some(include_str!("ko.tsv")),
|
||||
Language::Cs => Some(include_str!("cs.tsv")),
|
||||
Language::Pl => Some(include_str!("pl.tsv")),
|
||||
@@ -66,6 +67,7 @@ fn unknown_strings_pass_through_and_languages_round_trip() {
|
||||
(Language::Fr, "Français"),
|
||||
(Language::Id, "Bahasa Indonesia"),
|
||||
(Language::It, "Italiano"),
|
||||
(Language::Bs, "Bosanski"),
|
||||
(Language::Ko, "한국어"),
|
||||
(Language::Cs, "Čeština"),
|
||||
(Language::Pl, "Polski"),
|
||||
@@ -525,6 +527,7 @@ fn language_commands_switch_and_persist_without_a_document() {
|
||||
("polish", Language::Pl, "pl", "Polski", Some(("app.language.italian", "Italiano"))),
|
||||
("swedish", Language::Sv, "sv", "Svenska", Some(("app.language.italian", "Italiano"))),
|
||||
("turkish", Language::Tr, "tr", "Türkçe", Some(("app.language.italian", "Italiano"))),
|
||||
("bosnian", Language::Bs, "bs", "Bosanski", Some(("app.language.italian", "Italiano"))),
|
||||
] {
|
||||
switch_and_check(&mut app, &ctx, name, lang, code, own, earlier);
|
||||
}
|
||||
@@ -873,3 +876,13 @@ fn turkish_renders_with_bundled_fonts() {
|
||||
let letters = entries.iter().flat_map(|(_, _, tr)| tr.chars()).filter(|ch| ch.is_alphabetic() || "“”‘’–—…".contains(*ch));
|
||||
assert_bundled_fonts_cover(alphabet.chars().chain(Language::Tr.name().chars()).chain(letters));
|
||||
}
|
||||
|
||||
/// Every Bosnian locale, with region, script or encoding, picks the Bosnian catalog.
|
||||
#[test]
|
||||
fn system_language_picks_bosnian() {
|
||||
let l = locales;
|
||||
for tag in ["bs", "BS", "bs-BA", "bs_BA.UTF-8", "bs-Latn-BA", "bs-Cyrl-BA", "BS-ba"] {
|
||||
assert_eq!(l(&[tag]), Language::Bs, "{tag}");
|
||||
}
|
||||
assert_eq!(l(&["hr-HR", "bs-BA"]), Language::Bs, "Croatian has no catalog here, so the next preference wins");
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
||||
// Alphabetical by the language's own name after English; Cyrillic, then CJK names follow
|
||||
// the Latin ones. Settings ▸ General ▸ Interface Language lists them the same way.
|
||||
uic!("app.language.indonesian", "Bahasa Indonesia", ["Edit", "Language"], None),
|
||||
uic!("app.language.bosnian", "Bosanski", ["Edit", "Language"], None),
|
||||
uic!("app.language.czech", "Čeština", ["Edit", "Language"], None),
|
||||
uic!("app.language.german", "Deutsch", ["Edit", "Language"], None),
|
||||
uic!("app.language.spanish", "Español", ["Edit", "Language"], None),
|
||||
@@ -208,6 +209,7 @@ pub fn invoke(app: &mut FilmcraftApp, ctx: &egui::Context, id: &str, mut params:
|
||||
| "app.language.french"
|
||||
| "app.language.indonesian"
|
||||
| "app.language.italian"
|
||||
| "app.language.bosnian"
|
||||
| "app.language.korean"
|
||||
| "app.language.czech"
|
||||
| "app.language.polish"
|
||||
@@ -237,6 +239,7 @@ pub fn invoke(app: &mut FilmcraftApp, ctx: &egui::Context, id: &str, mut params:
|
||||
"app.language.french" => crate::i18n::Language::Fr,
|
||||
"app.language.indonesian" => crate::i18n::Language::Id,
|
||||
"app.language.italian" => crate::i18n::Language::It,
|
||||
"app.language.bosnian" => crate::i18n::Language::Bs,
|
||||
"app.language.korean" => crate::i18n::Language::Ko,
|
||||
"app.language.czech" => crate::i18n::Language::Cs,
|
||||
"app.language.polish" => crate::i18n::Language::Pl,
|
||||
@@ -837,6 +840,7 @@ pub fn menu_items(app: &FilmcraftApp) -> Vec<MenuItem> {
|
||||
"app.language.french" => it.checked = Some(app.ui.language == crate::i18n::Language::Fr),
|
||||
"app.language.indonesian" => it.checked = Some(app.ui.language == crate::i18n::Language::Id),
|
||||
"app.language.italian" => it.checked = Some(app.ui.language == crate::i18n::Language::It),
|
||||
"app.language.bosnian" => it.checked = Some(app.ui.language == crate::i18n::Language::Bs),
|
||||
"app.language.korean" => it.checked = Some(app.ui.language == crate::i18n::Language::Ko),
|
||||
"app.language.czech" => it.checked = Some(app.ui.language == crate::i18n::Language::Cs),
|
||||
"app.language.polish" => it.checked = Some(app.ui.language == crate::i18n::Language::Pl),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-29 · **Change:** minor (German covers the whole interface; Czech and Polish interface catalogs; Swedish and Russian cover the whole interface; Turkish catalog) · **Target:** Adobe Premiere Pro 2026 (26.5.2)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-30 · **Change:** minor (German covers the whole interface; Czech and Polish interface catalogs; Swedish and Russian cover the whole interface; Turkish catalog; Bosnian covers the whole interface) · **Target:** Adobe Premiere Pro 2026 (26.5.2)
|
||||
|
||||
Per-language status of FilmCraft's interface. Premiere 26.5.2 ships 10 interface languages (the
|
||||
installed bundle's `.lproj` folders): German, English, Spanish, French, Italian, Japanese, Korean,
|
||||
@@ -59,6 +59,7 @@ whole interface, one PR) at ~4–8 h per language for the catalog plus tests.
|
||||
| *Other shipped:* Polish | pl | every Spanish entry plus the native macOS menu items; counts written count-neutral | as Spanish | Latin (bundled fonts) | not recorded; AI-drafted, review welcome | partial | review |
|
||||
| *Other shipped:* Swedish | sv | 3,581 (100%) | as Spanish | Latin (bundled fonts, å ä ö) | not recorded; welcome | partial | review |
|
||||
| *Other shipped:* Turkish | tr | 3,590 (same entries as Spanish, plus the macOS Settings item) | as Spanish | Latin (bundled fonts) | not recorded; welcome | partial | review |
|
||||
| *Other shipped:* Bosnian | bs | 3,610 (every entry of the other catalogs) | as Spanish | Latin (bundled fonts) | AI-assisted, not yet native-reviewed; welcome | partial | review |
|
||||
| *Other shipped:* Ukrainian | uk | 351 (10.0%) | menus only | Cyrillic (bundled) | not recorded | menus only | 5–9 h |
|
||||
|
||||
UI strings are catalog entries (the non-comment lines of `src/i18n/<code>.tsv`; English counts the
|
||||
@@ -100,6 +101,7 @@ creative effect names, and the shared `Subtitle` key (caption format and graphic
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-30 | minor | Bosnian (bs, Latin, Ijekavian) covers the whole interface: 3,610 entries, Edit ▸ Language ▸ Bosanski, every `bs-*` locale; AI-assisted, not native-reviewed |
|
||||
| 2026-10-26 | minor | Swedish (sv) covers the whole interface (all 3,581 strings); System Language picks it for `sv-*` locales |
|
||||
| 2026-10-23 | minor | Missing translations never fail a test or build (they show in English); `cargo xtask i18n-report`, also run by `cargo xtask ci`, lists them per language |
|
||||
| 2026-10-19 | minor | Added Czech (`cs`) interface catalog and locale detection; rewrote all catalog rows with a binding glossary; AI-assisted, not yet proofread by a native speaker |
|
||||
|
||||
Reference in New Issue
Block a user