Programmatic language localisation without restart - ios

I want to create an iPhone application with English and Arabic language. I checked the Internationalization document for language switcher, however to take that into effect I have to manually go and change the iPhone setting. I don't want to do that. So what I am planning is on home screen I will have two button as English and Arabic. If user click Arabic, I will have arabic text and if user select English, app will be in english.
Any idea/ suggestion how to get this done?
Note: I don't want to manually go and change the language.
Edit 1
As per #Jano, I have done below.
Created new project. Added Arabic in Localization. Now I have two storyboard and two InfoPlist.strings file.
Added Localization.h and .m file as shown in answer.
Directory structure is MyProject-ar.lproj & MyProject-en.lproj
Content of Plist are "myButton01" = "Back"; & "myButton01" = "ظهر";
First View Controller have two button as English and Arabic. Called action on those button.
- (IBAction)pressedEnglish:(id)sender {
[Localization sharedInstance].fallbackLanguage = #"ar";
[Localization sharedInstance].preferredLanguage = #"en";
NSLog(#"pressed english");
}
- (IBAction)pressedArabic:(id)sender {
[Localization sharedInstance].fallbackLanguage = #"en";
[Localization sharedInstance].preferredLanguage = #"ar";
NSLog(#"pressed arabic");
}
In second view controller, I added one button and gave name as myButton. Now in viewDidLoad, I have
[self.myButton setTitle:localize(#"myButton01") forState:UIControlStateNormal];
I hope this should be working, however when I run the project, I see button as myButton01
Any reason why this is happening?
Edit 2
I got Edit 1 problem. I renamed InfoPlist.strings to Localizable.strings and it worked. But but but, I am still getting Arabic text irrespective of whatever button I press.
When finding reason, I found that it was because of below statement that we have in Localization.m
static Localization *shared = nil;
dispatch_once(&pred, ^{
shared = [[Localization alloc] init];
shared.fallbackLanguage = #"en";
shared.preferredLanguage = #"ar";
The problem is at last two lines. As we have set Arabic as preferredLanguage, I am always seeing the arabic text.
What changes will I need to do so that I can have it as changeable as per button pressed.

You want to set the language of the app from the app UI ignoring the user preference on the device. This is unusual, but here you go...
First write all your language strings on a directory structure like this:
i18n/en.lproj/Localizable.strings
i18n/ar.lproj/Localizable.strings
Create an additional directory with the corresponding two letter code for each additional language supported.
If the files are recognized as i18n resources, they will be presented like this:
Files will have a key=value with the following format:
"button.back" = "ظهر";
In your code, replace any localizable string with the key. Example:
[self.stateBtn setTitle:localize(#"button.back") forState:UIControlStateNormal];
Usually you would use NSLocalizedString(#"key",#"fallback") but since you want to ignore iPhone settings, I wrote a localize(#"key") macro above that will have the following implementation:
Localization.h
#ifndef localize
#define localize(key) [[Localization sharedInstance] localizedStringForKey:key]
#endif
#interface Localization : NSObject
#property (nonatomic, retain) NSBundle* fallbackBundle;
#property (nonatomic, retain) NSBundle* preferredBundle;
#property (nonatomic, copy) NSString* fallbackLanguage;
#property (nonatomic, copy) NSString* preferredLanguage;
-(NSString*) localizedStringForKey:(NSString*)key;
-(NSString*) pathForFilename:(NSString*)filename type:(NSString*)type;
+(Localization*)sharedInstance;
#end
Localization.m
#import "Localization.h"
#implementation Localization
+(Localization *)sharedInstance
{
static dispatch_once_t pred;
static Localization *shared = nil;
dispatch_once(&pred, ^{
shared = [[Localization alloc] init];
[shared setPreferred:#"en" fallback:#"ar"];
});
return shared;
}
-(void) setPreferred:(NSString*)preferred fallback:(NSString*)fallback
{
self.fallbackLanguage = fallback;
self.preferredLanguage = preferred;
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"Localizable" ofType:#"strings" inDirectory:nil forLocalization:self.fallbackLanguage];
self.fallbackBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];
bundlePath = [[NSBundle mainBundle] pathForResource:#"Localizable" ofType:#"strings" inDirectory:nil forLocalization:self.preferredLanguage];
self.preferredBundle = [[NSBundle alloc] initWithPath:[bundlePath stringByDeletingLastPathComponent]];
}
-(NSString*) pathForFilename:(NSString*)filename type:(NSString*)type
{
NSString *path = [self.preferredBundle pathForResource:filename ofType:type inDirectory:nil forLocalization:self.preferredLanguage];
if (!path) path = [self.fallbackBundle pathForResource:filename ofType:type inDirectory:nil forLocalization:self.fallbackLanguage];
if (!path) NSLog(#"Missing file: %#.%#", filename, type);
return path;
}
-(NSString*) localizedStringForKey:(NSString*)key
{
NSString* result = nil;
if (_preferredBundle!=nil) {
result = [_preferredBundle localizedStringForKey:key value:nil table:nil];
}
if (result == nil) {
result = [_fallbackBundle localizedStringForKey:key value:nil table:nil];
}
if (result == nil) {
result = key;
}
return result;
}
#end
This will use lookup the key strings in the arabic file, and if the key is missing, it will look in the arabic file. If you want it the other way, do the following from your button handlers:
[[Localization sharedInstance] setPreferred:#"ar" fallback:#"en"];
Sample project at Github.
If localisation doesn't work
If localisation doesn't work, use the plutil command line tool to verify the format of the file. It should output: Localizable.strings: OK. Example:
$ plutil -lint Localizable.strings
Localizable.strings: OK
This format is described in Internationalization Programming Topics > Localizing String Resources. You can optionally add // single-line or /* multi-line */ comments. For non latin languages it’s recommended to encode Localized.strings in UTF-16. You can convert between encodings in the inspector pane of XCode.
If it still doesn't work, check that you are copying the Localizable.strings file in the Copy files phase of your target. Note that when you add Localizable.strings files there, sometimes they appear in red, keep doing it until a file appears in black, then delete the red ones (hacky I know, blame Xcode).

Related

iOS - How to change app language in storyboard strings without programmatically?

I am developing an app in which an user can change language at any time. I have done this in a demo app. It is working fine. But there is an issue with it, that is for localisation app I have to do following 2 tasks.
Step 1: I have to create IB outlet for every object (i.e in every class ==> buttons, labels, textFields, TextViews).
Step 2 : I have to set language for every objects for example : [myButton setTitle:[Language get:#"Language Test" alter:nil] forState:UIControlStateNormal];
OR myLabel.text=LocalizedString(#"Forgot Passoword");
I have a project which has many screens, around 60 screens (in storyboard). Unfortunately in this project there is no IB outlet for all objects. Strings are set in via storyboard. Now I have to make support this app in another language also (current language english )name Arabic language.
Conclusion : If I do above 2 steps (create IB outlet for every objects
, then write code in .m file for every object). It will take very much
time. Is there any other better options please ? I want do not want
create IB outlet and coding in .m file. Any suggestion will be great!!
What I have done till now :
Follow these tuttorials :
(1). How to force NSLocalizedString to use a specific language
and sample projects :
(1). https://github.com/tonisalae/TSLanguageManager
(2). https://github.com/object2dot0/Advance-Localization-in-ios-apps
My code :
example calls:
[Language setLanguage:#"it"];
OR
[Language setLanguage:#"de"];
+(void)initialize {
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:#"AppleLanguages"];
NSString *current = [languages objectAtIndex:0];
[self setLanguage:current];
}
+(void)setLanguage:(NSString *)l {
NSLog(#"\n\n\t ***** Hint Lang. selected by user: %# ****\n\n", 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];
}
You can achieve this using User Defined Runtime Attribute. Follow this link for detail.

Zendesk iOS SDK multilanguage support

are there anyone familiar with Zendesk iOS SDK? I am trying to integrate this SDK to my iOS app and the integration was done really issue, the only problem I have currently is multi language support. My app have a view when you can change the language on the fly using this code:
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:lang forKey:#"lang"];
[defaults synchronize];
It changes the lang in app successfully, but it is not changing for Zendesk View. How to change language for Zendesk Chat View also?
Thanks!
The answer would be to override ZDCChatStrings bundle with your custom class, and whenever it will call for localizedStringForKey method return translation from your internal translations list. Code:
#import "NSBundle+Language.h"
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
#interface CustomBundle : NSBundle
#end
#implementation CustomBundle
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value
table:(NSString *)tableName
{
return NSLocalizedStringFromTable(key, g_appDelegate.lang, key);
}
#end
#implementation NSBundle (Language)
+ (void)registerBundle
{
static dispatch_once_t tempToken;
dispatch_once(&tempToken, ^{
NSBundle *bundle = [NSBundle bundleWithPath: [[NSBundle mainBundle] pathForResource: #"ZDCChatStrings" ofType: #"bundle"]];
object_setClass(bundle, [CustomBundle class]);
});
}
#end
Initialize somewhere in your AppDelegate:
[NSBundle registerBundle];
You need to change the Localizable string in ZenDesk Api Files not your custom file. But this is Internationalization. Language change according to your phone Language.
Goto Your Projects Supporting Files. There are ZDCChatStrings.bundle and ZDCChat.bundle are there. Open ZDCChatStrings.bundle. In this you see Localizable.strings for All languages.
Right click on strings files which you want and Open as Ascii Property List. Change strings accordingly.

Multi Language in iOS?

I want to make my app in two language.I have tried lots of localization code but it doesn't work. please help me i am not using storyboard . i have more than 15 xib files.
when i have made in single language i take a constant file and describes all label and button text.
but after some time i have two make app in two language .In my app i have option to select language by user.
my infoPlist.strings code given below
for turkish lang
"TT_TEXT" = "merhaba";
for English lang
"TT_TEXT" = "hello";
and at calling point i write
[self setTitleOnNavBar:NSLocalizedString(#"TT_TEXT", nil)];
it treats TT_TEXT as string.
Try with this code -
NSString *bundle_path = [[NSBundle mainBundle] pathForResource:#"Localizable" ofType:#"strings"inDirectory:nil forLocalization:#"en"];
NSBundle *localized_bundle = [[NSBundle alloc] initWithPath:[bundle_path stringByDeletingLastPathComponent]];
NSString *str = NSLocalizedStringFromTableInBundle(#"TT_TEXT",nil,localized_bundle, nil);
[self setTitleOnNavBar:str];
write your language country code in place of #"en" and use it.

ios Dynamic localization using storyboard localizable string

I have 2 languages in my app, English and Dutch, the point is i want the language to change dynamically, lets say on a button click.
Is there a way to do this without having to write NSLocalizedString(#"key",#"comment") in code ?? i.e: using only storyboard, and somehow make the storyboard refresh it self on button click or something.
Any help will be very much appreciated.
Use Singleton to Call you Local String.
first to set the language type By you Button;
- (void)setLanguageType:(LanguageType)languageType {
if (_languageType != languageType) {
_languageType = languageType;
[self setBundleForName:bundleForType(languageType)];
//POST notification if necessary
}
}
- (void)setBundleForName:(NSString* )name {
NSString* path = [[ NSBundle mainBundle ] pathForResource:name ofType:#"lproj"];
_bundle = [NSBundle bundleWithPath:path];
}
and then,
Get the string by localizedString.
[[__class sharedInstance] localizedStringForKey:__key];
- (NSString* )localizedStringForKey:(NSString* )key {
return [_bundle localizedStringForKey:key value:nil table:nil];
}
If you need more help,give me a message.
if you want to create a Custom localization system. Let's try this http://aggressive-mediocrity.blogspot.in/2010/03/custom-localization-system-for-your.html
I ended up using the LocalizationSystem classes in this link to load the languages i needed when i needed them, i had to bind all the text via code, i'm afraid there's no way around this unfortunately, if you want your language change to be in-app and realtime !!
Thanks all for your help.

class creation without xib file

Should I create .h .m and xib files together ? Is it recommended ?
I added a TextAudioViewController.xib later and associated it to TextAudioViewController class.
I added each outlets.
I have no error in ViewDidLoad. But the view is still empty after tapping into TabBar.
I wonder if it is because I didn't create all files together. A kind of missing link between xib and class files ?
With another class, I created .h .m and xib together and the view was successfully loaded after tapping into TabBar...
EDITED
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (self.tabBarController == nil)
{
NSArray* languages = [NSLocale preferredLanguages];
NSString* currentLang = [languages objectAtIndex:0];
NSString* path = #"";
if ([currentLang isEqual: #"en"] || [currentLang isEqual: #"fr"] || [currentLang isEqual: #"es"])
{
path = [[NSBundle mainBundle] pathForResource:currentLang ofType:#"lproj"];
}else{
path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
}
selectedBundle = [NSBundle bundleWithPath:path];
[self startApplication];
}
return YES;
}
It is not necessary that you should create all the files together. After creating the xib file follow these steps.
1.Select the filesowner and change the class name in to TextAudioViewController, you can find the class name under filesinspector.
2.Connect your view outlet to the filesowner view.
Now it shoiuld be working fine.
It is not necessary that you should use initWithNibName:. You can use this only if your class name and xib name are different.

Resources