Раскадровка / XIB и лучшие практики локализации - PullRequest
19 голосов
/ 13 января 2012

Официально рекомендованный метод для локализации XIB / Storyboard заключается в создании файлов .xib и .storyboard внутри xx.lproj (где xx - двухбуквенный идентификатор языка) для каждой локализации, которую вы хотите поддерживать.

Это создает проблему, потому что у вас есть несколько файлов, которые во многих случаях имеют один и тот же пользовательский интерфейс, которые подвержены изменениям. Если вы хотите изменить дизайн пользовательского интерфейса для одного представления, вам придется делать это несколько раз (хуже, если вы вводите локализуемые строковые значения в самой XIB). Это идет вразрез с принципом СУХОЙ.

Кажется, гораздо эффективнее звонить NSLocalizedString() там, где вам это нужно, и просто использовать один XIB или раскадровку для одной базовой локализации.

Итак, почему (не) я должен создавать локализованные файлы XIB / Storyboard?

Ответы [ 11 ]

35 голосов
/ 30 января 2014

Вы можете создать категорию для 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 для каждого языка.

27 голосов
/ 01 марта 2012

Для простой смены текстовых меток я сделал что-то вроде этого

+(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, когда все, что вам нужно - это локализованные метки.

4 голосов
/ 08 августа 2013

Решение Flax работает хорошо, следует отметить, что если у вас есть UILabels или UIButtons, которые содержатся в 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

Добавил бы это в качестве комментария к вышеупомянутому решению, но у меня нет 'rep!

2 голосов
/ 12 июля 2017

Как пояснил 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. Теперь перейдитев вашей раскадровке и для вашей кнопки и / или ярлыка, который вы хотите локализовать, просто добавьте в инспектор идентичности вашего объекта:

enter image description here

К вашему сведению: здесь Key Path - это имя функции, которую вы добавили в свои расширения (UIlabel и UIButton), а Value - это имя ключа, который вы хотитепереводить автоматически, который находится в вашем Localizable.strings файле.Например, в ваших Localizable.strings (французский) у вас есть ключ / значение "ourOffers" = "NOS OFFRES";

Теперь соберите и запустите.Ваш объект будет переведен на язык вашего устройства, если у вас есть ключ / значение в вашей Localizable.string.Наслаждайтесь:)

2 голосов
/ 13 января 2012

Вы можете автоматизировать многие из них с помощью ibtool. это достойное введение: http://www.bdunagan.com/2009/03/15/ibtool-localization-made-easy/

1 голос
/ 23 января 2016

Я искал именно тот ответ, который дал Лен, помеченный как правильный, но он мне понадобился в Свифте. Так что я перевел на это. Спасибо Лен.

    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

1 голос
/ 18 сентября 2013

Полезный пост, намного проще, чем несколько 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];
    }
}
0 голосов
/ 18 октября 2018

IMHO Xcode имеет одну из худших функций локализации, доступных ...

Мне действительно не нравится разрабатывать для Android, но я должен признать, что Android Studio имеет лучшую систему локализации.

Тем не менее, потому что я действительно не могу больше воссоздавать Storyboard.strings после каждого мода (вы знаете, Xcode не будет обновлять их для вас ...), вот как я делаю:

У меня есть парарасширений для циклов подпредставлений (и подпредставлений подпредставлений), и я имею дело с каждым из основных объектов (надписи, текстовое поле, кнопки ...), локализуя их основные свойства (текст, заполнить ...) с помощью простого помощника (AltoUtil.ls), которая является «короткой» версией для NSLocalizedString.

Затем я вставляю тексты и заполнители с подчеркиванием (например, «_first_name», «_email_address») в свою раскадровку / xibs и добавляю эти строки в каждый Localizable.strings file.

Теперь мне просто нужно вызвать функцию localize () в viewDidLoad (или где мне это нужно), такчто я могу иметь весь контроллер представления локализованным.Для ячеек я просто вызываю localize () внутри метода awakeFromNib (), например.

Я уверен, что это не самый быстрый метод (из-за цикла 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()        
    }
}
0 голосов
/ 23 февраля 2018

Я использовал тот же подход, который описал Лешек Сзари для моих взглядов в Swift.

Используя логическое значение в отличие от ключей локализации, я добавил выпадающее меню «Вкл / Выкл», которое определяет, должны ли исходные текстовые значения быть локализованы или нет. Это позволяет поддерживать раскадровку в чистоте без дополнительного ухода.

Image1

Когда выбрано значение, отдельный атрибут Runtime добавляется в представление и используется как условие из его установщика.

Image2

Image3

Вот код из моего файла .swift, который расширяет UIButton, UILabel, UITabBarItem и UITextField, включая заполнитель текстового поля и состояния элементов управления кнопки:

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
0 голосов
/ 18 марта 2013

Я наткнулся на этот пост и несколько других, пытаясь упростить локализацию xib для себя. Я опубликовал свой метод включения IBOutles для ярлыков / кнопок по этому вопросу, отлично сработал для меня, ограничивает все изменения файлами Localization.strings.

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

...