Що таке еквівалент NSLocalizedString у Swift?


228

Чи є еквівалент Swift NSLocalizedString(...)? В Objective-C, ми зазвичай використовуємо:

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

Як я можу досягти того ж у Свіфті? Я знайшов функцію:

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

Однак він дуже довгий і зовсім не зручний.


2
Найкраще створити більш коротку версію фрагмента коду: NSLocalizedString ("", коментар: "") ... Мені сподобалося рішення розширення, але проблема полягає в тому, що genstrings не захопить ці рядки у файл перекладу.
Матей Укмар

3
У Swift 3 ви можете просто NSLocalizedString("Cancel", comment: "Cancel button title")скористатися значеннями за замовчуванням. Думаю, це зручно.
LShi

Це дуже гарна стаття про локалізацію (розширення рядків, різні таблиці рядків і навіть плюралізацію): medium.com/@marcosantadev/…
LightMan

Це дуже хороша стаття про локалізацію у Свіфті
Менді,

Відповіді:


373

Я використовую наступне рішення:

1) створити розширення:

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

2) у файлі Localizable.strings :

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

3) приклад використання:

myLabel.text = "Hi".localized

насолоджуйтесь! ;)

--додано: -

для випадку з коментарями ви можете використовувати це рішення:

1) Розширення:

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

2) у .strings файл:

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

3) використовуючи:

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

92
Єдина проблема з цим полягає в тому, що ви не зможете використовувати genstringsутиліту для створення ваших .strings файлів.
Неді

9
Це дуже гарна ідея! Я також зробив це трохи розумнішим, змінивши його, func localized(comment: String = "") -> Stringщоб він став меншим і з додатковими коментарями :)
Gui Moura

2
Будь-яка ідея, як користуватися genstringsцим?
Кріс

48
Всі дуже схвильовані з приводу цієї відповіді, але проблема BIG (для будь-якого серйозного проекту з декількома мовами) полягає в тому, що це повністю перешкоджає вашому керуванню перекладеними повідомленнями, оскільки працюють genstringsлише над буквальними рядками, переданими в NSLocalizedString. Завдяки цьому розумному вирішенню ви втрачаєте можливість оновлювати файли .strings за допомогою genstringsінструменту, і принаймні для мене це означає, що я не зможу використовувати цей спрощений підхід.
Ерік ван дер Нойт

14
Я знайшов це чудове рішення, реалізоване в github.com/marmelroy/Localize-Swift . Проблема genstrings також вирішується користувацьким сценарієм python, включеним автором. Я не автор.
Tomek Cejner

279

NSLocalizedStringІснує і в світі Свіфта.

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

В tableName, bundleі valueпараметри, позначеніdefault ключовим словом , яке означає , що ми можемо опустити ці параметри при виконанні функції. У цьому випадку будуть використані їхні значення за замовчуванням.

Це призводить до висновку, що виклик методу можна спростити до:

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

Swift 5 - без змін, все одно працює так.


44
Єдина відмінність, що коментар не може бути нульовим, а автоматичне завершення далеко не інтуїтивно зрозуміло для короткої версії.
Марцін

1
це більше не працює, я отримую помилку, кажучи, що використовується недостатньо аргументів.
Додатки 4 U

2
Не в тому, що вищевказане є правильним у Xcode 6.3, Swift 1.2 із конкретною зміною від target-c, коментар (як заявив Марцін) не може бути нульовим, але він може бути "" (порожнім).
Ніл

2
Коментар з нулем / порожнім ускладнює перенесення рядка пізніше у файл рядка; якщо нічого іншого не додайте назву класу / файлу там, де він використовується як коментар.
Йоган

Це правильна відповідь. Після того, як Apple оновить її для Swift, Xcode зможе просто перетворити цей API у свій новий API Swift, і більше нічого не зламається. Навіть у меню рефракторів Xcode (v 11.4.1) є Wrap in NSLocalizedStringпараметр, який полегшує роботу, просто виділивши текст, клацнувши правою кнопкою миші та вибравши пункт меню.
Етан Аллен

28

Варіант існуючих відповідей:

Swift 5.1:

extension String {

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

}

Потім ви можете просто використовувати його з коментарями або без них:

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

Зауважте, що genstringsз цим рішенням не працюватиме.


14

За допомогою цього способу можна створити іншу реалізацію для різних типів (наприклад, Int або спеціальні класи, такі як CurrencyUnit, ...). Також можливо сканувати виклик цього методу за допомогою утиліти genstrings. Просто додайте до команди звичайний прапор

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

розширення:

import UIKit

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

використання:

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

Ця відповідь є дивовижною, і її слід більше підтримувати! Це найпростіше рішення, яке я знайшов поки що, якщо ви хочете уникнути залучення ще однієї бібліотеки. Це хороше рідне рішення.
cgossain

6

Версія Swift 3:) ...

import Foundation

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

6

Насправді ви можете використовувати дві фази для перекладу своїх текстів у проектах Swift:

1) Перший етап використовує старий спосіб створити всі ваші транслябельні рядки:

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

1.1) Тоді вам слід використовувати genstrings для створення Localizable.strings:

$ genstrings *swift

2) Після цього слід скористатися цією відповіддю .

2.1) Використовуйте параметр "Знайти та замінити" XCode на основі регулярного виразу. Що стосується даного прикладу (якщо у вас немає коментарів) регулярним виразом буде:

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

і замінити його на

$1.localized

або (якщо у вас є коментарі)

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

і замінити його на

$1.localizedWithComment(comment: $2)

Ви можете грати з регулярними формулами та різними комбінаціями розширень за своїм бажанням. Загальний спосіб - це розділення всього процесу на дві фази. Сподіваюся, що це допомагає.


1
Вибачте, я не знаю тут багато відповідей. Яка перевага методу над використанням NSLocalizedString("Cancel", comment: "Cancel button title")?
LShi

1
@LShi деякі люди скаржилися, що NSLocalizedStringвиглядає менш Swiftier, ніж це має виглядати. String.localizedз іншого боку, виглядає більш швидким, але ви не можете використовувати gesntringsкорисну програму, яка зазвичай використовується для полегшення вашої роботи з інтернаціоналізацією. Моя думка, що поєднувати обидва підходи досить просто. Тож головним чином мова йде про читабельність.
GYFK

Що станеться, якщо вам потрібно зробити ще один раунд genstrings? Чи є замінити назад все .localizedна NSLocalizedString?
Крістік

5

Створив невеликий помічний метод для випадків, коли «коментар» завжди ігнорується. Менше коду легше читати:

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

Просто помістіть його в будь-якому місці (поза класом) і Xcode знайде цей глобальний метод.


12
Це погана практика. Коментарі рекомендуються та корисні, якщо ви не робите весь переклад самостійно.
Єремія

Навіть якщо ви перекладаєте себе, коментарі будуть корисними, особливо у великому проекті.
shim

4

Мабуть, найкращий спосіб - це цей тут .

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

і

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

Ви можете використовувати його так

let message: String = .ThisApplicationIsCreated
print(message)

для мене це найкраще, тому що

  • Рядки з твердим кодом містяться в одному конкретному файлі, тому день, коли ви хочете його змінити, це дуже просто
  • Простіше у використанні, ніж кожен раз вручну вводити рядки у файл
  • genstrings все одно працюватимуть
  • Ви можете додати більше розширень, як-от одне на контролер подання, щоб зберегти речі акуратними

3
Варто зазначити, що рядки, визначені описаним способом, є статичними рядками. Додаток слід перезапустити після зміни мови в додатку Налаштування iOS. Якщо ні, перезапустіть його власноруч, щоб побачити зміни. Він також може мати надбавку пам'яті, оскільки ми ініціалізуємо всі рядки одразу, а не в той час, коли вони потрібні.
iDevAmit

2
Я думаю, що тут краще використовувати обчислювані властивості, як цеstatic var Hello: String = { return NSLocalizedString("Hello") }
мистецтво мрії

Захищений, тому що він не дотримується правил іменування
Крістік

3

Коли ви розробляєте SDK. Вам потрібна додаткова операція.

1) створити Localizable.strings як зазвичай, у YourLocalizeDemoSDK.

2) створити ті ж Localizable.strings у YourLocalizeDemo.

3) знайти свій шлях до розшарування до пакету 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))допоможе вам знайти пакет у YourLocalizeDemoSDK. Якщо ви використовуєтеBundle.main замість цього, ви отримаєте неправильне значення (адже це буде однаковий рядок з ключем).

Але якщо ви хочете використовувати розширення String, згадане dr OX . Вам потрібно зробити ще трохи. Розширення походження виглядає приблизно так.

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

Як ми знаємо, ми розробляємо SDK, Bundle.main отримаємо комплект пакету YourLocalizeDemo. Це не те, що ми хочемо. Нам потрібен комплект у YourLocalizeDemoSDK. Це хитрість, щоб швидко її знайти.

Запустіть код нижче в екземплярі NSObject у YourLocalizeDemoSDK. І ви отримаєте URL-адресу YourLocalizeDemoSDK.

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

Роздрукувавши обидва URL-адреси, ви побачите, що ми можемо побудувати базу данихURLofSDK на mainBundleURL. У цьому випадку це буде:

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

І розширення String буде:

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

Сподіваюся, це допомагає.


2

Я створив свій власний інструмент genstrings для вилучення рядків за допомогою спеціальної функції перекладу

extension String {

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

}

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

Він розбере всі ваші швидкі файли та експортує рядки та коментарі у вашому коді до файлу .strings.

Напевно, це не найпростіший спосіб зробити це, але це можливо.


1

Хоча це не відповідає на проблему скорочення, але це допомогло мені впорядкувати повідомлення, я створив структуру повідомлень про помилки, як нижче

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

Таким чином ви можете впорядкувати повідомлення та змусити працювати з інструментами.

І це використовується команда genstrings

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

1

Корисна інформація для використання в одиничних тестах:

Це проста версія, яку можна поширити на різні випадки використання (наприклад, за допомогою імен 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)
}

Використовуйте його так:

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

Або так із коментарем:

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

1
Не можна залишати коментарі поганою практикою.
Жозе

@ Жозе Дякуємо за ваш коментар. Код мався на увазі як ідея, а не як шаблон для копіювання та вставки. Але я додав можливість додавати коментарі, якщо хочете;)
GatoCurioso

1

Це є вдосконаленням "локалізованого" підходу. Почніть з додавання розширення класу, оскільки це допоможе будь-яким рядкам, які ви задавали програмно:

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

Приклад використання для рядків, які ви встановили програмно:

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

Тепер файли перекладу розповіді Xcode роблять файловий менеджер безладним, а також не обробляють оновлення до рекламної дошки. Кращим підходом є створення нового базового класу етикеток та присвоєння його всім вашим етикеткам:

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

Тепер кожна мітка, яку ви додасте та надаєте за замовчуванням за замовчуванням на дошці повідомлень, автоматично буде переведена, припускаючи, що ви надаєте для неї переклад.

Ви можете зробити те ж саме для 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

Коли ви перекладете, скажімо, з англійської мови, де фраза є однаковою, на іншу мову, де вона відрізняється (через стать, сполучення дієслів чи декланацію), найпростіша форма NSString у Swift, яка працює у всіх випадках, це три аргументи один . Наприклад, англійська фраза «попередній був», перекладається по- різному на російський мову для випадку «ваги» ( «всі попередні ий був») і «талії» ( "попередн а був а »).

У цьому випадку вам потрібно два різних переклади для одного джерела (з точки зору інструмента XLIFF, рекомендованого у WWDC 2018). Ви не можете досягти цього за допомогою двох аргументів NSLocalizedString, де "попередній був" буде однаковим і для "ключа", і для англійського перекладу (тобто для значення). Єдиний спосіб - використовувати три форми аргументів

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

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

де ключі ("попереднійWasFeminine" та "попереднійWasMasculine") різні.

Я знаю, що загальна порада полягає в тому, щоб перекласти фразу як ціле, однак, іноді це занадто багато часу і незручно.


-1

Локалізація з мовою за замовчуванням:

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: "")
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.