Qu'est-ce que NSLocalizedString équivalent dans Swift?

228

Existe-t-il un équivalent Swift de NSLocalizedString(...)? Dans Objective-C, nous utilisons généralement:

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

Comment puis-je réaliser la même chose dans Swift? J'ai trouvé une fonction:

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

Cependant, c'est très long et pas du tout pratique.

RaffAl
la source
2
Le mieux est de créer une version plus courte de l'extrait de code: NSLocalizedString ("", commentaire: "") ... J'ai aimé la solution d'extension, mais le problème est que genstrings ne capturera pas ces chaînes dans le fichier de traduction.
Matej Ukmar
3
Dans Swift 3, vous pouvez simplement utiliser NSLocalizedString("Cancel", comment: "Cancel button title")en profitant des valeurs par défaut. C'est pratique je pense.
LShi
Ceci est un très bon article sur la localisation (extension de chaîne, différentes tables de chaînes et même pluralisation): medium.com/@marcosantadev/…
LightMan
Ceci est un très bon article sur la localisation dans Swift pour une architecture robuste medium.com/@mendibarouk/…
Mendy

Réponses:

373

J'utilise la solution suivante:

1) créer une extension:

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

2) dans le fichier Localizable.strings :

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

3) exemple d'utilisation:

myLabel.text = "Hi".localized

prendre plaisir! ;)

--upd: -

pour le cas avec des commentaires, vous pouvez utiliser cette solution:

1) Extension:

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

2) dans le fichier .strings:

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

3) en utilisant:

myLabel.text = "Hi".localized(withComment: "with !!!")
dr OX
la source
92
Le seul problème avec cela est que vous ne pourrez pas utiliser l' genstringsutilitaire pour générer vos fichiers .strings.
Ned
9
Voilà une très bonne idée! Je l'ai également rendu un peu plus intelligent en changeant pour func localized(comment: String = "") -> Stringqu'il devienne plus petit et avec des commentaires facultatifs :)
Gui Moura
2
Une idée de comment l'utiliser genstringsavec ça?
Chris
48
Tout le monde est très excité par cette réponse, mais le GRAND problème (pour tout projet sérieux avec plusieurs langues) est que cela gâche complètement votre gestion de vos messages traduits, car genstringsne fonctionne que sur les chaînes littérales transmises à NSLocalizedString. Avec cette solution de contournement intelligente, vous perdez la possibilité de mettre à jour vos fichiers .strings à l'aide de l' genstringsoutil, et au moins pour moi, cela signifie que je ne pourrai pas utiliser cette approche simplifiée.
Erik van der Neut
14
J'ai trouvé cette excellente solution implémentée dans github.com/marmelroy/Localize-Swift . Le problème des chaînes de caractères est également résolu par un script python personnalisé inclus par l'auteur. Je ne suis pas auteur.
Tomek Cejner
279

Le NSLocalizedStringexiste aussi dans le monde du Swift.

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

La tableName, bundleet les valueparamètres sont marqués avec un defaultmot - clé qui signifie que nous pouvons omettre ces paramètres tout en appelant la fonction. Dans ce cas, leurs valeurs par défaut seront utilisées.

Cela conduit à la conclusion que l'appel de méthode peut être simplifié pour:

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

Swift 5 - pas de changement, fonctionne toujours comme ça.

RaffAl
la source
44
c'est seulement la différence que le commentaire ne peut pas être nul, et la saisie automatique est loin d'être intuitive pour la version courte.
Marcin
1
cela ne fonctionne plus, j'obtiens une erreur en disant que pas assez d'arguments sont utilisés.
Apps 4 U
2
Non pas que ce qui précède soit correct dans Xcode 6.3, Swift 1.2 avec le changement spécifique de objective-c, le commentaire (comme Marcin l'a déclaré) ne peut pas être nul, mais il peut être "" (vide).
Neil
2
Un commentaire nul / vide rend difficile le déplacement de la chaîne plus tard dans le fichier de chaîne; si rien d'autre, ajoutez le nom de classe / fichier où il est utilisé comme commentaire.
Johan
Ceci est la bonne réponse. Une fois qu'Apple le mettra à jour pour Swift, Xcode pourra simplement convertir automatiquement cette API en sa nouvelle API Swift et rien d'autre ne se cassera. Même dans le menu Réfracteur de Xcode actuellement (v 11.4.1), il existe une Wrap in NSLocalizedStringoption qui rend les choses vraiment faciles en surlignant simplement le texte, en cliquant avec le bouton droit et en sélectionnant l'élément de menu.
Ethan Allen
28

Une variation des réponses existantes:

Swift 5.1:

extension String {

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

}

Vous pouvez ensuite simplement l'utiliser avec ou sans commentaire:

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

Veuillez noter que genstringscela ne fonctionnera pas avec cette solution.

José
la source
14

En utilisant de cette façon, il est possible de créer une implémentation différente pour différents types (par exemple, des classes Int ou personnalisées comme CurrencyUnit, ...). Il est également possible de rechercher cette méthode à l'aide de l'utilitaire genstrings. Ajoutez simplement l'indicateur de routine à la commande

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

extension:

import UIKit

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

usage:

String.localize("foo.bar", comment: "Foo Bar Comment :)")
Kay
la source
Cette réponse est étonnante et devrait être plus appréciée! Il s'agit de la solution la plus simple que j'ai trouvée jusqu'à présent si vous cherchez à éviter d'ajouter une autre bibliothèque. Il s'agit d'une bonne solution native.
cgossain
6

Version Swift 3:) ...

import Foundation

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

En fait, vous pouvez utiliser deux phases pour traduire vos textes dans des projets Swift:

1) La première phase utilise l'ancienne méthode pour créer toutes vos chaînes traduisibles:

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

1.1) Ensuite, vous devez utiliser genstrings pour générer Localizable.strings:

$ genstrings *swift

2) Ensuite, vous devez utiliser cette réponse .

2.1) Utilisez votre option XCode "Rechercher et remplacer" basée sur l'expression régulière. Comme pour l'exemple donné (si vous n'avez pas de commentaires), l'expression régulière sera:

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

et remplacez-le par

$1.localized

ou (si vous avez des commentaires)

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

et remplacez-le par

$1.localizedWithComment(comment: $2)

Vous êtes libre de jouer avec regex et différentes combinaisons d'extensions comme vous le souhaitez. La manière générale consiste à diviser l'ensemble du processus en deux phases. J'espère que cela pourra aider.

GYFK
la source
1
Désolé, je ne comprends pas le point de nombreuses réponses ici. Quel est l'avantage de la méthode sur l'utilisation NSLocalizedString("Cancel", comment: "Cancel button title")?
LShi
1
@LShi se plaignaient de certaines personnes, cela NSLocalizedStringsemble moins rapide que cela ne devrait l'être. String.localizedd'autre part, semble plus Swifty mais vous ne pouvez pas utiliser l' gesntringsutilitaire qui est couramment utilisé pour faciliter votre travail avec l'internationalisation. Mon point est qu'il est assez facile de mélanger les deux approches. C'est donc principalement une question de lisibilité.
GYFK
Que se passe-t-il si vous devez refaire un tour genstrings? Remplacez-vous tous .localizedpar NSLocalizedString?
Cristik
5

Création d'une petite méthode d'aide pour les cas, où "commentaire" est toujours ignoré. Moins de code est plus facile à lire:

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

Mettez-le n'importe où (en dehors d'une classe) et Xcode trouvera cette méthode globale.

JOM
la source
12
C'est une mauvaise pratique. Les commentaires sont recommandés et utiles, sauf si vous effectuez vous-même toute la traduction.
Jeremiah
Même si vous vous traduisez vous-même, les commentaires seraient utiles, surtout dans un grand projet.
shim
4

La meilleure façon est probablement celle-ci ici .

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

et

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

vous pouvez ensuite l'utiliser comme ça

let message: String = .ThisApplicationIsCreated
print(message)

pour moi c'est le meilleur parce que

  • Les chaînes codées en dur sont dans un fichier spécifique, donc le jour où vous voulez le changer, c'est vraiment facile
  • Plus facile à utiliser que de taper manuellement les chaînes dans votre fichier à chaque fois
  • genstrings fonctionnera toujours
  • vous pouvez ajouter plus d'extensions, comme une par contrôleur de vue pour garder les choses en ordre
Robin Dorpe
la source
3
La chose à noter est que les chaînes définies de la manière décrite sont des chaînes statiques. L'application doit être relancée après avoir changé de langue dans l'application Paramètres iOS. Sinon, relancez-le vous-même afin de voir les changements. Il peut également y avoir une surcharge de mémoire, car nous initialisons toutes les chaînes à la fois, pas au moment où elles sont nécessaires.
iDevAmit
2
Je pense qu'il vaut mieux utiliser des propriétés calculées ici, comme cecistatic var Hello: String = { return NSLocalizedString("Hello") }
art-of-dreams
Voté car il ne respecte pas les directives de nommage de
Cristik
3

Lorsque vous développez un SDK. Vous avez besoin d'une opération supplémentaire.

1) créer des chaînes localisables comme d'habitude dans YourLocalizeDemoSDK.

2) créer les mêmes chaînes Localizable.strings chaînes YourLocalizeDemo.

3) Trouvez votre chemin de bundle de bundle de 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))vous aide à trouver l'ensemble dans YourLocalizeDemoSDK. Si tu utilisesBundle.main place, vous obtiendrez une valeur incorrecte (en fait, ce sera la même chaîne avec la clé).

Mais si vous souhaitez utiliser l'extension String mentionnée par dr OX . Vous devez en faire plus. L'extension d'origine ressemble à ceci.

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

Comme nous le savons, nous développons un SDK, Bundle.main obtiendra le bundle du bundle de YourLocalizeDemo. Ce n'est pas ce que nous voulons. Nous avons besoin du bundle dans YourLocalizeDemoSDK. C'est une astuce pour le trouver rapidement.

Exécutez le code ci-dessous dans une instance NSObject dans YourLocalizeDemoSDK. Et vous obtiendrez l'URL de YourLocalizeDemoSDK.

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

Imprimez les deux URL, vous constaterez que nous pouvons construire la base bundleURLofSDK sur mainBundleURL. Dans ce cas, ce sera:

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

Et l'extension String sera:

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

J'espère que ça aide.

Liam
la source
2

J'ai créé ma propre sorte d'outil genstrings pour extraire les chaînes en utilisant une fonction de traduction personnalisée

extension String {

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

}

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

Il analysera tous vos fichiers rapides et exportera les chaînes et les commentaires de votre code vers un fichier .strings.

Probablement pas la façon la plus simple de le faire, mais c'est possible.

Max
la source
1

Bien que cela ne réponde pas au problème de raccourcissement, mais cela m'a aidé à organiser les messages, j'ai créé une structure pour les messages d'erreur comme ci-dessous

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

De cette façon, vous pouvez organiser les messages et faire fonctionner les chaînes de caractères.

Et ceci est la commande genstrings utilisée

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

Utile pour une utilisation dans les tests unitaires:

Il s'agit d'une version simple qui peut être étendue à différents cas d'utilisation (par exemple avec l'utilisation 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)
}

Utilisez-le comme ceci:

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

Ou comme ça avec un commentaire:

NSLocalizedString("YOUR-KEY", referenceClass: self, comment: "usage description")
GatoCurioso
la source
1
C'est une mauvaise pratique de laisser de côté les commentaires.
José
@ José Merci pour votre commentaire. Le code était conçu comme une idée, pas comme un modèle à copier-coller. Mais j'ai ajouté la possibilité d'ajouter des commentaires si vous voulez;)
GatoCurioso
1

Il s'agit d'une amélioration de l'approche ".localized". Commencez par ajouter l'extension de classe, car cela vous aidera avec toutes les chaînes que vous définissez par programme:

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

Exemple d'utilisation des chaînes que vous définissez par programme:

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

Les fichiers de traduction du storyboard de Xcode rendent le gestionnaire de fichiers désordonné et ne gèrent pas non plus correctement les mises à jour du storyboard. Une meilleure approche consiste à créer une nouvelle classe d'étiquettes de base et à l'attribuer à toutes vos étiquettes 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()
    }
}

Désormais, chaque étiquette que vous ajoutez et fournissez par défaut dans le storyboard sera automatiquement traduite, en supposant que vous en ayez fourni une traduction.

Vous pouvez faire de même pour 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
la source
0

Lorsque vous traduisez, par exemple de l'anglais, où une phrase est la même, vers une autre langue où elle est différente (en raison du sexe, de la conjugaison ou de la déclinaison), la forme NSString la plus simple dans Swift qui fonctionne dans tous les cas est les trois arguments. . Par exemple, l'expression anglaise "précédente était" est traduite différemment en russe pour le cas de "poids" ("предыдущ ий был") et pour "taille" ("предыдущ ая был а ").

Dans ce cas, vous avez besoin de deux traductions différentes pour une seule source (en termes d'outil XLIFF recommandé dans la WWDC 2018). Vous ne pouvez pas y parvenir avec deux arguments NSLocalizedString, où "précédent était" sera le même pour la "clé" et la traduction anglaise (c'est-à-dire pour la valeur). La seule façon est d'utiliser la forme à trois arguments

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

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

où les clés ("previousWasFeminine" et "previousWasMasculine") sont différentes.

Je sais que le conseil général est de traduire la phrase dans son ensemble, cependant, parfois trop longue et peu pratique.

Vadim Motorine
la source
-1

Localisation avec langue par défaut:

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