Сегфафт не гарантируется стандартом C.
Вызов недопустимого указателя вызывает неопределенное поведение .
Это работает для меня: Swift 4:
Создайте файл с именем BundleExtension.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)
}
}
Теперь, когда вам нужно изменить язык вызывает этот метод:
func languageButtonAction() {
// This is done so that network calls now have the Accept-Language as "hi" (Using Alamofire) Check if you can remove these
UserDefaults.standard.set(["hi"], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
// Update the language by swaping bundle
Bundle.setLanguage("hi")
// Done to reintantiate the storyboards instantly
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
UIApplication.shared.keyWindow?.rootViewController = storyboard.instantiateInitialViewController()
}
Не полагайтесь на строки, которые вы установили в своем файле nib. Используйте свой наконечник только для макета & amp; настройка просмотров. Любая строка, отображаемая пользователю (текст кнопки и т. Д.), Должна быть в ваших файлах Localizable.strings, и когда вы загружаете свой nib, вам необходимо соответствующим образом установить текст на соответствующем представлении / элементе управления.
Чтобы получить пакет для текущего языка:
NSString *path = [[NSBundle mainBundle] pathForResource:currentLanguage ofType:@"lproj"];
if (path) {
NSBundle *localeBundle = [NSBundle bundleWithPath:path];
}
И использовать пакет для получения ваших локализованных строк:
NSLocalizedStringFromTableInBundle(stringThatNeedsToBeLocalized, nil, localeBundle, nil);
Также для форматирования даты вы можете захотеть посмотрите на
[NSDateFormatter dateFormatFromTemplate:@"HH:mm:ss"" options:0 locale:locale];
Чтобы использовать это, вам нужно будет создать NSLocale для соответствующего языка / страны, которую вы хотите использовать.
Вот что я сделал. Я думаю, что трюк состоял в том, чтобы использовать NSLocalizedStringFromTableInBundle вместо NSLocalizedString.
Для всех строк используйте это
someLabel.text = NSLocalizedStringFromTableInBundle(@"Your String to be localized, %@",nil,self.localeBundle,@"some context for translators");
Чтобы сменить язык, запустите этот код
NSString * language = @"zh-Hans"; //or whatever language you want
NSString *path = [[NSBundle mainBundle] pathForResource:language ofType:@"lproj"];
if (path) {
self.localeBundle = [NSBundle bundleWithPath:path];
}
else {
self.localeBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"] ];
}
После этого вам, скорее всего, захочется вызывать код обновления для обновления строк на новых языках, например снова запустите это
someLabel.text = NSLocalizedStringFromTableInBundle(@"Your String to be localized, %@",nil,self.localeBundle,@"some context for translators");
Вот и все. Не нужно перезапускать приложение. Совместимость с системными настройками (если вы установите язык через настройки iOS, он тоже будет работать). Нет необходимости в внешней библиотеке. Не нужно джейлбрейка. И это также работает с genstrings.
Конечно, вы должны по-прежнему выполнять обычное для своих настроек приложения:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:@"zh-Hans", nil] forKey:@"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
(и делать чек в вашем представленииDidLoad или что-то еще )
NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString *path = [[NSBundle mainBundle] pathForResource:language ofType:@"lproj"];
if (path) {
self.localeBundle = [NSBundle bundleWithPath:path];
}
else {
self.localeBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"] ];
}
У меня было аналогичное требование для iPad-приложения в режиме киоска с вкладкой навигации. Приложение не только нуждалось в поддержке языковых изменений на лету, но и должно было это сделать, зная, что большинство вкладок уже загружены из наконечника, поскольку приложение было перезагружено (в среднем) примерно раз в неделю, когда новый версия была загружена.
Я попробовал несколько предложений по использованию существующих механизмов локализации Apple, и все они имели серьезные недостатки, в том числе поддержку поддержки XCode 4.2 для локализованных nib - мои переменные соединения IBoutlet, как представляется, были правильно установлены в IB, но во время выполнения они часто были бы пустыми!?
Я завершил реализацию класса, который имитировал класс Apple NSLocalizedString, но который мог обрабатывать изменения во время выполнения, и всякий раз, когда изменение языка производилось пользователем класс отправил уведомление. Экраны, которые нуждались в локализованных строках (и изображениях) для изменения, объявили метод handleLocaleChange, который был вызван в viewDidLoad, и всякий раз, когда был опубликован LocaleChangedNotification.
Все мои кнопки и графика были разработаны независимо от языка, хотя текст заголовка и текст ярлыка обычно обновлялись в ответ на изменения локали. Если бы мне пришлось менять изображения, я мог бы сделать это в методах handleLocaleChange для каждого экрана, я полагаю.
Вот код. Он включает некоторую поддержку путей nib / bundle, которые я фактически не использую в конечном проекте.
MyLanguage.h // // MyLanguage.h // //
#import <Foundation/Foundation.h>
#define DEFAULT_DICTIONARY_FOR_STRINGS @""
#define ACCESSING_ALTERNATE_DICTIONARY_SETS_DEFAULT 1
#define LANGUAGE_ENGLISH_INT 0
#define LANGUAGE_SPANISH_INT 1
#define LANGUAGE_ENGLISH_SHORT_ID @"en"
#define LANGUAGE_SPANISH_SHORT_ID @"es"
#define LANGUAGE_CHANGED_NOTIFICATION @"LANGUAGE_CHANGED"
@interface MyLanguage : NSObject
{
NSString *currentLanguage;
NSDictionary *currentDictionary;
NSBundle *currentLanguageBundle;
}
+(void) setLanguage:(NSString *)languageName;
+(NSString *)stringFor:(NSString *)srcString forLanguage:(NSString *)languageName;
+(NSString *)stringFor:(NSString *)srcString;
+ (MyLanguage *)singleton;
@property (nonatomic, retain) NSBundle *currentLanguageBundle;
@property (nonatomic, retain) NSString *currentLanguage;
@property (nonatomic, retain) NSDictionary *currentDictionary;
@end
MyLanguage.m: // // MyLanguage.m
#import "MyLanguage.h"
#import "Valet.h"
#define GUI_STRING_FILE_POSTFIX @"GUIStrings.plist"
@implementation MyLanguage
@synthesize currentLanguage;
@synthesize currentDictionary;
@synthesize currentLanguageBundle;
+(NSDictionary *)getDictionaryNamed:(NSString *)languageName
{
NSDictionary *results = nil;
// for now, we store dictionaries in a PLIST with the same name.
NSString *dictionaryPlistFile = [languageName stringByAppendingString:GUI_STRING_FILE_POSTFIX];
NSString *plistBundlePath = [Valet getBundlePathForFileName:dictionaryPlistFile];
if ( [[NSFileManager defaultManager] fileExistsAtPath:plistBundlePath] )
{
// read it into a dictionary
NSDictionary *newDict = [NSDictionary dictionaryWithContentsOfFile:plistBundlePath];
results = [newDict valueForKey:@"languageDictionary"];
}// end if
return results;
}
+(NSString *)stringFor:(NSString *)srcString forDictionary:(NSString *)languageName;
{
MyLanguage *gsObject = [MyLanguage singleton];
// if default dictionary matches the requested one, use it.
if ([gsObject.currentLanguage isEqualToString:languageName])
{
// use default
return [MyLanguage stringFor:srcString];
}// end if
else
{
// get the desired dictionary
NSDictionary *newDict = [MyLanguage getDictionaryNamed:languageName];
// default is not desired!
if (ACCESSING_ALTERNATE_DICTIONARY_SETS_DEFAULT)
{
gsObject.currentDictionary = newDict;
gsObject.currentLanguage = languageName;
return [MyLanguage stringFor:srcString];
}// end if
else
{
// use current dictionary for translation.
NSString *results = [gsObject.currentDictionary valueForKey:srcString];
if (results == nil)
{
return srcString;
}// end if
return results;
}
}
}
+(void) setLanguage:(NSString *)languageName;
{
MyLanguage *gsObject = [MyLanguage singleton];
// for now, we store dictionaries in a PLIST with the same name.
// get the desired dictionary
NSDictionary *newDict = [MyLanguage getDictionaryNamed:languageName];
gsObject.currentDictionary = newDict;
gsObject.currentLanguage = languageName;
// now set up the bundle for nibs
NSString *shortLanguageIdentifier = @"en";
if ([languageName contains:@"spanish"] || [languageName contains:@"espanol"] || [languageName isEqualToString:LANGUAGE_SPANISH_SHORT_ID])
{
shortLanguageIdentifier = LANGUAGE_SPANISH_SHORT_ID;
}// end if
else
shortLanguageIdentifier = LANGUAGE_ENGLISH_SHORT_ID;
// NSArray *languages = [NSArray arrayWithObject:shortLanguageIdentifier];
// [[NSUserDefaults standardUserDefaults] setObject:languages forKey:@"AppleLanguages"];
//
NSString *path= [[NSBundle mainBundle] pathForResource:shortLanguageIdentifier ofType:@"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
gsObject.currentLanguageBundle = languageBundle;
[[NSNotificationCenter defaultCenter] postNotificationName:LANGUAGE_CHANGED_NOTIFICATION object:nil];
}
+(NSString *)stringFor:(NSString *)srcString;
{
MyLanguage *gsObject = [MyLanguage singleton];
// default is to do nothing.
if (gsObject.currentDictionary == nil || gsObject.currentLanguage == nil || [gsObject.currentLanguage isEqualToString:DEFAULT_DICTIONARY_FOR_STRINGS] )
{
return srcString;
}// end if
// use current dictionary for translation.
NSString *results = [gsObject.currentDictionary valueForKey:srcString];
if (results == nil)
{
return srcString;
}// end if
return results;
}
#pragma mark -
#pragma mark Singleton methods
static MyLanguage *mySharedSingleton = nil;
-(void) lateInit;
{
}
// PUT THIS METHOD DECLARATION INTO THE HEADER
+ (MyLanguage *)singleton;
{
if (mySharedSingleton == nil) {
mySharedSingleton = [[super allocWithZone:NULL] init];
[mySharedSingleton lateInit];
}
return mySharedSingleton;
}
+ (id)allocWithZone:(NSZone *)zone
{ return [[self singleton] retain]; }
- (id)copyWithZone:(NSZone *)zone
{ return self; }
- (id)retain
{ return self; }
- (NSUInteger)retainCount //denotes an object that cannot be released
{ return NSUIntegerMax; }
- (oneway void)release //do nothing
{ }
- (id)autorelease
{ return self; }
@end
просто используйте эту библиотеку, https://github.com/Baglan/MCLocalization , и вы можете легко изменить выбранный язык, на лету:)
Вы должны создать свой собственный макрос, похожий на NSLocalizedString, но основывает его на выбору из установленного NSUserDefaults значения (т. е. не беспокойтесь о значении значения по умолчанию для языка ящиков)
При изменении языка вы должны отправить уведомление, какие контроллеры представлений, представления и т. Д. Должны прослушивать и обновлять себя