iPhone SDK Articles

Tuesday, November 11, 2008

Localizating iPhone Apps - Custom Formatter


From time to time you may need to display some information which is locale specific and we may not be able to do it using NSNumberFormatter and NSDateFormatter. Any example might be displaying telephone numbers in a certain format. In such a case, we can create a custom formatter, which is what this tutorial does.

Introduction
The goal of this article is to create a custom formatter, used to display locale specific data. In this tutorial, I will create a new class to format phone numbers based on the user's current locale. If the locale is set to "en_US" then the phone number looks like 1(111)111-1111.

NSFormatter
To create a custom formatter, start by creating a new class which inherits from NSFormatter. In Xcode click File -> New File -> NSObject subclass, I have named my file "PhoneNumberFormatter". The new class you create will inherit from NSObject, simple delete that and inherit from NSFormatter. This is how the header file looks like

#import <UIKit/UIKit.h>


@interface PhoneNumberFormatter : NSFormatter {

NSLocale *locale;
}

@property (nonatomic, copy) NSLocale *locale;

- (NSString *) stringFromPhoneNumber:(NSNumber *)aNumber;

@end

We have a locale property, to keep track of the current user's locale and a method which takes a NSNumber and returns its string representation.

Since the class inherits from NSFormatter, it needs to override some methods. The three methods that we have to override are stringForObjectValue:(id)anObject, getObjectValue:(id)anObject forString:(NSString *)string errorDescription:(NSString *)error and attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes. This has been my understanding so far, if I' am wrong please send me an email. All these methods are really useful in Mac OS X development where we can assign a cell to an NSFormatter object (I have tried to do this in IB for iPhone and it does not work). stringForObjectValue returns NSString object that textually represents the cell's value and the other two methods are also useful in Mac OS X development and not really helpful in iPhone development. As a result, I will not be talking a lot about these methods.

Lets see how the initWithLocale and stringFromPhoneNumber methods looks like

- (void) initWithLocale {
[super init];

locale = [NSLocale currentLocale];
}

- (NSString *) stringFromPhoneNumber:(NSNumber *)aNumber {

NSString *localeString = [locale localeIdentifier];
NSString *tempStr = [[NSString alloc] initWithString:@""];
NSRange range;
range.length = 3;
range.location = 3;
//Returns the phone number 2032225200 as 1(203)222-5200
if([localeString compare:@"en_US"] == NSOrderedSame) {
NSString *areaCode = [[aNumber stringValue] substringToIndex:3];
NSString *phone1 = [[aNumber stringValue] substringWithRange:range];
NSString *phone2 = [[aNumber stringValue] substringFromIndex:6];

tempStr = [NSString stringWithFormat:@"1(%@)%@-%@", areaCode, phone1, phone2];
}

return tempStr;
}

In the initWithLocale method, we get the current user's locale and copy it to the internal variable. In stringFromPhoneNumber we write our logic to display the text representation of the NSNumber value, based on the locale. This is a very simple example and I' am sure there is a better way to parse and display the information.

Let's also take a look at stringForObjectValue method

- (NSString *) stringForObjectValue:(id)anObject {

if(![anObject isKindOfClass:[NSNumber class]])
return nil;
else
return [self stringFromPhoneNumber:anObject];
}

We first check if the object is kind of NSNumber and if it is we simple pass the control to stringFromPhoneNumber message.

This is how we will use the code in the application

- (void)applicationDidFinishLaunching:(UIApplication *)application {

NSNumber *phoneNumber = [NSNumber numberWithInt:1231231212];
PhoneNumberFormatter *pnf = [[PhoneNumberFormatter alloc] initWithLocale];

NSLog(@"Phone Number: %@ for locale: %@", [pnf stringFromPhoneNumber:phoneNumber], [[pnf locale] localeIdentifier]);

[pnf release];

// Override point for customization after application launch
[window makeKeyAndVisible];
}

The phone number and the locale will be displayed on the debugger window.

Conclusion
Creating a custom formatter, comes in handy when we have to display data like phone numbers and format it according to the user's current locale. By doing this, we also keep our code clean.

Happy Programming,
iPhone SDK Articles



Attachments
Suggested Readings


6 comments:

Anonymous said...

when i compile your code with release mode,
it has exception error while running.

iPhone SDK Articles said...

The source code is fixed. I had to change the method name from init to initWithLocale

Happy Programming,
iPhone SDK Articles

Anonymous said...

hi, and thanks.
was wondering about the init of the custom formatter.

if i want to set some of the specific properties, such as "setMaximumIntegerDigits", as an example, would i want to do that in an override of the init method?

mmilano said...

a question about making a custom formatter.

suppose i want to have my input formatted to the pattern, "##-##-##". more importantly, i would like to have an input field that allows the person to enter their number; as they type, the value displayed should build to the right.

example, step by step:
person types: 1
display: 1

person types: 3
display: 13

person types: 5
display: 13-5

person types: 7
display: 13-57

person types: 8
display: 13-57-8

person types: 9
display: 13-57-89

any advice?
i can only get the formatter to return the following - in step three (above), the results are:

display: "1-35"
instead of "13-5."

and so on( e.g.: "1-35-78" in step 5.

thanks in advance.

iPhone SDK Articles said...

@mmilano You need to do this in text changed method of the text box and then count the number of characters entered.

Hope this helps.

Happy Programming,
iPhone SDK Articles

iPhone SDK Articles said...

@Anonymous To which class does this property "setMaximumIntegerDigits" to?

I am not sure I understand your question.

Happy Programming,
iPhone SDK Articles