iOS开发基础128-应用本地化

iOS 应用程序的本地化(Localization)是指将应用程序的表现、文本与内容适配到不同的语言和地区,以便为全球用户提供良好的用户体验。苹果的 iOS 系统提供了一整套工具和框架来支持本地化。以下是详细的步骤和代码示例,用于在 iOS 应用程序中实现本地化。

一、基本流程

  1. 准备项目:在 Xcode 中创建一个新项目。
  2. 添加本地化支持:配置项目以支持多语言。
  3. 本地化字符串:创建 .strings 文件,并添加本地化字符串。
  4. 本地化资源文件:本地化图片、界面文件(如 .xib.storyboard 等)。
  5. 设置和读取本地化字符串:代码中如何读取本地化字符串。
  6. 切换语言:提供应用内语言切换功能(可选)。

二、具体步骤

1. 准备项目

在 Xcode 中创建一个新的 iOS 项目,命名为 "LocalizationDemo"。

2. 添加本地化支持

首先,打开你的项目设置,找到 "Localizations" 部分,点击 "+" 号添加新的语言。

  1. 选择你的项目文件(一般是根目录下与 .xcodeproj 文件同名的文件)。
  2. 在 "Info" 面板中,找到 "Localizations",点击 "+" 按钮添加需要支持的语言。例如,选择 "Chinese (Simplified)".

Xcode 将提示你选择需要本地化的文件,通常你至少需要选择 Main.storyboardInfoPlist.strings.

3. 本地化字符串

创建 .strings 文件并添加本地化字符串。

  1. 通过右键点击项目并选择 "New File..."(或者通过菜单栏选择 File > New > File),选择 Strings File 创建一个新的文件,命名为 Localizable.strings
  2. 添加对不同语言的支持:在资源导航器中选中 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. 切换应用内语言(可选)

在应用中提供语言切换功能是一个高级的需求。需要动态设置语言并使之在整个应用中生效。以下是一个实现方法。

  1. 创建一个类别:用于在运行时切换语言。

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
  1. 使用 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 应用程序实现全面的本地化支持。

  1. 在 Xcode 中配置本地化支持。
  2. 创建和管理本地化字符串文件 Localizable.strings
  3. 本地化资源文件,如图片和界面文件(.xib 和 .storyboard)。
  4. 通过 NSLocalizedString 宏动态读取本地化字符串。
  5. 使用 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。

示例代码

  1. 定义通知常量
// 在某个公共头文件中定义
extern NSString * const LanguageDidChangeNotification;
  1. 发布通知
[[NSNotificationCenter defaultCenter] postNotificationName:LanguageDidChangeNotification object:nil];
  1. 订阅通知

在需要更新 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 更新需求。选择哪种方法依赖于项目的复杂度和具体需求。

posted @ 2024-07-18 10:14  Mr.陳  阅读(19)  评论(0编辑  收藏  举报