Pular para o conteúdo
Development

Adaptando o Deep Cut Atlas para String Catalogs do Xcode (e a pegadinha da CLI que ninguém avisa)

Por Victor Da Luz•
swiftiosi18ndev-logdeep-cut-atlas

Estou deixando o Deep Cut Atlas pronto para lançar com suporte a espanhol e português brasileiro, o que significa que o primeiro passo de verdade não é traduzir nada. É dar ao Xcode um lugar para colocar as traduções. É isso que um String Catalog (Localizable.xcstrings) é: um único arquivo JSON que guarda toda string traduzível do app, indexada pelo texto original em inglês, com uma coluna por idioma.

O problema

Antes disso, o app não tinha nenhuma infraestrutura de localização. Sem .xcstrings, sem arquivos .strings, sem pastas .lproj, só cerca de 150 literais em inglês hardcoded espalhados por Text(), Button(), mensagens de toast e texto de erro em uns 20 dos 71 arquivos Swift do app.

O discurso do Xcode para String Catalogs é que a maior parte disso sai de graça: adicione o catálogo, construa uma vez, e o Xcode varre seu código-fonte SwiftUI atrás de Text("..."), Button("..."), .navigationTitle("...") e puxa todo argumento literal para dentro do catálogo automaticamente. Sem mudança de código na camada de view.

Essa parte é verdade. O que não é óbvio é onde a parte automática para.

O que não é capturado automaticamente

O scanner do Xcode só enxerga literais de string que estão diretamente no local de chamada do SwiftUI. Ele não consegue ver uma string que é montada em outro lugar e entregue ao Text() depois, como uma variável. Isso é a maioria das mensagens de toast e erro deste app, já que elas são montadas em view models e services, não digitadas inline numa view.

Concretamente, isso significou caçar e embrulhar manualmente: toda implementação de LocalizedError.errorDescription nos services de biblioteca musical, player, discover e loja (incluindo os quatro erros de compra/restauração); strings de toast montadas em view models, coisas como "Added to \(playlistDisplayName)"; as propriedades computadas displayName que alimentam múltiplos pontos de chamada de Text() a partir de uma única fonte de verdade; rótulos de acessibilidade com palavras de conexão (“by X”, “from X”, “now playing”) coladas entre si em cinco arquivos de view; e o texto de reconhecimento de privacidade do MusicKit com seu fallback error.localizedDescription.

Cada um desses foi embrulhado em String(localized:) para entrar no catálogo como uma chave real e traduzível, em vez de continuar invisível para a ferramentação.

Deixei algumas coisas de propósito intocadas: nomes de álbum/artista/faixa/playlist (dados reais da Apple Music, não texto do app), o ID de produto do StoreKit, chaves de armazenamento do UserDefaults. Nada disso é traduzível. É só dado que por acaso é uma string.

A pegadinha: o xcodebuild vai mentir para você

Aqui está a parte que custou mais tempo. Fiz tudo isso acima pela linha de comando, rodei xcodebuild, e o build teve sucesso, mas o catálogo continuava com zero chaves. Não “algumas chaves”. Zero.

Acontece que popular um String Catalog a partir dos literais escaneados é um passo exclusivo da IDE do Xcode. O xcodebuild compila arquivos de extração .stringsdata por arquivo-fonte (dá para ver isso acontecendo, SWIFT_EMIT_LOC_STRINGS está fazendo trabalho de verdade), mas ele nunca roda o passo de merge que de fato escreve essas chaves extraídas no Localizable.xcstrings. O build reporta sucesso porque, do ponto de vista do xcodebuild, é um sucesso. Ele simplesmente nunca toca no catálogo.

A correção é quase anticlimática: abrir o projeto no próprio Xcode e construir com Cmd-B. Esse único build roda o merge, e o catálogo vai de 0 chaves para 153.

Este app já tinha uma pegadinha exclusiva da IDE do Xcode (arquivos de configuração do StoreKit só carregam pela IDE, nunca pelo xcodebuild), então achar uma segunda na mesma categoria não foi uma surpresa total. Mas é um bom lembrete de que “o build teve sucesso” e “a coisa que você de fato queria que acontecesse, aconteceu” são duas afirmações diferentes. CI e automação construídos inteiramente em torno do xcodebuild podem silenciosamente perder passos que só existem dentro do próprio pipeline de build do Xcode.

Verificando que de fato funcionou

Depois que o catálogo foi populado, rodei a suíte de testes completa (198/198 passando) e depois percorri o simulador checando strings específicas que foram embrulhadas: um toast de “Album added to playlist name”, os rótulos de acessibilidade “by artist, from album”, os rótulos de tipo de lançamento Album/Single. Tudo renderizou de forma idêntica aos literais de antes do embrulho. Mesmo texto, mesma interpolação, nada truncado ou corrompido.

A única coisa que não deu para fazer nessa rodada foi a checagem equivalente num dispositivo físico via espelhamento. Não estava disponível nesta sessão, então isso é uma lacuna conhecida, não um passo pulado. Tudo o mais está feito: o catálogo só em inglês está completo, que era o objetivo real aqui. As traduções por idioma (es, pt-BR) são trabalho de continuação separado agora que existe um lugar para colocá-las.

Leitura relacionada

Você também pode achar útil

Proton

Proton Mail

E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
Proton

Proton Pass

Gerenciador de senhas focado em privacidade, da equipe por trás do Proton Mail.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
RackNerd

RackNerd VPS

Hospedagem VPS econômica para serviços leves que funcionam continuamente.

Como afiliado da RackNerd, ganho com compras qualificadas.

Saiba mais