Macro for NSLocalizedString not working - ios

I build a Macro for NSLocalizedString like this in my Prefix.pch:
#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizedText sharedInstance] localizedStringFor:key]
My LocalizedText class looks like this:
+ (LocalizedText *)sharedInstance {
#synchronized(self)
{
if (_singletonInstance == nil)
_singletonInstance = [[LocalizedText alloc] init];
}
return _singletonInstance;
}
- (NSString *) localizedStringFor:(NSString*) key
{
NSUserDefaults *deviceLanguage = [NSUserDefaults standardUserDefaults];
NSString *currentLanguage = [deviceLanguage objectForKey:#"deviceLanguage"];
NSString *path;
if([currentLanguage isEqual: #"en"])
path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
else if([currentLanguage isEqual: #"de"])
path = [[NSBundle mainBundle] pathForResource:#"de" ofType:#"lproj"];
else if([currentLanguage isEqual: #"it"])
path = [[NSBundle mainBundle] pathForResource:#"it" ofType:#"lproj"];
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* str=[languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
So for example, when i change my NSUserDefaults to #"en", i expect that every NSLocalizedString gives me the correct english translation, but it never changes and is always the default language. Whats wrong with my code?

Before debugging your Macro, I would set up a test as follows:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *currentLanguage = [defaults objectForKey:#"deviceLanguage"];
NSString *key = #"SomeKeyFromYourLocalizedFile";
NSString *localized = [[LocalizedText sharedInstance] localizedStringFor:key];
NSLog(#"%#: %#", currentLanguage, localized);
[defaults setObject:#"de" forKey:#"deviceLanguage"];
[defaults synchronize];
NSString *localized = [[LocalizedText sharedInstance] localizedStringFor:key];
NSLog(#"%#: %#", currentLanguage, localized);
[defaults setObject:#"it" forKey:#"deviceLanguage"];
[defaults synchronize];
NSString *localized = [[LocalizedText sharedInstance] localizedStringFor:key];
NSLog(#"%#: %#", currentLanguage, localized);
And see the output of that. Your bug is probably somewhere in there.
I expect:
en: English localized
de: German localized
it: Italian localized
After that, you would simply use your macro (which looks like it will work just like that). I tried:
#ifdef NSLocalizedString
#undef NSLocalizedString
#endif
#define NSLocalizedString(key, comment) NSLog(#"Output %#, %#", key, comment)
NSLocalizedString(#"First", #"Second");
Which results in:
2017-01-12 14:31:31.501 App[49000:50650596] Output First, Second

Related

How to change language at runtime in iOS [duplicate]

This question already has answers here:
Change Language in the app programmatically in iOS
(3 answers)
Closed 6 years ago.
In my iOS app I need an option to change language. Can we change it without changing the language in the device settings
You can use following code to set the language
- (void) setLanguage:(NSString*) lang {
NSString *path = [[NSBundle mainBundle] pathForResource:lang ofType:#"lproj"];
if (path == nil) {
myBundle = [NSBundle mainBundle];
} else {
myBundle = [NSBundle bundleWithPath:path];
}
if (myBundle == nil) {
myBundle = [NSBundle mainBundle];
}
}
And after that you can get the localizations with
- (NSString*) localizedStringForKey:(NSString*) key {
return [myBundle localizedStringForKey:key value:#"" table:nil];
}
For example store different static json/plist and etc in your app with different languages strings and when you switch option parse this and set to labels.
i am not sure but try this code once using NSUserDefaults
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* languages = [userDefaults objectForKey:#"AppleLanguages"];
[languages insertObject:#"de" atIndex:0]; // ISO639-1
[[NSUserDefaults standardUserDefaults] synchronize];

How can i generate and use unique deviceId for iOS devices?

In my previous app,I was using the below mentioned code to generate unique deviceId for iOS devices.But the problem is that it will generate a new code everytime when the app is reinstalled.How can i do this properly?
-(NSString*)uniqueIDForDevice //new..
{
NSString* uniqueIdentifier = nil;
if( [UIDevice instancesRespondToSelector:#selector(identifierForVendor)] ) { // >=iOS 7
uniqueIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
else
{ //<=iOS6, Use UDID of Device
CFUUIDRef uuid = CFUUIDCreate(NULL);
//uniqueIdentifier = ( NSString*)CFUUIDCreateString(NULL, uuid);- for non- ARC
uniqueIdentifier = ( NSString*)CFBridgingRelease(CFUUIDCreateString(NULL, uuid));// for ARC
CFRelease(uuid);
}
return uniqueIdentifier;
}
You can use the below
+ (NSString *)deviceUUID
{
if([[NSUserDefaults standardUserDefaults] objectForKey:[[NSBundle mainBundle] bundleIdentifier]])
return [[NSUserDefaults standardUserDefaults] objectForKey:[[NSBundle mainBundle] bundleIdentifier]];
#autoreleasepool {
CFUUIDRef uuidReference = CFUUIDCreate(nil);
CFStringRef stringReference = CFUUIDCreateString(nil, uuidReference);
NSString *uuidString = (__bridge NSString *)(stringReference);
[[NSUserDefaults standardUserDefaults] setObject:uuidString forKey:[[NSBundle mainBundle] bundleIdentifier]];
[[NSUserDefaults standardUserDefaults] synchronize];
CFRelease(uuidReference);
CFRelease(stringReference);
return uuidString;
}
}
first of all get vendorId(uniqueID) than store it to keychain
// Fetch vendorID from keychain first, if it exists then use it or fetch vendorID
//if user delete app and again install app than he get vendorID from keychain(if he hasn't cleared it)
NSString *vendorID = [UICKeyChainStore stringForKey:#"KEY TO SAVE TO Keychain" service:nil];
NSLog(#"VendorID from Keychain - %#",vendorID);
//if vandorid from keychain is not nil
if (vendorID)
{
[[NSUserDefaults standardUserDefaults] setObject:vendorID forKey:#"vendorId"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
//else it goes for new vendorid and then stored it to keychan
else
{
vendorID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[UICKeyChainStore setString:vendorID forKey:#"KEY TO SAVE TO Keychain" service:nil];
[[NSUserDefaults standardUserDefaults] setObject:vendorID forKey:#"vendorId"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"VendorID Local - %#",vendorID);
}
i use this class to save and retrive vendorID from keyChain!
just take one look at this.hope it will help you
use This link to UICKeyChainStore . I have added the following method call
[self getDeviceIdFromKeychain]; in appDelegate applicationdidFinishLaunchingWithOptions
-(void)getDeviceIdFromKeychain
{
NSString *deviceID = [UICKeyChainStore stringForKey:DEVICE_ID_FROM_KEYCHAIN_REFERENCE_APP service:nil];
NSLog(#"deviceID from Keychain = (%#)",deviceID);
//if vandorid from keychain is not nil
if (deviceID)
{
[[NSUserDefaults standardUserDefaults] setObject:deviceID forKey:DEVICE_ID];
[[NSUserDefaults standardUserDefaults] synchronize];
}
//else it goes for new vendorid and then stored it to keychan
else if (deviceID == (id)[NSNull null] || deviceID.length == 0 )
{
NSString *newDeviceId;
newDeviceId=[[self class] deviceUUID];
[[NSUserDefaults standardUserDefaults] setObject:newDeviceId forKey:DEVICE_ID];
[[NSUserDefaults standardUserDefaults] synchronize];
[UICKeyChainStore setString:newDeviceId forKey:DEVICE_ID_FROM_KEYCHAIN_REFERENCE_APP service:nil];
NSLog(#"new deviceID = (%#)",newDeviceId);
}
}
+ (NSString *)deviceUUID
{
if([[NSUserDefaults standardUserDefaults] objectForKey:[[NSBundle mainBundle] bundleIdentifier]])
return [[NSUserDefaults standardUserDefaults] objectForKey:[[NSBundle mainBundle] bundleIdentifier]];
#autoreleasepool {
CFUUIDRef uuidReference = CFUUIDCreate(nil);
CFStringRef stringReference = CFUUI
DCreateString(nil, uuidReference);
NSString *uuidString = (__bridge NSString *)(stringReference);
[[NSUserDefaults standardUserDefaults] setObject:uuidString forKey:[[NSBundle mainBundle] bundleIdentifier]];
[[NSUserDefaults standardUserDefaults] synchronize];
CFRelease(uuidReference);
CFRelease(stringReference);
return uuidString;
}
}
And i use them like this...(in my signinViewcontroller)
NSString *deviceIDfromNSUserDefaults = [[NSUserDefaults standardUserDefaults]valueForKey:DEVICE_ID ];
NSLog(#"deviceID from NSUserDefaults = (%#)",deviceIDfromNSUserDefaults);
I am using UUID to generate unique number. I doubt whether it is allowed to use vender ID if we are not using iAds in app.
I have achieved this by generating UUID and then storing into the keychain.
Keychain will store your UUID till user will not do Hard reset which is rare case.
As no API is provided by Apple to get unique device ID we have to find ways like this.

Update Root.Plist on Compile

I have an app settings file which displays the version number for our app. The idea is to update the Root.plist on compile so that we don't have to update two places rather than one. We update theBuild settings version and would like those settings to update the Root.plist on compile.
This code extracts the information for the build settings, how do I update the Root.plist file?
NSString *appBuildNo = [[NSBundle mainBundle]objectForInfoDictionaryKey:#"CFBundleShortVersionString"];
NSString *appBuildVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"];
NSString *appBuildStr = [NSString stringWithFormat:#"%# Build %#", appBuildNo, appBuildVersion];
NSURL * settingsURL = [[NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:#"Settings" withExtension:#"bundle"]]
URLForResource:#"Root" withExtension:#"plist"];
NSDictionary * settingsDict = [NSDictionary dictionaryWithContentsOfURL:settingsURL];
NSArray * settingsArr = [settingsDict objectForKey:#"PreferenceSpecifiers"];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
for( NSDictionary * setting in settingsArr ){
NSString * key = [setting objectForKey:#"DefaultValue"];
[defaults setValue:appBuildStr forKey:key];
[defaults synchronize];
break;
}
The idea is that defaults updates Root.plist but it is not working. Any help would be appreciated
Droppy was very helpful in helping me to fix this. After setting up my Settings Bundle and the Root.plist file, I added the following code to my AppDelegate and called it from the didFinishLaunchingWithOptions.
- (void)updateVersionInfo
{
//This method updates the Root settings to display current Version and Build No in Settings Bundle
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *appVersionNumber = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
NSString *appBuildNo = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"];
NSString *versionNumberInSettings = [NSString stringWithFormat:#"%# Build %#", appBuildNo, appVersionNumber];
[defaults setObject:versionNumberInSettings forKey:#"version"];
}
This code then adds the contents of the Version in the info.plist file to my Settings Bundle.

Load localized string from my app inside a static lib?

I have a project divided in layers, and the bottom one is included in the top one as a static lib.
The thing is that I need to localize a string in the static lib, using the translations present in my app (upper layer).
Is this possible somehow?
The way I managed to do this is loading the strings from a bundle instead of using NSLocalizedString:
+ (NSString *)getTranslationFromAppBundleForString:(NSString *)originalText {
NSString * lang = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString * bundlePath = [[NSBundle mainBundle] pathForResource:lang ofType:#"lproj"];
NSBundle * bundle = [NSBundle bundleWithPath:bundlePath];
return [bundle localizedStringForKey:originalText value:originalText table:nil];
}
You can create an LanguageAgent in your static library, add a bundle ressource in that library. Then use a function like this to get localizedString. In my application, I separate language by different table (see picture below for a table named 'Dictionaire'. You can have more than 1 table in your languages bundle.
-(NSString*) myLocalizedStringForKey:(NSString*) key ofTable:(NSString*)tableName {
//I save selected language in my NSUserDefaults.
NSString *selectedLanguage = [[NSUserDefaults standardUserDefaults] stringForKey:#"DefaultLanguage"];
if (selectedLanguage == nil) {
[[NSUserDefaults standardUserDefaults] setValue:#"en" forKey:#"DefaultLanguage"];
[[NSUserDefaults standardUserDefaults] synchronize];
selectedLanguage = [[NSUserDefaults standardUserDefaults] stringForKey:#"DefaultLanguage"];
}
NSString *langBundleNew = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingFormat:#"/langs/Languages.bundle/%#.lproj/",selectedLanguage]; //use your path to the Languages.bundle here.
if ([[NSFileManager defaultManager] fileExistsAtPath:langBundleNew]) {
NSBundle *aBundle = (NSBundle*)[self.dictLangueBundle objectForKey:selectedLanguage];
NSString* str=[aBundle localizedStringForKey:key value:#"[string not defined]" table:tableName];
return str;
} else {
return #"[]";
}
}
My language bundle is similar like this: ('Dictionaire' = name of the table)
Here is a sample of content in my Dictionnaire.strings for 'en.lproj':

NSLocalizedStringFromTable doesn't work

I'm trying to use NSLocalizedStringFromTable but with no results. I've got created Profile.strings file, clicked localized, in project settings I add Polish and English languages, so my file has 2 "files" inside and I typed the same strings with other values but when I switch languages and restart app there is still one localization used (Polish).
Profile.strings in Xcode:
Profile.strings
Profile.strings (Polish)
Profile.strings (English)
Polish:
"fullName.placeholder" = "Imie i nazwisko";
"emailAddress.placeholder" = "Adres email";
"phoneNumber.placeholder" = "Numer telefonu";
English:
"fullName.placeholder" = "Full name";
"emailAddress.placeholder" = "Email address";
"phoneNumber.placeholder" = "Phone number";
To get value I call:
NSLocalizedStringFromTable(#"fullName.placeholder", #"Profile", #"");
Any time I call this I've got value from Profile.strings (Polish)
What I'm doing wrong?
try to Reset Content and Settings of simulator it will work (:
Maybe you're not changing the language correctly. Try doing it in code. Like this:
- (void) setLanguage:(NSString*) l{
for (NSString *language1 in [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"]) {
NSBundle *bundle1 = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:language1 ofType:#"lproj"]];
NSLog(#"%#: %#", language1, NSLocalizedStringFromTableInBundle(#"left", #"Localizable", bundle1, nil));
}
NSBundle *bundle1 = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:l ofType:#"lproj"]];
/*
if ([l isEqualToString:#"en"]) {
bundle1 = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:#"English" ofType:#"lproj"]];
}
*/
if (bundle1 == nil)
//in case the language does not exists
[self resetLocalization];
else
bundle = bundle1;
NSMutableArray *langs = [NSMutableArray arrayWithArray: [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"]];
[langs removeObject:l];
NSMutableArray *newLangs = [NSMutableArray arrayWithObject:l];
[newLangs addObjectsFromArray:langs];
[[NSUserDefaults standardUserDefaults] setObject: newLangs forKey:#"AppleLanguages"];
}
And then you can do
[self setLanguage:#"en"];

Resources