104

I'm trying to localize my app using NSLocalizedString. When I import the XLIFF file, most works like a charm but something do not and some string is not localized. I have noticed that the problem is from NSLocalizedString containing something variable inside like:

NSLocalizedString(" - \(count) Notifica", comment: "sottotitolo prescrizione per le notifiche al singolare")

or

NSLocalizedString("Notifica per \(medicina!) della prescrizione \(prescription!)\nMemo: \(memoTextView.text)", comment: "Messaggio della Local Notification")

Maybe this is not the correct syntax for this kind of stuff. Someone can explain me how to do that in Swift?

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
msalafia
  • 2,703
  • 5
  • 23
  • 34
  • 1
    This is a [very good article](https://medium.com/@mendibarouk/enhance-your-localized-capabilities-on-your-ios-applications-d3ba17138077) about localization in Swift for a robust architecture – Mendy Apr 07 '19 at 08:09

8 Answers8

164

You can use the sprintf format parameters within NSLocalizedString, so your example can look like this:

let myString = String(format: NSLocalizedString(" - %d Notifica", comment: "sottotitolo prescrizione per le notifiche al singolare"), count)
zisoft
  • 22,770
  • 10
  • 62
  • 73
112

In Session #412 of the WWDC2014 "Localizing with Xcode 6" the proper way to this in Swift is the following:

String.localizedStringWithFormat(
    NSLocalizedString(" - %d Notifica",
    comment: "sottotitolo prescrizione per le notifiche al singolare"),
    count)

Note that localizedStringWithFormat sets the returned String's local based on given first argument:

  • No, it does not translate anything.
  • This confusion comes from wrong naming of NSLocalizedString.
  • It should have been named NSTranslatedString, or NSTranslatable (instead of NSLocalizedString).
Top-Master
  • 7,611
  • 5
  • 39
  • 71
Mark
  • 16,906
  • 20
  • 84
  • 117
  • 4
    Why do you need to use NSLocalizedString(...)? doesn't it happen in the implementation of String.localizedStringWithFormat(...)? – Yitzchak Sep 27 '16 at 05:27
  • 3
    Check out (http://stackoverflow.com/questions/26237549/how-does-localizedstringwithformat-work) – Mark Sep 27 '16 at 12:40
  • 2
    Thanks I already saw it after I asked. It will be very helpful for others so thanks – Yitzchak Sep 27 '16 at 12:56
26

I have followed the approach of creating extension to String as i have many strings to localize.

extension String {
    var localized: String {
        return NSLocalizedString(self, comment:"")
    }
}

To use it for localization in code do:

self.descriptionView.text = "Description".localized

For strings with dynamic variables follow :

self.entryTimeLabel.text = "\("Doors-open-at".localized) \(event.eventStartTime)"

Declare the strings in String files for different languages (example : Arabic and English)

enter image description here enter image description here

Hope will be helping!

JaspreetKour
  • 777
  • 9
  • 11
  • 5
    But it seems to me you're just attaching the time to the string. What if your localised string was like `The doors open at %@ o'clock`. Would your solution still work? – Houman Jun 04 '18 at 19:56
  • Yes, it is working perfectly as i'm getting time as String from back. – JaspreetKour Jun 05 '18 at 13:40
  • 2
    Thanks for including a reference to `Localizable.strings`. However, I think @Houman has a valid concern. – Matt Nov 14 '18 at 19:35
  • I don't know why people want to drop out "comment" value. It gives the description of the text and its purpose to localisation engineers without which they cannot make out the intention to use that text on a button or as a label etc. – Satyam Jul 19 '20 at 07:11
  • This is not ok as Houman said, but it's a generic way of concatenate strings. – trusk Jun 02 '21 at 11:43
  • This is not a good idea, because it assumes that the "doors open at" text will precede the time value in every language, but this is not necessarily true. It's better to use `localizedStringWithFormat` so that you can vary the position of the interpolated value if needed. – mginn Jul 06 '22 at 21:15
  • this answer is totally incorrect! In some languages you may have a different substring order to insert bad you hardcode the order – Gargo Apr 06 '23 at 05:51
16

Here is an extension I use in String, it adds a localizeWithFormat function with variable arguments,

extension String {

     func localizeWithFormat(arguments: CVarArg...) -> String{
        return String(format: self.localized, arguments: arguments)        
     }
            
     var localized: String{
         return Bundle.main.localizedString(forKey: self, value: nil, table: "StandardLocalizations")
     }
}

Usage:

let siriCalendarText = "AnyCalendar"
let localizedText = "LTo use Siri with my app, please set %@ as the default list on your device reminders settings".localizeWithFormat(arguments: siriCalendarTitle)

Just be careful not to use the same function and property names that String has. I normally use a 3 letter prefix for all my framework functions.

Ahmadreza
  • 6,950
  • 5
  • 50
  • 69
the Reverend
  • 12,305
  • 10
  • 66
  • 121
16

I tried the above solutions however the code below worked for me

SWIFT 4

extension String {

    /// Fetches a localized String
    ///
    /// - Returns: return value(String) for key
    public func localized() -> String {
        let path = Bundle.main.path(forResource: "en", ofType: "lproj")
        let bundle = Bundle(path: path!)
        return (bundle?.localizedString(forKey: self, value: nil, table: nil))!
    }


    /// Fetches a localised String Arguments
    ///
    /// - Parameter arguments: parameters to be added in a string
    /// - Returns: localized string
    public func localized(with arguments: [CVarArg]) -> String {
        return String(format: self.localized(), locale: nil, arguments: arguments)
    }

}

// variable in a class
 let tcAndPPMessage = "By_signing_up_or_logging_in,_you_agree_to_our"
                                     .localized(with: [tAndc, pp, signin])

// Localization File String
"By_signing_up_or_logging_in,_you_agree_to_our" = "By signing up or logging in, you agree to our \"%@\" and \"%@\" \nAlready have an Account? \"%@\"";
Pratik
  • 676
  • 10
  • 9
2

I wrote the same functions for UILabel

extension UILabel {
    
    func setLocalizedText(key: String) {
        self.text = key.localized
    }
    
    func setLocalizedText(key: String, arguments: [CVarArg]) {
        self.text = String(format: key.localized, arguments: arguments)
    }
}

If you want you can move this localized property to UILabel as well

extension String {
        
    var localized: String{
        return Bundle.main.localizedString(forKey: self, value: nil, table: nil)
    }
}

My localizable

"hi_n" = "Hi, %@!";

Used them like this:

self.greetingLabel.setLocalizedText(key: "hi_n", arguments: [self.viewModel.account!.firstName])
// Shows this on the screen -> Hi, StackOverflow!
OhhhThatVarun
  • 3,981
  • 2
  • 26
  • 49
0

I suggest writing a custom global function instead of an extension, because genstrings is just parsing the source code for NSLocalizedString with literals. That means you wont be able to automatically generate the .strings files from your source coder with the extension solutions.

func NSLocalizedString(_ key: String, tableName: String? = nil, bundle: Bundle = Bundle.main, value: String = "", comment: String, with arguments: CVarArg...) -> String {
    let localizedString = Foundation.NSLocalizedString(key, tableName: tableName, bundle: bundle, value: value, comment: comment)
    return String.localizedStringWithFormat(localizedString, arguments)
}
MrKew
  • 333
  • 1
  • 14
-4

I created an extension to String since I had many strings to be localized.

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

For example:

let myValue = 10
let anotherValue = "another value"

let localizedStr = "This string is localized: \(myValue) \(anotherValue)".localized
print(localizedStr)
Santosh
  • 2,900
  • 1
  • 16
  • 16
  • 6
    The big downside to this approach is that you have to manually extract the strings to send to the translator as `Editor > Export for Localization...` won't pick them up. – Jason Moore Jan 23 '17 at 17:29
  • Given what @JasonMoore suggested, I don't think this is the right approach. – Yuchen Feb 13 '17 at 20:20
  • @JasonMoore totally agree with you. Did any of you guys find a solution for this? – gasparuff Apr 12 '17 at 19:39
  • i don't knew why this solution is downrated :( . and almost the below nest below solution is the same but it have uprate – Amr Angry Aug 21 '17 at 14:35