【问题标题】:Storyboard/XIB and localization best practiceStoryboard/XIB 和本地化最佳实践
【发布时间】:2012-02-09 09:50:58
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: xcode localization xib


    【解决方案1】:

    恕我直言,Xcode 拥有最糟糕的本地化功能之一......

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

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

    我对循环子视图(和子视图的子视图)有几个扩展,我通过本地化它们的主要属性(文本、占位符...)来处理每个主要对象(标签、文本字段、按钮...)通过一个简单的助手(AltoUtil.ls),它是 NSLocalizedString 的“短”版本。

    然后我在我的故事板/xibs 中插入带有下划线的文本和占位符(例如“_first_name”、“_email_address”),并将这些字符串添加到每个 Localizable.strings 文件中。

    现在我只需要在 viewDidLoad(或者我需要它的地方)调用 localize() 函数,这样我就可以将整个视图控制器本地化。例如,对于单元格,我只需在 awakeFromNib() 方法中调用 localize()。

    我确信这不是最快的方法(由于子视图循环),但与我以前使用的其他方法相比,我没有任何减速,而且非常高效。

    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()        
        }
    }
    

    【讨论】:

      【解决方案2】:

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

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

      当一个值被选中时,一个单一的运行时属性被添加到视图中,并在它的设置器中用作条件。

      这是我的 .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
      

      【讨论】:

        【解决方案3】:

        正如 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. 现在进入你的 Storyboard 并为你的按钮和/或你想要本地化的标签添加你对象的身份检查器:

        仅供参考:此处的 Key Path 是您在扩展中添加的函数的名称(UIlabel 和 UIButton),Value 是您希望自动翻译的键的名称,位于 Localizable.strings 文件。例如,在您的 Localizable.strings (French) 中,您有键/值 "ourOffers" = "NOS OFFRES";

        现在构建并运行。如果您的 Localizable.string 中有键/值,您的对象将被翻译成您设备的语言。享受:)

        【讨论】:

          【解决方案4】:

          我一直在寻找 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

          【讨论】:

            【解决方案5】:

            您可以像这样在 UILabel、UIButton 等上创建一个类别:

            #import "UILabel+Localization.h"
            
            @implementation UILabel (Localization)
            
            - (void)setLocalizeKey:(NSString*)key
            {
                self.text = NSLocalizedString(key, nil);
            }
            
            @end
            

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

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

            【讨论】:

            【解决方案6】:

            有用的帖子,比多个 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];
                }
            }
            

            【讨论】:

              【解决方案7】:

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

              - (void)viewDidLayoutSubviews
              {
                  [LocalizationHelper replaceTextWithLocalizedTextInSubviewsForView:self.view];
              }
              

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

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

              会将此作为评论添加到上述解决方案中,但我没有'rep!

              【讨论】:

                【解决方案8】:

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

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

                【讨论】:

                • 对我来说似乎更好,因为我喜欢去破解代码,然后再回来翻译。这样,您可以不时拉出所有未翻译的字符串并赶上。接受的答案将要求您继续停止更新字符串或稍后搜索尚未添加到 plist 中的字符串。
                【解决方案9】:

                为了改变文本标签,我做了这样的事情

                +(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 的工作。

                【讨论】:

                • 我找到的最好的方法!只是我为 UILabel 案例添加了 [l sizeToFit]; 并将 b.titleLabel.text = NSLocalizedString(b.titleLabel.text, nil); 替换为 [b setTitle:NSLocalizedString(b.titleLabel.text, nil) forState:UIControlStateNormal]; 用于 UIButton 案例 - 以避免大小问题。将此按钮放在一些全局 Utils 类中也很有意义,而不是弄脏每个视图控制器
                • 不错的快捷方式,但现在必须在标签和按钮中添加/编辑字符串时跟踪它们,确保已将它们包含在字符串文件中。事后您将无法提取所有未翻译的字符串进行翻译。
                【解决方案10】:

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

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

                【讨论】:

                  【解决方案11】:

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

                  【讨论】:

                    猜你喜欢
                    • 2010-10-11
                    • 2015-12-10
                    • 2011-04-07
                    • 1970-01-01
                    • 2010-12-17
                    • 1970-01-01
                    • 2013-11-28
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多