Para um app para Mac com painéis pertencentes a frameworks, defina o fallback em dois níveis: qual idioma cada pacote seleciona e o que cada mensagem retorna quando a busca falha. Use uma localização de desenvolvimento em inglês e padrões explícitos em inglês para as mensagens que você controla.

Este guia implementa esse contrato para uma simples janela de desktop que contém o rótulo Workspace, pertencente ao app, e o rótulo Panel ready, pertencente ao framework. Os rótulos são fictícios. O objetivo é manter essas mensagens legíveis quando um idioma ou uma tradução estiver ausente, preservando a localização normal quando houver traduções.

O que o contrato de fallback deve garantir?

Adote este contrato para as mensagens pertencentes ao app e ao framework: use uma localização disponível selecionada pela correspondência de idiomas do sistema; use o inglês como localização de desenvolvimento quando nenhum idioma preferido corresponder; e retorne o padrão explícito em inglês de uma mensagem se a busca por sua string falhar. Cada mensagem deve identificar o pacote e a tabela de strings a que pertence.

A seleção de idioma considera os idiomas preferidos pelo usuário e as localizações disponíveis. A ausência do idioma de primeira escolha não significa necessariamente que o inglês será exibido imediatamente: outro idioma preferido pode corresponder primeiro. O guia arquivado da Apple sobre pacotes descreve essa sequência de seleção, seguida pela localização de desenvolvimento quando nenhum idioma preferido corresponde. A documentação atual define CFBundleDevelopmentRegion como o identificador do idioma de fallback do pacote. Acessar o conteúdo de um pacote, CFBundleDevelopmentRegion.

Use a tabela de decisão a seguir para definir os resultados esperados. Ela separa a seleção de recursos da busca por mensagens, em vez de tratar toda tradução ausente como a mesma condição.

Condição Contrato para as mensagens que você controla O que verificar
Há uma tradução disponível para um idioma preferido Exiba essa tradução Pacote, tabela e chave
O primeiro idioma preferido está indisponível, mas outro idioma preferido corresponde Permita que a correspondência de idiomas do sistema selecione a localização disponível Ordem de preferência e recursos incluídos
Nenhuma localização preferida corresponde Use os recursos em inglês do pacote para o idioma de desenvolvimento Idioma de desenvolvimento e recursos em inglês
A busca selecionada não consegue localizar a mensagem Retorne o padrão explícito em inglês da mensagem O defaultValue da mensagem
Uma busca do framework é direcionada por engano ao pacote do app Corrija o argumento do pacote Propriedade dos recursos
Existe uma tradução, mas o texto é inadequado Corrija a tradução Conteúdo e revisão do catálogo

A tabela combina as regras da Apple para seleção de recursos com o comportamento documentado do valor padrão do inicializador Swift. Trata-se de uma política de implementação derivada dessas APIs, não de um recurso de fallback separado nos Catálogos de Strings. Uma busca bem-sucedida que contenha texto inadequado não atende à condição documentada do inicializador para usar o valor padrão: uma busca malsucedida. String.init(localized:defaultValue:table:bundle:locale:comment:).

Configure os recursos-fonte em inglês em cada destino responsável

Para este exemplo, use uma instalação atual do Xcode, um app SwiftUI para macOS que tenha como destino o macOS 13 ou posterior e um destino de framework Swift dinâmico incorporado chamado PanelKit. Vincule e incorpore esse framework ao app. Esses são pré-requisitos para o projeto de exemplo; o nome do framework não identifica um produto existente.

Defina o inglês como idioma de desenvolvimento para ambos os destinos e verifique se CFBundleDevelopmentRegion de cada pacote compilado resolve para en. Forneça os recursos-fonte correspondentes em inglês em ambos os destinos. Um identificador de idioma de desenvolvimento descreve o idioma de fallback; ele não fornece o conteúdo das mensagens ausentes. A Apple documenta en como o identificador do idioma inglês. CFBundleDevelopmentRegion.

Crie um catálogo Localizable.xcstrings separado para cada destino. No Xcode, escolha Arquivo > Novo > Arquivo do Modelo, selecione Catálogo de Strings e nomeie-o Localizable. Atribua um catálogo ao destino do app e o outro ao destino do framework. Manter a propriedade dos recursos separada permite verificar cada busca de forma independente. A Apple documenta o fluxo de criação de catálogos e oferece suporte a vários catálogos. Localizar e variar texto com um catálogo de strings.

Depois de adicionar o código abaixo, escolha Produto > Compilar. O Xcode extrai para os catálogos as chamadas reconhecidas como localizáveis. Confirme que workspace.title aparece no catálogo do app e que panel.ready aparece no catálogo do framework, com os valores-fonte em inglês pretendidos. Adicione os idiomas aceitos usando o botão de adicionar na barra lateral do catálogo e insira as traduções das mensagens relevantes. Localizar e variar texto com um catálogo de strings.

A integridade do catálogo e a correção da busca em tempo de execução precisam de verificações separadas. Uma entrada traduzida no catálogo do framework não ajuda uma chamada que busca no pacote do app. A Apple instrui explicitamente o código que carrega strings fora do pacote principal a passar o pacote associado ao destino. Preparar o texto do app para tradução.

Atribua um pacote, uma chave, uma tabela e um padrão a cada mensagem

Use chaves fictícias estáveis e padrões legíveis em inglês. O inicializador Swift que aceita defaultValue permite usar uma chave diferente do texto-fonte exibido. Seu valor padrão é retornado quando a busca pela string localizada no pacote falha. O parâmetro table seleciona a tabela de strings; se omitido, seleciona Localizable. String.init(localized:defaultValue:table:bundle:locale:comment:).

Coloque as mensagens do framework no framework

Adicione este arquivo-fonte Swift completo ao destino do framework dinâmico PanelKit. A classe marcadora é definida nesse destino para que Bundle(for:) identifique seu pacote. A Apple documenta essa abordagem baseada em classe como alternativa à macro atual #bundle. Preparar o texto do app para tradução.

import Foundation

private final class PanelBundleAnchor: NSObject {}

public enum PanelText {
    public static var resourceBundle: Bundle {
        Bundle(for: PanelBundleAnchor.self)
    }

    public static var ready: String {
        String(
            localized: "panel.ready",
            defaultValue: "Panel ready",
            table: "Localizable",
            bundle: resourceBundle,
            comment: "Status label above the framework panel."
        )
    }
}

O framework resolve sua mensagem antes de retornar uma String ao app. Isso estabelece um limite claro de propriedade: o framework controla a chave, o texto-fonte, a tabela e o pacote de recursos. O app recebe o texto resultante para exibição.

Exiba as mensagens do app e do framework juntas

Substitua o arquivo gerado do ponto de entrada do app por este exemplo completo. Ele pressupõe que o módulo do framework vinculado se chama PanelKit e contém o arquivo anterior. Mantenha apenas um ponto de entrada do app no destino do app.

import Foundation
import SwiftUI
import PanelKit

@main
struct FallbackDemoApp: App {
    var body: some Scene {
        WindowGroup {
            VStack(alignment: .leading, spacing: 12) {
                Text(verbatim: String(
                    localized: "workspace.title",
                    defaultValue: "Workspace",
                    table: "Localizable",
                    bundle: .main,
                    comment: "Heading for the main workspace."
                ))

                Text(verbatim: PanelText.ready)
            }
            .padding(24)
        }
    }
}

A busca do app usa explicitamente .main; a busca do framework usa seu próprio pacote de recursos. Ambas fornecem padrões em inglês. Isso segue os parâmetros documentados pela Apple para selecionar pacotes e valores padrão. Compile e execute o projeto no Xcode para validar a configuração completa dos destinos; estes exemplos não foram executados aqui. String.init(localized:defaultValue:table:bundle:locale:comment:).

Para uma implementação existente que use Bundle.localizedString(forKey:value:table:), forneça um value não vazio e legível quando precisar de um resultado legível para uma chave ausente. A Apple documenta que uma chave ausente retorna a própria chave quando value está ausente ou vazio, e retorna value quando não está vazio. Portanto, uma chave como panel.ready precisa de um fallback explícito se exibir esse identificador violar o contrato da mensagem. localizedString(forKey:value:table:).

Decida se os frameworks podem selecionar idiomas diferentes

Passar o pacote correto indica onde buscar. A configuração de localização mista afeta a relação entre a seleção de localização e o app que contém o framework. A Apple disponibiliza o booleano CFBundleAllowMixedLocalizations, exibido como Recursos localizados podem ser misturados, para buscar strings localizadas em frameworks. CFBundleAllowMixedLocalizations.

A explicação desse comportamento nos Swift Forums afirma que habilitar a chave no Info.plist do app permite que outros pacotes selecionem localizações além das aceitas pelo app principal. Sem essa opção explícita, o pacote principal restringe a seleção deles. Essa é uma discussão explicativa mais antiga, útil em conjunto com a referência atual da Apple para a chave. Localização no Swift Package Manager.

Para a janela de exemplo, prefira manter a cobertura de idiomas aceitos igual no app e no framework, com recursos de desenvolvimento em inglês em ambos. Deixe a seleção independente de idioma do framework fora do contrato inicial. Se, em vez disso, você pretende permitir que o framework use traduções adicionais, defina o booleano CFBundleAllowMixedLocalizations do app como YES e teste essa configuração explicitamente. Localização no Swift Package Manager.

Habilitar essa chave não fornece uma tradução nem um padrão para mensagens ausentes. Essas responsabilidades continuam pertencendo aos arquivos de recursos e às chamadas de busca. Da mesma forma, um padrão fornecido na sua chamada define o resultado dessa chamada quando a busca falha; ele não estabelece um fallback universal em inglês para todo controle do sistema ou mensagem de framework.

Evite usar o argumento locale deste inicializador String como seletor do idioma da tradução. A Apple documenta explicitamente que ele controla a localização de valores interpolados, como números, e não altera a localidade usada para buscar a string localizada. Para esta tarefa, use a configuração de idiomas aceita e os controles de teste. String.init(localized:defaultValue:table:bundle:locale:comment:).

Teste idiomas ausentes separadamente de mensagens ausentes

Execute as verificações de localização em Produto > Esquema > Editar Esquema. Selecione Executar, abra Opções, escolha um Idioma do App, feche a caixa de diálogo e execute o app para Mac novamente. A Apple documenta esse fluxo para testar localizações sem alterar os ajustes de todo o sistema. Mantenha a seleção de região inalterada ao investigar um problema apenas de idioma. Testar localizações ao executar o app.

Use a matriz de testes a seguir como um recurso de diagnóstico específico do projeto. Para verificar o direcionamento dos pacotes, use temporariamente marcadores de teste diferentes em inglês, como App translation sample e Framework translation sample, em uma localização aceita que não seja a de origem. Esses são textos de diagnóstico, não traduções nem identificadores de idioma.

  1. Verifique o comportamento da origem. Execute com o inglês selecionado. Espere Workspace e Panel ready.
  2. Verifique o direcionamento aos recursos traduzidos. Selecione a localização de teste aceita. Espere os marcadores distintos dos dois catálogos responsáveis.
  3. Verifique o padrão para mensagem ausente. Em uma alteração de teste descartável, mude a chave da busca do framework para panel.ready.probe, deixe defaultValue inalterado e impeça que essa entrada de teste seja incluída no catálogo de teste. Se a busca falhar, espere Panel ready. Restaure a chave original depois.
  4. Verifique a seleção para idiomas não aceitos. Teste uma lista de preferências sem correspondência com nenhum idioma aceito. Confira se ambos os pacotes responsáveis usam seus recursos de desenvolvimento em inglês. Teste também uma primeira preferência não aceita seguida de uma preferência aceita, pois esse caso tem um resultado esperado diferente.
  5. Verifique a localização mista, se habilitada. Teste um idioma disponível somente no framework. Registre o comportamento pretendido e compare com ele a configuração de localização mista do app.

A terceira verificação isola uma busca malsucedida; a quarta isola a correspondência de idiomas. Testar apenas uma linha não traduzida do catálogo pode confundir essas condições, pois a configuração de recursos resultante também afeta a busca. O resultado esperado para uma chave ausente vem do inicializador Swift, enquanto a seleção de idiomas preferidos segue as regras dos pacotes. String.init(localized:defaultValue:table:bundle:locale:comment:), Acessar o conteúdo de um pacote.

Quando os resultados forem diferentes do esperado, inspecione o pacote responsável. developmentLocalization informa o valor correspondente a CFBundleDevelopmentRegion; preferredLocalizations informa as localizações disponíveis ordenadas segundo as preferências de idioma. Compare essas propriedades em Bundle.main e PanelText.resourceBundle. Elas ajudam a diagnosticar a configuração, mas as mensagens exibidas continuam sendo a verificação final de cada busca. developmentLocalization, preferredLocalizations.

Um contrato de fallback útil torna cada resultado explicável: o idioma selecionado vem da correspondência de pacotes, a mensagem vem da tabela a que pertence e uma busca malsucedida retorna um padrão explícito e legível. Aplique essas regras a todas as mensagens do app e do framework que você controla e, em seguida, verifique-as em conjunto na janela de desktop em que os usuários as encontram.

Fontes