Merge PR #3714: bs: Add Bosnian (bs) to the interface languages

This commit is contained in:
aiongg
2026-10-31 02:14:54 +00:00
10 changed files with 3663 additions and 5 deletions
+1
View File
@@ -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`) |
+1
View File
@@ -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% |
+2
View File
@@ -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") {
+1
View File
@@ -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"),
+13 -4
View File
@@ -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.
+13
View File
@@ -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");
}
+4
View File
@@ -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),
+3 -1
View File
@@ -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 |