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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
946de29893
commit
afb4a63655
@@ -0,0 +1,84 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user