Удобная локализация iOS приложений в Interface Builder

в 11:17, , рубрики: interface builder, iOS, ios development, ios programming, localization, localize, storyboard, swift, xib, разработка мобильных приложений, разработка под iOS

Удобная локализация iOS приложений в Interface Builder - 1

Не так давно я опубликовал подробную инструкцию по использованию LocoLaser — утилиты для локализации Android и iOS приложений в Google Sheets. Мне бы хотелось продолжить тему локализации и обратить больше внимания на iOS приложения. В отличии от Android, в iOS разработке есть ряд мелких но неприятных моментов, которые, в сумме, могут привести к совсем не мелким проблемам.

Сегодня я хочу уделить особое внимание Interface Builder-у. Все мы знаем, он не идеален. Но это единственное, что у нас есть и с этим приходится мириться. В этой статье я расскажу о главной проблеме, с которой вы можете столкнуться при локализации приложений в Interface Builder, а также расскажу как с ней можно справиться.

Суть проблемы

Когда вы переводите Storyboard или XIB файл, помимо основного файла с разметкой, создаются дополнительные файлы с ресурсными строками. Эти ресурсные файлы принято выгружать в специальные таблицы и отдавать переводчикам. Беда заключается в том, что ключи для строк, в этом файле, строятся на основе Object ID, которые генерируются автоматически и нет ни какой возможности на них повлиять. Если вас угораздило скопировать или вырезать а затем вставить какой либо View, Interface Builder сгенерирует новые идентификаторы и перевод будет потерян.

Каждый решает эту проблему как может. Помню, 2 года назад, на мобильной конференции я спрашивал одного ведущего iOS разработчика одной из ведущих фирм о том как они решают проблему локализации интерфейса. В то время я только начинал изучать iOS, но у меня уже был достаточно богатый опыт в области Android и мне было с чем сравнивать. Честно говоря, я был ошарашен ответом. Во ViewController через IBOutlet они получали ссылки на Label и прочие View и переводили их программно. В коде это выглядит приблизительно так:

class MainViewController: UIViewController {

    @IBOutlet var labelToTranslate: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.labelToTranslate.text = NSLocalizedString("scr_main_txt_example", comment: "Some Example text")
        ...
    }
    ...
}

В этом случае все строки находятся в одном файле Localizable.strings. Подобный метод в настоящий момент является наиболее распространенным и используется почти повсеместно. Признайтесь, получается не слишком элегантно. Мало того что вы засоряете ViewController лишним кодом, которому тут не место, так еще и не решаете проблему с копированием или перемещением View. Пора бы найти что-то получше.

Решение

И тут у меня есть кое-что, что я могу вам предложить. Дело в том, что в Interface Builder, в свойствах View, можно прописать так называемые «User Defined Runtime Attributes». Их то мы и будем использовать. Но для начала необходимо создать Extension для UILabel.

extension UILabel {
    
    public var lzText : String? {
        set {
            if newValue != nil {
                self.text = NSLocalizedString(newValue, comment: “”)
            }
            else {
                self.text = nil
            }
        }
        
        get {
            return self.text
        }
    }
}

Теперь у всех UILabel появилось свойство lzText при изменении которого в свойство text записывается локализованная строка. Используем это свойство в Interface Builder.

Удобная локализация iOS приложений в Interface Builder - 2

  1. Выбираем UILabel и переходим на вкладку «Identity Inspector»;
  2. Жмем кнопку добавления атрибута в «User Defined Runtime Attributes»;
  3. Указываем ключ атрибута «lzText», тип: «String», значение: «scr_main_txt_example»

И это все. Больше не нужно засорять код ничем лишним. Вы можете не бояться, что перевод или ссылка на View потеряется при копировании или перемещении в другой контейнер. Атрибуты копируются вместе с View. Единственное, что останется неизмеренным — это хранение всех строк в одном файле Localized.strings.

UPD:
Но это не все. DjPhoeniX предложил сделать еще лучше. И нам не потребуется почти ничего менять. Нужно лишь добавить @IBInspectable перед объявлением свойства.

Измененный Extensions.swift

extension UILabel {
    
    @IBInspectable public var lzText : String? {
        set {
            if newValue != nil {
                self.text = NSLocalizedString(newValue, comment: “”)
            }
            else {
                self.text = nil
            }
        }
        
        get {
            return self.text
        }
    }
}

Теперь это свойство также доступно на вкладке «Attributes Inspector».

Удобная локализация iOS приложений в Interface Builder - 3

Для еще большего удобства я подготовил файл, в котором собрано достаточно большое количество расширений для часто используемых View, где у каждого текстового свойства есть двойник с приставкой «lz»(сокращение слова «localized»). Вы можете найти этот файл в примере по использованию LocoLaser: LocalizationExtensions.swift. Весь проект опубликован под лицензией Apache 2.0, можете смело копировать этот файл к себе и начинать использовать.

Помимо расширений для View, в LocalizationExtensions.swift добавлено расширение к классу String. Оно добавляет вычисляемое свойство localized, которое возвращает локализованную строку. Если перевод найти не получается, отправляется оповещение через NotificationCenter. Вы можете подписаться на эти оповещения и обрабатывать их как вам заблагорассудится. В Debug билде можно писать в лог или показывать уведомления, в Release билде отправлять репорт в систему аналитики.

В итоге, после применения вышеописанного метода, вся работа со строками остается в Interface Builder. Плюс ко всему вы получите дополнительный механизм по отлову «битых» строк.

На этом закончу. Спасибо за внимание. Пользуйтесь на здоровье!

Автор: Денис

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js