Как локализовать SPM модуль?
Swift Package Manager существует уже довольно давно, его популярность растёт, как и количество модулей, доступных для использования. Если в вашем модуле присутствует (или будет присутствовать, если вы пришли сюда из поисковика) UI, то, вероятнее всего, в нём есть различные ресурсы - текстовые или изображения. В таком случае будет здорово, если ваш модуль будет локализован, ведь это гарантирует полезность вашего модуля для как можно большего числа разработчиков.
Обозначьте локализацию по умолчанию
Чтобы локализовать ресурсы вашего модуля, передайте опциональный параметр defaultLocalization
внутри вашего Package.swift
. В качестве значения он принимает строку с двухбуквенными кодами языков ISO 639-1 или трёхбуквенными кодами языков ISO 639-2 с дополнительными обозначениями региона или алфавита. Ознакомиться со списком кодов можно здесь.
В этом примере в качестве локализации по умолчанию используется английский язык:
let package = Package(
name: "AnyLocalizedKit",
defaultLocalization: "en",
...
Создайте директорию с ресурсами
Внутри папки с названием модуля создайте папку с "Resources". Теперь внутри Package.swift
для каждого таргета добавьте опциональное поле resources
- это массив, содержащий пути для ресурсов.
В нашем случае, добавим путь для целой папки:
targets: [
.target(
name: "AnyLocalizedKit",
dependencies: [],
resources: [.process("Resources")]
)
Обратите внимание, как в приведенном выше примере кода используется функция process(_:localization:)
. Когда вы явно объявляете список ресурсов, вы должны выбрать одно из этих правил, чтобы определить, как Xcode обрабатывает файл ресурсов:
Apple советует использовать
process(_:localization:)
в большинстве случаев, чтобы Xcode обрабатывал ресурсы в соответствии с платформой, для которой вы создаете модуль. Например, Xcode может оптимизировать файлы изображений для платформы, которая поддерживает такую оптимизацию.Используйте
copy(_:)
для тех случаев, когда существуют требования, чтобы сохранить определенную структуру каталогов для ресурсов или просто чтобы файл ресурсов оставался нетронутым.
Добавьте ресурсы для различных языков
Добавление локализованных ресурсов осуществляется в директориях, имеющих название, в котором используется вышеупомянутый код языка ISO-639, за которым следует .lproj
. Примеры: en-GB.lproj
, eng.lproj
, en.lproj
. Xcode распознает локализованные ресурсы в каталогах .lproj
и автоматически создает resource bundles.
Примечание: код языка должен быть одинаковым для всего модуля. Не получится указать
defaultLocalization: "eng"
, а папку с ресурсами назватьen-GB.lproj
. Вам выведется соответствующее предупреждение в Xcode.
После создания всех папок, вы можете начать добавлять ресурсы. Если с изображениями всё понятно, то с текстовой локализацией есть вопросы, ведь классического "Strings File" для создания нет. Вам необходимо создать "Empty" файл, который нужно переименовать в Localizable.strings
. Выглядеть это будет примерно так:
На следующем скриншоте показана структура модуля с локализованными ресурсами:
В каждый из файлов Localizable.strings
добавим для примера одну строчку: greetings = "Hello!";
Пример создания UIView с локализацией
Для примера создадим экземпляры классов UIImageView
и UILabel
. Создадим экземпляр класса UIImage
, в который передадим нашу локализованную картинку:
let image = UIImage(named: "flag", in: .module, with: nil)
Для UILabel
необходимо задать свойство text
:
label.text = NSLocalizedString("greetings", bundle: .module, comment: "")
Пример создания локализованного UIView:
import UIKit
open class AnyLocalizedView: UIView {
private let imageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "flag", in: .module, with: nil))
imageView.contentMode = .scaleAspectFit
return imageView
}()
private let label: UILabel = {
let label = UILabel()
label.text = NSLocalizedString("greetings", bundle: .module, comment: "")
return label
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [imageView, label])
stackView.spacing = 12
stackView.alignment = .center
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
public init() {
super.init(frame: .zero)
setupView()
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
addSubview(stackView)
NSLayoutConstraint.activate([
stackView.centerYAnchor.constraint(equalTo: centerYAnchor),
stackView.centerXAnchor.constraint(equalTo: centerXAnchor),
])
}
}
Всё что нам осталось - это использовать наш локализованный модуль:
import UIKit
import AnyLocalizedKit
class ViewController: UIViewController {
private let localizedView = AnyLocalizedView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(localizedView)
localizedView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
localizedView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
localizedView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
}
}
Полученный результат будет выглядеть как-то так
SwiftUI
Если вы используете SwiftUI, то воспользуйтесь следующим кодом:
Text("greetings", bundle: .module)
Локализация Storyboard и файлов Interface Builder
Если ваш модуль содержит Storyboard или файлы Interface Builder в качестве ресурсов, используйте базовую интернационализацию, чтобы избавить локализаторы от необходимости изменять эти файлы напрямую. Чтобы Xcode автоматически распознавал базовую локализацию в вашем модуле, выполните следующие шаги:
Создайте каталог с именем, например, "Resources", для ваших локализованных ресурсов.
Создайте подкаталог с именем
Base.lproj
и поместите в него раскадровки пакета и файлы Interface Builder.Поместите каталоги
.lproj
для всех поддерживаемых языков в каталог Ресурсов. Если вы предпочитаете явно объявлять ресурсы для базовой интернационализации, используйтеprocess(_:)
и передайте Resource.Localization.base для этого.
Например, используйте следующее, чтобы объявить файл .xib
, который поддерживает базовую интернационализацию:
targets: [
.target(
...
resources: [
.process("Resources"),
.process(”путь/к/ViewController.xib”, localization: .base)
]
)
Примечание
В процессе локализации вы можете столкнуться с такой ошибкой:
Для её решения вам необходимо просто собрать проект. Если не получилось, то воспользуйтесь набором стандартных действий:
Очистить Build Folder;
Перезапустить Xcode;
Очистить DerivedData.