This seems like a super basic question, but I just can't seem to find the answer anywhere :-( I am able to do this in Objective C, but I am getting stuck in Swift.
What I need to do:
Take an Integer value
Format it into a localized string
Inject the value into another string using the stringWithFormat equivalent method (since the other string is localized as well, which is not shown in simplified examples below)
How it's easily done in Objective C -- this works:
// points is of type NSNumber *
NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.locale = [NSLocale currentLocale];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSString *ptsString = [formatter stringFromNumber:points];
NSString *message = [NSString stringWithFormat:#"You've earned %# points", ptsString];
My best attempt at doing this in Swift -- compiler error on last line:
// points is of type Int
let formatter = NSNumberFormatter()
formatter.locale = NSLocale.currentLocale()
formatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
let ptsString = formatter.stringFromNumber(points)!
let message = String(format: "You've earned %# points", arguments: ptsString)
I'm getting the following error in Xcode on that last line:
"Cannot convert value of type 'String' to expected argument type '[CVarArgType]'"
(In my actual code, the message into which I want to insert the points value is itself localized as well, but I have simplified this example, as I'm getting the exact same error in both cases.)
What am I missing here..?
Thanks so much for any help,
Erik
You need to wrap the arguments in a collection. Like this:
let message = String(format: "You've earned %# points", arguments: [ptsString])
You can also use this method:
let message = "You've earned \(ptsString) points"
Additionally you can create an extension method to do this:
extension String {
func format(parameters: CVarArgType...) -> String {
return String(format: self, arguments: parameters)
}
}
Now you can do this:
let message = "You've earned %# points".format("test")
let message2params = "You've earned %# points %#".format("test1", "test2")
Sometimes, you need a little more control - so if you need to have leading zeros, you could use 'stringWithFormat' just like in objective-C
let ptsString = String(format: "%02d", points)
Related
So right now I have the following code:
- (NSString*)convertToLocalCurrencyFormat:(NSDecimalNumber*)result {
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
formatter.currencyCode = self.comparisonCurrency;
formatter.usesSignificantDigits = YES;
return [formatter stringFromNumber:result];
}
When I pass in an NSDecimalNumber* containing 678071967196719797153475347466.94627863, it gets formatted to ¥678,072,000,000,000,000,000,000,000,000 (with the currencyCode set to JPY). If I leave out the formatter.usesSignificantDigits = YES line, then it gets formatted to ¥678,071,967,196,719,797,153,475,347,467, closer, but still dropping the decimal and following values.
However, when I pass in 6780.0416000000012517376, it's formatted correctly to ¥6,780.04 with the significant digits line. It gets formatted to ¥6,780 without the significant digits line.
I know that NSNumberFormatter can take in any NSNumber as a parameter, but can only deal with values as precise as doubles, leaving NSDecimalNumber with no errors and incorrect results.
How can I format NSDecimalNumbers with currency codes without loss of precision?
Thanks
Try setting the minimum fraction digits instead:
formatter.minimumFractionDigits = 2;
HTH
I am learning to localise my app to Simplified Chinese. I am following this tutorial on how to do this.
Because the tutorial is based on Obj-C, formatted strings can be written like this:
"Yesterday you sold %# apps" = "Ayer le vendió %# aplicaciones";
"You like?" = "~Es bueno?~";
But I am using Swift. And in Swift I don't think you can use %# to indicate that there is something to be placed there. We have string interpolation right?
My app is kind of related to maths. And I want to display which input(s) is used to compute the result in a detailed label of a table view cell. For example
--------------
1234.5678
From x, y <---- Here is the detailed label
--------------
Here, From x, y means "The result is computed from x and y". I want to translate this to Chinese:
从 x, y 得出
Before, I can just use this:
"From \(someVariable)"
with the strings file:
"From" = "从 得出";
And this is how I would use it in code
"\(NSLocalizedString("From", comment: "")) \(someVariable)"
But if this were used in the Chinese version, the final string will be like this:
"从 得出 x, y"
I mean I can put the 从 and 得出 in two different entries in the strings file. But is there a better way to do it?
You can use %# in Swift's String(format:...), it can be substituted
by a Swift String or any instance of a NSObject subclass.
For example, if the Localizable.strings file contains the definition
"From %#, %#" = "从 %#, %# 得出";
then
let x = 1.2
let y = 2.4
let text = String(format: NSLocalizedString("From %#, %#", comment: ""), "\(x)", "\(y)")
// Or alternatively:
let text = String(format: NSLocalizedString("From %#, %#", comment: ""), NSNumber(double: x), NSNumber(double: y))
produces "从 1.2, 2.4 得出". Another option would be to use the
%f format for double floating point numbers:
"From %f, %f" = "从 %f, %f 得出";
with
let text = String(format: NSLocalizedString("From %f, %f", comment: ""), x, y)
See Niklas' answer
for an even better solution which localizes the number representation
as well.
From WWDC 2017:
let format = NSLocalizedString("%d popular languages", comment:"Number of popular languages")
label.text = String.localizedStringWithFormat(format, popularLanguages.count)
Swift localize a string
One more simple example
let changeable = "something"
let result = String(format: NSLocalizedString("stringId", comment: ""), arguments: [changeable]) // Hello World and something
localizable.strings with
"stringId" = "Hello World and %#";
comment parameter doesn't have effect on result and is used for translators and by genstrings code-gen as comment
New in iOS 15 and macOS Monterey you can use the new refined method for String.
String(localized: "From \(x), \(y)", comment: "The result is computed from x and y")
They did a lot of updates in 2021 for localization with Xcode. Check this video from WWDC21 for more info.
https://developer.apple.com/videos/play/wwdc2021/10221/
In objective C, if we want to get strings added at runtime as below
John Appleseed is the name
YourLocalizable.strings
"theStringToDisplay" = "%# is the name";
ViewController.m
NSString *username = #"John Appleseed";
NSString *messageBeforeFormat = NSLocalizedStringFromTable(#"theStringToDisplay", #"YourLocalizable", nil);
NSString *messageAfterFormat = [NSString stringWithFormat:messageBeforeFormat, username ];
self.yourLabel.text = messageAfterFormat;
Further explanation in this nice post
https://www.oneskyapp.com/academy/learn-ios-localization/2-format-strings-plurals/
In Swift, how would you create an NSNumberFormatter that would preserve trailing zeros after a decimal (12.000) while also generating a number appropriate for the current locale?
My current code and example output:
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.locale = NSLocale.currentLocale()
formatter.maximumFractionDigits = 10
var doubleNumString = "12.0"
println(formatter.numberFromString(doubleNumString)) //in English it prints 12, want it to print 12.0
var doubleNumString = "12.000"
println(formatter.numberFromString(doubleNumString)) //prints 12, want it to print 12.000
var doubleNumString = "12.125"
println(formatter.numberFromString(doubleNumString)) //prints 12.125 as expected
var doubleNumString = "1234"
println(formatter.numberFromString(doubleNumString)) //prints 1,234 as expected
I've already coded it such that if the string ends in a decimal ("12.") then it won't use this formatter to generate the number and will instead just display the number then the decimal (but I will need to improve that because some languages read right to left).
One solution would be to check if the string contains a period and if so, check if all digits that follow it are 0, and if so then don't run it through the number formatter and instead run only the int value through the formatter then append/prepend the decimal followed by the appropriate number of 0's.
Is there a better/cleaner solution?
As mentioned by Martin R, you can set the minimumFractionDigits and maximumFractionDigits to the same number which will enforce that many fraction digits always be displayed. To know how many to display you need to take a substring after the decimal to the end and count its elements. To know whether or not all of the fraction digits are 0's, I created a helper method that converts that substring to a number and if it equals 0 then you know they were all 0's.
Unfortunately you need to convert the string to a localized number using a couple different NSNumberFormatters based on the original string number. So if it does contain a decimal and everything after it is a 0 then you need to create a different formatter, convert the string to a number, then convert that number to a string in order to display it respecting the user's locale. Otherwise you can just use your original number formatter.
This function takes care of your requirement. pass same for & from locale (e.g. en_US)
+ (NSString*) stringForString:(NSString*) string forLocale:(NSString*) toLocaleCode fromLocal:(NSString*) fromLocaleCode {
NSLocale *fromLocale = [[NSLocale alloc] initWithLocaleIdentifier:fromLocaleCode];
NSNumberFormatter *sourceFormatter = [[NSNumberFormatter alloc] init];
[sourceFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[sourceFormatter setUsesGroupingSeparator:NO];
[sourceFormatter setLocale:fromLocale];
NSNumber *localizedNumber = [sourceFormatter numberFromString:string];
if (!localizedNumber) {
return string;
}
NSLocale *toLocale = [[NSLocale alloc] initWithLocaleIdentifier:toLocaleCode];
NSNumberFormatter *destinationFormatter = [[NSNumberFormatter alloc] init];
[destinationFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[destinationFormatter setUsesGroupingSeparator:NO];
[destinationFormatter setLocale:toLocale];
NSString *localizedString = [destinationFormatter stringFromNumber:localizedNumber];
//add the zeros which were dropped because of the sourceDecimalString number conversion e.g. 0.20 is converted to 0.2
if (localizedString.length < string.length) {
NSRange rangeOfDecimal = [string rangeOfString:sourceFormatter.decimalSeparator];
if (rangeOfDecimal.location != NSNotFound) {
NSString* sourceDecimalString = [string substringFromIndex:rangeOfDecimal.location];
rangeOfDecimal = [localizedString rangeOfString:destinationFormatter.decimalSeparator];
if (rangeOfDecimal.location != NSNotFound) {
NSString* destinationDecimalString = [localizedString substringFromIndex:rangeOfDecimal.location];
if (destinationDecimalString.length < sourceDecimalString.length) {
int difference = sourceDecimalString.length - destinationDecimalString.length;
int toalDecimalDigits = (destinationDecimalString.length - 1) + difference; //-1 to remove '.'
destinationFormatter.minimumFractionDigits = toalDecimalDigits;
destinationFormatter.maximumFractionDigits = toalDecimalDigits;
localizedString = [destinationFormatter stringFromNumber:localizedNumber];
}
}
else{//this indicates no decimal separator in the return string
int toalDecimalDigits = (sourceDecimalString.length - 1); //-1 to remove '.'
destinationFormatter.minimumFractionDigits = toalDecimalDigits;
destinationFormatter.maximumFractionDigits = toalDecimalDigits;
localizedString = [destinationFormatter stringFromNumber:localizedNumber];
}
}
}
return localizedString;
}
I'm trying to figure how to append some parameters to a localized string?
The desired result (in a string) is "TITLE-xxxx ddmmyy".
TITLE is to be localized.
xxxx is a calculated number and ddmmyy is a fixed date format.
Thanks for any help!
Given TITLE, myDateFormatter which is a NSDateFormatter and number, which is an int:
NSString* myString = [NSString stringWithFormat:#"%#-%d %#", NSLocalizedString(TITLE, #"comment"), number, [myDateFormatter stringFromDate:date]];
Of course you can change number to an NSNumber or something if you wish so (change %d to %# then).
I am putting the finishing touches on my app and for some reason the values are not being shown as a decimal value.
if ( row == 0 ) {
float number1 = ([initamount.text floatValue ]);
initamount.text = [[NSString alloc]initWithFormat:#"%2.f Yard", number1];
double answer = number1 *3;
result.text = [[NSString alloc]initWithFormat:#"%2.f Feet", answer];
}
here is the code i am really tired and could be missing something.
Correct format string should be (note that desired precision should come after '.' in format specifier)
#"%.2f Yard"