So erzwingen Sie, dass NSLocalizedString eine bestimmte Sprache verwendet

263

Auf dem iPhone wird NSLocalizedStringdie Zeichenfolge in der Sprache des iPhone zurückgegeben. Ist es möglich, NSLocalizedStringdie Verwendung einer bestimmten Sprache zu erzwingen , damit die App in einer anderen Sprache als das Gerät angezeigt wird?

CodeFlakes
quelle
Bitte beziehen Sie sich auf: stackoverflow.com/a/48187049/6665075 Funktioniert wie ein Zauber
Ankit Kumar Gupta

Antworten:

261

NSLocalizedString()(und Varianten davon) greifen Sie auf den Schlüssel "AppleLanguages" NSUserDefaultszu, um die Einstellungen des Benutzers für bevorzugte Sprachen zu bestimmen. Dies gibt eine Reihe von Sprachcodes zurück, wobei der erste vom Benutzer für sein Telefon festgelegt wird und die nachfolgenden als Fallbacks verwendet werden, wenn eine Ressource nicht in der bevorzugten Sprache verfügbar ist. (Auf dem Desktop kann der Benutzer mehrere Sprachen mit einer benutzerdefinierten Reihenfolge in den Systemeinstellungen angeben.)

Sie können die globale Einstellung für Ihre eigene Anwendung überschreiben, wenn Sie möchten, indem Sie die Methode setObject: forKey: verwenden, um Ihre eigene Sprachliste festzulegen. Dies hat Vorrang vor dem global festgelegten Wert und wird an jeden Code in Ihrer Anwendung zurückgegeben, der die Lokalisierung durchführt. Der Code dafür würde ungefähr so ​​aussehen:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"de", @"en", @"fr", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; //to make the change immediate

Dies würde Deutsch zur bevorzugten Sprache für Ihre Bewerbung machen, mit Englisch und Französisch als Fallbacks. Sie sollten dies irgendwann früh beim Start Ihrer Anwendung aufrufen. Weitere Informationen zu Sprach- / Gebietsschemaeinstellungen finden Sie hier: Themen der Internationalisierungsprogrammierung: Abrufen der aktuellen Sprache und des Gebietsschemas

Brian Webster
quelle
4
Dies funktioniert bei mir nicht, es wird die Standardsprache verwendet, egal was passiert.
Quano
50
Denniss; Es scheint besser zu funktionieren, wenn Sie die Spracheinstellungen vor dem Start der App festlegen . Ich mache es in der main () Funktion, bevor UIApplicationMain () aufgerufen wird. Sobald es tatsächlich ausgeführt wird, ändert es nicht die verwendete Sprache, sondern legt nur die Einstellungen für das nächste Mal fest.
Geon
3
Sie müssen die Sprache festlegen, bevor Sie UIKit initialisieren, und Sie müssen ein vollständiges Gebietsschema für Sprache + Region angeben. Ein vollständiges Beispiel finden Sie in blog.federicomestrone.com/2010/09/15/…
fedmest
18
Durch das Festlegen von AppleLanguages ​​wird zur Laufzeit eine Sprache geändert, wenn dies in main () erfolgt - true! aber ... das ERSTE Mal, wenn die App installiert wird, werden die nsuserdefaults NACH dem Aufruf von UIApplicationMain () initialisiert, wodurch die zuvor festgelegten AppleLanguages ​​ignoriert werden und ein hässlicher Neustart der App erforderlich ist, um die gewünschte Sprache anzuzeigen.
JohnPayne
3
Dies funktioniert nicht mit mehreren in Localizable.stringsdict definierten Elementen. Irgendeine Idee, ob es eine mögliche Lösung gibt?
Mihai Fratu
147

Ich hatte kürzlich das gleiche Problem und wollte nicht meinen gesamten NSLocalizedString starten und patchen noch die App zum Neustart zwingen, damit die neue Sprache funktioniert. Ich wollte, dass alles so funktioniert, wie es ist.

Meine Lösung bestand darin, die Klasse des Hauptpakets dynamisch zu ändern und das entsprechende Paket dort zu laden:

Header-Datei

@interface NSBundle (Language)
+(void)setLanguage:(NSString*)language;
@end

Implementierung

#import <objc/runtime.h>

static const char _bundle=0;

@interface BundleEx : NSBundle
@end

@implementation BundleEx
-(NSString*)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    NSBundle* bundle=objc_getAssociatedObject(self, &_bundle);
    return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}
@end

@implementation NSBundle (Language)
+(void)setLanguage:(NSString*)language
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        object_setClass([NSBundle mainBundle],[BundleEx class]);
    });
    objc_setAssociatedObject([NSBundle mainBundle], &_bundle, language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]] : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

Wenn Ihre App gestartet wird und bevor Sie Ihren ersten Controller laden, rufen Sie einfach an:

[NSBundle setLanguage:@"en"];

Wenn Ihr Benutzer seine bevorzugte Sprache in Ihrem Einstellungsbildschirm ändert, rufen Sie ihn einfach erneut auf:

[NSBundle setLanguage:@"fr"];

Um auf die Systemstandards zurückzusetzen, übergeben Sie einfach nil:

[NSBundle setLanguage:nil];

Genießen...

Für diejenigen, die eine Swift-Version benötigen:

var bundleKey: UInt8 = 0

class AnyLanguageBundle: Bundle {

    override func localizedString(forKey key: String,
                                  value: String?,
                                  table tableName: String?) -> String {

        guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
              let bundle = Bundle(path: path) else {

            return super.localizedString(forKey: key, value: value, table: tableName)
            }

        return bundle.localizedString(forKey: key, value: value, table: tableName)
    }
}

extension Bundle {

    class func setLanguage(_ language: String) {

        defer {

            object_setClass(Bundle.main, AnyLanguageBundle.self)
        }

        objc_setAssociatedObject(Bundle.main, &bundleKey,    Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
}
Gilad Novik
quelle
7
Hi @Wirsing, das funktioniert bisher großartig für mich. Ich habe sogar eine App in den Store hochgeladen und keine Beschwerden von Apple.
Gilad Novik
7
Dies ist eine großartige Lösung, auf die ich hier auch verweise: medium.com/ios-apprentice/905e4052b9de
James Tang
1
Hallo @JamesTang, vielen Dank für die Referenz und Ihren Beitrag - ich habe dort viele nützliche Informationen zur Lokalisierung gefunden.
Gilad Novik
3
@ Gilad Ihre Methode funktioniert perfekt für dynamische Zeichenfolgen (Zeichenfolgen, die ich in localizable.strings definiert habe), aber für Storyboard-Schaltflächen und Beschriftungen funktioniert sie nur bei der Hauptmethode. Können Sie Ihre Antwort um die Lokalisierung von UI-Steuerelementen erweitern? Ich meine, wie man das Storyboard aktualisiert (ohne es zu schließen), nachdem ich [NSBundle setLanguage: @ "??"] aufgerufen habe.
Jawad Al Shaikh
2
Was XIB / Storyboard betrifft: Sie müssen sicherstellen, dass Sie diese Methode aufrufen, bevor Sie die Ansicht laden. Soweit das Aktualisieren von Ansichten live: In meinem Fall habe ich den Ansichts-Controller einfach neu geladen (ich habe ihn in einen Navigations-Controller eingefügt und nur den Controller ersetzt. Beim erneuten Laden wurden die neu übersetzten Zeichenfolgen angezeigt). Ich arbeite an einem Cocoapod dafür, ich werde dort wahrscheinlich auch ein Beispiel einfügen. Bleiben Sie dran ...
Gilad Novik
137

Normalerweise mache ich das so, aber Sie MÜSSEN alle Lokalisierungsdateien in Ihrem Projekt haben.

@implementation Language

static NSBundle *bundle = nil;

+(void)initialize 
{
    NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
    NSArray* languages = [defs objectForKey:@"AppleLanguages"];
    NSString *current = [[languages objectAtIndex:0] retain];
    [self setLanguage:current];
}

/*
  example calls:
    [Language setLanguage:@"it"];
    [Language setLanguage:@"de"];
*/
+(void)setLanguage:(NSString *)l
{
    NSLog(@"preferredLang: %@", l);
    NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];
    bundle = [[NSBundle bundleWithPath:path] retain];
}

+(NSString *)get:(NSString *)key alter:(NSString *)alternate 
{
    return [bundle localizedStringForKey:key value:alternate table:nil];
}

@end
Mauro Delrio
quelle
1
+1. Dies ist ein wirklich schöner Trick, den ich sonst nirgends gesehen habe. Indem Sie ein "Sub-Bundle" aus einem der Lokalisierungsordner erstellen, können Sie dafür sorgen, dass das String-Material einwandfrei funktioniert, solange Sie NSLocalizedString mit etwas umschließen, das hier Umwege macht.
Ben Zotto
4
Ausgezeichneter Mauro. Mir ist aufgefallen, dass es auch für Dateien aus Ihrem Projekt funktionieren kann. Wenn Sie aus irgendeinem Grund (wie in meinem Fall) Zeichenfolgendateien aus dem Netzwerk herunterladen und in Ihrem Verzeichnis 'Dokumente' speichern müssen (mit der Ordnerstruktur Documents / en.lproj / Localizable.strings, Documents / fr.lproj / Localizable.strings, ...). Sie können auch ein NSBundle erstellen. Verwenden Sie einfach diesen Code für den Pfad: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
arnaud del.
1
Mir scheint, dass dies der beste Ansatz ist, zusammen mit dem Beitrag von Alessandro unten. Es ist nicht so aufdringlich und sollte keinen Neustart erfordern.
PKCLsoft
1
Was mache ich falsch? Ich habe eine Sprachklasse erstellt, NSObject geerbt, diese in die Implementierung eingefügt, die Methodennamen in .h eingefügt und dann [Language setLanguage: @ "es"] eingefügt. In meiner Schaltfläche "Sprache ändern" wird Code ausgeführt (die Schaltflächenmethode wird aufgerufen und wechselt zur Sprachklasse), führt jedoch keine Aktionen aus. Ich habe auch meine localizable.strings (Spanisch) eingerichtet. Aber es scheint für viele andere Leute zu funktionieren. Bitte jemand dumm es für mich.
Sir RupertIII
1
@KKendall du nennst es so: [Language setLanguage: @ "es"]; NSString * stringToUse = [Sprache abrufen: @ "Ihr Text" alter: nil];
Mikrasya
41

Nicht unter iOS 9 verwenden. Dies gibt null für alle durchlaufenden Zeichenfolgen zurück.

Ich habe eine andere Lösung gefunden, mit der Sie die Sprachzeichenfolgen aktualisieren können, ohne die App neu zu starten und mit Genzeichenfolgen kompatibel:

Fügen Sie dieses Makro in die Datei Prefix.pch ein:

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]

und wo immer Sie eine lokalisierte Zeichenfolge benötigen:

NSLocalizedStringFromTableInBundle(@"GalleryTitleKey", nil, currentLanguageBundle, @"")

So stellen Sie die Sprache ein:

[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];

Funktioniert auch mit aufeinanderfolgenden Sprachsprüngen wie:

NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"fr"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"it"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:@"de"] forKey:@"AppleLanguages"];
NSLog(@"test %@", NSLocalizedStringFromTableInBundle(@"NewKey", nil, currentLanguageBundle, @""));
Tudor
quelle
2
@ powerj1984: nein, es werden nur die Zeichenfolgen in Ihren Quelldateien geändert. Wenn Sie die xib-Sprachen ändern möchten, müssen Sie die xibs manuell aus dem Bundle Ihrer ausgewählten Sprache neu laden.
Gklka
@gklka Ich habe die von Tudozier angegebenen Schritte befolgt, aber meine XIBs ändern die Sprache nicht, wie Sie gesagt haben, XIB von Hand neu zu laden. Was bedeutet es eigentlich, XIBs neu zu laden?
Dk Kumar
@ DkKumar: Sie müssen etwas Ähnliches tun: stackoverflow.com/questions/21304988/…
gklka
@gklka Ihre Antwort scheint zu korrigieren und ich habe die gleichen Schritte ausgeführt, die Sie in Ihrer Antwort beschrieben haben. Das Problem ist jedoch, wenn wir den Bundle-Pfad ändern, werden alle Ressourcen (dh die in XIB verwendete PNG-Datei) in der gefunden Das gleiche Bundle, also müssen wir den Bilderordner in allen Bundles wie en.lproj, es.lproj usw. überkopieren. Dies wirkt sich also auf die Erhöhung der IPA-Größe aus. Gibt es also eine Möglichkeit, dieses Problem zu lösen? Wie Sie in Ihrem Beitrag gesagt haben, lassen Sie mich dies versuchen und Sie mehr darüber. Vielen Dank, dass Sie mir dabei geholfen haben
Dk Kumar
1
Dies ist in iOS 9 schrecklich kaputt und gibt die Null für alle Zeichenfolgen zurück.
Alex Zavatone
32

Wie bereits erwähnt, machen Sie einfach:

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:@"el", nil] forKey:@"AppleLanguages"];

Um jedoch zu vermeiden, dass die App neu gestartet werden muss, setzen Sie die Zeile main.mkurz vor UIApplicationMain(...) in die Hauptmethode von .

Frédéric Feytons
quelle
2
Sehr hilfreiche Antwort! PS NSAutoreleasePool * pool ..mag für Nicht-Anfänger offensichtlich klingen, aber Sie sollten diese Zeile einfügen, nachdem oder einige automatisch freigegebene Objekte auslaufen.
Pt2ph8
1
WICHTIG: Dies funktioniert nicht, wenn Sie [[NSBundle mainBundle] URLForResource:withExtension:]vorher anrufen .
Kof
3
Was ist, wenn Sie Swift verwenden, dann gibt es keine main.m?
Turing getestet
@turingtested hast du es mit Swift und Storyboard versucht, funktioniert es?
iDevAmit
Hallo, wie man dynamischen Text lokalisiert, der vom Server kommt, in meiner Anwendung jeden Text, den ich in vereinfachter chinesischer Sprache anzeigen muss, aber wie man den Text in Chinesisch anzeigt, der in Englisch kommt. Hilf mir.
Mahesh Narla
31

Der Trick, eine bestimmte Sprache zu verwenden, indem Sie sie aus der App auswählen, besteht darin, die NSLocalizedStringVerwendung eines bestimmten Bundles abhängig von der ausgewählten Sprache zu erzwingen.

Hier ist der Beitrag, den ich für diese Lernlokalisierung in iOS-Apps geschrieben habe

und hier ist der Code einer Beispiel-App- Vorlokalisierung in iOS-Apps

object2.0
quelle
Es funktioniert auch unter iOS7. Ich muss von Brians Lösung zu dieser wechseln, da iOS die Sprachoption anscheinend wieder überschreibt. Daher habe ich mich an die erstmalige Einstellung der Sprache gehalten. Ihre Lösung funktioniert wie ein Zauber!
Haxpor
14

Was halten Sie von dieser Lösung für Swift 3?

extension String {

    func localized(forLanguage language: String = Locale.preferredLanguages.first!.components(separatedBy: "-").first!) -> String {

        guard let path = Bundle.main.path(forResource: language == "en" ? "Base" : language, ofType: "lproj") else {

            let basePath = Bundle.main.path(forResource: "Base", ofType: "lproj")!

            return Bundle(path: basePath)!.localizedString(forKey: self, value: "", table: nil)
        }

        return Bundle(path: path)!.localizedString(forKey: self, value: "", table: nil)
    }
}

Einfache Verwendung:

"report".localized(forLanguage: "pl") //forced language
"report".localized() //default language selected by user in settings, in case when your app doesnt support selected lanaguage, the default one is selected, here is an english.
Bartłomiej Semańczyk
quelle
Hallo, wie man dynamischen Text lokalisiert, der vom Server kommt, in meiner Anwendung jeden Text, den ich in vereinfachter chinesischer Sprache anzeigen muss, aber wie man den Text in Chinesisch anzeigt, der in Englisch kommt. Hilf mir.
Mahesh Narla
Dies ist eine brillante Antwort;)
Bartłomiej Semańczyk
tolle Antwort. half mir
Dilip Tiwari
13

Wie Brian Webster erwähnt, muss die Sprache "irgendwann zu Beginn des Starts Ihrer Anwendung" eingestellt werden. Ich dachte applicationDidFinishLaunching:an dieAppDelegate sollte ein geeigneter Ort dafür sein, da ich dort alle anderen Initialisierungen mache.

Aber wie William Denniss erwähnt, scheint dies erst nach dem Neustart der App Auswirkungen zu haben , was irgendwie nutzlos ist.

Es scheint gut zu funktionieren, wenn ich den Code in die Hauptfunktion setze:

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Force language to Swedish.
    [[NSUserDefaults standardUserDefaults]
     setObject:[NSArray arrayWithObject:@"sv"]
     forKey:@"AppleLanguages"];

    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

Ich würde mich über Kommentare dazu freuen.

Geon
quelle
In meiner Implementierung ist es eine vom Benutzer einstellbare Sache - also öffne ich einfach einen Dialog, der ihnen sagt, dass sie neu starten müssen :)
William Denniss
Es funktioniert fast für alle - mit Ausnahme des lokalisierten Default.png-Images.
Lukasz
2
Wenn Sie Benutzer eine Sprache festlegen lassen und sie dann auffordern, die App neu zu starten, denken Sie daran, dass die NSUserDefaults möglicherweise nicht gespeichert werden, wenn sie zu schnell neu starten. Die meisten Benutzer sind nicht so schnell, aber wenn Sie die App testen, sind Sie möglicherweise zu schnell und sehen infolgedessen ein inkonsistentes Verhalten. Ich brauchte ein paar Stunden, um zu erkennen, warum mein Sprachwechsel manchmal funktionierte und manchmal nicht!
Arlomedia
3
Das ist kein Problem, verwenden Sie es einfach [[NSUserDefaults standardUserDefaults] synchronize];nach einem AnrufsetObject:forKey:
Ben Baron
11

Mir gefällt die Methode von Mauro Delrio am besten. Ich habe auch Folgendes in meine Project_Prefix.pch eingefügt

#import "Language.h"    
#define MyLocalizedString(key, alt) [Language get:key alter:alt]

Wenn Sie also jemals die Standardmethode (die NSLocalizedString verwendet) verwenden möchten, können Sie in allen Dateien eine schnelle Syntaxersetzung vornehmen.

dnaxxx
quelle
1
Ich mag auch seine Methode und füge eine Methode zum Laden von PNG-Bildern hinzu: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [Bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage-Zuweisung] initWithContentsOfFile: fullPath]; return [imageObj autorelease]; }
Jagie
11

NSLocalizedString()liest den Wert für den Schlüssel AppleLanguagesaus den Standardbenutzerstandards ( [NSUserDefaults standardUserDefaults]). Mit diesem Wert wird zur Laufzeit eine geeignete Lokalisierung unter allen vorhandenen Lokalisierungen ausgewählt. Wenn Apple beim Start der App das Standardwörterbuch für Benutzer erstellt, sucht es in den Systemeinstellungen nach dem Schlüssel für die bevorzugte Sprache und kopiert den Wert von dort. Dies erklärt beispielsweise auch, warum das Ändern der Spracheinstellungen in OS X keine Auswirkungen auf die Ausführung von Apps hat, sondern nur auf Apps, die danach gestartet werden. Nach dem Kopieren wird der Wert nicht aktualisiert, nur weil sich die Einstellungen ändern. Aus diesem Grund startet iOS alle Apps neu, wenn Sie die Sprache ändern.

Allerdings werden alle Werte des Wörterbuchs Benutzereinstellungen können durch Befehlszeilenargumente werden überschrieben. Siehe NSUserDefaultsDokumentation zum NSArgumentDomain. Dies schließt sogar die Werte ein, die aus der App-Einstellungsdatei (.plist) geladen werden. Dies ist sehr gut zu wissen, wenn Sie einen Wert nur einmal zum Testen ändern möchten .

Wenn Sie also die Sprache nur zum Testen ändern möchten, möchten Sie Ihren Code wahrscheinlich nicht ändern (wenn Sie vergessen, diesen Code später zu entfernen ...), sondern Xcode anweisen, Ihre App mit Befehlszeilenparametern zu starten ( zB spanische Lokalisierung verwenden):

Geben Sie hier die Bildbeschreibung ein

Sie müssen Ihren Code überhaupt nicht berühren. Erstellen Sie einfach verschiedene Schemata für verschiedene Sprachen und Sie können die App schnell einmal in einer Sprache und einmal in einer anderen Sprache starten, indem Sie einfach das Schema wechseln.

Mecki
quelle
Es funktioniert nur zum ersten Mal, danach wird es wieder in die Gerätepräferenz geladen. Sprache ..
Aks
@Aks Funktioniert jedes Mal für mich. Stellen Sie sicher, dass Sie das richtige Schema starten, dass Sie von Xcode aus starten (Relaunches, Gabeln usw. erhalten das Argument nicht) und dass Sie die Sprache nicht überschreiben, Optionswie dies bei neueren Xcode-Versionen von Apple der Fall ist auch.
Mecki
Vielen Dank für Ihre Antwort @Mecki .. Für mich funktioniert es nicht in xcode, aber wenn ich im Code erwähne, funktioniert es .. Aber trotzdem, wenn ich mein Gerät mit einigen anderen bevorzugten Sprachen eingestellt habe, muss es die App neu starten, um in meine bevorzugte Sprache zu laden Sprache, die ich in Argumenten übergeben ....
Aks
@Aks Wenn Sie dies auf einem Gerät verwenden, funktioniert es nur, wenn Sie direkt von Xcode aus starten. Es funktioniert nicht, wenn Sie die App erneut starten, indem Sie auf das entsprechende Symbol auf einem Gerät tippen, und es funktioniert auch nicht, wenn die App beendet wird (z weil es in den Hintergrund ging) und vom System neu gestartet wird (da dieser Neustart kein Neustart von Xcode ist) - daher funktioniert das Wechseln zu einer anderen App und zurück möglicherweise nicht, wenn iOS Ihre App zwischendurch beenden muss (z. B. weil es braucht den Speicher).
Mecki
10

Ich habe eine Lösung gefunden, mit der Sie sie verwenden können NSLocalizedString. Ich erstelle eine NSBundleAnrufkategorie NSBundle+RunTimeLanguage. Die Schnittstelle ist so.

// NSBundle+RunTimeLanguage.h
#import <Foundation/Foundation.h>
@interface NSBundle (RunTimeLanguage)
#define NSLocalizedString(key, comment) [[NSBundle mainBundle] runTimeLocalizedStringForKey:(key) value:@"" table:nil]
- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName;
@end

Die Implementierung ist so.

// NSBundle+RunTimeLanguage.m
#import "NSBundle+RunTimeLanguage.h"
#import "AppDelegate.h"

@implementation NSBundle (RunTimeLanguage)

- (NSString *)runTimeLocalizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    NSString *path= [[NSBundle mainBundle] pathForResource:[appDelegate languageCode] ofType:@"lproj"];
    NSBundle *languageBundle = [NSBundle bundleWithPath:path];
    NSString *localizedString=[languageBundle localizedStringForKey:key value:key table:nil];
    return localizedString;
}
@end

Fügen Sie dann einfach den Import NSBundle+RunTimeLanguage.hin die verwendeten Dateien hinzu NSLocalizedString.

Wie Sie sehen können, speichere ich meinen languageCode in einer Eigenschaft von AppDelegate. Dies kann überall gespeichert werden, wo Sie möchten.

Das einzige, was ich daran nicht mag, ist eine Warnung, die NSLocalizedStringMarco neu definiert hat. Vielleicht könnte mir jemand helfen, diesen Teil zu reparieren.

qman64
quelle
1
Fügen Sie #undef NSLocalizedStringkurz vor hinzu #define, um die Warnung zu deaktivieren
Beryllium
Funktioniert dies bei der Lokalisierung für XIB-Dateien? Ich habe .xib-Datei und .strings, um den Namen der Benutzeroberfläche in eine bestimmte Sprache zu übersetzen. Ich habe es versucht und es funktioniert nicht mit XIB, aber ich bin nicht sicher, ob ich die Dinge richtig gemacht habe
Kong Hantrakool
Kong Entschuldigung für die verspätete Antwort. Dies funktioniert überall dort, wo Sie NSLocalizedString verwenden. Es wird also nicht direkt in einem XIB funktionieren. Sie würden IBOutlets für die Zeichenfolgen in der XIB benötigen und müssten dann die Zeichenfolgenwerte programmgesteuert im Code festlegen.
Qman64
Dieser Ansatz hat bei mir funktioniert und ich konnte die Sprachen im laufenden Betrieb ändern. Beachten Sie, dass Sprachcodes wie "es", fr "und" en "einwandfrei funktionierten." Zh "(für vereinfachtes Chinesisch) schlug jedoch fehl und gab mir Nullzeichenfolgen zurück. Ich habe auf meinem System eine globale Suche nach" es.lproj "durchgeführt Sehen Sie, wo sich die Dateien befanden, auf die ich zugegriffen habe, und entdeckte dort, dass "zh.lproj" tatsächlich "zh-Hans.lproj" ist.
Gallymon
9

Schnelle Version:

NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()
daaniaal
quelle
8

In einer Nussschale :

Lokalisieren Sie Ihre Anwendung

Als erstes müssen Sie Ihre App in mindestens zwei Sprachen lokalisieren (in diesem Beispiel Englisch und Französisch).

NSLocalizedString überschreiben

Verwenden Sie in Ihrem Code anstelle der NSLocalizedString(key, comment)Verwendung ein Makro, MYLocalizedString(key, comment)das wie folgt definiert ist: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Dieser MYLocalizationSystemSingleton wird:

  • Stellen Sie die Sprache ein, indem Sie den richtigen lokalisierten NSBundle- Benutzer festlegen
  • Gibt den lokalisierten NSString gemäß dieser zuvor festgelegten Sprache zurück

Benutzersprache einstellen

Wenn der Benutzer die Anwendungssprache auf Französisch geändert hat, rufen Sie an [[MYLocalizationSystem sharedInstance] setLanguage:@"fr"];

- (void)setLanguage:(NSString *)lang
{
    NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj"];
    if (!path)
    {
        _bundle = [NSBundle mainBundle];
        NSLog(@"Warning: No lproj for %@, system default set instead !", lang);
        return;
    }

    _bundle = [NSBundle bundleWithPath:path];
}

In diesem Beispiel hat diese Methode das lokalisierte Bundle auf fr.lproj gesetzt

Lokalisierte Zeichenfolge zurückgeben

Sobald Sie das lokalisierte Bundle festgelegt haben, können Sie mit dieser Methode die richtige lokalisierte Zeichenfolge von ihm erhalten:

- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value
{
    // bundle was initialized with [NSBundle mainBundle] as default and modified in setLanguage method
    return [self.bundle localizedStringForKey:key value:value table:nil];
}

Hoffe das wird dir helfen.

Weitere Details finden Sie in diesem Artikel von NSWinery.io

user3465357
quelle
Wir bitten Sie, in Ihrer Antwort nicht nur auf eine Lösung zu verweisen. Der Link funktioniert möglicherweise eines Tages nicht mehr. Obwohl Sie den Link nicht aus Ihrer Antwort entfernen müssen, bitten wir Sie, Ihre Antwort so zu bearbeiten, dass sie eine Zusammenfassung des relevanten Abschnitts des verlinkten Artikels enthält.
Oblivious Sage
7

Swift 3-Erweiterungen:

extension Locale {
    static var preferredLanguage: String {
        get {
            return self.preferredLanguages.first ?? "en"
        }
        set {
            UserDefaults.standard.set([newValue], forKey: "AppleLanguages")
            UserDefaults.standard.synchronize()
        }
    }
}

extension String {
    var localized: String {

    var result: String

    let languageCode = Locale.preferredLanguage //en-US

    var path = Bundle.main.path(forResource: languageCode, ofType: "lproj")

    if path == nil, let hyphenRange = languageCode.range(of: "-") {
        let languageCodeShort = languageCode.substring(to: hyphenRange.lowerBound) // en
        path = Bundle.main.path(forResource: languageCodeShort, ofType: "lproj")
    }

    if let path = path, let locBundle = Bundle(path: path) {
        result = locBundle.localizedString(forKey: self, value: nil, table: nil)
    } else {
        result = NSLocalizedString(self, comment: "")
    }
        return result
    }
}

Verwendungszweck:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized
ChikabuZ
quelle
Vielen Dank. Gut arbeiten.
Krunal Nagvadia
5

In der zu definierenden Datei .pch :

#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:@"lproj"]]


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")
MrPiliffo
quelle
Wie entferne ich die Warnung 'ovverride ...'?
Idan Moshe
1
Einfache Lösung macht alles was ich brauche. Konnte dies als Leitfaden zur Lösung meines Problems verwenden. Wenn pathForResource nil zurückgibt, weil ich keine Lokalisierungsdatei habe, verwende ich pathForResource: @ "Base", da ich sonst nichts versucht habe, Zeichenfolgen aus der Basislokalisierung zu ziehen. Danke dir.
GRW
Dies ist hacky (ebenso wie alle anderen Antworten) und es war am einfachsten, es in dem Unit-Test zu implementieren, den ich geschrieben habe.
Max
4

Vielleicht sollten Sie dies ergänzen (in der .pch-Datei nach #import):

extern NSBundle* bundle; // Declared on Language.m

#ifdef NSLocalizedString
    #undef NSLocalizedString
    // Delete this line to avoid warning
    #warning "Undefining NSLocalizedString"
#endif

#define NSLocalizedString(key, comment) \
    [bundle localizedStringForKey:(key) value:@"" table:nil]
D33pN16h7
quelle
/Users/pwang/Desktop/Myshinesvn/Myshine/viewController/OrientationReadyPagesView.m:193:31: Verwendung der nicht deklarierten Kennung 'Bundle'
Pengwang
4

Swift 3-Lösung:

let languages = ["bs", "zh-Hant", "en", "fi", "ko", "lv", "ms", "pl", "pt-BR", "ru", "sr-Latn", "sk", "es", "tr"]
UserDefaults.standard.set([languages[0]], forKey: "AppleLanguages")

Gab einige Beispiele für Sprachcodes, die verwendet werden können. Hoffe das hilft

Jeremy Bader
quelle
3

Sie können ein Unterpaket mit den lokalisierten Zeichenfolgen erstellen, mit denen Sie dies tun möchten, und diese dann NSLocalizedStringFromTableInBundle()zum Laden verwenden. (Ich gehe davon aus, dass dies ein Inhalt ist, der von der normalen UI-Lokalisierung, die Sie möglicherweise in der App ausführen, getrennt ist.)

Sixten Otto
quelle
3

Für meinen Fall habe ich zwei lokalisierte Dateien, ja und en

und ich möchte es zu en zwingen, wenn die bevorzugte Sprache im System weder en noch ja ist

Ich werde die Datei main.m bearbeiten

Ich werde prüfen, ob die erste bevorzugte Sprache en oder ja ist. Wenn nicht, werde ich die zweite bevorzugte Sprache in en ändern.

int main(int argc, char *argv[])
{

    [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"AppleLanguages"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    NSString *lang = [[NSLocale preferredLanguages] objectAtIndex:0];

    if (![lang isEqualToString:@"en"]  &&  ![lang isEqualToString:@"ja"]){

        NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[NSLocale preferredLanguages]];
        [array replaceObjectAtIndex:1 withObject:@"en"];

        [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"AppleLanguages"];
        [[NSUserDefaults standardUserDefaults] synchronize];


    }

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));


    }


}
chings228
quelle
Danke @ chings228, es ist nützlich für mich, aber dies ändert nichts am Standard-Startbild (Splash) für die App. Ich habe für jede Sprache unterschiedliche Splashs. Wissen Sie, wie Sie sich dafür bewerben können?
JERC
Ich denke, der Splash wird ausgeführt, bevor das Hauptprogramm gestartet wird, daher weiß ich nicht, wie ich ihn überschreiben soll. Vielleicht kann Plist helfen, aber es funktioniert möglicherweise beim nächsten
Öffnen
2

Sie können so etwas tun:

NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"Localizable" ofType:@"strings" inDirectory:nil forLocalization:@"es"];


NSBundle *spanishBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];

NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):
JERC
quelle
2

Basierend auf der Antwort von Tudorizer , die Sprache zu ändern, ohne die Anwendung zu verlassen oder neu zu starten .

Verwenden Sie anstelle eines Makros eine Klasse für den Zugriff auf die bevorzugte Sprache, um zu überprüfen, ob ein bestimmter Sprachcode vorhanden ist.

Im Folgenden finden Sie eine Klasse, mit der Sie das aktuelle Sprachpaket erhalten, das für iOS 9 funktioniert:

@implementation OSLocalization

+ (NSBundle *)currentLanguageBundle
{
    // Default language incase an unsupported language is found
    NSString *language = @"en";

    if ([NSLocale preferredLanguages].count) {
        // Check first object to be of type "en","es" etc
        // Codes seen by my eyes: "en-US","en","es-US","es" etc

        NSString *letterCode = [[NSLocale preferredLanguages] objectAtIndex:0];

        if ([letterCode rangeOfString:@"en"].location != NSNotFound) {
            // English
            language = @"en";
        } else if ([letterCode rangeOfString:@"es"].location != NSNotFound) {
            // Spanish
            language = @"es";
        } else if ([letterCode rangeOfString:@"fr"].location != NSNotFound) {
            // French
            language = @"fr";
        } // Add more if needed
    }

    return [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:@"lproj"]];
}

/// Check if preferred language is English
+ (BOOL)isCurrentLanguageEnglish
{
    if (![NSLocale preferredLanguages].count) {
        // Just incase check for no items in array
        return YES;
    }

    if ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:@"en"].location == NSNotFound) {
        // No letter code for english found
        return NO;
    } else {
        // Tis English
        return YES;
    }
}

/*  Swap language between English & Spanish
 *  Could send a string argument to directly pass the new language
 */
+ (void)changeCurrentLanguage
{
    if ([self isCurrentLanguageEnglish]) {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
    } else {
        [[NSUserDefaults standardUserDefaults] setObject:@[@"en"] forKey:@"AppleLanguages"];
    }
}
@end

Verwenden Sie die obige Klasse, um auf eine Zeichenfolgendatei / image / video / etc zu verweisen:

// Access a localized image
[[OSLocalization currentLanguageBundle] pathForResource:@"my_image_name.png" ofType:nil]
// Access  a localized string from Localizable.strings file
NSLocalizedStringFromTableInBundle(@"StringKey", nil, [OSLocalization currentLanguageBundle], @"comment")

Ändern Sie die Sprache inline wie unten oder aktualisieren Sie die Methode "changeCurrentLanguage" in der obigen Klasse, um einen Zeichenfolgenparameter zu verwenden, der auf die neue Sprache verweist.

[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
Antonioni
quelle
2

In Swift 4 habe ich es gelöst, ohne neu starten oder Bibliotheken verwenden zu müssen.

Nachdem ich viele Optionen ausprobiert hatte, fand ich diese Funktion, bei der Sie die zu übersetzende Zeichenfolge stringToLocalize (von Localizable.String, die Zeichenfolgendatei) und die Sprache, in die Sie sie übersetzen möchten, sowie den zurückgegebenen Wert übergeben der String, den Sie in der Strings-Datei haben:

    func localizeString (stringToLocalize: String, language: String) -> String
    {
        let path = Bundle.main.path (forResource: language, ofType: "lproj")
        let languageBundle = Bundle (path: path!)
        return languageBundle! .localizedString (forKey: stringToLocalize, value: "", table: nil)
    }

Unter Berücksichtigung dieser Funktion habe ich diese Funktion in einer Swift-Datei erstellt:

struct CustomLanguage {

    func createBundlePath () -> Bundle {
        let selectedLanguage = //recover the language chosen by the user (in my case, from UserDefaults)
        let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj")
        return Bundle(path: path!)!
    }
}

So greifen Sie von der gesamten App und in jeder Zeichenfolge der übrigen ViewController zu, anstatt Folgendes zu setzen:

NSLocalizedString ("StringToLocalize", comment: ")

Ich habe es durch ersetzt

let customLang = CustomLanguage() //declare at top
let bundleLanguage = customLang.createBundle()

NSLocalizedString("StringToLocalize", tableName: nil, bundle: bundleLanguage, value: "", comment: “”) //use in each String

Ich weiß nicht, ob es der beste Weg ist, aber ich fand es sehr einfach und es funktioniert für mich, ich hoffe es hilft dir!

ainareta685
quelle
1

Diese Funktion versucht, eine lokalisierte Zeichenfolge für die aktuelle Sprache abzurufen. Wenn sie nicht gefunden wird, wird sie in englischer Sprache abgerufen.

- (NSString*)L:(NSString*)key
{
    static NSString* valueNotFound = @"VALUE_NOT_FOUND";
    static NSBundle* enBundle = nil;

    NSString* pl = [NSLocale preferredLanguages][0];
    NSString* bp = [[NSBundle mainBundle] pathForResource:pl ofType:@"lproj"];
    NSBundle* b = [NSBundle bundleWithPath:bp];

    NSString* s = [b localizedStringForKey:key value:valueNotFound table:nil];
    if ( [s isEqualToString:valueNotFound] ) {
        if ( !enBundle ) {
            bp = [[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"];
            enBundle = [NSBundle bundleWithPath:bp];
        }
        s = [enBundle localizedStringForKey:key value:key table:nil];
    }

    return s;
}
Cherpak Evgeny
quelle
1

Ich wollte Unterstützung für eine Sprache hinzufügen, die von iOS nicht offiziell unterstützt wird (nicht im Abschnitt Sprache unter Systemeinstellungen aufgeführt). Indem ich dem Internationalization Tutorial von Apple und einigen Hinweisen von Brian Webster und geon gefolgt bin, habe ich mir diesen Code ausgedacht (in main.m):

int main(int argc, char * argv[]) {
    @autoreleasepool {
        // Grab regional settings locale, for Slovenian this is either sl_SI or en_SI
        NSLocale *locale = [NSLocale currentLocale];
        NSString *ll = [locale localeIdentifier]; // sl_SI

        // Grab the first part of language identifier
        NSArray *comp = [ll componentsSeparatedByString:@"_"];
        NSString *ll1 = @"en";
        if (comp.count > 0) {
            ll1 = comp[0]; // sl, en, ...
        }
        // Check if we already saved language (user can manually change it inside app for example)
        if (![[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"]) {
            //   Slovenian (Slovenia),            Slovenia
            if ([ll isEqualToString:@"sl_SI"] || [ll isEqualToString:@"en_SI"]) {
                ll1 = @"sl-SI"; // This is the part of localized path for Slovenian language that Xcode generates
            }
            // Add more unsupported languages here...

            [[NSUserDefaults standardUserDefaults] setObject:ll1 forKey:@"SelectedLanguage"]; // Save language
        }
        else {
            // Restore language as we have previously saved it
            ll1 = [[NSUserDefaults standardUserDefaults] objectForKey:@"SelectedLanguage"];
        }
        // Overwrite NSLocalizedString and StoryBoard language preference
        [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:ll1, @"en", @"fr", nil] forKey:@"AppleLanguages"];
        // Make sure settings are stored to disk
        [[NSUserDefaults standardUserDefaults] synchronize];

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

Dies funktioniert sowohl für Storyboard- als auch für NSLocalizedString-Code. Der Code geht davon aus, dass der Benutzer die Möglichkeit hat, die Sprache in der App später manuell zu ändern.

Vergessen Sie natürlich nicht, die richtigen Storyboard-Übersetzungen und Localizable.strings-Übersetzungen hinzuzufügen (siehe dazu den Link zur Apple-Seite oben).

frin
quelle
Ich sehe, dass Sie auch Probleme mit der slowenischen Sprache haben :) Um diese Antwort für Sprachen zu verbessern, die von iOS nicht unterstützt werden, haben Sie herausgefunden, ob wir benutzerdefinierte lokalisierbare Zeichenfolgen festlegen können, die für Systemsteuerungen in der App verwendet werden (Bearbeiten, Fertig, Mehr, ...)?
Miham
1

Hier ist eine anständige Lösung für dieses Problem, und es ist kein Neustart der Anwendung erforderlich.

https://github.com/cmaftuleac/BundleLocalization

Diese Implementierung funktioniert durch Optimieren in NSBundle. Die Idee ist, dass Sie die Methode localizedStringForKey für die Instanz des NSBundle-Objekts überschreiben und diese Methode dann für ein anderes Bundle mit einer anderen Sprache aufrufen. Einfach und elegant, voll kompatibel mit allen Arten von Ressourcen.

Corneliu Maftuleac
quelle
Dieser Code aus dem Projekt hat mir sehr geholfen. So finden Sie ein Bundle für einen bestimmten Sprachcode ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
Eonil
Diese Klasse funktioniert gut, aber nachdem Sie die Sprache in eine andere geändert haben, hat dies keine Auswirkungen.
Anilkumar iOS ReactNative
0

Was auch immer Sie alle tun, der beste Weg ist, den Kurznamen für die angegebene Sprache zu verwenden, dh: fr, en, nl, de, it usw., und diesen einem globalen Wert zuzuweisen.

Erstellen Sie eine Auswahlansicht, die wie ein Dropdown-Menü angezeigt wird (Kombination einer Schaltfläche, auf deren Klicken eine Auswahlansicht von unten mit einer Liste von Sprachen angezeigt wird), und wählen Sie die gewünschte Sprache aus. Lassen Sie den Kurznamen intern speichern. Erstellen Sie eine .h + .m-Datei mit dem Namen LocalisedString.

Stellen Sie den globalen Wert von short_name so ein, dass er dem erhaltenen Wert in LocalisedString.m entspricht. Wenn die gewünschte Sprache ausgewählt ist, weisen Sie NSBundlePath zu, um Projektunterverzeichnisse für die benötigte Sprache zu erstellen. für zB nl.proj, en.proj.

Wenn der jeweilige Projektordner ausgewählt ist, rufen Sie die lokalisierte Zeichenfolge für die jeweilige Sprache auf und ändern Sie die Sprache dynamisch.

Keine Regeln gebrochen.

Abhijeet
quelle
0

Für Swift können Sie die main.swiftDatei überschreiben und dort die UserDefaults-Zeichenfolge festlegen, bevor die App ausgeführt wird. Auf diese Weise müssen Sie die App nicht neu starten, um den gewünschten Effekt zu erzielen.

import Foundation
import UIKit

// Your initialisation code here
let langCultureCode: String = "LANGUAGE_CODE"

UserDefaults.standard.set([langCultureCode], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))

zusammen mit dem Entfernen von @UIApplicationMainin Ihrer AppDelegate.swiftDatei gepaart .

Andreas
quelle