Enum with localized string in swift

15,156

Solution 1

You can use any StringLiteralConvertible, Equatable type for RawValue type of enum.

So, how about:

import Foundation

struct LocalizedString: StringLiteralConvertible, Equatable {

    let v: String

    init(key: String) {
        self.v = NSLocalizedString(key, comment: "")
    }
    init(localized: String) {
        self.v = localized
    }
    init(stringLiteral value:String) {
        self.init(key: value)
    }
    init(extendedGraphemeClusterLiteral value: String) {
        self.init(key: value)
    }
    init(unicodeScalarLiteral value: String) {
        self.init(key: value)
    }
}

func ==(lhs:LocalizedString, rhs:LocalizedString) -> Bool {
    return lhs.v == rhs.v
}

enum DietWithoutResidueOption: LocalizedString {
    case NoDiet = "NoDiet"
    case ThreeDays = "ThreeDays"
    case FiveDays  = "FiveDays"

    var localizedString: String {
        return self.rawValue.v
    }

    init?(localizedString: String) {
        self.init(rawValue: LocalizedString(localized: localizedString))
    }
}

Using this, you can construct DietWithoutResidueOption by 3 ways:

let option1 = DietWithoutResidueOption.ThreeDays
let option2 = DietWithoutResidueOption(rawValue: "ThreeDays") // as Optional
let option3 = DietWithoutResidueOption(localizedString: "OUI, SUR 3 JOURS")  // as Optional

and extract the localized string with:

let localized = option1.localizedString

Solution 2

Try this, it's pretty simple and straight forward:

enum ChoicesTitle: String {
    case choice1 = "Choice 1"
    case choice2 = "Choice 2"
    case choice3 = "Choice 3"
    case choice4 = "Choice 4"
    case choice5 = "Choice 5"
    case choice6 = "Choice 6"

    func localizedString() -> String {
        return NSLocalizedString(self.rawValue, comment: "")
    }

    static func getTitleFor(title:ChoicesTitle) -> String {
        return title.localizedString()
    }
}

And you could use it like this:

let stringOfChoice1: String = ChoicesTitle.getTitleFor(title: .choice1)

Hope this works for you

Solution 3

this is a late answer, but I just had a chat with Apple Engineers about that topic they recommend to do it that way:

    enum LocalizedStrings {
        case title

        var localized: String {
            switch self {
            case .title:
                return NSLocalizedString("My Title", comment: "My Comment")
            }
        }
    }

In your case the solution would be not much different from the original code:

    enum DietWithoutResidueOption {
        case NoDiet
        case ThreeDays
        case FiveDays

        var localizedString: String {
            switch self {
            case .NoDiet:
                return NSLocalizedString("NoDiet", comment: "Some comment")
            case .ThreeDays:
                return NSLocalizedString("ThreeDays", comment: "Some comment")
            case .FiveDays:
                return NSLocalizedString("FiveDays", comment: "Some comment")
            }
        }

        static func dietWithoutResidueOptionWith(localizedString: String) -> DietWithoutResidueOption {
            switch localizedString {
            case DietWithoutResidueOption.ThreeDays.localizedString:
                return DietWithoutResidueOption.ThreeDays
            case DietWithoutResidueOption.FiveDays.localizedString:
                return DietWithoutResidueOption.FiveDays
            default:
                return DietWithoutResidueOption.NoDiet
            }
        }
    }

The reason is that they don't want you to pass variables into NSLocalizedString(). This has something to do with optimization and parsing the strings. Imagine Xcode generating the localizable.strings file on it's own at some point, but it could not find the strings, because they are passed as variables.

Solution 4

A nice approach is to create a struct of Localizable Strings with static variables like so:

LocalizableStrings.swift

struct LocalizableStrings {
    static let noDiet  = NSLocalizedString("NoDiet", comment: "")
    static let threeDays  = NSLocalizedString("ThreeDays", comment: "")
    static let fiveDays  = NSLocalizedString("FiveDays", comment: "")
}

Localizable.strings

"NoDiet" = "NON, JE N'AI PAS DE RÉGIME";
"ThreeDays" = "OUI, SUR 3 JOURS";
"FiveDays"  = "OUI, SUR 5 JOURS";

And your enum would look like that:

Enum

enum DietWithoutResidueOption {
    case NoDiet,
    ThreeDays,
    FiveDays

    var description : String {
        get {
            switch(self) {
            case .NoDiet:
                return LocalizableStrings.noDiet
            case .ThreeDays:
                return LocalizableStrings.threeDays
            case .FiveDays:
                return LocalizableStrings.fiveDays
            }
        }
    }
}

So, for instance, to get your description you can do like below:

DietWithoutResidueOption.NoDiet.description

The good thing about this approach is that you put the keys of your localizable strings on a single file. So, for instance, if you change the NoDiet key on your Localizable.strings file you only need to update the LocalizableStrings.swift file, instead of all the places where we have the NoDiet key as a string. Furthermore, you take the risk of spelling wrong the NoDiet key in some file where it is being used and your code will compile with no errors, meanwhile using a static variable from LocalizableStrings.swift you can avoid that, as your code will not compile and you will see an error message saying where the error is.

Solution 5

Ohter alternative :

Enum

enum Title : String {

  case CEO = "CEOKey"
  case CTO = "CTOKey"
  case CFO = "CFOKey"

  private static let allTitles = [CEO, CTO, CFO]

  var localizedString: String {
    return NSLocalizedString(self.rawValue, comment: "")
  }

  init!(rawValue: String) {
    var keys =  Title.allTitles
    var filtered = keys.filter { $0.rawValue == rawValue }

    self = filtered.first!
  }

  init!(localizedString: String) {
    var keys =  Title.allTitles
    var filtered = keys.filter { $0.localizedString == localizedString }

    self = filtered.first!
  }
}

Localizable.strings

"CEOKey" = "Chief Executive Officer";
"CTOKey" = "Chief Technical Officer";
"CFOKey" = "Chief Financial Officer";

Constract enum :

let option1 = Title.CFO
let option2 = Title(rawValue: "CTOKey") // init from key
let option3 = Title(localizedString: NSLocalizedString("CEOKey", comment: ""))  // init from value

Extract the localized strings :

println("option1 localized string : \(option1.localizedString)")
println("option2 localized string : \(option2.localizedString)")
println("option3 localized string : \(option3.localizedString)")

Input

option1 localized string : Chief Financial Officer
option2 localized string : Chief Technical Officer
option3 localized string : Chief Executive Officer

This code will generate exception, if localized strings or keys not found

Share:
15,156

Related videos on Youtube

HamzaGhazouani
Author by

HamzaGhazouani

Mobile developer [email protected]

Updated on June 21, 2022

Comments

  • HamzaGhazouani
    HamzaGhazouani almost 2 years

    I want to use enumeration with localized string, so I do like this, it works, but the problem of this solution is : I can't get easily enum value from localized string, I must have the key to do it :

    let option = DietWithoutResidueOption(rawValue: "NoDiet")
    

    If not I must to call dietWithoutResidueOptionWith method to get enum value... :/

    There are a better solution to store directly localizedString and not keys in enum ?

    Thanks

    Enumeration

      enum DietWithoutResidueOption: String {
      case NoDiet = "NoDiet"
      case ThreeDays = "ThreeDays"
      case FiveDays  = "FiveDays"
    
      private func localizedString() -> String {
        return NSLocalizedString(self.rawValue, comment: "")
      }
    
      static func dietWithoutResidueOptionWith(#localizedString: String) -> DietWithoutResidueOption {
        switch localizedString {
        case DietWithoutResidueOption.ThreeDays.localizedString():
          return DietWithoutResidueOption.ThreeDays
        case DietWithoutResidueOption.FiveDays.localizedString():
          return DietWithoutResidueOption.FiveDays
        default:
          return DietWithoutResidueOption.NoDiet
        }
      }
    }
    

    Localizable.strings

    "NoDiet" = "NON, JE N'AI PAS DE RÉGIME";
    "ThreeDays" = "OUI, SUR 3 JOURS";
    "FiveDays"  = "OUI, SUR 5 JOURS";
    

    call

    println(DietWithoutResidueOption.FiveDays.localizedString())
    
    • Mendy
      Mendy about 5 years
      This is a very good article about localization in Swift for a robust architecture
    • Sujananth
      Sujananth about 2 years
      Localizing a enum is a bad idea. Because you cannot convert a value in localizable file to the respective enum easily. The ID in the localizable may be unique, but the value will be changing and repeating.
  • NerdyTherapist
    NerdyTherapist almost 7 years
    You must not forget to add the localized versions of the string to 'Localizable.strings', otherwise there would be no translated version.
  • Mark A. Donohoe
    Mark A. Donohoe about 6 years
    Wouldn't it make more sense to put the 'var localizedString...' in an extension of RawRepresentable where RawValue is LocalizedString so you'd get it for free on all enums that use LocalizedString as their raw value type?
  • Sn0wfreeze
    Sn0wfreeze almost 5 years
    I just talked to an Apple Engineer about localization and they recommend not to pass variables in NSLocalizedString() as it will stop the strings from beeing discovered automatically.
  • Sn0wfreeze
    Sn0wfreeze almost 5 years
    I really like your approach as it uses Apple's recommendations and just uses a different NSLocalizedString() for every string!
  • Marek H
    Marek H almost 4 years
    link doesn't exists