Самое элегантное решение для реализации локализации в проекте с несколькими пакетами

#ios #swift #module #localization #swift-package-manager

#iOS #swift #модуль #локализация #swift-package-manager

Вопрос:

В нашем проекте мы используем множество пакетов (Swift Package Manager), чтобы разделить один большой проект на множество атомарных функций и сервисов. Конечно, каждая функция имеет собственные локализуемые ресурсы, а также мы хотим позволить конечному пользователю наших пакетов настраивать локализации и локализовать на разных языках только в своем проекте. В то же время мы стараемся создавать хорошо выглядящий неповторяемый код. И это пример нашего текущего решения:

 internal extension String {

/// Copies could be localized by adding localizations files in project.
/// Also default localization could be changed by using the same keys in localization file in project
var localized: String {
    let localisationFromMainBundle = NSLocalizedString(self,
                                                       tableName: nil,
                                                       bundle: .main,
                                                       value: "",
                                                       comment: "")
    if localisationFromMainBundle == self {
        return NSLocalizedString(self,
                                 tableName: nil,
                                 bundle: .module,
                                 value: "",
                                 comment: "")
    } else {
        return localisationFromMainBundle
    }
}
 

}

Вы можете видеть, что этот код пытается локализовать текст из .main bundle, но если это не удалось (локализованная строка равна ключу), он пытается локализовать его из .module bundle . И мы используем его в коде так же, как

 "localisation_key".localise
 

Но здесь у нас есть один неприятный момент: нам приходится копировать этот код в каждый модуль, потому что, если мы создадим еще один пакет утилит для сохранения этого кода, он начнет искать ресурсы .module не внутри модуля, специфичного для конкретной функции, где мы вызываем «ключ».локализовать, но внутри себя.

Кто-нибудь знает, как это реализовать лучше?

Комментарии:

1. Как насчет использования вместо: func localize(bundle: Bundle = Bundle.main) (или по умолчанию модульного) i? Таким образом, верхнему уровню может быть проще решить, какой из них использовать? По умолчанию или на заказ? И т.д.

2. Возможно, я просто не могу понять, но мы не хотим разрешать выбирать пакет. Мы находимся внутри модуля и используем локализации из этого модуля по коду, который находится в том же модуле. И это работает. Проблема: я думаю, что копировать одно и то же расширение в модули 5-100 неправильно, и я попытался переместить представленное расширение в самый базовый модуль с модификатором public, но в этом случае это расширение начинает просматривать файлы локализации в этом самом базовом модуле, но они находятся на один уровень выше модуля или на несколько уровнейвыше (не в .main bundle, в .module, но отличается)

3. Вы можете попробовать это: поместите этот код в дополнительный пакет и убедитесь, что код встроен: extension String { @inlinable @inline(__always) func localized() -> String { ... } } не уверен, что это работает, дайте мне знать! 😉 Однако лично я бы выбрал что-то похожее на то, что предложил Ларме.

4. В моем случае создание вычисляемого свойства @inlinable не помогает, потому что я получил ошибку компиляции, что Bundle.module является внутренним. Игра вокруг использования чего-то другого пока не помогает.

5. Теперь я решил использовать идею Ларме