**`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>
85 lines
3.6 KiB
Swift
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
|
|
}
|
|
}
|