麦芽を支える技術

麦芽(ばくが、英語:malt)とは、麦、特に大麦の種子を発芽させたもので、ビール、ウイスキー、水飴の原料となる。(Wikipediaより)

Xcode11からja_JPロケールのNumberFormatter出力でnbspが入るようになった

事の発端

元々以下のように数値を通貨形式の文字列フォーマットに変換する処理のユニットテスト書いてあったんだけど、Xcode11にしてからテストが失敗するようになってしまった。

let target = 1000
XCTAssertEqual(target.yenFormattedString, "1,000円")  // "1,000 円" is not equal "1,000円"

ちなみに、ここでやっている .yenFormattedString は以下のようなIntのExtension。

extension Int {
    var yenFormattedString: String {
        let formatter = NumberFormatter()
        formatter.locale = Locale(identifier: "ja_JP")
        formatter.numberStyle = .currencyPlural
        return numberFormatter.string(from: self as NSNumber) ?? ""
    }
}

"1,000 円" is not equal "1,000円" と言われているので、の前に半角スペース入るように変わったのかな?と思い、テストを修正してみたら以下のようになった。

let target = 1000
XCTAssertEqual(target.yenFormattedString, "1,000 円")  // "1,000 円" is not equal "1,000 円"

"1,000 円" is not equal "1,000 円" ??

文字コード調査

これは普通の半角スペースじゃなくなんか謎の文字が入ってるのでは?と考え、エラーになった文字列をコピーしてodコマンドで確認してみたら以下のような違いが発覚。

# 通常の半角スペース
$ echo "1,000 円" | od -tx1 -a
0000000    31  2c  30  30  30  20  e5  86  86  0a                        
           1   ,   0   0   0  sp     86  86  nl                        
# NumberFormatter出力
$ echo "1,000 円" | od -tx1 -a
0000000    31  2c  30  30  30  c2  a0  e5  86  86  0a                    
           1   ,   0   0   0           86  86  nl                    

0x20 が通常の半角スペースだけど、今回の出力結果は 0xC2A0 だった。

0xC2A0 ググってみたらnbsp(Non-Breaking SPace)とのこと。

  は半角スペースではないというお話 (フェンリル | デベロッパーズブログ)

ロケールでは元々起きていたらしい

もうちょっと調べてみたら、ユーロなどの通貨では結構前から同じことになっていたらしい。

【追記】XCTAssertEqualが("foo bar") is not equal to ("foo bar")などと寝ぼけたことを言う【NSNumberFormatter】 - DRYな備忘録

iphone - NSNumberFormatter, how to remove blank spaces in currency symbol - Stack Overflow

確かに手元のXcode10系で試したら同じようになった。 Xcode11からja_JPロケールもこれと同じ動きになった模様。

f:id:asmz0:20190928112119p:plain:w500
Xcode10.3だとユーロはnbsp入るが日本円はnbsp入らない

f:id:asmz0:20190928112231p:plain:w500
Xcode11.0だとユーロでも日本円でもnbsp入る

バグなのか仕様なのか🤔

通貨表現だから、UILabelなどで勝手にそこでワードラップされないようにnbspになっていると考えれば、理に適ってるのかもしれない。

ただちょっと正式にこういう仕様になったのかどうかのドキュメント見つけられていないので、誰か知ってたら教えてくださいm( )m

とりあえず現状はテストの期待結果にnbsp入れてテストを通してる。