Files
Flaro/Packages/FlixyKit/Sources/FlixyPlayback/Decision/LanguageCode.swift
T
ScarriffleandClaude Opus 5 afb4a63655 Der Kern der Sprachwahl: Kürzel, die nicht lügen, und Abwägen statt Filtern
**`LanguageCode`** bringt Kürzel auf eine vergleichbare Form. Der Anlass
ist ein stiller Fehler: verglichen wurden die ersten zwei Zeichen, also
`ge` gegen `de` — und `ger` ist die ISO-639-2/B-Form von Deutsch, die
ffmpeg in viele Dateien schreibt. Bei solchen Titeln wirkte die
Spracheinstellung überhaupt nicht.

`gsw` bleibt `gsw`. Schweizerdeutsch ist keine Schreibweise von Deutsch,
und es gleichzusetzen hiesse, jemandem Schweizerdeutsch unterzuschieben,
der Hochdeutsch wollte. Es gehört in die Rangliste, nicht in eine
Gleichsetzung.

**`AudioTrackChooser`** wägt ab, statt zu filtern: jede Spur bekommt
Punkte, die höchste gewinnt. Damit gibt es immer eine Antwort, und
Audiodeskription wird gemieden, ohne unmöglich zu werden — gibt es eine
Sprache nur mit Audiodeskription, ist sie besser als Stille.

Gemerkt wird nicht eine Nummer, sondern eine Beschreibung der Spur:
Folgen derselben Serie haben unterschiedlich sortierte Spuren, und
„Spur 2" bedeutet in der nächsten Folge etwas anderes.

Siebzehn Tests, darunter der ZDF-Fall wörtlich — deutsche Spur mit
Audiodeskription als Standard, englisch betitelte deutsche Spur daneben
— und zwar in beide Richtungen: mit Meiden gewinnt die richtige, ohne
Meiden gewinnt das Kürzel. Sonst stünde der Schalter nur dabei.

Dazu trägt `MediaStream` jetzt `title`, `isDefault`, `isHearingImpaired`
und `isOriginal`. Der Name aus der Datei ist das zweite Signal neben dem
Kürzel und manchmal das einzige richtige.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 19:15:08 +02:00

85 lines
3.6 KiB
Swift

import Foundation
/// Sprachkürzel vergleichen, ohne auf ihre Schreibweise hereinzufallen.
///
/// **Warum es das gibt.** Verglichen wurden bisher die ersten zwei Zeichen.
/// Das ist in einem Fall falsch, und ausgerechnet der ist häufig:
///
/// deu ~ de → Treffer
/// ger ~ de → KEIN Treffer
/// de-DE ~ de → Treffer
///
/// `ger` ist die ISO-639-2/B-Form von Deutsch, und ffmpeg schreibt sie in
/// viele Dateien. Bei solchen Titeln wirkte die Spracheinstellung
/// **überhaupt nicht** — lautlos, ohne dass irgendetwas darauf hindeutete.
///
/// Zwanzig Sprachen haben zwei verschiedene dreibuchstabige Formen: eine
/// bibliografische (/B, aus dem Englischen) und eine terminologische (/T, aus
/// der Sprache selbst). Nur die zwanzig stehen unten — für alle anderen ist
/// die dreibuchstabige Form eindeutig, und eine vollständige Tabelle wäre
/// siebenhundert Zeilen, die niemand prüft.
public enum LanguageCode {
/// Paare, bei denen dieselbe Sprache zwei dreibuchstabige Kürzel hat.
///
/// Zusätzlich die zweibuchstabige Form, denn das ist das Ziel: alles,
/// was dasselbe meint, soll auf denselben Wert fallen.
private static let aliases: [String: String] = [
"alb": "sq", "sqi": "sq",
"arm": "hy", "hye": "hy",
"baq": "eu", "eus": "eu",
"bur": "my", "mya": "my",
"chi": "zh", "zho": "zh",
"cze": "cs", "ces": "cs",
"dut": "nl", "nld": "nl",
"fre": "fr", "fra": "fr",
"geo": "ka", "kat": "ka",
"ger": "de", "deu": "de",
"gre": "el", "ell": "el",
"ice": "is", "isl": "is",
"mac": "mk", "mkd": "mk",
"mao": "mi", "mri": "mi",
"may": "ms", "msa": "ms",
"per": "fa", "fas": "fa",
"rum": "ro", "ron": "ro",
"slo": "sk", "slk": "sk",
"tib": "bo", "bod": "bo",
"wel": "cy", "cym": "cy",
// Die häufigen, die keine zwei Formen haben, aber oft dreibuchstabig
// in Dateien stehen.
"eng": "en", "spa": "es", "ita": "it", "por": "pt", "rus": "ru",
"jpn": "ja", "kor": "ko", "pol": "pl", "swe": "sv", "nor": "no",
"dan": "da", "fin": "fi", "tur": "tr", "ara": "ar", "heb": "he",
"hin": "hi", "tha": "th", "vie": "vi", "ukr": "uk", "hun": "hu",
"bul": "bg", "hrv": "hr", "srp": "sr", "slv": "sl", "est": "et",
"lav": "lv", "lit": "lt", "cat": "ca", "gle": "ga",
]
/// Bringt ein Kürzel auf eine Form, die sich vergleichen lässt.
///
/// **`gsw` bleibt `gsw`.** Schweizerdeutsch ist keine Schreibweise von
/// Deutsch, sondern eine eigene Sprache ohne zweibuchstabiges Kürzel. Es
/// auf `de` abzubilden wäre bequem und falsch: wer Hochdeutsch will,
/// bekäme Schweizerdeutsch untergeschoben. Dasselbe gilt für Bairisch
/// (`bar`), Plattdeutsch (`nds`) und alles andere dieser Art — sie gehören
/// in die Rangliste, nicht in eine Gleichsetzung.
public static func normalized(_ raw: String?) -> String? {
guard let raw else { return nil }
// Region abschneiden: „de-DE", „pt_BR", „zh-Hans".
let ohneRegion = raw
.lowercased()
.split(whereSeparator: { $0 == "-" || $0 == "_" })
.first
.map(String.init) ?? ""
guard !ohneRegion.isEmpty else { return nil }
if let bekannt = aliases[ohneRegion] { return bekannt }
return ohneRegion
}
/// Ob zwei Kürzel dieselbe Sprache meinen.
public static func matches(_ lhs: String?, _ rhs: String?) -> Bool {
guard let links = normalized(lhs), let rechts = normalized(rhs) else { return false }
return links == rechts
}
}