O que é NSLocalizedString equivalente no Swift?

228

Existe um equivalente Swift de NSLocalizedString(...)? Em Objective-C, geralmente usamos:

NSString *string = NSLocalizedString(@"key", @"comment");

Como posso conseguir o mesmo no Swift? Eu encontrei uma função:

func NSLocalizedString(
    key: String,
    tableName: String? = default,
    bundle: NSBundle = default,
    value: String = default,
    #comment: String) -> String

No entanto, é muito longo e nada conveniente.

RaffAl
fonte
2
O melhor é criar uma versão mais curta do snippet de código: NSLocalizedString ("", comment: "") ... Gostei da solução de extensão, mas o problema é que o genstrings não captura essas seqüências no arquivo de tradução.
Matej Ukmar
3
No Swift 3, você pode apenas NSLocalizedString("Cancel", comment: "Cancel button title")aproveitar os valores padrão. É conveniente, eu acho.
LShi
Este é um artigo muito bom sobre localização (extensão de strings, tabelas de strings diferentes e até pluralização): medium.com/@marcosantadev/… #
LightMan
Este é um artigo muito bom sobre a localização em Swift para uma arquitetura robusta medium.com/@mendibarouk/...
Mendy

Respostas:

373

Eu uso a próxima solução:

1) criar extensão:

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

2) no arquivo Localizable.strings :

"Hi" = "Привет";

3) exemplo de uso:

myLabel.text = "Hi".localized

desfrutar! ;)

--upd: -

para o caso de comentários, você pode usar esta solução:

1) Extensão:

extension String {
    func localized(withComment:String) -> String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: withComment)
    }
}

2) no arquivo .strings:

/* with !!! */
"Hi" = "Привет!!!";

3) usando:

myLabel.text = "Hi".localized(withComment: "with !!!")
dr OX
fonte
92
O único problema é que você não poderá usar o genstringsutilitário para gerar seus arquivos .strings.
Ned
9
Essa é uma ideia muito boa! Eu também fez um pouco mais inteligente, mudando para func localized(comment: String = "") -> Stringassim torna-se menor e com comentários opcionais :)
Gui Moura
2
Alguma idéia de como usar genstringsisso?
Chris
48
Todo mundo está muito empolgado com esta resposta, mas o grande problema (para qualquer projeto sério com vários idiomas) é que isso atrapalha completamente o gerenciamento de suas mensagens traduzidas, porque genstringssó funciona em cadeias literais passadas para o NSLocalizedString. Com essa solução inteligente, você perde a capacidade de atualizar seus arquivos .strings usando a genstringsferramenta e, pelo menos para mim, isso significa que não poderei usar essa abordagem simplificada.
Erik van der Neut 02/03
14
Encontrei esta ótima solução implementada em github.com/marmelroy/Localize-Swift . O problema de genstrings também é resolvido pelo script python personalizado incluído pelo autor. Eu não sou um autor.
Tomek Cejner
279

O NSLocalizedStringtambém existe no mundo do Swift.

func NSLocalizedString(
    key: String,
    tableName: String? = default,
    bundle: NSBundle = default,
    value: String = default,
    #comment: String) -> String

A tableName, bundlee valueos parâmetros são marcados com uma defaultpalavra-chave que significa que podemos omitir esses parâmetros ao chamar a função. Nesse caso, seus valores padrão serão usados.

Isso leva à conclusão de que a chamada do método pode ser simplificada para:

NSLocalizedString("key", comment: "comment")

Swift 5 - nenhuma mudança, ainda funciona assim.

RaffAl
fonte
44
a única diferença é que o comentário não pode ser nulo e o preenchimento automático está longe de ser intuitivo para a versão curta.
Marcin
1
isso não está mais funcionando, recebo um erro dizendo que argumentos insuficientes são usados.
Apps 4 U
2
Não que o descrito acima esteja correto no Xcode 6.3, Swift 1.2 com a alteração específica do objetivo-c, o comentário (como Marcin afirmou) não pode ser nulo, mas pode estar "" (vazio).
314 Neil
2
Um comentário nulo / vazio dificulta a realocação da sequência posteriormente no arquivo; se nada mais adicionar o nome da classe / arquivo onde é usado como comentário.
26415 Johan Johan
Essa é a resposta correta. Depois que a Apple atualizá-lo para o Swift, o Xcode poderá converter apenas essa API automaticamente em sua nova Swift API e nada mais será interrompido. Mesmo no menu Refractor do Xcode atualmente (v 11.4.1), existe uma Wrap in NSLocalizedStringopção que facilita muito as coisas, apenas destacando o texto, clicando com o botão direito do mouse e selecionando o item de menu.
Ethan Allen
28

Uma variação das respostas existentes:

Swift 5.1:

extension String {

    func localized(withComment comment: String? = nil) -> String {
        return NSLocalizedString(self, comment: comment ?? "")
    }

}

Você pode simplesmente usá-lo com ou sem comentário:

"Goodbye".localized()
"Hello".localized(withComment: "Simple greeting")

Observe que genstringsnão funcionará com esta solução.

Jose
fonte
14

Usando esta maneira, é possível criar uma implementação diferente para diferentes tipos (por exemplo, Int ou classes personalizadas como CurrencyUnit, ...). Também é possível procurar esse método usando o utilitário genstrings. Basta adicionar o sinalizador de rotina ao comando

genstrings MyCoolApp/Views/SomeView.swift -s localize -o .

extensão:

import UIKit

extension String {
    public static func localize(key: String, comment: String) -> String {
        return NSLocalizedString(key, comment: comment)
    }
}

uso:

String.localize("foo.bar", comment: "Foo Bar Comment :)")
Kay
fonte
Esta resposta é incrível e deve ser votada mais! Esta é a solução mais simples que encontrei até agora, se você estiver procurando evitar trazer mais uma biblioteca. Esta é uma boa solução nativa.
cgossain 8/01
6

Versão Swift 3:) ...

import Foundation

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}
Jan
fonte
6

Na verdade, você pode usar duas fases para traduzir seus textos em projetos Swift:

1) A primeira fase é usar a maneira antiga de criar todas as suas strings traduzíveis:

NSLocalisedString("Text to translate", comment: "Comment to comment")

1.1) Então você deve usar genstrings para gerar Localizable.strings:

$ genstrings *swift

2) Depois, você deve usar esta resposta .

2.1) Use a opção "Localizar e substituir" do XCode com base na expressão regular. Quanto ao exemplo dado (se você não tiver comentários), a expressão regular será:

NSLocalizedString\((.*)\, comment:\ \"\"\) 

e substitua-o por

$1.localized

ou (se você tiver comentários)

NSLocalizedString\((.*)\, comment:\ (.*)\)

e substitua-o por

$1.localizedWithComment(comment: $2)

Você é livre para jogar com regex e diferentes combinações de extensões, conforme desejar. A maneira geral é dividir todo o processo em duas fases. Espero que ajude.

GYFK
fonte
1
Desculpe, não entendi muitas respostas aqui. Qual é o benefício do método sobre o uso NSLocalizedString("Cancel", comment: "Cancel button title")?
LShi
1
@LShi algumas pessoas estavam reclamando, isso NSLocalizedStringparece menos rápido do que deveria parecer. String.localizedpor outro lado, parece mais Swifty, mas você não pode usar o gesntringsutilitário com ele, que é comumente usado para facilitar seu trabalho com a internacionalização. Meu argumento é que é muito fácil combinar as duas abordagens. Então, principalmente, é uma questão de legibilidade.
GYFK 07/02
O que acontece se você precisar fazer outra rodada genstrings? Você substituir volta tudo .localizedpor NSLocalizedString?
Cristik 04/01
5

Criou um pequeno método auxiliar para casos em que "comentário" é sempre ignorado. Menos código é mais fácil de ler:

public func NSLocalizedString(key: String) -> String {
    return NSLocalizedString(key, comment: "")
}

Coloque-o em qualquer lugar (fora de uma classe) e o Xcode encontrará esse método global.

JOM
fonte
12
Isso é uma prática ruim. Os comentários são recomendados e úteis, a menos que você esteja fazendo toda a tradução.
Jeremiah
Mesmo se você estiver se traduzindo, os comentários serão úteis, especialmente em um projeto grande.
shim
4

Provavelmente, o melhor caminho é esse aqui .

fileprivate func NSLocalizedString(_ key: String) -> String {
    return NSLocalizedString(key, comment: "")
}

e

import Foundation
extension String {
    static let Hello = NSLocalizedString("Hello")
    static let ThisApplicationIsCreated = NSLocalizedString("This application is created by the swifting.io team")
    static let OpsNoFeature = NSLocalizedString("Ops! It looks like this feature haven't been implemented yet :(!")
}

então você pode usá-lo assim

let message: String = .ThisApplicationIsCreated
print(message)

para mim este é o melhor porque

  • As strings codificadas estão em um arquivo específico; portanto, no dia em que você deseja alterá-las, é muito fácil
  • Mais fácil de usar do que digitar manualmente as strings no seu arquivo sempre
  • Genstrings ainda funcionará
  • você pode adicionar mais extensões, como uma por controlador de exibição para manter as coisas organizadas
Robin Dorpe
fonte
3
O que deve ser observado é que as strings definidas da maneira descrita são strings estáticas. O aplicativo deve ser reiniciado depois de alterar o idioma no aplicativo Configurações do iOS. Caso contrário, reinicie-o sozinho para ver as alterações. Ele também pode ter uma sobrecarga de memória, pois inicializamos todas as seqüências de uma vez, não no momento em que elas são necessárias.
IDevAmit
2
Eu acho que é melhor usar propriedades computadas aqui, como estestatic var Hello: String = { return NSLocalizedString("Hello") }
arte-de-sonha
Voto negativo
3

Quando você está desenvolvendo um SDK. Você precisa de alguma operação extra.

1) crie Localizable.strings como de costume no YourLocalizeDemoSDK.

2) crie as mesmas Localizable.strings em YourLocalizeDemo.

3) encontre o caminho do pacote do YourLocalizeDemoSDK.

Swift4 :

// if you use NSLocalizeString in NSObject, you can use it like this
let value = NSLocalizedString("key", tableName: nil, bundle: Bundle(for: type(of: self)), value: "", comment: "")

Bundle(for: type(of: self))ajuda a encontrar o pacote configurável no YourLocalizeDemoSDK. Se você usar Bundle.main, em vez disso, obterá um valor errado (na verdade, será a mesma string que a chave).

Mas se você quiser usar a extensão String mencionada pelo dr OX . Você precisa fazer um pouco mais. A extensão de origem se parece com isso.

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

Como sabemos, estamos desenvolvendo um SDK, Bundle.mainque receberá o pacote do pacote YourLocalizeDemo. Não é isso que queremos. Precisamos do pacote em YourLocalizeDemoSDK. Este é um truque para encontrá-lo rapidamente.

Execute o código abaixo em uma instância NSObject em YourLocalizeDemoSDK. E você receberá o URL do YourLocalizeDemoSDK.

let bundleURLOfSDK = Bundle(for: type(of: self)).bundleURL
let mainBundleURL = Bundle.main.bundleURL

Imprima os dois URLs, você descobrirá que podemos criar a base bundleURLofSDK em mainBundleURL. Nesse caso, será:

let bundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent("Frameworks").appendingPathComponent("YourLocalizeDemoSDK.framework")) ?? Bundle.main

E a extensão String será:

extension String {
    var localized: String {
        let bundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent("Frameworks").appendingPathComponent("YourLocalizeDemoSDK.framework")) ?? Bundle.main
        return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "")
    }
}

Espero que ajude.

Liam
fonte
2

Eu criei meu próprio tipo de ferramenta para gerar extrações de strings usando uma função de tradução personalizada

extension String {

    func localizedWith(comment:String) -> String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: comment)
    }

}

https://gist.github.com/Maxdw/e9e89af731ae6c6b8d85f5fa60ba848c

Ele analisará todos os seus arquivos rápidos e exportará as seqüências de caracteres e os comentários no seu código para um arquivo .strings.

Provavelmente não é a maneira mais fácil de fazer isso, mas é possível.

Máx.
fonte
1

Embora isso não responda ao problema de encurtamento, mas me ajudou a organizar as mensagens, criei uma estrutura para mensagens de erro como abaixo

struct Constants {
    // Error Messages
    struct ErrorMessages {
        static let unKnownError = NSLocalizedString("Unknown Error", comment: "Unknown Error Occured")
        static let downloadError = NSLocalizedString("Error in Download", comment: "Error in Download")
    }
}

let error = Constants.ErrorMessages.unKnownError

Dessa forma, você pode organizar as mensagens e fazer com que as gerações funcionem.

E este é o comando genstrings usado

find ./ -name \*.swift -print0 | xargs -0 genstrings -o .en.lproj
anoop4real
fonte
1

Útil para uso em testes de unidade:

Esta é uma versão simples que pode ser estendida a diferentes casos de uso (por exemplo, com o uso de tableNames).

public func NSLocalizedString(key: String, referenceClass: AnyClass, comment: String = "") -> String 
{
    let bundle = NSBundle(forClass: referenceClass)
    return NSLocalizedString(key, tableName:nil, bundle: bundle, comment: comment)
}

Use-o assim:

NSLocalizedString("YOUR-KEY", referenceClass: self)

Ou assim com um comentário:

NSLocalizedString("YOUR-KEY", referenceClass: self, comment: "usage description")
GatoCurioso
fonte
1
É uma prática ruim deixar de fora os comentários.
José
@ José Obrigado pelo seu comentário. O código foi concebido como uma ideia, não como modelo para copiar e colar. Mas eu adicionei a opção para adicionar comentários se você quiser;)
GatoCurioso
1

Esta é uma melhoria na abordagem ".localized". Comece adicionando a extensão de classe, pois isso ajudará em todas as strings que você estava configurando programaticamente:

extension String {
    func localized (bundle: Bundle = .main, tableName: String = "Localizable") -> String {
        return NSLocalizedString(self, tableName: tableName, value: "\(self)", comment: "")
    }
}

Exemplo de uso para seqüências de caracteres definidas programaticamente:

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

Agora, os arquivos de tradução de storyboard do Xcode tornam o gerenciador de arquivos confuso e também não lidam bem com atualizações no storyboard. Uma abordagem melhor é criar uma nova classe básica de etiquetas e atribuí-la a todos os seus rótulos de storyboard:

class BasicLabel: UILabel {
    //initWithFrame to init view from code
    override init(frame: CGRect) {
      super.init(frame: frame)
      setupView()
    }

    //initWithCode to init view from xib or storyboard
    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      setupView()
    }

    //common func to init our view
    private func setupView() {
        let storyboardText = self.text
        text = storyboardText?.localized()
    }
}

Agora, todos os marcadores que você adicionar e fornecer o padrão padrão no storyboard serão traduzidos automaticamente, desde que você tenha fornecido uma tradução para ele.

Você pode fazer o mesmo com o UIButton:

class BasicBtn: UIButton {
    //initWithFrame to init view from code
    override init(frame: CGRect) {
      super.init(frame: frame)
      setupView()
    }

    //initWithCode to init view from xib or storyboard
    required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      setupView()
    }

    //common func to init our view
    private func setupView() {
        let storyboardText = self.titleLabel?.text
        let lclTxt = storyboardText?.localized()
        setTitle(lclTxt, for: .normal)
    }
}
Dave G
fonte
0

Quando você traduz, digamos, do inglês, onde uma frase é a mesma, para outro idioma em que é diferente (por causa do sexo, conjugações verbais ou declinação), a forma mais simples do NSString no Swift que funciona em todos os casos é os três argumentos. . Por exemplo, a frase em inglês "previous was" é traduzida de forma diferente para o russo no caso de "weight" ("предыдущ ий был") e para "cintura" ("предыдущ ая был а ").

Nesse caso, você precisa de duas traduções diferentes para uma fonte (em termos da ferramenta XLIFF recomendada na WWDC 2018). Você não pode consegui-lo com dois argumentos NSLocalizedString, onde "anterior era" será o mesmo para a "chave" e a tradução em inglês (ou seja, para o valor). A única maneira é usar a forma de três argumentos

NSLocalizedString("previousWasFeminine", value: "previous was", comment: "previousWasFeminine")

NSLocalizedString("previousWasMasculine", value: "previous was", comment: "previousWasMasculine")

onde chaves ("previousWasFeminine" e "previousWasMasculine") são diferentes.

Eu sei que o conselho geral é traduzir a frase como um todo, no entanto, às vezes é muito demorado e inconveniente.

Vadim Motorine
fonte
-1

Localização com idioma padrão:

extension String {
func localized() -> String {
       let defaultLanguage = "en"
       let path = Bundle.main.path(forResource: defaultLanguage, ofType: "lproj")
       let bundle = Bundle(path: path!)

       return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
    }
}
investigador
fonte