[iOS] 多语言-图片、布局、文字等

iOS的多语言和Android比起来感觉上还是更方便一点,毕竟XCode的操作感更强一点,但是会辛苦PM同学啦。。

首先,多语言这个事儿是分为两种的,(1) 是App内有切换语言的入口,比如东航的app就可以在app内切换语言,不管你的系统语言是神马;(2) 就是跟着系统settings里面的language走。当然这两种实现其实是相斥的,如果App内有语言切换入口,那么就不应该随着系统语言改变而改变啦。

1. 如何跟着系统语言切换

首先我们先来看第一种,根据Settings里面的语言设置自动更改App内的语言。

  • 插一句如何知道系统当前的语言:
NSArray *appLanguages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"];
NSString *languageName = [appLanguages objectAtIndex:0];

输出:
zh-Hans-CN

主要需要更改语言的主要分三大类:
(1)系统提示、App名称等
(2)字符串
(3)xib
(4)图片、音乐、视频等

但这些能够实现随语言切换最基础的基础,是要先在Target的地方给App设置支持的语言,如下图所示:

增加语言支持
※App名称等

App的各种信息其实都是存在InfoPlist里面滴,所以其实这些系统信息的多语言化,就是InfoPlist的多语言化。

首先先新建一个文件,类型是Strings File:


新建多语言字符串文件

然后重命名为InfoPlist.strings,然后选中文件后,在右侧选中Localization。


本地化

然后我们再次点击这个文件,右侧的Localization就变为了下图左侧的样子,勾选一下没有选中的Chinese,我们的文件左边就会有一个小三角,展开可以看到两个文件,一个是English的,一个是Chinese的。

字符串多语言

然后我们就可以分别编辑一下这两个文件,例如设置App名称在两种语言下不一样,就可以分别写入:

// English
CFBundleDisplayName = "Example 1";

// Simplified Chinese
CFBundleDisplayName = "样例1";

然后切换语言你就会发现,App名称会自动变化啦~~

一个小小的注意事项:
如果是debug并且和真机连着调试ing,切换语言会导致App crash,并且如果不断开调试,不断打开不断crash。。这个非常正常,用户切换语言的话,所有App都会重新启动,所以只要断掉调试,就可以正常打开了,也不会有crash,只是切换语言会重新看到splash罢了。

如果我们把上面的两个InfoPlist文件show in finder,会发现其实这两个文件在不同的文件夹下,如下图所示:


目录结构

其实多语言就是把不同的语言文件分开放入不同的folder酱紫,但Xcode会帮你做,这点还是蛮方便滴。

※字符串

字符串其实和上面的App信息非常类似,毕竟其实App info也是字符串显示出来的。第一步仍旧是新建一个Strings File,然后选择右侧的Localization,并且勾选你需要的语言,最后会生成多个语言的文件,分别编辑即可。

多语言字符串

这个文件的编辑形式就是key value键值对,左边是key,然后右边的value就是字符串的内容,每对儿最后加个分号。我们在程序中只需要传入Key,App就会自动根据当前语言文件里面的键值对找到相应的value。

self.textField.text = NSLocalizedString(@"text_content", nil);

第一个参数是key,第二个是comment就是注释,用于说明这个key的,可以传nil。

如果key写错了会怎么样呢?
我试着把key写成了text_contentt,发现界面显示的就是text_contentt,不会显示空白,会直接把key作为value。

#define NSLocalizedString(key, comment) \
        [NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:nil]
#define NSLocalizedStringFromTable(key, tbl, comment) \
        [NSBundle.mainBundle localizedStringForKey:(key) value:@"" table:(tbl)]
#define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
        [bundle localizedStringForKey:(key) value:@"" table:(tbl)]
#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \
        [bundle localizedStringForKey:(key) value:(val) table:(tbl)]

使用NSLocalizedString(key, comment)读取字符串只能读取主bundle中Localizable.strings文件内的key value,如果你的文件不叫Localizable.strings,假设叫donepage.strings,那么你要引入的时候就应该用下面的语句:

NSLocalizedStringFromTable(@"text_content",@"donepage", nil)

如果你用了一个专门的语言包bundle,则可以使用下面的两种NSLocalizedStringFromTableInBundle & NSLocalizedStringWithDefaultValue来设置~
(P.S. 其实我感觉语言包还挺好的,方便多个app复用。)

※xib

布局的多语言会有一点不一样,如果你希望改界面的字符串有很多种办法,可以用代码NSLocalizedString来区分,还可以直接用xib做。

类似上面的操作,xib文件的右侧选则Localization,然后也会生成多个文件,如下图所示:


xib的多语言

此时如果你打开下面的文件,会发现里面也是key value对,只是key其实是系统自动根据xib对应生成的,不可以自己修改哦,我们只需要改value就可以啦~

xib的键值对

比较神奇的是,xib不仅支持文字的多语言,还支持不同语言使用不同的xib,你只需要按照下图的设置将string改为xib,即会生成不同的xib文件,来替代Strings File。

布局多语言

这个时候我们的文件就会变成下面这样啦,然后你就可以改布局了~


布局文件

其实这个超方便,毕竟不同语言可能长度不一样会对布局美观产生影响,对部分语言做特殊的布局可以更好的适配。

※图片等其他资源

以前版本的Xcode对多语言图片、音频等资源的支持好像木有现在这么好,现在只要和字符串类似,同样是从右侧的Localization开启并勾选你想设置的语言,就会生成多个文件,然后你只要用show in finder然后替换你想换掉的语言的图片就可以啦,使用的时候仍旧是用[UIImage imageNamed:]就行,和正常使用没有任何区别。

图片多语言化

其实音频之类的也可以多语言化~ 虽然我还木有尝试。。但其实原理都是一样的,同名文件放入不同语言的folder,当然这个只对本地音频有效啦,如果从网上去下载音频,这种时候就需要判断当前语言了。

2. App内设置语言切换

第一部分的系统语言切换其实非常方便,而且修改语言以后App还会自动重启。但是有些App是可以与系统语言不一致的,支持App内独立设置语言,这种时候如何实现呢?

App内切换语言的重点就是切换bundle,从不同语言的资源包拿文件,如果图片之类的需要也是酱紫做。注意哦,如果App内有设置入口,不用在target的地方增加语言以免矛盾,那个只是系统切换语言用的。

以字符串为例,先建几个bundle,例如下图:


语言bundle

然后写一个Manager来进行语言偏好的保存及修改:(这里用UserDefault存实际可以用MMKV哈)

// .h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

static NSString * const kChineseSimple = @"zh-Hans";
static NSString * const kChineseTraditional = @"zh-Hant";
static NSString * const kEnglishUS = @"en";

static NSString * const kNotificationUserLanguageChanged = @"UserLanguageChanged";

@interface LanguageManager : NSObject

+ (instancetype)sharedInstance;

- (NSString *)getStringForKey:(NSString *)key;
- (void)setNewLanguage:(NSString *)language;

@end

NS_ASSUME_NONNULL_END


// .m文件
#import "LanguageManager.h"

static NSString * const kUserLanguage = @"UserLanguage";

@implementation LanguageManager

+ (instancetype)sharedInstance {
    static LanguageManager *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[LanguageManager alloc] init];
    });
    
    return instance;
}

- (NSBundle *)bundle {
    NSString * setLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:kUserLanguage];
    
    if (setLanguage == nil) {
        setLanguage = kChineseSimple;
    }
    NSString * bundlePath = [[NSBundle mainBundle] pathForResource:setLanguage ofType:@"lproj"];
    
    return [NSBundle bundleWithPath:bundlePath];
}

- (NSString *)getStringForKey:(NSString *)key {
    NSBundle * bundle = [[LanguageManager sharedInstance] bundle];
    if (bundle) {
        
        return NSLocalizedStringFromTableInBundle(key, @"Localizable", bundle, nil);
    }
    
    return NSLocalizedString(key, nil);
}

- (void)setNewLanguage:(NSString *)language {
    NSString * setLanguage = [[NSUserDefaults standardUserDefaults] objectForKey:kUserLanguage];
    if ([language isEqualToString:setLanguage]) {
        return;
    }
    
    else if ([language isEqualToString:kChineseSimple]) {
        [[NSUserDefaults standardUserDefaults] setObject:kChineseSimple forKey:kUserLanguage];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    
    else if ([language isEqualToString:kChineseTraditional]) {
        [[NSUserDefaults standardUserDefaults] setObject:kChineseTraditional forKey:kUserLanguage];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    
    else if ([language isEqualToString:kEnglishUS]) {
        [[NSUserDefaults standardUserDefaults] setObject:kEnglishUS forKey:kUserLanguage];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    
    [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationUserLanguageChanged object:nil];
}

@end

然后页面只要使用manager的方法来设置字符串,并且监听语言切换的notification,并进行update字符串即可。
这里其实还可以利用runtime给对象增加自动监听notification并且update的方法,但这里就简单的把职责交给页面啦

@implementation MainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self updateLanguage];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateLanguage) name:kNotificationUserLanguageChanged object:nil];
}

- (void)updateLanguage {
    self.textField.text = [[LanguageManager sharedInstance] getStringForKey:@"text_content"];
}

- (IBAction)chineseAction:(id)sender {
    [[LanguageManager sharedInstance] setNewLanguage:kChineseSimple];
}


- (IBAction)englishAction:(id)sender {
    [[LanguageManager sharedInstance] setNewLanguage:kEnglishUS];
}

实现的效果如下图:


App内语言切换

如果你希望图片等资源文件也跟着语言切换变动,则也需要manager提供图片设置的接口,从相应的bundle里面拿资源,并且页面监听到notification以后重新设置一遍图片。

Reference:
1.https://www.jb51.net/article/96872.htm
2.https://www.jianshu.com/p/55e5732e9b5b

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容