Creating a localized iPhone app but allowing the user to change language for the application - iphone

I'm working on a localized app and everything is working fine. The problem is I want to allow the user to specifically select the lenguage for the specific app, in the app settings folder. This should users that their phone is set to one language (e.g. french) to set the app to work in English.
I'm currently using NSLocalizedString to get localized string but looking through all variation of the macro I can't find one that will let me specify the language.
Any ideas on how to do it?

There are three issues here:
Strings
Other resources (including NIBs)
System messages
The last is almost certainly not fixable, so we will leave it be. They're going to show up in the device language.
The other two are solvable, but you will need to do more things by hand. For strings, instead of creating a single Localizable.strings and then localizing it, create completely separate tables (English.strings, French.strings, etc.) Then, use NSLocalizedStringFromTable(), passing the language as the table.
For NIBs, there are two approaches. You can put each set of localized NIBs into its own Bundle and then pass that Bundle rather than nil to -initWithNibName:bundle:. Alternately, you can hand-load the NIBs after finding them with [NSBundle -pathForResource:ofType:inDirectory:forLocalization:].

There is a better way to do this. You can force the language like so:
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:#"en", nil] forKey:#"AppleLanguages"];
And undo this setting by:
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"AppleLanguages"];
NB. you will normally have to restart the app for this to take affect.
Consider if you need to call [[NSUserDefaults standardUserDefaults] synchronize];
I agree there is little need to allow the user to specify a language. However the one exception is being able to override the language and set it to the developer's native language. If the user can speak the developer's language (e.g. English for me) then they may wish to use the App in that language, if the translations are incorrect.
I reference this answer: How to force NSLocalizedString to use a specific language (the answer doesn't actually work for me, but following the ideas in the comments did. The undo stuff I worked out.

The trick to use specific language by selecting it from the app is to force the NSLocalizedString to use specific bundle depending on the selected language ,
here is the post i have written for this http://learning-ios.blogspot.com/2011/04/advance-localization-in-ios-apps.html
and here is the code of one sample app https://github.com/object2dot0/Advance-Localization-in-ios-apps

The correct "User experience" is for the user to select their language via the system preference panel; not your app (or your app's settings panel, etc.). There is no way to override this per-app and we wouldn't want any app changing the system wide setting.

Related

iOS:How to get current application language

The application that I'm working on supports 3 languages: English, French and German.
How I can get the current application language (NOT the device language)?
The problem is that I have to get the current language of the application in order to send it with a request to the server and the respond should be in the right language. The device language is useless because if the user switch the os language to Italian, the app is running in English and I need to send english to the server.
Thanks
The accepted answer is a workaround.
Regarding language preferences in the device itself, you have the
[NSLocale preferredLanguages]
which will give you an ordered array of the preferred languages as defined in the system's General Settings.
The Cocoa Touch framework will take this list of preferred languages into account when loading the app's localization resources and filter it according to the translations you provide in the bundle.
This filtered and ordered list of localized languages can be obtained with
[[NSBundle mainBundle] preferredLocalizations]
Your server requests should match the first value in this array or you will have a language mismatch between app and server data.
What i always do:
Add a string entry into the Localizable.strings files.
I always use the key "lang"="de"; (or "lang"="en", etc.).
Then you can use it in your NSURLRequest by adding the language over NSLocalizedString(#"lang", #"")
With that method you have absolute control what is going to be sent to you backend.
You may use the preferredLocalizations method of the NSBundle class:
NSString *currentLocalization = [[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0];
Since iOS 13 the language can be set for each app individually.
In the session "Creating Great Localized Experiences with Xcode 11" at WWDC19 they showed two options for determining the user settings for the application.
Get the currently running application language:
Bundle.main.preferredLocalizations.first
Get the best language, given an external language code list:
let availableLanguages = Server.requestLanguages()
Bundle.preferredLocalizations(from: availableLanguages).first
The app language will change when the user change the device language and you can get it from the NSUserDefaults (i will show you how if you want to ) unless there are an option to change the language inside the app then you can easy save the current used language and send it to the server when ever you want.

How to display these items in other language?

I search around the web, but I can't find the answer, maybe this question is too simple.
In the app I developed, how to display those default items, such as back BarButtonItem, ImagePicker buttons, or in the UITextView, the pop-up menu with copy, paste, cut, replace and select all, I can't figure out how to display these items in other language, for example, Chinese, Japanese etc.
Thanks.
Attribute in *-Info.plist "Localization native development region" is responsible for another thing.
CFBundleDevelopmentRegion (String - iOS, Mac OS X) specifies the native region for the bundle. This key contains a string value that usually corresponds to the native language of the person who wrote the bundle. The language specified by this value is used as the default language if a resource cannot be located for the user’s preferred region or language.
(Source)
Solutions:
In my app I have custom localization (not standard mechanism with *.lproj). And to say the system that I can handle localization manually with some languages, I set the CFBundleLocalizations (in Xcode "Localizations"). It's array property where you can specify handled languages. In Xcode could be problems to set Localizations attribute to type 'array', solution here.
CFBundleLocalizations (Array - iOS, Mac OS X) identifies the localizations handled manually by your application. If your executable is unbundled or does not use the existing bundle localization mechanism, you can include this key to specify the localizations your application does handle.
Each entry in this property’s array is a string identifying the language name or ISO language designator of the supported localization.
(Source)
UIKit is a framework and has localized resources (such pop-up menu of uitextfield). In Bundle settings(*-Info.plist) you can set CFBundleAllowMixedLocalizations (set to YES) to retrieve those resources.
CFBundleAllowMixedLocalizations (Boolean - iOS, Mac OS X) specifies whether the bundle supports the retrieval of localized strings from frameworks. This key is used primarily by Foundation tools that link to other system frameworks and want to retrieve localized resources from those frameworks.
(Source)
Read up on the internationalization technology:
http://developer.apple.com/internationalization/
In particular, the thing you're talking about is known as "Localization".
I just found this tutorial via google:
http://www.switchonthecode.com/tutorials/a-simple-localization-example-for-the-iphone
N.B. "internationalization" is sometimes written as "I18N" because it is such a long word! (Weird acronym, but it's because the word is an 'I' followed by 18 letters and then an 'N')
I wrote a tutorial on this where I stepped through how to do this (based on an application I localized in six languages):
Developing localized iPhone applications
http://www.pushplay.net/2009/08/developing-localized-iphone-applications/
I also described some of the language/idiom issues I was dealing with, which might be useful to keep in mind. There is the technical problem of how to implement multiple languages, but there is also the communication problem of having things be nonsensical when directly translated out of context...
EDIT: to respond to your comment, I'd still do it the same way (localizing everything). If not, set it up to localize anyway, but only translate the specific strings you want (and leave the rest as English).
If you really want to do everything yourself, you can do this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *languages = [defaults objectForKey:#"AppleLanguages"];
NSString *currentLanguage = [languages objectAtIndex:0];
Then you can compare the currentLanguage variable and display the proper text in an if/then/else if/etc code block. I'd probably recommend having an "else display in english" at the end of the code block, just in case the language returned is different from what you were expecting.
EDIT: I just re-read what you were asking, and realized you meant localizing system functions. That's not something you can control -- when a user goes into their Settings and changes their language, all of those system interfaces (like "Cut/Copy/Paste") will automatically be localized. As long as they set their proper language in their Settings, that should work automatically.

make the language of my IPHONE app change from the app settings without changing the language of the device

I want to make the language changeable from the application settings. For that I made a settings.bundle and i forced the langauge using this code How to force NSLocalizedString to use a specific language , I also put the UIApplicationExitsOnSuspend to YES to make my app reload every time it goes to the background.
It partially worked for me but I have 2 problems :
- The splash screen (which is localized) is not changing with this method.
- I must enter/goBack twice to make the language change effective on the application Views an this only on the OS 4 and higher.
Has anyone a solution for that?
You need to create you own translation framework for that ! Sorry !
Unfortunately there's no built-in way. What would be probably easiest would be integrate service like GetLocalization.com, so that your app would download language file via API (then it's easy to add new languages and translations even app is already released). Then what you need is just simple loader that loads that language file to memory and function that replaces the original string with translated one in run-time. You can calculate hash for original string so it's fast to search them, good algorithm for this is Peter Weinberger's hashPJW.
The trick to use specific language by selecting it from the app is to force the NSLocalizedString to use specific bundle depending on the selected language ,
here is the post i have written for this http://learning-ios.blogspot.com/2011/04/advance-localization-in-ios-apps.html
and here is the code of one sample app https://github.com/object2dot0/Advance-Localization-in-ios-apps

How to migrate NSUserDefaults in new release?

I have an iPhone app that stores some settings using NSUserDefault standardUserDefaults.
When I add new features to the app I need to add new default settings and so I have to migrate/upgrade the NSUserDefaults. For now, I just store the version number and check this when the app is launched. It has quickly become very messy as I have to add lots of if statements. I cannot assume that the user is just upgrading from the previous version but perhaps even a couple of versions before.
I like the way that CoreData seems to handle migrating table changes but I want to provide 2.2.1 SDK compatibility and of course CoreData is not the same thing as NSUserDefaults.
Any suggestions or best practices?
Hmm… I wouldn't "upgrade" the user defaults in this way, but instead query NSUserDefaults as usual, only if the default isn't set (objectForKey will return nil in that case), use a default value (which may then be written back to the user defaults to save time the next time). Since you'll have to do this every time a "new" default (i.e. one that didn't exist in 1.0) is read, I advise doing a wrapper class/file that does this, this way the code is written only once for each "new" default.
Also, while the question/problem is different, this answer works just as well in your case.

Classes that mimic the behavior of iPhone Application Settings...but within an app?

Has anyone written iPhone classes that mimic the behavior of the Application Settings? It would be nice to be able to define settings tables for use within my app using exactly the same XML structure, etc.
See Is there a library or framework for setting preferences from within an iPhone application?
Esp. the mySettings library mentioned there:
mySettings [...] uses a plist
configuration file like the one used
by the settings app, with some added
options.
[...]
By default the settings themselves are
stored in the standard user defaults
object ([NSUserDefaults
standardUserDefaults]), but you can
use any object that supports key-value
coding. This enables you to use your
model classes directly in the settings
view.
The application settings (or NSUserDefaults) is essentially a glorified NSDictionary. You add objects to the settings and associate them with keys, so you can retrieve them later on.
If you want to do this, just create a class which wraps a singleton instance of a NSDictionary. That way you could reference it throughout your app like:
[[MyAppSettings sharedInstance] objectForKey:key];