故事板/ XIB和本地化最佳实践

时间:2012-01-13 05:35:55

标签: xcode localization xib

XIB / Storyboard本地化的officially recommended method用于在xx.lproj中创建.xib和.storyboard文件(其中xx是两个字母的语言ID),用于您要支持的每个本地化。

这会产生问题,因为您有多个文件在很多情况下共享相同的UI,这些文件很容易发生变化。如果您想为一个视图重新设计UI,则必须多次执行(更糟糕的是,如果您在xib中输入了可本地化的字符串值)。这与DRY原则背道而驰。

在您需要的地方调用NSLocalizedString()似乎更有效率,只需使用一个XIB或Storyboard进行一次基本本地化。

那么,为什么(不)我创建本地化的XIB / Storyboard文件?

11 个答案:

答案 0 :(得分:33)

您可以在UILabel,UIButton等上创建一个类别:

#import "UILabel+Localization.h"

@implementation UILabel (Localization)

- (void)setLocalizeKey:(NSString*)key
{
    self.text = NSLocalizedString(key, nil);
}

@end

然后在xib文件上使用用户定义的运行时属性将UILabel(或UIButton等)链接到Localizable.strings文件中保存的密钥

user defined runtime attributes

通过这种方式,您可以将所有字符串放在一个文件中,而不必为每种语言创建单独的xib。

答案 1 :(得分:27)

只是更改文字标签我做了类似的事情

+(void) replaceTextWithLocalizedTextInSubviewsForView:(UIView*)view
{
    for (UIView* v in view.subviews)
    {
        if (v.subviews.count > 0)
        {
            [self replaceTextWithLocalizedTextInSubviewsForView:v];
        }

        if ([v isKindOfClass:[UILabel class]])
        {
            UILabel* l = (UILabel*)v;
            l.text = NSLocalizedString(l.text, nil);
            [l sizeToFit];
        }        

        if ([v isKindOfClass:[UIButton class]])
        {
            UIButton* b = (UIButton*)v;
            [b setTitle:NSLocalizedString(b.titleLabel.text, nil) forState:UIControlStateNormal];
        }        
    }    
}

viewDidLoad:中调用此函数,如下所示:

[[self class] replaceTextWithLocalizedTextInSubviewsForView:self.view];

当你想要的只是本地化标签时,它为我节省了很多声明和连接IBOutlets的工作。

答案 2 :(得分:4)

Flax的解决方案效果很好,有一点需要注意的是,如果UILabelsUIButtons包含UICollectionViewCells UICollectionViews中的viewDidLayoutSubviews(或类似)集合在当前视图中经常更改,例如由于用户操作或由异步请求填充,然后使用正确的本地化字符串更新标签,您可以在viewDidLoad而不是- (void)viewDidLayoutSubviews { [LocalizationHelper replaceTextWithLocalizedTextInSubviewsForView:self.view]; } 中调用本地化函数}(只调用一次):

@implementation LocalizationHelper

+(void) replaceTextWithLocalizedTextInSubviewsForView:(UIView*)view
{
    for (UIView* v in view.subviews)
       ... <code from above> ...
}
@end

从这段代码中可以看出,我将本地化方法保存在一个静态助手类中(正如其他章节所建议的那样):

{{1}}

将此作为对上述解决方案的评论添加,但我没有得到'代表!

答案 3 :(得分:2)

您可以使用ibtool自动执行大量操作。这是一个不错的介绍:http://www.bdunagan.com/2009/03/15/ibtool-localization-made-easy/

答案 4 :(得分:2)

正如Leszek S所解释的,您可以创建一个类别。

在这里,我将以swift 3为例,为UILabel和UIButton提供扩展:

  1. 首先创建一个“ StringExtension.swift ”文件
  2. 添加此代码:

    extension String { func localized() -> String { let bundle = Bundle.main return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "") } }

  3. 然后使用您想要的名称创建另一个新文件(例如)“ LocalizableObjectsExtensions.swift

  4. 在其上添加UIButton的扩展名和UILabel的扩展名(当然您可以根据需要创建扩展名,UITextField ...):

    extension UIButton { var localizedText: String { set (key) { setTitle(key.localized(), for: .normal) } get { return titleLabel!.text! } } }

    extension UILabel { var localizedText: String { set (key) { text = key.localized() } get { return text! } } }

  5. 现在进入你的故事板和你的按钮和/或你标记你想要本地化只需添加你的身份检查器对象:

  6. enter image description here

    仅供参考:此处为密钥路径,它是您在扩展程序中添加的功能的名称(UIlabel和UIButton),而值是您希望自动翻译的密钥的名称,位于 Localizable.strings 文件。例如,在您的 Localizable.strings(法语)中,您有键/值“ourOffers”=“NOS OFFRES”;

    现在构建&amp;跑。如果您的Localizable.string中有键/值,则您的对象将以您设备的语言进行翻译。享受:)

答案 5 :(得分:1)

有用的帖子,比多个XIB容易得多。我将代码扩展为处理UISegmentedControl

if ([v isKindOfClass:[UISegmentedControl class]]) {
    UISegmentedControl* s = (UISegmentedControl*)v;
    for (int i = 0; i < s.numberOfSegments; i++) {
        [s setTitle:NSLocalizedString([s titleForSegmentAtIndex:i],nil) forSegmentAtIndex:i];
    }
}

答案 6 :(得分:1)

我正在寻找Flax给出的确切答案,标记为正确,但我在Swift中需要它。所以我翻译成了它。谢谢亚麻。

    func replaceTextWithLocalizedTextInSubviewsForView(view: UIView) {

    for v in view.subviews {

        if v.subviews.count > 0 {
            self.replaceTextWithLocalizedTextInSubviewsForView(v)
        }

        if (v.isKindOfClass(UILabel)) {
            let myLabel: UILabel = v as! UILabel
            myLabel.text = NSLocalizedString(myLabel.text!, comment: "Text to translate.")
            myLabel.sizeToFit()
        }

        if (v.isKindOfClass(UIButton)) {
            let myButton: UIButton = v as! UIButton
            myButton.setTitle(NSLocalizedString((myButton.titleLabel?.text)!, comment: "Text to translate.") as String, forState: .Normal)
            myButton.sizeToFit()
        }
    }
}

适用于Swift 2.1

答案 7 :(得分:0)

我看的每个地方都说你必须为每个本地化实例复制整个xib文件,即使你真的只想翻译文本并用不同的语言为每个本地化实例复制文本。

如果有人知道只复制xib的用户可见文本(使用其他语言)而不复制每种语言的整个xib文件的方法,请告诉我们。

答案 8 :(得分:0)

我在尝试使自己更容易进行xib本地化时遇到了这篇文章和其他几篇文章。我在这个问题上发布了包含IBOutles标签/按钮的方法,对我来说很有用,所有更改都限于Localization.strings文件。

https://stackoverflow.com/a/15485572/1449834

答案 9 :(得分:0)

我使用了与Leszek Szary在Swift中描述的类似方法。

使用布尔值而不是本地化键,我添加了一个&#34;开/关&#34;下拉菜单,确定是否应本地化初始文本值。这样可以使故事板保持清洁,而无需任何额外的维护。

Image1

选择值后,会将单个运行时属性添加到视图中,并将其用作其设置器中的条件。

Image2

Image3

以下是我的.swift文件中的代码,该文件扩展了UIButtonUILabelUITabBarItemUITextField,包括文本字段占位符和按钮控件状态:

import UIKit

extension String {
    public var localize: String {
        return NSLocalizedString(self, comment: "")
    }
}

extension UIButton {
    @IBInspectable public var Localize: Bool {
        get { return false }
        set { if (newValue) {
            setTitle( title(for:.normal)?.localize,      for:.normal)
            setTitle( title(for:.highlighted)?.localize, for:.highlighted)
            setTitle( title(for:.selected)?.localize,    for:.selected)
            setTitle( title(for:.disabled)?.localize,    for:.disabled)
        }}
    }
}

extension UILabel {
    @IBInspectable public var Localize: Bool {
        get { return false }
        set { if (newValue) { text = text?.localize }}
    }
}

extension UITabBarItem {
    @IBInspectable public var Localize: Bool {
        get { return false }
        set { if (newValue) { title = title?.localize }}
    }
}

extension UITextField {
    @IBInspectable public var Localize: Bool {
        get { return false }
        set { if (newValue) {
            placeholder = placeholder?.localize
            text = text?.localize
        }}
    }
}

您还可以使用新属性轻松转换程序运行时设置的值,如下所示:

let button = UIButton()

button.setTitle("Normal Text", for: .normal)
button.setTitle("Selected Text", for: .selected)

button.Localize = true

答案 10 :(得分:0)

IMHO Xcode具有最糟糕的本地化功能之一...

我真的不喜欢为Android开发,但我必须承认Android Studio具有更好的本地化系统。

这就是说,因为我真的再也受不了在每个mod之后重新创建Storyboard.strings(您知道,Xcode不会为您更新它们),所以我就是这样做的:

我有几个扩展来循环子视图(和子视图的子视图),并且通过定位其主要属性(文本,placeholde ...)来处理每个主要对象(标签,文本字段,按钮...)通过一个简单的帮助器(AltoUtil.ls),它是NSLocalizedString的“简短”版本。

然后,在情节提要/ xib中插入带有下划线的文本和占位符(例如“ _first_name”,“ _ email_address”),并将这些字符串添加到每个Localizable.strings文件中。

现在,我只需要在viewDidLoad中调用 localize() 函数(或在我需要的任何地方),以便可以对整个视图控制器进行本地化。例如,对于单元格,我只需在awakeFromNib()方法内调用localize()。

我确信这不是最快的方法(由于subviews循环),但是与我以前使用的其他方法相比,我的速度不会降低,而且效率很高。

import UIKit

extension UIView {

    func localize()
    {
        for view in self.allSubviews()
        {
            if let label = view as? UILabel
            {
                label.text = AltoUtil.ls(label.text)
            }
            else if let textField = view as? UITextField
            {
                textField.text = AltoUtil.ls(textField.text)
                textField.placeholder = AltoUtil.ls(textField.placeholder)
            }
            else if let button = view as? UIButton
            {
                button.setTitle(AltoUtil.ls(button.title(for: UIControl.State.normal)), for: UIControl.State.normal)
            }
            else if let searchBar = view as? UISearchBar
            {
                searchBar.placeholder = AltoUtil.ls(searchBar.placeholder)
            }
        }
    }

    func allSubviews() -> [UIView]
    {
        return subviews + subviews.flatMap { $0.allSubviews() }
    }
}

第二个扩展名是本地化视图控制器中的视图控制器标题和选项卡栏项。您可以添加任何需要本地化的项目。

import UIKit

extension UIViewController {

    func localize()
    {
        self.title = AltoUtil.ls(self.navigationItem.title)
        self.tabBarItem?.title = AltoUtil.ls(self.tabBarItem?.title)

        self.view.localize()        
    }
}