Come forzare NSLocalizedString a utilizzare una lingua specifica


Risposte:


263

NSLocalizedString()(e relative varianti) accedono alla chiave "AppleLanguages" NSUserDefaultsper determinare quali sono le impostazioni dell'utente per le lingue preferite. Ciò restituisce un array di codici lingua, il primo è quello impostato dall'utente per il telefono e i successivi utilizzati come fallback se una risorsa non è disponibile nella lingua preferita. (sul desktop, l'utente può specificare più lingue con un ordine personalizzato nelle Preferenze di Sistema)

È possibile sovrascrivere l'impostazione globale per la propria applicazione, se lo si desidera, utilizzando il metodo setObject: forKey: per impostare il proprio elenco di lingue. Ciò avrà la precedenza sul valore impostato a livello globale e verrà restituito a qualsiasi codice nell'applicazione che sta eseguendo la localizzazione. Il codice per questo sarebbe simile a:

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

Ciò renderebbe il tedesco la lingua preferita per la tua applicazione, con inglese e francese come riserva. Dovresti chiamarlo prima o poi all'avvio della tua applicazione. Puoi leggere ulteriori informazioni sulle preferenze di lingua / locale qui: Internazionalizzazione Argomenti di programmazione: ottenere la lingua e le impostazioni internazionali correnti


4
Questo non funziona per me, utilizzerà la lingua predefinita indipendentemente da cosa.
quano

50
Denniss; Sembra funzionare meglio se imposti la preferenza della lingua prima dell'avvio dell'app. Lo faccio nella funzione main (), prima che UIApplicationMain () venga chiamato. Una volta che è effettivamente in esecuzione, non cambierà la lingua utilizzata, ma imposterà semplicemente la preferenza per la volta successiva.
geon

3
Devi impostare la lingua prima di inizializzare UIKit e devi specificare una lingua completa + locale regionale - controlla questo per un esempio completo blog.federicomestrone.com/2010/09/15/…
fedmest

18
l'impostazione di AppleLanguages ​​cambierà una lingua in fase di esecuzione se eseguita in main () - true! ma ... la PRIMA VOLTA che l'app viene installata, i nsuserdefaults vengono inizializzati DOPO aver chiamato UIApplicationMain (), che ignorerà gli AppleLanguages ​​impostati in precedenza e richiederà un brutto riavvio forzato dell'app per vedere la lingua desiderata.
JohnPayne

3
Questo non funziona con elementi plurali definiti in Localizable.stringsdict. Qualche idea se c'è una possibile soluzione?
Mihai Fratu

147

Ho avuto lo stesso problema di recente e non volevo avviare e patchare il mio intero NSLocalizedString né forzare il riavvio dell'app affinché la nuova lingua funzionasse. Volevo che tutto funzionasse così com'è.

La mia soluzione era cambiare dinamicamente la classe del bundle principale e caricare lì il bundle appropriato:

File di intestazione

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

Implementazione

#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

Quindi, in pratica, quando la tua app si avvia e prima di caricare il tuo primo controller, chiama semplicemente:

[NSBundle setLanguage:@"en"];

Quando il tuo utente cambia la sua lingua preferita nella schermata delle impostazioni, richiamala semplicemente di nuovo:

[NSBundle setLanguage:@"fr"];

Per ripristinare le impostazioni predefinite del sistema, passare semplicemente a zero:

[NSBundle setLanguage:nil];

Godere...

Per chi necessita di una versione Swift:

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)
    }
}

7
Ciao @Wirsing, finora per me funziona benissimo. Ho persino caricato un'app nello store e nessuna lamentela da parte di Apple.
Gilad Novik

7
Questa è un'ottima soluzione, ho anche fatto riferimento a questo qui: medium.com/ios-apprentice/905e4052b9de
James Tang

1
Ciao @JamesTang, grazie per il riferimento e il tuo post - ho trovato molte informazioni utili sulla localizzazione.
Gilad Novik

3
@Gilad il tuo metodo funziona perfettamente per le stringhe dinamiche (stringhe che ho definito in localizable.strings) ma per quanto riguarda i pulsanti e le etichette dello storyboard funziona solo con il metodo principale. quindi puoi estendere la tua risposta per includere la localizzazione dei controlli dell'interfaccia utente? intendo come aggiornare (senza chiudere) lo storyboard dopo aver richiamato [NSBundle setLanguage: @ "??"] ;?
Jawad Al Shaikh

2
Per quanto riguarda XIB / storyboard: devi assicurarti di chiamare questo metodo PRIMA di caricare la vista. Per quanto riguarda l'aggiornamento delle viste in tempo reale: nel mio caso ho semplicemente ricaricato il controller della vista (l'ho inserito in un controller di navigazione e ho appena sostituito il controller. Dopo averlo ricaricato, ha ottenuto le nuove stringhe tradotte). Sto lavorando a un cocoapod per questo, probabilmente includerò anche un esempio lì. Restate sintonizzati ...
Gilad Novik

137

Di solito lo faccio in questo modo, ma DEVI avere tutti i file di localizzazione nel tuo progetto.

@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

1
+1. Questo è davvero un bel trucco che non ho visto da nessun'altra parte. Creando un "sub bundle" da una delle cartelle di localizzazione, puoi far funzionare bene le stringhe purché racchiudi NSLocalizedString con qualcosa che devia qui.
Ben Zotto

4
Eccellente Mauro. Ho notato che può funzionare anche per file fuori dal tuo progetto. Se per qualche motivo (come nel mio caso), è necessario scaricare file di stringhe dalla rete e memorizzarli nella directory 'Documenti' (con la struttura delle cartelle Documents / en.lproj / Localizable.strings, Documents / fr.lproj / Localizable.strings, ...). Puoi anche creare un NSBundle. Usa questo codice per il percorso: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
arnaud del.

1
Mi sembra che questo sia l'approccio migliore, insieme al contributo di Alessandro di seguito. Non è così invadente e non dovrebbe richiedere un riavvio.
PKCLsoft

1
Che cosa sto facendo di sbagliato? Ho creato una classe Language, ereditando NSObject, l'ho inserita nell'implementazione, ho messo i nomi dei metodi in .h, quindi ho messo [Language setLanguage: @ "es"]; nel mio pulsante Cambia lingua, il codice viene eseguito (il metodo del pulsante viene chiamato e va alla classe della lingua), ma non fa nulla. Ho anche il mio localizable.strings (spagnolo) impostato. Ma sembra funzionare per molte altre persone. Per favore, qualcuno lo stupisca per me.
SirRupertIII

1
@KKendall lo chiami così: [Language setLanguage: @ "es"]; NSString * stringToUse = [Language get: @ "Your Text" alter: nil];
Mikrasya,

41

Non utilizzare su iOS 9. Questo restituisce zero per tutte le stringhe passate attraverso di esso.

Ho trovato un'altra soluzione che ti consente di aggiornare le stringhe della lingua, senza riavviare l'app e compatibile con le stringhe gens:

Metti questa macro nel Prefix.pch:

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

e dove mai hai bisogno di una stringa localizzata usa:

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

Per impostare la lingua utilizzare:

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

Funziona anche con salti di lingua consecutivi come:

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, @""));

2
@ powerj1984: no, cambierà solo le stringhe nei tuoi file sorgente. Se vuoi cambiare le lingue xib, devi ricaricare le xibs a mano, dal bundle della lingua selezionata.
gklka

@gklka Ho seguito i passaggi forniti da Tudozier ma i miei XIB non cambiano la lingua, come hai detto a ricaricare XIB a mano, cosa significa ricaricare XIB?
Dk Kumar

@DkKumar: Devi fare qualcosa di simile a questo: stackoverflow.com/questions/21304988/...
gklka

@gklka La tua risposta sembra corretta e ho fatto gli stessi passaggi che hai descritto nella tua risposta, ma il problema è che se cambiamo il percorso del bundle troverà tutte le risorse (cioè il file .png usato in XIB) nel stesso bundle, quindi dobbiamo copiare la cartella delle immagini in tutto il bundle come en.lproj, es.lproj e così via .. quindi avrà effetto sull'aumento delle dimensioni dell'IPA, quindi c'è un modo per superare questo problema? come hai detto nel tuo post, fammi provare questo e ti permetto di più su questo Grazie per avermi aiutato per lo stesso
Dk Kumar

1
Questo è orribilmente rotto in iOS 9, restituendo il null per tutte le stringhe.
Alex Zavatone

32

Come detto prima, basta fare:

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

Ma per evitare di dover riavviare l'app, metti la riga nel metodo principale di main.m, appena prima UIApplicationMain(...).


3
Risposta molto utile! PS Può sembrare ovvio per i non principianti, ma dovresti inserire quella riga dopo NSAutoreleasePool * pool ..o alcuni oggetti rilasciati automaticamente potrebbero fuoriuscire.
pt2ph8

1
IMPORTANTE: non funzionerà se chiami [[NSBundle mainBundle] URLForResource:withExtension:]prima.
Kof

3
E se si utilizza Swift, quindi non c'è main.m?
turingtested il

@turingtested hai provato su swift e storyboard, funziona?
iDevAmit

Ciao, Come localizzare il testo dinamico che proviene dal server, nella mia applicazione ogni testo che devo visualizzare in lingua cinese semplificata, Ma come visualizzare il testo in cinese che arriva in inglese. aiutami.
Mahesh Narla

32

Il trucco per utilizzare una lingua specifica selezionandola dall'app è forzare l' NSLocalizedStringuso di un pacchetto specifico a seconda della lingua selezionata,

ecco il post che ho scritto per questo localizzazione avanzata di apprendimento nelle app ios

ed ecco il codice di una localizzazione anticipata di un'app di esempio nelle app ios


Funziona anche su iOS7, devo passare dalla soluzione di Brian a questa in quanto sembra che iOS sovrascriva nuovamente l'opzione delle lingue, quindi sono rimasto bloccato con l'impostazione della lingua per la prima volta. La tua soluzione funziona a meraviglia!
haxpor

14

Come menzionato da Brian Webster, la lingua deve essere impostata "all'inizio dell'avvio dell'applicazione". Ho pensato applicationDidFinishLaunching:di AppDelegatedovrebbe essere un luogo adatto per farlo, dal momento che è dove faccio tutti gli altri inizializzazione.

Ma come menziona William Denniss, ciò sembra avere effetto solo dopo il riavvio dell'app, il che è un po 'inutile.

Tuttavia, sembra funzionare bene se inserisco il codice nella funzione principale:

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;
}

Apprezzerei qualsiasi commento su questo.


nella mia implementazione è una cosa impostabile dall'utente - quindi faccio apparire una finestra di dialogo che dice loro che dovranno riavviare :)
William Denniss

Funziona, quasi per tutti, tranne per l'immagine Default.png localizzata.
Lukasz

2
Se consenti agli utenti di impostare una lingua e quindi di chiedere loro di riavviare l'app, ricorda che NSUserDefaults potrebbe non essere salvato se si riavvia troppo rapidamente. La maggior parte degli utenti non è così veloce, ma quando provi l'app potresti essere troppo veloce e di conseguenza vedere un comportamento incoerente. Mi ci sono volute un paio d'ore per capire perché il mio cambio di lingua a volte funzionava ea volte no!
arlomedia

3
Non è un problema, basta usare [[NSUserDefaults standardUserDefaults] synchronize];dopo aver chiamatosetObject:forKey:
Ben Baron

14

Cosa ne pensate di questa soluzione per 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)
    }
}

Utilizzo semplice:

"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.

Ciao, Come localizzare il testo dinamico che proviene dal server, nella mia applicazione ogni testo che devo visualizzare in lingua cinese semplificata, Ma come visualizzare il testo in cinese che arriva in inglese. aiutami.
Mahesh Narla

Questa è una risposta brillante;)
Bartłomiej Semańczyk

ottima risposta. mi ha aiutato
Dilip Tiwari

12

NSLocalizedString()legge il valore per la chiave AppleLanguagesdalle impostazioni predefinite dell'utente standard ( [NSUserDefaults standardUserDefaults]). Utilizza quel valore per scegliere una localizzazione appropriata tra tutte le localizzazioni esistenti in fase di esecuzione. Quando Apple crea il dizionario predefinito dell'utente all'avvio dell'app, cerca la chiave della lingua preferita nelle preferenze di sistema e copia il valore da lì. Questo spiega anche, ad esempio, perché la modifica delle impostazioni della lingua in OS X non ha alcun effetto sull'esecuzione delle app, ma solo sulle app avviate successivamente. Una volta copiato, il valore non viene aggiornato solo perché le impostazioni cambiano. Ecco perché iOS riavvia tutte le app se cambi la lingua.

Tuttavia, tutti i valori del dizionario di default dell'utente possono essere sovrascritti dagli argomenti della riga di comando. Vedere la NSUserDefaultsdocumentazione su NSArgumentDomain. Ciò include anche quei valori caricati dal file delle preferenze dell'app (.plist). È davvero utile sapere se si desidera modificare un valore solo una volta per il test .

Quindi, se vuoi cambiare la lingua solo per il test, probabilmente non vuoi modificare il tuo codice (se dimentichi di rimuovere questo codice in seguito ...), dì invece a Xcode di avviare la tua app con i parametri della riga di comando ( ad es. usa la localizzazione spagnola):

inserisci qui la descrizione dell'immagine

Non c'è bisogno di toccare il tuo codice. Basta creare schemi diversi per lingue diverse e puoi avviare rapidamente l'app una volta in una lingua e una volta in un'altra semplicemente cambiando schema.


Funziona solo per la prima volta dopo di che si carica di nuovo nella pref. lingua ..
Risposta il

@Aks funziona ogni volta per me. Assicurati di avviare lo schema corretto, assicurati di iniziare da Xcode (riavvii, fork, ecc. Non riceveranno l'argomento) e assicurati di non sovrascrivere la lingua Optionscome con le versioni più recenti di Xcode Apple lo offre anche.
Mecki

Grazie per la tua risposta @Mecki .. Per me non funziona in xcode ma quando lo menziono nel codice, funziona .. Tuttavia, se ho impostato il mio dispositivo con altre lingue preferite, è necessario riavviare l'app per caricarla nella mia preferita lingua che sto trasmettendo in argomenti ....
Risposta il

@Aks Quando lo usi su un dispositivo, funzionerà solo se avviato direttamente da Xcode, non funzionerà quando avvii di nuovo l'app toccando la sua icona su alcuni dispositivi e non funzionerà anche quando l'app viene interrotta (ad es. perché è andato in background) e viene riavviato dal sistema (poiché questo riavvio non è un riavvio eseguito da Xcode), quindi passare a un'app diversa e viceversa potrebbe non funzionare se iOS deve terminare la tua app nel mezzo (ad es. ha bisogno della memoria).
Mecki

Puoi combinare questo passaggio di argomento con la modifica in StandardUserDefaults e funzionerà come un incantesimo ogni volta
Leemur

11

Mi piace di più il metodo di Mauro Delrio. Ho anche aggiunto quanto segue nel mio Project_Prefix.pch

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

Quindi, se vuoi usare il metodo standard (che usa NSLocalizedString) puoi fare una rapida sostituzione della sintassi in tutti i file.


1
Mi piace anche il suo metodo e aggiungo un metodo per caricare le immagini png: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[UIImage alloc] initWithContentsOfFile: fullPath]; return [imageObj autorelease]; }
Jagie

10

Ho trovato una soluzione che ti permette di usare NSLocalizedString. Creo una categoria di NSBundlechiamata NSBundle+RunTimeLanguage. L'interfaccia è così.

// 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

L'implementazione è così.

// 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

Invece di aggiungere semplicemente l'importazione NSBundle+RunTimeLanguage.hai file che utilizzano NSLocalizedString.

Come puoi vedere memorizzo il mio languageCode in una proprietà di AppDelegate. Questo potrebbe essere memorizzato ovunque desideri.

L'unica cosa che non mi piace è un Avvertimento che NSLocalizedStringMarco ha ridefinito. Forse qualcuno potrebbe aiutarmi a riparare questa parte.


1
Aggiungi #undef NSLocalizedStringappena prima #defineper disabilitare l'avviso
berillio

Funziona con la localizzazione per il file xib? Ho file .xib e .strings per tradurre il nome dell'interfaccia utente in una particolare lingua. Ho provato e non funziona con XIB ma non sono sicuro di aver fatto le cose correttamente
Kong Hantrakool

Kong scusa per il ritardo nella risposta. Funziona ovunque utilizzi NSLocalizedString. Quindi non funzionerà direttamente in un XIB. Avresti bisogno di IBOutlet per le stringhe nello XIB e quindi dovresti impostare a livello di codice i valori di stringa nel codice.
qman64

Questo approccio ha funzionato per me e sono stato in grado di cambiare le lingue al volo. Nota che i codici di lingua come "es", fr "e" en "hanno funzionato bene. Ma" zh "(per il cinese semplificato) non è riuscito e mi ha restituito stringhe nulle. Ho fatto una ricerca globale sul mio sistema per" es.lproj " vedere dove si trovavano i file a cui stavo accedendo e lì ho scoperto che "zh.lproj" è in realtà "zh-Hans.lproj".
Gallymon

9

Versione rapida:

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

8

In poche parole:

Localizza la tua applicazione

La prima cosa che devi fare è localizzare la tua app con almeno due lingue (inglese e francese in questo esempio).

Sostituisci NSLocalizedString

Nel tuo codice, invece di usare NSLocalizedString(key, comment), usa una macro MYLocalizedString(key, comment)definita in questo modo: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Questo MYLocalizationSystemsingleton:

  • Imposta la lingua impostando il corretto utente NSBundle localizzato richiesto
  • Restituisce la NSString localizzata in base alla lingua impostata in precedenza

Imposta la lingua dell'utente

Quando l'utente ha cambiato la lingua dell'applicazione in francese, chiama [[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 questo esempio, questo metodo imposta il bundle localizzato su fr.lproj

Restituisce una stringa localizzata

Dopo aver impostato il bundle localizzato, sarai in grado di ottenere la stringa localizzata corretta da lui con questo metodo:

- (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];
}

Spero che questo ti possa aiutare.

Troverai maggiori dettagli in questo articolo da NSWinery.io


Ti chiediamo non solo di inserire un link a una soluzione nella tua risposta; il collegamento potrebbe smettere di funzionare un giorno. Sebbene non sia necessario rimuovere il collegamento dalla risposta, ti chiediamo di modificare la risposta per includere un riepilogo della sezione pertinente dell'articolo collegato.
Oblivious Sage

7

Estensioni Swift 3:

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
    }
}

Utilizzo:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized

Grazie. Lavorare bene.
Krunal Nagvadia

5

Nel file .pch da definire:

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


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")

Come posso eliminare l'avviso "ovverride ..."?
Idan Moshe

1
La soluzione semplice fa tutto ciò di cui ho bisogno. È stato in grado di usarlo come guida per risolvere il mio problema. Se pathForResource restituisce zero perché non ho un file di localizzazione, allora utilizzo pathForResource: @ "Base" poiché nient'altro che ho provato estrae le stringhe dalla localizzazione di base. Grazie.
GRW

Questo è hacky (così sono tutte le altre risposte), ed è stato più facile da implementare nello unit test che stavo scrivendo.
Max

4

Forse dovresti completare con questo (sul file .pch dopo #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]

/Users/pwang/Desktop/Myshinesvn/Myshine/viewController/OrientationReadyPagesView.m:193:31: Uso dell'identificatore non dichiarato "bundle"
pengwang

4

Soluzione Swift 3:

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")

Ha fornito alcuni esempi di codici di lingua che possono essere utilizzati. Spero che sia di aiuto


4

In swift 4, l'ho risolto senza bisogno di riavviare o utilizzare le librerie.

Dopo aver provato molte opzioni, ho trovato questa funzione, dove si passa la stringaToLocalize (di Localizable.String, il file di stringhe) che si desidera tradurre e la lingua in cui si desidera tradurla, e ciò che restituisce è il valore per quella stringa che hai nel file di stringhe:

    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)
    }

Tenendo conto di questa funzione, ho creato questa funzione in un file Swift:

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!)!
    }
}

Per accedere dall'intera app e in ogni stringa del resto di ViewControllers, invece di inserire:

NSLocalizedString ("StringToLocalize", comment: “")

L'ho sostituito con

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

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

Non so se sia il modo migliore, ma l'ho trovato molto semplice, e per me funziona, spero ti aiuti!


Questa risposta è ottima perché funziona anche in un pacchetto Swift in esecuzione su Linux, ad esempio.
Rémi B.

3

È possibile creare un sub-bundle con l'insieme di stringhe localizzate con cui si desidera eseguire questa operazione e quindi utilizzarle NSLocalizedStringFromTableInBundle()per caricarle. (Presumo che questo sia un contenuto separato dalla normale localizzazione dell'interfaccia utente che potresti eseguire sull'app.)


3

per il mio caso ho due file localizzati, ja e en

e vorrei forzarlo a en se la lingua preferita nel sistema non è né en né ja

ho intenzione di modificare il file main.m

controllerò se la prima lingua preferita è en o ja, altrimenti cambierò la seconda lingua preferita in en.

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]));


    }


}

Grazie @ chings228 è utile per me, ma questo non cambia l'immagine di avvio predefinita (splash) per l'app. Ho diversi splash per ogni lingua. sai come fare domanda per questo ??
JERC

Penso che lo splash venga eseguito prima che il programma principale inizi a funzionare, quindi non so come sovrascriverlo, potrebbe essere plist può aiutare ma potrebbe funzionare per la prossima volta che l'app si apre
chings228

2

Puoi fare qualcosa del genere:

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


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

NSLocalizedStringFromTableInBundle(@"House", nil, spanishBundle, nil):

2

Ecco una soluzione decente per questo problema e non richiede il riavvio dell'applicazione.

https://github.com/cmaftuleac/BundleLocalization

Questa implementazione funziona modificando all'interno di NSBundle. L'idea è di sovrascrivere il metodo localizedStringForKey sull'istanza dell'oggetto NSBundle e quindi chiamare questo metodo su un bundle diverso con una lingua diversa. Semplice ed elegante completamente compatibile con tutti i tipi di risorse.


Questo codice del progetto mi ha aiutato molto. Come trovare un pacchetto per il codice della lingua specifico ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
eonil

Questa classe funziona bene, ma dopo aver cambiato la lingua in un'altra, non ha effetto.
Anilkumar iOS - ReactNative

2

Basato sulla risposta di Tudorizer per cambiare lingua senza uscire o riavviare l'applicazione.

Invece di una macro, utilizza una classe per accedere alla lingua preferita per verificare se è presente un codice lingua specifico.

Di seguito è riportata una classe utilizzata per ottenere il pacchetto di lingue corrente che funziona per iOS 9:

@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

Usa la classe sopra per fare riferimento a un file di stringa / immagine / video / ecc:

// 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")

Cambia la lingua in linea come sotto o aggiorna il metodo "changeCurrentLanguage" nella classe sopra per accettare un parametro stringa che fa riferimento alla nuova lingua.

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

1

Questa funzione proverà a ottenere una stringa localizzata per la lingua corrente e se non viene trovata la otterrà utilizzando la lingua inglese.

- (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;
}

1

Volevo aggiungere il supporto per una lingua che non è ufficialmente supportata da iOS (non elencata nella sezione Lingua nelle impostazioni di sistema). Seguendo il Tutorial sull'internazionalizzazione di Apple e alcuni suggerimenti qui di Brian Webster e Geon, sono arrivato a questo pezzo di codice (mettilo 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]));
    }
}

Funziona bene sia per il codice Storyboard che per il codice NSLocalizedString. Il codice presuppone che l'utente avrà la possibilità di cambiare manualmente la lingua all'interno dell'app in un secondo momento.

Ovviamente, non dimenticare di aggiungere le traduzioni corrette dello Storyboard e Localizable.strings (vedi il link alla pagina Apple sopra per come farlo).


Vedo che stai anche affrontando problemi con la lingua slovena :) Per migliorare questa risposta per le lingue non supportate da iOS, hai capito se possiamo impostare stringhe localizzabili personalizzate che verranno utilizzate per i controlli di sistema nell'app (Modifica, Fatto, Altro, ...)?
miham

0

qualunque cosa facciate, il modo migliore è prendere lo short_name per la lingua specificata, cioè: fr, en, nl, de, it, ecc ... e assegnare lo stesso a un valore globale.

fai apparire una vista di selezione come un menu a discesa (combinazione di un pulsante al clic di cui appare una vista di selezione dal basso con un elenco di lingue) e seleziona la lingua che desideri. lascia che il nome breve venga memorizzato internamente. creare un file .h + .m denominato LocalisedString.

Impostare il valore globale di short_name in modo che sia uguale al valore ottenuto in LocalisedString.m Quando viene selezionata la lingua richiesta, assegnare NSBundlePath per creare sottodirectory del progetto per la lingua richiesta. ad esempio, nl.proj, en.proj.

Quando viene selezionata la cartella del progetto particolare, chiamare la stringa localizzata per la rispettiva lingua e cambiare la lingua dinamicamente.

nessuna regola infranta.


0

Per Swift puoi sovrascrivere il main.swiftfile e impostare la stringa UserDefaults lì prima che l'app venga eseguita. In questo modo non è necessario riavviare l'App per vedere l'effetto desiderato.

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))

accoppiato con la rimozione di @UIApplicationMainnel AppDelegate.swiftfile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.