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