Ответ 1
У меня было аналогичное требование для iPad-приложения с режимом киоска с вкладкой навигации. Приложение не только нуждалось в поддержке языковых изменений на лету, но и должно было это сделать, зная, что большинство вкладок уже загружены из наконечника, поскольку приложение было перезагружено (в среднем) примерно раз в неделю, когда новый версия была загружена.
Я попробовал несколько советов по использованию существующих механизмов локализации Apple, и все они имели серьезные недостатки, в том числе поддержку поддержки XCode 4.2 для локализованных nib - мои переменные соединения IBoutlet, по-видимому, корректно устанавливались в IB, но во время выполнения они часто имеют значение null!?
Я завершил реализацию класса, который имитировал класс 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