iOS开发基础128-应用本地化
iOS 应用程序的本地化(Localization)是指将应用程序的表现、文本与内容适配到不同的语言和地区,以便为全球用户提供良好的用户体验。苹果的 iOS 系统提供了一整套工具和框架来支持本地化。以下是详细的步骤和代码示例,用于在 iOS 应用程序中实现本地化。
一、基本流程
- 准备项目:在 Xcode 中创建一个新项目。
- 添加本地化支持:配置项目以支持多语言。
- 本地化字符串:创建
.strings
文件,并添加本地化字符串。 - 本地化资源文件:本地化图片、界面文件(如
.xib
、.storyboard
等)。 - 设置和读取本地化字符串:代码中如何读取本地化字符串。
- 切换语言:提供应用内语言切换功能(可选)。
二、具体步骤
1. 准备项目
在 Xcode 中创建一个新的 iOS 项目,命名为 "LocalizationDemo"。
2. 添加本地化支持
首先,打开你的项目设置,找到 "Localizations" 部分,点击 "+" 号添加新的语言。
- 选择你的项目文件(一般是根目录下与
.xcodeproj
文件同名的文件)。 - 在 "Info" 面板中,找到 "Localizations",点击 "+" 按钮添加需要支持的语言。例如,选择 "Chinese (Simplified)".
Xcode 将提示你选择需要本地化的文件,通常你至少需要选择 Main.storyboard
和 InfoPlist.strings
.
3. 本地化字符串
创建 .strings
文件并添加本地化字符串。
- 通过右键点击项目并选择 "New File..."(或者通过菜单栏选择
File > New > File
),选择Strings File
创建一个新的文件,命名为Localizable.strings
。 - 添加对不同语言的支持:在资源导航器中选中
Localizable.strings
文件,然后在右侧面板(File Inspector)中勾选需要支持的语言。
Xcode 会为每个语言创建一个 Localizable.strings
文件。
示例 Localizable.strings
文件内容:
- English (默认)
"greeting" = "Hello";
"farewell" = "Goodbye";
- Chinese (Simplified)
"greeting" = "你好";
"farewell" = "再见";
4. 本地化资源文件
可以同样方式本地化图片和界面文件。例如:
- 对于图片,你可以将它们放在适当的语言文件夹中,如
zh-Hans.lproj
。 - 对于
.storyboard
或.xib
文件,可以在文件检查器中勾选需要支持的语言。
本地化 Main.storyboard
在 Xcode 中选中 Main.storyboard
文件,然后在右侧属性检查器中添加支持的语言。Xcode 会为每种语言创建一个独立的 .strings
文件,用于翻译 storyboard
中的文本。
示例
/* English - Main.strings */
"27.title" = "Main Screen";
"5uE-hV-x4d.text" = "Press me";
/* 简体中文 - Main.strings (Chinese Simplified) */
"27.title" = "主界面";
"5uE-hV-x4d.text" = "点我";
5. 读取本地化字符串
在代码中读取本地化字符串使用 NSLocalizedString
宏。
示例代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UILabel *greetingLabel;
@property (nonatomic, strong) UIButton *farewellButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.greetingLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 50)];
self.greetingLabel.text = NSLocalizedString(@"greeting", @"Greeting");
[self.view addSubview:self.greetingLabel];
self.farewellButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.farewellButton.frame = CGRectMake(50, 200, 200, 50);
[self.farewellButton setTitle:NSLocalizedString(@"farewell", @"Farewell") forState:UIControlStateNormal];
[self.farewellButton addTarget:self action:@selector(farewellButtonTapped) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.farewellButton];
}
- (void)farewellButtonTapped {
NSLog(@"%@", NSLocalizedString(@"farewell", @"Farewell"));
}
@end
6. 切换应用内语言(可选)
在应用中提供语言切换功能是一个高级的需求。需要动态设置语言并使之在整个应用中生效。以下是一个实现方法。
- 创建一个类别:用于在运行时切换语言。
NSBundle+Language.h
#import <Foundation/Foundation.h>
@interface NSBundle (Language)
+ (void)setLanguage:(NSString*)language;
@end
NSBundle+Language.m
#import "NSBundle+Language.h"
#import <objc/runtime.h>
static const char _bundle = 0;
@interface BundleEx : NSBundle
@end
@implementation BundleEx
- (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
NSBundle *bundle = objc_getAssociatedObject(self, &_bundle);
return bundle ? [bundle localizedStringForKey:key value:value table:tableName] : [super localizedStringForKey:key value:value table:tableName];
}
@end
@implementation NSBundle (Language)
+ (void)setLanguage:(NSString*)language {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
object_setClass([NSBundle mainBundle], [BundleEx class]);
});
if (language == nil) {
objc_setAssociatedObject([NSBundle mainBundle], &_bundle, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} else {
NSString *path = [[NSBundle mainBundle] pathForResource:language ofType:@"lproj"];
NSBundle *bundle = [NSBundle bundleWithPath:path];
objc_setAssociatedObject([NSBundle mainBundle], &_bundle, bundle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
@end
- 使用
NSBundle
切换语言:
示例代码
#import "ViewController.h"
#import "NSBundle+Language.h"
@interface ViewController ()
@property (nonatomic, strong) UILabel *greetingLabel;
@property (nonatomic, strong) UIButton *languageButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.greetingLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 50)];
self.greetingLabel.text = NSLocalizedString(@"greeting", @"Greeting");
[self.view addSubview:self.greetingLabel];
self.languageButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.languageButton.frame = CGRectMake(50, 200, 200, 50);
[self.languageButton setTitle:NSLocalizedString(@"farewell", @"Farewell") forState:UIControlStateNormal];
[self.languageButton addTarget:self action:@selector(changeLanguage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.languageButton];
}
- (void)changeLanguage {
// Switch between English and Simplified Chinese for demonstration
NSString *currentLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:@"appLanguage"];
NSString *newLanguage = [currentLanguage isEqualToString:@"zh-Hans"] ? @"en" : @"zh-Hans";
[[NSUserDefaults standardUserDefaults] setObject:newLanguage forKey:@"appLanguage"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSBundle setLanguage:newLanguage];
[self updateUI];
}
- (void)updateUI {
self.greetingLabel.text = NSLocalizedString(@"greeting", @"Greeting");
[self.languageButton setTitle:NSLocalizedString(@"farewell", @"Farewell") forState:UIControlStateNormal];
}
@end
三、小结
通过上述步骤,可以为你的 iOS 应用程序实现全面的本地化支持。
- 在 Xcode 中配置本地化支持。
- 创建和管理本地化字符串文件
Localizable.strings
。 - 本地化资源文件,如图片和界面文件(.xib 和 .storyboard)。
- 通过
NSLocalizedString
宏动态读取本地化字符串。 - 使用
NSBundle
动态切换应用内语言(可选)。
本地化是一个细致的过程,涉及到用户界面的各个方面。
四、关于NSLocalizedString
NSLocalizedString
是一个由苹果提供的宏,专门用来处理本地化字符串,它是 iOS 和 macOS 开发中本地化的基础工具。这个宏的定义如下:
#define NSLocalizedString(key, comment) \
[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
它的作用是从主 Bundle 中的 Localizable.strings
文件中查找对应的键值对,并返回本地化后的字符串。如果找不到对应的键,则使用传入的键作为默认值。
如何高效地更新 UI?如果每此语言切换都要手动写 updateUI
来更新所有 UI 元素的本地化字符串,这样的做法会非常繁琐且容易出错。为了简化 UI 更新,可以采用以下几种策略:
五、使用观察者模式
通过 NotificationCenter
发送语言切换通知,所有订阅该通知的对象可以在接收到通知时更新其 UI。
示例代码
- 定义通知常量
// 在某个公共头文件中定义
extern NSString * const LanguageDidChangeNotification;
- 发布通知
[[NSNotificationCenter defaultCenter] postNotificationName:LanguageDidChangeNotification object:nil];
- 订阅通知
在需要更新 UI 的视图控制器中订阅通知,并在发生通知时更新 UI。
#import "ViewController.h"
#import "NSBundle+Language.h"
NSString * const LanguageDidChangeNotification = @"LanguageDidChangeNotification";
@interface ViewController ()
@property (nonatomic, strong) UILabel *greetingLabel;
@property (nonatomic, strong) UIButton *languageButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.greetingLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 50)];
[self.view addSubview:self.greetingLabel];
self.languageButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.languageButton.frame = CGRectMake(50, 200, 200, 50);
[self.languageButton addTarget:self action:@selector(changeLanguage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.languageButton];
[self updateUI];
// 订阅语言变化通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(languageDidChange)
name:LanguageDidChangeNotification object:nil];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)changeLanguage {
// 切换语言逻辑(同前)
NSString *currentLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:@"appLanguage"];
NSString *newLanguage = [currentLanguage isEqualToString:@"zh-Hans"] ? @"en" : @"zh-Hans";
[[NSUserDefaults standardUserDefaults] setObject:newLanguage forKey:@"appLanguage"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSBundle setLanguage:newLanguage];
[[NSNotificationCenter defaultCenter] postNotificationName:LanguageDidChangeNotification object:nil];
}
- (void)updateUI {
self.greetingLabel.text = NSLocalizedString(@"greeting", @"Greeting");
[self.languageButton setTitle:NSLocalizedString(@"farewell", @"Farewell") forState:UIControlStateNormal];
}
- (void)languageDidChange {
[self updateUI];
}
@end
六、应用 MVVM 模式
通过 MVVM(Model-View-ViewModel)模式,将数据和逻辑与视图分离,实现更简洁的界面更新。ViewModel 类检测语言变化,并通知 ViewController 更新视图。
七、使用 KVO(Key-Value Observing)
观察语言设置的变化,一旦检测到变化,更新视图。
示例代码
// 在 ViewModel 中观察语言改变
@interface ViewModel : NSObject
@property (nonatomic, strong) NSString *currentLanguage;
// 其他属性和方法
@end
@implementation ViewModel
- (instancetype)init {
self = [super init];
if (self) {
// 注册观察语言设置变化
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"appLanguage"
options:NSKeyValueObservingOptionNew
context:nil];
[self updateCurrentLanguage];
}
return self;
}
- (void)dealloc {
[[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:@"appLanguage"];
}
- (void)updateCurrentLanguage {
self.currentLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:@"appLanguage"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"appLanguage"]) {
[self updateCurrentLanguage];
}
}
@end
在 ViewController 中使用 ViewModel 更新 UI:
@interface ViewController ()
@property (nonatomic, strong) ViewModel *viewModel;
@property (nonatomic, strong) UILabel *greetingLabel;
@property (nonatomic, strong) UIButton *languageButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.viewModel = [[ViewModel alloc] init];
self.greetingLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 200, 50)];
[self.view addSubview:self.greetingLabel];
self.languageButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.languageButton.frame = CGRectMake(50, 200, 200, 50);
[self.languageButton addTarget:self action:@selector(changeLanguage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.languageButton];
// 订阅 ViewModel 的变化
[self.viewModel addObserver:self forKeyPath:@"currentLanguage" options:NSKeyValueObservingOptionNew context:nil];
[self updateUI];
}
- (void)dealloc {
[self.viewModel removeObserver:self forKeyPath:@"currentLanguage"];
}
- (void)changeLanguage {
// 切换语言逻辑与前同
NSString *currentLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:@"appLanguage"];
NSString *newLanguage = [currentLanguage isEqualToString:@"zh-Hans"] ? @"en" : @"zh-Hans";
[[NSUserDefaults standardUserDefaults] setObject:newLanguage forKey:@"appLanguage"];
[[NSUserDefaults standardUserDefaults] synchronize];
[NSBundle setLanguage:newLanguage];
}
- (void)updateUI {
self.greetingLabel.text = NSLocalizedString(@"greeting", @"Greeting");
[self.languageButton setTitle:NSLocalizedString(@"farewell", @"Farewell") forState:UIControlStateNormal];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"currentLanguage"]) {
[self updateUI];
}
}
@end
在实际开发中,可以通过 NotificationCenter
、KVO 或 MVVM 等模式,简化和自动化 UI 更新,以应对语言切换带来的频繁 UI 更新需求。选择哪种方法依赖于项目的复杂度和具体需求。