вырезаем из 17го спринта описание локализации на примере emoji mixer
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
Прежде чем переводить приложение на новые языки, его нужно подготовить в коде. Откройте проект `EmojiMixer` — в этом уроке он понадобится вам для практики!
|
||||
|
||||
# Интернационализация
|
||||
# Интернационализация
|
||||
|
||||
Текст, который вы используете для заголовков экранов, лейблов, кнопок и алертов – это *статические строки*.
|
||||
|
||||
Статические строки — фиксированный текст, который не меняется в процессе работы приложения. Чтобы пользователь смог прочитать текст на понятном для себя языке, нам надо найти эти строки и разместить их в специальных файлах локализации.
|
||||
Статические строки — фиксированный текст, который не меняется в процессе работы приложения. Чтобы пользователь смог прочитать текст на понятном для себя языке, нам надо найти эти строки и разместить их в специальных файлах локализации.
|
||||
|
||||
> Относитесь к тексту, который видит пользователь, как к ресурсу — такому же, как картинки, цвета или шрифты. Если для них есть специальные папки и каталоги, то и для текста должны существовать такие хранилища. Так вы сможете подключить любой новый язык с наименьшими трудозатратами.
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
Их несколько:
|
||||
|
||||
- внутри кода — статические строки, конкатенация (сложение) строк и интерполированные
|
||||
- Info.plist — настройки приложения
|
||||
- в файлах Interface Builder (.xib, .storybord)
|
||||
- Info.plist — настройки приложения
|
||||
- в файлах Interface Builder (.xib, .storyboard)
|
||||
|
||||
А ещё есть текст, написанный на картинках, но про его локализацию поговорим отдельно.
|
||||
|
||||
Кнопка
|
||||
Понятно!
|
||||
|
||||
Прежде чем приступать к интернационализации приложения, надо отделить все статические строки от вёрстки и разместить их в специальном файле для локализации.
|
||||
Внимательно просмотрите на [макеты `EmojiMixer`](https://www.figma.com/design/tfuJs9k2StXOzn1pf5BY8c/Emoji-Mixer?node-id=0-1&p=f&t=5G18lvTAXczS90D5-0), экраны и вью, найдите использованные строки.
|
||||
Прежде чем приступать к интернационализации приложения, надо отделить все статические строки от вёрстки и разместить их в специальном файле для локализации.
|
||||
Внимательно просмотрите на `EmojiMixer`, экраны и вью, найдите использованные строки (у нас всего одна строка "Delete All" для кнопки в NavigationBar).
|
||||
|
||||
Кнопка
|
||||
Далее
|
||||
@@ -44,21 +44,18 @@
|
||||
|
||||
`"ключ" = "локализованное значение";`
|
||||
|
||||
В качестве ключа обычно используют английское словосочетание, описывающее назначение строки. Давайте составим такой файл для проекта `EmojiMixer`:
|
||||
В качестве ключа обычно используют английское словосочетание, описывающее назначение строки. Давайте составим такой файл для проекта `EmojiMixer`:
|
||||
|
||||
```
|
||||
"emptyState.title" = "Что будем отслеживать?";
|
||||
"delete" = "Удалить";
|
||||
"delete.confirmation" = "Уверены что хотите удалить трекер?";
|
||||
"cancel" = "Отменить";
|
||||
```swift
|
||||
"delete_all" = "Удалить всё";
|
||||
```
|
||||
|
||||
> Обратите внимание: кавычки нужны, только если вы используете символ пробела в ключе. В остальных случаях их можно опускать.
|
||||
> Обратите внимание: кавычки нужны, только если вы используете символ пробела в ключе. В остальных случаях их можно опускать.
|
||||
|
||||
Итак, мы подготовили базовый файл: строки, полученные из него, будут видны всем пользователям — при условии, что для этих строк не добавлен соответствующий язык. Поэтому очень важно, чтобы в строках оказались фразы на самых популярных языках (например, на английском и на французском).
|
||||
|
||||
**Кнопка**
|
||||
А что произойдёт, если пользователю нужно выбрать язык, который не поддерживается приложением?
|
||||
А что произойдёт, если пользователю нужно выбрать язык, который не поддерживается приложением?
|
||||
|
||||
В таком случае iOS по умолчанию использует базовый язык. Но даже если ваше приложение поддерживает язык, для которого локализована не каждая строка, у Apple будет решение!
|
||||
|
||||
@@ -109,7 +106,7 @@
|
||||
- `key`, ключ перевода `Localizable.strings` — используется для поиска строки в файле;
|
||||
|
||||
- `comment`, комментарий — используется, чтобы предоставлять дополнительную информацию переводчикам – например, местоположение строки в элементе, контекст строки в приложении и так далее;
|
||||
|
||||
|
||||
- `tableName` — нужен для указания файла с локализованными данными, если тот отличается от файла `Localizable.strings`;
|
||||
|
||||
- `bundle` — позволяет выбирать пакеты ресурсов, из которых надо извлечь строку; если этот параметр не указан, по умолчанию используется главный пакет приложения;
|
||||
@@ -123,27 +120,27 @@
|
||||
Кнопка
|
||||
Понятно!
|
||||
|
||||
Давайте теперь попробуем получить такую функцию для ключа "emptyState.title":
|
||||
Давайте теперь попробуем получить такую функцию для ключа "delete_all":
|
||||
|
||||
```swift
|
||||
let emptyStateText = NSLocalizedString("emptyState.title", comment: "Text displayed on empty state")
|
||||
```swift
|
||||
let deleteAllText = NSLocalizedString("delete_all", comment: "Text displayed on delete all button")
|
||||
```
|
||||
|
||||
Полученную строку можно установить в свойство `text` у UILabel, например:
|
||||
|
||||
```swift
|
||||
let emptyStateText = NSLocalizedString("emptyState.title", comment: "Text displayed on empty state")
|
||||
emptyStateLabel.text = emptyStateText
|
||||
```swift
|
||||
let deleteAllText = NSLocalizedString("delete_all", comment: "Text displayed on delete all button")
|
||||
deleteAllLabel.text = deleteAllText
|
||||
```
|
||||
|
||||
> Обновите текстовые элементы в приложении `EmojiMixer` самостоятельно.
|
||||
> Обновите текстовые элементы в приложении `EmojiMixer` самостоятельно.
|
||||
|
||||
Отлично! А теперь давайте научимся запускать приложение для разных локалей, чтобы проверить работу локализации!
|
||||
Отлично! А теперь давайте научимся запускать приложение для разных локалей, чтобы проверить работу локализации!
|
||||
|
||||
Кнопка
|
||||
Давайте!
|
||||
|
||||

|
||||

|
||||
|
||||
1. В верхней панели управления проектом нажмите на имя таргета вашего приложения (`EmojiMixer`).
|
||||
|
||||
@@ -153,27 +150,25 @@ emptyStateLabel.text = emptyStateText
|
||||
|
||||
4. В верхней части окна перейдите на вкладку "Options".
|
||||
|
||||
5. В пункте "App Language" выберите язык, на котором хотите тестировать локализацию приложения из списка.
|
||||
5. В пункте "App Language" выберите язык, на котором хотите тестировать локализацию приложения из списка.
|
||||
|
||||
Заметим, что выбранная настройка изменит язык *только* для приложения и не повлияет на язык операционной системы симулятора. Закройте окно редактирования схемы и запустите проект на симуляторе, используя `Cmd + R`.
|
||||
|
||||
Вот так можно поменять настройки локали устройства, чтобы переключаться не только между языками, но и регионами (для этого нужно изменить соседнее поле `Region`).
|
||||
Вот так можно поменять настройки локали устройства, чтобы переключаться не только между языками, но и регионами (для этого нужно изменить соседнее поле `Region`).
|
||||
|
||||
> Обратите внимание: изменения в этом файле меняют файл проекта `<AppName>.xcscheme`. Но любые изменения для тестирования НЕ нужно добавлять в git-репозиторий!
|
||||
|
||||

|
||||
|
||||
# Процесс локализации
|
||||
# Процесс локализации
|
||||
|
||||
Мы рассмотрели, как локализуются строки на небольшом приложении, где текстовых ресурсов немного. Но в международных приложениях текстовых элементов и их состояний могут быть тысячи, а языковых локалей — десятки! Сейчас мы вынесли строки для локализации, перевели их в онлайн-переводчике и руками добавили новые языки в приложение. В больших продуктах разработчики сами не переводят строки и ничего не добавляют руками — для этого в каждой команде выделен отдельный человек, или даже целая команда локализации, которая управляет этим процессом.
|
||||
|
||||
Xcode позволяет разделить разработку и перевод приложения, чтобы эти процессы могли развиваться параллельно.
|
||||
|
||||
Сначала идёт интернационализация, то есть вы пишите код так, чтобы можно было делегировать работу над переводами другой команде. На этом шаге мы определяем текстовые элементы для локализации и верстаем экраны по определённым правилам.
|
||||
Сначала идёт интернационализация, то есть вы пишите код так, чтобы можно было делегировать работу над переводами другой команде. На этом шаге мы определяем текстовые элементы для локализации и верстаем экраны по определённым правилам.
|
||||
|
||||
> С этого момента вы можете заниматься тестированием, даже если переводов ещё нет. Для тестирования в Xcode существуют *псевдолокализации*, которые помогают тестировать приложения в различных локале-зависимых условиях. Когда будете готовы локализовать приложение, попробуйте экспортировать локализируемый текст с использованием стандартных форматов файлов и отправить его команде локализации для перевода на несколько языков. Этот процесс мы рассмотрим в следующих уроках.
|
||||
|
||||
Пока вы ждёте переводов текста, можно продолжить разработку приложения и дополнительно выполнить самостоятельные шаги по локализации — например, добавить аудио- и графические файлы в свой проект. Когда переводы будут готовы, импортируйте локализацию в проект и тщательно протестируйте своё приложение, на *каждом* поддерживаемом языке и регионе.
|
||||
Пока вы ждёте переводов текста, можно продолжить разработку приложения и дополнительно выполнить самостоятельные шаги по локализации — например, добавить аудио- и графические файлы в свой проект. Когда переводы будут готовы, импортируйте локализацию в проект и тщательно протестируйте своё приложение, на *каждом* поддерживаемом языке и регионе.
|
||||
|
||||
Xcode поддерживает инкрементальную локализацию (ГЛОССАРИЙ: В разработке программного обеспечения термин «инкремент» часто относится к небольшому, но существенному улучшению или изменению, которое применяется к проекту или продукту.) вашего проекта. То есть вы можете добавлять новые переводы частями, поэтапно:
|
||||
|
||||
@@ -200,7 +195,7 @@ Xcode поддерживает инкрементальную локализа
|
||||
|
||||
В идеале нужно было бы протестировать переводы всех языков с вышеуказанными характеристиками, но это потребует очень много усилий. К счастью, Xcode предлагает инструмент для решения и этой задачи.
|
||||
|
||||
# Псевдолокализация
|
||||
# Псевдолокализация
|
||||
|
||||
Как упоминалось выше, в Xcode добавлены специальные псевдоязыки, которые можно использовать для тестирования. Псевдоязык можно выбрать в том же меню "App Language" при редактировании схемы приложения.
|
||||
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
|
||||
Для начала давайте посмотрим, как настройки устройства влияют на то, как приложение будет обходиться с датами и измерениями.
|
||||
|
||||
Откройте в iPhone или на симуляторе `Настройки -> Основные -> Язык и регион`. В самом низу экрана вы увидите стандарты форматов для выбранной локали. По умолчанию iPhone сам выбирает настройки для языка и региона, чтобы пользователям не пришлось об этом думать. Здесь вы можете увидеть настройки календаря, температуры, системы измерений, форматы дат, чисел и даже какой день недели должен быть первым в календаре.
|
||||
Откройте в iPhone или на симуляторе `Настройки -> Основные -> Язык и регион`. В самом низу экрана вы увидите стандарты форматов для выбранной локали. По умолчанию iPhone сам выбирает настройки для языка и региона, чтобы пользователям не пришлось об этом думать. Здесь вы можете увидеть настройки календаря, температуры, системы измерений, форматы дат, чисел и даже какой день недели должен быть первым в календаре.
|
||||
|
||||
> Когда мы говорим про настройки какой-либо локали, то имеем в виду общий случай – базовые настройки. Никто не запрещает вашим пользователям изменять их по-своему, даже если это будет нестандартно для региона. Просто помните, что если человек переедет в другую страну, ему будет удобнее использовать соответствующую комбинацию этих настроек.
|
||||
|
||||
Вы можете поменять настройки на этом экране, чтобы посмотреть, как другие приложения учитывают их в своём интерфейсе или чтобы протестировать своё приложение.
|
||||
|
||||

|
||||

|
||||
|
||||
Итак, мы узнали, что форматы дат, чисел и валют могут отличаться в зависимости от настроек локали пользователя. Важно, чтобы наше приложение было понятным для пользователя, поэтому такие данные нужно корректно сконвертировать для каждой локали.
|
||||
|
||||
Кнопка
|
||||
А как предусмотреть всё-вообще-всё?
|
||||
|
||||
К счастью, перебирать все варианты и изучать особенности каждого региона не нужно — у Apple для этого есть специальные *форматеры*.
|
||||
К счастью, перебирать все варианты и изучать особенности каждого региона не нужно — у Apple для этого есть специальные *форматеры*.
|
||||
|
||||
Пользователь (как и вы сейчас) может поменять настройки языка во время работы приложения. Форматеры, которые мы рассмотрим далее, помогут вам корректно манипулировать данными в коде и не загружать голову лишними сценариями: данные будут показываться пользователю так, будто это сам выбрал их как в настройках телефона. Это удобно: когда мы переключаемся между приложениями, они выглядят привычно и предсказуемо. Так формируется бесшовный опыт взаимодействия с платформой. Для правильного форматирования данных мы будем использовать свойство `Locale`.
|
||||
|
||||
@@ -52,10 +52,10 @@ let customFormattedDate = customDateFormatter.string(from: Date())
|
||||
|
||||
--- КОНЕЦ СКРЫВАШКИ
|
||||
|
||||
Этот способ позволит получить для любого языка переведённую дату в строго заданном порядке:
|
||||
Этот способ позволит получить для любого языка переведённую дату в строго заданном порядке:
|
||||
|
||||
- `Пятница, Апрель 19, 2023`
|
||||
- `Friday, April 19, 2023`
|
||||
- `Пятница, Апрель 19, 2023`
|
||||
- `Friday, April 19, 2023`
|
||||
|
||||
Приятный бонус от Swift: дата сразу переводится на язык пользователя! Вам не нужно просить кого-то или самим переводить дни недели и месяцы. Так произошло, потому что мы указали форматеру свойство `locale`.
|
||||
|
||||
@@ -64,7 +64,7 @@ let customFormattedDate = customDateFormatter.string(from: Date())
|
||||
Кнопка
|
||||
Далее
|
||||
|
||||
Некоторые форматы записи даты и времени могут неоднозначно прочитываться в разных регионах. Например, по записи `06/09/23` невозможно однозначно определить, использован ли шаблон `dd/mm/yy` или `mm/dd/yy`. Так и пользователям может быть непонятно (или попросту непривычно), что приложение пытается им сказать. Кроме дат, надо помнить о том, что в одних странах привыкли к 12-часовому формату времени, а в других — к 24-часовому. Где-то принято использовать обозначения времени суток AM (ГЛОССАРИЙ: AM — лат. "ante meridiem", «до полудня») и PM (ГЛОССАРИЙ: PM – лат. "post meridiem", «после полудня»). Apple рекомендует избегать всего этого разнообразия и пользоваться заготовленными *пресетами* стилей (ГЛОССАРИЙ: Пресет – набор правил и предустановленных параметров: например, стили для времени и даты у `DateFormatter`). Они учтут все эти сложности за вас:
|
||||
Некоторые форматы записи даты и времени могут неоднозначно прочитываться в разных регионах. Например, по записи `06/09/23` невозможно однозначно определить, использован ли шаблон `dd/mm/yy` или `mm/dd/yy`. Так и пользователям может быть непонятно (или попросту непривычно), что приложение пытается им сказать. Кроме дат, надо помнить о том, что в одних странах привыкли к 12-часовому формату времени, а в других — к 24-часовому. Где-то принято использовать обозначения времени суток AM (ГЛОССАРИЙ: AM — лат. "ante meridiem", «до полудня») и PM (ГЛОССАРИЙ: PM – лат. "post meridiem", «после полудня»). Apple рекомендует избегать всего этого разнообразия и пользоваться заготовленными *пресетами* стилей (ГЛОССАРИЙ: Пресет – набор правил и предустановленных параметров: например, стили для времени и даты у `DateFormatter`). Они учтут все эти сложности за вас:
|
||||
|
||||
```swift
|
||||
let dateFormatter = DateFormatter()
|
||||
@@ -72,40 +72,35 @@ dateFormatter.dateStyle = .medium
|
||||
dateFormatter.timeStyle = .short
|
||||
dateFormatter.locale = Locale.current
|
||||
let formattedDate = dateFormatter.string(from: Date())
|
||||
```
|
||||
```
|
||||
|
||||
В результате получится строка, содержание которой будет отличаться в зависимости от региона и календаря:
|
||||
|
||||
`Apr 19, 2023 at 11:03 PM` или `19 апр. 2023 г., 23:03`.
|
||||
`Apr 19, 2023 at 11:03 PM` или `19 апр. 2023 г., 23:03`.
|
||||
|
||||
> Обратите внимание: календари тоже различаются! В разных странах могут по-своему исчислять эры, месяцы и даже количество дней. Например, в Японии новая эра начинается с каждым новым императором. Длина месяца может варьироваться от года к году, а дней может быть от 5 до 31! Всегда проверяйте такие факты и ни в коем случае не пытайтесь полагаться на собственное представление о времени! Это никого ещё к успеху не приводило!
|
||||
|
||||
Кнопка
|
||||
Далее
|
||||
|
||||
Теперь давайте доработаем динамические строки в `EmojiMixer`! В нашем макете даты указываются в двух местах:
|
||||
Продолжим изучать работу со строками.
|
||||
|
||||
1. `UILabel` — в заголовке у секций с карточками.
|
||||
Чтобы дата `07.12.22` не вводила в заблуждение, 12 июля это или 7 декабря, адаптируем её для наших иностранных пользователей.
|
||||
|
||||
2. `UIDatePicker` — для выбора даты создания карточки.
|
||||
|
||||

|
||||
|
||||
Чтобы дата `07.12.22` не вводила в заблуждение, 12 июля это или 7 декабря, адаптируем её для наших иностранных пользователей.
|
||||
|
||||
Создайте форматер в приложении, как показано в примере. Присвойте свойству `text` заголовка отформатированное значение:
|
||||
Создайте форматер, как показано в примере и распечатайте отформатированное значение:
|
||||
|
||||
```swift
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .medium
|
||||
dateFormatter.timeStyle = .short
|
||||
dateFormatter.locale = .current
|
||||
dateLabel.text = dateFormatter.string(from: Date())
|
||||
```
|
||||
let formattedDate = dateFormatter.string(from: Date())
|
||||
print(formattedDate)
|
||||
```
|
||||
|
||||
Запустите и сравните полученное отображение с макетом.
|
||||
Запустите и изучите полученный вывод.
|
||||
|
||||
Здесь вы заметите неточность — дата выводится слишком подробно: указано время, а месяц указан не в числовом виде. Чтобы это исправить, откройте документацию Apple [DateFormatter.Style](https://developer.apple.com/documentation/foundation/dateformatter/style) и подберите пресет, похожий на дату в макете.
|
||||
Здесь вы заметите что дата выводится слишком подробно: указано время, а месяц указан не в числовом виде. Чтобы это исправить, откройте документацию Apple [DateFormatter.Style](https://developer.apple.com/documentation/foundation/dateformatter/style) и подберите пресет, похожий на дату `07.12.22`.
|
||||
|
||||
ТАБЛИЧКА: DateFormatter.Style // Дата // Время
|
||||
|
||||
@@ -115,18 +110,16 @@ dateLabel.text = dateFormatter.string(from: Date())
|
||||
`short` // `11/23/37` // `3:30 PM`
|
||||
`none` // будет пропущено // будет пропущено
|
||||
|
||||
В нашем макете дата должна состоять полностью из цифр. Значит, нам подойдёт пресет `short`. Время нам не нужно выводить — возьмём для него пресет `none`:
|
||||
Дата должна состоять полностью из цифр. Значит, нам подойдёт пресет `short`. Время нам не нужно выводить — возьмём для него пресет `none`:
|
||||
|
||||
```
|
||||
```swift
|
||||
dateFormatter.dateStyle = .short
|
||||
dateFormatter.timeStyle = .none
|
||||
```
|
||||
|
||||
Запустите проект в разных локалях. Форматер будет выводить числа в привычном для локали порядке, а для арабского даже заменит цифры на восточно-арабские.
|
||||
|
||||

|
||||
|
||||
> При тестировании проекта обратите внимание, что `UIDatePicker` будет самостоятельно менять отображение в зависимости от установленной локали. Специально настраивать `UIDatePicker` не не нужно: Apple уже позаботилась о разработчиках, предоставив нам значение по умолчанию в свойстве [`locale`](https://developer.apple.com/documentation/uikit/uidatepicker/1615995-locale).
|
||||
> При тестировании проекта Tracker обратите внимание, что `UIDatePicker` будет самостоятельно менять отображение в зависимости от установленной локали. Специально настраивать `UIDatePicker` не не нужно: Apple уже позаботилась о разработчиках, предоставив нам значение по умолчанию в свойстве [`locale`](https://developer.apple.com/documentation/uikit/uidatepicker/1615995-locale).
|
||||
|
||||
Для работы с датами Apple также придумали дополнительные форматеры. Предлагаем вам самостоятельно посмотреть в документации примеры с ними:
|
||||
|
||||
@@ -136,7 +129,7 @@ dateFormatter.timeStyle = .none
|
||||
|
||||
- [RelativeDateTimeFormatter](https://developer.apple.com/documentation/foundation/relativedatetimeformatter) – предлагает локализацию времени относительно момента времени. Например, можно получить такие значения как «завтра», «вчера», или «4 минуты назад», причём полностью переведённые на правильный язык.
|
||||
|
||||
- [ISO8601DateFormatter](https://developer.apple.com/documentation/foundation/iso8601dateformatter) — форматер, поддерживающий конвертацию дат в формат стандарта ISO 8601. Его удобно использовать при передаче данных между приложением и сервером.
|
||||
- [ISO8601DateFormatter](https://developer.apple.com/documentation/foundation/iso8601dateformatter) — форматер, поддерживающий конвертацию дат в формат стандарта ISO 8601. Его удобно использовать при передаче данных между приложением и сервером.
|
||||
|
||||
> Обратите внимание: с iOS 15 доступен упрощённый [способ форматирования дат](https://developer.apple.com/documentation/foundation/date/3796312-formatted). Можно обратиться на объекте с датой к методу `formatted()` и использовать те же самые стили, чтобы получить необходимый формат. В этом уроке мы рассматриваем общеупотребимые варианты, которые подойдут как для новых, так и для более старых версий iOS. Познакомиться с новым API мы также предлагаем вам самостоятельно.
|
||||
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
# Локализация изображений
|
||||
|
||||
В этом уроке мы поговорим о том, какие ситуации требуют от нас локализации изображений в iOS-приложении и как это реализовать в проекте.
|
||||
|
||||
# Адаптация изображений для Right-to-Left-языков
|
||||
## Адаптация изображений для Right-to-Left-языков
|
||||
|
||||
Вы уже видели, как меняется приложение, если поменять направление чтения с LTR на RTL в зависимости от региона. Это также наиболее частая причина для адаптации изображения в зависимости от той или иной локали.
|
||||
Вы уже видели, как меняется приложение, если поменять направление чтения с LTR на RTL в зависимости от региона. Это также наиболее частая причина для адаптации изображения в зависимости от той или иной локали.
|
||||
|
||||
Давайте рассмотрим это на примере иконки в `EmojiMixer`, расположенной внутри карточки с эмоджи. Добавьте в Asset-каталог иконку c закладкой (в нашем примере она выглядит как красный треугольник):
|
||||
> Для экспериментов, описанных в этом уроке, вы можете создать новый пустой проект в Xcode. Добавьте в него любое изображение, чтобы применять на практике рассматриваемые инструменты.
|
||||
|
||||

|
||||
Давайте рассмотрим это на примере изображения, которое вы добавили в ваш проект.
|
||||
|
||||
Добавим в ячейку `UIImageView` элемент и зафиксируем положение «верхний правый угол»:
|
||||
Добавим на экран `UIImageView` и зафиксируем положение в верхнем правом углу. Для этого в коде вашего ViewController'а можно использовать следующий код:
|
||||
|
||||
```swift
|
||||
contentView.addSubview(markImageView)
|
||||
markImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
// Предполагается, что imageView уже создан, например, на сториборде или программно
|
||||
view.addSubview(imageView)
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
markImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||
markImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||
markImageView.heightAnchor.constraint(equalToConstant: 44),
|
||||
markImageView.widthAnchor.constraint(equalTo: markImageView.heightAnchor),
|
||||
imageView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
imageView.heightAnchor.constraint(equalToConstant: 100),
|
||||
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
|
||||
])
|
||||
```
|
||||
|
||||
Протестируем приложение для RTL языка (можно выбрать ранее добавленный арабский либо псевдо-RTL, как мы упоминали в предыдущем уроке).
|
||||
Протестируем приложение для RTL языка (можно выбрать ранее добавленный арабский либо псевдо-RTL, как мы упоминали в предыдущем уроке).
|
||||
|
||||
Теперь приложение зеркально отобразило интерфейс (как и положение иконки), но саму иконку оставило в том виде, в каком мы её добавляли в каталог.
|
||||
|
||||

|
||||
Теперь приложение зеркально отобразило интерфейс (как и положение иконки), но саму картинку оставило в том виде, в каком мы её добавляли в каталог.
|
||||
|
||||
Конечно, можно было бы добавить отзеркаленную версию картинки и программно перебрать поддерживаемые RTL-языки, чтобы выставить картинку. Но Xcode позволяет это сделать без единой строчки кода!
|
||||
|
||||
@@ -37,7 +38,7 @@ NSLayoutConstraint.activate([
|
||||
|
||||

|
||||
|
||||
Давайте посмотрим, какие значения предлагает Xcode:
|
||||
Давайте посмотрим, какие значения предлагает Xcode:
|
||||
|
||||
- `Fixed` — стандартное значение, которое указывает, что изображение будет использовано «как есть» для любого направления чтения. Используйте это свойство для иллюстраций или изображений, которые хорошо смотрятся без отзеркаливания — аватарки, фотографии, иконки социальных сетей или логотипы компаний.
|
||||
|
||||
@@ -47,9 +48,9 @@ NSLayoutConstraint.activate([
|
||||
|
||||
- `Both` — Xcode предложит вам добавить две версии изображения для одного asset'а и будет использовать соответствующее изображение для LTR- и RTL-языков. Такое может пригодиться, если вам надо не просто отобразить картинку зеркально, а полностью её заменить.
|
||||
|
||||
> Выберите нашу иконку в Asset Catalog. В окне инспектора атрибутов установите значение "Direction" на "Left to Right, Mirrors". Запустите приложение, чтобы убедиться, что закладка отзеркалилась.
|
||||
> Выберите ваше изображение в Asset Catalog. В окне инспектора атрибутов установите значение "Direction" на "Left to Right, Mirrors". Запустите приложение, чтобы убедиться, что изображение отзеркалилось.
|
||||
|
||||
# Поддержка локализованных изображений
|
||||
## Поддержка локализованных изображений
|
||||
|
||||
Кроме настройки отражений изображения, Xcode также позволяет выбирать разные изображения для каждого языка (точно так же, как это делается со строками). Давайте рассмотрим примеры, когда это необходимо делать:
|
||||
|
||||
@@ -73,25 +74,25 @@ NSLayoutConstraint.activate([
|
||||
|
||||
3. Перетащите локализованное изображение или изображения в соответствующие поля для выбранных языков.
|
||||
|
||||
В качестве примера для арабского языка мы заменили цвет иконки для закладки:
|
||||
В качестве примера для арабского языка мы можем заменить цвет изображения:
|
||||
|
||||

|
||||

|
||||
|
||||
> Точно так же, как для изображений, вы можете сделать локализованные цвета в ассет-каталоге: алогоритм добавления локализации очень похож.
|
||||
> Точно так же, как для изображений, вы можете сделать локализованные цвета в ассет-каталоге: алгоритм добавления локализации очень похож.
|
||||
|
||||
Кнопка
|
||||
Далее
|
||||
|
||||
Вместо того, чтобы добавлять новую картинку, перекрашенную в другой цвет, мы могли бы завести отдельные цвета для иконки закладки: красный и фиолетовый. Для этого понадобится:
|
||||
Вместо того, чтобы добавлять новую картинку, перекрашенную в другой цвет, мы могли бы завести отдельные цвета для нашего изображения: например, красный и фиолетовый. Для этого понадобится:
|
||||
|
||||
1. Настроить изображение в Asset-каталоге, выбрать свойство `Render As "Template Image"`.
|
||||
|
||||
2. Воспользоваться свойством `tintColor` у `UIImageView`.
|
||||
|
||||
3. Установить цвет из ассета с помощью инициализатора с названием ассета `UIColor(named: "markColor")`.
|
||||
3. Установить цвет из ассета с помощью инициализатора с названием ассета `UIColor(named: "iconColor")`.
|
||||
|
||||
Этот способ сэкономит вашему приложению память: вам не придётся добавлять ещё одно изображение, ведь дополнительный цвет сохранится в текстовом формате. Однако для изображений более сложных по цвету рекомендуем воспользоваться предыдущим способом.
|
||||
Этот способ сэкономит вашему приложению память: вам не придётся добавлять ещё одно изображение, ведь дополнительный цвет сохранится в текстовом формате. Однако для изображений более сложных по цвету рекомендуем воспользоваться предыдущим способом.
|
||||
|
||||
# Подведём итоги
|
||||
## Подведём итоги
|
||||
|
||||
В этом уроке мы рассмотрели, как в Xcode добавляются локализованные ассеты, а также разобрали ситуации, когда нам это может пригодиться. В следующем уроке разберём нюансы вёрстки.
|
||||
В этом уроке мы рассмотрели, как в Xcode добавляются локализованные ассеты, а также разобрали ситуации, когда нам это может пригодиться. В следующем уроке разберём нюансы вёрстки.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||

|
||||
|
||||
# Правила вёрстки
|
||||
# Правила вёрстки
|
||||
|
||||
При добавлении локализации в первую очередь нужно учитывать то, что размер текста перестанет быть фиксированным. Теперь он будет зависеть от длины слов в предложениях, а также от того, насколько точным будет перевод для интерфейса. Переведённый текст может оказаться гораздо длиннее, чем тот, который вы использовали в макетах на языке оригинала.
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
# Semantic Content Attribute
|
||||
|
||||
Кроме anchor'ов Auto Layout, нам также доступно свойство [`Semantic Content Attribute`](https://developer.apple.com/documentation/uikit/uiview/1622461-semanticcontentattribute). Аналогично свойству `Direction`, это свойство помогает зафиксировать отображение для LTR и RTL-языков.
|
||||
Кроме anchor'ов Auto Layout, нам также доступно свойство [`Semantic Content Attribute`](https://developer.apple.com/documentation/uikit/uiview/1622461-semanticcontentattribute). Аналогично свойству `Direction`, это свойство помогает зафиксировать отображение для LTR и RTL-языков.
|
||||
|
||||
Чтобы найти `Semantic Content Attribute` для элемента интерфейса в Interface Builder, откройте в правой боковой панели Xcode раздел "Attributes Inspector" и найдите поле "Semantic". В коде для наследников UIView будет доступно свойство `semanticContentAttribute`.
|
||||
|
||||
По умолчанию там задано значение `Unspecified` — это значит, что элемент может иметь зеркальное отображение для RTL-языка. Но иногда нам нужно зафиксировать, что в этом случае пригодится именно `Force Left-to-Right` (или `Force Right-to-Left`) — тогда элемент всегда будет иметь LTR-ориентацию (или RTL), даже если язык системы по умолчанию имеет RTL-направление чтения.
|
||||
|
||||
Также вы можете запретить переворачивать изображения или тексты, содержащие брендинговое название: Apple и на арабском будет Apple.
|
||||
Также вы можете запретить переворачивать изображения или тексты, содержащие брендинговое название: Apple и на арабском будет Apple.
|
||||
|
||||
> Давайте разберём это на примере с тестовым полем. В RTL-языках символы при печати будут появляться справа от каретки. Для ввода данных в общепринятых стандартах такое поведение следует переопределить на LTR. Так, независимо от языка, номер телефона во всех странах принято вводить слева направо.
|
||||
|
||||
@@ -45,8 +45,6 @@
|
||||
|
||||
Если ваш дизайн-макет предусматривает горизонтальное выравнивание элементов, вы можете ориентироваться на значение `userInterfaceLayoutDirection`, чтобы изменить отображение вручную (например, коллекцию с уменьшением при скролле элементов). Чтобы определить точку для анимированного уменьшения элемента, ориентируйтесь на вид отображения в приложении.
|
||||
|
||||

|
||||
|
||||
Если ваше приложение содержит анимации, которые зависят от направления языка (например, анимация перехода между экранами или анимация перемещения элементов), вам также может понадобиться определить направление языка и в соответствии с этим скорректировать направление анимации.
|
||||
|
||||
# Подведём итоги
|
||||
@@ -55,7 +53,7 @@
|
||||
|
||||
Чтобы поддерживать RTL-вёрстку с разных сторон:
|
||||
|
||||
1. Чаще используйте `leading-trailing` констрейнты в своей вёрстке для учёта RTL-языков.
|
||||
1. Чаще используйте `leading-trailing` констрейнты в своей вёрстке для учёта RTL-языков.
|
||||
2. Явно задавайте свойство `Semantic Content Attribute` для более тонкой настройки отзеркаливания элементов.
|
||||
3. Чтобы определить актуальное направление интерфейса, используйте значение `UIUserInterfaceLayoutDirection`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user