Qual è l'equivalente di NSLocalizedString in Swift?


228

Esiste un equivalente di Swift di NSLocalizedString(...)? In Objective-C, di solito usiamo:

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

Come posso ottenere lo stesso in Swift? Ho trovato una funzione:

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

Tuttavia, è molto lungo e per niente conveniente.


2
La cosa migliore è creare una versione più breve dello snippet di codice: NSLocalizedString ("", comment: "") ... Mi è piaciuta la soluzione di estensione, ma il problema è che genstrings non acquisirà queste stringhe nel file di traduzione.
Matej Ukmar,

3
In Swift 3 puoi semplicemente utilizzare NSLocalizedString("Cancel", comment: "Cancel button title")sfruttando i valori predefiniti. È conveniente penso.
LShi

Questo è un ottimo articolo sulla localizzazione (estensione della stringa, diverse tabelle di stringhe e persino pluralizzazione): medium.com/@marcosantadev/…
LightMan

Questo è un ottimo articolo sulla localizzazione in Swift per un'architettura robusta medium.com/@mendibarouk/…
Mendy

Risposte:


373

Uso la prossima soluzione:

1) crea estensione:

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

2) nel file Localizable.strings :

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

3) esempio di utilizzo:

myLabel.text = "Hi".localized

godere! ;)

--upd: -

per caso con commenti puoi usare questa soluzione:

1) Estensione:

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

2) nel file .strings:

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

3) utilizzando:

myLabel.text = "Hi".localized(withComment: "with !!!")

92
L'unico problema è che non sarai in grado di utilizzare l' genstringsutilità per generare i tuoi file .strings.
Ned,

9
È un'ottima idea! L'ho anche reso un po 'più intelligente cambiando in func localized(comment: String = "") -> Stringmodo che diventi più piccolo e con commenti opzionali :)
Gui Moura

2
Qualche idea su come usare genstringscon questo?
Chris,

48
Tutti sono molto entusiasti di questa risposta, ma il GRANDE problema (per qualsiasi progetto serio con diverse lingue) è che questo confonde completamente la gestione dei messaggi tradotti, perché genstringsfunziona solo su stringhe letterali passate a NSLocalizedString. Con questa soluzione intelligente, perdi la possibilità di aggiornare i tuoi file .strings utilizzando lo genstringsstrumento e, almeno per me, ciò significa che non sarò in grado di utilizzare questo approccio semplificato.
Erik van der Neut

14
Ho trovato questa ottima soluzione implementata in github.com/marmelroy/Localize-Swift . Il problema delle genstrings è risolto anche dallo script python personalizzato incluso dall'autore. Non sono un autore.
Tomek Cejner,

279

Il NSLocalizedStringesiste anche nel mondo della Swift.

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

I tableName, bundlee valueparametri sono contrassegnati con undefault parola chiave che significa che possiamo omettere questi parametri durante la chiamata della funzione. In questo caso, verranno utilizzati i loro valori predefiniti.

Ciò porta alla conclusione che la chiamata al metodo può essere semplificata per:

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

Swift 5 : nessuna modifica, funziona ancora così.


44
è l'unica differenza che il commento non può essere nullo e il completamento automatico è tutt'altro che intuitivo per la versione breve.
Marcin,

1
questo non funziona più, viene visualizzato un errore che indica che non vengono utilizzati argomenti sufficienti.
App 4 U

2
Non che quanto sopra sia corretto in Xcode 6.3, Swift 1.2 con la modifica specifica da obiettivo-c, il commento (come affermato da Marcin) non può essere nullo, ma può essere "" (vuoto).
Neil,

2
Un commento nullo / vuoto rende difficile riposizionare la stringa in un secondo momento nel file di stringa; se non altro aggiungere il nome della classe / file dove viene utilizzato come commento.
Johan,

Questa è la risposta corretta Una volta che Apple lo aggiorna per Swift, Xcode sarà in grado di convertire automaticamente questa API nella sua nuova API Swift e nient'altro si romperà. Anche nel menu Rifrattore di Xcode attualmente (v 11.4.1) c'è Wrap in NSLocalizedStringun'opzione che rende le cose davvero facili semplicemente evidenziando il testo, facendo clic con il tasto destro e selezionando la voce di menu.
Ethan Allen,

28

Una variazione delle risposte esistenti:

Swift 5.1:

extension String {

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

}

Puoi quindi semplicemente usarlo con o senza commento:

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

Si noti che genstringsnon funzionerà con questa soluzione.


14

Usando in questo modo è possibile creare un'implementazione diversa per tipi diversi (ad esempio classi Int o personalizzate come CurrencyUnit, ...). È anche possibile cercare questo metodo invocare usando l'utilità genstrings. Aggiungi semplicemente il flag di routine al comando

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

estensione:

import UIKit

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

utilizzo:

String.localize("foo.bar", comment: "Foo Bar Comment :)")

Questa risposta è sorprendente e dovrebbe essere votata di più! Questa è la soluzione più semplice che ho trovato finora se stai cercando di evitare di portare ancora un'altra libreria. Questa è una buona soluzione nativa.
cgossain,

6

Versione Swift 3:) ...

import Foundation

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

6

In realtà, puoi utilizzare due fasi per tradurre i tuoi testi nei progetti Swift:

1) La prima fase sta usando il vecchio modo per creare tutte le tue stringhe traducibili:

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

1.1) Quindi dovresti usare genstrings per generare Localizable.strings:

$ genstrings *swift

2) Successivamente, dovresti usare questa risposta .

2.1) Utilizzare l'opzione "Trova e sostituisci" XCode basata sull'espressione regolare. Per quanto riguarda l'esempio fornito (se non hai commenti) l'espressione regolare sarà:

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

e sostituirlo con

$1.localized

o (se hai commenti)

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

e sostituirlo con

$1.localizedWithComment(comment: $2)

Sei libero di giocare con regex e diverse combinazioni di estensioni come desideri. Il modo generale è di dividere l'intero processo in due fasi. Spero che aiuti.


1
Mi dispiace non ottenere il punto di molte risposte qui. Qual è il vantaggio del metodo rispetto all'utilizzo NSLocalizedString("Cancel", comment: "Cancel button title")?
LShi

1
@Lhi alcuni si lamentavano, NSLocalizedStringsembra meno Swiftier, di quanto dovrebbe sembrare. String.localizedd'altra parte sembra più Swifty ma non si è in grado di utilizzare l' gesntringsutilità con essa, che viene comunemente utilizzata per facilitare il lavoro con l'internazionalizzazione. Il mio punto è che è abbastanza facile mescolare entrambi gli approcci. Quindi principalmente è una questione di leggibilità.
GYFK

Cosa succede se devi fare un altro giro di genstrings? Sostituisci tutto .localizeddi nuovo NSLocalizedString?
Cristik,

5

Creato un piccolo metodo di supporto per i casi in cui il "commento" viene sempre ignorato. Meno codice è più facile da leggere:

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

Mettilo ovunque (fuori da una classe) e Xcode troverà questo metodo globale.


12
Questa è una cattiva pratica. I commenti sono consigliati e utili a meno che tu non stia eseguendo tutta la traduzione da solo.
Geremia,

Anche se stai traducendo te stesso, i commenti sarebbero utili, specialmente in un grande progetto.
shim

4

Probabilmente il modo migliore è questo qui .

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 :(!")
}

puoi quindi usarlo in questo modo

let message: String = .ThisApplicationIsCreated
print(message)

per me questo è il migliore perché

  • Le stringhe hardcoded sono in un file specifico, quindi il giorno in cui vuoi cambiarlo è davvero facile
  • Più facile da usare rispetto alla digitazione manuale delle stringhe nel file ogni volta
  • genstrings continuerà a funzionare
  • puoi aggiungere più estensioni, come una per controller di visualizzazione per mantenere tutto in ordine

3
La cosa da notare è che le stringhe definite nel modo descritto sono stringhe statiche. L'app dovrebbe essere riavviata dopo aver cambiato la lingua nell'app Impostazioni iOS. In caso contrario, riavvialo da solo per vedere i cambiamenti. Può anche avere un sovraccarico di memoria, poiché inizializziamo tutte le stringhe contemporaneamente, non nel momento in cui sono necessarie.
iDevAmit

2
Penso che sia meglio usare le proprietà calcolate qui, in questo modostatic var Hello: String = { return NSLocalizedString("Hello") }
art-of-dreams il

Sottovalutato perché non segue le linee guida per la denominazione di
Cristik

3

Quando si sviluppa un SDK. Hai bisogno di qualche operazione extra.

1) creare Localizable.strings come al solito in YourLocalizeDemoSDK.

2) creare gli stessi Localizable.strings in YourLocalizeDemo.

3) trova il tuo percorso bundle di 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))ti aiuta a trovare il pacchetto in YourLocalizeDemoSDK. Se usiBundle.main invece, si otterrà un valore errato (in realtà sarà la stessa stringa con la chiave).

Ma se vuoi usare l'estensione String menzionata dal dr OX . Devi fare ancora un po '. L'estensione dell'origine è simile a questa.

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

Come sappiamo, stiamo sviluppando un SDK, Bundle.main otterremo il pacchetto del pacchetto YourLocalizeDemo. Non è quello che vogliamo. Abbiamo bisogno del pacchetto in YourLocalizeDemoSDK. Questo è un trucco per trovarlo rapidamente.

Esegui il codice seguente in un'istanza NSObject in YourLocalizeDemoSDK. E otterrai l'URL di YourLocalizeDemoSDK.

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

Stampa entrambi i due URL, scoprirai che possiamo costruire bundleURLofSDK base su mainBundleURL. In questo caso, sarà:

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

E l'estensione String sarà:

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: "")
    }
}

Spero che sia d'aiuto.


2

Ho creato il mio tipo di strumento genstrings per estrarre le stringhe usando una funzione di traduzione personalizzata

extension String {

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

}

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

Analizzerà tutti i tuoi file rapidi ed esporterà le stringhe e i commenti nel tuo codice in un file .strings.

Probabilmente non è il modo più semplice per farlo, ma è possibile.


1

Anche se questo non risponde al problema della riduzione, ma questo mi ha aiutato a organizzare i messaggi, ho creato una struttura per i messaggi di errore come di seguito

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

In questo modo puoi organizzare i messaggi e far funzionare le corde gens.

E questo è il comando genstrings usato

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

1

Utile per l'utilizzo nei test unitari:

Questa è una versione semplice che può essere estesa a diversi casi d'uso (ad es. Con l'uso di 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)
}

Usalo in questo modo:

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

O come questo con un commento:

NSLocalizedString("YOUR-KEY", referenceClass: self, comment: "usage description")

1
È una cattiva pratica lasciare fuori i commenti.
José,

@ José Grazie per il tuo commento. Il codice era inteso come un'idea, non come modello per copia e incolla. Ma ho aggiunto l'opzione per aggiungere commenti se vuoi;)
GatoCurioso

1

Questo è un miglioramento dell'approccio ".localized". Inizia con l'aggiunta dell'estensione di classe in quanto ciò ti aiuterà con tutte le stringhe che stavi impostando programmaticamente:

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

Esempio di utilizzo per le stringhe impostate a livello di codice:

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

Ora i file di traduzione dello storyboard di Xcode rendono disordinato il file manager e non gestiscono bene gli aggiornamenti dello storyboard. Un approccio migliore consiste nel creare una nuova classe di etichette di base e assegnarla a tutte le etichette dello 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()
    }
}

Ora ogni etichetta che aggiungi e fornisci per impostazione predefinita lo storyboard verrà automaticamente tradotta, supponendo che tu abbia fornito una traduzione per essa.

Puoi fare lo stesso per 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)
    }
}

0

Quando si traduce, diciamo dall'inglese, dove una frase è la stessa, in un'altra lingua in cui è diversa (a causa del genere, coniugazioni verbali o declinazione) la forma NSString più semplice in Swift che funziona in tutti i casi è i tre argomenti uno . Ad esempio, la frase inglese "precedente era", è tradotta in modo diverso in russo per il caso di "peso" (" предыдущ ий был") e per "girovita" (" предыдущ ая был а ").

In questo caso sono necessarie due diverse traduzioni per una fonte (in termini di strumento XLIFF raccomandato nel WWDC 2018). Non puoi ottenerlo con due argomenti NSLocalizedString, in cui "precedente era" sarà lo stesso sia per la "chiave" che per la traduzione inglese (ovvero per il valore). L'unico modo è utilizzare il modulo a tre argomenti

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

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

dove le chiavi ("previousWasFeminine" e "previousWasMasculine") sono diverse.

So che il consiglio generale è quello di tradurre la frase nel suo insieme, tuttavia a volte troppo tempo e scomodo.


-1

Localizzazione con lingua predefinita:

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: "")
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.