首页 » iOS编程(第4版) » iOS编程(第4版)全文在线阅读

《iOS编程(第4版)》25.1 通过NSNumberFormatter实施国际化

关灯直达底部

本节将使用NSNumberFormatter类为BNRItem的value属性实施数字格式和货币符号的国际化。

实际上,Homepwner中的部分字符串已经实施了国际化。启动Homepwner,添加一个新的BNRItem对象,在添加界面中,BNRDetailViewController中的dateLabel会根据当前区域设置格式化显示日期。在美国,日期的格式是:月 日,年。点击Cancel按钮退出添加界面。

接下来打开设置应用,将Region Format(区域格式)改为United Kingdom(英国)(英文界面:General ? International ? Region Format;中文界面:通用 ? 多语言环境 ? 区域格式)。然后返回Homepwner并再次添加一个新的BNRItem对象,这次dateLabel显示的日期格式是:日 月 年。由此可知,日期字符串已经国际化了。问题是,之前并没有刻意为该字符串实施国际化,Homepwner是如何做到这点的?

图25-2 日期格式:美国与英国

第10章通过NSDateFormatter对象生成了当前日期字符串,并赋给dateLabel的text属性。NSDateFormatter有一个名为locale的属性,默认会指向代表设备当前区域的NSLocale对象。当Homepwner通过NSDateFormatter对象生成日期字符串时,NSDateFormatter对象会先检查自己的locale属性,然后根据区域格式使用对应格式的字符串。因此,日期字符串从一开始就已经国际化了。

NSLocale对象表示某个区域的本土文化信息,其中包括如何显示符号、日期和小数,以及是否使用公制(metric system)等。用户通过设置应用,可以选择当前的区域(region),例如美国或英国。为什么Apple会在这里使用单词区域(region)而不是国家(country)?这是因为某些国家会有多个区域,各自有不同的设置(设置应用的Region Format(区域格式)表格列出了所有可以选择的区域)。

向NSLocale类发送currentLocale消息,可以得到一个NSLocale对象,该对象表示用户的当前区域设置。通过NSLocale对象,可以获取这类信息:“该区域的货币符号是什么?”或“该区域使用的是公制吗?”

要获取此类信息,需要向NSLocale对象发送objectForKey:消息,并传入相应的NSLocale常量(读者可以在NSLocale的类参考手册中找到所有可用的常量)。代码如下:

NSLocale *locale = [NSLocale currentLocale];

BOOL isMetric = [[locale objectForKey:NSLocaleUsesMetricSystem] boolValue];

NSString *currencySymbol = [locale objectForKey:NSLocaleCurrencySymbol];

下面对BNRItemCell中显示的值实施国际化。打开Homepwner.xcodeproj。

NSLocale功能强大,但是直接使用会比较烦琐,因此Apple提供了更方便的类,例如之前使用的NSDateFormatter。类似的还有NSNumberFormatter,可以根据区域设置格式化显示数字。NSNumberFormatter提供了stringFromNumber:方法,用于返回当前区域格式的数字,如123, 456.789或123 456, 789等。

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];

NSString *numberAsString = [numberFormatter stringFromNumber:@123456.789];

NSNumberFormatter对象还可以格式化显示货币。如果将NSNumberFormatter对象的numberStyle属性设置为NSNumberFormatterCurrencyStyle,NSNumberFormatter对象不但会格式化数字,而且会添加当前区域格式的货币符号。(在某些国家,NSNumberFormatter在数字格式与货币格式下返回的字符串可能有很大差异,不仅仅是多了一个货币符号。)

NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];

currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;

NSString *numberAsString = [currencyFormatter stringFromNumber:@123456.789];

在BNRItemsViewController.m的tableView:cellForRowAtIndexPath:方法中添加一个静态的NSNumberFormatter对象currencyFormatter,并将numberStyle设置为NSNumberFormatterCurrencyStyle。代码如下:

cell.serialNumberLabel.text = item.serialNumber;

// 创建一个静态NSNumberFormatter对象

static NSNumberFormatter *currencyFormatter = nil;

if (currencyFormatter == nil) {

currencyFormatter = [[NSNumberFormatter alloc] init];

currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;

}

cell.valueLabel.text = [NSString stringWithFormat:@“$%d”,

item.valueInDollars];

目前BNRItemCell使用“$%d”格式生成表示物品价值的字符串。这样会导致即使当前区域格式不是美国(United States),物品价值仍然会显示美元符号。下面改用currencyFormatter生成当前区域格式的物品价值,代码如下:

// 为货币创建一个NSNumberFormatter

static NSNumberFormatter *currencyFormatter = nil;

if (currencyFormatter == nil) {

currencyFormatter = [[NSNumberFormatter alloc] init];

currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;

}

cell.valueLabel.text = [NSString stringWithFormat:@“$%d”,

item.valueInDollars];

cell.valueLabel.text = [currencyFormatter

stringFromNumber:@(item.valueInDollars)];

cell.thumbnailView.image = item.thumbnail;

构建并运行应用。如果读者从一开始就按照本书的步骤操作,那么现在BNRItemCell显示的物品价值格式应该是United Kingdom(英国)。接下来在设置应用中将区域格式重新改为United States(美国),再返回Homepwner。

读者也许会以为BNRItemCell会恢复到美国格式,显示以美元($)为单位的物品价值,但是BNRItemCell并没有立刻更新物品价值的格式。这时需要重新加载UITableView对象的数据。可以先进入添加界面再取消添加,这样BNRItemsViewController会收到viewWillAppear:消息并调用UITableView对象的reloadData方法,更新BNRItemCell。这时BNRItemCell会正确地显示以美元为单位的物品价值(注意,这里仅仅是替换了符号,并没有进行英镑到美元的汇率转换)。

如果要及时响应当前区域设置的变化,可以向NSNotificationCenter注册当前区域设置发生变化的通知:NSCurrentLocaleDidChangeNotification。在BNRItemsView- Controller对象的init方法中,将BNRItemsViewController对象注册为该通知的观察者:

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

[nc addObserver:self

selector:@selector(updateTableViewForDynamicTypeSize)

name:UIContentSizeCategoryDidChangeNotification

object:nil];

// 注册当前区域设置发生变化的通知

[nc addObserver:self

selector:@selector(localeChanged:)

name:NSCurrentLocaleDidChangeNotification

object:nil];

}

return self;

然后添加localeChanged:方法,代码如下:

- (void)localeChanged:(NSNotification *)note

{

[self.tableView reloadData];

}

构建并运行应用。先在设置应用中修改当前的区域格式,再返回Homepwner。这次BNRItemCell会立刻更新物品价值的显示格式。

之前介绍过使用NSLocale对象获取当前区域设置的货币符号,读者可能会想到直接使用该符号拼接一个具体数值作为货币字符串。实际上,不同区域的货币格式可能不仅仅是货币符号不同,为了理解使用NSNumberFormatter的好处,请读者将区域格式改为德国(Germany)。从图25-3可以发现,除了货币符号之外,还有一些细节也发生了变化:①货币符号的位置(德国:货币符号位于数值之后;美国或英国:货币符号位于数值之前)。②空格(德国:数值与货币符号之间有一个空格;美国或英国:数值与货币符号之间没有空格)。③小数点符号(德国:逗号;美国或英国:点号)。④千位分隔符(德国:点号;美国或英国:逗号)。

图25-3 数字格式:美国、英国和德国