Na iPhonie NSLocalizedString
zwraca ciąg znaków w języku iPhone'a. Czy można wymusić NSLocalizedString
użycie określonego języka, aby aplikacja działała w innym języku niż urządzenie?
Na iPhonie NSLocalizedString
zwraca ciąg znaków w języku iPhone'a. Czy można wymusić NSLocalizedString
użycie określonego języka, aby aplikacja działała w innym języku niż urządzenie?
Odpowiedzi:
NSLocalizedString()
(i jego warianty) uzyskaj dostęp do klawisza „AppleLanguages”, NSUserDefaults
aby 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
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)
}
}
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
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, @""));
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.m
tuż przed UIApplicationMain
(...).
NSAutoreleasePool * pool ..
jak wycieknie kilka automatycznie wydanych obiektów.
[[NSBundle mainBundle] URLForResource:withExtension:]
wcześniej.
Sztuczka polegająca na użyciu określonego języka przez wybranie go z aplikacji polega na wymuszeniu NSLocalizedString
uż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
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.
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.
[[NSUserDefaults standardUserDefaults] synchronize];
po rozmowie telefonicznejsetObject:forKey:
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.
NSLocalizedString()
odczytuje wartość klucza AppleLanguages
ze 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 NSUserDefaults
dokumentację 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):
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.
Options
jak w przypadku nowszych wersji Xcode, które Apple oferuje także.
Wymyśliłem rozwiązanie, które pozwala ci używać NSLocalizedString
. Tworzę kategorię NSBundle
połą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.h
do 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 NSLocalizedString
nowo zdefiniowało Marco. Być może ktoś może mi pomóc naprawić tę część.
#undef NSLocalizedString
tuż przed, #define
aby wyłączyć ostrzeżenie
Pierwszą rzeczą, którą musisz zrobić, to zlokalizować aplikację w co najmniej dwóch językach (angielski i francuski w tym przykładzie).
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 MYLocalizationSystem
singleton:
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
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
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
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, @"")
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]
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
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).
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]));
}
}
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):
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"];
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!
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;
}
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).
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.
NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:@"lproj" ];
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.
W przypadku Swift można zastąpić main.swift
plik 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 @UIApplicationMain
w Twoim AppDelegate.swift
pliku.