I have been facing a problem with iOS Programming since its the first time I'm using Localization,,
I used the following to know which lang I'm having:
- (NSString*) getLanguage{
NSArray* languages = [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"];
NSString *preferredLang = [languages objectAtIndex:0];
return preferredLang;}
and there is button to change the lang That calls function to change the lang
- (void) setLanguage:(NSString*) l{
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:l, nil] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults]synchronize];}
The language changes successfully, but I need to close and reopen the app to see the result, is there any other way to change the localisation automatically after switching lang?
NOTE: it should woke with storyboards too.
As #Tudorizer describes in this link after you translate your files
use this solution:
Put this macro in the Prefix.pch:
#define currentLanguageBundle [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:[[NSLocale preferredLanguages] objectAtIndex:0] ofType:#"lproj"]]
and where ever you need a localized string use:
NSLocalizedStringFromTableInBundle(#"GalleryTitleKey", nil, currentLanguageBundle, #"");
To set the language use:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObject:#"de"] forKey:#"AppleLanguages"];
Multiple times:
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, #""));
You can switch between languages in your application using the following class:
(Note that anyway you should refresh content of all controllers stored in memory to get full effect.)
// AMLocalization.h
#define SetAppLanguage(language) [[AMLocalization sharedLocalization] setLanguage:language]
#define GetAppLanguage() [[AMLocalization sharedLocalization] language]
#define LSC(key, comment) [[AMLocalization sharedLocalization] localizedStringForKey:(key) value:(comment)]
#define UDKeyAppLanguage #"UDKeyAppLanguage"
typedef enum {
UILanguageUnknown,
UILanguageEnglish,
UILanguageFrench,
..................
} UILanguage;
#interface AMLocalization : NSObject
+ (AMLocalization *)sharedLocalization;
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment;
- (BOOL) setLanguage:(UILanguage) lang;
- (UILanguage) language;
#end
// AMLocalization.m
#import "AMLocalization.h"
static AMLocalization *_s_sharedLocalization = nil;
#implementation AMLocalization {
NSBundle *_bundle;
NSDictionary *_languageSet;
}
+ (AMLocalization *)sharedLocalization
{
static dispatch_once_t once;
dispatch_once(&once, ^{
_s_sharedLocalization = [AMLocalization new];
});
return _s_sharedLocalization;
}
- (id) init
{
if (self = [super init]) {
_bundle = [NSBundle mainBundle];
_languageSet = #{
#(UILanguageEnglish) : #"en",
#(UILanguageFrench) : #"fr"
};
}
return self;
}
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment
{
return [_bundle localizedStringForKey:key value:comment table:nil];
}
- (BOOL) setLanguage:(UILanguage) lang
{
UILanguage activeLang = [self language];
if (lang == UILanguageUnknown) {
_bundle = [NSBundle mainBundle];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:UDKeyAppLanguage];
}
else {
NSString *path = [[NSBundle mainBundle] pathForResource:_languageSet[#(lang)] ofType:#"lproj"];
_bundle = [NSBundle bundleWithPath:path];
[[NSUserDefaults standardUserDefaults] setInteger:lang forKey:UDKeyAppLanguage];
}
[[NSUserDefaults standardUserDefaults] synchronize];
return activeLang != lang;
}
- (UILanguage) language
{
UILanguage lang = [[NSUserDefaults standardUserDefaults] integerForKey:UDKeyAppLanguage];
if (lang == UILanguageUnknown) {
NSString* preferredLang = [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"][0];
for (NSNumber *key in _languageSet) {
if ([_languageSet[key] isEqualToString:preferredLang]) {
lang = [key integerValue];
break;
}
}
}
return lang;
}
#end
Related
The problem is that we want to toggle the language at run time.
We have an application supporting English and arabic.
What I am doing is changing the "AppleLanguages" and then reseting the root view controller.
I have used following code :
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* languages = [userDefaults objectForKey:#"AppleLanguages"];
NSString *language = nil;
if([[languages objectAtIndex:0] isEqual:#"en"]) {
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"ar", #"en",nil] forKey:#"AppleLanguages"];
language = #"ar";
} else {
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"en",#"ar", nil] forKey:#"AppleLanguages"];
language = #"en";
}
UIViewController *initViewController = [storyBoard instantiateInitialViewController];
[appDelegate.window setRootViewController:initViewController];
appDelegate.window.rootViewController = [storyBoard instantiateViewControllerWithIdentifier:#"Home"];
appDelegate.window.backgroundColor = [UIColor whiteColor ];
[appDelegate.window makeKeyAndVisible];
The native localization changes but have to restart the app to see the effect on UI.
Thanks in Advance.
The problem was that the language change was not reflecting on bundle.
So I was unable to change the UI as well as localized text.
We can use following code to change the bundle settings.
The following code will toggle the language at run time.
Need to subclass the NSBundle class as shown bellow:-
#implementation BundleEx
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
{
NSBundle *bundle = objc_getAssociatedObject(self, &kBundleKey);
if (bundle) {
return [bundle localizedStringForKey:key value:value table:tableName];
}
else {
return [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]);
});
BOOL appleTextDirection = NO;
BOOL rightToLeftWritingDirection = NO;
if ([language isEqual:#"ar"]) {
rightToLeftWritingDirection =YES;
appleTextDirection = NO;
if ([[[UIView alloc] init] respondsToSelector:#selector(setSemanticContentAttribute:)]) {
[[UIView appearance] setSemanticContentAttribute:
UISemanticContentAttributeForceRightToLeft];
}
}else {
rightToLeftWritingDirection =NO;
appleTextDirection = YES;
if ([[[UIView alloc] init] respondsToSelector:#selector(setSemanticContentAttribute:)]) {
[[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
}
}
[[NSUserDefaults standardUserDefaults] setBool:appleTextDirection forKey:#"AppleTextDirection"];
[[NSUserDefaults standardUserDefaults] setBool:rightToLeftWritingDirection forKey:#"NSForceRightToLeftWritingDirection"];
[[NSUserDefaults standardUserDefaults] synchronize];
id value = language ? [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:#"lproj"]] : nil;
objc_setAssociatedObject([NSBundle mainBundle], &kBundleKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
And the code used to toggle:-
-(void)toggleTheLanguageWith:(NSString *)identifier{
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* languages = [userDefaults objectForKey:#"AppleLanguages"];
NSString *language = nil;
if ([[languages objectAtIndex:0] rangeOfString:#"en"].location != NSNotFound) {
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"ar",#"en",nil] forKey:#"AppleLanguages"];
language = #"ar";
}else if ([[languages objectAtIndex:0] rangeOfString:#"ar"].location != NSNotFound) {
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"en",#"ar", nil] forKey:#"AppleLanguages"];
language = #"en";
}
[[NSUserDefaults standardUserDefaults]synchronize];
[NSBundle setLanguage:language];
UIStoryboard *mystoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
self.window.rootViewController = [mystoryboard instantiateViewControllerWithIdentifier:identifier];
[self.window makeKeyAndVisible];
[UIView transitionWithView:self.window duration:1 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
} completion:^(BOOL finished) {
}];
}
Is it possible to use a .String file like the Localizable.String but the language change with a button in the application instead of language in settings ?
If not, is there a way to indicate how to use the EN language in the FR langage for example :
In the FR language or EN language, I use => FXFormFieldTitle:NSLocalizedString(#"SMSInfo", #"")
And respectively I have that line in the FR localizable.String
/*
File.strings
Formbox
Created by OlostA on 04/11/2016.
Copyright © 2016 NewTelApps. All rights reserved.
*/
"SMSInfo" = "Souhaitez-vous recevoir par sms les informations de la marque ?";
And that line in the EN localizable.String
/*
File.strings
Formbox
Created by OlostA on 04/11/2016.
Copyright © 2016 NewTelApps. All rights reserved.
*/
"SMSInfo" = "Do you want to receive the brand informations by sms?";
So question. If I am in the FR language, is there a way to use the SMSInfo in the localizable.strings EN ?
Yes this is possible to change language from app.
[LanguageManager saveLanguageByIndex:indexPath.row andCode:model.strLanguageCode];
LanguageManager.h
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, ELanguage)
{
ELanguageEnglish,
ELanguageSpanish,
ELanguageFrench,
ELanguageGerman,
ELanguageItalian,
ELanguageArabic,
ELanguageChinese,
ELanguageCount
};
#interface LanguageManager : NSObject
+ (void)setupCurrentLanguage;
+ (NSArray *)languageStrings;
+ (NSString *)currentLanguageString;
+ (NSString *)currentLanguageCode;
+ (NSInteger)currentLanguageIndex;
+ (void)saveLanguageByIndex:(NSInteger)index andCode:(NSString *)strCode;
+ (BOOL)isCurrentLanguageRTL;
#end
LanguageManager.m
#import "LanguageManager.h"
#import "NSBundle+Language.h"
static NSString * const LanguageCodes[] = { #"en", #"es", #"fr", #"de",#"it",#"ar" ,#"zh-Hans"};
static NSString * const LanguageStrings[] = { #"English", #"Spanish", #"French", #"German",#"Italian",#"Arabic",#"Chinese"};
static NSString * const LanguageSaveKey = #"currentLanguageKey";
#implementation LanguageManager
+ (void)setupCurrentLanguage
{
NSString *currentLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:LanguageSaveKey];
if (!currentLanguage) {
NSArray *languages = [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"];
if (languages.count > 0) {
currentLanguage = languages[0];
[[NSUserDefaults standardUserDefaults] setObject:currentLanguage forKey:LanguageSaveKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
#ifndef USE_ON_FLY_LOCALIZATION
[[NSUserDefaults standardUserDefaults] setObject:#[currentLanguage] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
#else
[NSBundle setLanguage:currentLanguage];
#endif
}
+ (NSArray *)languageStrings
{
NSMutableArray *array = [NSMutableArray array];
for (NSInteger i = 0; i < ELanguageCount; ++i) {
[array addObject:NSLocalizedString(LanguageStrings[i], #"")];
}
return [array copy];
}
+ (NSString *)currentLanguageString
{
NSString *string = #"";
NSString *currentCode = [[NSUserDefaults standardUserDefaults] objectForKey:LanguageSaveKey];
for (NSInteger i = 0; i < ELanguageCount; ++i) {
if ([currentCode isEqualToString:LanguageCodes[i]]) {
string = NSLocalizedString(LanguageStrings[i], #"");
break;
}
}
return string;
}
+ (NSString *)currentLanguageCode
{
return [[NSUserDefaults standardUserDefaults] objectForKey:LanguageSaveKey];
}
+ (NSInteger)currentLanguageIndex
{
NSInteger index = 0;
NSString *currentCode = [[NSUserDefaults standardUserDefaults] objectForKey:LanguageSaveKey];
for (NSInteger i = 0; i < ELanguageCount; ++i) {
if ([currentCode isEqualToString:LanguageCodes[i]]) {
index = i;
break;
}
}
return index;
}
+ (void)saveLanguageByIndex:(NSInteger)index andCode:(NSString *)strCode
{
if (index >= 0 && index < ELanguageCount) {
[[NSUserDefaults standardUserDefaults] setObject:strCode forKey:LanguageSaveKey];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSUserDefaults standardUserDefaults] setObject:#[strCode] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSBundle setLanguage:strCode];
}
}
+ (BOOL)isCurrentLanguageRTL
{
NSInteger currentLanguageIndex = [self currentLanguageIndex];
return ([NSLocale characterDirectionForLanguage:LanguageCodes[currentLanguageIndex]] == NSLocaleLanguageDirectionRightToLeft);
}
I have following code to switch language runtime:
-(void) switchToLanguage:(NSString *)lang{
self.language = lang;
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:self.language, nil]
forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
And I have a Helper function that retrieves localised strings:
+(NSString *) getLocalizedString:(NSString *)key{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:appDelegate.language];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
return [dict objectForKey:key];
}
This is working. My storyboards are also localised, but they are not changing when I switch to another language.
How can I get localised values for the storyboard strings?
Changing the Language at run time is a bit tricky.
This is the best way I've used to do so with the help of this tiny class:
Language.m:
#import "Language.h"
#implementation Language
static NSBundle *bundle = nil;
+(void)initialize {
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:#"AppleLanguages"];
NSString *current = [languages objectAtIndex:0];
[self setLanguage:current];
}
+(void)setLanguage:(NSString *)l
{
NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:#"lproj" ];
bundle = [NSBundle bundleWithPath:path];
}
+(NSString *)get:(NSString *)key alter:(NSString *)alternate
{
return [bundle localizedStringForKey:key value:alternate table:nil];
}
#end
Language.h:
import <Foundation/Foundation.h>
#interface Language : NSObject
+(void)setLanguage:(NSString *)l;
+(NSString *)get:(NSString *)key alter:(NSString *)alternate;
#end
When you want to change the Language:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"en", #"de", nil] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
[Language setLanguage:#"en"];
[(AppDelegate *)[[UIApplication sharedApplication] delegate] window].rootViewController = [self.storyboard instantiateInitialViewController];
When you want to set a string:
[self.someButton setTitle:[Language get:#"Some Button Text" alter:nil] forState:UIControlStateNormal];
When using my app in a foreign country, the google GMSGeocoder is returning the response in local language automatically. how can I set it to always return the the response in English?
Im using GMS SDK 1.7 and my code is something like this:
GMSGeocoder *geoCoder = [[GMSGeocoder alloc] init];
[geoCoder reverseGeocodeCoordinate:self.cellLocation.coordinate completionHandler:^(GMSReverseGeocodeResponse *respones, NSError *err) {
if([respones firstResult]) {
GMSAddress* address = [respones firstResult];
NSString* fullAddress = [NSString stringWithFormat:#"%#, %#",address.thoroughfare, address.locality];
self.theTextField.text = fullAddress;
} else {
self.theTextField.text = #"";
}
}];
Using a GMSGeocoder category can solve this issue, inspired by #DaNLtR
After that , It can set geocoder result as English .
#implementation GMSGeocoder (Load)
+(void)load {
[[self class] setUserLanguage:#"en-CN"];// set your wanted language.
NSLog(#"GMSGeocoder + load!");
}
- (void)dealloc {
[[self class] resetSystemLanguage];
NSLog(#"GMSGeocoder + dealloc!");
}
+ (void)setUserLanguage:(NSString *)userLanguage
{
if (!userLanguage.length) {
[[self class] resetSystemLanguage];
return;
}
[[NSUserDefaults standardUserDefaults] setValue:userLanguage forKey:#"UserLanguage"];
[[NSUserDefaults standardUserDefaults] setValue:#[userLanguage] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
+ (void)resetSystemLanguage
{
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"UserLanguage"];
[[NSUserDefaults standardUserDefaults] setValue:nil forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#end
Why should in category?
A:I tested setLanguage: before GMSGeocoder reverseGeocodeCoordinate method, it can't affect geocoder result. After I saw DaNLtR's answer , I think we can setLanguge in load method.
Why should reset language?
A:Avoide affect other module or framework .
Just change it to your language parameters
int main(int argc, char * argv[])
{
#autoreleasepool {
//For Google Maps hebrew response
//[[NSUserDefaults standardUserDefaults] setObject:#[#"en"] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] setObject:#[#"he",#"he-IL"] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
if ([objGlobalUser.strAppLangID isEqualToString:[#"en" uppercaseString]]) {
objGlobalUser.strLanguage =#"en";
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:objGlobalUser.strLanguage, nil] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"preferredLang: %#", objGlobalUser.strLanguage);
} else {
objGlobalUser.strLanguage =#"ar";
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:objGlobalUser.strLanguage, nil] forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
NSString *path= [[ NSBundle mainBundle ] pathForResource:objGlobalUser.strLanguage ofType:#"lproj" ];
self.viewController = [[MyViewController alloc] initWithNibName:#"MyViewController"
bundle:[NSBundle bundleWithPath:path]];
It only shows strings not images when language change
As for me, I've done all manipulations with localization in main.m and it works fine.
Here's an example:
//Localization Language setup
typedef enum {
LLAuto = 0,
LLEnglish = 1,
LLFrench = 2,
LLJapanese = 3,
LLSpanish = 4
} TLocalizationLanguage;
static NSString * const LocalizationLanguages[] = {
#"auto", #"en", #"fr", #"ja", #"es"
};
void setupLocalizationLanguage() {
TLocalizationLanguage UsedLng = (TLocalizationLanguage) USE_LOCALIZATION_LANGUAGE;
if (UsedLng == LLAuto) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"AppleLanguages"];
} else {
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:LocalizationLanguages[UsedLng], nil] forKey:#"AppleLanguages"];
}
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"Use localization language: %#", LocalizationLanguages[UsedLng]);
}
// -------------------------------------------------------------------------------
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
setupLocalizationLanguage();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
where USE_LOCALIZATION_LANGUAGE is an environment variable.
I hope it will be useful to you.