4. Prepping Your App for Localization

Measure twice, cut once

English Proverb

This chapter walks you through creating an international supporting app called “I18nExerciser.” It will display elements of a locale—language, region, decimal character, date format—the topics of Chapter 1, “International Settings.” This app will be localized only for English strings, and will be coded to support internationalization. We will be mindful of locale support with the code we use for this app. This is where our chapter quote, “Measure twice, cut once,” comes into play. It is generally applied to building something, often out of wood; measuring twice means you need to cut the wood only once, as opposed to having to cut a second piece of wood. Planning your app correctly means you need to put only minimal effort into internationalizing your app. That will be demonstrated by a combination of this chapter and the following chapter.

We’ll take what we learned in Chapter 3, “Coding for Locale,” and apply it to our sample app. In Chapter 5, “Localizing Your App,” we will take this app and run it through the localization process. The hope is that we’ve configured and coded our sample app such that we’ve coded only once, and it will support all the iOS supported languages and locales. Our app should automatically update and display the expected formats according to whatever the System locale setting is.

Building an International Exerciser

Let’s build an app! We’re going to step through building an app, adding features at each step from the topics covered in Chapter 1. As mentioned in the intro, we’ll be working only with English as our language and U.S. regional settings. We’ll also be working with other common U.S. settings as well as the Gregorian calendar and a U.S. keyboard.

The app we’ll put together really serves two purposes. One goal is to use it as common ground to look at good locale practices with your code to set it up once to be internationally ready. The other goal is to provide locale information and to see some of this information in action. By this I mean being able to input a locale identifier and then very quickly getting information about that locale.


Where’s My MVC?

For creating our internationalization exerciser, I’m going to keep things as straightforward as possible. I try to follow Model-View-Controller (MVC) design patterns and its variants as much as possible. That said, for our sample project, I’m going to place a lot more code in the view controller than I normally do. Do not hesitate to follow along with your own project; I just want to make sure we have a common point of reference.


App Creation Overview

Our app creation will be broken down by categories that follow the classes called out in Chapter 3, so there will be a string category, a date category, and so on. When we move to a new category, we’ll create a new view controller for our project and implement the feature. Category specifics are called out in the following subsections.

Covered Locale Categories

For the NSLocale class, the current System settings that are available to access include these:

Image Language

Image Locale

Image Calendar

Image Script

Image Variant

Image Quotation mark characters—beginning and ending

Image Measurement system and whether the Metric system is used

Image Exemplar character set

For the NSDateFormatter class, the NSCalendar and NSDate classes are included as well, in order to give the following:

Image Date formatting

Image Date styles—Short, Medium, Long, and Full

The sample for the NSNumberFormatter class will take a given number and display its format with the following applied to it:

Image Decimal

Image Currency

Image Percentage

Image Spelled out

The NSString class example will include the following:

Image Sorting

Image Case—lower, upper, capitalization

Image Proper display of high Unicode value characters

The last two classes included in this exercise are the Address Book Framework and the NSTimeZone. The example for the Address Book Framework will exercise the name order for contacts. The NSTimeZone example is a scenario that includes receiving the current time via a JSON response, converting that string into a date object, and finally doing date math to calculate the current time.

View Controller Creation

For each of the locale categories, we’ll be adding a new view controller. I am assuming you’re well versed in working with Objective-C, using Xcode, creating new view controller objects, and working with XIBs.

The view controllers will consist mostly of UILabels with outlets set up to those labels. There will also be UIButtons requiring IBActions connected to them as well. There will also be UITextFields, UITextView, and a couple of UITableViews. The assumption is that you are familiar with these elements, and basic instructions will be given about them.

Locale Components Affected by Setting Changes

Locale components respond differently to setting changes. Table 4.1 lists what components are changed or updated when either the language or the locale settings are changed. What’s going on here is that not every locale component is affected by a change to the locale System setting. To give a specific example, the results from number formatting via NSNumberFormatter objects will be updated when the language setting is changed but will not be updated when the locale is changed.

Image

Table 4.1 Settings That Affect Locale Components Updating

Auto Updating Locale

As mentioned in the preceding section, locale components are updated when a System setting is changed. But how do we get the code to realize that the setting has changed? If we grab the state of the NSLocale object via [NSLocale currentLocale], we’re working with a cached value. We’ve got a couple of options available to update the NSLocale object, and I’ve used both of them in this project.

The first approach is to implement auto updating within the app delegate. I suggest adding the following code in both the applicationDidEnterForeground and the applicationWillEnterForeground app delegate methods:

[NSLocale autoupdatingCurrentLocale];

In the scenario here, our app has gone into the background, and we’ve brought up the System App, made a change to the International settings, and finally brought the app into the foreground. Again, we’re wiring stuff up ahead of time. For this portion of the project at this point in the chapter, we will not be changing the locale but will be keeping the settings as language English, region United States.

The second approach is to set up an observer watching for the NSCurrentLocaleDidChange Notification. This notification is keyed specifically on a change to the Settings app.

Home Screen

This is the starting point of our application. I’m going to break the app into several view controllers, each one having a separate responsibility. The home screen will have several labels of top-level locale information:

Image Current system language setting

Image Current system locale/region setting

Image Current calendar setting

The home screen view controller will also include a UIImageView. Although this initially seems out of place, it will come in to play in Chapter 5 as we start the localization procedures.

The More button will take you to a table view controller listing the other view controllers displaying categorized information. Figure 4.1 is a screenshot of this implementation.

Image

Figure 4.1 The home screen view controller.

Our project also uses a table view to categorize the other view controllers that will be used to display locale specifics. As you know, when working with Storyboards, when you add a Navigation Controller, you get a table view controller for free. This will allow us to display the detailed view controllers and easily get back to our category list. We’ll also add a navigation item to the table view controller to return to the home page. As you add new view controllers, you’ll add an entry for that new view controller to the array that the table view entries are based on.

Figure 4.1 shows the layout of the various views: ten UILabels, one UIImageView, and one UIButton.

First, create the project by following these steps:

1. From Xcode, select File, New, Project.

2. Under iOS, choose Application, and then choose Single View Application.

3. Click Next.

4. Name your project I18nExerciser, and then select Next, Create.

5. Add the following code to the ViewController.m file:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshListing) name:NSCurrentLocaleDidChangeNotification object:nil];
    [self setUpHomeScreen];
}

-(void)viewDidAppear:(BOOL)animated{
    [self setUpHomeScreen];
}

The following code is implemented to allow us to have multilined labels:

-(void)viewDidLayoutSubviews{
    [_currentDate setNumberOfLines:0];
    [_currentDate sizeToFit];
}

For the image file, I’ve used globe.jpg; feel free to substitute your own. Make sure you have included it in your project:

-(void)setUpHomeScreen{
self.currentLanguage.text = [[NSLocale preferredLanguages] firstObject];
    self.currentLocale.text = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
    self.currentCalendar.text = [[[[NSLocale currentLocale] objectForKey:NSLocaleCalendar] calendarIdentifier] capitalizedString];
    self.imageView.image = [UIImage imageNamed:@"globe.jpg"];

NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]];
    NSDate *date = [NSDate date];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setLocale:locale];
    [formatter setDateStyle:NSDateFormatterFullStyle];
    self.currentDate.text = [formatter stringFromDate:date];
}

-(void)refreshListing{
    self.currentLanguage.text = [[NSLocale preferredLanguages] objectAtIndex:0];
    self.currentLocale.text = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
    self.currentCalendar.text = [[[[NSLocale currentLocale] objectForKey:NSLocaleCalendar] calendarIdentifier] capitalizedString];
}

Now, set up the Storyboard:

1. From the Object Library, drag nine UILabels, one UIButton, and one UIImageView. Match the layout shown in Figure 4.1.

2. Drag a segue from the More button to the table view controller.

3. Add the following outlets to the implementation file and wire them accordingly to the elements listed in Step 1 via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *currentLanguage;
@property (weak, nonatomic) IBOutlet UILabel *currentLocale;
@property (weak, nonatomic) IBOutlet UILabel *currentCalendar;
@property (weak, nonatomic) IBOutlet UILabel *currentDate;

Table View Controller

For our table view controller that’s part of the Storyboard, implement table view delegate methods:

numberOfSectionsInTableView:
tableView:numberOfRowsInSection:
tableView:cellForRowAtIndexPath:
tableView:didSelectRowAtIndexPath:

We’ll modify the tableView:didSelectRowAtIndexPath: method each time we add a new view controller. It will hold a switch statement to access the array holding the list of view controllers.

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.tableView.delegate = self;

    self.listing = @[@""];
}

#pragma mark - UITableView delegate methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
    return [self.listing count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
    cell.selectionStyle = UITableViewCellAccessoryDisclosureIndicator;
    cell.textLabel.text = [self.listing objectAtIndex:indexPath.row];

    return cell;
}

The switch statement that follows is going to be modified with each new view controller we add to the project. For now, just get it implemented with the single case statement.

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath{

    switch (indexPath.row) {
         case 0:
            //Add custom methods to load View Controllers;
            break;

        default:
            break;
    }
}

In the Storyboard, add a bar button item and change its title to “Home.” Add a segue from it to the home page to allow us to return to it.

Measurement System

The following steps create a view controller that will display the current locale’s measurement system and indicate if that locale uses the metric system. Starting with this section and with each subsequent section, we’ll be creating view controllers for our app. As I mentioned at the start of this chapter, a view controller will be created for each of the locale categories. I will list the steps to create a new view controller object once in this section and then just reference these steps for subsequent view controllers.

The general view controller creation steps include these:

1. From Xcode, select File, New, File.

2. Choose Cocoa Touch under iOS.

3. Click Objective-C Class and then Next.

4. Give your class a name, and the names will be called out in each locale category section. You’ll want to make it a subclass of UIViewController.

5. Enable the Also Create XIB File option. We’ll work with the XIBs to add our labels, buttons, text fields, and so on.

6. Click Next.

When working with the XIB and Interface Builder, you generally will be dragging the specified element in the Object Library from the Utilities pane.

Here are the steps:

1. Create a new view controller. Name it MeasurementsViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library drag four UILabels. Match the layout shown in Figure 4.2.

Image

Figure 4.2 The measurement system used by the current locale.


NSLocalizedString() Macro

We’ll go into greater detail about the NSLocalizedString() macro in the next chapter. This macro makes calls to the NSBundle class instance method localizedStringForKey:value:table: that replaces strings with the appropriate translation according to generated string tables. For now, anywhere you have strings in your code, wrap them in this macro. This is a major assist in localizing the strings within your code.


4. Import the MeasurementsViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Measurement System.”

6. Wrap our view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadMeasurementsVC];
break;

8. Add the custom method, loadMeasurementsVC, to the implementation file:

-(void)loadMeasurementsVC{
    MeasurementsViewController *measurementsVC = [[MeasurementsViewController alloc] init];
    [self.navigationController pushViewController:measurementsVC animated:YES];
}

9. Implement the following code to the LocaleTableViewController.m file:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self measurementSystems];
}

-(void)setUpMeasurementSystems{

    self.measurementSystem.text = [[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleMeasurementSystem];
    if([[[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleUsesMetricSystem]  isEqual: @0]){
        self.usesMetricSystem.text = NSLocalizaedString(@"NO", nil);
    }else{
        self.usesMetricSystem.text = NSLocalizedString(@"YES", nil);
    }
}

To code the UI, add the following outlets to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UILabel *measurementSystem;
@property (weak, nonatomic) IBOutlet UILabel *usesMetricSystem;

Quotes and Scripts

This section lists the steps to create a view controller to display the characters the current locale uses for beginning and ending quotation marks. This view controller will also display the locale’s script and any variants associated with that script.

Here are the steps:

1. Create a new view controller. Name it QuotesAndScriptViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag eight UILabels onto the XIB. Match the layout shown in Figure 4.3.

Image

Figure 4.3 Quotation mark characters used by the current locale.

4. Import the QuotesAndScriptViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Quotes and Scripts.”

6. Wrap our view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadQuotesAndScriptsVC];
break;

8. Add the custom method, loadQuotesAndScriptsVC, to the implementation file:

-(void)loadQuotesAndScriptsVC{
    QuotesAndScriptViewController *quotesAndScriptVC = [[QuotesAndScriptViewController alloc] init];
    [self.navigationController pushViewController:quotesAndScriptVC animated:YES];
}

9. Implement the following method to the QuotesAndScriptViewController.m file:

- (void)viewDidLoad
{
    [super viewDidLoad];
self.locale = [[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]];
    [self quotedText];
    [self scriptAndVariantCodes];
}

-(void)viewDidLayoutSubviews{
    [_quoteLabel setNumberOfLines:0];
    [_quoteLabel sizeToFit];
    [_variantCode setNumberOfLines:0];
    [_variantCode sizeToFit];
    [_scriptCode setNumberOfLines:0];
    [_scriptCode sizeToFit];
}

-(void)quotedText{
    NSString *quotedString = NSLocalizedString(@"Remember a panda eats shoots and leaves.", nil);
    NSString *beginningQuote = [self.locale objectForKey:NSLocaleQuotationBeginDelimiterKey];
    NSString *endingQuote = [self.locale objectForKey:NSLocaleQuotationEndDelimiterKey] ;
    self.quoteLabel.text = [quotedString stringByAppendingString:endingQuote];
    self.quoteLabel.text = [beginningQuote stringByAppendingString:self.quoteLabel.text];
}

-(void)scriptAndVariantCodes{
if (![self.locale objectForKey: NSLocaleScriptCode]) {
        self.scriptCode.text = NSLocalizaedString(@"No Script code for current locale.",nil);
    }else{
        self.scriptCode.text = [self.locale objectForKey: NSLocaleScriptCode];
    }
    if (![self.locale objectForKey: NSLocaleVariantCode]) {
        self.variantCode.text = NSLocalizaedString(@"No Variant code for current locale.", nil);
    }else{
       self.variantCode.text = [self.locale objectForKey: NSLocaleVariantCode];
    }
}

To code the UI, add the following properties and outlets and IBActions to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UILabel *quoteLabel;
@property (weak, nonatomic) IBOutlet UILabel *scriptCode;
@property (weak, nonatomic) IBOutlet UILabel *variantCode;
@property (nonatomic, strong) NSLocale *locale;

Calendar and Date Components

The steps listed in this section will create an example to display the current calendar type as set in the System settings. The steps include how to take the current date and pull out the day, month, and year components. There’s nothing locale-specific with NSDateComponent objects. The interesting scenario we will look at with this example is their flexibility in working with Gregorian, Buddhist, or Japanese calendars.

Here are the steps:

1. Create a new view controller. Name it CalendarAndDateComponentsViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag 11 UILabels. Match the layout shown in Figure 4.4.

Image

Figure 4.4 Calendar display and date components view controller.

4. Import the CalendarAndDateComponentsViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Date Components and Calendar.”

6. Wrap the view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadCalendarAndDateComponentsVC];
break;

8. Add the custom method, loadCalendarAndDateComponentsVC, to the implementation file:

-(void)loadCalendarAndDateComponentsVC{
    CalendarAndDateComponentsViewController *calendarAndDateComponentsVC = [[CalendarAndDateComponentsViewController alloc] init];
    [self.navigationController pushViewController:calendarAndDateComponentsVC animated:YES];
}

9. Implement the following code to the implementation file. Note that the addObserver:selector:name:object method is implemented so that our app will be notified of any changes to the locale setting:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshListing) name:NSCurrentLocaleDidChangeNotification object:nil];
    [self currentCalendarAndDate];
}

-(void)viewDidLayoutSubviews{
    [_currentDate setNumberOfLines:0];
    [_currentDate sizeToFit];
}

-(void)currentCalendarAndDate{
    NSLocale *locale = [NSLocale autoupdatingCurrentLocale];
    NSDate *date = [NSDate date];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setLocale:locale];
    [formatter setDateFormat:@"EEEE, MMM dd, yyyy"];

    NSDateComponents *components = [[NSDateComponents alloc] init];

    if ([[[[NSLocale currentLocale] objectForKey:NSLocaleCalendar] calendarIdentifier] isEqualToString:@"gregorian"]) {
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    } else if ([[[[NSLocale currentLocale] objectForKey:NSLocaleCalendar] calendarIdentifier] isEqualToString:@"japanese"]){
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSJapaneseCalendar];
        components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    } else if ([[[[NSLocale currentLocale] objectForKey:NSLocaleCalendar] calendarIdentifier] isEqualToString:@"buddhist"]){
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSBuddhistCalendar];
        components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    }

    NSArray *array = [formatter veryShortWeekdaySymbols];

    self.currentCalendar.text =  [[[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleCalendar] calendarIdentifier];
    self.currentDate.text = [formatter stringFromDate:date];
    self.componentDay.text = [NSString stringWithFormat:@"%ld", (long)[components day]];
    self.componentMonth.text = [NSString stringWithFormat:@"%ld", (long)[components month]];
    self.componentYear.text = [NSString stringWithFormat:@"%ld", (long)[components year]];
}
-(void)refreshListing{
    [self currentCalendarAndDate];
}

To code the UI, add the following properties and outlets and IBActions to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UILabel *currentCalendar;
@property (weak, nonatomic) IBOutlet UILabel *currentDate;
@property (weak, nonatomic) IBOutlet UILabel *componentDay;
@property (weak, nonatomic) IBOutlet UILabel *componentMonth;
@property (weak, nonatomic) IBOutlet UILabel *componentYear;

Date Formatting

When working with NSDateFormatter objects, we have the full respect of locale-specific date formats. This example takes advantage of the built-in date styles.

Short, Medium, Long, Full Date Styles

This section demonstrates using the date formatter to display date styles that will automatically update when the locale changes. Here are the steps to create a view controller that will display the short, medium, long, and full date styles examples:

1. Create a new view controller. Name it DateFormatterViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag 22 UILabels. Match the layout shown in Figure 4.5.

Image

Figure 4.5 Date and time formatting view controller.

4. Import the DateFormatterViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Date Formatter.”

6. Wrap our view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadDateFormatterVC];
break;

8. Add the custom method, loadDateFormatterVC, to the implementation file:

-(void)loadDateFormatterVC{
    DateFormatterViewController *dateFormatterVC = [[DateFormatterViewController alloc] init];
    [self.navigationController pushViewController:dateFormatterVC animated:YES];
}

9. Implement the following code to the implementation file:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setDateFormatExamples];
    [self setTimeFormatExamples];
}

-(void)viewDidLayoutSubviews{
    [_fullFormatDate setNumberOfLines:0];
    [_fullFormatDate sizeToFit];
    [_fullFormatTime setNumberOfLines:0];
    [_fullFormatTime sizeToFit];
}

-(void)setDateFormatExamples{
    self.noFormatDate.text = [self formattedDateTime:0 justTime:NO];
    self.shortFormatDate.text = [self formattedDateTime:1 justTime:NO];
    self.mediumFormatDate.text = [self formattedDateTime:2 justTime:NO];
    self.longFormatDate.text = [self formattedDateTime:3 justTime:NO];
    self.fullFormatDate.text = [self formattedDateTime:4 justTime:NO];
}

The following method is a helper method to walk through the array of date style constants. If the justTime argument is set to YES, then just the time should be applied:

-(NSString *)formattedDateTime:(int)dateStyleIndex justTime:(BOOL)justTime{
    int dateStyle[] = {NSDateFormatterNoStyle, NSDateFormatterShortStyle, NSDateFormatterMediumStyle,
NSDateFormatterLongStyle, NSDateFormatterFullStyle};

    NSDate *date = [NSDate date];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setLocale:[NSLocale autoupdatingCurrentLocale]];

    if (justTime) {
        [dateFormatter setTimeStyle:dateStyle[dateStyleIndex]];

    }else{
        [dateFormatter setDateStyle:dateStyle[dateStyleIndex]];
    }
    return [dateFormatter stringFromDate:date];
}

-(void)setTimeFormatExamples{
    self.noFormatTime.text = [self formattedDateTime:0 justTime:YES];
    self.shortFormatTime.text = [self formattedDateTime:1 justTime:YES];
    self.mediumFormatTime.text = [self formattedDateTime:2 justTime:YES];
    self.longFormatTime.text = [self formattedDateTime:3 justTime:YES];
    self.fullFormatTime.text = [self formattedDateTime:4 justTime:YES];
}

To code the UI, add the following outlets to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (nonatomic, weak) IBOutlet UILabel *noFormatDate;
@property (nonatomic, weak) IBOutlet UILabel *shortFormatDate;
@property (nonatomic, weak) IBOutlet UILabel *mediumFormatDate;
@property (nonatomic, weak) IBOutlet UILabel *longFormatDate;
@property (nonatomic, weak) IBOutlet UILabel *fullFormatDate;
@property (nonatomic, weak) IBOutlet UILabel *noFormatTime;
@property (nonatomic, weak) IBOutlet UILabel *shortFormatTime;
@property (nonatomic, weak) IBOutlet UILabel *mediumFormatTime;
@property (nonatomic, weak) IBOutlet UILabel *longFormatTime;
@property (nonatomic, weak) IBOutlet UILabel *fullFormatTime;

Date Strings to Date Objects

This example is making a request to an endpoint (http://date.jsontest.com/) to retrieve the current date and time. It demonstrates working with a date string, which happens to be the result of a JSON request converting that to a date object and then being able to manipulate it. It wraps up with doing a calculation with grabbing the current locale’s time zone and converting the server time to the locale’s local time. Here are the steps to follow to create this view controller and wire it accordingly to display the correct date strings:

1. Create a new view controller. Name it DateStringToObjViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag nine UILabels to the XIB. Match the layout shown in Figure 4.6.

Image

Figure 4.6 View controller for date math and time zones.

4. Import the DateStringToObjViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Date - String to Object.”

6. Wrap our view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadDateStringToObjViewController];
break;

8. Add the custom method, loadDateStringToObjViewController, to the implementation file:

-(void) loadDateStringToObjViewController {
DateStringToObjViewController *dateStringToObjVC = [[DateStringToObjViewController alloc] init];
self.navigationController pushViewController: dateStringToObjVC animated:YES];
}

9. Implement the following code to the implementation file:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setTimeZone];
    [self JSONRequest];
    [self calculateCurrentTime];
}

-(void)JSONRequest{
NSData *dateData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:dateURL]];
    NSError *error;
    self.dateDictionary = [NSJSONSerialization JSONObjectWithData:dateData options:NSJSONReadingMutableContainers error:&error];

    if (error) {
        NSLog(@"Error %@", error);
    } else{
        self.jsonDate.text = self.dateDictionary[@"date"];
        self.jsonTime.text = self.dateDictionary[@"time"];
    }

}

-(void)setTimeZone{
    NSTimeZone *currentTimeZone = [NSTimeZone localTimeZone];
    self.currentTimeZone.text = [currentTimeZone name];
}

-(void)calculateCurrentTime{
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale preferredLanguages] objectAtIndex:0]];

    NSString *dateStr1 = [self.dateDictionary[@"date"] stringByAppendingString:@" "];
    NSString *dateStr = [dateStr1 stringByAppendingString:self.dateDictionary[@"time"]];

    NSDateFormatter *dateFormatterUS = [[NSDateFormatter alloc] init];
    [dateFormatterUS setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]];
    [dateFormatterUS setDateFormat:@"MM-dd-yyyy hh:mm:ss a"];
    NSDate *dateUS = [dateFormatterUS dateFromString:dateStr];

    NSString *dateString = [dateFormatterUS stringFromDate:dateUS];

    NSDate *date = [NSDate date];
    date = dateUS;

    NSTimeZone *currentTimeZone = [NSTimeZone localTimeZone];
    NSTimeZone *utcTimeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];

    NSInteger currentGMTOffset = [currentTimeZone secondsFromGMTForDate:date];
    NSInteger gmtOffset = [utcTimeZone secondsFromGMTForDate:date];
    NSTimeInterval gmtInterval = currentGMTOffset - gmtOffset;

    NSDate *destinationDate = [[NSDate alloc] initWithTimeInterval:gmtInterval sinceDate:date] ;

    NSDateFormatter *dateFormatters = [[NSDateFormatter alloc] init];
    [dateFormatters setLocale:locale];
    [dateFormatters setDateFormat:@"hh:mm:ss a"];
    [dateFormatters setDateStyle:NSDateFormatterShortStyle];
    [dateFormatters setTimeStyle:NSDateFormatterShortStyle];
    [dateFormatters setDoesRelativeDateFormatting:YES];
    [dateFormatters setTimeZone:[NSTimeZone systemTimeZone]];

    self.timeZoneTime.text = [dateFormatters stringFromDate: destinationDate];
}

To code the UI, follow these steps:

1. Add the following properties and outlets and IBActions to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UILabel *jsonTime;
@property (weak, nonatomic) IBOutlet UILabel *jsonDate;
@property (weak, nonatomic) IBOutlet UILabel *currentTimeZone;
@property (weak, nonatomic) IBOutlet UILabel *timeZoneTime;
@property (strong, nonatomic) NSMutableDictionary *dateDictionary;

2. Add the following constant outside of the @interface, which is the server endpoint reference:

NSString *const dateURL = @"http://date.jsontest.com/";

Number Formatting

The number formatting section takes advantage of the NSNumberFormatter class localizedStringFromNumber:numberStyle method.

Decimal, Currency, Percent, and Spelled Out

This example works with a hard-coded string to display a locale’s decimal and thousands separators. It also works with the locale’s currency format. The spelled-out style will “verbally” display the number provided; for example, if the number was 23, the spelled-out result would be “twenty-three” in the System language specified.

The spelledOut label is set in the viewDidLayoutSubviews method to have a number of lines set to zero and have the sizeToFit method called. This provides for a multilined label. Follow these steps to create this view controller to display this numbering format:

1. Create a new view controller. Name it NumberFormatterViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag nine UILabels onto the XIB. Match the layout shown in Figure 4.7.

Image

Figure 4.7 Number formatter view controller.

4. Import the NumberFormatterViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Number Formatting.”

6. Wrap the view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadNumberFormattingVC];
break;

8. Add the custom method, loadNumberFormattingVC, to the implementation file:

case 5:
    [self loadNumberFormattingVC];
    break;

9. Implement the following code to the NumberFormatterViewController.m file:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self setNumberFormats];
}

-(void)viewDidLayoutSubviews{
    [_spelledOutFormat setNumberOfLines:0];
    [_spelledOutFormat sizeToFit];
}

-(void)setNumberFormats{

    NSNumber *number = @4815.1623;
    NSNumber *percentNumber = @0.2342;

    self.currencyFormat.text = [NSNumberFormatter localizedStringFromNumber:number numberStyle:NSNumberFormatterCurrencyStyle];
    self.decimalFormat.text  = [NSNumberFormatter localizedStringFromNumber:number numberStyle:NSNumberFormatterDecimalStyle];
    self.percentFormat.text = [NSNumberFormatter localizedStringFromNumber:percentNumber numberStyle:NSNumberFormatterPercentStyle];
    self.spelledOutFormat.text  = [NSNumberFormatter localizedStringFromNumber:number numberStyle:NSNumberFormatterSpellOutStyle];
}

To code the UI, add the following outlets to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UILabel *decimalFormat;
@property (weak, nonatomic) IBOutlet UILabel *currencyFormat;
@property (weak, nonatomic) IBOutlet UILabel *spelledOutFormat;
@property (weak, nonatomic) IBOutlet UILabel *percentFormat;

Contact Names/Address Book Framework

Japanese and Korean locales list their contacts with last name first, followed by the first name. To support this and have our contacts respect those locales and automatically reflect the correct order, we will want to work with the Address Book Framework and create a record object to work with. Follow these steps to create a view controller to display the correct contact formatting for the given locale:

1. Create a new view controller. Name it ContactNameViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library drag six UILabels onto the XIB. Match the layout shown in Figure 4.8.

Image

Figure 4.8 View controller for contacts.

4. Import the ContactNameViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Contacts.”

6. Wrap the view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadContactNameViewController];
break;

8. Add the custom method, loadContactNameViewController, to the implementation file:

-(void)loadContactNameViewController{
    ContactNameViewController *contactNameVC = [[ContactNameViewController alloc] init];
    [self.navigationController pushViewController:contactNameVC animated:YES];
}

9. Implement the following method to the ContactNameViewController.m file:

#import <AddressBookUI/AddressBookUI.h>

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self createRecord];
}

- (void)viewDidAppear:(BOOL)animated
{
    [self createRecord];
}

-(void)createRecord{
    NSString *first = @"John";
    NSString *last = @"Locke";

    self.firstName.text = first;
    self.lastName.text = last;

    ABRecordRef  record = ABPersonCreate();
    ABRecordSetValue(record, kABPersonFirstNameProperty, (__bridge CFStringRef)first, NULL);
    ABRecordSetValue(record, kABPersonLastNameProperty, (__bridge CFStringRef)last, NULL);

self.contactListing.text = (__bridge_transfer NSString *)ABRecordCopyCompositeName(record);
CFRelease(record);
}

To code the UI, add the following outlets to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UILabel *firstName;
@property (weak, nonatomic) IBOutlet UILabel *lastName;
@property (weak, nonatomic) IBOutlet UILabel *contactListing;

Right now, our contacts view controller will display our contact as “John Locke”—first name followed by last name as is correct with a U.S. locale.

Strings

This section covers our implementation for the NSString class locale support. We’ll look at changing the case and sorting.

String Case

This section covers the changing of a string’s case to lowercase, uppercase, and capitalized. Again, we’re looking only at English strings. But when we start to include languages with other rules, our code will automatically respect the rules for case for those other languages. Our code uses the viewDidLayoutSubviews method for the multiline labels, setting setNumberOfLines: and sizeToFit. Also, there is a static string of characters, containing high-value Unicode characters that should not throw a wrench into our string generation tasks in Chapter 5. The steps to create this view controller to display the different cases of characters are listed here:

1. Create a new view controller. Name it StringsUpperLowerViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag nine UILabels onto the XIB. Match the text given to the labels shown in Figure 4.9. For the “Higher Value” characters, use the Mac OS Character view to populate that label.

Image

Figure 4.9 View controller for string cases.

4. Import the StringsUpperLowerViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “String Case.”

6. Wrap the view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadStringsUpperLowerVC];
break;

8. Add the custom method, loadStringsUpperLowerVC, to the implementation file:

-(void)loadStringsUpperLowerVC{
    StringsUpperLowerViewController *stringCaseVC = [[StringsUpperLowerViewController alloc] init];
    [self.navigationController pushViewController:stringCaseVC animated:YES];
}

9. Implement the following method to the StringsUpperLowerViewController.m file:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self makeYourCase];
}

-(void)viewDidLayoutSubviews{
    [_upperCaseResult setNumberOfLines:0];
    [_upperCaseResult sizeToFit];
    [_lowercaseResult setNumberOfLines:0];
    [_lowercaseResult sizeToFit];
    [_capitalizeResult setNumberOfLines:0];
    [_capitalizeResult sizeToFit];
}

-(void)makeYourCase{
    NSString *subjectString = NSLocalizaedString(@"The boy threw the ball.";
    NSLocale *locale = [NSLocale autoupdatingCurrentLocale];

    self.upperCaseResult.text = [subjectString uppercaseStringWithLocale:locale];
    self.lowercaseResult.text = [subjectString lowercaseStringWithLocale:locale];
    self.capitalizeResult.text = [subjectString capitalizedStringWithLocale:locale];
}

To code the UI, add the following properties and outlets to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UILabel *upperCaseResult;
@property (weak, nonatomic) IBOutlet UILabel *lowercaseResult;
@property (weak, nonatomic) IBOutlet UILabel *capitalizeResult;

Sorting

Again, we are just working with English text. We can sort it in alphabetic order and then return it to its original order. The table view is populated by a static array of English text. This will be exercising the NSString localizedCompare: method. The following steps walk us through adding a table view to our view controller and enable buttons to sort and unsort the list:

1. Create a new view controller. Name it SortingViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag a button and change its label to Sort List. Drag another button and change its label to “Unsort List,” as shown in Figure 4.10.

Image

Figure 4.10 Sorting view controller.

4. Drag a table view onto the XIB. Make the view controller its delegate and datasource.

5. Import the SortingViewController.h header file into the LocaleTableViewController.m file.

6. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Sorting.”

7. Wrap the view controller name in the NSLocalizedString() macro.

8. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadSortingViewController];
break;

9. Add the custom method, loadSortingViewController, to the implementation file:

    -(void)loadSortingViewController{
    SortingViewController *sortingVC = [[SortingViewController alloc] init];
    [self.navigationController pushViewController:sortingVC animated:YES];
    }

10. Implement the following method to the SortingViewController.m file:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.listTableView.delegate = self;
    self.listTableView.dataSource = self;
    [self.listTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
self.englishText = @[@"Soffits", @"subcurrent", @"outmatch", @"predelegating", @"Seneca", @"reciprocity", @"vernation", @"predeficient", @"nondistortion", @"syndetic", @"nettlesome", @"agenesis", @"unscabbed", @"gibberish.", @"Calliope", @"caddishness", @"nongestic", @"definitising", @"Kaiser", @"gabber", @"guessingly", @"entomologizing", @"distrait", @"amaze", @"tetradrachmal", @"inconceivableness", @"guard.", @"Unruffled", @"offender", @"amphitoky", @"comprehend", @"toxically", @"trillion", @"bannerol", @"ampulla", @"trichopteran", @"Nineveh", @"chain", @"putting", @"emotionalize", @"joule.", @"Crust", @"schizogony", @"marvel", @"crashingly", @"rails", @"carpingly", @"demandable", @"reiterating", @"veneered", @"hygienically", @"inimitability", @"testudinarian.", @"Sequential", @"Ockham", @"Aubrey", @"Sabena", @"nonvibration", @"finch", @"parse", @"home", @"out", @"gleam", @"physiocratic", @"unteeming", @"harpsichordist", @"smutchless", @"Muscat.", @"aubrey", @"Outmatch"];
}

- (IBAction)sortList:(id)sender {
    self.sortedArray = [NSArray new];

     self.sortedArray = [self.englishText sortedArrayUsingSelector:@selector(localizedCompare:)];
    [self.listTableView reloadData];
    self.sortListButton.enabled = NO;
    self.unsortButton.enabled = YES;
}

- (IBAction)unsortList:(id)sender {
    self.sortedArray = nil;
    [self.listTableView reloadData];
    self.sortListButton.enabled = YES;
    self.unsortButton.enabled = NO;
}

11. Implement the tableView:cellForRowAtIndexPath: and tableView:numberOfRowsInSection: table view delegate methods.

To code the UI, add the following properties and outlets and IBActions to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (nonatomic, strong) NSArray *englishText;
@property (nonatomic, strong) NSArray *sortedArray;

@property (weak, nonatomic) IBOutlet UITableView *listTableView;
@property (weak, nonatomic) IBOutlet UIButton *unsortButton;
@property (weak, nonatomic) IBOutlet UIButton *sortListButton;

- (IBAction)sortList:(id)sender;
- (IBAction)unsortList:(id)sender;

This will sort and unsort our given English text array. In the next chapter we’ll add two more arrays and verify that their locales are respected when sorting occurs.

Exemplar Character Set

The NSLocale’s NSLocaleExemplarCharacterSet key returns an NSCharacterSet, which contains the exemplar character set for a given locale. Recall from Chapter 3 that this exemplar contains the essential characters a locale needs for written communication. In Latin character sets, this represents their “alphabets,” whereas with CJK languages, it represents their essential ideographs.

We need to loop through the character set array, comparing the elements within that array against a range of Unicode characters. If the element is a “member” of that given Unicode range, it is populated into the entireList array. To grab the actual character, we’ll work with the NSString method stringWithCharacters:length:.

We’ll add a text field to enter the locale identifier and set the view controller as the text field delegate so that we can control dismissing the keyboard. I’ve also handled moving the text view up so that it is not covered by the keyboard when we scroll through it.

A touch event is set up on the window to dismiss the keyboard to scroll through the text view. We’ll also add a button to grab the current locale and populate the text field with that data. The following steps set up the view controller to display the exemplar character set for a specified locale:

1. Create a new view controller. Name it CharacterSetViewController.

2. In the Project Explorer select the XIB.

3. From the Object Library, drag one UILabel, one UITextField, and one UITextView. Match the layout shown in Figure 4.11.

Image

Figure 4.11 View controller for exemplar character sets.

4. Import the CharacterSetViewController.h header file into the LocaleTableViewController.m file.

5. Modify the “listings” array in the LocaleTableViewController.m file to include our new view controller. Name the entry “Exemplar Character Set.”

6. Wrap the view controller name in the NSLocalizedString() macro.

7. In LocaleTableViewController.m modify the switch statement in the tableView:didSelectRowAtIndexPath: method to push our new view controller. Verify that the case is pointing to the correct index:

case CORRECTINDEX:
[self loadExemplarCharSetVC];
break;

8. Add the custom method, loadExemplarCharSetVC, to the implementation file:

-(void)loadExemplarCharSetVC{
    ExemplarCharacterSetViewController *exemplarCharacterSetVC = [[ExemplarCharacterSetViewController alloc] init];
    [self.navigationController pushViewController:exemplarCharacterSetVC animated:YES];
}

9. We’ll want to add some observers to notify us if the keyboard changes. That way, when our character set gets large enough, especially with CJK character sets, we can hide the keyboard and be able to see the entire contents of our text view and easily scroll through it. Modify the view controller’s viewDidLoad method to the following:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.localeIdentifier.delegate = self;
    [[NSNotificationCenter defaultCenter] addObserver:self
                           selector:@selector(keyboardWillShow:)
                           name:UIKeyboardWillShowNotification
                           object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                           selector:@selector(keyboardWillHide:)
                           name:UIKeyboardWillHideNotification
                           object:nil];
    [self.view setMultipleTouchEnabled:YES];
    self.textView.text = NSLocalizedString(@"Enter a valid locale identifier.", nil);
}

10. This code will then loop through Unicode ranges, verify whether the current Unicode character is a member of our specified locale’s character set, and if it is, add it to the entireList array:

-(void) logCharacterSet:(NSCharacterSet*)characterSet{
unichar charsetCharacters[1];
NSMutableArray *charSetArray = [NSMutableArray new];
self.entireList = @"";

// Loop through Unicode values
for (unichar ucValue = 0; ucValue < UNICHAR_MAX ucValue ++)
{
    if ([characterSet characterIsMember:ucValue])
    {
         NSString * characters = [NSString stringWithCharacters:&ucValue length:1];
        [charSetArray addObject:characters];
        self.entireList = [self.entireList stringByAppendingString:characters];
    }
}
}

11. The following code watches for the return key from the text field. After that event occurs, the text view is populated.

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:self.localeIdentifier.text];
    NSCharacterSet *exemplarCharacterSet = [locale objectForKey: NSLocaleExemplarCharacterSet];
    [self logCharacterSet:exemplarCharacterSet];
    self.textView.text = self.entireList;

    UIFont *systemFont = [UIFont systemFontOfSize:12.0f];
    UIFont *newFont =     [systemFont fontWithSize:18.0f];
    self.textView.font = newFont;

    [textField resignFirstResponder];

    return YES;
}

12. Add this touch event to dismiss the keyboard.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [self.view endEditing:YES];
}

13. Let’s add methods based on notification that will show or hide the keyboard.

- (void)keyboardWillShow:(NSNotification *)aNotification{
    NSTimeInterval animationDuration =
    [[[aNotification userInfo]
objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect frame = self.view.frame;
    frame.origin.y -= 140;
    [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
    [UIView setAnimationDuration:animationDuration];
    self.view.frame = frame;
    [UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification *)aNotification{
    NSTimeInterval animationDuration =
    [[[aNotification userInfo]
objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect frame = self.view.frame;
    frame.origin.y += 140;
    [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
    [UIView setAnimationDuration:animationDuration];
    self.view.frame = frame;
    [UIView commitAnimations];
}

To code the UI, add the following properties and outlets to the implementation file. Wire them accordingly to the XIB via a Ctrl-drag:

@property (weak, nonatomic) IBOutlet UITextField *localeIdentifier;
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (nonatomic, strong) NSString *entireList;

To see the exemplars, enter a value locale identifier, such as “en_US” or “ja_JP” and tap return.

Other Locale-Specific Topics

This last section of the chapter touches on our sample application’s support for right-to-left languages and for a locale’s telephone format.

Right-to-Left Languages

We’ll dig more deeply into support of right-to-left languages such as Arabic and Hebrew in Chapter 6, “Adjusting the UI.” We’ll add a new view controller to this chapter’s project with a Text Field, label, and text view that will demonstrate the required settings and properties to have your app support right-to-left text entries.

Telephone Format

I’d love to tell you it is possible to respect the locale’s setting for phone number formatting, but it’s not. The issue is that the NSNumberFormatter class does not support brackets, spaces, or dashes. If these characters are part of the formatting template, the method will fail silently, and the output will be unformatted text. So if we tried a custom format of @“(###) ###-####” to support a U.S. telephone format starting with a generic NSNumber of @2125551234, the output would not be “(212) 555-1234” but instead would be “2125551234.” GitHub projects are available that will correctly format a given phone number based on locale. These projects include the following:

Image http://the-lost-beauty.blogspot.com/2010/01/locale-sensitive-phone-number.html

Image https://github.com/rmaddy/RMPhoneFormat

Image https://github.com/me2day/libPhoneNumber-iOS

Summary

This chapter covered creating an app while constantly keeping locale support in mind. We covered making sure that currencies, decimal separator characters, and grouping symbols were not hard-coded. We added flexibility to our app to support regions and locales. We “measured twice and cut once” so as not to need to rewrite our code to support other countries and other languages.

In Chapter 5, we’ll run this app through the process to localize strings and test it within different languages and locales.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset