本节将使用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 数字格式:美国、英国和德国