Интернационализация (локализация) в XCode (iOS/macOS)

Вступление


При поиске в google по запросам Core Data или Autolayout, первый результат поиска содержит обновляемую информацию в «новом формате документации». Потратив определенный объем времени, читая и следуя инструкциям, достаточно не сложно ознакомиться с основными инструментами предоставляемыми той или иной технологией(особенно если чтение на английским не является для вас серьезным препятствием).

Аналогичную информацию можно найти и по локализации продукта. Но в отличии от примеров указанных выше она:

  • Имеет старый менее удобный, как по мне, формат для чтения.
  • Она даже не будет первой в выдаче по запросу «site:developer.apple.com internationalization»
  • И что самое, интересное имеет в заголовке очень любопытное замечание: «Important: This document is no longer being updated. For the latest information about Apple SDKs, visit the documentation website.»

A первой в выдаче мы видим страницу, которая в большей мере выглядит, как инструмент продажи локализации разработчикам и менеджерам, когда основной интерес представляют разрозненные ссылки в разделах WWDC Session Videos и Documentation and Sample Code.

В данной статье, я попытаюсь коротко упомянуть все инструменты, которые помогут вам в этом, как выяснилось для меня, нелегком деле. Каждый пункт я попытаюсь снабдить несколькими ссылками, пройдя по которым вы сможете получить более исчерпывающую информацию. Хватит вступительных слов, к делу.

shut up and show me the code

Используйте форматеры данных


их список

Начиная разрабатывать, я не знал о их существовании, но относительно недавно я услышал о них в лекции на WWDC.

При желании самим создавать строковые представления данных стоит зайти в.
System Preferences/Language & Region/Advanced и посмотреть все предоставляемые настройки.





Просмотр данных настроек, напрочь у меня отбил желание писать велосипеды, теперь я всегда в первую очередь ищу подходящий форматер, и за последний год не было случая, чтобы я не смог найти нужный мне.

Как по мне, список стилей в NumberFormatter, очень ярко показывает, широкий список возможностей, которые будут работать «из коробки», без дополнительных затрат со стороны разработчика приложения.

Пример:

let dateComponents = DateComponents(hour: 3, minute: 2, second: 10)

let dateComponentsForatter = DateComponentsFormatter()

dateComponentsForatter.unitsStyle = .short
Swift.print("\(dateComponentsForatter.string(from: dateComponents)!)")

dateComponentsForatter.unitsStyle = .full
Swift.print("\(dateComponentsForatter.string(from: dateComponents)!)")

dateComponentsForatter.unitsStyle = .spellOut
Swift.print("\(dateComponentsForatter.string(from: dateComponents)!)")
/*
 Output for English:
 3 hrs, 2 min, 10 secs
 3 hours, 2 minutes, 10 seconds
 three hours, two minutes, ten seconds
 
 Output for Russian:
 3 ч, 2 мин, 10 с
 3 часа, 2 минуты, 10 секунд
 три часа, два минуты, десять секунд
 */

У форматтеров есть большое разнообразие настроек и стилей, рассматривать их тут все, как по мне будет излишним, но я уверен, что пройдясь по данным в этой части ссылкам вы явно найдете для себя что-то стоящее, если ранее их не активно использовали.

Не задавайте явно dateFormat у DateFormatter используйте setLocalizedDateFormatFromTemplate. (WWDC)

Как добавить поддержку нового языка


Я добавлю скрины, и думаю, что слова тут будут излишни. Единственное, что хочется отметить, это то, что язык можно добавлять только в проект, а не в один из таргетов.



В появившемся модальном окне оставьте галочки только рядом с теми файлами, которые планируете локализировать.



Импорт/Экспорт


В реальном приложении у вас будет много файлов локализации. «xib», «strings», «stringsdict», «plist»(кол-во которых ограничивает лишь ваша фантазия.)… собирать их все в один архив, после отправлять переводчикам, а после перевода, распихивать по проекту это то еще удовольствие.

Осознавая это Apple предоставила нам весьма удобный инструмент для работы с локализацией строковых ресурсов: Импорт/Экспорт xliff файлов (рассказ на WWDC).

Для использования этой функциональности, выберите ваш проект как показано на скрине



Далее зайдите в пункт меню Editor, Export For Localization, выберите список языков на локализацию, сохраните в желаемое место на диске.

Вуаля! вы только что вынесли все строковые ресурсы вашего проекта в одну папочку, которую можно смело отправлять на перевод.

После модификации данных файлом переводчиком вы сможете экспортировать их в пару кликов используя пункт меню Editor/Import Localizations.

Вот пример работы с xliff от хабражителя. Не совсем согласен с минусами указанными им, да и много чего поменялось с выходом XCode 10, но это весьма подробный пример, следуя которому можно достаточно быстро понять всю технику работы с xliff.

Как локализировать файл


Для этого выберете файл в дереве проекта, после чего в панели утилит выберете первую вкладку.



Нажмите на кнопку «Localize...».



В появившемся окне, выберите требуемый язык, нажмите на «Localize».

В дереве проекта появились файлы локализации.



А в правой панели, расставляя галочки, вы можете удалять/добавлять файлы локализации.



Как быстро переключить язык при тестировании в XCode


Нажимаем на таргет правее от кнопок «Build and Run» & «Stop».



Выбираем «Edit Scheme». В появившемся окне выбираем «Run» в левом вертикальном списке и «Options» в шапке правой части окна.



В появившемся окне выбираем желаемый язык. Закрываем окно «Edit Scheme»
Следующий запуск программы уже будет с выбранным вами языком.

Следует отдельно обратить внимание на то, что внизу выпадающего списка, на последнем скриншоте, есть 2 пункта (псевдоязыка):

  • Right to Left Pseudolanguage
  • Double Length Pseudolanguage

Они могут помочь вам в тестировании.

Autolayout


Что тут сказать. Если вы по каким-то причинам все еще используете явное задание координат, вам придется тяжко. Все что я могу сказать: «Постарайтесь свести явное задание координат к минимуму. Autolayout ваш лучший помощник в этом вопросе».

На чем бы я сакцентировал внимание:

  • Избегайте выставления явных размеров элементам с меняющимся контентом(кнопки, надписи, прочие...)
  • Проверьте при случае лишний раз "content hugging and content compression resistance priorities"
  • Если у вас есть пробелы в понимании основных принципов расстановки NSLayoutConstraint, всем будет лучше, если вы закроете их, а после приступите к локализации.
  • Сразу определитесь, какие элементы в вашем приложении могут быть многострочными, а какие нет.
  • Выставьте всем надписям NSLineBreakMode
  • Не рассчитывайте на то, что текст будет меньше определенной величины, это не верно в общем случае. И на практике вы с этим столкнетесь. Удостоверьтесь в том, что элемент ограничен со всех сторон. И даже маленькая кнопка в левой части экрана имеет ограничение справа (это не даст ей вылезти за пределы экрана и налезть на другой элемент справа). Интересно, что при работе в storyboard на маке отсутствие такого ограничения приводит к отображению варнинга, а в xib нет.
  • Будьте осторожны с «Content Compression Resistance» > 500 на мак. Такие элементы могут расширять NSWindow, на которых лежат. При необходимости такого расширения ставьте элементу ограничения на размер в виде неравенства. Это тот редкий случай, когда значение придется подбирать интуитивно.
  • Большинство языков относятся к left-to-right languages (пишем слева направо), но часть часть языков, таких как арабский и иврит относятся к right-to-left languages. Для поддержки данной вариативности выбирайте leading вместо left и trailing вместо right. Но, как всегда, к этому сдедует подходить с умом и не менять местами части света на компасе, например. (WWDC about right-to-left languages)
  • В некоторых случаях вам придется менять установленные ограничения (NSLayoutConstraint) в зависимости от текущего языка.

Порядок слов может меняться при переводе


При формировании сроки используя скажем -[NSString stringWithFormat:] мы можем в спецификаторе задавать номер аргумента, который следует вставить.

Swift.print("\(String(format: "%@ %@",     "s1", "s2"))")
Swift.print("\(String(format: "%1$@ %2$@", "s1", "s2"))")
Swift.print("\(String(format: "%2$@ %1$@", "s1", "s2"))")
Swift.print("\(String(format: "%1$@ %1$@", "s1", "s2"))")
Swift.print("\(String(format: "%1$@ %2$@ %3$@", "s1", "s2", "s3"))")
Swift.print("\(String(format: "%1$@ %3$@", "s1", "s2", "s3"))")//(**)

/*
 Output:
 s1 s2
 s1 s2
 s2 s1
 s1 s1
 s1 s2 s3
 s1 s2
 */

Следует помнить, что пропуск одного из аргументов может привести к непредвиденному поведению.
When numbered argument specifications are used, specifying the Nth argument requires that all the leading arguments, from the first to the (N-1)th, are specified in the format string.
источник

WWDC

Локализация NSAttributedString


Иногда приходится локализировать не только обычные строки, но атрибутные. Для этой цели можно использовать Atributika.

let b = Style("b").font(.boldSystemFont(ofSize: 20)).foregroundColor(.red)
label.attributedText = "Hello <b>World</b>!!!".style(tags: b).attributedString

В Objective-C Analyzer позволяет найти часть не локализированного контента.


Заходим в настройки проекта, выставляем флаг «Missing Localizability» в YES.



В меню XCode выбираем «Product/Analyze». Видим, что появился warning аналайзера.



Нажав на него видим причину.



Подправив код

    self.label.stringValue = NSLocalizedString(@"Test Label", @"");

Перезапускаем аналайзер и варнинг пропал.

Что еще стоит глянуть, но у меня не хватило времени чтобы описать в этой части


  • В Xcode 10 при импорте/экспорте вы работаете не просто с xliff файлом, а с пакетом содержащим все локализированные ресурсы, в который так-же входит и описанный в моей статье xliff + картинки + ксибки +…
  • Можно локализировать ксибку, не используя никакой код.
  • Можно прописав в коде NSLocalizedString простым вызовом скрипта вытянуть все тексты/комментарии в strings файлы с соответствующим названием.
  • Используя Stringsdict можно описать все формы множественных чисел.
  • При ограниченном бюджете, или вопросов о переводе, можно легко подглядеть в переводы программ Apple
  • Локализировать стоит не только тексты, одна картинна может быть привычной для вас, но совершенно не ясна людям по ту сторону земного шара.
  • Один и тот-же текст в английском, может иметь разные переводы в зависимости от контекста, из чего следует 2 вещи. Комментарии крайне важны и не стоит сокращать кол-во переводов объединяя по ключам(Как перевести «Book» без контекста? это «книга» или действие «бронировать»?)

Пы. Сы. (страх и раскаяние автора)


В первую очередь хочу сказать, что остается еще много чего о чем стоит рассказать(как минимум заметки из предыдущего раздела). Так что если статья зайдет, то я постараюсь выделить время и написать продолжение разобрав некоторые части более подробно, и описать фичи вовсе не затронутые мной.(я долго пытался написать исчерпывающую статью, но понял, что это потребует приблизительно вечность.)

Отдельно прошу прощение за упущенные знаки препинания и прочие недочеты…

Буду рад любым вашим отзывам :)
Теги:
ios, macos, xcode, localization

Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.