Apa yang setara NSLocalizedString di Swift?

228

Adakah yang setara dengan Swift NSLocalizedString(...)? Dalam Objective-C, kami biasanya menggunakan:

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

Bagaimana saya bisa mencapai hal yang sama di Swift? Saya menemukan fungsi:

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

Namun, ini sangat panjang dan tidak nyaman sama sekali.

RaffAl
sumber
2
Yang terbaik adalah membuat versi potongan kode yang lebih pendek: NSLocalizedString ("", komentar: "") ... Saya menyukai solusi ekstensi, tetapi masalahnya genstrings tidak akan menangkap string ini ke dalam file terjemahan.
Matej Ukmar
3
Di Swift 3 Anda bisa menggunakan NSLocalizedString("Cancel", comment: "Cancel button title")memanfaatkan nilai-nilai default. Nyaman menurut saya.
LShi
Ini adalah artikel yang sangat bagus tentang pelokalan (ekstensi string, tabel string yang berbeda, dan bahkan pluralisasi): medium.com/@marcosantadev/…
LightMan
Ini adalah artikel yang sangat bagus tentang pelokalan di Swift untuk medium
Mendy

Jawaban:

373

Saya menggunakan solusi berikutnya:

1) buat ekstensi:

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

2) dalam file Localizable.strings :

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

3) contoh penggunaan:

myLabel.text = "Hi".localized

Nikmati! ;)

--upd: -

untuk kasus dengan komentar Anda dapat menggunakan solusi ini:

1) Perpanjangan:

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

2) dalam file .strings:

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

3) menggunakan:

myLabel.text = "Hi".localized(withComment: "with !!!")
dr OX
sumber
92
Satu-satunya masalah dengan ini adalah bahwa Anda tidak akan dapat menggunakan genstringsutilitas untuk menghasilkan file .strings Anda.
Ned
9
Itu ide yang sangat bagus! Saya juga membuatnya sedikit lebih pintar dengan mengubah func localized(comment: String = "") -> Stringsehingga menjadi lebih kecil dan dengan komentar opsional :)
Gui Moura
2
Adakah yang tahu bagaimana cara menggunakannya genstrings?
Chris
48
Semua orang sangat senang dengan jawaban ini, tetapi masalah BESAR (untuk setiap proyek serius dengan beberapa bahasa) adalah ini benar-benar mengacaukan pengelolaan pesan terjemahan Anda, karena genstringshanya bekerja pada string literal yang diteruskan ke NSLocalizedString. Dengan solusi cerdas ini, Anda kehilangan kemampuan untuk memperbarui file .strings Anda menggunakan genstringsalat ini, dan setidaknya bagi saya itu berarti saya tidak akan dapat menggunakan pendekatan yang disederhanakan ini.
Erik van der Neut
14
Saya menemukan solusi hebat ini diimplementasikan di github.com/marmelroy/Localize-Swift . Masalah genstring juga diselesaikan dengan skrip python khusus yang disertakan oleh penulis. Saya bukan penulis.
Tomek Cejner
279

Yang NSLocalizedStringada juga di dunia Swift.

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

The tableName, bundledan valueparameter ditandai dengan defaultkata kunci yang berarti kita dapat menghilangkan parameter ini sementara memanggil fungsi. Dalam hal ini, nilai standarnya akan digunakan.

Ini mengarah pada kesimpulan bahwa pemanggilan metode dapat disederhanakan menjadi:

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

Swift 5 - tidak ada perubahan, masih berfungsi seperti itu.

RaffAl
sumber
44
hanya perbedaan bahwa komentar tidak boleh nol, dan pelengkapan otomatis jauh dari intuitif untuk versi pendek.
Marcin
1
ini tidak berfungsi lagi saya mendapatkan kesalahan dengan mengatakan bahwa tidak cukup argumen yang digunakan.
Aplikasi 4 U
2
Bukan berarti di atas benar dalam Xcode 6.3, Swift 1.2 dengan perubahan spesifik dari objektif-c, komentar (seperti yang dikatakan Marcin) tidak boleh nol, tetapi bisa "" (kosong).
Neil
2
Komentar kosong / kosong menyulitkan untuk memindahkan string nanti di file string; jika tidak ada yang lain tambahkan kelas / nama file di mana itu digunakan sebagai komentar.
Johan
Ini jawaban yang benar. Setelah Apple memperbaruinya untuk Swift, Xcode akan dapat mengonversi API ini secara otomatis ke API Swift yang baru dan tidak ada lagi yang akan rusak. Bahkan dalam menu Refraktor Xcode saat ini (v 11.4.1) ada Wrap in NSLocalizedStringopsi yang membuat semuanya sangat mudah dengan hanya menyorot teks, mengklik kanan, dan memilih item menu.
Ethan Allen
28

Variasi dari jawaban yang ada:

Swift 5.1:

extension String {

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

}

Anda kemudian dapat menggunakannya dengan atau tanpa komentar:

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

Harap dicatat bahwa genstringstidak akan berfungsi dengan solusi ini.

José
sumber
14

Dengan menggunakan cara ini, dimungkinkan untuk membuat implementasi yang berbeda untuk tipe yang berbeda (yaitu kelas Int atau khusus seperti CurrencyUnit, ...). Anda juga dapat memindai metode ini dengan menggunakan utilitas genstrings. Cukup tambahkan bendera rutin ke perintah

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

perpanjangan:

import UIKit

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

pemakaian:

String.localize("foo.bar", comment: "Foo Bar Comment :)")
Kay
sumber
Jawaban ini luar biasa dan harus lebih ditingkatkan! Ini adalah solusi paling sederhana yang saya temukan sejauh ini jika Anda ingin menghindari membawa perpustakaan lain. Ini adalah solusi asli yang bagus.
cgossain
6

Versi Swift 3:) ...

import Foundation

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

Sebenarnya, Anda dapat menggunakan dua fase untuk menerjemahkan teks Anda di proyek Swift:

1) Fase pertama menggunakan cara lama untuk membuat semua string Anda yang dapat diterjemahkan:

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

1.1) Maka Anda harus menggunakan genstring untuk menghasilkan Localizable.strings:

$ genstrings *swift

2) Setelah itu, Anda harus menggunakan jawaban ini .

2.1) Gunakan opsi "Temukan dan Ganti" XCode Anda berdasarkan ekspresi reguler. Adapun contoh yang diberikan (jika Anda tidak memiliki komentar) ekspresi regulernya adalah:

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

dan ganti dengan

$1.localized

atau (jika Anda memiliki komentar)

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

dan ganti dengan

$1.localizedWithComment(comment: $2)

Anda bebas bermain dengan regex dan kombinasi ekstensi yang berbeda sesuai keinginan. Cara umumnya adalah memecah seluruh proses menjadi dua fase. Semoga itu bisa membantu.

GYFK
sumber
1
Maaf saya tidak mendapatkan banyak jawaban di sini. Apa manfaat metode ini daripada menggunakan NSLocalizedString("Cancel", comment: "Cancel button title")?
LShi
1
@LShi beberapa orang mengeluh, yang NSLocalizedStringterlihat kurang Swiftier, daripada seharusnya. String.localizeddi sisi lain terlihat lebih Swifty tetapi Anda tidak dapat menggunakan gesntringsutilitas dengan yang biasa digunakan untuk memudahkan pekerjaan Anda dengan internasionalisasi. Maksud saya adalah cukup mudah untuk menggabungkan kedua pendekatan. Jadi terutama itu adalah masalah keterbacaan.
GYFK
Apa yang terjadi jika Anda perlu melakukan putaran lain genstrings? Apakah Anda mengganti kembali semua .localizedoleh NSLocalizedString?
Cristik
5

Dibuat metode pembantu kecil untuk kasus, di mana "komentar" selalu diabaikan. Lebih sedikit kode lebih mudah dibaca:

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

Letakkan di mana saja (di luar kelas) dan Xcode akan menemukan metode global ini.

JOM
sumber
12
Ini praktik buruk. Komentar direkomendasikan dan bermanfaat kecuali Anda melakukan sendiri semua terjemahannya.
Yeremia
Bahkan jika Anda menerjemahkan sendiri, komentar akan sangat membantu, terutama dalam proyek besar.
shim
4

Mungkin cara terbaik adalah satu ini di sini .

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

dan

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

Anda dapat menggunakannya seperti ini

let message: String = .ThisApplicationIsCreated
print(message)

bagi saya ini adalah yang terbaik karena

  • String hardcoded ada dalam satu file tertentu, jadi hari Anda ingin mengubahnya sangat mudah
  • Lebih mudah digunakan daripada mengetik string secara manual di file Anda setiap saat
  • genstring masih akan bekerja
  • Anda dapat menambahkan lebih banyak ekstensi, seperti satu per tampilan controller untuk menjaga semuanya tetap rapi
Robin Dorpe
sumber
3
Hal yang perlu diperhatikan adalah bahwa String yang didefinisikan dengan cara yang dijelaskan adalah string statis. Aplikasi harus diluncurkan kembali setelah mengubah bahasa di aplikasi Pengaturan iOS. Jika tidak, luncurkan ulang sendiri untuk melihat perubahan. Itu juga dapat memiliki overhead memori, karena kita menginisialisasi semua string sekaligus, bukan pada saat mereka dibutuhkan.
iDevAmit
2
Saya pikir lebih baik menggunakan properti yang dihitung di sini, seperti inistatic var Hello: String = { return NSLocalizedString("Hello") }
seni impian
Turun karena tidak mengikuti pedoman penamaan
Cristik
3

Ketika Anda sedang mengembangkan SDK. Anda perlu beberapa operasi tambahan.

1) buat Localizable.strings seperti biasa di YourLocalizeDemoSDK.

2) buat Localizable.strings yang sama di YourLocalizeDemo.

3) temukan Bundle Path Anda dari 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))membantu Anda menemukan bundel di YourLocalizeDemoSDK. Jika Anda menggunakan Bundle.mainsebagai gantinya, Anda akan mendapatkan nilai yang salah (sebenarnya string itu akan sama dengan kunci).

Tetapi jika Anda ingin menggunakan ekstensi String yang disebutkan oleh dr OX . Anda perlu melakukan lebih banyak lagi. Ekstensi asal terlihat seperti ini.

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

Seperti yang kita ketahui, kami sedang mengembangkan SDK, Bundle.mainakan mendapatkan bundel bundel YourLocalizeDemo. Bukan itu yang kita inginkan. Kami membutuhkan bundel di YourLocalizeDemoSDK. Ini adalah trik untuk menemukannya dengan cepat.

Jalankan kode di bawah ini dalam instance NSObject di YourLocalizeDemoSDK. Dan Anda akan mendapatkan URL YourLocalizeDemoSDK.

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

Cetak kedua url, Anda akan menemukan bahwa kita dapat membangun bundleURLofSDK berdasarkan mainBundleURL. Dalam hal ini, itu akan menjadi:

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

Dan ekstensi String akan menjadi:

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

Semoga ini bisa membantu.

Liam
sumber
2

Saya telah membuat semacam alat alat pstring untuk mengekstraksi string menggunakan fungsi terjemahan khusus

extension String {

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

}

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

Ini akan mem-parsing semua file cepat Anda dan mengekspor string dan komentar dalam kode Anda ke file .strings

Mungkin bukan cara termudah untuk melakukannya, tetapi itu mungkin.

Maks
sumber
1

Meskipun ini tidak menjawab masalah pemendekan, tetapi ini membantu saya untuk mengatur pesan, saya membuat struktur untuk pesan kesalahan seperti di bawah ini

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

Dengan cara ini Anda dapat mengatur pesan dan membuat genstring berfungsi.

Dan ini adalah perintah genstring yang digunakan

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

Bermanfaat untuk penggunaan dalam unit test:

Ini adalah versi sederhana yang dapat diperluas ke berbagai kasus penggunaan (misalnya dengan penggunaan 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)
}

Gunakan seperti ini:

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

Atau suka ini dengan komentar:

NSLocalizedString("YOUR-KEY", referenceClass: self, comment: "usage description")
GatoCurioso
sumber
1
Adalah praktik yang buruk untuk meninggalkan komentar.
José
@ José Terima kasih atas komentar Anda. Kode itu dimaksudkan sebagai ide, bukan sebagai template untuk salin dan tempel. Tapi saya menambahkan opsi untuk menambahkan komentar jika Anda ingin;)
GatoCurioso
1

Ini merupakan peningkatan pada pendekatan ".localized". Mulailah dengan menambahkan ekstensi kelas karena ini akan membantu dengan string apa pun yang Anda atur secara terprogram:

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

Contoh penggunaan untuk string yang Anda atur secara pemrograman:

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

Sekarang file terjemahan storyboard Xcode membuat pengelola file berantakan dan juga tidak menangani pembaruan storyboard dengan baik. Pendekatan yang lebih baik adalah membuat kelas label dasar baru dan menetapkannya untuk semua label papan cerita Anda:

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

Sekarang setiap label yang Anda tambahkan dan berikan default default untuk di storyboard akan secara otomatis diterjemahkan, dengan asumsi Anda telah menyediakan terjemahan untuk itu.

Anda dapat melakukan hal yang sama untuk 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
sumber
0

Ketika Anda menerjemahkan, katakanlah dari bahasa Inggris, di mana frasa sama, ke bahasa lain di mana berbeda (karena jenis kelamin, konjugasi kata kerja atau kemerosotan) bentuk NSString paling sederhana di Swift yang berfungsi dalam semua kasus adalah tiga argumen satu . Misalnya, frasa bahasa Inggris "sebelumnya adalah", diterjemahkan secara berbeda ke bahasa Rusia untuk kasus "berat" ("предыдущ ий был") dan untuk "pinggang" ("предыдущ ая был а ").

Dalam hal ini Anda memerlukan dua terjemahan berbeda untuk satu Sumber (dalam hal alat XLIFF direkomendasikan dalam WWDC 2018). Anda tidak dapat mencapainya dengan dua argumen NSLocalizedString, di mana "sebelumnya" akan sama untuk "kunci" dan terjemahan bahasa Inggris (yaitu untuk nilainya). Satu-satunya cara adalah menggunakan bentuk argumen tiga

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

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

di mana kunci ("beforeWasFeminine" dan "priorWasMasculine") berbeda.

Saya tahu bahwa saran umum adalah menerjemahkan frasa sebagai keseluruhan, namun terkadang terlalu memakan waktu dan merepotkan.

Vadim Motorine
sumber
-1

Lokalisasi dengan bahasa default:

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