ios - extension - xcode extract localized strings




Come forzare NSLocalizedString per utilizzare una lingua specifica (17)

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 codice, invece di utilizzare NSLocalizedString(key, comment) , utilizzare una macro MYLocalizedString(key, comment) definita in questo modo: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Questo singleton di MYLocalizationSystem :

  • Imposta la lingua impostando l'utente NSBundle localizzato giusto che richiede
  • Restituisce la NSString localizzata in base a questa lingua impostata in precedenza

Imposta la lingua dell'utente

Quando l'utente modifica 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 pacchetto localizzato su fr.lproj

Restituisce stringa localizzata

Una volta impostato il pacchetto localizzato, sarai in grado di ottenere da lui la stringa localizzata corretta 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.

Troverete maggiori dettagli in questo articolo da NSWinery.io

Su iPhone NSLocalizedString restituisce la stringa nella lingua dell'iPhone. È possibile forzare NSLocalizedString a utilizzare una lingua specifica per avere l'app in una lingua diversa da quella del dispositivo?


È possibile creare un sotto-bundle con il set di stringhe localizzate con cui si desidera eseguire questa operazione e quindi utilizzare NSLocalizedStringFromTableInBundle() per caricarle. (Suppongo che questo sia contenuto separato dalla normale localizzazione dell'interfaccia utente che potresti fare sull'app.)


Come menziona Brian Webster, il linguaggio deve essere impostato "qualche tempo prima all'avvio dell'applicazione". Ho pensato che applicationDidFinishLaunching: del AppDelegate dovrebbe essere un posto adatto per farlo, dal momento che è dove faccio tutte le altre inizializzazioni.

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

Sembra funzionare bene se inserisco il codice nella funzione principale, comunque:

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.


Cosa ne pensi 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)
    }
}

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

Forse dovresti integrare questo (nel 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]

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

La mia soluzione era quella di modificare 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 inizia e prima di caricare il tuo primo controller, chiama semplicemente:

[NSBundle setLanguage:@"en"];

Quando l'utente cambia la sua lingua preferita nella schermata delle impostazioni, chiamala di nuovo:

[NSBundle setLanguage:@"fr"];

Per ripristinare le impostazioni predefinite del sistema, passa semplicemente nil:

[NSBundle setLanguage:nil];

Godere...

Per coloro che hanno bisogno 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)
    }
}


Mi piace il miglior 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]

Pertanto, se si desidera utilizzare il metodo standard (che utilizza NSLocalizedString), è possibile eseguire una sostituzione rapida della sintassi in tutti i file.


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

Questa funzione cercherà di ottenere una stringa localizzata per la lingua corrente e, se non viene trovata, la otterrà usando 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;
}

Versione Swift:

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

Volevo aggiungere il supporto per un linguaggio che non è ufficialmente supportato da iOS (non elencato nella sezione Lingua nelle impostazioni di sistema). Seguendo l' Internationalization Tutorial di Apple e alcuni suggerimenti qui da Brian Webster e Geon, ho trovato 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]));
    }
}

This works well for both Storyboard and NSLocalizedString code. The code assumes that user will have an option to manually change language inside app later on.

Of course, don't forget to add proper Storyboard translations and Localizable.strings translations (see link to Apple page above for how to do that).


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

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

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

Questo renderebbe il tedesco la lingua preferita per la tua applicazione, con l'inglese e il francese come fallback. Si consiglia di chiamarlo prima possibile all'avvio dell'applicazione. Puoi leggere ulteriori informazioni sulle preferenze di lingua / impostazioni internazionali qui: Argomenti di programmazione per l'internazionalizzazione: ottenere la lingua e le impostazioni internazionali correnti


NSLocalizedString() legge il valore per la chiave AppleLanguages dai default utente standard ( [NSUserDefaults standardUserDefaults] ). Utilizza tale valore per scegliere una localizzazione appropriata tra tutte le localizzazioni esistenti in fase di runtime. Quando Apple crea il dizionario predefinito dell'utente all'avvio dell'app, cerca la lingua o le lingue preferite nelle preferenze di sistema e copia il valore da lì. Ciò spiega anche, ad esempio, perché la modifica delle impostazioni della lingua in OS X non ha alcun effetto sull'esecuzione delle app, 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 predefinito dell'utente possono essere sovrascritti dagli argomenti della riga di comando. Vedere la documentazione NSUserDefaults su NSArgumentDomain . Ciò include anche quei valori che vengono caricati dal file delle preferenze dell'app (.plist). È molto utile sapere se si desidera modificare un valore solo una volta per il test .

Quindi, se vuoi cambiare la lingua solo per i test, probabilmente non vuoi modificare il tuo codice (se ti dimentichi di rimuovere questo codice più tardi ...), invece di dire a Xcode di avviare la tua app con i parametri della riga di comando ( ad esempio, utilizzare la localizzazione spagnola):

Non è necessario 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 lo schema.


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

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

Metti questa macro in Prefix.pch:

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

e dove mai hai bisogno di usare una stringa localizzata:

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

Per impostare la lingua utilizzare:

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

Funziona anche con il salto di lingua consecutivo 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, @""));

Swift 3 extensions:

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

Uso:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized

whatever you all do, the best way is to take the short_name for the specified language, ie: fr, en, nl, de, it, etc... and assign the same to a global value.

make a picker view to pop up like a drop down menu (combination of a button on click of which a picker view appears from below with a list of languages) and select the language you desire. let the short name be stored internally. make a .h + .m file named LocalisedString.

Set the global value of short_name to be equal to the obtained value in LocalisedString.m When the required language is selected assign the NSBundlePath to create project sub-directories for the needed language. for eg, nl.proj, en.proj.

When the particular proj folder is selected call the localised string for the respective language and change the language dynamically.

no rules broken.





internationalization