Custom localization system for your iphone games

__________

Localizating your application is crucial if you want to reach a broader audience, imaging how a Spanish or German kid will react if the game they try to play is in english or french, they just won't buy your game.

The iphone SDK has it's own localization system, which works very well, but as you may know the default localization system does not allow you to change the language in run-time, just use the OS default system, and it also does not allow other languages beyond what's defined on the iphone OS. So, if you want to change the language from a menu in your game there are many things you can do:
  • write your own system, which is utterly boring and implies parsing your language files, or...
  • overload and use what the framework has to offer.
This post tries to explain, first, how to the new localization system works, and second how to add localization to your system and how to use it to change the language in run-time.

Note: Although it is used for games it might be used for your own applications as well.

How the new system works

a. The iphone SDK way

The iphone SDK provides the NSLocalizableString("tag", "alternative") macro to obtain the localized string, from the Localizable.strings, for the given "tag" string or in case it didn't find a proper match it returns the "alternative" string.

I'll explain in the Implementation section how to build the Localizable.strings, just know that it has a strings in the format:
"tag" = "Localized text for the tag";
E.g.
NSString *localizedStr = NSLocalizableString(@"hello", @"Hello World");
//IF it finds the "hello" tag on the localizable.strings for the current OS language it will return the localized string.
//ELSE will return "Hello World"
b. The new way

With adding the LocalizationSystem.h and LcalizationSystem.m you add the following functionality to the default system.
  • you'll be fully compatible with what was already done.
  • you'll be able to change the language in run-time
  • you'll be able to support languages not added in the iphone OS implementation, like Esperanto, Catalá, Elfic,...
Using it:

It's usage is fairly simple, there has been defined 4 macros that extends the default functionality. These macros are:

1. LocalizationSetLanguage("language")
Using the following macro you'll be able to change the localization default language into the language you want.
// Sets the desired language of the ones you have.
// example calls:
// LocalizationSetLanguage(@"Italian");
// LocalizationSetLanguage(@"German");
// LocalizationSetLanguage(@"Spanish");
//
// If this function is not called it will use the default OS language.
// If the language does not exists y returns the default OS language.
- (void) setLanguage:(NSString*) l{
NSLog(@"preferredLang: %@", l);

NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:@"lproj" ];

if (path == nil)
//in case the language does not exists
[self resetLocalization];
else
bundle = [[NSBundle bundleWithPath:path] retain];
}
The "language" string should match what you have on you Localizable.strings file

2. AMLocalizedString("tag", "alternative")
Gets the Localized string:
// Gets the current localized string as in NSLocalizedString.
//
// example calls:
// AMLocalizedString(@"Text to localize",@"Alternative text, in case hte other is not find");
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)comment
{
return [bundle localizedStringForKey:key value:comment table:nil];
}
The AMLocalizedString works just the same way as the NSLocalizedString.

3. LocalizationReset;
Resets all settings, in case you'll allow your user to leave the language settings as default.
// Resets the localization system, so it uses the OS default language.
//
// example call:
// LocalizationReset;
- (void) resetLocalization
{
bundle = [NSBundle mainBundle];
}
4. LocalizationGetLanguage;
Gets the current set language.
// Just gets the current setted up language.
// returns "es","fr",...
//
// example call:
// NSString * currentL = LocalizationGetLanguage;
- (NSString*) getLanguage{

NSArray* languages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];

NSString *preferredLang = [languages objectAtIndex:0];

return preferredLang;
}

Implementation

Step 1. Creating the project

I'll use a Cocos2d 0.99.0 project as I wanted to demonstrate the system on games, but you'll be able to create the project better suits your application.

Step 2. Add localization to your project

To do so, you'll need to:
  1. in resources group within your project, right click add... -> New File
  2. I'll appear a window like the following. Go to Mac OS X -> Resources and select Strings File. Name your file "Localizable.string".
At this point you have added the localization file to you project, now open the information of the file (right click -> Get Info / cmd + i), and on the General tab press the Make File Localizable. You will get this as a result.

Now its time to add the localization you want.
tip: Name it as the language name in english. e.g "spanish", "french", "german",...

Step 3. Add the text.

Now open each of the files to add the text you want to localize, remember the format. Do it in the desired language file.

"tag" = "localized text";
"hello" = "HOLA MUNDO";
Add this point is exactly as if you were adding normal localization.

Step 4. Add the new localizatio file to your project.

You have to add the LocalizationSystem.h and LocalizationSystem.m to your project.

This can be done in two ways.
  1. Right click -> Add existing files... wherever you want to add the files, select them and click ok.
  2. You can darg from the sample project the group containing both files to your project.
Step 5. Just use the library.

1. Add
#import "LocalizationSystem.h"
In the classes that need localization,

2. Use AMLocalizableString instead of NSLocalizableString.

3. Use LocalizationSetLanguage to change the language.
LocalizationSetLanguage(@"Spanish");
CCLabel* label = [CCLabel labelWithString:AMLocalizedString(@"hello",@"Hello World") fontName:@"Marker Felt" fontSize:32];
The label will contain "Hola mundo".

Download

You can download a sample project with everything on it.
It consist of a clean project, just to be as familiar as possible, with the library added.

that will run as:

Each button represents a language added and OS, returns to the OS default language.

The files:
LocalizationSystem.h
LocalizationSystem.m

The sample project
Test project

If anyone has any suggestion or question leave it in the comments. I'll be glad to answer.
Thank you for reading.

35 comments:

Daria said...

Do you by any chance know what exactly is going on with localizable.strings? when are they loaded? are they kept in memory all the time? I have a large localizable.strings file and I'm trying to find the way to improve startup time. thanks in advance

Anderson Tagata said...

In your tutorial, you made a little mistake...
Should change NSLocalizedString to AMLocalizedString, you said:
2. Use AMLocalizableString instead of NSLocalizableString.

Nice post! Thanks

Bart said...

VERY very helpful. Kudos for putting this out there, and explaining it so well.

Zaur Amikishiyev said...

Juan Albero,

In the following code what is the meaning of [[self alloc] init]?

+ (LocalizationSystem *)sharedLocalSystem
{
@synchronized([LocalizationSystem class])
{
if (!_sharedLocalSystem){
[[self alloc] init];
}
return _sharedLocalSystem;
}
// to avoid compiler warning
return nil;
}

Alex Szilagyi said...

Can anyone provide me a tip how to implement this way for many text labels? Do I have to create a method for my each label?

Dan said...

Thanks Reuben.

I added following macro to help get localized images...

LocalizationSystem.h
---
#define AMLocalizedImagePath(imagename, imagetype) \
[[LocalizationSystem sharedLocalSystem] localizedImagePathForImg:(imagename) type:(imagetype)]

.
.
.

- (NSString *)localizedImagePathForImg:(NSString *)imagename type:(NSString *)imagetype;
---


LocalizationSystem.m
---

- (NSString *)localizedImagePathForImg:(NSString *)imagename type:(NSString *)imagetype
{
NSString * imgPath = [bundle pathForResource:imagename ofType:imagetype];

//NSLog(@"Returning imagePath:%@",imgPath);

return imgPath;
}

-----


Changing images after setting new language:
------------
UIImage *img = [[UIImage alloc] initWithContentsOfFile:AMLocalizedImagePath(@"app_logo", @"png")];

self.appLogo.image = img;

-------------

Dan said...

I could be wrong, but

"LocalizationGetLanguage" does not work.
it returns selected language on the device, no app selected language..

Alex Szilagyi said...

The same issue that @Dan has, anyone fixed it?

Alex Szilagyi said...

Update: Actually I'm using LocalizationSetLanguage(@"LanguageName"); but it gets the default one...

ezod said...

@Dan, @Alex:

I just put the following line at the end of method "- (void) setLanguage:(NSString*) l" :

[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:l, nil] forKey:@"AppleLanguages"];

This will simply overwrite the "AppleLanguages" Array and will contain only the preferred language.

(Works for me ;))

Ivan Vučica said...

You probably refer to NSLocalizedString, not NSLocalizableString. The second argument of this macro is not the alternative, but the explanation which'll be written out in the autogenerated .strings file(s).

Quang Thanh Le said...

Hi guys,
What is license for these files?

what does "Copyright Aggressive Mediocrity 2010. All rights reserved." mean?

Vyacheslav Khlichkin said...

Could you help me please: after adding two files (LocalizationSystem.m/.h) into my project comlier return me this stuff:

Ld /Users/iMac/Library/Developer/Xcode/DerivedData/Tale-evnomempxsbnpacapbyygvatjzig/Build/Products/Debug-iphonesimulator/Tale.app/Tale normal i386
cd /Users/iMac/projects/Cocos2D/Tale
setenv IPHONEOS_DEPLOYMENT_TARGET 6.0
setenv PATH "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/llvm-gcc-4.2 -arch i386 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk -L/Users/iMac/Library/Developer/Xcode/DerivedData/Tale-evnomempxsbnpacapbyygvatjzig/Build/Products/Debug-iphonesimulator -F/Users/iMac/Library/Developer/Xcode/DerivedData/Tale-evnomempxsbnpacapbyygvatjzig/Build/Products/Debug-iphonesimulator -filelist /Users/iMac/Library/Developer/Xcode/DerivedData/Tale-evnomempxsbnpacapbyygvatjzig/Build/Intermediates/Tale.build/Debug-iphonesimulator/Tale.build/Objects-normal/i386/Tale.LinkFileList -Xlinker -objc_abi_version -Xlinker 2 -lz -Xlinker -no_implicit_dylibs -fobjc-link-runtime -mios-simulator-version-min=6.0 -framework QuartzCore -framework OpenGLES -framework OpenAL -framework AudioToolbox -framework AVFoundation -framework UIKit -framework Foundation -framework CoreGraphics -o /Users/iMac/Library/Developer/Xcode/DerivedData/Tale-evnomempxsbnpacapbyygvatjzig/Build/Products/Debug-iphonesimulator/Tale.app/Tale

Undefined symbols for architecture i386:
"_OBJC_CLASS_$_LocalizationSystem", referenced from:
objc-class-ref in MainMenuLayer.o
ld: symbol(s) not found for architecture i386
collect2: ld returned 1 exit status


(null): "_OBJC_CLASS_$_LocalizationSystem", referenced from:

(null): Objc-class-ref in MainMenuLayer.o

(null): Symbol(s) not found for architecture i386

(null): Collect2: ld returned 1 exit status



what should i correct? thank you

ps im newbie )

Vyacheslav Khlichkin said...

I solved the problem ) that was caused of manually adding files to project

Joan Albero said...

Hello. As commented before "LocalizationGetLanguage" may not work.

I haven't tried this in a long time and may have change since I first wrote the code with iOS 3.X and now we are 3 major updates later.

Glad is helping people.

mahmoud helaly said...

Hello,

I finished all the tutorial steps but still the app get the operating system (iPad OS) not the language which set on my code

Hire iPhone Application Developers said...

Thanks for share it. I got some important points which will help to me in future so thanks again.

Hire iPhone Game Developer

jems said...

You can download a sample project with everything on it.
It consist of a clean project, just to be as familiar as possible, with the library added. coque iphone 4

Warren Santos said...

I am very thankful to all your team for sharing such inspirational information.
backup extractor for iphone

hafiz asim said...

I want to find the paid directory submission service ,which can make strategical internet business project, based on directory submission. I strive to win my web contest.
www.edupolicies.com

hafiz asim said...

No one in the world is able to aid you with papers completing better than famous writing service can do. Hence, that is ok if you buy essay papers "bestcustompapers.com" from the reliable custom company.
www.nationalacademiespress.com

alfred said...

eep your iPhone away from things that can scratch it. If you are going to put it in your pocket or purse, don't put it in the same place you keep your keys or your change, as either one is likely to scratch it.

E_Cell

Mickey James said...

I’m flattened by your contents keep up the excellent work.
recipes and soup

chris.hoefle said...

Thank you so much for your description. I struggled with this, and your solution was my rescue. I modified your code slighly for my needings, if anyone is interessted please get in touch.

Ning said...

great post! helps me understand how the bundle works. I added a method to get the static "bundle" from "LocalizationSystem.h and m", so I can load my localized html files and images. Thanks again!

david said...

I was reading this post.It is very interesting and I hope you will be sharing more informative post in future.Thanks


iPhone Games

Pedro Viana said...

AWESOME!

Richard Cooke said...

Your writings, articles, blogs I mean over all contents is must read matter. Ur Hun Website

boris baker said...

Never found such informative articles
Look for Rainbow Systems Here

Sir Ward said...

I Never ever found such edifying blogs.
Relationship with Milvia Design

liyan gong said...
This comment has been removed by the author.
liyan gong said...

Thank you very much for sharing this great information. It works.
iPhone photo recovery
iPhone contact recovery

amber2012 said...

Excellent!!works just fine!!- iPhone Data Recovery

vey smith said...

Very classic blogs I’ve never seen to any site. Relationship Issues With Leonardo Spencer

Peggy Stoffel said...

I'm confident that once you read this again you come to read these articles and blogs.
Go to Camome

Post a Comment

top