Что такое NSLocalizedString эквивалент в Swift?

228

Есть ли в Swift эквивалент NSLocalizedString(...)? В Objective-C, мы обычно используем:

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

Как я могу добиться того же в Swift? Я нашел функцию:

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

Однако это очень долго и совсем не удобно.

RaffAl
источник
2
Лучше всего создать более короткую версию фрагмента кода: NSLocalizedString ("", comment: "") ... Мне понравилось решение расширения, но проблема в том, что genstrings не будут захватывать эти строки в файл перевода.
Матей Укмар
3
В Swift 3 вы можете просто использовать NSLocalizedString("Cancel", comment: "Cancel button title")преимущества значений по умолчанию. Это удобно, я думаю.
LShi
Это очень хорошая статья о локализации (расширение строк, таблицы разных строк и даже плюрализация): medium.com/@marcosantadev/…
LightMan
Это очень хорошая статья о локализации в Swift для надежной архитектуры medium.com/@mendibarouk/…
Менди

Ответы:

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

наслаждаться! ;)

--upd: -

для случая с комментариями вы можете использовать это решение:

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
Все очень взволнованы этим ответом, но БОЛЬШАЯ проблема (для любого серьезного проекта с несколькими языками) состоит в том, что это полностью портит ваше управление вашими переведенными сообщениями, потому что genstringsработает только с литеральными строками, переданными в NSLocalizedString. С этим умным обходным путем вы теряете возможность обновлять ваши файлы .strings с помощью genstringsинструмента, и по крайней мере для меня это означает, что я не смогу использовать этот упрощенный подход.
Эрик ван дер Нойт
14
Я нашел это отличное решение, реализованное в github.com/marmelroy/Localize-Swift . Проблема genstrings также решена с помощью специального скрипта Python, включенного автором. Я не автор.
Томек Джейнер
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 - без изменений, все так же работает.

RaffAl
источник
44
Разница лишь в том, что комментарий не может быть нулевым, а автозаполнение далеко не интуитивно понятно для короткой версии.
Марчин
1
это больше не работает, я получаю сообщение об ошибке, говорящее, что недостаточно аргументов.
Приложения 4 U
2
Не то, чтобы вышеприведенное было правильным в Xcode 6.3, Swift 1.2 с определенным изменением по сравнению с target-c, комментарий (как сказал Марчин) не может быть нулевым, но это может быть "" (пусто).
Нейл
2
Нулевой / пустой комментарий затрудняет перемещение строки позже в строковом файле; если больше ничего не добавляется имя класса / файла, где оно используется в качестве комментария.
Йохан
Это правильный ответ. Как только Apple обновит его для Swift, Xcode сможет просто автоматически преобразовать этот API в свой новый Swift API, и больше ничего не сломается. Даже в настоящее время в меню рефрактора 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не будет работать с этим решением.

Хосе
источник
15

Используя этот способ, можно создать другую реализацию для разных типов (например, 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)

Вы можете играть с регулярными выражениями и различными комбинациями расширений по своему желанию. Общий способ - разделить весь процесс на две фазы. Надеюсь, это поможет.

GYFK
источник
1
Извините, я не понимаю смысла многих ответов здесь. В чем преимущество метода перед использованием NSLocalizedString("Cancel", comment: "Cancel button title")?
LShi
1
@LShi некоторые люди жаловались, что NSLocalizedStringвыглядит менее быстрым, чем должно выглядеть. String.localizedс другой стороны, выглядит более Swifty, но вы не можете использовать gesntringsутилиту, которая обычно используется для облегчения вашей работы с интернационализацией. Я хочу сказать, что оба подхода довольно легко смешать. Так что в основном это вопрос читабельности.
GYFK
Что произойдет, если вам нужно сделать еще один раунд genstrings? Есть ли заменить обратно все .localizedна NSLocalizedString?
Кристик
5

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

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

Просто поместите его где угодно (вне класса), и Xcode найдет этот глобальный метод.

ЛОМ
источник
12
Это плохая практика. Комментарии рекомендуются и полезны, если вы не делаете весь перевод самостоятельно.
Иеремия
Даже если вы переводите сами, комментарии будут полезны, особенно в большом проекте.
Шим
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") }
арт-оф-мечты
Проголосовал, потому что он не следует правилам именования
Cristik
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, упомянутое доктором 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, вы обнаружите, что мы можем построить базу bundleURLofSDK на 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: "")
    }
}

Надеюсь, поможет.

Liam
источник
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

Он проанализирует все ваши swift-файлы и экспортирует строки и комментарии в вашем коде в файл .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
anoop4real
источник
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")
GatoCurioso
источник
1
Это плохая практика, чтобы оставлять комментарии.
Хосе
@ Хосе Спасибо за ваш комментарий. Код был задуман как идея, а не как шаблон для копирования и вставки. Но я добавил опцию добавления комментариев, если хотите;)
GatoCurioso
1

Это улучшение подхода ".localized". Начните с добавления расширения класса, поскольку это поможет с любыми строками, которые вы устанавливали программно:

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

где ключи («previousWasFeminine» и «previousWasMasculine») разные.

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

Вадим Моторин
источник
-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: "")
    }
}
Исследователь
источник