Was entspricht NSLocalizedString in Swift?

228

Gibt es ein Swift-Äquivalent von NSLocalizedString(...)? In verwenden Objective-Cwir normalerweise:

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

Wie kann ich dasselbe in Swift erreichen? Ich habe eine Funktion gefunden:

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

Es ist jedoch sehr lang und überhaupt nicht bequem.

RaffAl
quelle
2
Am besten erstellen Sie eine kürzere Version des Code-Snippets: NSLocalizedString ("", Kommentar: "") ... Ich mochte die Erweiterungslösung, aber das Problem ist, dass Genstrings diese Zeichenfolgen nicht in Übersetzungsdateien erfassen.
Matej Ukmar
3
In Swift 3 können Sie einfach NSLocalizedString("Cancel", comment: "Cancel button title")die Standardwerte nutzen. Es ist bequem, denke ich.
LShi
Dies ist ein sehr guter Artikel über Lokalisierung (String-Erweiterung, verschiedene Strings-Tabellen und sogar Pluralisierung): medium.com/@marcosantadev/…
LightMan
Dies ist ein sehr guter Artikel über die Lokalisierung in Swift für eine robuste Architektur medium.com/@mendibarouk/...
Mendy

Antworten:

372

Ich benutze die nächste Lösung:

1) Erweiterung erstellen:

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

2) in der Datei Localizable.strings :

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

3) Anwendungsbeispiel:

myLabel.text = "Hi".localized

genießen! ;)

--upd: -

Für den Fall mit Kommentaren können Sie diese Lösung verwenden:

1) Verlängerung:

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

2) in der .strings-Datei:

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

3) mit:

myLabel.text = "Hi".localized(withComment: "with !!!")
Dr. OX
quelle
92
Das einzige Problem dabei ist, dass Sie das genstringsDienstprogramm nicht zum Generieren Ihrer .strings-Dateien verwenden können.
Ned
9
Das ist eine sehr gute Idee! Ich habe es auch ein bisschen schlauer gemacht, indem ich zu gewechselt habe, func localized(comment: String = "") -> Stringdamit es kleiner und mit optionalen Kommentaren wird :)
Gui Moura
2
Irgendeine Idee, wie man das benutzt genstrings?
Chris
48
Alle sind sehr aufgeregt über diese Antwort, aber das GROSSE Problem (für jedes ernsthafte Projekt mit mehreren Sprachen) ist, dass dies Ihre Verwaltung Ihrer übersetzten Nachrichten völlig genstringsdurcheinander bringt , da nur wörtliche Zeichenfolgen funktionieren, die an NSLocalizedString übergeben werden. Mit dieser cleveren Problemumgehung verlieren Sie die Möglichkeit, Ihre .strings-Dateien mit dem genstringsTool zu aktualisieren , und zumindest für mich bedeutet dies, dass ich diesen vereinfachten Ansatz nicht verwenden kann.
Erik van der Neut
14
Ich fand diese großartige Lösung in github.com/marmelroy/Localize-Swift implementiert . Das Problem der Genstrings wird auch durch ein benutzerdefiniertes Python-Skript gelöst, das vom Autor bereitgestellt wird. Ich bin kein Autor.
Tomek Cejner
279

Das NSLocalizedStringexistiert auch in der Swift-Welt.

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

Die tableName, bundleund valueParameter sind mit einem gekennzeichnetdefault Schlüsselwort , das bedeutet , dass wir diese Parameter weglassen kann , während die Funktion aufrufen. In diesem Fall werden ihre Standardwerte verwendet.

Dies führt zu der Schlussfolgerung, dass der Methodenaufruf vereinfacht werden kann, um:

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

Swift 5 - keine Änderung, funktioniert immer noch so.

RaffAl
quelle
44
Es ist nur ein Unterschied, dass ein Kommentar nicht gleich Null sein kann und die automatische Vervollständigung für die Kurzversion alles andere als intuitiv ist.
Marcin
1
Dies funktioniert nicht mehr. Ich erhalte die Fehlermeldung, dass nicht genügend Argumente verwendet werden.
Apps 4 U
2
Nicht dass das Obige in Xcode 6.3, Swift 1.2 mit der spezifischen Änderung von Ziel-c korrekt ist, der Kommentar (wie Marcin sagte) kann nicht Null sein, aber er kann "" (leer) sein.
Neil
2
Ein null / leerer Kommentar macht es schwierig, die Zeichenfolge später in der Zeichenfolgendatei zu verschieben. Wenn nichts anderes hinzugefügt wird, fügen Sie den Klassen- / Dateinamen hinzu, in dem er als Kommentar verwendet wird.
Johan
Dies ist die richtige Antwort. Sobald Apple es für Swift aktualisiert hat, kann Xcode diese API einfach automatisch in seine neue Swift-API konvertieren, und nichts anderes wird kaputt gehen. Sogar im Refraktor-Menü von Xcode (Version 11.4.1) gibt es eine Wrap in NSLocalizedStringOption, die die Sache wirklich einfach macht, indem Sie einfach Text markieren, mit der rechten Maustaste klicken und den Menüpunkt auswählen.
Ethan Allen
28

Eine Variation der vorhandenen Antworten:

Swift 5.1:

extension String {

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

}

Sie können es dann einfach mit oder ohne Kommentar verwenden:

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

Bitte beachten Sie, dass genstringsdies mit dieser Lösung nicht funktioniert.

José
quelle
14

Auf diese Weise ist es möglich, eine unterschiedliche Implementierung für verschiedene Typen zu erstellen (z. B. Int- oder benutzerdefinierte Klassen wie CurrencyUnit, ...). Es ist auch möglich, mit dem Dienstprogramm genstrings nach dieser Methode zu suchen. Fügen Sie dem Befehl einfach das Routine-Flag hinzu

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

Erweiterung:

import UIKit

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

Verwendung:

String.localize("foo.bar", comment: "Foo Bar Comment :)")
Kay
quelle
Diese Antwort ist erstaunlich und sollte mehr positiv bewertet werden! Dies ist die einfachste Lösung, die ich bisher gefunden habe, wenn Sie vermeiden möchten, eine weitere Bibliothek einzubringen. Dies ist eine gute native Lösung.
cgossain
6

Swift 3 Version :) ...

import Foundation

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

Tatsächlich können Sie Ihre Texte in Swift-Projekten in zwei Phasen übersetzen:

1) In der ersten Phase werden alle übersetzbaren Zeichenfolgen auf die alte Weise erstellt:

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

1.1) Dann sollten Sie genstrings verwenden, um Localizable.strings zu generieren:

$ genstrings *swift

2) Danach sollten Sie diese Antwort verwenden .

2.1) Verwenden Sie Ihre XCode-Option "Suchen und Ersetzen" basierend auf dem regulären Ausdruck. Für das angegebene Beispiel (wenn Sie keine Kommentare haben) lautet der reguläre Ausdruck:

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

und ersetzen Sie es durch

$1.localized

oder (wenn Sie Kommentare haben)

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

und ersetzen Sie es durch

$1.localizedWithComment(comment: $2)

Sie können nach Belieben mit Regex und verschiedenen Erweiterungskombinationen spielen. Der allgemeine Weg besteht darin, den gesamten Prozess in zwei Phasen aufzuteilen. Hoffentlich hilft das.

GYFK
quelle
1
Entschuldigung, ich verstehe hier nicht viele Antworten. Was ist der Vorteil der Methode gegenüber der Verwendung NSLocalizedString("Cancel", comment: "Cancel button title")?
LShi
1
@LShi einige Leute haben sich beschwert, das NSLocalizedStringsieht weniger schnell aus, als es aussehen sollte. String.localizedAuf der anderen Seite sieht es schneller aus, aber Sie können kein gesntringsDienstprogramm verwenden, das üblicherweise verwendet wird, um Ihre Arbeit mit der Internationalisierung zu erleichtern. Mein Punkt ist, dass es ziemlich einfach ist, beide Ansätze zu mischen. Es geht also hauptsächlich um die Lesbarkeit.
GYFK
Was passiert, wenn Sie eine weitere Runde machen müssen genstrings? Ersetzen Sie alles wieder .localizeddurch NSLocalizedString?
Cristik
5

Erstellt eine kleine Hilfsmethode für Fälle, in denen "Kommentar" immer ignoriert wird. Weniger Code ist leichter zu lesen:

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

Platzieren Sie es einfach irgendwo (außerhalb einer Klasse) und Xcode findet diese globale Methode.

JOM
quelle
12
Das ist schlechte Praxis. Kommentare werden empfohlen und sind hilfreich, es sei denn, Sie führen die gesamte Übersetzung selbst durch.
Jeremiah
Selbst wenn Sie selbst übersetzen, wären die Kommentare hilfreich, insbesondere in einem großen Projekt.
Shim
4

Der wahrscheinlich beste Weg ist dieser hier .

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

und

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

Sie können es dann so verwenden

let message: String = .ThisApplicationIsCreated
print(message)

Für mich ist das das Beste, weil

  • Die fest codierten Zeichenfolgen befinden sich in einer bestimmten Datei. An dem Tag, an dem Sie sie ändern möchten, ist dies also ganz einfach
  • Einfacher zu verwenden, als die Zeichenfolgen in Ihrer Datei jedes Mal manuell einzugeben
  • Genstrings werden immer noch funktionieren
  • Sie können weitere Erweiterungen hinzufügen, z. B. eine pro Ansichts-Controller, um die Übersichtlichkeit zu gewährleisten
Robin Dorpe
quelle
3
Zu beachten ist, dass die auf die beschriebene Weise definierten Zeichenfolgen statische Zeichenfolgen sind. Die App sollte nach dem Ändern der Sprache in der iOS-Einstellungen-App neu gestartet werden. Wenn nicht, starten Sie es selbst neu, um Änderungen zu sehen. Es kann auch einen Speicheraufwand verursachen, da wir alle Zeichenfolgen auf einmal initialisieren und nicht zu dem Zeitpunkt, zu dem sie benötigt werden.
iDevAmit
2
Ich denke, es ist besser, hier berechnete Eigenschaften wie diese zu verwendenstatic var Hello: String = { return NSLocalizedString("Hello") }
Art-of-
Downvoted , weil sie die Swift folgt nicht Benennungsrichtlinien
Cristik
3

Wenn Sie ein SDK entwickeln. Sie benötigen eine zusätzliche Operation.

1) Erstellen Sie Localizable.strings wie gewohnt in YourLocalizeDemoSDK.

2) Erstellen Sie die gleichen Localizable.strings in YourLocalizeDemo.

3) Finden Sie Ihren Bundle-Pfad von 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))hilft Ihnen, das Bundle in YourLocalizeDemoSDK zu finden. Wenn Sie Bundle.mainstattdessen verwenden, erhalten Sie einen falschen Wert (tatsächlich ist es dieselbe Zeichenfolge mit dem Schlüssel).

Aber wenn Sie die von Dr. OX erwähnte String-Erweiterung verwenden möchten . Sie müssen noch etwas tun. Die Ursprungserweiterung sieht so aus.

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

Wie wir wissen, entwickeln wir ein SDK, Bundle.main erhalten das Bundle des YourLocalizeDemo-Bundles. Das wollen wir nicht. Wir benötigen das Bundle in YourLocalizeDemoSDK. Dies ist ein Trick, um es schnell zu finden.

Führen Sie den folgenden Code in einer NSObject-Instanz in YourLocalizeDemoSDK aus. Und Sie erhalten die URL von YourLocalizeDemoSDK.

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

Wenn Sie beide URLs drucken, werden Sie feststellen, dass wir eine bundleURLofSDK-Basis auf mainBundleURL erstellen können. In diesem Fall ist es:

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

Und die String-Erweiterung lautet:

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

Ich hoffe es hilft.

Liam
quelle
2

Ich habe mein eigenes Genstrings-Tool zum Extrahieren von Strings mithilfe einer benutzerdefinierten Übersetzungsfunktion erstellt

extension String {

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

}

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

Es analysiert alle Ihre schnellen Dateien und exportiert die Zeichenfolgen und Kommentare in Ihrem Code in eine .strings-Datei.

Wahrscheinlich nicht der einfachste Weg, aber es ist möglich.

Max
quelle
1

Dies ist zwar keine Antwort auf das Verkürzungsproblem, hat mir jedoch bei der Organisation der Nachrichten geholfen. Ich habe eine Struktur für Fehlermeldungen wie unten erstellt

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

Auf diese Weise können Sie die Nachrichten organisieren und Genstrings zum Laufen bringen.

Und dies ist der verwendete Befehl genstrings

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

Hilfreich für die Verwendung in Unit-Tests:

Dies ist eine einfache Version, die auf verschiedene Anwendungsfälle erweitert werden kann (z. B. unter Verwendung von 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)
}

Verwenden Sie es so:

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

Oder so mit einem Kommentar:

NSLocalizedString("YOUR-KEY", referenceClass: self, comment: "usage description")
GatoCurioso
quelle
1
Es ist eine schlechte Praxis, die Kommentare wegzulassen.
José
@ José Danke für deinen Kommentar. Der Code war als Idee gedacht, nicht als Vorlage zum Kopieren und Einfügen. Aber ich habe die Option hinzugefügt, Kommentare hinzuzufügen, wenn Sie wollen;)
GatoCurioso
1

Dies ist eine Verbesserung gegenüber dem "lokalisierten" Ansatz. Beginnen Sie mit dem Hinzufügen der Klassenerweiterung, da dies bei allen Zeichenfolgen hilft, die Sie programmgesteuert festgelegt haben:

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

Beispiel für die Verwendung für Zeichenfolgen, die Sie programmgesteuert festlegen:

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

Jetzt machen die Storyboard-Übersetzungsdateien von Xcode den Dateimanager unübersichtlich und verarbeiten Aktualisierungen des Storyboards auch nicht gut. Ein besserer Ansatz besteht darin, eine neue grundlegende Labelklasse zu erstellen und sie allen Storyboard-Labels zuzuweisen:

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()
    }
}

Jetzt wird jedes Etikett, das Sie hinzufügen und für das im Storyboard die Standardvorgabe angegeben ist, automatisch übersetzt, vorausgesetzt, Sie haben eine Übersetzung dafür bereitgestellt.

Sie können dasselbe für UIButton tun:

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.
quelle
0

Wenn Sie beispielsweise aus dem Englischen, wo eine Phrase dieselbe ist, in eine andere Sprache übersetzen möchten, in der sie sich unterscheidet (aufgrund des Geschlechts, der Verbkonjugationen oder der Deklination), ist die einfachste NSString-Form in Swift, die in allen Fällen funktioniert, die drei Argumente eins . Beispielsweise wird der englische Ausdruck "Vorheriges war" für den Fall "Gewicht" ("предыдущ щ был") und für "Taille" ("предыдущ ая был а " anders als Russisch übersetzt ") .

In diesem Fall benötigen Sie zwei verschiedene Übersetzungen für eine Quelle (in Bezug auf das in WWDC 2018 empfohlene XLIFF-Tool). Sie können dies nicht mit zwei Argumenten erreichen: NSLocalizedString, wobei "previous was" sowohl für den "key" als auch für die englische Übersetzung (dh für den Wert) gleich ist. Die einzige Möglichkeit besteht darin, die Form mit drei Argumenten zu verwenden

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

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

Dabei sind die Schlüssel ("previousWasFeminine" und "previousWasMasculine") unterschiedlich.

Ich weiß, dass der allgemeine Rat darin besteht, den Ausdruck als Ganzes zu übersetzen, aber manchmal ist er zu zeitaufwändig und unpraktisch.

Vadim Motorine
quelle
-1

Lokalisierung mit Standardsprache:

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