Localized text not showing for Privacy alert in iOS - ios

I am developing an iOS application which supports English and Arabic. User can change the application language from inside the app.
When user changes the language I am setting it like ,
//change app language
UserDefaults.standard.set([language], forKey: "AppleLanguages")
currentLanguage = language
UserDefaults.standard.synchronize()
//current language updating
var currentLanguage : String{
get{
if let selectedLanguage = UserDefaults.standard.string(forKey: "selectedLanguage"){
return selectedLanguage
}else{
let language = Locale.preferredLanguages[0]
if language.hasPrefix("ar"){
return SupportedLanguage.ar.rawValue
}else{
return SupportedLanguage.en.rawValue
}
}
}
set{
UserDefaults.standard.setValue(newValue, forKey: "selectedLanguage")
}
}
In this way, App is not exiting. Just reloading the root view controller
The issue I am facing is, when I change the application language like this, the privacy alerts like “..requesting permission for using Location”, “… would ,like to use Photo album” etc are not showing in the selected language. I have created InfoPlist.string files for English and Arabic and added like
NSCameraUsageDescription = ".... would like to access Camera";
NSLocationAlwaysAndWhenInUseUsageDescription = ".... wants to use your current location for better usability";
Still its not showing. Also I tried deleting, cleaning app, deleting derived data.
Any idea why its happening?

Changing AppleLanguages key needs app to be restarted so new localization applied , you can try to use NSLocalizedString with tableName or change current bundle you read from , but system localization won't be changed until app restarted

Related

Cannot access localized resource from app extension

I'm having trouble accessing localized resources from my Intents extension.
I've got a couple of JSON files that are localized into different languages in a framework that's shared between my app and the extension. When I access them though my app via path(forResource:ofType), it accesses the proper localized version, but if I call it from the extension in the Shortcuts app, then it returns the English, even if both the app itself and the Shortcuts app are both set to another language.
The only way I can get the correct language to appear is if I change the device language - changing it just for the app or for Shortcuts doesn't have an effect.
I'm building with the iOS 14 SDK, but the same behavior occurs on both iOS 13 and 14.
Firstly, please check Target Membership of Localizable file, make sure it was checked your extension.
Secondly, save your app language in UserDefaults which have AppGroups.
static func setLanguageExtension(_ language: String, forKey: String) {
if let userDefault = UserDefaults(suiteName: "APP_LANGUAGE") {
userDefault.setValue(language, forKey: forKey)
}
}
static func getLanguageExtension(key: String) -> String? {
if let userDefault = UserDefaults(suiteName: "APP_LANGUAGE") {
return userDefault.string(forKey: key)
} else {
return "en-US"
}
}

Localisation Settings Not Changing the System Defined Text in Swift 4.2

I have 2 lang support for my app
1) English - en
2) German - de
I have done all the procedures for localization, the only issue is
whenever I change the language from "en" to "de" or vice versa then
after the system text is not changing to the latest lang, but it
reflects when I kill the app and reopen it.
For Example:
The popover Copy-LookUp-Share is not localsied to German Lang. but the other things from .string file are lcoalised properly.
My change lang code:
func setLanguage(languageCode:String) {
var appleLanguages = UserDefaults.standard.object(forKey: "AppleLanguages") as! [String]
appleLanguages.remove(at: 0)
appleLanguages.insert(languageCode, at: 0)
UserDefaults.standard.set(appleLanguages, forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
if let languageDirectoryPath = Bundle.main.path(forResource: languageCode, ofType: "lproj") {
bundle = Bundle.init(path: languageDirectoryPath)
} else {
resetLocalization()
}
}
func resetLocalization() {
bundle = Bundle.main
}
FYI: Similar thing happens in 'WeChat' iOS application.
Those are UIKit SDK private menu items and you cannot control them, probably they are just created once then kept cached.
The only thing you can try is to force update in the moment of language change, like
UIMenuController.shared.update()
Note: actually using "AppleLanguages" key for that purpose is not documented, so it is kind of hucky. L10N is system settings originated feature, by design.

Swift - how to get ios device current language

My device running iOS 12 and has English language as its primary language and Hebrew as it's secondary language.
Now I'm opening my application with English as it's Base localization.
In the application I have list of three languages: English, Hebrew and French.
At first the attribute Locale.preferredLanguages result is: ["en-IL", "he-IL", "fr-IL"]
If I want to localize my entire application without to change the operating system language I'm changing only AppleLanguages array on UserDefaults like that:
func currentAppleLanguage() -> String {
return (UserDefaults.standard.object(forKey: "AppleLanguages") as? NSArray)?
.firstObject as? String ?? ""
}
func setAppleLanguageTo(lang: String) {
UserDefaults.standard.set([lang], forKey: "AppleLanguages")
}
And after I make that change I'm restarting my application and the language changes.
The thing is after I change AppleLanguages at UserDefaults for example to "he", the Locale.preferredLanguages attribute turns into: ["he"]
So now I don't have the preferredLanguages fallback localizations that set on the operating system at the settings application.
Moreover, I'd like to now how can I get the current running device language on the operating system even after I change the application language with AppleLanguages like facebook does
I'd like to mention I notice that when I edit the running scheme of Application Language to another language it changes the AppleLanguages as well.
Instead of replacing the array of "AppleLanguages" with just a single value, update the existing list so the specified language is moved to the top.
func currentAppleLanguage() -> String {
return UserDefaults.standard.stringArray(forKey: "AppleLanguages")?.first ?? ""
}
func setAppleLanguageTo(lang: String) {
// Get the current list
var languages = UserDefaults.standard.stringArray(forKey: "AppleLanguages") ?? []
// Get all locales using the specified language
let matching = languages.filter { $0.hasPrefix(lang) }
if matching.count > 0 {
// Remove those entries from the list
languages.removeAll { $0 == lang }
// Add them back at the start of the list
languages.insert(contentsOf: matching, at: 0)
} else {
// It wasn't found in the list so add it at the top
languages.insert(lang, at: 0)
}
UserDefaults.standard.set(languages, forKey: "AppleLanguages")
}
This keeps the full list. It just reorders the values so the desired language is first.

UITests-How to get current language in UITests

XCUIApplication *app = [[XCUIApplication alloc] init];
[app.buttons[#"login"] tap];
See as above, in tests.m file, I want get login button by string "login", But, my app support multi-languages. How to get current language of my app.
The method below is the method how I get current Language. User set language in setting view of my app. I store current Language in NSUserDefaults.
+ (NSString*)currentLanguage
{
NSUserDefaults *lanUser = [NSUserDefaults standardUserDefaults];
NSString *currentLanguage = [lanUser valueForKey:kCNOGASingularLanguage];
if([currentLanguage length]==0)
currentLanguage = kApplicationLanguageEnglish;
return currentLanguage;
}
Storing the user's language in your app's UserDefaults and then accessing that from your UITest won't work. Your app and the UITest are running as separate processes, which means that your UITest cannot access your app's UserDefaults
There is a simple solution: To become independent from the users language you can set the accessibilityIdentifier on your UIButton and then access the button via the accessibilityIdentifier:
In your app:
button.accessibilityIdentifier = #"login";
In your UITest:
[app.buttons[#"login"] tap];
The accessibilityIdentifier is never displayed and VoiceOver also does not read it, so it does not have to be localized. Just make sure you are using accessibilityIdentifier and not accessibilityLabel. Because accessibilityLabel will be read by VoiceOver for handicapped users and should be localized.
ALTERNATIVE
If you cannot use accessibilityIdentifier to query your UI elements you could use your app's LocalizableStrings file in your UITests to get the localized button title (in this case):
First add your Localizable.strings file to your UITest target, then access the file via the UITest bundle to get the localized strings (I use a little helper method for that):
func localized(_ key: String) -> String {
let uiTestBundle = Bundle(for: AClassFromYourUITests.self)
return NSLocalizedString(key, bundle: uiTestBundle, comment: "")
}
I wrote a little blog post about this a while back with some more details.

iOS 9 ShareData between AppGroup through NSUserDefaults

I'm trying to add TodayExtension to my existing app and I want that app and extension will share data through NSUserDefaults.
According to this tutorial http://www.glimsoft.com/06/28/ios-8-today-extension-tutorial/
I did like it say:
Create new target with Today extension
Add new App-Group in Extension and App
Add code to app
Add code to extension
App Code
func updateExtensionData() {
let key = "identityCode"
let valueForStore:Float = 2.0
let extensiontDefaults = NSUserDefaults(suiteName: "group.company.mineSharedDefaults")
extensiontDefaults?.setFloat(valueForStore, forKey: key)
let results = extensiontDefaults?.synchronize()
let checkCode = extensiontDefaults?.objectForKey(key)
print("code for save \(valueForStore) synchronize result \(results) readed code \(checkCode!)")
}
Extension code
private func updateData() {
let key = "identityCode"
let extensionDefaults = NSUserDefaults(suiteName: "group.company.mineSharedDefaults")
let checkCode = extensionDefaults?.floatForKey(key)
print("synchronize result \(resut) readed code \(checkCode!)")
}
Issue
My issue is that extension always read 0 instead of 2. Clearly I'm missing something. There is some major difference between iOS8 and 9 in case of AppGroup and NSUserDefaults? I know that there is but between app an watchOS2.
For being more clear this is keys from App and Extension NSUserDefaults
App Keys
AppleKeyboards
AppleKeyboardsExpanded
AddingEmojiKeybordHandled
AppleLanguages
ApplePasscodeKeyboards
PreferredLanguages
AppleLocale
NSInterfaceStyle
MSVLoggingMasterSwitchEnabledKey
NSLanguages
AppleITunesStoreItemKinds
identityCode
AppleLanguagesDidMigrate
PKEnableStockholmSettings
Extension Keys
AppleKeyboards
AppleKeyboardsExpanded
AddingEmojiKeybordHandled
AppleLanguages
ApplePasscodeKeyboards
PreferredLanguages
AppleLocale
NSInterfaceStyle
MSVLoggingMasterSwitchEnabledKey
NSLanguages
AppleITunesStoreItemKinds
AppleLanguagesDidMigrate
PKEnableStockholmSettings
It's clear that key identityCode is not appearing in extension at all.
Ok I'm not sure what helps but I've done two thing:
rename current AppGroup identifier and create new one with name group.my.entire.bundle.id.of.my.app.sharedData instead of group.appname.sharedData
remove and create new AppGroup identifier
I can use "identityCode" as key.
CThere could be several things!
first; did you allow keychain sharing under "Capabilities" for both your container app and the extension, and did you set the Keychain groups to the same group?
I recommend using this: https://github.com/kefbytes/KeychainWrapper
download it, it's very simple, just add the swiftfile to your project
allow keychain sharing under cabailities,
set your serviceName and accesgroup like this
KeychainWrapper.serviceName = "give it a name in here"
(servicename is neccessary for it to work)
KeychainWrapper.accessGroup = "write your accessgroup name here"
(accesgroup is optional, since you are setting it under Capabilities)
save a value to a key like this
KeychainWrapper.setString("2", forKey: "identityCode"
and retrieve with
KeychainWrapper.stringForKey("identityCode")

Resources