Ergänzen fehlender Xcode-Funktionen - Teil 1

4. Oktober 2016

In diesem Artikel und den beiden folgenden geht es darum, wie du gewisse Schwächen von Xcode mithilfe von Tools und Programmierlösungen ausgleichen kannst. Hierzu wird für jedes Thema zunächst das Problem mit Xcode und dessen mögliche Folgen in der Praxis erläutert, woraufhin ein Lösungsvorschlag gegeben wird. Diese Woche konzentrieren wir uns in auf die fehlende Aktualisierung von Übersetzungen. Teil 2 wird sich mit dynamisch referenzierten Ressourcen beschäftigen, Teil 3 wird dir Informationen zu fehlenden Code Conventions und TODO-Warnungen geben.

Fehlende Aktualisierung von Übersetzungen

Problem

Xcode bietet die Möglichkeit mithilfe des in der Foundation Library definierten Makros NSLocalizedString(key, comment) Texte programmatisch zu lokalisieren. Dies ermöglicht das Anbieten einer App in vielen verschiedenen Programmiersprachen, deren Übersetzungen in den Localizable.strings-Dateien zusammengefasst sind (für jede Sprache eine eigene). Dabei wird im Quellcode lediglich ein Key verwendet, welcher in den .strings-Dateien die jeweiligen Texte in den jeweiligen Sprachen zugewiesen werden.

Eine Localizable.strings für die deutsche Sprache sieht etwa so aus:

"HOME.NAV_BAR.TITLE" = "Start";
"HOME.SETTINGS_BUTTON.NORMAL_TITLE" = "Einstellungen";
"SETTINGS.NAV_BAR.TITLE" = "Einstellungen";

Die zugehörige englische Version dann etwa so:

"HOME.NAV_BAR.TITLE" = "Home";
"HOME.SETTINGS_BUTTON.NORMAL_TITLE" = "Settings";
"SETTINGS.NAV_BAR.TITLE" = "Settings";

Im Code wird das Ganze dann folgendermaßen verwendet:

titleLabel.text = NSLocalizedString("HOME.NAV_BAR.TITLE", "")

Führt man eine App aus, sucht sie sich auf dem Endgerät je nach Geräteeinstellung die passenden Texte automatisch heraus und ersetzt die Keys, die im Quellcode verwendet wurden. Leider muss man in Xcode aktuell jedoch jeden neuen Key, den man im Code mittels NSLocalizedString verwendet, in jeder einzelnen Sprachfassung der .strings Dateien einzeln händisch hinzufügen. Dies kann je nach Projekt sehr viel Zeit in Anspruch nehmen und ist zudem fehleranfällig.

Außerdem lassen sich in Xcode ähnlich wie beim NSLocalizedString-Makro auch textbasierte Interface-Elemente über Keys lokalisieren, wobei hier jedoch eine XIB-Datei oder ein Storyboard in der Sprache "Base" zum Einsatz kommt, worin das Design für alle Sprachen festgelegt wird (siehe Base Internationalization). Auch hier bietet Xcode keine Option, neu hinzugefügte Textelemente im Nachhinein in den dazugehörigen .strings-Dateien zu ergänzen, sondern erneut ist nervige händische Arbeit gefragt.

Fügt man in einem Storyboard oder einer XIB etwa ein neues UILabel-Element hinzu und vergibt dort den Text "Nutzername", so bleiben die zugehörigen Strings-Dateien zum Storyboard oder XIB leer und erhalten nicht automatisch das neue UILabel als einen neuen Key-Eintrag. Die einzige Ausnahme bildet hier der Moment, in dem man ein Storyboard oder XIB erstmals lokalisiert. Beim Lokalisierungsvorgang durchsucht Xcode die Interface-Elemente und tut, was es eigentlich regelmäßig tun sollte, nämlich die gefundenen Keys in allen Sprachdateien automatisch anzulegen – dies ist in einer Welt mit sich verändernden Anforderungen jedoch keine ausreichende Lösung. Hier muss dringend eine ergänzend funktionierende Lösung her.

Lösungsvorschlag

Das Open Source Tool BartyCrouch wurde genau zur Lösung dieser beiden Probleme geboren. Die Installation findet via Homebrew mit den Befehlen brew tap flinesoft/bartycrouch und brew install flinesoft/bartycrouch/bartycrouch statt. Anschließend konfiguriert man BartyCrouch mittels eines Build-Scripts, welches im einfachen Fall folgendermaßen aussieht:

if which bartycrouch > /dev/null; then
    # Incrementally update all Storyboards/XIBs strings files
    bartycrouch interfaces -p "$PROJECT_DIR/Sources"

    # Add new keys to Localizable.strings files from NSLocalizedString in code
    bartycrouch code -p "$PROJECT_DIR/Sources" -l "$PROJECT_DIR/Sources" -a
else
    echo "warning: BartyCrouch not installed, download it from https://github.com/Flinesoft/BartyCrouch"
fi

Wie man in einem Projekt ein Build Script hinzufügt, wird hier ausführlich erklärt. Ist das einmal geschehen, wird BartyCrouch fortan automatisch alle Base-lokalisierten Storyboards und XIBs im Projektordner durchsuchen und aus den gefundenen Texten Einträge in den Strings-Dateien erstellen. Das Gleiche wird auch mit Objective-C und Swift-Dateien getan und entsprechend die Localizable.strings Dateien aktualisiert.

Da man beim Entwickeln von Natur aus immer wieder builden muss, braucht man sich also nach dem einmaligen Einrichten des Skripts pro Projekt um nichts mehr zu kümmern, da nun alle neuen Lokalisierungen bei jedem Build aktuell gehalten werden. Die Übersetzungen werden hierbei übrigens leer gelassen, diese müssen dann natürlich noch ausgefüllt werden.

Ausnahmen

BartyCrouch funktioniert grundsätzlich so, dass es sämtliche übersetzbaren Interface-Elemente in Storyboards und XIBs in die .strings Dateien aufnimmt. Manchmal legt man jedoch etwa ein UILabel-Element im Interface Builder an mit der Absicht, dessen Wert erst später programmatisch zu setzen – eine Übersetzung wäre dafür also sinnlos. In solchen Fällen bietet BartyCrouch die Möglichkeit, mithilfe der Markierung #bc-ignore! das Ignorieren des Interface Elements bei der automatischen Übersetzung zu erzwingen. Das so markierte Element wird dann nicht in die .strings Dateien eingefügt. Dies kann etwa so aussehen:

Beispielbild mit teilweise automatisch generierten String-Keys Hier werden die Werte (rechts) für die Bezeichner (links) programmatisch gesetzt und können deshalb von der Übersetzung ausgeschlossen werden.