I'm building a version of an App I have already released, but with a few changes. It's not quite a lite/full version relationship, but they're similar enough that I'm using the same project with a different target.
I'd like to reword almost all of the strings I have used in the first version for the new version, and was wondering the best way to approach this. Rather than use #ifdef/#else statements before the declaration of each string, I was thinking of using NSLocalizedStrings. However, the actual language is still the same.
I read in this post that you can set the language yourself, so presumably I can invent my own language and set it to that. But I'm wondering if this is the best way to go about things? Any advice would be most welcome.
You can have multiple string tables for any given language (that is multiple .strings files). When you want a localised string, you can obtain it through:
NSString *str;
// Look up string in Full.strings
str = [[NSBundle mainBundle] localizedStringForKey:#"SomeKey"
value:#"DefaultValue"
table:#"Full"];
// Look up strings in Lite.strings
str = [[NSBundle mainBundle] localizedStringForKey:#"SomeKey"
value:#"DefaultValue"
table:#"Lite"];
Since the table for this method can be variable, you can even switch it at runtime. The above assumes you have a Full.strings table and a Lite.strings table.
Full.strings
"SomeKey" = "This string appears in the full version";
Lite.strings
"SomeKey" = "This string appears in the lite version";
You may not want to ship them together, if that is the case, you can configure your Info.plist to contain the name of the table to use for a specific target (if you add an entry called "TableToUse", you can get it via [[[NSBundle mainBundle] infoDictionary] objectForKey:#"TableToUse"])
NSLocalizedStrings actually is a macro defined as
#define NSLocalizedString(key, comment) \
[[NSBundle mainBundle] localizedStringForKey:(key) value:#"" table:nil]
With table parameter set as nil, the code will use the default one "Localizable" so if we add another localized string file, we should call [[NSBundle mainBundle] localizedStringForKey:value:table: directly instead of calling NSLocalizedStrings
I'd be very hesitant to invent my own language, but you probably don't need to either. If you use NSLocalizedString in the appropriate places and then use genstrings to extract these to a Localizable.strings (see the docs), then you could simple have two versions of this file and then copy the correct version in each target.
Related
There's a lot of pieces to this, but from what I can tell, they're all necessary to reproduce the problem.
I made a trivial NSURL subclass, like so:
class URL2: NSURL { }
I made a file URL with it:
let f = URL2(fileURLWithPath: "/Users/me/Downloads/ついて.pdf")
Then I tried returning it from previewPanel(panel:previewItemAtIndex index:).
Result: I get a generic file icon in the QLPreviewPanel (but it has the correct filename).
Curiously, if I do any of these differently, it works:
If I use a plain NSURL(fileURLWithPath: "/Users/me/Downloads/ついて.pdf"), it displays the file contents correctly.
If I use an ASCII-only filename, it displays the file contents correctly.
If I do something else with the URL2 (like some NSFileManager operation), it locates the file just fine.
What could I be doing wrong that causes QuickLook to be unhappy with my NSURL subclass in this case?
Subclassing NSURL (or NSURLRequest) is often a path to madness, thanks in no small part (I think) to heavy use of NSXPC in various parts of the OS.
I would suggest using a category with associated objects instead. This should avoid the edge case you're hitting, while still allowing you to add custom methods and properties to the NSURL objects.
I am working on an iphone application and I am wondering what the correct practice is for abstracting out strings. I am used to creating a file with constant strings and referencing them in my application (such as urls, port numbers or even button labels). I am wondering if this is considered good practice in Obj-C and if so what the best way to do it is? Should I make a class with the strings? Or use the ".strings" file?
p.s I may be localizing my application at a later time. I havent looked into how to do it, but I figure abstracting out my strings is a good idea while i'm developping.
Thank you!
MGA
generally, you interface with NSBundle. You use a string to read a localized version of the string (which is loaded from a localized strings file).
there are also some macros some people use to lighten the syntax, these are prefixed with NSLocalizedString. NSLocalizedString implementations use NSBundle.
imo, you should use string constants to identify the localized string you read (like you should with dictionaries and object keys).
you can declare your constants using this form (objc assumed):
extern NSString* const MONAppString_Download;
and define like so:
NSString* const MONAppString_Download = #"Download";
then access it using:
NSString * tableName = nil; // << using the default
NSString * localized =
[[NSBundle mainBundle]
localizedStringForKey:MONAppString_Download
value:MONAppString_Download // << return the string using the default localization if not found
table:tableName];
sometimes it helps to create wrapper functions to reduce the noise, especially when you use them in many places:
// #return what's set to the above variable named 'localized'.
NSString * MONLocalized_Download();
then you set up your strings files like a map, one for each localization you support.
so, whenever you need to read a string which is visible to the user, you use the above form. also consider that there are other resources to localize (nibs, images, pdfs, etc.) which you may bundle with your app. much of the work here is also abstracted by NSBundle, of CFBundle, if you prefer.
good luck
There are a lot of nice little functions in System.IO.Path like replacing a file extension, append to a path, getting a filename, getting a directory from a path in C#. Is there an iOS equivalent or somewhat close API?
thanks!
Yes, there is. Have a look at "Working with Paths" section in NSString reference.
To work with actual files you need to use NSFileManager class
NSString has a number of methods that allow you to manipulate paths.
It's not super elegant, but it's very useful.
NSString *textFile = #"readme.txt"
NSString *markdownFile = [[textFile stringByDeletingPathExtension] stringByAppendingPathExtension:#"markdown"];
// markdownFile is now "readme.markdown"
How can I use an NSString in a file where that string wasn't created?
ex.
I created a string in thisone.m, but I want to use the same sting (ie the same data) in thatone.m
the data from this string will be coming from a UITextField
If you don't have access to the thisone object, you can store the string as a ThisOne class variable, as long as you don't need a different one for each of your objects. Put the in your class (not inside a method, but outside of the #implementation)
extern BOOL theString;
The access is by
[ThisOne theString];
This is not as good as ennuikiller's answer, but it might be what you need.
I'm not sure exactly what your asking but there are many ways to share data between files (or objects). You can define it as an instance variable in one class and take a reference to the object instance in other class. You can pass the data to a method called on the other object, or you can share it as a global variable by making it an instance variable of UIApplication.
Again, without being more specific in your question, this should get you thinking along the right path.
As a simple example:
#interface MyObject : NSObject {
NSString *mystring;
}
I know some people don't like #define's, but they work well for this old-school C programmer.
In each project, I have a file "Strings.h" which contains a bunch of #define's, such as:
#define SK_String_I_Want_To_Display #"String I Want To Display"
(where SK_ is my preface to indicate "string constant".)
For localization, I have another file called "LocalStrings.h" with strings like:
#define SK_LOCAL(a) NSLocalizedString(#a, "") // keeps string defs simple
#define SK_Localized_String SK_LOCAL("Localized String")
#define SK_Another_String SK_LOCAL("Another String")
Then I just #import "Strings.h" or "LocalStrings.h" where needed. Because I have all localized strings in one file it's easy to make sure I have localized everything.
The biggest issue with this approach is that you have to be careful not to do something like this:
#define SK_Another_String SK_LOCAL("Another String");
--- as that semicolon at the end can cause tricky bugs that are hard to find.
The overhead of having the #define expanded in place is pretty low. Compiles take a bit longer if you change one of these .h files, but I find the solution works well.
I'm just starting out with iphone development and ran across some example code that used #"somestring"
someLabel.txt = #"string of text";
Why does the string need the '#'? I'm guessing it's some kind of shortcut for creating an object?
It creates an NSString object with that string as opposed to the standard c char* that would be created without the '#'
In Objective-C, the syntax #"foo" is an immutable, literal instance of NSString.
Just an interesting side note... NSString literals created by using the #"..." notation are not autoreleased. They essentially just hang around until your program terminates.
Just a caution that if you want to maintain control over whether or not this object gets released (freed) down the road you may want to consider using something like:
[NSString stringWithString:#"..."];
...instead. This would create an autoreleased version of the same string that will be freed from memory next time the "autorelease pool is drained".
Just food for thought.
Cheers-