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

Задача намеренно ограничена: отобразить искусственно созданную подпись Open panel из каталога Panel.xcstrings фреймворка. Вам понадобятся проект приложения для Mac в Xcode и целевой объект фреймворка, которым вы управляете. В основном примере используется Xcode 26 или новее, а целевая версия — macOS 12 или новее; Apple указывает, что инициализатор локализованной строки доступен начиная с macOS 12. Документация по инициализатору Swift.

Определите пакет, таблицу и ключ до изменения кода

Рассматривайте локализованное сообщение как поиск по трём явно заданным координатам. Пакет указывает владельца ресурса, таблица — группу строк в этом пакете, а ключ — запись в этой таблице. API локализации Apple задают пакет и таблицу отдельно, поскольку строковые ресурсы могут находиться как в целевом объекте фреймворка, так и в целевом объекте приложения. Подготовка текста приложения к переводу.

Для этого примера перед открытием каталога зафиксируйте следующий контракт. Это иллюстративные имена, а не названия, предписанные Apple.

Часть Пример значения Назначение
Владелец ресурса Целевой объект вашего фреймворка Содержит каталог
Файл каталога Panel.xcstrings Хранит исходный текст таблицы и переводы
Аргумент таблицы Panel Выбирает каталог без расширения
Ключ PANEL_OPEN_ACTION Идентифицирует сообщение
Исходное значение на английском Open panel Задаёт понятный текст интерфейса
Комментарий Подпись к действию, открывающему панель. Объясняет назначение сообщения

Указание table: \"Panel\" выбирает таблицу, представленную файлом Panel.xcstrings. Если не указывать таблицу, выбирается Localizable. Аналогично, если не указывать пакет, инициализатор строки ищет ресурс в основном пакете приложения. Поэтому для ключа в каталоге фреймворка нужны и пакет этого фреймворка, и правильная таблица. Параметры локализованной строки Swift.

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

Добавьте каталог строк в целевой объект фреймворка

В Xcode выберите File > New > File from Template, укажите платформу, выполните поиск по string и выберите String Catalog. Назовите файл Panel. Убедитесь, что каталог добавлен в целевой объект фреймворка вместе с исходным кодом фреймворка, который будет на него ссылаться. Каталоги Apple позволяют задавать собственные имена, а в демонстрации локализации фреймворка Apple каталог создаётся в целевом объекте фреймворка. Локализация и варьирование текста с помощью каталога строк, Демонстрация локализации фреймворка от Apple.

В примере используется семантический ключ, а отображаемый текст не служит ключом. Идентификатор PANEL_OPEN_ACTION и значение по умолчанию Open panel заданы в коде явно. Apple поддерживает такой подход с помощью инициализатора, принимающего статический ключ и отдельное значение локализации по умолчанию. Инициализатор Swift со статическим ключом.

Добавив приведённый ниже исходный код, соберите целевой объект фреймворка. Во время сборки Xcode обнаруживает вызовы локализуемого API и обновляет соответствующие каталоги. Откройте Panel.xcstrings и проверьте, что в нём есть PANEL_OPEN_ACTION, исходное значение на английском Open panel и поясняющий комментарий. Если записи нет, проверьте принадлежность исходного файла целевому объекту и то, собирался ли целевой объект, содержащий этот файл. Также Apple предоставляет команду Editor > Assistant, с помощью которой можно просмотреть код, связанный с записью каталога. Заполнение и проверка каталога строк.

Разрешите сообщение внутри фреймворка

Поместите этот полный исходный файл в целевой объект фреймворка. Он определяет локализованную строку и небольшую метку SwiftUI, которую может отображать содержащее её приложение для Mac. Также в нём есть метод диагностики, используемый в следующем разделе. Код сверялся с документированными сигнатурами Apple; выполнить его здесь не удалось, поскольку компилятор Swift недоступен.

import Foundation
import SwiftUI

public enum PanelMessages {
    public static var openPanel: String {
        String(
            localized: "PANEL_OPEN_ACTION",
            defaultValue: "Open panel",
            table: "Panel",
            bundle: #bundle,
            comment: "Label for the action that opens the panel."
        )
    }

    public static func lookupReport() -> String {
        let missingMarker = "__PANEL_LOOKUP_MISSING__"
        let result = #bundle.localizedString(
            forKey: "PANEL_OPEN_ACTION",
            value: missingMarker,
            table: "Panel"
        )

        if result == missingMarker {
            return "No entry resolved from the selected bundle and table."
        }

        return "Resolved table value: \(result)"
    }
}

public struct PanelActionLabel: View {
    public init() {}

    public var body: some View {
        Text(verbatim: PanelMessages.openPanel)
    }
}

Выражение #bundle вычисляется в месте его записи. В этом исходном коде фреймворка оно возвращает пакет, связанный с целевым объектом фреймворка. Если переместить то же выражение в исходный код целевого объекта приложения, оно вернёт пакет приложения. Для целевого объекта пакета Swift Apple указывает, что макрос возвращает пакет ресурсов этого целевого объекта. Размещайте поиск рядом с принадлежащими ему ресурсами. Макрос bundle в Foundation.

Объявление возвращает обычную строку String, локализация которой уже разрешена. Представление использует Text(verbatim:), чтобы явно отобразить этот результат. Открытое представление — это только подпись; добавьте его в существующее представление приложения там, где должен отображаться текст.

Используйте класс фреймворка, если макрос пакета недоступен

Apple также позволяет находить пакет фреймворка через класс, определённый в этом фреймворке. Для обычного фреймворка с собственными ресурсами ниже приведён полноценный альтернативный поставщик сообщений. Используйте его вместо предыдущего объявления PanelMessages, сохранив описанные выше требования к каталогу и целевой версии.

import Foundation

private final class PanelBundleAnchor: NSObject {}

public enum PanelMessages {
    public static var openPanel: String {
        String(
            localized: "PANEL_OPEN_ACTION",
            defaultValue: "Open panel",
            table: "Panel",
            bundle: Bundle(for: PanelBundleAnchor.self),
            comment: "Label for the action that opens the panel."
        )
    }
}

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

Убедитесь, что читаемый текст получен успешным поиском

То, что на экране видна надпись Open panel, ещё не доказывает, что поиск в каталоге прошёл успешно. Если поиск не удаётся, инициализатор возвращает defaultValue, поэтому один и тот же видимый текст может означать как успешный поиск, так и использование резервного значения. Такое поведение удобно для создания понятного текста интерфейса, но во время разработки оно может скрыть ошибку в выборе пакета или таблицы. Поведение значения по умолчанию в Swift.

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

Эта диагностика напрямую следует из документированного поведения Bundle.localizedString(forKey:value:table:): если ключ отсутствует, а переданное резервное значение непустое, метод возвращает это значение. Если ключ отсутствует, а резервное значение равно nil или пустое, метод возвращает сам ключ. Благодаря метке ошибку проще отличить от допустимого исходного текста. Возвращаемые значения локализованной строки пакета).

Наблюдение Что проверить дальше
Диагностика возвращает сообщение об отсутствующей записи Убедитесь, что каталог включён в выбранный пакет, затем проверьте Panel и PANEL_OPEN_ACTION
Диагностика разрешает неожиданное значение Проверьте именно эту запись в выбранной таблице и язык текущего запуска
Диагностика разрешает правильное значение, но интерфейс отличается Проследите, какое значение фактически отображает интерфейс
Во время запуска с переводом отображается исходное значение на английском Проверьте перевод записи и конфигурацию запуска; успешный поиск сам по себе не доказывает наличие запрошенного перевода

Таблица задаёт последовательность устранения неполадок, выведенную из контракта поиска. Успешная проверка метки подтверждает, что поиск вернул запись, но не подтверждает, что для каждого поддерживаемого языка есть готовый перевод. Проверяйте разрешение ресурсов и полноту переводов отдельно.

Проверьте подпись на языке, выбранном для приложения Mac

Если приложение поддерживает дополнительные языки, выберите нужный язык на боковой панели каталога и введите перевод в текстовом поле этой записи. Затем проверьте результат в содержащем каталог приложении для Mac. Apple описывает прямой ввод переводов в редакторе каталога и обновление состояния перевода каждой записи по мере работы. Добавление переводов в каталог строк.

Чтобы настроить запуск для отладки, выберите Product > Scheme > Edit Scheme, затем Run и откройте Options. Задайте в поле App Language язык, поддерживаемый проектом, закройте редактор и снова запустите приложение для Mac. Если нужно, чтобы язык запуска соответствовал системным настройкам, выберите System Language. Тестирование локализаций при запуске приложения.

Не используйте аргумент locale: инициализатора строки вместо этой проверки языка. Apple указывает, что этот аргумент задаёт локализацию подставляемых значений, например чисел, и не меняет локаль поиска локализованной строки. В примере он опущен, поскольку подпись не содержит подставляемых значений. Параметр locale локализованной строки Swift.

Итоговый контракт должен быть небольшим и явным: Panel.xcstrings принадлежит фреймворку, поиск выбирает Panel, а PANEL_OPEN_ACTION идентифицирует подпись. Проверьте запись с помощью диагностики, а затем проверьте отображаемый перевод на языке, выбранном для запуска приложения. Такая последовательность позволяет сначала исправить поиск ресурсов и не тратить время на текст, который приложение может вообще не считывать.

Источники