Wenn es schnell gehen soll: Prüfen Sie die Platzhalter einer Übersetzung direkt im Browser, kostenlos, ohne Anmeldung und ohne Upload.
Woher das % kommt
In einer iOS-App wird ein lokalisierter String mit einer Variable zu einem Format-String. In SwiftUI geschieht das implizit:
Text("\(count) items left") // key: "%lld items left"
Text("Welcome back, \(name)!") // key: "Welcome back, %@!"
String(localized: "Total: \(price) €") // price is a Double: "Total: %lf €"
Ein Int ergibt %lld, ein String ergibt %@, ein Double ergibt %lf. Mit der älteren API schreiben Sie das Format selbst: String(format: NSLocalizedString("items_left", comment: ""), count), und die .strings-Datei enthält %d oder %ld.
So oder so bekommt der Übersetzer Text mit %-Zeichen mittendrin, und genau dort geht etwas kaputt.
Drei Arten, eine Übersetzung kaputtzumachen
Den Bezeichner weglassen. "Invitez à modifier cette liste" statt "Invitez %@ à modifier cette liste". Nichts stürzt ab: Das Argument wird einfach ignoriert, und der Name der Person verschwindet vom Bildschirm. Das ist der häufigste und unauffälligste Fehler.
Den Typ ändern. %lld durch %@ ersetzen oder umgekehrt. Das Format liest das Argument dann mit dem falschen Typ: im besten Fall eine unsinnige Zahl, im schlimmsten Fall ein Absturz, weil %@ ein Objekt erwartet, wo es eine Ganzzahl bekommt.
Die Reihenfolge stillschweigend ändern. "%@ shared %@" wird in einer Sprache mit anderer Wortstellung zu einem Satz, in dem die beiden Namen vertauscht sind. Die Syntax stimmt, der Sinn nicht.
Mit Positionen umstellen
Um die Reihenfolge zu ändern, nummerieren Sie die Argumente: %1$@ ist das erste Argument, %2$@ das zweite, egal wo sie im Satz stehen.
/* source */ "%@ shared %@ with you" = "%1$@ shared %2$@ with you";
/* French */ "%@ shared %@ with you" = "%1$@ vous a partagé %2$@";
/* other order */ "%@ shared %@ with you" = "%2$@ : partagé par %1$@";
Eine Regel zum Merken: Innerhalb eines Strings wird entweder alles nummeriert oder nichts. %1$@ und %@ zu mischen ist undefiniertes Verhalten.
Eine Falle im Französischen (und im Deutschen): „20 % Rabatt“
Wenn Sie auf Französisch oder Deutsch veröffentlichen, sollten Sie diese kennen. Beide Typografien setzen ein Leerzeichen vor das Prozentzeichen: „20 % de remise“, „20 % Rabatt“. In einem Format-String ist das ein Problem, denn % d (Prozent, Leerzeichen, d) ist ein gültiger Bezeichner: Das Leerzeichen ist ein printf-Flag („positive Zahlen mit einem Leerzeichen auffüllen“) und d die Ganzzahl-Konvertierung. Der Satz
"Profitez de %lld % de remise"
enthält also zwei Bezeichner, %lld und % d, für ein einziges Argument. Der zweite liest einen Wert, den es nicht gibt. Die korrekte Form verdoppelt das Zeichen:
"Profitez de %lld %% de remise"
Im Deutschen ist % R zwar kein gültiger Bezeichner, aber auch dort gilt: In einem String mit Variablen schreiben Sie %%. Das betrifft nur Strings, die durch ein Format laufen, also Strings mit mindestens einer Variable. Ein festes „20 % Rabatt“ ohne Argument wird so angezeigt, wie es geschrieben ist. Übersetzungen ändern sich aber: An dem Tag, an dem jemand diesem Satz eine Variable hinzufügt, wird das einzelne % zum Fehler.
Pluralformen
Für „1 Artikel / 3 Artikel“ schreiben Sie nicht zwei Schlüssel und ein if in Ihren Code. In einem String Catalog bekommt der Schlüssel %lld items left Pluralvarianten (one, other, je nach Sprache zusätzlich few oder many), jede mit ihrem eigenen %lld. Hat ein Satz zwei Zahlen, die unabhängig voneinander variieren, nutzt Xcode Substitutionen (%#@items@): ein seltenerer Fall, der in Xcode bearbeitet wird.
Vor dem Release prüfen
Die Grundprüfung ist mechanisch: Für jede Übersetzung muss die Liste der Bezeichner (Typ und Position) mit dem Ausgangstext übereinstimmen. Genau das macht die QA von LocalePilot bei .strings- und .xcstrings-Dateien: Sie erkennt %@, %1$@, %d, %ld, %lld, %.2f, %u, %i und %s, behandelt %% als Text und markiert vor dem Export einen fehlenden oder überzähligen Bezeichner. Seltene Konvertierungen wie %e oder %x ignoriert sie bewusst, damit „20%en“ nicht für einen Fehler gehalten wird. Den Fall „% d“ oben müssen Sie selbst prüfen: Suchen Sie in Ihren französischen und deutschen Übersetzungen nach einem %, auf das ein Leerzeichen folgt.