11

en_US我的 iOS 应用程序以不同的本地化( 、、、en_AU等)显示不同的货币(美元、日元、澳元、欧元ja_JP)。

对于日语地区/语言(两者都在我的设备上设置),如果我有:

NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
fmt.numberStyle = NSNumberFormatterCurrencyStyle;
fmt.currencyCode = @"JPY";
NSString *labelText = [fmt stringFromNumber:@1000000];

我的标签文本是¥1,000,000. 但是,在日文和中文中,可能会写入大于 10,000 的数字100万円,这是我想要的输出。

知道我可以编写什么代码100万円作为输出吗?

我想在我的代码检查语言环境/区域时避免逻辑块,但我觉得这就是我遇到的问题(例如,使用方法调用fmt.multipler = @(1/10000)将 1,000,000 除以 10,000 以获得正确的值)。

4

3 回答 3

4

EDIT: Up to date gist here: https://gist.github.com/fjolnir/cd72ea39be1476023adf

Old thread, but I came across it while looking for a solution so I figured I'd post my implementation.

The formatter itself does not handle placement of 円, but that's easy to do outside of it. (as the example below demonstrates)

The expected output of the below is:

2015-03-11 18:00:13.376 LENumberFormatter[82736:3604947] 12億3,460万円
2015-03-11 18:00:13.377 LENumberFormatter[82736:3604947] 25円

-

@import Foundation;
@import ObjectiveC.message;

typedef NS_ENUM(NSUInteger, LENumberFormatterAbbreviationStyle) {
    kLEAbbreviateShort, // 2.5m
    kLEAbbreviateNormal // 2m 5k
};

@interface LENumberFormatter : NSNumberFormatter
@property(nonatomic) BOOL abbreviateLargeNumbers;
@property(nonatomic) LENumberFormatterAbbreviationStyle abbreviationStyle;
@end


@implementation LENumberFormatter
- (instancetype)init
{
    if((self = [super init])) {
        self.abbreviationStyle = [self _usingKanjiNumbers]
                               ? kLEAbbreviateNormal
                               : kLEAbbreviateShort;
    }
    return self;
}

- (NSString *)stringForObjectValue:(id const)aObj
{
    if(!_abbreviateLargeNumbers || ![aObj isKindOfClass:[NSNumber class]])
        return [super stringForObjectValue:aObj];

    // Copy ourselves to get format the partial digits using the settings on self
    LENumberFormatter * const partialFormatter = [self copy];
    partialFormatter.currencySymbol = @"";
    if(_abbreviationStyle == kLEAbbreviateNormal)
        partialFormatter.maximumFractionDigits = 0;

    NSString *(^partialFormat)(NSNumber*) = ^(NSNumber *num) {
        NSString *(*superImp)(struct objc_super*,SEL,NSNumber*) = (void*)&objc_msgSendSuper;
        return superImp(&(struct objc_super) { partialFormatter, self.superclass }, _cmd, num);
    };

    double n = [aObj doubleValue];
    BOOL const shortFormat = _abbreviationStyle == kLEAbbreviateShort;

    NSDictionary * const separators         = [self _localizedGroupingSeparators];
    NSArray      * const separatorExponents = [separators.allKeys sortedArrayUsingSelector:@selector(compare:)];

    BOOL const currencySymbolIsSuffix = [self.positiveFormat hasSuffix:@"¤"];
    NSMutableString * const result = currencySymbolIsSuffix || self.numberStyle != NSNumberFormatterCurrencyStyle
                                   ? [NSMutableString new]
                                   : [self.currencySymbol mutableCopy];
    NSUInteger significantDigits = 0;
    NSNumber *lastExp = nil;
    for(NSNumber *exp in separatorExponents.reverseObjectEnumerator) {
        double divisor = pow(10, exp.shortValue);
        if(divisor > n)
            continue;

        if(lastExp)
            significantDigits += lastExp.doubleValue - exp.doubleValue;
        lastExp = exp;

        if(self.usesSignificantDigits && significantDigits >= self.maximumSignificantDigits)
            break;

        double partialNum = shortFormat
                          ? n/divisor
                          : floor(n/divisor);
        NSString * const digits = [self _groupRecursively] && ![exp isEqual:@0]
                                ? [partialFormatter stringFromNumber:@(partialNum)]
                                : partialFormat(@(partialNum));
        [result appendFormat:@"%@%@", digits, separators[exp]];

        n = fmod(n, divisor);

        if(shortFormat)
            break; // Just use a float+first hit

        // If we make it here, partialNum is integral and we can use log10 to find the number of digits
        significantDigits += log10(partialNum) + 1;
        partialFormatter.maximumSignificantDigits -= digits.length;

    }
    if(n > 0
       && !shortFormat
       && (!self.usesSignificantDigits || significantDigits < self.maximumSignificantDigits))
    {
        partialFormatter.maximumFractionDigits = self.maximumFractionDigits;
        [result appendString:partialFormat(@(n))];
    }
    if(self.numberStyle == NSNumberFormatterCurrencyStyle && currencySymbolIsSuffix && self.currencySymbol)
        [result appendString:self.currencySymbol];

    return result.length > 0
         ? [result stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]
         : [super stringForObjectValue:aObj];
}

- (BOOL)_usingKanjiNumbers
{
    return [self.locale.localeIdentifier rangeOfString:@"^(ja|zh)_"
                                               options:NSRegularExpressionSearch].location != NSNotFound;
}
- (NSDictionary *)_localizedGroupingSeparators
{
    if(self._usingKanjiNumbers)
        return @{ @2: @"百", @3: @"千", @4: @"万", @8: @"億" };
    else {
        NSBundle * const bundle = [NSBundle bundleForClass:self.class];
        return @{ 
            @3: [bundle localizedStringForKey:@"thousandSuffix" value:@"k " table:nil],
            @6: [bundle localizedStringForKey:@"millionSuffix"  value:@"m " table:nil]
        };
    }
}

- (BOOL)_groupRecursively
{
    // Return _usingKanjiNumbers if you want:
    // 12億3千4百56万7千8百90
    // Rather than:
    // 1億2,3456万7千8百90
    return NO;
}

- (instancetype)copyWithZone:(NSZone * const)aZone
{
    LENumberFormatter * const copy = [super copyWithZone:aZone];
    copy.abbreviateLargeNumbers = _abbreviateLargeNumbers;
    copy.abbreviationStyle      = _abbreviationStyle;
    return copy;
}
@end


int main(int argc, char *argv[]) {
    @autoreleasepool {
        LENumberFormatter * const f = [LENumberFormatter new];
        f.locale = [NSLocale localeWithLocaleIdentifier:@"ja_JP"];
//        f.locale = [NSLocale localeWithLocaleIdentifier:@"en_US"];
        f.numberStyle = NSNumberFormatterCurrencyStyle;
        f.abbreviateLargeNumbers = YES;
        f.abbreviationStyle = kLEAbbreviateNormal; // Automatic if using system locale
        f.maximumSignificantDigits = 5;
        f.usesSignificantDigits = YES;
//        f.currencyCode   = @"JPY";
//        f.currencySymbol = @"¥";

        if([f.locale.localeIdentifier hasPrefix:@"ja"]) {
            f.positiveFormat = @"#,##0¤";
            if([f.currencyCode isEqualToString:@"JPY"])
                // We allow ourselves this special case because *日本円 just looks dumb
                f.currencySymbol = @"円";
            else
                f.currencySymbol = [f.locale displayNameForKey:NSLocaleCurrencyCode
                                          value:f.currencyCode];
        }

        NSLog(@"%@", [f stringFromNumber:@1234567890]);
        NSLog(@"%@", [f stringFromNumber:@25]);
    }
}
于 2015-03-11T09:06:44.887 回答
2

In the end, I subclassed NSNumberFormatter and overrode stringWithNumber:.

Here is the relevant code I used to re-configure the NSNumberFormatter when the currency code is JPY.

  NSString *localeString = [self.locale localeIdentifier];
  if ([localeString isEqualToString:@"ja_JP"])
  {
    // 1-oku
    if (num >= 100000000)
    {
      self.negativeFormat = @"-#,###0億円";
      self.positiveFormat = @"#,###0億円";
      self.multiplier = @(1.0f/100000000.0f);
    }
    // 1-man
    else if (num >= 10000)
    {
      self.negativeFormat = @"-#,###0万円";
      self.positiveFormat = @"#,###0万円";
      self.multiplier = @(1.0f/10000.0f);
    }
    // Less than 10,000
    else
    {
      self.negativeFormat = @"-#,###0円";
      self.positiveFormat = @"#,###0円";
    }
  }
  // This could be en_AU, en_UK, en_US -- but all use "million yen"
  else if ([localeString hasPrefix:@"en"])
  {
    // We only care about 1M JPY+
    if (num >= 1000000)
    {
      self.negativeFormat = @"-¥#,###0M";
      self.positiveFormat = @"¥#,###0M";
      self.multiplier = @(1.0f/1000000.0f);
    }
  }
于 2013-01-23T06:59:24.580 回答
1
于 2013-01-23T03:04:32.650 回答