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

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.

Related

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.

Creating multiple themes in an iPhone App

I have recently released my first app to the App Store and still have a very long way to go with iOS development.
I'm looking to introduce themes into my app as an update so the user can select from 4-5 different themes.
I've got a tab bar controller and have set the 5th tab to be the "Settings" tab which contains a Table View with cells. The first cell contains the text "Themes" where the user can select it, be taken to a new Table view/Collection View to select the themes.
So I searched online and came across this incredible answer on doing just this:
How to create Multiple Themes/Skins for iphone apps?
Because I'm still new to development, I'm in need of assistance to take this forward.
To start off with, I have two themes:
1) DefaultTheme (Newiphonebackground.png)
2) PurplePinkTheme (Purplepinknew.png)
Following the instructions, I have created one plist for the Default theme and one plist for the PurplePink theme.
In the ThemeManager class that I created, I have:
- (id)init
{
if ((self = [super init]))
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *themeName = [defaults objectForKey:#"theme"] ?: #"DefaultTheme";
NSString *path = [[NSBundle mainBundle] pathForResource:themeName ofType:#"plist"];
self.styles = [NSDictionary dictionaryWithContentsOfFile:path];
}
return self;
}
+ (ThemeManager *)sharedManager
{
static ThemeManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[ThemeManager alloc] init];
}
return sharedManager;
}
In my table view, where the theme will get applied (it's going to get applied to every screen in the app but this is just to start off with where I'm testing with one scene in the app), in the viewDidLoad, I put:
NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *imageName = [styles objectForKey:#"DefaultTheme"];
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
self.tableView.backgroundView = backgroundImageView;
That does nothing different because it applies the default theme. However if in the ThemeManager, I change the plist to be:
NSString *themeName = [defaults objectForKey:#"theme"] ?: #"PurplePinkTheme";
and in the viewDidLoad of the separate Table View, I set the code to be:
NSString *imageName = [styles objectForKey:#"PurplePinkTheme"];
Then upon loading my application, my PurplePinkTheme loads.
So far so good, but I don't think I've done anything substantial here.
I am confused from this point on. I am looking to start off with changing just the background images from the in-app settings and once I have that done, I'll look to change the custom navigation bars, etc.
So my questions are:
1) Why do I create one plist per theme?
2) How exactly do I link multiple plists in the ThemeManager class?
3) How do I link all of the themes up to the settings Theme cells allowing the user to choose?
I'm sorry this is vague, but I really need to understand how exactly I can carry on here. For example, I just don't get how to have multiple plists in the ThemeManager and how to move forward from here.
To recap, I, for now just want the ability to have multiple plist files with the different backgrounds, and for the user to go the settings tab in my app, click on the "Themes" cell and be able to select a different theme for the app without restarting the app.
I know the tutorial does carry on with explanations on that, but I'm just not quite sure I understand it all.
Any guidance on this would be massively appreciated.
Many thanks,
Your question is super long so I must confess I did not read the whole thing. That said I spent a lot of time with themes and the best solution I have found is to create an object that handles formatting. This is how I think you can implement it:
Create a formatter object extending NSObject
Have a property for each changeable piece of the theme:
for example if the background changes images you can have a UIImage in there called background. If the font color changes you have a UIFont property in there.
create a shared instance of your formater by adding:
+(Formater *) sharedInstance; //add to .h
+ (Formater *) sharedInstance //add to .m
{
if (!_sharedInstance)
{
_sharedInstance = [[Formater alloc] init];
}
}
Now in your view controller all you need to do is create a reference to your shared item and use that to style your view controller. (remember to make the changes in viewWillAppear not or it will not change after the setting is changed.
Voila!

Passing data through ViewControllers

I'm developing an app that will support multiple languages and I'm looking for the best way to set the different languages.
The app works with a UINavigationController. In the first ViewController you can select the language pressing a UIButton and then in the following view controllers the labels' texts would be changed to the corresponding language.
The way I'm doing it right now is by changing the value of a BOOL property when I create the instance of the new ViewController depending on the UIButton sender tag.
FirstViewController.m
-(void)goToSecondVC{
SecondViewController *secondVC = [[SecondViewController alloc]init];
if ([sender tag] == 1) {
secondVC.english = YES;
}else{
secondVC.english = NO;
}
[self.navigationController pushViewController:startScreenVC];
}
SecondViewController.m
-(void)viewWillAppear:(BOOL)animated{
if(self.english){
self.myLabel.text = #"This text will be in English";
}else{
self.myLabel.text = #"This text will be in Spanish";
}
I know this is probably not the best way to achieve this task. What would you recommend, notifications, delegation, singletons? I'm looking for a kind of global variable that could be written and read from every ViewController
You should be using localization for this.
You can get the language like this:
NSString *language = [[NSLocale currentLocale] objectForKey: NSLocaleLanguageCode];
Take a look at this this tutorial for localization:
http://www.raywenderlich.com/2876/localization-tutorial-for-ios
or this SO ansawer
The implementation is straightforward and correct.
Since you want to have this information known to every view controller, a better approach is to use KVO and a store for the language info value.
For example, save it to NSUserDefaults. Then from any view controller your could access it.
Then if some view controller wants to get notification when this value gets changed, it could observe the NSUserDefaults object for that value. (with Storyboard, you could use a Shared User Defaults Controller).
If you want to access the current language setting from any place in your app its worth taking a look at the Singleton design pattern. Here's an excellent summary.
You can also use the [NSUserDefaults standardUserDefaults] which is a predefined Singleton object or simply create your own.

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.

Programmatic language localisation without restart

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).

Resources