localizable.strings not working with Arabic string - ios

I created a Localizable.strings file in XCode and then 2 languages in it.(english + Arabic )
I filled up these files with the language translations, but the just show translation in english, when I start with Arabic the key appear!
in my code :
NSLocalizedString("title", comment: "")
Localizable.strings(english)
"title" = "Error" ;
Localizable.strings(Arabic)
"title" = "خطأ" ;

I careated sample one and tried in Objective C.I got it.
I set "title" = "خطأ" in Arabic Localization files
"title" = "عنوان";
Now I have to change English to Arabic.
First I set the design in storyboard
Then Click Project.Choose Localization in Info
If you click the +(Below Localization) it shows the pop up view
Now choose Arabic.When click Arabic it shows window.You should click finish.
We need to create the string file for the localization now.I set string file name as LocalizationArabic
Once you create the String file it looks like below.
Then click File Inspector when pressing LocalizationArabic string file.Now click the Localization.It shows Empty Check box Arabic and English like below.
Here we must check the check box.Also when we check the check box the LocalizationArabic folder creates with three string files like below
Then I entered the language which I want to translate from English to Arabic in string file.
Finally I created the Header file for the Localization Language
The Header file name is LanguageHeader.It looks like below.
Now the code part starts here
First the Localization class of NSObject class
Localization.h
#import <Foundation/Foundation.h>
#import "LanguageHeader.h"
#interface Localization : NSObject
+(Localization *)sharedInstance;
+(NSString*) strSelectLanguage:(int)curLang;
+(NSString*) languageSelectedStringForKey:(NSString*) key;
#end
Localization.m
#import "Localization.h"
int currentLanguage,selectedrow;
#implementation Localization
+(Localization *)sharedInstance
{
static Localization *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[Localization alloc] init];
});
return sharedInstance;
}
+(NSString*) strSelectLanguage:(int)curLang{
if(curLang==ARABIC){
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"ar", nil]forKey:#"AppleLanguages"];
}
else{
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"en", nil]forKey:#"AppleLanguages"];
}
[[NSUserDefaults standardUserDefaults] synchronize];
currentLanguage=curLang;
NSString *strLangSelect = [[[NSUserDefaults standardUserDefaults]objectForKey:#"AppleLanguages"] objectAtIndex:0];
return strLangSelect;
}
+(NSString*) languageSelectedStringForKey:(NSString*) key
{
NSString *path;
NSString *strSelectedLanguage = [[[NSUserDefaults standardUserDefaults]objectForKey:#"AppleLanguages"] objectAtIndex:0];
//When we check with iPhone,iPad device it shows "en-US".So we need to change it to "en"
strSelectedLanguage = [strSelectedLanguage stringByReplacingOccurrencesOfString:#"en-US" withString:#"en"];
if([strSelectedLanguage isEqualToString:[NSString stringWithFormat: #"en"]]){
currentLanguage=ENGLISH;
selectedrow=ENGLISH;
path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
}
else{
currentLanguage=ARABIC;
selectedrow=ARABIC;
path = [[NSBundle mainBundle] pathForResource:#"ar" ofType:#"lproj"];
}
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* str=[languageBundle localizedStringForKey:key value:#"" table:#"LocalizationArabic"];
return str;
}
#end
Then ViewController.h
#import <UIKit/UIKit.h>
#import "Localization.h"
#interface ViewController : UIViewController{
Localization *localization;
}
#property (strong, nonatomic) IBOutlet UILabel *lblTitle;
- (IBAction)actionChangeLanguageToArabic:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize lblTitle;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
localization = [Localization sharedInstance];
lblTitle.text = [Localization languageSelectedStringForKey:#"title"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)actionChangeLanguageToArabic:(id)sender {
[Localization strSelectLanguage:ARABIC];
lblTitle.text = [Localization languageSelectedStringForKey:#"title"];
}
#end
Above code works perfectly.
Output Screen shot are below
When run the app first
After clicking the button

Related

obj-c app is not localized programmatically

I try to change the language programmatically through the extension of NSBundle -- NSBundle setLanguage: (NSString *) language. I have several application localizations.
Some localizations work correctly, but when installing Spanish and Italian languages, instead of localized strings, I see the names of the localizable.strings parameters. The same happens with some other languages, but not with all the lines.
Tell me please what can be the problem
UPD:
here is some of the lines in English:
"CLOSE_TITLE" = "Close";
"PAYMENT_TITLE" = "Pay";
"PAYMENT_TITLE_IN" = "Deposit";
and they are also in Spanish:
"CLOSE_TITLE" = "Cerrar";
"PAYMENT_TITLE" = "Retirar";
"PAYMENT_TITLE_IN" = "Depositar";
There is code of extension:
#import "NSBundle+Language.h"
#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
How are you dragging the new localization files to the project? I found out that when you add new languages to xCode, it's best to first remove the whole folder from xCode completely and then drag everything including the new languages back all at the same time :)
As it turned out, the code had a non-printable space character so part of the parameters after this character is not translated. XСode does not display this character. This can happen if you translate through web services. When I removed this symbol in all localizations, everything worked correctly.

How to change my Label language Without changing my system Language When Button press?

I am creating one sample demo for changing label Language when button pressed using localization.
I have tried so many times but could not able to display different language.
My Question is I want change my Language English to other language.
I have already create Localizable.string file but not able to change language.
NOTE:Do not change system(simulator)language.
You can create a Helper class. See the class I'm using below.
LocalizeHelper.h
#import <Foundation/Foundation.h>
// some macros (optional, but makes life easy)
// Use "LocalizedString(key)" the same way you would use "NSLocalizedString(key,comment)"
#define LocalizedString(key) [[LocalizeHelper sharedLocalSystem] localizedStringForKey:(key)]
// "language" can be (for american english): "en", "en-US", "english". Analogous for other languages.
#define LocalizationSetLanguage(language) [[LocalizeHelper sharedLocalSystem] setLanguage:(language)]
#define LocalizationGetLanguage() [[LocalizeHelper sharedLocalSystem] getLanguage]
#interface LocalizeHelper : NSObject
// a singleton:
+ (LocalizeHelper*) sharedLocalSystem;
// this gets the string localized:
- (NSString*) localizedStringForKey:(NSString*) key;
//set a new language:
- (void) setLanguage:(NSString*) lang;
//get current language
- (NSString *)getLanguage;
#end
LocalizeHelper.m
#import "LocalizeHelper.h"
#import "Constants.h"
// Singleton
static LocalizeHelper* SingleLocalSystem = nil;
// my Bundle (not the main bundle!)
static NSBundle* myBundle = nil;
#implementation LocalizeHelper
//-------------------------------------------------------------
// allways return the same singleton
//-------------------------------------------------------------
+ (LocalizeHelper*) sharedLocalSystem {
// lazy instantiation
if (SingleLocalSystem == nil) {
SingleLocalSystem = [[LocalizeHelper alloc] init];
}
return SingleLocalSystem;
}
//-------------------------------------------------------------
// initiating
//-------------------------------------------------------------
- (id) init {
self = [super init];
if (self) {
// use systems main bundle as default bundle
myBundle = [NSBundle mainBundle];
}
return self;
}
//-------------------------------------------------------------
// translate a string
//-------------------------------------------------------------
// you can use this macro:
// LocalizedString(#"Text");
- (NSString*) localizedStringForKey:(NSString*) key {
// this is almost exactly what is done when calling the macro NSLocalizedString(#"Text",#"comment")
// the difference is: here we do not use the systems main bundle, but a bundle
// we selected manually before (see "setLanguage")
return [myBundle localizedStringForKey:key value:#"" table:nil];
}
//-------------------------------------------------------------
// set a new language
//-------------------------------------------------------------
// you can use this macro:
// LocalizationSetLanguage(#"German") or LocalizationSetLanguage(#"de");
- (void) setLanguage:(NSString*) lang {
// path to this languages bundle
NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:#"lproj" ];
if (path == nil) {
// there is no bundle for that language
// use main bundle instead
myBundle = [NSBundle mainBundle];
} else {
// use this bundle as my bundle from now on:
myBundle = [NSBundle bundleWithPath:path];
// to be absolutely shure (this is probably unnecessary):
if (myBundle == nil) {
myBundle = [NSBundle mainBundle];
}
}
[[NSUserDefaults standardUserDefaults] setObject:lang forKey:#"lang"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
//-------------------------------------------------------------
// get current language
//-------------------------------------------------------------
- (NSString *)getLanguage {
return [[NSUserDefaults standardUserDefaults] objectForKey:#"lang"];
}
#end
When you want to change the language from your app call this function LocalizationSetLanguage(#"fr");.
In order to get a localized string, call LocalizedString(#"string");.

How to change Language runtime in iphone app?

I am using below code.
This code saves Locale name, but how refresh whole application with new language.
Here langcode variable is dynamic as per user selection.
NSString *langCode = #"fr";
NSArray *languages = nil;
languages = [NSArray arrayWithObject:langCode];
[[NSUserDefaults standardUserDefaults] setObject:languages forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
Which is remains for change the language in runtime? (From my app setting screen)
After making category on Language it will solve the issue.
.h file
#interface NSBundle (Language)
+ (void)setLanguage:(NSString *)language;
#end
.m file
#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);
[[NSNotificationCenter defaultCenter] postNotificationName:#"changeLanguage" object:self];
}
From my setting view controller called above method of the category class and also fire post notification for whole application.
[NSBundle setLanguage:langCode];
With adding the LocalizationSystem file in your code, you'll be able to change the language in run-time.
download link
http://dl.dropbox.com/u/2917666/LocalizationSystem/LocalizationSystem.h
http://dl.dropbox.com/u/2917666/LocalizationSystem/LocalizationSystem.m
for more information http://aggressive-mediocrity.blogspot.com/2010/03/custom-localization-system-for-your.html
Store this variable in ApplicationDelegate file. Define its property and use anytime via appDelegate instance when you login. Once selection is changed, change the application bundle as you desire also modify the text and all.
put the language selection code whether in loop or button selection as per your requirement.
NSPath* path = [[NSBundle mainBundle] pathForResource:#"es" ofType:#"lproj"];
APP_DELEGATE.strLanguageSelectedFromLoginView = #"SPANISH";
or
NSPath* path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
APP_DELEGATE.strLanguageSelectedFromLoginView = #"ENGLISH";
and
languageBundle = [NSBundle bundleWithPath:path];
and finally load the laguageBundle.
NSBundle* languageBundle to be defined in app delegate.

Changing language of iOS app [duplicate]

When I change the app used language independently on the device language it doesn't take effect until I close the app and restart it. How to not require app to be restarted for loading all nib files and .strings files again depending on the selected language?
I use this to change language at runtime:
NSArray* languages = [NSArray arrayWithObjects:#"ar", #"en", nil];
[[NSUserDefaults standardUserDefaults] setObject:languages forKey:#"AppleLanguages"];
This works for me :
Swift 4 :
Create a file named BundleExtension.swift and add the following code to it -
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)
}
}
Now whenever you need to change the language call this method :
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()
}
I had a similar requirement for a Kiosk mode iPad app with tabbed navigation. Not only did the app need to support on-the-fly language changes, but had to do so knowing that most of the tabs were already loaded from the nibs since the app was only restarted (on average) about once a week when a new version was loaded.
I tried several suggestions to leverage the existing Apple localization mechanisms and they all had serious drawbacks, including wonky support in XCode 4.2 for localized nibs -- my IBoutlet connection variables would appear to be set correctly in IB, but at runtime they would often be null!?
I wound up implementing a class that mimicked the Apple NSLocalizedString class but which could handle runtime changes, and whenever a language change was made by a user my class posted a notification. Screens that needed localized strings (and images) to change declared a handleLocaleChange method, which was called at viewDidLoad, and whenever the LocaleChangedNotification was posted.
All of my buttons and graphics were designed to be language independent, although the title text and label text was typically updated in response to locale changes. If I had to change images, I could have done so in the handleLocaleChange methods for each screen, I suppose.
Here is the code. It includes some support for nib/bundle paths which I actually don't use in the final project.
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
Don't rely on strings that you have set in your nib file. Use your nib only for layout & setup of views. Any string that is shown to the user (button text, etc) needs to be in your Localizable.strings files, and when you load your nib you need to set the text on the corresponding view/control accordingly.
To get the bundle for the current language:
NSString *path = [[NSBundle mainBundle] pathForResource:currentLanguage ofType:#"lproj"];
if (path) {
NSBundle *localeBundle = [NSBundle bundleWithPath:path];
}
And to use the bundle to obtain your localized strings:
NSLocalizedStringFromTableInBundle(stringThatNeedsToBeLocalized, nil, localeBundle, nil);
Also for date formatting, you might want to look into
[NSDateFormatter dateFormatFromTemplate:#"HH:mm:ss"" options:0 locale:locale];
To use that you will need to create a NSLocale for the corresponding language/country which you wish to use.
Heres what I did. I guess the trick was to use NSLocalizedStringFromTableInBundle instead of NSLocalizedString.
For all strings, use this
someLabel.text = NSLocalizedStringFromTableInBundle(#"Your String to be localized, %#",nil,self.localeBundle,#"some context for translators");
To change language, run this code
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"] ];
}
After this, you will likely want to call whatever update code to update the strings to the new languages, for e.g. run this again
someLabel.text = NSLocalizedStringFromTableInBundle(#"Your String to be localized, %#",nil,self.localeBundle,#"some context for translators");
Thats all. No need restart app. Compatible with system settings as well (if you set a language through iOS settings, it will work too). No need external library. No need jailbreak. And it works with genstrings too.
Of course, you should still do the usual for your app settings to persist:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"zh-Hans", nil] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
(and do a check in your viewDidLoad or something)
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"] ];
}
You should create your own macro similar to NSLocalizedString but bases the bundle it chooses a string from on a NSUserDefaults value you set (i.e. don't worry about what the value of apples language defaults value is)
When you change the language you should send out a notification, which view controllers, views etc should listen for and refresh themselves

How to localize text based on criterion other than language

I have an application which will be marketed in different European countries. We've gone through the process of localizing the application so that its strings are maintained in the language-specific .lproj files in the Settings.bundle. This all works fine. The problem is that there are some strings which don't key off language, but off the country where the app is run. For example, there are strings which differ between the Austrian version of the app and the German version of the app, even though both these countries speak German. When it's run for the first time, the app asks the user which country it's running in.
Is there a way in which I can maintain these country-specific strings in a resource file, and have the resource file used at run time be decided by a user setting, in this case the country where the app is running, rather than the device language?
Thanks,
Peter Hornby
Define two bundles on a singleton, fallback and preferred...
#import <Foundation/Foundation.h>
#interface Localization : NSObject
#property (nonatomic, retain) NSString* fallbackCountry;
#property (nonatomic, retain) NSString* preferredCountry;
#property (nonatomic, retain) NSDictionary* fallbackCountryBundle;
#property (nonatomic, retain) NSDictionary* preferredCountryBundle;
+(Localization *)sharedInstance;
- (NSString*) countryStringForKey:(NSString*)key;
#end
#import "Localization.h"
#implementation Localization
#synthesize fallbackCountryBundle, preferredCountryBundle;
#synthesize fallbackCountry, preferredCountry;
+(Localization *)sharedInstance
{
static dispatch_once_t pred;
static Localization *shared = nil;
dispatch_once(&pred, ^{
shared = [[Localization alloc] init];
[shared setFallbackCountry:#"country-ES"];
NSLocale *locale = [NSLocale currentLocale];
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
[shared setPreferredCountry:[NSString stringWithFormat:#"country-%#",countryCode]];
});
return shared;
}
-(void) setFallbackCountry:(NSString*)country
{
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:country ofType:#"strings"];
self.fallbackCountryBundle = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
trace(#"Fallback: %# %#",[bundlePath lastPathComponent], self.fallbackCountryBundle);
}
-(void) setPreferredCountry:(NSString*)country
{
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:country ofType:#"strings"];
self.preferredCountryBundle = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:nil];
if (!exists) warn(#"%#.strings %#", country, exists ? #"FOUND" : #"NOT FOUND");
trace(#"Preferred: %# %#",[bundlePath lastPathComponent], self.preferredCountryBundle);
}
- (NSString*) countryStringForKey:(NSString*)key
{
NSString* result = nil;
if (preferredCountryBundle!=nil) result = [preferredCountryBundle objectForKey:key];
if (result == nil) result = [fallbackCountryBundle objectForKey:key];
if (result == nil) result = key;
return result;
}
#end
Then call it from a macro function
#define countryString(key) [[Localization sharedInstance]countryStringForKey:key];
Write a default file for ES, and one file per supported language. eg:
/*
country-ES.strings
*/
"hello" = "hello";
And just get the value for the key:
countryString(#"hello");

Resources