Is it possible to change the Three20 language/localization at runtime without restarting the app?
Currently, I managed to change the language via altering the value of AppleLanguages in the main.m
There's a "hack" for it. You can load your own NSBundle with the localized text and use that NSBundle instead. Note that if the localized language file is missing, the app won't run, so make sure you set a correct language.
Above your AppDelegate implementation, add a custom NSBundle declaration:
static NSBundle *bundle = nil;
And then load the language you desire into that bundle:
[[NSUserDefaults standardUserDefaults] setObject: [NSArray arrayWithObjects:#"he", nil] forKey:#"AppleLanguages"];
NSLocale* locale = TTCurrentLocale();
NSString *path = [[NSBundle mainBundle] pathForResource:[locale localeIdentifier] ofType:#"lproj" ];
bundle = [[NSBundle bundleWithPath:path] retain];
You will add a custom function in your AppDelegate to get the localized text too (instead of NSLocalizedString)
///////////////////////////////////////////////////////////////////////////////////////////////////
+ (NSString*)get:(NSString*)key {
return [bundle localizedStringForKey:key value:nil table:nil];
}
To make things easier, you can add a static function in the pch file:
#import "AppDelegate.h"
#define MyLocalizedString(key, alt) [AppDelegate get:key]
Related
I have an app that requires 2 languages, English and French.
I have already set up the Localizable.strings files in their respective "en.lproj" and "fr.lproj" folders ... and when I change the iPad's language (in the native settings app), then launch my app, it does in fact load the correct text (i.e. either English copy or French copy).
I need to have a UISegmentedControl toggle between the 2 languages without having to restart the app.
How do I get the app to change the (current) language so that when I call a method that (re)sets all the UILabels' text and UIImageViews' images they read from the opposite .lproj folder's Localizable.strings file?!?
I know how to use UISegmentedControl, and that is not my question. I'm looking more for a line of code that sets the application's bundle language or locale or something (as I'm quite new to internationalization.localization).
-
Example of how I set the image for a UIImageView:
myUIImageView1.image = [UIImage imageNamed:NSLocalizedString(#"myUIImageView1", #"comment for the translator")];
Example of how I set the text of a UILabel:
myLabel1.text = NSLocalizedString(#"myLabel1", #"comment for the translator");
FOUND THE SOLUTION!!!
The following test app had a function that read the desired string from the correct 'Localizable.strings' file (based on the language selected):
https://github.com/object2dot0/Advance-Localization-in-ios-apps
-
I took this code, and the code required to set the app's primary language (found in the above answer posted by Brayden: How to force NSLocalizedString to use a specific language), and put them together.
Here's what my code looks like now (note - my UISegmentedControl calls a function in it's view's viewController [when the UISegmentedControl's 'Value Changed' method is triggered] that then calls the toggleLanguage function in the parent viewController):
-(void)toggleLanguage:(NSString *)primaryLanguage secondaryLanguage:(NSString *)secondaryLanguage
{
//set app's primary language
defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSArray arrayWithObjects:primaryLanguage,secondaryLanguage,nil] forKey:#"AppleLanguages"];
[defaults synchronize];
//update UILabel and UIImageViews
[self setLanguageSpecificItems];
}
-(NSString *)languageSelectedStringForKey:(NSString *)key
{
//read primary language
NSArray *appleLanguagesArray = [defaults objectForKey:#"AppleLanguages"];
NSString *currentLanguage = [appleLanguagesArray objectAtIndex:0];
//get the path to the desired lproj file
NSString *path;
if(currentLanguage==#"en")
{
path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
}
else
if(currentLanguage==#"fr")
{
path = [[NSBundle mainBundle] pathForResource:#"fr" ofType:#"lproj"];
}
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
//find and return the desired string
NSString* str=[languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
-(void)setLanguageSpecificItems
{
myUIImageView1.image = [UIImage imageNamed:[self languageSelectedStringForKey:#"myUIImageView1"]];
myLabel1.text = [self languageSelectedStringForKey:#"myLabel1"];
}
-
Thanks for the help everyone!!!
-Chris Allinson
I am using iOS 5, XCODE 4.2
In my loginViewController:
-(IBAction)loginButton:(id)sender{
textClass=[[MainScreenController alloc ] init];
NSLog(#"ShowText called");
textClass.selectedLanguage=currentLanguage;
[self.view addSubview:textClass.view] ;
NSLog(#"txtclaslan=%d",textClass.selectedLanguage);
}
In my MainViewController viewDidLoad: method:
mainScreenTitle.text=[self languageSelectedStringForKey:#"screenTitle"];
And in languageSelectedStringForKey:
-(NSString*) languageSelectedStringForKey:(NSString*) key{
NSString *path;
if(selectedLanguage==ENGLSIH_LANGUAGE)
path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
else if(selectedLanguage==TURKISH_LANGUAGE)
path = [[NSBundle mainBundle] pathForResource:#"tr" ofType:#"lproj"];
else
path = [[NSBundle mainBundle] pathForResource:#"en" ofType:#"lproj"];
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* str = [languageBundle localizedStringForKey:key value:#"" table:nil];
return str;
}
Here in this method languageSelectedStringForKey when I load the MainViewController I my application crashes. I don't get any error just this line is highlighted
NSBundle* languageBundle = [NSBundle bundleWithPath:path]; < Thread 1
What can be the issue with this?
Thanks in advance
As suggested:
Looking at your code it seems that if the language is not English and not Turkish then path is not set.
You may also want to follow this advice from the [NSBundle localizedStringForKey:value:table:] method description:
"Using the user default NSShowNonLocalizedStrings, you can alter the behavior of localizedStringForKey:value:table: to log a message when the method can’t find a localized string. If you set this default to YES (in the global domain or in the application’s domain), then when the method can’t find a localized string in the table, it logs a message to the console and capitalizes key before returning it."
In my MainViewController I am using this code:
static NSBundle *bundle = nil;
+(void)setLanguage:(NSString *)l {
NSLog(#"preferredLang: %#", l);
NSString *path = [[ NSBundle mainBundle ] pathForResource:l ofType:#"lproj" ];
bundle = [[NSBundle bundleWithPath:path] retain];}
+(void)initialize {
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:#"AppleLanguages"];
NSString *current = [[languages objectAtIndex:0] retain];
[self setLanguage:current];
}
+(NSString *)get:(NSString *)key alter:(NSString *)alternate {
return [bundle localizedStringForKey:key value:alternate table:nil];}
And in application main:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:#"tr", #"en", nil] forKey:#"AppleLanguages"];
Its working fine and showing the turkish language in my application other than the iPhone native language. But, I want to allow the user to change the language through buttons, like if user clicks on english language button, a dialogue should open that application will be restarted and your lang will be changed.
How to do that?
I am using iOS 5....
Your app should not provide a custom language selector.
Users probably have prior knowledge on how to change the preferred language by navigating to General > International > Language in the Settings application.
Further details in Apple's Internationalization Programming Guide
my application run vary well but now when UISwitch button is on at that time i have to convet whole application in spanish when off then convert in to english how it possible plz give any replay for that.
i18n
Create the following structure:
resources/i18n/en.lproj/Localizable.strings
resources/i18n/es.lproj/Localizable.strings
Create an additional directory with the corresponding two letter code for each additional language supported.
It's recommended to encode Localized.strings in UTF-16. You can convert between encodings in the inspector pane of XCode.
If the files are recognized as i18n resources, they will be presented like this:
A sample file has the following content:
"hello"="hola";
Then use the following in your program:
NSString *string = NSLocalizedString(#"hello", nil);
Choose language dynamically
To change the language for your application dynamically use this code:
#implementation Language
static NSBundle *bundle = nil;
+(void)initialize {
NSUserDefaults* defs = [NSUserDefaults standardUserDefaults];
NSArray* languages = [defs objectForKey:#"AppleLanguages"];
NSString *current = [[languages objectAtIndex:0] retain];
[self setLanguage:current];
}
/*
example calls:
[Language setLanguage:#"es"];
[Language setLanguage:#"en"];
*/
+(void)setLanguage:(NSString *)code {
NSLog(#"preferredLang: %#", code);
NSString *path = [[ NSBundle mainBundle ] pathForResource:code ofType:#"lproj" ];
// Use bundle = [NSBundle mainBundle] if you
// dont have all localization files in your project.
bundle = [[NSBundle bundleWithPath:path] retain];
}
+(NSString *)get:(NSString *)key alter:(NSString *)alternate {
return [bundle localizedStringForKey:key value:alternate table:nil];
}
#end
Then translate your strings like this:
NSString *hello [Language get:#"hello", nil, nil];
The code above was originally posted by Mauro Delrio as an answer to How to force NSLocalizedString to use a specific language.
I've created a project with a Core Data model in it. The application looks for the model file (.momd) and runs just fine.
Unfortunately, the unit test keeps returning null:
NSURL *dataModelURL = [[NSBundle mainBundle] URLForResource:#"myDataModel" withExtension:#"momd"];
I can see the myDataModel.xdatamodeld folder and file in BOTH the main target and the unit testing target's Compile Sources directory - but that doesn't seem to be enough. What else am I missing in the unit test target?
Thanks,
-Luther
Unfortunately, a unit test target does not use the application's main bundle but it creates a special UnitTest-bundle. So if you need to use bundled resources (like a Core Data model) within your tests, you need to work around that issue.
The most simple and most flexible workaround would be using the bundleForClass: method of NSBundle within your testing code. The parameter for that method can simply be given by [self class] within your tests. That way you can reuse this code without having to adjust the bundle identifiers in multiple projects.
Example:
- (void)testBundleLocation
{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSURL *url = [bundle URLForResource:#"myDataModel" withExtension:#"momd"];
...
}
The answer has to do with the bundle. A unit test target doesn't use the 'main' bundle. It creates its own bundle which, in my case, defaulted to 'com.yourcompany.UnitTest' - straight out of the [Target]-info.plist.
The corrected solution then looks like this:
NSBundle *bundle = [NSBundle bundleWithIdentifier:#"com.yourcompany.UnitTests"];
NSURL *url = [bundle URLForResource:#"myDataModel" withExtension:#"momd"];
Thanks
Had a similar problem, i solved it using the OCMock framework, so i did not need to change the application code
#interface TestCase()
#property (nonatomic, strong) id bundleMock;
#end
#implementation TestCase
- (void)setUp
{
self.bundleMock = [OCMockObject mockForClass:[NSBundle class]];
[[[self.bundleMock stub] andReturn:[NSBundle bundleForClass:[self class]]] mainBundle];
[super setUp];
}
- (void)tearDown
{
[self.bundleMock stopMocking];
[super tearDown];
}
This method will get your bundle from any target. However, for each target you add, you have to manually add the plist bundle identifier to the identifiers array, because there is no way to get it programmatically. The advantage is that you can use the same code for testing or running the application.
+(NSBundle*) getBundle
{
NSBundle *bundle = nil;
// try your manually set bundles
NSArray *identifiers = [NSArray arrayWithObjects: #"com.your.application",
#"com.your.test",
nil];
for(NSString *bundleId in identifiers) {
bundle = [NSBundle bundleWithIdentifier:bundleId];
if (bundle!=nil) break;
}
// try the main bundle
if (bundle==nil) bundle = [NSBundle mainBundle];
// abort
assert(bundle!=nil && "Missing bundle. Check the Bundle identifier on
the plist of this target vs the identifiers array in this class.");
return bundle;
}
My problem was indeed the wrong Bundle! As I was trying to use a database from/within a Framework I 'simply' has to load the db from the corresponding Bundle !
Here is some code in Swift4 using MagicalRecord:
// Load the bundle
let frameworkBundle = Bundle(for: AClassFromTheFramework.self)
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [frameworkBundle])
// Use the new `managedObjectModel` by default
MagicalRecord.setShouldAutoCreateManagedObjectModel(false)
NSManagedObjectModel.mr_setDefaultManagedObjectModel(managedObjectModel)
// Load the database
MagicalRecord.setupCoreDataStack(withAutoMigratingSqliteStoreNamed: "db.sqlite")
And voilà!