IOS本地化应用
BK项目已完成7788,在项目的后期需要被翻译成多国语言版。为了适应全球多个国家使用多个存储。
应用本地化是分别对字符串、图片和 xib 或 storyboard 文件本地化,而传统的做法是对 xib 上的字符串(包含UILabel和UIButton、UITextField等)关联一个变量,通过NSLocalizedString这个函数去查找 Localizeable .strings 文件里的key值进行本地化操作,或者是生成同一个xib文件的不同语言版本号。如 MainVC.xib(German) 和 MainVC.xib(English)。但这样做未免过于繁杂,像人们常说的 tedious and useless.
还是先介绍一下本地化的一般流程:
(1)伪本地化伪本地化是将字符串本地化为无意义语言的过程。
即将须要翻译的字符串替换成其它如果已经是翻译过的“译文”,能够用谷歌翻译替换一下或者是将全部元音字母替换成x。比如:“Press here to continue” 会变成 “Prxss hxrx tx cxntxnxx”。这样做的目的是为了尽早发现问题。
(2)冻结UI 在应用开发的周期中必需要有一个明白的时间点来冻结UI。在此之后要坚决杜绝会影响本地化的资源变更。nib 文件能够在XCode中锁定。以防止改动可本地化的属性、不可本地化的属性或者是全部属性,如图所看到的。然后将需要翻译的文本发给翻译人员或者是本地化服务提供商去翻译即可了。
(3)本地化将资源发给本地化服务提供商之后,他们会发回翻译完毕的文件。依据翻译的文本进行本地化工作。
(4)版本号控制用版本号控制系统记录下你的每一次变更。
(5)測试不用怎么说,必需要的步骤。
(6a)合并逻辑变更逻辑变更一般不会影响到nib文件和本地化的工作。多人协作的项目还是须要合并一些变更的逻辑的。
(6b)本地化变更假设你做了一些本地化变更,比方改变了已本地化的文本,那么就须要从头開始这个过程。并将这些变更发给本地化人员。能够重用之前的字符串翻译,这么做会大大提高效率,但仍然非常麻烦。所以,应尽量避免在开发后期引入这类变更。
应用本地化的文章之前已经就有非常多大牛写过了。这里就不在赘述了,直接贴出本人读过的认为还不错的文章:
1、MJ 的应用程序本地化,2013年写的,对于XCode5,有些操作界面已经不一样了,但思想是不变的。
2、IOS应用国际化教程(2014版),这个比較新,并且是使用 storyboard 的。
3、RAYWENDERLICH 上的 Internationalization Tutorial for iOS [2014 Edition] 这上面的文章都非常不错,非常值得一读,强烈推荐。
另外。重点是要讲我在 github 上找到的一个类,很棒,优雅的代码一直感动到我眼泪哗哗直流~~
这是github上的项目地址:HERE ,假设有找到很多其它更好的优秀代码,请知会一声。
就像作者所说的那样:
下面是OHAutoNIBi18n.m类。做了一下小改动,在不改变 frame 的情况下。对 UILabel、UIButton、UITextField 的字体大小做了一下自适应。
// // OHAutoNIBi18n.m // // Created by Olivier on 03/11/10. // Copyright 2010 FoodReporter. All rights reserved. // #import <objc/runtime.h> #import <UIKit/UIKit.h> static inline NSString* localizedString(NSString* aString); static inline void localizeUIBarButtonItem(UIBarButtonItem* bbi); static inline void localizeUIBarItem(UIBarItem* bi); static inline void localizeUIButton(UIButton* btn); static inline void localizeUILabel(UILabel* lbl); static inline void localizeUINavigationItem(UINavigationItem* ni); static inline void localizeUISearchBar(UISearchBar* sb); static inline void localizeUISegmentedControl(UISegmentedControl* sc); static inline void localizeUITextField(UITextField* tf); static inline void localizeUITextView(UITextView* tv); static inline void localizeUIViewController(UIViewController* vc); // ------------------------------------------------------------------------------------------------ @interface NSObject(OHAutoNIBi18n) -(void)localizeNibObject; @end @implementation NSObject(OHAutoNIBi18n) #define LocalizeIfClass(Cls) if ([self isKindOfClass:[Cls class]]) localize##Cls((Cls*)self) -(void)localizeNibObject { LocalizeIfClass(UIBarButtonItem); else LocalizeIfClass(UIBarItem); else LocalizeIfClass(UIButton); else LocalizeIfClass(UILabel); else LocalizeIfClass(UINavigationItem); else LocalizeIfClass(UISearchBar); else LocalizeIfClass(UISegmentedControl); else LocalizeIfClass(UITextField); else LocalizeIfClass(UITextView); else LocalizeIfClass(UIViewController); if (self.isAccessibilityElement == YES) { self.accessibilityLabel = localizedString(self.accessibilityLabel); self.accessibilityHint = localizedString(self.accessibilityHint); } // Call the original awakeFromNib method [self localizeNibObject]; // this actually calls the original awakeFromNib (and not localizeNibObject) because we did some method swizzling } +(void)load { // Autoload : swizzle -awakeFromNib with -localizeNibObject as soon as the app (and thus this class) is loaded Method localizeNibObject = class_getInstanceMethod([NSObject class], @selector(localizeNibObject)); Method awakeFromNib = class_getInstanceMethod([NSObject class], @selector(awakeFromNib)); method_exchangeImplementations(awakeFromNib, localizeNibObject); } @end ///////////////////////////////////////////////////////////////////////////// static inline NSString* localizedString(NSString* aString) { if (aString == nil || [aString length] == 0) return aString; // Don't translate strings starting with a digit if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[aString characterAtIndex:0]]) return aString; #if OHAutoNIBi18n_DEBUG #warning Debug mode for i18n is active static NSString* const kNoTranslation = @"$!"; NSString* tr = [[NSBundle mainBundle] localizedStringForKey:aString value:kNoTranslation table:nil]; if ([tr isEqualToString:kNoTranslation]) { if ([aString hasPrefix:@"."]) { // strings in XIB starting with '.' are typically used as temporary placeholder for design // and will be replaced by code later, so don't warn about them return aString; } NSLog(@"No translation for string '%@'",aString); tr = [NSString stringWithFormat:@"$%@$",aString]; } return tr; #else return [[NSBundle mainBundle] localizedStringForKey:aString value:nil table:nil]; #endif } // ------------------------------------------------------------------------------------------------ static inline void localizeUIBarButtonItem(UIBarButtonItem* bbi) { localizeUIBarItem(bbi); /* inheritence */ NSMutableSet* locTitles = [[NSMutableSet alloc] initWithCapacity:[bbi.possibleTitles count]]; for(NSString* str in bbi.possibleTitles) { [locTitles addObject:localizedString(str)]; } bbi.possibleTitles = [NSSet setWithSet:locTitles]; #if ! __has_feature(objc_arc) [locTitles release]; #endif } static inline void localizeUIBarItem(UIBarItem* bi) { bi.title = localizedString(bi.title); } static inline void localizeUIButton(UIButton* btn) { NSString* title[4] = { [btn titleForState:UIControlStateNormal], [btn titleForState:UIControlStateHighlighted], [btn titleForState:UIControlStateDisabled], [btn titleForState:UIControlStateSelected] }; [btn.titleLabel setAdjustsFontSizeToFitWidth:YES]; [btn setTitle:localizedString(title[0]) forState:UIControlStateNormal]; if (title[1] == [btn titleForState:UIControlStateHighlighted]) [btn setTitle:localizedString(title[1]) forState:UIControlStateHighlighted]; if (title[2] == [btn titleForState:UIControlStateDisabled]) [btn setTitle:localizedString(title[2]) forState:UIControlStateDisabled]; if (title[3] == [btn titleForState:UIControlStateSelected]) [btn setTitle:localizedString(title[3]) forState:UIControlStateSelected]; } static inline void localizeUILabel(UILabel* lbl) { lbl.adjustsFontSizeToFitWidth = YES; // lbl.minimumScaleFactor = 6.0f; lbl.text = localizedString(lbl.text); } static inline void localizeUINavigationItem(UINavigationItem* ni) { ni.title = localizedString(ni.title); ni.prompt = localizedString(ni.prompt); } static inline void localizeUISearchBar(UISearchBar* sb) { sb.placeholder = localizedString(sb.placeholder); sb.prompt = localizedString(sb.prompt); sb.text = localizedString(sb.text); NSMutableArray* locScopesTitles = [[NSMutableArray alloc] initWithCapacity:[sb.scopeButtonTitles count]]; for(NSString* str in sb.scopeButtonTitles) { [locScopesTitles addObject:localizedString(str)]; } sb.scopeButtonTitles = [NSArray arrayWithArray:locScopesTitles]; #if ! __has_feature(objc_arc) [locScopesTitles release]; #endif } static inline void localizeUISegmentedControl(UISegmentedControl* sc) { NSUInteger n = sc.numberOfSegments; for(NSUInteger idx = 0; idx<n; ++idx) { [sc setTitle:localizedString([sc titleForSegmentAtIndex:idx]) forSegmentAtIndex:idx]; } } static inline void localizeUITextField(UITextField* tf) { tf.adjustsFontSizeToFitWidth = YES; tf.text = localizedString(tf.text); tf.placeholder = localizedString(tf.placeholder); } static inline void localizeUITextView(UITextView* tv) { tv.text = localizedString(tv.text); } static inline void localizeUIViewController(UIViewController* vc) { vc.title = localizedString(vc.title); }
tips:本地化的时候还须要注意:
1、别忘了从右向左读的语言。
2、不要随便如果逗号就是千位分隔符以及句点就是小数点。在不同的语言中可能会有不同。
3、注意数字和日期的格式化(输入和输出都须要进行格式化)。
至此,,应用本地化的事儿就简单多了,剩下的事情就交给翻译人员去吧~
參考文章:
Apple官方文档:Localizing your APP
Apple官方文档:Internationalize Your App
Apple官方文档: Data Formatting Guide
Apple官方文档: Internationalization Programming Topics
Apple官方文档: Internationalization and Localization
本地化协作工具:https://www.transifex.com/product/
版权声明:本文博客原创文章,博客,未经同意,不得转载。