Localization with variable and constant definition in header file - iphone

How do I use NSLocalizedString in this case when I have a header where I define a few parameters, say:
#define appKey #"appKey1 is: %#"
I think I know that my Localizable.strings should look like that:
"blabla" = "appKey1 is: %#"
but how do I use NSLocalizedString? I read that I need to use stringWithFormat, but not sure how...
thanks!

You would define your constant as:
#define appKey NSLocalizedString(#"appKey1 is: %#", #"appkey constant")
Then it should get picked up by the genstrings tool in the usual way.
In the strings file it would then come out like this:
/* appkey constant */
"appKey1 is: %#" = "appKey1 is: %#";
And you would translate just the right hand side.

String literals are acceptable in NSLocalizedStrings. What you need to do is something like
#define appKey NSLocalizedString(BlahBlah , comments);
"BlahBlah" = "appKey1 is: %#";
(Be sure to end your lines with a semi-colon in Localizable.strings, or it will end up being corrupted).

This is how you would do it normally,
NSString * myString = [NSString stringWithFormat:#"appKey1 is: %#",yourAppKeyString];
Since you have it defined you can use it like so
NSString * myString = [NSString stringWithFormat:appKey,yourAppKeyString];
Either case both would fill your myString like so
yourAppKeyString = #"keyString";
myString = #"appKey1 is: keyString";

NSString * myString = [NSString stringWithFormat: NSLocalizedString(#"appKey", #""),yourAppKeyString];

Related

What is the relevance of this String format specifier?

I'm trying to get an understanding of some code I came across recently.
In an answer to a question here https://stackoverflow.com/a/51173170/1162328, the author made use of a String with a format specifier when looping over files in the documentDirectory. Can anyone shed some light on what %#/%# is actually doing?
for fileName in fileNames {
let tempPath = String(format: "%#/%#", path, fileName)
// Check for specific file which you don't want to delete. For me .sqlite files
if !tempPath.contains(".sql") {
try fileManager.removeItem(atPath: tempPath)
}
}
Reading the Apple documentation archive for Formatting Basics I came across this:
In format strings, a ‘%’ character announces a placeholder for a value, with the characters that follow determining the kind of value expected and how to format it. For example, a format string of "%d houses" expects an integer value to be substituted for the format expression '%d'. NSString supports the format characters defined for the ANSI C functionprintf(), plus ‘#’ for any object.
What exactly then, is %#/%# doing?
Each format specifier is replaced by one of the following arguments (usually in the same order, although that can be controlled with positional arguments). So in your case, the first %# is replaced by path and the second %# is replaced by fileName. Example:
let path = "/path/to/dir"
let fileName = "foo.txt"
let tempPath = String(format: "%#/%#", path, fileName)
print(tempPath) // /path/to/dir/foo.txt
The preferred way to build file names and paths is to use the corresponding URL methods instead of string manipulation. Example:
let pathURL = URL(fileURLWithPath: path)
let tempURL = pathURL.appendingPathComponent(fileName)
if tempURL.pathExtension != "sql" {
try FileManager.default.removeItem(at: tempURL)
}
%# is something similar to %d or anything like that. This is the way of string interpolation in Swift.
To be exact %# is placeholder for object - used in Objective-C A LOT. Since NSString * was object (now it is only String), it was used to insert NSString * into another NSString *.
Also given code is just rewritten objective-c code which was something like
NSString *tempPath = [NSString stringWithFormat:#"%#/%#", path, filename];
which can be rewritten in swift:
let tempPath = path + "/" + fileName
Also, given path = "Test" and fileName = "great" will give output Test/great.
One more note: %# is as good as dangerous. You can put UITableView as well as String in it. It will use description property for inserting into string.

How to replace occurrences of multiple strings with multiple other strings [NSString]

NSString *string = [myString stringByReplacingOccurrencesOfString:#"<wow>" withString:someString];
I have this code. Now suppose my app's user enters two different strings I want to replace with two different other strings, how do I achieve that? I don't care if it uses private APIs, i'm developing for the jailbroken platform. My user is going to either enter or or . I want to replace any occurrences of those strings with their respective to-be-replaced-with strings :)
Thanks in advance :P
Both dasblinkenlight’s and Matthias’s answers will work, but they both result in the creation of a couple of intermediate NSStrings; that’s not really a problem if you’re not doing this operation often, but a better approach would look like this.
NSMutableString *myStringMut = [[myString mutableCopy] autorelease];
[myStringMut replaceOccurrencesOfString:#"a" withString:somethingElse];
[myStringMut replaceOccurrencesOfString:#"b" withString:somethingElseElse];
// etc.
You can then use myStringMut as you would’ve used myString, since NSMutableString is an NSString subclass.
The simplest solution is running stringByReplacingOccurrencesOfString twice:
NSString *string = [[myString
stringByReplacingOccurrencesOfString:#"<wow>" withString:someString1]
stringByReplacingOccurrencesOfString:#"<boo>" withString:someString2];
I would just run the string replacing method again
NSString *string = [myString stringByReplacingOccurrencesOfString:#"foo" withString:#"String 1"];
string = [string stringByReplacingOccurrencesOfString:#"bar" withString:#"String 2"];
This works well for me in Swift 3.1
let str = "hi hello hey"
var replacedStr = (str as NSString).replacingOccurrences(of: "hi", with: "Hi")
replacedStr = (replacedStr as NSString).replacingOccurrences(of: "hello", with: "Hello")
replacedStr = (replacedStr as NSString).replacingOccurrences(of: "hey", with: "Hey")
print(replacedStr) // Hi Hello Hey

regular expression to match " but not \"

How can I construct a regular expression which matches an literal " but only if it is not preceded by the escape slash namely \
I have a NSMutableString str which prints the following on NSLog. The String is received from a server online.
"Hi, check out \"this book \". Its cool"
I want to change it such that it prints the following on NSLog.
Hi, check out "this book ". Its cool
I was originally using replaceOccurencesOfString ""\" with "". But then it will do the following:
Hi, check out \this book \. Its cool
So, I concluded I need the above regular expression to match only " but not \" and then replace only those double quotes.
thanks
mbh
[^\\]\"
[^m] means does not match m
Not sure how this might translate to whatever is supported in the iOS apis, but, if they support anchoring (which I think all regex engines should), you're describing something like
(^|[^\])"
That is, match :
either the beginning of the string ^ or any character that's not
\ followed by:
the " character
If you want to do any sort of replacement, you'll have to grab the first (and only) group in the regex (that is the parenthetically grouped part of the expression) and use it in the replacement. Often this value labeled as $1 or \1 or something like that in your replacement string.
If the regex engine is PCRE based, of course you could put the grouped expression in a lookbehind so you wouldn't need to capture and save the capture in the replacement.
Not sure about regex, a simpler solution is,
NSString *str = #"\"Hi, check out \\\"this book \\\". Its cool\"";
NSLog(#"string before modification = %#", str);
str = [str stringByReplacingOccurrencesOfString:#"\\\"" withString:#"#$%$#"];
str = [str stringByReplacingOccurrencesOfString:#"\"" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#"#$%$#" withString:#"\\\""];//assuming that the chances of having '#$%$#' in your string is zero, or else use more complicated word
NSLog(#"string after modification = %#", str);
Output:
string before modification = "Hi, check out \"this book \". Its cool"
string after modification = Hi, check out \"this book \". Its cool
Regex: [^\"].*[^\"]. which gives, Hi, check out \"this book \". Its cool
It looks like it's a JSON string? Perhaps created using json_encode() in PHP on the server? You should use the proper JSON parser in iOS. Don't use regex as you will run into bugs.
// fetch the data, eg this might return "Hi, check out \"this book \". Its cool"
NSData *data = [NSData dataWithContentsOfURL:#"http://example.com/foobar/"];
// decode the JSON string
NSError *error;
NSString *responseString = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
// check if it worked or not
if (!responseString || ![responseString isKindOfClass:[NSString class]]) {
NSLog(#"failed to decode server response. error: %#", error);
return;
}
// print it out
NSLog(#"decoded response: %#", responseString);
The output will be:
Hi, check out "this book ". Its cool
Note: the JSON decoding API accepts an NSData object, not an NSString object. I'm assuming you also have a data object and are converting it to a string at some point... but if you're not, you can convert NSString to NSData using:
NSString *responseString = [NSJSONSerialization JSONObjectWithData:[myString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error];
More details about JSON can be found at:
http://www.json.org
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html

Get NSString From NSURL

I try to get NSString from NSURL with this method:
NSString *tmp2 = [item.path absoluteString];
Unfortunately I get instead of the NSURL:
<CFURL 0x173c50 [0x3f1359f8]>{type = 0, string = /var/mobile/Applications/A30FD2E4-A273-4522-AFD5-A981EFD3C2AA/Documents/*** *** - *** ***.***, encoding = 134217984, base = (null)}
I get :
file://localhost/var/mobile/Applications/A30FD2E4-A273-4522-AFD5-A981EFD3C2AA/Documents/***%20***%20-%20***%20***.***
any idea why?
The NSURL documentation clearly states that absoluteString returns an NSString, just like your code above. This is the string representation of the absolute path, so what you are getting is what you should be getting.
However, looking at the documentation you could also use path, relativePath or relativeString to get a string representation of the url in other formats (absolute or relative paths that either do or do not conform to RFC 1808 (a now obsolete percent encoding).

How to check if NSString is contains a numeric value?

I have a string that is being generate from a formula, however I only want to use the string as long as all of its characters are numeric, if not that I want to do something different for instance display an error message.
I have been having a look round but am finding it hard to find anything that works along the lines of what I am wanting to do. I have looked at NSScanner but I am not sure if its checking the whole string and then I am not actually sure how to check if these characters are numeric
- (void)isNumeric:(NSString *)code{
NSScanner *ns = [NSScanner scannerWithString:code];
if ( [ns scanFloat:NULL] ) //what can I use instead of NULL?
{
NSLog(#"INSIDE IF");
}
else {
NSLog(#"OUTSIDE IF");
}
}
So after a few more hours searching I have stumbled across an implementation that dose exactly what I am looking for.
so if you are looking to check if their are any alphanumeric characters in your NSString this works here
-(bool) isNumeric:(NSString*) hexText
{
NSNumberFormatter* numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
NSNumber* number = [numberFormatter numberFromString:hexText];
if (number != nil) {
NSLog(#"%# is numeric", hexText);
//do some stuff here
return true;
}
NSLog(#"%# is not numeric", hexText);
//or do some more stuff here
return false;
}
hope this helps.
Something like this would work:
#interface NSString (usefull_stuff)
- (BOOL) isAllDigits;
#end
#implementation NSString (usefull_stuff)
- (BOOL) isAllDigits
{
NSCharacterSet* nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
NSRange r = [self rangeOfCharacterFromSet: nonNumbers];
return r.location == NSNotFound && self.length > 0;
}
#end
then just use it like this:
NSString* hasOtherStuff = #"234 other stuff";
NSString* digitsOnly = #"123345999996665003030303030";
BOOL b1 = [hasOtherStuff isAllDigits];
BOOL b2 = [digitsOnly isAllDigits];
You don't have to wrap the functionality in a private category extension like this, but it sure makes it easy to reuse..
I like this solution better than the others since it wont ever overflow some int/float that is being scanned via NSScanner - the number of digits can be pretty much any length.
Consider NSString integerValue - it returns an NSInteger. However, it will accept some strings that are not entirely numeric and does not provide a mechanism to determine strings which are not numeric at all. This may or may not be acceptable.
For instance, " 13 " -> 13, "42foo" -> 42 and "helloworld" -> 0.
Happy coding.
Now, since the above was sort of a tangent to the question, see determine if string is numeric. Code taken from link, with comments added:
BOOL isNumeric(NSString *s)
{
NSScanner *sc = [NSScanner scannerWithString: s];
// We can pass NULL because we don't actually need the value to test
// for if the string is numeric. This is allowable.
if ( [sc scanFloat:NULL] )
{
// Ensure nothing left in scanner so that "42foo" is not accepted.
// ("42" would be consumed by scanFloat above leaving "foo".)
return [sc isAtEnd];
}
// Couldn't even scan a float :(
return NO;
}
The above works with just scanFloat -- e.g. no scanInt -- because the range of a float is much larger than that of an integer (even a 64-bit integer).
This function checks for "totally numeric" and will accept "42" and "0.13E2" but reject " 13 ", "42foo" and "helloworld".
It's very simple.
+ (BOOL)isStringNumeric:(NSString *)text
{
NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:text];
return [alphaNums isSupersetOfSet:inStringSet];
}
Like this:
- (void)isNumeric:(NSString *)code{
NSScanner *ns = [NSScanner scannerWithString:code];
float the_value;
if ( [ns scanFloat:&the_value] )
{
NSLog(#"INSIDE IF");
// do something with `the_value` if you like
}
else {
NSLog(#"OUTSIDE IF");
}
}
Faced same problem in Swift.
In Swift you should use this code, according TomSwift's answer:
func isAllDigits(str: String) -> Bool {
let nonNumbers = NSCharacterSet.decimalDigitCharacterSet()
if let range = str.rangeOfCharacterFromSet(nonNumbers) {
return true
}
else {
return false
}
}
P.S. Also you can use other NSCharacterSets or their combinations to check your string!
For simple numbers like "12234" or "231231.23123" the answer can be simple.
There is a transformation law for int numbers: when string with integer transforms to int (or long) number and then, again, transforms it back to another string these strings will be equal.
In Objective C it will looks like:
NSString *numStr=#"1234",*num2Str=nil;
num2Str=[NSString stringWithFormat:#"%lld",numStr.longlongValue];
if([numStr isEqualToString: num2Str]) NSLog(#"numStr is an integer number!");
By using this transformation law we can create solution
to detect double or long numbers:
NSString *numStr=#"12134.343"
NSArray *numList=[numStr componentsSeparatedByString:#"."];
if([[NSString stringWithFormat:#"%lld", numStr.longLongValue] isEqualToString:numStr]) NSLog(#"numStr is an integer number");
else
if( numList.count==2 &&
[[NSString stringWithFormat:#"%lld",((NSString*)numList[0]).longLongValue] isEqualToString:(NSString*)numList[0]] &&
[[NSString stringWithFormat:#"%lld",((NSString*)numList[1]).longLongValue] isEqualToString:(NSString*)numList[1]] )
NSLog(#"numStr is a double number");
else
NSLog(#"numStr is not a number");
I did not copy the code above from my work code so can be some mistakes, but I think the main point is clear.
Of course this solution doesn't work with numbers like "1E100", as well it doesn't take in account size of integer and fractional part. By using the law described above you can do whatever number detection you need.
C.Johns' answer is wrong. If you use a formatter, you risk apple changing their codebase at some point and having the formatter spit out a partial result. Tom's answer is wrong too. If you use the rangeOfCharacterFromSet method and check for NSNotFound, it'll register a true if the string contains even one number. Similarly, other answers in this thread suggest using the Integer value method. That is also wrong because it will register a true if even one integer is present in the string. The OP asked for an answer that ensures the entire string is numerical. Try this:
NSCharacterSet *searchSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
Tom was right about this part. That step gives you the non-numerical string characters. But then we do this:
NSString *trimmedString = [string stringByTrimmingCharactersInSet:searchSet];
return (string.length == trimmedString.length);
Tom's inverted character set can TRIM a string. So we can use that trim method to test if any non numerals exist in the string by comparing their lengths.