Jak zmusić NSLocalizedString do używania określonego języka

Odpowiedzi:

261

NSLocalizedString()(i jego warianty) uzyskaj dostęp do klawisza „AppleLanguages”, NSUserDefaultsaby określić ustawienia użytkownika dla preferowanych języków. Zwraca to tablicę kodów językowych, z których pierwszy jest ustawiony dla użytkownika przez telefon, a kolejne są używane jako rezerwowe, jeśli zasób nie jest dostępny w preferowanym języku. (na pulpicie użytkownik może określić wiele języków z niestandardową kolejnością w Preferencjach systemowych)

Jeśli chcesz, możesz zastąpić globalne ustawienie własnej aplikacji, używając metody setObject: forKey: do ustawienia własnej listy języków. Będzie to miało pierwszeństwo przed ustawioną globalnie wartością i zostanie zwrócone do dowolnego kodu w aplikacji, który dokonuje lokalizacji. Kod do tego wyglądałby mniej więcej tak:

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

To sprawiłoby, że niemiecki byłby preferowanym językiem dla twojej aplikacji, a angielski i francuski jako awarie. Możesz to nazwać na początku uruchamiania aplikacji. Możesz przeczytać więcej o preferencjach językowych / regionalnych tutaj: Internacjonalizacja Tematy programowania: Pobieranie bieżącego języka i ustawień regionalnych

Brian Webster
źródło
4
To nie działa dla mnie, użyje domyślnego języka bez względu na wszystko.
quano
50
Denniss; Wydaje się, że działa lepiej, jeśli ustawisz preferencje językowe przed uruchomieniem aplikacji. Robię to w funkcji main (), przed wywołaniem UIApplicationMain (). Po uruchomieniu program nie zmieni używanego języka, ale po prostu ustawi preferencje na następny raz.
geon
3
Musisz ustawić język przed zainicjowaniem UIKit i musisz podać pełne ustawienia regionalne + język - sprawdź to, aby uzyskać pełny przykładowy blog.federicomestrone.com/2010/09/15/…
fedmest
18
ustawienie AppleLanguages ​​zmieni język w czasie wykonywania, jeśli zostanie wykonane w main () - prawda! ale ... PIERWSZY RAZ jest zainstalowana aplikacja, nsuserdefault jest inicjowany PO wywołaniu UIApplicationMain (), co zignoruje wcześniej ustawione AppleLanguages ​​i będzie wymagało brzydkiego ponownego uruchomienia aplikacji, aby zobaczyć pożądany język.
JohnPayne
3
Nie działa to z wieloma elementami zdefiniowanymi w Localizable.stringsdict. Masz pomysł, czy istnieje jakaś poprawka?
Mihai Fratu,
147

Miałem ostatnio ten sam problem i nie chciałem uruchamiać i łatać całego NSLocalizedString ani zmuszać aplikacji do ponownego uruchomienia, aby nowy język działał. Chciałem, żeby wszystko działało takie, jakie jest.

Moim rozwiązaniem była dynamiczna zmiana klasy pakietu głównego i załadowanie tam odpowiedniego pakietu:

Plik nagłówka

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

Realizacja

#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

Zasadniczo po uruchomieniu aplikacji i przed załadowaniem pierwszego kontrolera wystarczy wywołać:

[NSBundle setLanguage:@"en"];

Gdy użytkownik zmieni preferowany język na ekranie ustawień, po prostu zadzwoń ponownie:

[NSBundle setLanguage:@"fr"];

Aby przywrócić ustawienia domyślne systemu, wystarczy przekazać zero:

[NSBundle setLanguage:nil];

Cieszyć się...

Dla tych, którzy potrzebują wersji 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)
    }
}
Gilad Novik
źródło
7
Cześć @Wirsing, do tej pory działa świetnie. Przesłałem nawet jedną aplikację do sklepu i Apple nie narzeka.
Gilad Novik
7
To świetne rozwiązanie, odwołuję się również tutaj: medium.com/ios-apprentice/905e4052b9de
James Tang
1
Cześć @JamesTang, Dziękuję za referencję i twój post - Znalazłem tam wiele przydatnych informacji dotyczących lokalizacji.
Gilad Novik
3
@Gilad twoja metoda działa idealnie w przypadku ciągów dynamicznych (ciągów, które zdefiniowałem w localizable.strings), ale jeśli chodzi o przyciski i etykiety serii ujęć, działa ona tylko przy głównej metodzie. więc czy możesz rozszerzyć swoją odpowiedź o lokalizację elementów sterujących interfejsu użytkownika? mam na myśli, jak odświeżyć (bez zamykania) scenorys po wywołaniu [NSBundle setLanguage: @ "??"] ;?
Jawad Al Shaikh
2
Co do XIB / storyboardu: musisz upewnić się, że wywołujesz tę metodę PRZED załadowaniem widoku. Jeśli chodzi o aktualizację widoków na żywo: w moim przypadku po prostu ponownie załadowałem kontroler widoku (umieściłem go w kontrolerze nawigacyjnym i właśnie go wymieniłem. Po ponownym załadowaniu - otrzymałem nowe przetłumaczone ciągi). Pracuję nad tym cocoapod, prawdopodobnie podam tam również przykład. Bądź na bieżąco ...
Gilad Novik
137

Zwykle robię to w ten sposób, ale MUSISZ mieć wszystkie pliki lokalizacji w swoim projekcie.

@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
źródło
1
+1. To naprawdę fajna sztuczka, której nigdzie indziej nie widziałem. Tworząc „paczka podrzędna” z jednego z folderów lokalizacyjnych, możesz sprawić, aby łańcuch znaków działał dobrze, o ile otoczysz NSLocalizedString czymś, co się tutaj obchodzi.
Ben Zotto
4
Doskonałe Mauro. Zauważyłem, że może to również działać w przypadku plików z twojego projektu. Jeśli z jakiegoś powodu (jak w moim przypadku) musisz pobrać pliki ciągów z sieci i zapisać je w katalogu „Dokumenty” (ze strukturą folderów Dokumenty / en.lproj / Localizable.strings, Documents / fr.lproj / Localizable.strings, ...). Możesz nawet zrobić NSBundle. Wystarczy użyć tego kodu dla ścieżki: NSString * path = [NSHomeDirectory () stringByAppendingPathComponent: [NSString stringWithFormat: @ "/ Documents /% @. Lproj", l, nil]];
arnaud del.
1
Wydaje mi się, że jest to najlepsze podejście, wraz z wkładem Alessandro poniżej. Nie jest to tak nachalne i nie powinno wymagać ponownego uruchomienia.
PKCLsoft
1
Co ja robię źle? Zrobiłem klasę Language, dziedzicząc NSObject, umieściłem to w implementacji, umieściłem nazwy metod w .h, a następnie wstawiłem [Language setLanguage: @ "es"]; w moim przycisku zmiany języka kod działa (metoda button zostaje wywołana i przechodzi do klasy Language), ale nic nie robi. Mam również skonfigurowane moje localizable.strings (hiszpański). Ale wydaje się, że działa dla wielu innych ludzi. Proszę, niech ktoś mnie stępi.
SirRupertIII
1
@ KKendall nazywasz to tak: [Language setLanguage: @ "es"]; NSString * stringToUse = [Language get: @ "Your Text" alter: nil];
Mikrasya
41

Nie używaj w systemie iOS 9. Zwraca zero dla wszystkich ciągów przez niego przechodzących.

Znalazłem inne rozwiązanie, które pozwala zaktualizować ciągi językowe, bez ponownego uruchamiania aplikacji i zgodne z ciągami znaków:

Umieść to makro w Prefix.pch:

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

i gdziekolwiek potrzebujesz zlokalizowanego ciągu znaków:

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

Aby ustawić język, użyj:

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

Działa nawet z kolejnymi przeskokami językowymi, takimi jak:

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
źródło
2
@ powerj1984: nie, zmieni tylko ciągi w plikach źródłowych. Jeśli chcesz zmienić języki XIB, musisz ręcznie załadować XIB z pakietu wybranego języka.
gklka
@gklka Postępowałem zgodnie z instrukcjami podanymi przez Tudoziera, ale moje XIB-y nie zmieniają języka, jak powiedziałeś ręcznie przeładuj XIB, co to właściwie znaczy przeładować XIB?
Dk Kumar
@DkKumar: Musisz zrobić coś podobnego do tego: stackoverflow.com/questions/21304988/...
gklka
@gklka Twoja odpowiedź wydaje się poprawna i zrobiłem te same kroki, co opisany w odpowiedzi, ale problem polega na tym, że jeśli zmienimy ścieżkę pakietu, to znajdzie ona wszystkie zasoby (tj. plik .png używany w XIB) w ten sam pakiet, więc musimy skopiować folder obrazów we wszystkich pakietach, takich jak en.lproj, es.lproj itd. .. więc wpłynie to na zwiększenie rozmiaru IPA, więc czy jest jakiś sposób na rozwiązanie tego problemu? tak jak powiedziałeś w swoim poście, pozwól, że spróbuję tego i jeszcze więcej na ten temat. Dziękujemy za pomoc w tym samym
Dk Kumar
1
Jest to strasznie zepsute w iOS 9, zwracając wartość zerową dla wszystkich ciągów.
Alex Zavatone
32

Jak powiedziano wcześniej, po prostu wykonaj:

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

Ale aby uniknąć konieczności ponownego uruchamiania aplikacji, umieść wiersz w głównej metodzie main.mtuż przed UIApplicationMain(...).

Frédéric Feytons
źródło
2
Bardzo pomocna odpowiedź! PS Może to wydawać się oczywiste dla początkujących, ale powinieneś wstawić tę linię po tym, NSAutoreleasePool * pool ..jak wycieknie kilka automatycznie wydanych obiektów.
pt2ph8
1
WAŻNE: To nie zadziała, jeśli zadzwonisz [[NSBundle mainBundle] URLForResource:withExtension:]wcześniej.
Kof
3
A jeśli użyjesz Swift, to nie będzie main.m?
przetestowano
@turingtested czy wypróbowałeś już szybkie i scenorys, czy to działa?
iDevAmit,
Cześć, Jak zlokalizować dynamiczny tekst przychodzący z serwera, w mojej aplikacji każdy tekst muszę wyświetlać w uproszczonym języku chińskim, ale jak wyświetlać tekst w języku chińskim, który nadchodzi w języku angielskim. Pomóż mi.
Mahesh Narla
31

Sztuczka polegająca na użyciu określonego języka przez wybranie go z aplikacji polega na wymuszeniu NSLocalizedStringużycia określonego pakietu w zależności od wybranego języka,

oto post, który napisałem dla tej zaawansowanej lokalizacji nauki w aplikacjach iOS

a oto kod jednej przykładowej zaawansowanej lokalizacji aplikacji w aplikacjach iOS

obiekt2.0
źródło
Działa również na iOS7, muszę przejść z rozwiązania Briana na to, ponieważ wygląda na to, że iOS ponownie zastępuje opcję języków, więc utknąłem przy pierwszym ustawieniu języka. Twoje rozwiązanie działa jak urok!
haxpor
14

Co sądzisz o tym rozwiązaniu dla 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)
    }
}

Proste użycie:

"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
źródło
Cześć, Jak zlokalizować dynamiczny tekst przychodzący z serwera, w mojej aplikacji każdy tekst muszę wyświetlać w uproszczonym języku chińskim, ale jak wyświetlać tekst w języku chińskim, który nadchodzi w języku angielskim. Pomóż mi.
Mahesh Narla
To genialna odpowiedź;)
Bartłomiej Semańczyk
świetna odpowiedź. pomogło mi
Dilip Tiwari
13

Jak wspomina Brian Webster, język należy ustawić „na początku uruchamiania aplikacji”. Myślałem applicationDidFinishLaunching:oAppDelegate powinno to być odpowiednie miejsce do zrobienia tego, ponieważ tam wykonuję całą inną inicjalizację.

Ale, jak wspomina William Denniss, wydaje się, że działa to dopiero po ponownym uruchomieniu aplikacji, co jest w pewnym sensie bezużyteczne.

Wydaje się, że działa dobrze, jeśli wstawię kod do głównej funkcji, jednak:

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

Będę wdzięczny za wszelkie komentarze na ten temat.

geon
źródło
w mojej implementacji jest to kwestia możliwa do ustawienia przez użytkownika - więc po prostu wyświetlam okno dialogowe z informacją, że będą musieli zrestartować :)
William Denniss
Działa prawie dla wszystkich - z wyjątkiem zlokalizowanego obrazu Default.png.
Łukasz
2
Jeśli pozwalasz użytkownikom ustawić język, a następnie nakazujesz im ponowne uruchomienie aplikacji, pamiętaj tylko, że NSUserDefault może nie zostać zapisany, jeśli uruchomią się zbyt szybko. Większość użytkowników nie jest tak szybka, ale podczas testowania aplikacji możesz być zbyt szybki i zobaczyć niespójne zachowanie. Kilka godzin zajęło mi zrozumienie, dlaczego moja zmiana języka czasami działała, a czasem nie!
arlomedia,
3
To nie jest problem, skorzystaj [[NSUserDefaults standardUserDefaults] synchronize];po rozmowie telefonicznejsetObject:forKey:
Ben Baron
11

Najbardziej podoba mi się metoda Mauro Delrio. Dodałem również następujące elementy w moim Project_Prefix.pch

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

Jeśli więc kiedykolwiek chcesz użyć standardowej metody (która używa NSLocalizedString), możesz szybko podstawić składnię we wszystkich plikach.

dnaxxx
źródło
1
Podoba mi się również jego metoda i dodaję metodę ładowania obrazów png: + (UIImage ) imageNamed: (NSString *) imageFileName {NSString fullPath = [bundle pathForResource: imageFileName ofType: @ "png"]; UIImage * imageObj = [[Alokacja UIImage] initWithContentsOfFile: fullPath]; return [imageObj autorelease]; }
Jagie
11

NSLocalizedString()odczytuje wartość klucza AppleLanguagesze standardowych wartości domyślnych użytkownika ( [NSUserDefaults standardUserDefaults]). Wykorzystuje tę wartość, aby wybrać odpowiednią lokalizację spośród wszystkich istniejących lokalizacji w czasie wykonywania. Gdy Apple tworzy słownik domyślny użytkownika podczas uruchamiania aplikacji, wyszukuje preferowany język (-y) w preferencjach systemowych i kopiuje z niego wartość. To wyjaśnia również na przykład, dlaczego zmiana ustawień języka w OS X nie ma wpływu na uruchamianie aplikacji, tylko na aplikacje uruchomione później. Po skopiowaniu wartość nie jest aktualizowana tylko dlatego, że zmieniają się ustawienia. Dlatego iOS ponownie uruchamia wszystkie aplikacje, jeśli zmienisz język.

Jednak wszystkie wartości słownika domyślnego użytkownika mogą zostać zastąpione argumentami wiersza poleceń. Zobacz NSUserDefaultsdokumentację na NSArgumentDomain. Obejmuje to nawet te wartości, które są ładowane z pliku preferencji aplikacji (.plist). To naprawdę dobrze wiedzieć, jeśli chcesz zmienić wartość tylko raz podczas testowania .

Jeśli więc chcesz zmienić język tylko na potrzeby testowania, prawdopodobnie nie chcesz zmieniać kodu (jeśli zapomnisz usunąć go później ...), zamiast tego powiedz Xcode, aby uruchomił aplikację z parametrami wiersza poleceń ( np. użyj hiszpańskiej lokalizacji):

wprowadź opis zdjęcia tutaj

W ogóle nie trzeba dotykać kodu. Po prostu utwórz różne schematy dla różnych języków i możesz szybko uruchomić aplikację raz na jeden język, a raz na inny, po prostu przełączając schemat.

Mecki
źródło
Działa tylko po raz pierwszy, po czym ponownie ładuje się do urządzenia pref. język ..
Aks
@Aks Działa za każdym razem dla mnie. Upewnij się, że uruchamiasz prawidłowy schemat, upewnij się, że zaczynasz od Xcode (ponowne uruchomienie, rozwidlenia itp. Nie dostanie argumentu) i upewnij się, że nie zastąpisz języka w taki sposób, Optionsjak w przypadku nowszych wersji Xcode, które Apple oferuje także.
Mecki
Dzięki za odpowiedź @Mecki .. Dla mnie nie działa w xcode, ale kiedy wspomnę w kodzie, działa .. Ale mimo to, jeśli ustawiłem urządzenie z innymi preferowanymi językami, trzeba ponownie uruchomić aplikację, aby załadować się do mojego preferowanego język, który przekazuję w argumentach ....
Aks
@Zadania Gdy używasz tego na urządzeniu, będzie działać tylko po uruchomieniu bezpośrednio z Xcode, nie będzie działać, gdy ponownie uruchomisz aplikację, dotykając jej ikony na niektórych urządzeniach, a także nie będzie działać, gdy aplikacja zostanie zabita (np. ponieważ poszedł w tło) i zostaje ponownie uruchomiony przez system (ponieważ ten restart nie jest restartem wykonanym przez Xcode) - więc przejście do innej aplikacji i wstecz może nie działać, jeśli iOS musi zabić aplikację pomiędzy nimi (np. ponieważ potrzebuje pamięci).
Mecki
10

Wymyśliłem rozwiązanie, które pozwala ci używać NSLocalizedString. Tworzę kategorię NSBundlepołączeń NSBundle+RunTimeLanguage. Interfejs jest taki.

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

Implementacja jest taka.

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

Następnie dodaj import NSBundle+RunTimeLanguage.hdo używanych plików NSLocalizedString.

Jak widać, przechowuję mój languageCode we właściwości AppDelegate. To może być przechowywane w dowolnym miejscu.

Jedyne, co mi się nie podoba, to Ostrzeżenie, które na NSLocalizedStringnowo zdefiniowało Marco. Być może ktoś może mi pomóc naprawić tę część.

qman64
źródło
1
Dodaj #undef NSLocalizedStringtuż przed, #defineaby wyłączyć ostrzeżenie
beryl
Czy to działa na lokalizację pliku Xib? Mam plik .xib i .strings do tłumaczenia nazwy interfejsu użytkownika w określonym języku. Próbowałem i nie działa z XIB, ale nie jestem pewien, czy zrobiłem wszystko poprawnie
Kong Hantrakool
Kong przepraszam za opóźnioną odpowiedź. Działa to wszędzie tam, gdzie używasz NSLocalizedString. Więc nie będzie działać bezpośrednio w XIB. Potrzebowałbyś IBOutletów do łańcuchów w XIB, a następnie musiałbyś programowo ustawić wartości łańcuchów w kodzie.
qman64
Takie podejście zadziałało dla mnie i mogłem zmieniać języki w locie. Zauważ, że kody językowe takie jak „es”, fr ”i„ en ”działały dobrze. Ale„ zh ”(dla chińskiego uproszczonego) zawiodło i zwróciło ciągi zerowe. Przeprowadziłem globalne wyszukiwanie w moim systemie hasła„ es.lproj ”, aby zobaczyć, gdzie były pliki, do których uzyskiwałem dostęp, i odkryłem tam, że „zh.lproj” to tak naprawdę „zh-Hans.lproj”.
Gallymon
9

Wersja szybka:

NSUserDefaults.standardUserDefaults().setObject(["fr"], forKey: "AppleLanguages")
NSUserDefaults.standardUserDefaults().synchronize()
daaniaal
źródło
8

W skrócie :

Znajdź swoją aplikację

Pierwszą rzeczą, którą musisz zrobić, to zlokalizować aplikację w co najmniej dwóch językach (angielski i francuski w tym przykładzie).

Przesłoń NSLocalizedString

W kodzie zamiast NSLocalizedString(key, comment)używać makra MYLocalizedString(key, comment)zdefiniowanego w następujący sposób: #define MYLocalizedString(key, comment) [[MYLocalizationSystem sharedInstance] localizedStringForKey:(key) value:(comment)];

Ten MYLocalizationSystemsingleton:

  • Ustaw język, ustawiając odpowiedni zlokalizowany użytkownik NSBundle, o który prosi
  • Zwraca zlokalizowane NSString zgodnie z tym wcześniej ustawionym językiem

Ustaw język użytkownika

Gdy użytkownik zmieni język aplikacji na francuski, zadzwoń [[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];
}

W tym przykładzie ta metoda ustawiła zlokalizowany pakiet na fr.lproj

Zwróć zlokalizowany ciąg

Po ustawieniu zlokalizowanego pakietu będziesz mógł uzyskać od niego odpowiedni zlokalizowany ciąg za pomocą tej metody:

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

Mam nadzieję, że to ci pomoże.

Więcej szczegółów znajdziesz w tym artykule z NSWinery.io

użytkownik3465357
źródło
Prosimy o nie tylko link do rozwiązania w swojej odpowiedzi; link może przestać działać pewnego dnia. Chociaż nie musisz usuwać linku z odpowiedzi, prosimy o edycję odpowiedzi w celu dołączenia streszczenia odpowiedniej sekcji powiązanego artykułu.
Oblivious Sage
7

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

Stosowanie:

Locale.preferredLanguage = "uk"

label.text = "localizedKey".localized
ChikabuZ
źródło
Dzięki. Pracuje dobrze.
Krunal Nagvadia,
5

W pliku .pch, aby zdefiniować:

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


#define NSLocalizedString(str,nil) NSLocalizedStringFromTableInBundle(str, nil, currentLanguageBundle, @"")
MrPiliffo
źródło
Jak pozbyć się ostrzeżenia „oververride ...”?
Idan Moshe
1
Proste rozwiązanie robi wszystko, czego potrzebuję. Był w stanie wykorzystać to jako przewodnik do rozwiązania mojego problemu. Jeśli pathForResource zwraca zero, ponieważ nie mam pliku lokalizacji, to używam pathForResource: @ „Base”, ponieważ nic innego, co próbowałem, nie pobiera ciągów z podstawowej lokalizacji. Dziękuję Ci.
GRW
Jest to hacking (podobnie jak wszystkie inne odpowiedzi) i najłatwiej było je zaimplementować w teście jednostkowym, który pisałem.
Maks.
4

Może powinieneś uzupełnić ten (w pliku .pch po #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
źródło
/Users/pwang/Desktop/Myshinesvn/Myshine/viewController/OrientationReadyPagesView.m:193:31: Korzystanie z niezadeklarowanego „pakietu” identyfikatora
pengwang
4

Rozwiązanie 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")

Podano przykłady kodów językowych, których można użyć. Mam nadzieję że to pomoże

Jeremy Bader
źródło
3

Możesz zbudować pakiet częściowy z zestawem zlokalizowanych ciągów, z którymi chcesz to zrobić, a następnie użyć NSLocalizedStringFromTableInBundle()do ich załadowania. (Zakładam, że jest to treść oddzielna od zwykłej lokalizacji interfejsu użytkownika, którą możesz wykonywać w aplikacji).

Sixten Otto
źródło
3

w moim przypadku mam dwa zlokalizowane pliki, ja i en

i chciałbym zmusić go do en, jeśli preferowanym językiem w systemie nie jest en ani ja

zamierzam edytować plik main.m

sprawdzę, czy pierwszym preferowanym jest en lub ja, jeśli nie, zmienię drugi preferowany język na 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]));


    }


}
chings228
źródło
Dzięki @ chings228 jest to dla mnie przydatne, ale to nie zmienia domyślnego obrazu uruchamiania (splash) dla aplikacji Mam różne splashy dla każdego języka. Czy wiesz jak się o to ubiegać?
JERC
myślę, że powitanie działa przed uruchomieniem głównego programu, więc nie wiem, jak go przesłonić, być może plist może pomóc, ale może zadziałać przy następnym otwarciu aplikacji
chings228
2

Możesz zrobić coś takiego:

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
źródło
2

Na podstawie odpowiedzi Tudorizera, aby zmienić język bez opuszczania lub ponownego uruchamiania aplikacji.

Zamiast makra użyj klasy, aby uzyskać dostęp do preferowanego języka, aby sprawdzić, czy występuje określony kod języka.

Poniżej znajduje się klasa używana do uzyskania bieżącego pakietu językowego, który działa na 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

Użyj powyższej klasy, aby odwołać się do pliku ciągu / obrazu / wideo / etc:

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

Zmień język in-line, jak poniżej lub zaktualizuj metodę „changeCurrentLanguage” w powyższej klasie, aby pobrać parametr ciągu odwołujący się do nowego języka.

[[NSUserDefaults standardUserDefaults] setObject:@[@"es"] forKey:@"AppleLanguages"];
Antonioni
źródło
2

W swift 4 rozwiązałem go bez konieczności ponownego uruchamiania lub używania bibliotek.

Po wypróbowaniu wielu opcji znalazłem tę funkcję, w której przekazujesz stringToLocalize (pliku Localizable.String, plik ciągów), który chcesz przetłumaczyć, oraz język, w którym chcesz go przetłumaczyć, a to, co zwraca, to wartość ten ciąg znaków, który masz w pliku ciągów:

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

Biorąc pod uwagę tę funkcję, stworzyłem tę funkcję w pliku 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!)!
    }
}

Aby uzyskać dostęp z całej aplikacji oraz w każdym ciągu pozostałych elementów ViewControllers, zamiast umieszczać:

NSLocalizedString ("StringToLocalize", comment: ")

Zastąpiłem go

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

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

Nie wiem, czy to najlepszy sposób, ale uznałem to za bardzo proste i działa dla mnie, mam nadzieję, że ci to pomoże!

ainareta685
źródło
1

Ta funkcja spróbuje uzyskać zlokalizowany ciąg dla bieżącego języka, a jeśli go nie znajdzie, otrzyma go w języku angielskim.

- (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
źródło
1

Chciałem dodać obsługę języka, który nie jest oficjalnie obsługiwany przez iOS (niewymieniony w sekcji Język w ustawieniach systemu). Postępując zgodnie z samouczkiem internacjonalizacji Apple i kilkoma wskazówkami Briana Webstera i geona, wymyśliłem ten fragment kodu (umieść go w 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]));
    }
}

Działa to dobrze zarówno dla kodu Storyboard, jak i NSLocalizedString. Kod zakłada, że ​​użytkownik będzie miał później opcję ręcznej zmiany języka w aplikacji.

Oczywiście nie zapomnij dodać poprawnych tłumaczeń Storyboard i Localizable.strings (aby to zrobić, zobacz link do strony Apple powyżej).

frin
źródło
Widzę, że masz również problemy z językiem słoweńskim :) Aby poprawić tę odpowiedź dla języków nieobsługiwanych przez iOS, czy zastanawiasz się, czy możemy ustawić niestandardowe lokalizowalne ciągi znaków, które będą używane do sterowania systemem w aplikacji (Edycja, Gotowe, Więcej, ...)
miham
1

Oto przyzwoite rozwiązanie tego problemu i nie wymaga ponownego uruchomienia aplikacji.

https://github.com/cmaftuleac/BundleLocalization

Ta implementacja działa poprzez podkręcenie w NSBundle. Chodzi o to, że przesłonisz metodę localizedStringForKey w instancji obiektu NSBundle, a następnie wywołujesz tę metodę w innym pakiecie z innym językiem. Prosty i elegancki w pełni kompatybilny ze wszystkimi rodzajami zasobów.

Corneliu Maftuleac
źródło
Ten kod z projektu bardzo mi pomógł. Jak znaleźć pakiet dla określonego kodu języka ---NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
Eonil 12.04.16
Ta klasa działa dobrze, ale po zmianie języka na inny nie działa.
Anilkumar iOS ReactNative
0

cokolwiek byście nie zrobili, najlepszym sposobem jest pobranie krótkiej nazwy dla określonego języka, tj .: fr, en, nl, de, it itp. ... i przypisanie jej do wartości globalnej.

utwórz widok selektora, aby wyskoczył jak menu rozwijane (kombinacja przycisku, po kliknięciu którego pojawi się widok selektora z listą języków) i wybierz żądany język. niech krótka nazwa będzie przechowywana wewnętrznie. utwórz plik .h + .m o nazwie LocalisedString.

Ustaw wartość globalną short_name na równą wartości uzyskanej w LocalisedString.m Po wybraniu wymaganego języka przypisz ścieżkę NSBundlePath, aby utworzyć podkatalogi projektu dla potrzebnego języka. na przykład nl.proj, en.proj.

Po wybraniu konkretnego folderu proj wywołaj zlokalizowany ciąg znaków dla odpowiedniego języka i zmieniaj język dynamicznie.

żadnych zasad łamanych.

Abhijeet
źródło
0

W przypadku Swift można zastąpić main.swiftplik i ustawić tam ciąg UserDefaults przed uruchomieniem aplikacji. W ten sposób nie musisz ponownie uruchamiać aplikacji, aby zobaczyć pożądany efekt.

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

sparowany wraz z usunięciem @UIApplicationMainw Twoim AppDelegate.swiftpliku.

Andreas
źródło