I'm developing an iOS app with five languages.I have done all the coding and now I'm trying to switch between languages using the button.But can't change the language..I found the problem but cant find the solution.
[NSBundle setLanguage:code];
Problem this line not calling..
Any help please? thanks..
Use localization to change the language of the application without restart.
Go to project -> select your project in the document outline -> Add the new language.
Add Localized.strings files to your project.
Add the strings which are used for localization in Localizable.strings(XXX)
For English "hello" = "Hello World";
For Hindi "hello" = "नमस्ते दुनिया";
Code:
import UIKit
extension String {
var localized: String {
let lang = currentLanguage()
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
//Remove here and add these in utilities class
func saveLanguage(_ lang: String) {
UserDefaults.standard.set(lang, forKey: "Locale")
UserDefaults.standard.synchronize()
}
func currentLanguage() -> String {
return UserDefaults.standard.string(forKey: "Locale") ?? ""
}
}
enum Language: String {
case english = "English"
case hindi = "हिंदी"
}
class ViewController: UIViewController {
var language = Language.english
override func viewDidLoad() {
super.viewDidLoad()
//Initial Setup
String().saveLanguage("en")
languageLabel.text = "hello".localized
languageButton.setTitle(language.rawValue, for: .normal)
}
func updateLanguage() {
if language == .english {
String().saveLanguage("hi")
language = .hindi
} else {
String().saveLanguage("en")
language = .english
}
languageLabel.text = "hello".localized
languageButton.setTitle(language.rawValue, for: .normal)
}
#IBOutlet weak var languageLabel: UILabel!
#IBOutlet weak var languageButton: UIButton!
#IBAction func changeLanguageButtonTapped(_ sender: UIButton) {
updateLanguage()
}
}
Related
I am using two languages 1) english 2) French
In my project I need to change label language text without creating IBOutlet
I have created two string files called Localizable(English) and Localizable(French)
added text constants like this in project
code: I have created changeLanguage method and saved its language code in UserDefaults
static func changeLanguage(with : String) {
if with == "fr"{
Bundle.setLanguage("fr")
CommonUserDefaults.accessInstance.save("fr", forType: .languageCode)
}
else {
Bundle.setLanguage("en")
CommonUserDefaults.accessInstance.save("en", forType: .languageCode)
}
}
extension String {
var localizedWithLanguage : String{
NSLocalizedString(self, comment: "")
}
}
private var bundleKey: UInt8 = 0
class AnyLanguageBundle: Bundle {
override func localizedString(forKey key: String,
value: String?,
table tableName: String?) -> String {
guard let path = objc_getAssociatedObject(self, &bundleKey) as? String,
let bundle = Bundle(path: path) else {
return super.localizedString(forKey: key, value: value, table: tableName)
}
return bundle.localizedString(forKey: key, value: value, table: tableName)
}
}
extension Bundle {
class func setLanguage(_ language: String) {
defer {
object_setClass(Bundle.main, AnyLanguageBundle.self)
}
objc_setAssociatedObject(Bundle.main, &bundleKey, Bundle.main.path(forResource: language, ofType: "lproj"), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
and changing language like this in viewcontroller
class LoginVC: UIViewController {
#IBOutlet weak var emailTF: UITextField!
#IBOutlet weak var passwordTF: UITextField!
override func viewWillAppear(_ animated: Bool) {
Bundle.setLanguage("fr")
emailTF.placeholder = emailTF.placeholder?.localizedWithLanguage
passwordTF.placeholder = passwordTF.placeholder?.localizedWithLanguage
}
}
If I write emailTF.placeholder?.localizedWithLanguage like this then email and password placeholder text changing but i need to change static label text also but that is not changing why?
below o/p "Welcome back" and "Enter Your Email and Password" why these texts are not changed
how to change these label texts without crating IBOutlet, please guide me
I want to implement storyboard localization in swift language. (means I want to localization for fix label and button text)
I have already idea about NSLocalizedString but I dont want to code for fix text Label
for e.g
NSLocalizedString("Welcome", comment: "")
I have already add Localizable.strings file as well as Main.string file for particular language. but I can not success in implement Localization
Follow below steps to localize Storyboard Elements:
Click on your project.
In Localizations section click on + icon and add language that you want to localize.
Once you add language you will see String file of that language.
Go to storyboard and click on UI Element that you want to localize.
Select identity inspector, In Document portion you will see Object ID that we need to use for localize that element.
Now goto localization file that created from step 3.
In that string file you will see Object ID of elements. Change value of that Object ID key it will reflect to that particular language only.
Once you have your localisation running, you can add extensions for UI elements, introducing easy localisation for them.
For UIlabel it will looks something like this:
public extension UILabel {
#IBInspectable public var localizedText: String? {
get {
return text
}
set {
text = NSLocalizedString(newValue ?? "", comment: "")
}
}
}
The #IBInspectable allows you setting the localisation key either from the storyboard and programatically too.
The storyboard localisation is the Apple provided way, but it bugs me a lot - not the most programmer-friendly for sure.
Bhumesh
I have used this library for in - app localisation. Which is very easy to use.
https://github.com/marmelroy/Localize-Swift
Now For Storyboard support I have Created Following extension that is IBDesignable So you can easily provide localised text from storyboard itself
1 ) Add This into new swift file
import Localize_Swift
#IBDesignable class LocalizableLabel: UILabel {
#IBInspectable var table :String? // Table
#IBInspectable var key:String? // KEY
#IBInspectable var extraTextToAppend:String? // Some text need to append , if any
override func awakeFromNib() {
guard let key = key else {return}
self.text = key.localized(using: table)
NotificationCenter.default.addObserver(self, selector: #selector(setText), name: NSNotification.Name(LCLLanguageChangeNotification), object: nil)
if let extraText = self.extraTextToAppend, let text = self.text {
self.text = text + extraText
}
}
#objc func setText () {
guard let key = key else {return}
self.text = key.localized(using: table)
if let extraText = self.extraTextToAppend, let text = self.text {
self.text = text + extraText
}
}
}
#IBDesignable class LocalizableButton: UIButton {
#IBInspectable var table :String?
#IBInspectable var key:String?
override func awakeFromNib() {
guard let key = key else {return}
self.setTitle(key.localized(using: table), for: .normal)
if let attributedText = self.attributedTitle(for: .normal) {
let mutableAttributedText = NSMutableAttributedString(attributedString: attributedText)
mutableAttributedText.replaceCharacters(in: NSMakeRange(0, mutableAttributedText.length), with: key.localized(using: table))
self.setAttributedTitle(mutableAttributedText, for: .normal)
}
NotificationCenter.default.addObserver(self, selector: #selector(setText), name: NSNotification.Name(LCLLanguageChangeNotification), object: nil)
}
#objc func setText () {
guard let key = key else {return}
self.setTitle(key.localized(using: table), for: .normal)
if let attributedText = self.attributedTitle(for: .normal) {
let mutableAttributedText = NSMutableAttributedString(attributedString: attributedText)
mutableAttributedText.replaceCharacters(in: NSMakeRange(0, mutableAttributedText.length), with: key.localized(using: table))
self.setAttributedTitle(mutableAttributedText, for: .normal)
}
}
}
#IBDesignable class LocalizeUINavigationItem: UINavigationItem {
#IBInspectable var table :String?
#IBInspectable var key:String?
override func awakeFromNib() {
guard let key = key else {return}
self.title = key.localized(using: table)
NotificationCenter.default.addObserver(self, selector: #selector(setText), name: NSNotification.Name(LCLLanguageChangeNotification), object: nil)
}
#objc func setText () {
guard let key = key else {return}
self.title = key.localized(using: table)
}
}
#IBDesignable class LocalizableUITextField: UITextField {
#IBInspectable var table_placeholder :String?
#IBInspectable var key_place_holder:String?
override func awakeFromNib() {
guard let key = key_place_holder else {return}
self.placeholder = key.localized(using: table_placeholder)
NotificationCenter.default.addObserver(self, selector: #selector(setText), name: NSNotification.Name(LCLLanguageChangeNotification), object: nil)
}
#objc func setText () {
guard let key = key_place_holder else {return}
self.placeholder = key.localized(using: table_placeholder)
}
}
2) Goto Storyboard set class to label and provide the key
3) Run and test
class ViewController: UIViewController {
#IBOutlet weak var resetOutlet: MyButton! {
didSet {
resetOutlet.setTitle("RESET".localized().uppercased(), for: .normal)
}
}
}
extension String {
func localized(tableName: String = "Localizable") -> String {
if let languageCode = Locale.current.languageCode, let preferredLanguagesFirst = Locale.preferredLanguages.first?.prefix(2) {
if languageCode != preferredLanguagesFirst {
if let path = Bundle.main.path(forResource: "en", ofType: "lproj") {
let bundle = Bundle.init(path: path)
return NSLocalizedString(self, tableName: tableName, bundle: bundle!, value: self, comment: "")
}
}
}
return NSLocalizedString(self, tableName: tableName, value: self, comment: "")
}
}
I am following below link for multilanguage functionality in my app but when i am changing the language of Finnish i.e "fi" to chineese simplified i.e "zh-Hans" then it is not wokring.
Change Language in the app programmatically
Here is code:
class LanguageManager: NSObject {
var availableLocales = [CustomLocale]()
static let sharedInstance = LanguageManager()
var lprojBasePath = String()
override fileprivate init() {
super.init()
let english = CustomLocale(languageCode: GlobalConstants.englishCode, countryCode: "gb", name: "United Kingdom")
let finnish = CustomLocale(languageCode: GlobalConstants.finnishLangCode, countryCode: "cn", name: "China")
self.availableLocales = [english,finnish]
self.lprojBasePath = getSelectedLocale()
}
fileprivate func getSelectedLocale()->String{
let lang = Locale.preferredLanguages//returns array of preferred languages
let languageComponents: [String : String] = Locale.components(fromIdentifier: lang[0])
if let languageCode: String = languageComponents["kCFLocaleLanguageCodeKey"]{
for customlocale in availableLocales {
if(customlocale.languageCode == languageCode){
return customlocale.languageCode!
}
}
}
return "en"
}
func getCurrentBundle()->Bundle{
if let bundle = Bundle.main.path(forResource: lprojBasePath, ofType: "lproj"){
return Bundle(path: bundle)!
}else{
fatalError("lproj files not found on project directory. /n Hint:Localize your strings file")
}
}
func setLocale(_ langCode:String){
UserDefaults.standard.set([langCode], forKey: "AppleLanguages")//replaces Locale.preferredLanguages
UserDefaults.standard.synchronize()
self.lprojBasePath = getSelectedLocale()
}
}
class CustomLocale: NSObject {
var name:String?
var languageCode:String?
var countryCode:String?
init(languageCode: String,countryCode:String,name: String) {
self.name = name
self.languageCode = languageCode
self.countryCode = countryCode
}
}
I created a UITextField where, when the user writes a link and enters, my textfield disappears and my webView appears.
What I am trying to do is, when the user writes the first time their link, the textfield saves that link and when the user opens again the app, the web view opens directly from the last link that the user wrote in the textfield. Basically the stored link should run the second time.
Here is all my code:
import UIKit
import Foundation
let urlKey = "User URL"
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
self.webView.hidden = true
self.textField.addTarget(self, action: #selector(ViewController.textFieldDidUpdate(_:)), forControlEvents: UIControlEvents.EditingChanged)
if doesURLExist() {
self.textField.text = getURL()
}
}
// Text Field Delegate
func textFieldDidUpdate(textField: UITextField)
{
// Remove Spaces
textField.text = textField.text!.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)
// Validate URL
NSURL.validateUrl(textField.text, completion: { (success, urlString, error) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if (success)
{
self.saveURL(urlString!)
self.webView.hidden = false
self.textField.hidden = true
let request = NSURLRequest(URL: NSURL(string: urlString!)!)
self.webView.loadRequest(request)
}
else
{
self.webView.stopLoading()
self.webView.hidden = true
}
})
})
}
#IBAction func dismissKeyboard(sender: AnyObject) {
self.resignFirstResponder()
self.view.endEditing(true)
}
func saveURL(urlString: String) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(urlString, forKey: urlKey)
}
func getURL() -> String {
let defaults = NSUserDefaults.standardUserDefaults()
let urlString = defaults.objectForKey(urlKey) as! String
return urlString
}
func doesURLExist() -> Bool {
let defaults = NSUserDefaults.standardUserDefaults()
guard let _ = defaults.objectForKey(urlKey) where defaults.objectForKey(urlKey) is String else {
return false
}
return true
}
}
Here is my project in GitHub: https://github.com/anappleapp/NSURLvalidation
You'll want to check if the url exists first by calling doesURLExist, if it does, you opt out of presenting that textfield. If it does not exist, call saveURL. NSUserDefaults provides a simple means to store lightweight data.
let urlKey = "User URL"
func saveURL(urlString: String) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(urlString, forKey: urlKey)
}
func getURL() -> String {
let defaults = NSUserDefaults.standardUserDefaults()
let urlString = defaults.objectForKey(urlKey) as! String
return urlString
}
func doesURLExist() -> Bool {
let defaults = NSUserDefaults.standardUserDefaults()
guard let _ = defaults.objectForKey(urlKey) where defaults.objectForKey(urlKey) is String else {
return false
}
return true
}
So your class should look something like:
import UIKit
import Foundation
let urlKey = "User URL"
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
self.webView.hidden = true
self.textField.addTarget(self, action: #selector(ViewController.textFieldDidUpdate(_:)), forControlEvents: UIControlEvents.EditingChanged)
if(doesURLExist) {
self.textField.text = getURL()
}
// Demo UI Settings
}
}
// Text Field Delegate
func textFieldDidUpdate(textField: UITextField)
{
// Remove Spaces
textField.text = textField.text!.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)
// Validate URL
NSURL.validateUrl(textField.text, completion: { (success, urlString, error) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if (success) {
self.saveURL(urlString)
self.webView.hidden = false
self.textField.hidden = true
let request = NSURLRequest(URL: NSURL(string: urlString!)!)
self.webView.loadRequest(request)
} else {
self.webView.stopLoading()
self.webView.hidden = true
}
})
})
}
Don't forget to add the original functions to your class.
You should save the entered string to user defaults. When your app opens you should check user defaults to see if there's already a saved string.
Swift 2 code to save your URL to user defaults:
NSUserDefaults.standardUserDefaults().setObject(urlString!, forKey: "EnteredURLString")
Swift 2 code to check whether there's a saved URL string:
if let urlString = NSUserDefaults.standardUserDefaults().stringForKey("EnteredURLString") {
}
I have a short bit of code that is meant to take a string, find words in a library, and replace them from a random array of other words. For some reason, when I hit the button, nothing happens! I made it work in the playground, so what am I doing wrong?
App code:
import UIKit
extension Array {
func randomItem() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
extension String {
func replace(target: String, withString: String) -> String
{
return self.stringByReplacingOccurrencesOfString(target, withString: withString, options: NSStringCompareOptions.LiteralSearch, range: nil)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
maintext.text = "The Dog Chases the Ball"
self.submit.setTitle("Change It", forState: UIControlState.Normal)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet var maintext: UITextView!
#IBOutlet var submit: UIButton!
#IBAction func submitTapped(sender: UIButton) {
self.submit.setTitle("Again", forState: UIControlState.Normal)
var AllWords = maintext.text
var WordsArray = AllWords.componentsSeparatedByString(" ")
var Dog = ["Pup", "Canine", "Wolf"]
var Ball = ["Frisbee", "Stick", "Car"]
AllWords.replace("Dog", withString: Dog.randomItem()).replace("Ball", withString: Ball.randomItem())
}
}
Play Ground:
import UIKit
import Foundation
extension Array {
func randomItem() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
extension String
{
func replace(target: String, withString: String) -> String
{
return self.stringByReplacingOccurrencesOfString(target, withString: withString, options: NSStringCompareOptions.LiteralSearch, range: nil)
}
}
var Text = "The Dog Loves to Chase the Ball"
//: Old Text Loaded as an array
var AllWords = Text.componentsSeparatedByString(" ")
//: New Word Library
let Dog = ["Pup", "Canine", "Wolf"]
let Ball = ["Frisbee", "Stick", "Car"]
for element in AllWords {
Text.replace(element, withString: Dog.randomItem())
Text.replace(element, withString: Ball.randomItem())
}
Text.replace("Dog", withString: Dog.randomItem()).replace("Ball", withString: Ball.randomItem())
Thank you!
You're copying maintext.text into AllWords and then replacing the text in AllWords. AllText now has a copy of what's in maintext.text, but is not pointing to the textview's text property. After replacing the text in AllWords, you should assign AllWords to maintext.text:
var AllWords = maintext.text
var WordsArray = AllWords.componentsSeparatedByString(" ")
var Dog = ["Pup", "Canine", "Wolf"]
var Ball = ["Frisbee", "Stick", "Car"]
AllWords.replace("Dog", withString: Dog.randomItem()).replace("Ball", withString: Ball.randomItem())
maintext.text = AllWords // You need set the textview's text here
If that still fails, try jumping back on the main thread when setting the text:
dispatch_async(dispatch_get_main_queue(), { () -> Void in
maintext.text = AllWords
})
The type String in Swift is a value type that means that any change creates a new instance of String.
The string of the text view is left unchanged.
So after the replace task you have to reassign the value to the text view
maintext.text = AllWords
PS: please name variables with a first lowercase letter