O que é internacionalização (i18n) e localização (l10n) no iOS
Internacionalização (i18n) é preparar o app para suportar múltiplos idiomas e formatos regionais sem reescrever telas. Localização (l10n) é fornecer os textos (e, quando necessário, regras) para cada idioma/região.
No iOS, o idioma e a região do usuário influenciam:
- Textos exibidos (traduções)
- Pluralização (1 item vs 2 itens)
- Formatação de datas e horas
- Separadores decimais e de milhar
- Moeda e símbolo monetário
Em SwiftUI, você pode usar chaves de localização diretamente em Text e deixar o sistema escolher a tradução correta com base nas configurações do dispositivo/simulador.
Estrutura de arquivos: como o iOS organiza localizações
O padrão mais comum é usar arquivos Localizable.strings (um por idioma). No Xcode, isso é feito marcando o arquivo como Localized, e o projeto cria variações por idioma (por exemplo, pt-BR e en).
Boas práticas de organização
- Use chaves estáveis (não use o texto como chave). Ex.:
home.title,cart.items_count. - Agrupe por tela/feature via prefixos:
settings.,profile.,checkout.. - Evite concatenar strings manualmente (isso quebra gramática em outros idiomas). Prefira strings com variáveis.
Passo a passo: adicionando Português e Inglês com Localizable.strings
1) Adicionar idiomas ao projeto
No Xcode:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
- Selecione o projeto (ícone azul) > PROJECT > Info
- Em Localizations, clique em
+e adicione Portuguese (Brazil) (pt-BR) e English (en)
2) Criar o arquivo Localizable.strings
No Xcode:
- File > New > File... > Strings File
- Nomeie como
Localizable.strings - Selecione o arquivo > no painel da direita (File Inspector) marque Localize...
- Escolha um idioma base (geralmente English) e confirme
- Marque também a localização para Portuguese (Brazil)
3) Preencher as chaves em cada idioma
Exemplo de conteúdo para Localizable.strings (English):
"home.title" = "Home";"home.welcome" = "Welcome, %@!";"home.last_update" = "Last update: %@";"cart.title" = "Cart";"currency.example" = "Total: %@";Exemplo de conteúdo para Localizable.strings (Portuguese (Brazil)):
"home.title" = "Início";"home.welcome" = "Bem-vindo(a), %@!";"home.last_update" = "Última atualização: %@";"cart.title" = "Carrinho";"currency.example" = "Total: %@";Repare no uso de %@ para inserir valores (strings) dentro do texto. Isso evita concatenação e permite que cada idioma reorganize a frase.
Usando chaves localizáveis em SwiftUI
Text com chave direta
Quando você passa uma string literal para Text, o SwiftUI trata como LocalizedStringKey por padrão, então ele busca a chave no Localizable.strings:
Text("home.title")Quando a chave é dinâmica (String variável)
Se a chave vem de uma variável String, o SwiftUI não consegue inferir como chave localizável automaticamente. Use:
let key = "home.title"Text(LocalizedStringKey(key))Use isso com cuidado: chaves dinâmicas dificultam manutenção e revisão de traduções.
Strings com variáveis: interpolação localizável
Para inserir valores em textos localizados, você tem duas abordagens comuns:
1) Usar String(localized:) (recomendado para controle e testes)
Você gera uma String já localizada e passa para o Text:
let userName = "Ana"let message = String(localized: "home.welcome", arguments: [userName])Text(message)Isso usa o formato do Localizable.strings com %@.
2) Usar interpolação em Text com chave
Em muitos casos, você pode usar interpolação diretamente:
let userName = "Ana"Text("home.welcome \(userName)")Essa forma é prática, mas pode ficar limitada quando você precisa de formatações específicas (número, moeda, data) ou quando quer garantir que a frase inteira seja controlada por arquivo de localização.
Pluralização no iOS: .stringsdict
Pluralização não deve ser feita com if count == 1 e duas strings separadas, porque alguns idiomas têm mais de duas formas de plural. O iOS oferece .stringsdict para regras de plural.
Passo a passo: criando um Localizable.stringsdict
- File > New > File... > procure por Strings Dictionary File
- Nomeie como
Localizable.stringsdict - Localize o arquivo (como fez com o
Localizable.strings) para en e pt-BR
Exemplo de plural para itens no carrinho
Localizable.stringsdict (English) (estrutura XML):
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>cart.items_count</key> <dict> <key>NSStringLocalizedFormatKey</key> <string>%#@items@</string> <key>items</key> <dict> <key>NSStringFormatSpecTypeKey</key> <string>NSStringPluralRuleType</string> <key>NSStringFormatValueTypeKey</key> <string>d</string> <key>one</key> <string>%d item</string> <key>other</key> <string>%d items</string> </dict> </dict></dict></plist>Localizable.stringsdict (Portuguese (Brazil)):
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>cart.items_count</key> <dict> <key>NSStringLocalizedFormatKey</key> <string>%#@items@</string> <key>items</key> <dict> <key>NSStringFormatSpecTypeKey</key> <string>NSStringPluralRuleType</string> <key>NSStringFormatValueTypeKey</key> <string>d</string> <key>one</key> <string>%d item</string> <key>other</key> <string>%d itens</string> </dict> </dict></dict></plist>Agora, para usar essa chave com um número:
let count = 1let text = String(localized: "cart.items_count", arguments: [count])Text(text)Troque count para 2 e valide a forma plural automaticamente.
Formatação respeitando Locale: datas, números e moeda
Mesmo com o app em inglês, um usuário pode estar com região Brasil, e vice-versa. Por isso, formatação deve respeitar Locale (região) e não apenas idioma.
Datas: usando Date.FormatStyle
Em SwiftUI/Swift moderno, prefira Date.FormatStyle:
let date = Date()Text(date, format: .dateTime.day().month().year().hour().minute())Esse formato se adapta ao padrão do usuário. Para forçar um locale específico (útil em debug), você pode aplicar:
let date = Date()Text(date.formatted(.dateTime.day().month().year().locale(Locale(identifier: "pt_BR"))))Números: separadores e casas decimais
Use FormatStyle para números:
let rating = 4.75Text(rating, format: .number.precision(.fractionLength(2)))Em pt-BR, tende a aparecer com vírgula (ex.: 4,75). Em en-US, com ponto (4.75), dependendo da região.
Moeda: símbolo e padrão local
Para moeda, use .currency com o código ISO:
let total = 1234.5Text(total, format: .currency(code: "BRL"))Se você quer que a moeda acompanhe a região do usuário, você precisa decidir a regra do seu app (por exemplo, sempre BRL para um app brasileiro, ou moeda configurável). Para exibir com a moeda da região atual, você pode obter o código:
let total = 1234.5let currencyCode = Locale.current.currency?.identifier ?? "USD"Text(total, format: .currency(code: currencyCode))Exercício: aplicar Português e Inglês no app do curso e validar no simulador
Neste exercício, você vai criar uma tela simples (ou adaptar uma existente) para provar: (1) textos localizados, (2) pluralização, (3) data e moeda formatadas por região.
1) Adicionar chaves necessárias
No Localizable.strings de cada idioma, garanta estas chaves:
| Chave | en | pt-BR |
|---|---|---|
home.title | Home | Início |
home.welcome | Welcome, %@! | Bem-vindo(a), %@! |
home.last_update | Last update: %@ | Última atualização: %@ |
currency.example | Total: %@ | Total: %@ |
No Localizable.stringsdict, garanta a chave cart.items_count para en e pt-BR (como mostrado acima).
2) Criar uma view de teste de localização
Crie uma view (por exemplo, LocalizationDemoView) e apresente os elementos:
import SwiftUIstruct LocalizationDemoView: View { @State private var itemCount: Int = 1 private let userName = "Ana" private let lastUpdate = Date() private let total: Double = 1234.5 var body: some View { VStack(alignment: .leading, spacing: 16) { Text("home.title") .font(.title.bold()) Text(String(localized: "home.welcome", arguments: [userName])) Text(String(localized: "cart.items_count", arguments: [itemCount])) Stepper("Itens: \(itemCount)", value: $itemCount, in: 0...20) Divider() let formattedDate = lastUpdate.formatted(.dateTime.day().month().year().hour().minute()) Text(String(localized: "home.last_update", arguments: [formattedDate])) let currencyCode = Locale.current.currency?.identifier ?? "USD" let formattedMoney = total.formatted(.currency(code: currencyCode)) Text(String(localized: "currency.example", arguments: [formattedMoney])) } .padding() }}O Stepper aqui serve para você alternar rapidamente entre 0, 1, 2, 3... e observar a pluralização.
3) Inserir a tela no fluxo do app
Sem alterar sua arquitetura, você pode apresentar essa view em um ponto do app usado no curso (por exemplo, como uma tela acessível via navegação). O importante é conseguir rodar e testar no simulador.
4) Validar idioma e região no simulador
Você vai validar duas coisas: (a) idioma (tradução) e (b) região (formatação).
Opção A: mudar idioma e região do próprio simulador
- No simulador: Settings > General > Language & Region
- Altere iPhone Language para English e depois para Português
- Altere Region (por exemplo, United States vs Brazil) e observe data, número e moeda
- Feche e reabra o app se necessário para refletir mudanças
Opção B: forçar idioma/região apenas para o app (mais rápido para testar)
No Xcode: Product > Scheme > Edit Scheme... > Run > Options:
- Em Application Language, selecione English e depois Portuguese
- Em Application Region, selecione United States e depois Brazil
Validações esperadas:
- Textos:
home.titlemuda entre “Home” e “Início”. - Plural: com
itemCount = 1aparece “1 item”; comitemCount = 2aparece “2 items” (en) / “2 itens” (pt-BR). - Data: ordem e separadores mudam conforme a região.
- Moeda: símbolo e formatação mudam conforme
Locale.current(se você usou o código da moeda da região atual).
Erros comuns e como evitar
Chave não encontrada
Se a chave não existir no arquivo do idioma atual, o iOS pode exibir a própria chave. Verifique:
- Se o arquivo está realmente localizado para aquele idioma
- Se a chave é idêntica (maiúsculas/minúsculas contam)
- Se você editou o arquivo correto (en vs pt-BR)
Concatenar strings quebra tradução
Evite:
Text("Hello, " + userName)Prefira uma frase inteira com variável:
Text(String(localized: "home.welcome", arguments: [userName]))Pluralização feita na mão
Evite manter duas chaves (singular/plural) e escolher com if. Use .stringsdict para ficar correto em mais idiomas.
Formatar moeda/data manualmente
Evite montar data com "\(day)/\(month)/\(year)" ou moeda com "R$ \(value)". Use FormatStyle para respeitar Locale.