I’m maintaining two constants files in my app where in which one file contains all the constants defined using #define and other file contains all the constants defined using:
static NSString const *holidaysearch = #"holidaySearch"
Basically I use them for forming the URL for server calls. Our app is already in app store. So in a update to my app that I published last week I have made changes to both constant file by modifying the values only.
For e.g.:
static NSString const *holidaysearch = #"getholidaySearch"
(When means in real time in my app I have changed a url). But when user is updating the app and running the app the constant is still referring to the old value (i.e holidaysearch) due to which we are facing some issues. But it's working fine when the user is installing latest version directly. So is there any concept of iOS caching the constants file while updating the app or anything else needed to be done? Suggest me fix for this.
I would start by changing all of your #define constants to NSString * const. Also, static NSString const *holidaysearch should be static NSString * const holidaysearch. Here's an explanation as to why.
I am thinking how to update all UILabel having storyboard and UITableviewController as subview while i change language in select language page of my app not device language.
can anyone help me how can i resolved this issues
You can manage this using Micro Like this
open .pch file in supporting file and write down like this and one more thing you need to decide languageId first that you pass as "number"
#define kCancelText(number) (number == 1 ? #"Cancel" : (number == 2 ? #"取消" : (number == 4 ? #"cancelar" :#"Cancel" )))
after define this you need to call like this
NSLog(#"Varible Print :: %#", kCancelText(1)); //Note : here 1 is language id .. I have define three language . 1. English 2. Mandarin 3. Spanish
You can use according to language id .. One more thing you don't need for import any this for this because its always global in app.
For this you can use NSNotificationCenter through which you can post your Notification with a key, now in all the classes where you need to make changes after posting this notification, you can just add a NSNotificationObserver with the same key, from where you can call a method and do changes what so ever you require.
For using NSNotification use this link.
For that you have to this steps:
1>first if you have two language like arabic and English then add two .strings file
Messages_en.strings
Messages_ar.strings
// this is Messages_en.strings file in this declare key and value
Messages_en.strings
"answer" = "Answer";
Messages_ar.strings
"answer" = "الإجابة";
2> make all label custom label like MyUILabel_custom and add all labels in all screens of this class so if you want to modify something in viewwillappear like in arabic you want to change alignment you can do in that
3> when button is clicked language should be stored in NSUserDefaults
and then you can access it everywhere
[[NSUserDefaults standardUserDefaults]setValue:#"en" forKey:#"lang"];
4> In Constants.h file add this line
#define IsArabic ([[[NSUserDefaults standardUserDefaults] valueForKey:#"lang"] isEqualToString:#"ar"]? YES : NO)
#define getString(key) NSLocalizedStringFromTable(key, (IsArabic ? #"Messages_ar" : #"Messages_en"), #"Fix it:-(")
5> you can check this way in all screens viewwillappear this way
self.lblRewardsPoint.text = getString(#"rewards_point");
6>In tableview also you can do this way you have to reload all data and if your data came from api then you have to recall the api .
-(NSString *)GetLocalString:(NSString *)text{
NSString *path;
path = [[NSBundle mainBundle] pathForResource:currentLanguageDocumentPath ofType:#"lproj"];
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* str=[languageBundle localizedStringForKey:text value:#"" table:nil];
return str;
}
Here just add string files of languages in project and pass there file name in 'currentLanguageDocumentPath'.
text -> key of text .
In my app i have such thing
#define ACCT_ID #"someaddress#gmail.com"
Is there any way to set dynamical value to this define block?
FOr ex. i make request to webserver, which returns me some string, and later i set this string into #define block.
Is there anyway to achieve this?
you also use nsuserdefault like:
set value
[[NSUserDefaults standardUserDefaults]setObject:#"abc#gmail.com" forKey:#"ACCT_ID"];
get value
NSString *str= [[NSUserDefaults standardUserDefaults]valueForKey:#"ACCT_ID"];
remove value
[[NSUserDefaults standardUserDefaults]removeObjectForKey:#"ACCT_ID"]
Well of course not, since all lines begin with # happening during pre-processor compilation.
Therefor, the value given to ACCT_ID detemind before even your program compiled and it can not be changed
Ended up with following:
#define ACCT_ID [[SingletonClass myinstance] getEmail]
It seems that they have deprecated the Phonegap variable DeviceInfo as of Cordova 1.5 (I think I saw that it was technically deprecated for 1.5 as well, but left it in). I know there is the device variable, but I was using the DeviceInfo variable to store some of my own data via the callback to
- (NSDictionary*)deviceProperties{...}
It seems that function is still called for the var device data, but it doesn't let me add any data to that variable. Does anyone know if there is anyway to get back the functionality of having custom device data or is it gone forever?
OK so the workaround I came up with was to simply create the variable myself in a javascript call so that the rest of the javascript code still access it as needed. I etd a catagory to override
- (void)viewDidAppear:(BOOL)animated;
and in the implementation I called the following code
NSString* jsString = [NSString stringWithFormat:#"window.DeviceInfo = {param1:%#, param2:%#}}", param1, param2];
[self.viewController.webView stringByEvaluatingJavaScriptFromString:jsString];
That added code allowed for backward compatibility with code based on the editable DeviceInfo variable that has been removed.
I have seems some apps can change the language internally within the app without the need of restarting the app, I am wondering how they are implemented.
For example, for us using NSLocalizedString, I know it is possible to set the language at runtime at main.m when your AppDelegate is not initialized, but once it is initialized (particularly your view controller is created), change it has not effect until the next restart
[[NSUserDefaults standardUserDefaults]
setObject:[NSMutableArray arrayWithObjects:language, nil]
forKey:#"AppleLanguages"];
Anyone have idea how those dynamic language change can be done without restarting the app?
There's some discussion of other approaches here, in particular a notification based approach:
iOS: How to change app language programmatically WITHOUT restarting the app?
In my view there are really three tasks here:
(1) re-localization of resources automatically loaded from nibs. (for example if you dynamically instantiate another custom UIView from a nib, the "old" language strings and settings (images, text direction) will still be loaded)
(2) re-localization of strings currently displayed on the screen.
(3) re-localization of strings inserted by the developer (you) in program code.
Let's start with (3). If you look for the definition you will notice that NSLocalizedString is a macro. So if you don't want to change existing code too much, you can probably solve the problem of (3) by creating a new header file. In that header file, #undef and then re-#define NSLocalizedString to pick the localized string from the appropriate place--not the one that iOS defaults to, but one that you keep track of in some global variable (e.g., in an app delegate ivar). If you don't want to redefine NSLocalizedString but you still make your own alternative , you should probably still #undef NSLocalizedString if you don't want future developers to accidentally call it instead of the macro you replace it with. Not an ideal solution, but maybe the most practical.
As for (1), if you haven't done your localization in Interface Builder, but rather you do it dynamically in viewDidLoad, etc., no problem. You can use the same behavior just discussed (i.e., the modified NSLocalizedString, etc.). Otherwise you can either (a) implement a notification system as described in the link above (complicated), or (b) consider moving localization from IB to viewDidLoad, or (c) try overriding initWithNibName: and swap out the object loaded with the old language resources, with one loaded with the new language resources. This was an approach mentioned by Mohamed at the very bottom of this discussion: http://learning-ios.blogspot.ca/2011/04/advance-localization-in-ios-apps.html. He claims it causes problems (viewDidLoad isn't called). Even if it doesn't work, trying it out might point you towards something that does.
Finally, (2) is presumably the easiest task: just remove and re-add the current view (or in some cases, just redraw it).
the idea is to write a new macro like NSLocalizedString which should check if to take the translation from another specific bundle or not.
The method 2 in this article explain exactly how to do it.
In this particular case, the author doesn't use a new macro, but directly set a custom class for [NSBundle mainBundle].
I hope that #holex will understand the problem reading this.
I'm always using this way, it works perfectly, it might help you as well.
you should set all the texts with NSLocalizableString(...) for the UI for the current language in the -viewWillAppear: method of your every UIViewController.
using this way you (I mean, the users) don't need to restart the application after changing the language of iOS in the Settings.
of course, I'm using the Apple's standard localisation architecture.
UPDATE on (24 Oct 2013)
I've experienced the –viewWillAppear: method won't be performed for the actual view when the application enters to foreground; to solve that issue I also commit the procedure (see above) when I receive UIApplicationWillEnterForegroundNotification notification in the view.
My implementation uses a class to change the language and access the current language bundle. It's an example so if you were to use different languages than I am then change the methods to use your exact language codes.
This class will access the preferred languages from NSLocale and take the first object which is the language being used.
#implementation OSLocalization
+ (NSBundle *)currentLanguageBundle
{
// Default language incase an unsupported language is found
NSString *language = #"en";
if ([NSLocale preferredLanguages].count) {
// Check first object to be of type "en","es" etc
// Codes seen by my eyes: "en-US","en","es-US","es" etc
NSString *letterCode = [[NSLocale preferredLanguages] objectAtIndex:0];
if ([letterCode rangeOfString:#"en"].location != NSNotFound) {
// English
language = #"en";
} else if ([letterCode rangeOfString:#"es"].location != NSNotFound) {
// Spanish
language = #"es";
} else if ([letterCode rangeOfString:#"fr"].location != NSNotFound) {
// French
language = #"fr";
} // Add more if needed
}
return [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language ofType:#"lproj"]];
}
/// Check if preferred language is English
+ (BOOL)isCurrentLanguageEnglish
{
if (![NSLocale preferredLanguages].count) {
// Just incase check for no items in array
return YES;
}
if ([[[NSLocale preferredLanguages] objectAtIndex:0] rangeOfString:#"en"].location == NSNotFound) {
// No letter code for english found
return NO;
} else {
// Tis English
return YES;
}
}
/* Swap language between English & Spanish
* Could send a string argument to directly pass the new language
*/
+ (void)changeCurrentLanguage
{
if ([self isCurrentLanguageEnglish]) {
[[NSUserDefaults standardUserDefaults] setObject:#[#"es"] forKey:#"AppleLanguages"];
} else {
[[NSUserDefaults standardUserDefaults] setObject:#[#"en"] forKey:#"AppleLanguages"];
}
}
#end
Use the class above to reference a string file / image / video / etc:
// Access a localized image
[[OSLocalization currentLanguageBundle] pathForResource:#"my_image_name.png" ofType:nil]
// Access a localized string from Localizable.strings file
NSLocalizedStringFromTableInBundle(#"StringKey", nil, [OSLocalization currentLanguageBundle], #"comment")
Change language in-line like below or update the "changeCurrentLanguage" method in the class above to take a string parameter referencing the new language code.
// Change the preferred language to Spanish
[[NSUserDefaults standardUserDefaults] setObject:#[#"es"] forKey:#"AppleLanguages"];
I was stuck in same issue, my requirement was "User can select language from drop down & application have to work according selected language (English or arabic)" What i have done i create two XIB and fetch XIB and Text according selected language. In this way user can select language. I used NSBundle for the same. like
For XIB
self.homeScreen = [[HomeScreen alloc] initWithNibName:#"HomeScreen" bundle:[CommonData sharedCommonData].languageBundle];
For Text
_lblHeading.text = [self languageSelectedStringForKey:#"ViewHeadingInfo"];
/**
This method is responsible for selecting language bundle according to user's selection.
#param: the string which is to be converted in selected language.
#return: the converted string.
#throws:
*/
-(NSString*) languageSelectedStringForKey:(NSString*) key
{
NSString* str=[[CommonData sharedCommonData].languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
You need to load another bundle like this(where #"en" could be locale you need):
NSString *path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
NSBundle *languageBundle = [NSBundle bundleWithPath:path];
and make macros/function like NSLocalizedString which use your loaded bundle or use methods on that bundle directly like this
[languageBundle localizedStringForKey:key value:value table:tableName];
[[NSBundle mainBundle] localizations] lists all app localizations(including "Base").
Also I wrote helper class which does this(note that it has ReactiveCocoa as a dependency). It allows language change without app restart and sends current locale each time it's changed.