it-swarm.com.de

Best Practice mit NSLocalizedString

Ich verwende (wie alle anderen) NSLocalizedString, um meine App zu lokalisieren.

Leider gibt es mehrere "Nachteile" (nicht unbedingt der Fehler von NSLocalizedString selbst), einschließlich

  • Keine automatische Vervollständigung von Zeichenketten in Xcode. Dies macht das Arbeiten nicht nur fehleranfällig, sondern auch ermüdend.
  • Am Ende kann es sein, dass Sie eine Zeichenfolge neu definieren, nur weil Sie nicht wussten, dass bereits eine entsprechende Zeichenfolge vorhanden war (d. H. "Bitte geben Sie das Kennwort ein" oder "Geben Sie zuerst das Kennwort ein").
  • Ähnlich wie beim Autocompletion-Problem müssen Sie sich die Kommentar-Strings "merken/kopieren". Andernfalls genstring erhält mehrere Kommentare für einen String
  • Wenn Sie genstring verwenden möchten, nachdem Sie bereits einige Zeichenfolgen lokalisiert haben, müssen Sie darauf achten, dass Ihre alten Lokalisierungen nicht verloren gehen.
  • Die gleichen Saiten sind in Ihrem gesamten Projekt verstreut. Beispielsweise haben Sie NSLocalizedString(@"Abort", @"Cancel action") überall verwendet, und Sie werden von Code Review aufgefordert, die Zeichenfolge in NSLocalizedString(@"Cancel", @"Cancel action") umzubenennen, um den Code konsistenter zu machen.

Was ich mache (und nach einigen Suchen auf SO habe ich gedacht, dass viele Leute dies tun), ist eine separate strings.h-Datei zu haben, in der ich den gesamten localize-Code #define hinterlegt habe. Zum Beispiel

// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(@"Cancel", nil)
// Somewhere else
NSLog(@"%@", NSLS_COMMON_CANCEL);

Dies bietet im Wesentlichen die Code-Vervollständigung, eine einzige Stelle zum Ändern von Variablennamen (sodass keine Genstring-Funktion mehr erforderlich ist) und ein eindeutiges Schlüsselwort für die automatische Umgestaltung. Dies ist jedoch mit den Kosten verbunden, die mit einer ganzen Reihe von #define-Anweisungen enden, die nicht inhärent strukturiert sind (d. H. Wie LocString.Common.Cancel oder ähnliches). 

Obwohl dies gut funktioniert, habe ich mich gefragt, wie Sie das in Ihren Projekten tun. Gibt es andere Ansätze, um die Verwendung von NSLocalizedString zu vereinfachen? Gibt es vielleicht sogar einen Rahmen, der es einschließt?

129
JiaYow

NSLocalizedString hat einige Einschränkungen, aber es ist für Cocoa so zentral, dass es unangemessen ist, benutzerdefinierten Code für die Lokalisierung zu schreiben, was bedeutet, dass Sie ihn verwenden müssen. Ein wenig Werkzeug kann helfen, hier gehe ich vor:

Aktualisieren der String-Datei

genstrings überschreibt Ihre String-Dateien und verwirft alle Ihre vorherigen Übersetzungen . Ich schrieb update_strings.py , um die alte String-Datei zu analysieren, führen Sie genstrings aus und füllen Sie die leeren Felder aus, damit Sie Ihre vorhandenen Dateien nicht manuell wiederherstellen müssen translations . Das Skript versucht, die vorhandenen String-Dateien so gut wie möglich zusammenzubringen, um zu vermeiden, dass beim Aktualisieren zu große Unterschiede auftreten.

Benennen Sie Ihre Saiten

Wenn Sie NSLocalizedString wie angekündigt verwenden:

NSLocalizedString(@"Cancel or continue?", @"Cancel notice message when a download takes too long to proceed");

Sie können am Ende dieselbe Zeichenfolge in einem anderen Teil Ihres Codes definieren, was zu Konflikten führen kann, da derselbe englische Begriff in verschiedenen Kontexten eine unterschiedliche Bedeutung haben kann (OK und Cancel). Deshalb benutze ich immer eine bedeutungslose All-Caps-String mit einem modulspezifischen Präfix und einer sehr genauen Beschreibung:

NSLocalizedString(@"DOWNLOAD_CANCEL_OR_CONTINUE", @"Cancel notice window title when a download takes too long to proceed");

Verwenden Sie dieselbe Zeichenfolge an verschiedenen Stellen

Wenn Sie dieselbe Zeichenfolge mehrmals verwenden, können Sie entweder ein Makro wie Sie verwenden oder als Instanzvariable in Ihrem View-Controller oder in Ihrer Datenquelle zwischengespeichert haben .. Diese Beschreibung müssen Sie nicht wiederholen Die Instanzen der gleichen Lokalisierung, die immer verwirrend ist, werden möglicherweise veraltet und inkonsistent. Da Instanzvariablen Symbole sind, können Sie die automatische Vervollständigung für diese gebräuchlichsten Übersetzungen und "manuelle" Zeichenfolgen für die bestimmte, die sowieso nur einmal vorkommen würden.

Ich hoffe, dass Sie mit diesen Tipps die Kakaolokalisierung produktiver gestalten werden!

92
ndfred

Für die automatische Vervollständigung von Zeichenfolgen in Xcode können Sie http://questbe.at/lin/ versuchen.

30
hiroshi

Ich stimme mit ndfred überein, aber ich möchte folgendes hinzufügen:

Zweiter Parameter kann als ... Standardwert verwendet werden !!

(NSLocalizedStringWithDefaultValue funktioniert mit genstring nicht richtig. Deshalb habe ich diese Lösung vorgeschlagen.)

Hier ist meine benutzerdefinierte Implementierung, die NSLocalizedString verwendet, die Kommentar als Standardwert verwendet:

1. Definieren Sie in Ihrem vorkompilierten Header (.pch-Datei) das Makro 'NSLocalizedString' neu:

// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"

#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key  comment:_comment]

2. eine Klasse erstellen, um den Lokalisierungshandler zu implementieren

#import "LocalizationHandlerUtil.h"

@implementation LocalizationHandlerUtil

static LocalizationHandlerUtil * singleton = nil;

+ (LocalizationHandlerUtil *)singleton
{
    return singleton;
}

__attribute__((constructor))
static void staticInit_singleton()
{
    singleton = [[LocalizationHandlerUtil alloc] init];
}

- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
    // default localized string loading
    NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];

    // if (value == key) and comment is not nil -> returns comment
    if([localizedString isEqualToString:key] && comment !=nil)
        return comment;

    return localizedString;
}

@end

3. Verwenden Sie es!

Stellen Sie sicher, dass Sie in Ihren App Build-Phasen ein Ausführungsskript hinzufügen, damit die Localizable.strings-Datei bei jedem Build aktualisiert wird, d. H. Neue lokalisierte Zeichenfolge wird in Ihre Localized.strings-Datei eingefügt:

Mein Build-Phase-Skript ist ein Shell-Skript:

Shell: /bin/sh
Shell script content: find . -name \*.m | xargs genstrings -o MyClassesFolder

Wenn Sie also diese neue Zeile in Ihren Code einfügen:

self.title = NSLocalizedString(@"view_settings_title", @"Settings");

Führen Sie dann einen Build durch. Ihre ./Localizable.scripts-Datei enthält diese neue Zeile:

/* Settings */
"view_settings_title" = "view_settings_title";

Und da key == value für 'view_settings_title' ist, gibt der benutzerdefinierte LocalizedStringHandler den Kommentar zurück, d. H. 'Settings'.

Voilà :-)

23
Pascal

Ich habe ein Skript geschrieben, um Localizable.strings in mehreren Sprachen zu verwalten. Während es bei der automatischen Vervollständigung nicht hilft, hilft es, .strings-Dateien mit folgendem Befehl zusammenzuführen:

merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings

Weitere Informationen finden Sie unter: https://github.com/hiroshi/merge_strings

Einige von Ihnen finden es nützlich, hoffe ich.

2
hiroshi

In Swift verwende ich Folgendes, z. für die Schaltfläche "Ja" in diesem Fall: 

NSLocalizedString("btn_yes", value: "Yes", comment: "Yes button")

Beachten Sie die Verwendung von value: für den Standardtextwert. Der erste Parameter dient als Übersetzungs-ID. Die Verwendung des Parameters value: hat den Vorteil, dass der Standardtext später geändert werden kann, die Übersetzungs-ID jedoch unverändert bleibt. Die Localizable.strings-Datei enthält "btn_yes" = "Yes";

Wenn der value:-Parameter nicht verwendet wird, wird der erste Parameter sowohl für die Übersetzungs-ID als auch für den Standardtextwert verwendet. Die Localizable.strings-Datei würde "Yes" = "Yes"; enthalten. Diese Art der Verwaltung von Lokalisierungsdateien scheint seltsam zu sein. Besonders wenn der übersetzte Text lang ist, ist auch die ID lang. Wenn ein Zeichen des Standardtextwerts geändert wird, wird auch die Übersetzungs-ID geändert. Dies führt zu Problemen, wenn externe Übersetzungssysteme verwendet werden. Das Ändern der Übersetzungs-ID wird als Hinzufügen von neuem Übersetzungstext verstanden, der möglicherweise nicht immer gewünscht wird.

1
petrsyn

Ich selbst bin oft mit dem Codieren beschäftigt und habe vergessen, die Einträge in .strings-Dateien abzulegen. Daher habe ich Hilfsskripte, um herauszufinden, was ich schulde, um es wieder in .strings-Dateien zu packen und zu übersetzen.

Da ich über NSLocalizedString mein eigenes Makro verwende, bitte überprüfen und aktualisieren Sie das Skript, bevor Sie es verwenden, da ich der Einfachheit halber angenommen habe, dass nil als zweiter Parameter für NSLocalizedString verwendet wird. Der Teil, den Sie ändern möchten, ist

NSLocalizedString\(@(".*?")\s*,\s*nil\) 

Ersetzen Sie es einfach durch etwas, das zu Ihrer Makro- und NSLocalizedString-Verwendung passt.

Hier kommt das Drehbuch, du brauchst ja nur Teil 3. Der Rest ist einfacher zu sehen, woher alles kommt:

// Part 1. Get keys from one of the Localizable.strings
Perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings

// Part 2. Get keys from the source code
grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | Perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/'

// Part 3. Get Part 1 and 2 together.

comm -2 -3 <(grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | Perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/' | sort | uniq) <(Perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt

Die Ausgabedatei enthält Schlüssel, die im Code gefunden wurden, jedoch nicht in der Datei Localizable.strings. Hier ist ein Beispiel:

"MPH"
"Map Direction"
"Max duration of a detailed recording, hours"
"Moving ..."
"My Track"
"New Trip"

Sicherlich kann mehr poliert werden, aber ich dachte, ich würde teilen.

Wenn jemand nach einer schnellen Lösung sucht. Vielleicht möchten Sie meine Lösung überprüfen, die ich hier zusammengestellt habe: SwiftyLocalization

Mit wenigen Schritten zum Einrichten haben Sie eine sehr flexible Lokalisierung in Google Spreadsheet (Kommentar, benutzerdefinierte Farbe, Hervorhebung, Schriftart, mehrere Arbeitsblätter usw.).

Die Schritte sind kurz: Google Spreadsheet -> CSV-Dateien -> Localizable.strings

Darüber hinaus generiert es auch Localizables.Swift, eine Struktur, die für Sie als Schnittstelle für das Abrufen und Dekodieren von Schlüsseln fungiert (Sie müssen jedoch manuell festlegen, wie String aus Schlüssel dekodiert wird).

Warum ist das großartig?

  1. Sie brauchen keinen Schlüssel mehr als einfachen String an allen Stellen.
  2. Falsche Schlüssel werden beim Kompilieren erkannt.
  3. Xcode kann die automatische Vervollständigung durchführen. 

Es gibt Tools, die Ihren lokalisierbaren Schlüssel automatisch vervollständigen können. Ein Verweis auf eine echte Variable stellt sicher, dass es sich immer um einen gültigen Schlüssel handelt, andernfalls wird er nicht kompiliert.

// It's defined as computed static var, so it's up-to-date every time you call. 
// You can also have your custom retrieval method there.

button.setTitle(Localizables.login.button_title_login, forState: .Normal)

Das Projekt verwendet Google App Script zum Konvertieren von Sheets -> CSV und eines Python-Skripts zum Konvertieren von CSV-Dateien -> Localizable.strings. Sie können sich dieses Beispielblatt kurz ansehen, um zu erfahren, was möglich ist.

0
aunnnn
#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]
0
baozhifei

bei iOS 7 und Xcode 5 sollten Sie die Methode 'Localization.strings' vermeiden und die neue Methode 'base localization' verwenden. Es gibt einige Tutorials, wenn Sie nach "Basislokalisierung" suchen.

Apple-Dokument: Basislokalisierung

0
Ronny Webers