Localizing the Cut|Copy|Paste menu on iOS - objective-c

Im having some issues localizing a danish app ive made. (The language, not the pastry)
I have set the CFBundleDevelopmentRegion to da_DK for danish in my info.plist, but the popup appearing for text input is still in english, even on phones running the danish OS.
How in Jobs name can i change this ?
The test device is a non-jailbroken iPhone 4S running iOS 5.1 with Danish as its iOS setting, and a danish itunes account associated.
I do not use .xibs for designs. all interfaces are programmed as viewcontrollers.

In the Xcode's file tree (Project Navigator) select your project. in the right hand pane select your project again. select Info and add your language.
I created a sample project, this is the result:

You can do this directly in the info.plist. Something like this:
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>de</string>
<string>es</string>
<string>ja</string>
</array>

Try adding/setting the "Localized resources can be mixed" flag in Info.plist to YES.

You must localize your app in Danish to make the standard UI elements appear in that language. This is to avoid having a UI with mixed languages.
If you don't use xibs, you'd usually do this by adding a Localizable.strings file to your project. In Xcode's "Add File" dialog, you can use the "Strings File" template (under "Resources") for this.
To actually localize the strings file, open the file inspector (⌘ ⌥ 1) and click the + button in the "Localization" section. You'll end up with the file being displayed as a group in the project navigator, with a sub-entry for each language.
The strings file has the format:
"Label_Text" = "Smørrebrød";
(don't forget the semicolon)
To use localized strings in your code, you can use the NSLocalizedString macro like this:
myLabel.text = NSLocalizedString(#"Label_Text", nil);
(The second parameter is for a comment. This can be useful if you use the genstrings tool to extract localizable strings from your code and give the resulting file to a professional translator.)
If you use the English strings as keys, you can leave the English version of Localizable.strings empty (but don't delete it).
Having a Localizable.strings file in the language that the user has selected will also cause standard UI elements, such as the editing menu, photo picker, and so forth, to appear in that language.

If you can't get it working the official way, as provided by #vikingosegundo, you can do this with some creative engineering (Creative as in, oh my god that is dangerous). I discovered this method when I accidentally overrode [NSBundle localizedStringForKey:value:tableName:].
1) Add a category to NSBundle with the following methods:
#import <objc/runtime.h>
+ (void) load {
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(localizedStringForKey:value:table:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_localizedStringForKey:value:table:));
method_exchangeImplementations(original, swizzled);
}
- (NSString*) swizzled_localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName {
NSLog(#"Key: %#. Value: %#", key, value);
return [self swizzled_localizedStringForKey: key value:value table:tableName];
}
2) Where I simply log the key/value, you want to put an if ([key isEqualToString: xxx] ) block. In there, you want to catch (at least some of) the following key values: Cut, Copy[Menu], Select, Select All, Paste, Delete[Menu], Replace..., Define, Speak, Pause. These are the default values that can appear there.
3) When you have caught the value you can look up in a custom table or use hardcoded values. If you look up in a custom table make sure you have a catch in your swizzled method to avoid infinite looping in your custom table.
NB: Why do you need to swizzle? Because this over-rides all Apple text for you app. You will still want the defaults for all the other strings, so you need to swizzle to get the defaults for the strings you aren't interested in.
Good luck.
Paul

Search if your .xib is localized (you'll find it in the inspector on the right panel) if so go to your Project/Target-Settings press the +-Sign and select "Duplicate English to Danish" or something which means the same (I can't check the right item at the moment)
Btw it's called iPhone 4S.

Related

Preferences for opening curly brackets in Xcode

I have this problem.
Because i always put "{" in the same line of the function name, like:
- (void)doSomething {
...
}
it's annoying that Xcode creates functions like:
- (id)initWithFrame:(CGRect)frame
{
...
}
So, is there any option to change this behavior of Xcode?
You can go look in the Xcode app bundle (Ctrl-click on it and choose Show Package Contents) and then go to Contents/Developer/Library/Xcode/Templates and find the actual template files.
Then you can change the format of the default code to your hearts content. Remember that upgrading Xcode will probably undo your changes.
Additionally why not post a ticket on bug report.apple.com asking for more consistent template code :)

Compile time check for valid file references in Xcode

Is it possible to force the Xcode complier to verify that files referenced in code are valid?
There are multiple points in Cocoa development when you naturally reference a file programmatically via an NSString:
[UINib nibWithNibName:#"MyNib" bundle:nil];
[UIImage imageNamed:#"MyImage"];
[[UIViewController alloc] initWithNibName:#"MyNib" bundle:nil];
Is there any way at compile time to check is these file references are valid?
Often times after using above methods, I end up changing the name of the referenced file but forget to change the name in code. Everything complies without a problem and it is only when you happen to go to the portion of the app that accesses this file that the bug will reveal itself.
Is there another approach or technique that people use to avoid this sort of error?
Referencing a file name via a string feels very fragile.
Warning: This answer is mostly outdated. The general idea is fine but better solutions exist now (e.g. Image assets with a SwiftGen script to generate an enum).
Nibs usually have a class with the same name as the file, e.g.
[[MyViewController alloc] initWithNibName:NSStringFromClassName([MyViewController class]) bundle:nil];
I usually hide it into the controller's init method as [self class].
For image loading, compile-time checks are difficult. Help yourself with macros, first replace the loading method by a simple macro, e.g.
#define LOAD_IMAGE(__IMAGE_NAME__) [UIImage imageNamed:__IMAGE_NAME__]
First thing you should do is to put an assert into this macro and always check that the image was successfully loaded. It's not a compile-time check but it helps to find missing resources.
The second thing is to write a ruby/python/shell/(any scripting language) script that will search your source files for LOAD_IMAGE and check if the file (between parenthesis) exists. As a shell script, it will be very simple (e.g. using grep). You can add this script into your xcode project to be run when compiling.
Don't forget to check images referenced by xibs.
However, often you have to create the image name dynamically, e.g. NSString* imageName = [NSString stringWithFormat:#"image_%i", index]. There's no way how you can check this at compile time.
Also don't forget to do the reverse check - don't include image files which are not used anywhere.
AutoComplete for [UIImage imageNamed:] by Kent Sutherland.
This provides code completion support within Xcode - a brilliant piece of code. This is working for me in Xcode 4.6:
Currently this project does not have support for strings other than imageNamed:. To support those, I will try to write a compile time script. Or maybe I will become bold and try to extend Mr. Sutherland's spectacular work.
Xcode doesn't support this, but if this problem is really biting you then you could use the following hack:
Give every in-bundle file a unique prefix (e.g. app__)
When you add a file to your project, make sure you first rename it to add this prefix.
Your compile time (pre-distribution) check then has two parts: 1) Search through all .m files and enumerate strings that begin with the prefix. You shouldn't have to check if the string is quoted since your prefix is unique. 2) grep project.pbxproj for each string to check if it is included in the bundle.
With some effort, this process can be mostly automated and also optimized, but the above recipe ought to work.
here is a bash script that we use that lists all images on disk but NOT referenced in code.
https://gist.github.com/3750087
it would likely be easy to reverse this to check for non-exting images and xibs.
Anyways, the script should be a good starting point

How to intercept reading of plist values in Objective-C code?

We're using the new Urban Airship iOS plugin for PhoneGap.
In the plugin's plist file, we're supposed to enter the app-specific keys needed to enable push notifications.
The problem is we have two versions, free and paid, of the same app, but the plist file only accommodates one version.
Essentially, we need to modify the Objective-C code to read different plist values, depending on whether it's the free or premium version.
We currently manage both versions with the same code base and Xcode project. Unless we change the plugin code, it seems like we need to create a new Xcode project, which we don't want to do.
How do we adjust Urban Airship's Objective-C files to read different values from the plsit file?
Sorry to keep you waiting, I wanted to give you a very detailed answer instead of rushing last night :) So here we go.
First in your project we need to add a new target. Go to your project settings and right click your target. Click duplicate.
You'll get a new target probably named Target-copy. You'll also get a new info.plist file just for that target.
Next we're going to edit our Pro version's Built Settings. Scroll or search and find Apple LLVM compiler 4.0 Preprocessing. Add to both your Debug and Release configurations. I normally just go with the simple PRO=1. You also need to add PRO=0 to your lite version or it will be undefined when you try to build that version.
Now lets look at how to add a custom plist like I'm sure you'll need. First create two folders. Its important these are folders not groups. In each folder we can create a plist with the exact same filename.
Since Now you can add something to each of them. I just added a key property and a value pro string / lite string. Finally to the code. In the sample project I made I simple overrode viewDidLoad but obviously this will work anywhere. Since the plists have the same name you can load them with one line of code. They'll never get mixed up because they are only copied to their respective target. If you need to do code level based logic you can use the PRO preprocessor we made.
- (void)viewDidLoad
{
[super viewDidLoad];
// This will load the proper plist automatically.
NSLog(#"Plist Value: %#",[[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Property List" ofType:#"plist"]] objectForKey:#"property"]);
// Also remember we set up a preprocessor PRO. you can use it as well.
if (PRO) {
NSLog(#"Only Show for Pro");
} else {
NSLog(#"Only Show for Lite");
}
NSLog(#"This will show for both");
}
This is the method I use for all my lite/pro version apps so I can share a common codebase without copying it between projects or other complicated systems. It has worked pretty well for me so far :) Happy Coding!
Source
Figured someone may be able to use the project to look at so here it is on GitHub.

Sending requests to System Events from Objective-C/C?

Is there any way to convert the following applescript to Objective-C/C?
tell application "System Events" to set visible of process "Safari" to false
I know I could execute this applescript in Objective-C using the NSAppleScript class or calling system("osascript -e '...'"), however isn't there another way?
How does applescript do this?
Alternatively can I hide a window from another application from Objective-C/C?
Update:
I have found out that you can use SBApplication class to do this:
SBApplication *SystemEvents = [SBApplication applicationWithBundleIdentifier:#"com.apple.systemevents"];
/*SystemEventsApplicationProcess*/ id Safari = [[SystemEvents performSelector:#selector(applicationProcesses)] objectWithName:#"Safari"];
[Safari setVisible:NO]; // Doesn't work!
However this doesn't work as setVisible probably doesn't do what I think.
This is the class hierarchy of SystemEventsApplicationProcess:
SystemEventsApplicationProcess : SystemEventsProcess : SystemEventsUIElement : SystemEventsItem : SBObject : NSObject
And here are the methods available for these SystemEventsXXX classes:
SystemEventsApplicationProcess
applicationFile
SystemEventsProcess
setVisible:
visible
unixId
totalPartitionSize
shortName
partitionSpaceUsed
name
id
hasScriptingTerminology
setFrontmost:
frontmost
fileType
file
displayedName
creatorType
Classic
bundleIdentifier
backgroundOnly
architecture
acceptsRemoteEvents
acceptsHighLevelEvents
windows
menuBars
SystemEventsUIElement
select
clickAt:
setValue:
value
title
subrole
setSize:
size
setSelected:
selected
roleDescription
role
setPosition:
position
orientation
name
minimumValue
maximumValue
help
setFocused:
focused
entireContents
enabled
objectDescription
objectClass
accessibilityDescription
windows
valueIndicators
UIElements
toolBars
textFields
textAreas
tables
tabGroups
staticTexts
splitterGroups
splitters
sliders
sheets
scrollBars
scrollAreas
rows
relevanceIndicators
radioGroups
radioButtons
progressIndicators
popUpButtons
popOvers
outlines
menuItems
menuButtons
menuBarItems
menuBars
menus
lists
incrementors
images
growAreas
groups
drawers
comboBoxes
columns
colorWells
checkboxes
buttons
busyIndicators
browsers
attributes
actions
SystemEventsItem
setName:
name
id
removeActionFromUsingActionName:usingActionNumber:
pick
keyUp
keyDown
increment
editActionOfUsingActionName:usingActionNumber:
doScript
doFolderActionFolderActionCode:withItemList:withWindowSize:
decrement
confirm
cancel
attachedScripts
attachActionToUsing:
stop
start
saveAs:in:
moveTo:
exists
duplicateTo:withProperties:
delete
closeSaving:savingIn:
setProperties:
properties
objectClass
SBObject
// ...
NSObject
// ...
You can use NSRunningApplication, which represents (as its name implies) a running application, and has a -hide method.
NSWorkspace will give you a list of all the running apps: [[NSWorkspace sharedWorkspace] runningApplications], which you can filter, or you can get the object representing Safari using its bundle identifier: +[NSRunningApplication runningApplicationsWithBundleIdentifier:] (note that actually returns an array in case there are multiple running instances of the same app).
The code won't work unless you add the scripting bridge framework to your project and a couple other things. Have you done that... I can't tell. This link seems to have a good explanation of what is required if you need instructions.
By the way, "set visible" means hide the application just like if you hid it from the application menu. However if you want to hide an application I'm sure there's an NSWorkspace method.
Last bit of advice... for only a few lines of applescript code NSApplescript would be your best option. If you intend to use lots of applescript script code then the scripting bridge is the better choice, although I myself often just put a compiled script in my project and then use NSApplescript to initiate the handlers from that script. You can also use the ApplescriptObjC language too. You have lots of choices.

Changing button titles from different .m files

I am writing some code that works in several languages. When the user changes the language I need to change all the screen prompts and button text. When I am in the .m for that screen I simply type:
[[self.tabBarController.viewControllers objectAtIndex:1] setTitle:#"Setup"];
But how to I change the prompts in the other screens without having to go to each .m and make the changes. I am assuming that "self" can be replaced to allow me to do this.
It sounds like you're trying to use localized strings. Something like this might help you find the right path:
http://www.icanlocalize.com/site/tutorials/iphone-applications-localization-guide/
Basically, instead of using strings like #"Setup" you'll use something like this
[[self.tabBarController.viewControllers objectAtIndex:1] setTitle:
NSLocalizedString("setup_button_title", "Title for the setup button")];