- iOS 9.0+
- Swift 4.1+
pod 'SKLocalizable', :git => "https://github.com/steelkiwi/SKLocalizable.git"Will be available later
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
* Avoid using parameters, wrapped in parameters syntax
* Example: $($(parameter)) - this won't be handled correctly
*
* 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 - http://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) -> StringExample:
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 - UISearchBar -
placeholder
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 SKLocalizableto your project -
How I can localize another native or mine custom component, except mentioned in #3.1?
Please check item #3.2.
If you have localized native component, not available in #3.1 list, we will be very grateful if you make a Pull Request to olesenko@steelkiwi.com with your add-on -
I want to localize several properties of component. Do you have default solution?
It's a pretty complex thing to handle automatically, so we don't.
You can use notification.languageChangedand implement this case manually
SKLocalizable is released under the MIT license
Created by Viktor Olesenko on 03.10.18
