- iOS 9.0+
- Swift 4.1+
pod 'SKLocalizable', :git => "https://github.com/steelkiwi/SKLocalizable.git"
dependencies: [
.package(url: "https://github.com/steelkiwi/SKLocalizable.git", from: "1.0.1")
]
Just copy Sources
Folder to your Project
Localizable.strings forming recommendations:
/*
* 1. Group logic blocks to sections. Ex: Enums, Errors, Screens, etc
*
* 2. Section name format:
* // ======
* // 'MARK: - Section Name
* // ======
*
* 3. Separate subsections using 2 newlines and MARK
*
* 4. Lines format:
* /* *'/ - comment about string
* "Key" = "Value";
*
* Optional: 'Key' should contain name of screen and identifier (any number of words), separated with "." symbol
*
* 5. Parameters in strings should be named using format:
* $(ParameterNameKey) - it will be automatically replaced with value from passed dict while localization
*
* 6. Localize plural numbers in Localizable.stringsdict
* How to use plural localization - https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html
* Plural rules - https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
*/
Localizable.strings file example:
// ======
// 'MARK: - Demo screen
// ======
// MARK - Language Buttons
/* Button, that switches to English language */
"DemoVC.LanguageButton.English" = "English";
/* Button, that switches to Russian language */
"DemoVC.LanguageButton.Russian" = "Русский";
// MARK - UI Components
/* Label, whose text value is set in the code */
"DemoVC.CodeLabel.Text" = "Label - Code";
/* Label, whose text value is set in the IB file */
"DemoVC.IBLabel.Text" = "Label - Storyboard";
/* Button, whose title value is set in the IB file */
"DemoVC.Button.Title" = "Button - Storyboard";
/* TextField, whose placeholder value is set in the IB file */
"DemoVC.TextField.Placeholder" = "TextField - Storyboard - Placeholder";
/* TextField, whose text value is set in the code */
"DemoVC.TextField.Text" = "TextField - Code - Text";
localizationFile
value is used for defining specific .strings
file.
Default value is nil
, so Localizable.strings
file will be used
Use one of the described options for components localization:
label.localizationKey = "DemoVC.CodeLabel.Text"
You can set localized value directly in code using String localized
method:
/// Get self as key and return related localized value
///
/// - Parameter tableName: .strings file name
/// - Parameter bundle: language bundle, from where should be taken value. Default is Bundle.localiztion
/// - Parameter arguments: JSON, where:
/// - key - is key in localized string value from .strings file
/// - value - string, which will be used for replacing key in localized string
/// - Returns: localized value if found. Key (self) otherwise
public func localized(tableName: String? = nil, bundle: Bundle = .localization, arguments: Dictionary<String, Any>? = nil) -> String
Example:
textField.text = "DemoVC.TextField.Text".localized()
If you need to use plurals in your project, use String method localizedPlural
:
/// Load formatted string value from tableName.stringsDict and paste arguments at corresponding places in format string
///
/// - Parameter tableName: .strings file name
/// - Parameter arguments: set of parameters, which should be use for plural replacement
public func localizedPlural(tableName: String? = nil, arguments: CVarArg...) -> String {
return String.localizedStringWithFormat(self.localized(tableName: tableName), arguments)
}
Example:
Localizable.stringsdict
contains the following key:
<key>Date.UnitPlural.Hour</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@hours@</string>
<key>hours</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%d hour</string>
<key>other</key>
<string>%d hours</string>
</dict>
</dict>
Usage: "Date.UnitPlural.Hour".localizedPlural(arguments: hoursIntValue)
Date.UnitPlural.Hour
will return "%d hour" string if hoursIntValue
== 1 and "%d hours" otherwise. After that, value hoursIntValue
will be inserted at %d
place and we will receive "1 hour" / "5 hours" result string
Useful links about plurals:
For language changing use Bundle.localization. It's default value is .main
Setting new language: Bundle.localization = Bundle.init(languageCode: languageCode)
, where languageCode
is 2-symbol presentation of language. If you're not sure about code, you can take it from .lproj
folder name after adding a new project language
When new value is set, languageChanged
, notification will be send via NotificationCenter.default
Components with defined localizationKey
will change their value automatically
You can also subscribe to languageChanged
notification and handle this event manually:
NotificationCenter.default.addObserver(self, selector: #selector(languageChanged(_:)), name: .languageChanged, object: nil)
@objc
private func languageChanged(_ notification: Notification) {
// Manually set localized values
}
Localizable
protocol is just a list of required methods for localization. It isn't used directly because of external module.
UIView
implement methods from Localizable
protocol, so any of it's subclasses can expand localization functionality
There exist components that are already localized:
- UILabel -
text
- UIButton -
title
- UITextField -
placeholder
- UIViewController -
title
- UITabBarItem -
title
- UIBarButtonItem -
title
All mentioned components implement required method localize
in which they set localized text to corresponding property in a required way.
Example of UILabel implementation:
@objc
public override func localize() {
self.text = localizationKey?.localized(tableName: self.localizationFile)
}
Also this components overrides localizationKey
property of UIView
. The main purpose of this override - is make property IBInspectable
You can easily append Localizable
behaviour to any custom component of yours
Just make an extension which will override localize
method and will set localized value to corresponding field of your component. The only thing in this case is you should save open
access modifier.
Example:
extension UIPickerView {
open override func localize() {
// Localization logic here
}
}
Also, you can add IBInspectable
attributes to localizationKey
or localizationFile
via overriding (check UILabel+Localizable.swift file for example)
Just override localize
method. Saving open
is not required.
Example:
class CustomButton: UIButton {
override func localize() {
// Localization logic here
}
}
In this case, you can duplicate NSObject+Localizable.swift implementation for your class
-
XCode error
Value of type .. has no member ..
It happens when module wasn't imported automatically. Just addimport SKLocalizable
to your project -
How I can localize another native or mine custom component, except mentioned in #3.1?
Please check item #3.2.
If you localized native component, we will be very grateful if you make a Pull Request to [email protected] with your add-on -
I want to localize several properties of component. Do you have default solution?
It's a pretty difficult thing to handle automatically, so we don't.
Use notification.languageChanged
and implement this case manually
SKLocalizable is released under the MIT license
Created by Viktor Olesenko on 03.10.18