Localizable.strings & prefix operator [duplicate] - ios

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

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

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

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.

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.

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

This is my example
enum Localization: String {
case appName = "app_name"
case appOk = "app_ok"
case appError = "app_error"
case placeholderNoContent = "placeholder_no_content"
case homeTitle = "home_title"
public func localized(args: CVarArg...) -> String {
let localizedString = NSLocalizedString(self.rawValue, comment: "")
return withVaList(args, { (args) -> String in
return NSString(format: localizedString, locale: Locale.current, arguments: args) as String
})
}
}
Usage
self.homeTitleLabel = Localization.homeTitle.localized()
This Localization enum can easily be used with string formats.

Try this protocol which I created, and you can import, use it like below.
https://github.com/Wei18/ZWExt/blob/master/ZWExt/Classes/Protocol/Localizable.swift
enum SomeKey: String, Localizable {
case MenuGreeting = "lb_menu_greeting"
case HaveBook = "I have %# books"
}
// Sample
let menuGreeting: String = SomeKey.MenuGreeting.localized()
let iHaveBoxes: String = SomeKey.HaveBook.localized([3])
/*
// You also can make it with html.
SomeKey.CustomCase.localizedHTML()
SomeKey.CustomCase.localizedHTML([])
*/

Related

Swift - How to get Raw Value from ENUM

I have the following enum:
enum MembershipLevel: Int {
case basic = 25
case bronze = 50
case silver = 100
case gold = 500
case platinum = 1000
}
which then I have the following firebase lookup:
userRef.child(userId).child("memeberlevel").observeSingleEvent(of: .value, with: { (snapshot) in
self.userRef.child(userId).child("count").observeSingleEvent(of: .value, with: { (snap) in
if((snap.value!) < MembershipLevel.(snapshot.value!).rawValue) {
completion(false)
} else {
completion(true)
}
})
})
The code above throws a complier error due to the following code:
MembershipLevel.(snapshot.value).rawValue
How can I reference the enum values dynamically, since the code snippet MembershipLevel.basic.rawValue is perfectly OK?
You should not use an enum here. Enum cases cannot be referred to dynamically.
While you could do something like this:
enum MembershipLevel: Int, CaseIterable {
case basic = 25
case bronze = 50
case silver = 100
case gold = 500
case platinum = 1000
init?(string: String) {
if let value = MembershipLevel.allCases.first(where: { "\($0)" == string }) {
self = value
} else {
return nil
}
}
}
// usage:
let userValue = snapValue.value as! Int
let membershipString = snapshot.value as! String
if userValue < MembershipLevel(string: membershipString)!.rawValue {
}
It might break in the future as I don't think "\($0)" producing the enum case name is guaranteed.
I would use a dictionary:
let membershipLevelNameDict = [
"basic": 25,
"bronze": 50,
"silver": 100,
"gold": 500,
"platinum": 1000
]
Usage:
let userValue = snapValue.value as! Int
let membershipString = snapshot.value as! String
if userValue < membershipLevelNameDict[membershipString] ?? 0 {
}
Using this dictionary, you can also create an instance of your enum:
membershipLevelNameDict[membershipString].flatMap(MembershipLevel.init(rawValue:))
But if you want to compare the raw values, just access the dictionary directly like in the first snippet.
You can make your enumeration conform to Comparable protocol and create an initializer that takes an Int:
extension MembershipLevel: Comparable {
init?(_ value: Any?) {
switch value {
case let int as Int:
self.init(rawValue: int)
case let string as String:
switch string {
case "basic": self = .basic
case "bronze": self = .bronze
case "silver": self = .silver
case "gold": self = .gold
case "platinum": self = .platinum
default: return nil
}
default: return nil
}
}
static func < (lhs: Self, rhs: Self) -> Bool {
lhs.rawValue < rhs.rawValue
}
}
Now you can initialize your enumeration from the snapshot values and compare them directly
if let lhs = MembershipLevel(snap.value),
let rhs = MembershipLevel(snapshot.value),
lhs < rhs {
completion(false)
}
} else {
completion(true)
}

How to create an enum where few cases contains extra properties

I need 4 file objects to upload. Few files have date, few files have image. What is the data structure should I use?
I tried to create an enum
enum File: String, CaseIterable {
case a = "A"
case b = "B"
case c = "C"
case d = "D"
var size: String//enum cannot contain stored properties
}
I tried to create a struct
struct File {
var name: String//for 4 files
var size: String//only for A and B
var image: UIImage// only for C and D
}
This could help you-
enum File: String, CaseIterable {
case a = "A"
case b = "B"
case c = "C"
case d = "D"
static let sizeMapper: [File: String] = [
.a: "Size A",
.b: "Size B"
]
static let nameMapper: [File: String] = [
.a: "Name A",
.b: "Name B",
.c: "Name C",
.d: "Name D"
]
static let imageMapper: [File: UIImage] = [
.c: UIImage(),
.d: UIImage()
]
var size: String {
return File.sizeMapper[self] ?? ""
}
var name: String {
return File.nameMapper[self] ?? ""
}
var image: UIImage {
return File.imageMapper[self] ?? UIImage()
}
}
call it like this-
let size = File.a.size
print(size)
let name = File.a.name
print(name)
let image = File.c.image
print(image)
The error says it all:
enum cannot contain stored properties
Ideally you're the one to provide values to your enum's properties. Meaning they should be read-only. You can however make a new instance of your enum.
Here's an example:
enum File: String, CaseIterable {
case a = "A"
case b = "B"
case c = "C"
case d = "D"
var size: String {
switch self {
case .a: return "Some size for A"
case .b: return "Some size for B"
case .c: return "Some size for C"
case .d: return "Some size for D"
}
}
}
let fileA = File(rawValue: "A")
let fileB = File(rawValue: "B")
let size = fileA.size //Some size for A
let size = fileB.size //Some size for B
If you answer is "How to create an enum with extra properties", then you can write something like this:
enum File {
case containingDate(fileName: String, date: Date)
case containingImage(fileName: String, image: UIImage)
}
Also, note that enum with raw types (in your case it is String) can't contain extra parameters.
PS: to be honest, I don't see any advantages of using enums in your situation
You should use both enum and struct here:
// Enum with associated values for differences
enum FileType {
case withDate(date: Date)
case withImage(image: UIImage)
}
// Struct to bind common
struct File {
var name: String
var fileType: FileType
}
And then you can have your files defined:
extension File {
// Files with date
static let a = File(name: "a", fileType: .withDate(date: Date()))
static let b = File(name: "b", fileType: .withDate(date: Date()))
// Files with image
static let c = File(name: "c", fileType: .withImage(image: UIImage()))
static let d = File(name: "d", fileType: .withImage(image: UIImage()))
}
And for simple way to access date and image you can declare those handy extensions:
extension FileType {
var date: Date? {
if case let .withDate(date) = self {
return date
} else {
return nil
}
}
var image: UIImage? {
if case let .withImage(image) = self {
return image
} else {
return nil
}
}
}
extension File {
var date: Date? {
return fileType.date
}
var image: UIImage? {
return fileType.image
}
}
And then use it like this:
let date = File.a.date

How to make an array string from enum member raw value in swift

I am new in programming and swift. I have an enum like this
enum City : String {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
}
Could I possibly get that city name to an array from enum raw value? I hope I can get something like this :
let city = ["tokyo","london","new york"]
Something like this.
let cities = [City.tokyo, .london, .newYork]
let names = cities.map { $0.rawValue }
print(names) // ["tokyo", "london", "new york"]
To get all enum values as an array see this.
Swift 4.0
If you want to iterate through enum you can do like this.
enum City : String {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
static let allValues = [tokyo,london,newYork]
}
let values = City.allValues.map { $0.rawValue }
print(values) //tokyo london new york
Hope this might help. Please look into this https://stackoverflow.com/a/28341290/2741603 for more detail
enum City : String {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
}
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var k = 0
return AnyIterator {
let next = withUnsafeBytes(of: &k) { $0.load(as: T.self) }
if next.hashValue != k { return nil }
k += 1
return next
}
}
var cityList:[String] = []
for item in iterateEnum(City.self){
cityList.append(item.rawValue)
}
print(cityList)
Starting from the answer by https://stackoverflow.com/users/5991255/jaydeep-vora, you can also add conformity to CaseIterable protocol and then use the allCases method
enum City : String, CaseIterable {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
}
let values = City.allCases.map { $0.rawValue }
print(values)

How to get enum from raw value in Swift?

I'm trying to get enum type from raw value:
enum TestEnum: String {
case Name
case Gender
case Birth
var rawValue: String {
switch self {
case .Name: return "Name"
case .Gender: return "Gender"
case .Birth: return "Birth Day"
}
}
}
let name = TestEnum(rawValue: "Name") //Name
let gender = TestEnum(rawValue: "Gender") //Gender
But it seems that rawValue doesn't work for string with spaces:
let birth = TestEnum(rawValue: "Birth Day") //nil
Any suggestions how to get it?
Too complicated, just assign the raw values directly to the cases
enum TestEnum: String {
case Name = "Name"
case Gender = "Gender"
case Birth = "Birth Day"
}
let name = TestEnum(rawValue: "Name")! //Name
let gender = TestEnum(rawValue: "Gender")! //Gender
let birth = TestEnum(rawValue: "Birth Day")! //Birth
If the case name matches the raw value you can even omit it
enum TestEnum: String {
case Name, Gender, Birth = "Birth Day"
}
In Swift 3+ all enum cases are lowercased
Full working example:
enum TestEnum: String {
case name = "A Name"
case otherName
case test = "Test"
}
let first: TestEnum? = TestEnum(rawValue: "A Name")
let second: TestEnum? = TestEnum(rawValue: "OtherName")
let third: TestEnum? = TestEnum(rawValue: "Test")
print("\(first), \(second), \(third)")
All of those will work, but when initializing using a raw value it will be an optional. If this is a problem you could create an initializer or constructor for the enum to try and handle this, adding a none case and returning it if the enum couldn't be created. Something like this:
static func create(rawValue:String) -> TestEnum {
if let testVal = TestEnum(rawValue: rawValue) {
return testVal
}
else{
return .none
}
}
With Swift 4.2 and CaseIterable protocol it is not that hard at all!
Here is an example of how to implement it.
import UIKit
private enum DataType: String, CaseIterable {
case someDataOne = "an_awesome_string_one"
case someDataTwo = "an_awesome_string_two"
case someDataThree = "an_awesome_string_three"
case someDataFour = "an_awesome_string_four"
func localizedString() -> String {
// Internal operation
// I have a String extension which returns its localized version
return self.rawValue.localized
}
static func fromLocalizedString(localizedString: String) -> DataType? {
for type in DataType.allCases {
if type.localizedString() == localizedString {
return type
}
}
return nil
}
}
// USAGE EXAMPLE
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let dataType = DataType.fromLocalizedString(localizedString: self.title) {
loadUserData(type: dataType)
}
}
You can easily modify it to return the DataType based on the rawValue. I hope it helps!
You can define enum like this -
enum TestEnum: String {
case Name, Gender, Birth
}
OR
enum TestEnum: String {
case Name
case Gender
case Birth
}
you can provide an init method which defaults to one of the member values.
enum TestEnum: String {
case Name, Gender, Birth
init() {
self = .Gender
}
}
In the example above, TestEnum.Name has an implicit raw value of "Name", and so on.
You access the raw value of an enumeration case with its rawValue property:
let testEnum = TestEnum.Name.rawValue
// testEnum is "Name"
let testEnum1 = TestEnum()
// testEnum1 is "Gender"
Display the rawvalue using Enum
import UIKit
enum car: String {
case bmw = "BMW"
case jaquar = "JAQUAR"
case rd = "RD"
case benz = "BENZ"
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
label.text = car.bmw.rawValue
}
}
Here is example of more useable code in swift 4.1
import UIKit
enum FormData {
case userName
case password
static let array = [userName, password]
var placeHolder: String {
switch self {
case .userName:
return AppString.name.localized // will return "Name" string
case .password:
return AppString.password.localized // will return "Password" string
}
}
}
enum AppString: String {
case name = "Name"
case password = "Password"
var localized: String {
return NSLocalizedString(self.rawValue, comment: "")
}
}
I think this is a quick and clean solution for swift 4.2 (you can c&p to playground)
import UIKit
public enum SomeEnum: String, CaseIterable {
case sun,moon,venus,pluto
}
let str = "venus"
let newEnum = SomeEnum.allCases.filter{$0.rawValue == str}.first
// newEnum is optional
if let result = newEnum {
print(result.rawValue)
}
enum withdrawBalceTimeGenrateError : String , Error{
case insufficientBalance = "Plz Check Balance"
}
withdrawBalceTimeGenrateError.insufficientBalance.rawValue // Plz Check Balance

Enum with localized string in swift

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())
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
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
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.
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.
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
This is my example
enum Localization: String {
case appName = "app_name"
case appOk = "app_ok"
case appError = "app_error"
case placeholderNoContent = "placeholder_no_content"
case homeTitle = "home_title"
public func localized(args: CVarArg...) -> String {
let localizedString = NSLocalizedString(self.rawValue, comment: "")
return withVaList(args, { (args) -> String in
return NSString(format: localizedString, locale: Locale.current, arguments: args) as String
})
}
}
Usage
self.homeTitleLabel = Localization.homeTitle.localized()
This Localization enum can easily be used with string formats.
Try this protocol which I created, and you can import, use it like below.
https://github.com/Wei18/ZWExt/blob/master/ZWExt/Classes/Protocol/Localizable.swift
enum SomeKey: String, Localizable {
case MenuGreeting = "lb_menu_greeting"
case HaveBook = "I have %# books"
}
// Sample
let menuGreeting: String = SomeKey.MenuGreeting.localized()
let iHaveBoxes: String = SomeKey.HaveBook.localized([3])
/*
// You also can make it with html.
SomeKey.CustomCase.localizedHTML()
SomeKey.CustomCase.localizedHTML([])
*/

Resources