LocaleはiOSとmacOSのFoundationクラスで、ユーザーの言語的・文化的な慣習(日付形式、数値形式、通貨、測定単位)をカプセル化します。Apple Developer Documentation(2024年)によると、LocaleはDateFormatterが月をどう表示するか(1月またはJanuary)、数値の小数点区切り(カンマかドットか)、通貨記号(ルーブル、ドル、ユーロ)を決定します。各Localeインスタンスはru_RUやen_USのような識別子に結びついており、最初の部分は言語コード(ISO 639-1)、2番目の部分は地域コード(ISO 3166-1)です。TimeZoneとは異なり、Localeは時刻の絶対値には影響せず、文字列表現にのみ影響します。
ポイント
LocaleはSwiftの値型(Objective-CではNSLocale)で、特定の言語と地域に固有の書式ルールを表します。時刻の絶対オフセットを決定するTimeZoneとは異なり、Localeは時刻、数値、通貨が文字列表現でどのように見えるかを決定します。同じ日付2024-07-21でも、ru_RUでは“2024年7月21日”、en_USでは“July 21, 2024”と表示されます。
各Localeインスタンスは2つのコンポーネントで構成されます:言語(月名、曜日名、語順を決定)と地域(数値形式、通貨、カレンダーを決定)。これらの組み合わせはBCP 47標準に従って識別子にエンコードされます:ru_RU(ロシア語、ロシア)、en_US(英語、アメリカ)、de_DE(ドイツ語、ドイツ)。
Unicode CLDR(2024年)によると、iOSでサポートされるロケールの数は700以上の言語と地域の組み合わせを超えています。FoundationはUnicodeコンソーシアムが維持する最も包括的なローカライゼーションデータリポジトリであるCLDR(Common Locale Data Repository)のデータを使用しています。これにより、すべてのAppleデバイスで一貫した書式が保証されます。
DateFormatterはLocaleを使用して、正しい月名と曜日名を選択し、日付コンポーネントの順序(日/月/年または月/日/年)と区切り文字を決定します。Localeを明示的に指定しない場合、DateFormatterはデバイスのロケールを使用します。これはUIには適切ですが、書式が固定されるべきサーバーデータには危険です。
| コンポーネント | ru_RU | en_US | de_DE |
|---|---|---|---|
| 日付(medium) | 2024年7月21日 | Jul 21, 2024 | 21.07.2024 |
| 数値(1000.5) | 1 000,5 | 1,000.5 | 1.000,5 |
| 通貨(100) | 100,00 ₽ | $100.00 | 100,00 € |
| カレンダー | グレゴリオ暦 | グレゴリオ暦 | グレゴリオ暦 |
| リスト区切り | ; | , | ; |
NumberFormatterはLocaleを使用して、小数点区切り(カンマまたはドット)、グループ区切り(スペース、カンマ、ドット)、通貨記号を決定します。数値解析時にLocaleを無視することは、国際化アプリケーションにおけるバグの一般的な原因の1つです:数値“1,5”はru_RUでは“1.5”を意味しますが、en_USの数値パーサーはカンマの後の“5”として読み取ります。
重要:Calendar.currentを介して作成されたCalendarはデバイスのロケールを継承します。明示的にロケールを設定したCalendar(identifier: .gregorian)が、予測可能な書式設定の推奨方法です。ISO 8601の日付を扱う場合は、常にLocale(identifier: “en_US_POSIX”)を使用してください。これは地域設定の影響を受けない技術的な書式設定のための特別なロケールです。
Locale識別子は言語コード(ISO 639-1、2文字)と地域コード(ISO 3166-1、2文字)をアンダースコアで区切って構成されます。例:ru_RU、en_US、fr_FR、zh_Hans_CN(中国語、簡体字、中国)。FoundationはWeb標準で使用されるBCP 47形式(ru-RU、en-US)の識別子もサポートしています。
完全な識別子の他に、Localeは言語のみで作成することもできます:Locale(identifier: “ru”)はロシア語とその言語のデフォルト地域(通常はロシア)でロケールを返します。英語も同様に、Locale(identifier: “en”)は米国地域を使用します。この方法は、特定の地域に縛られずに書式言語を設定するのに便利です。
特別なロケールにはen_US_POSIXがあります。これは日付と数値の機械的な書式設定のための技術ロケールで、ユーザー設定に関係なく安定した形式を保証します。このロケールはサーバーAPIからの日付解析、特にISO 8601形式に必須です。グレゴリオ暦、24時間制、小数点区切りとしてドットを使用します。
import Foundation
// 利用可能なロケール識別子
let available: [String] = Locale.availableIdentifiers
print("総ロケール数: \(available.count)")
// ロシアのロケールをフィルタ
let russianLocales = available.filter { $0.hasPrefix("ru") }
print("ロシアのロケール: \(russianLocales)")
// ロケールコンポーネント
let locale = Locale(identifier: "de_DE")
print("言語: \(locale.languageCode ?? "nil")")
print("地域: \(locale.regionCode ?? "nil")")
print("通貨: \(locale.currencyCode ?? "nil")")
print("カレンダー: \(locale.calendar.identifier)")
利用可能なロケールの確認はLocale.availableIdentifiersで現在のiOSバージョンがサポートするすべての識別子の配列を返します。地域でフィルタリングするには、regionCodeチェック付きでLocale.availableIdentifiers.filterを使用します。これはハードコードされたリストなしで地域選択UIを構築するのに便利です。
Locale.currentは、iOSの設定(Settings > General > Language & Region)でユーザーが設定したデバイスの現在のロケールを取得する主要な方法です。このプロパティは、アプリケーションを再起動せずに設定で言語や地域を変更すると自動的に更新されます。ただし、コンテンツ表示に適したロケールと一致しない場合があります。ユーザーはインターフェース言語を英語に設定しても、日付をロシア形式で表示したい場合があります。
ユーザーの好みをより正確に判断するには、Locale.preferredLanguagesを使用します。これはユーザーの優先順位で並べられた言語の配列です。最初の要素がプライマリインターフェース言語です。このリストはLanguage & Regionの設定に対応しており、優先順に言語をドラッグして並べ替えることも含みます。コミュニケーションアプリ(メッセンジャー、メールクライアント)は、コンテンツ表示言語を選択する際にこの順序を考慮する必要があります。
import Foundation
// 現在のシステムロケール
let current = Locale.current
print("現在のロケール: \(current.identifier)")
print("言語: \(current.language?.disjointName ?? "nil")")
// ユーザーの優先言語
let preferred = Locale.preferredLanguages
print("優先言語: \(preferred)")
// 現在のロケールから地域を取得
if let region = current.regionCode {
let regionLocale = Locale(identifier: "en_\(region)")
let countryName = regionLocale.localizedString(
forRegionCode: region
)
print("国: \(countryName ?? region)")
}
// 24時間形式を確認
let uses24h = current.uses24hClock(
for: .dateAndTime
)
print("24時間制を使用: \(uses24h)")
UIのローカライゼーション:インターフェース言語で月名や曜日名を表示するには、ロケールを設定したCalendarを使用します。Calendar.current.symbols(for: .month)は現在のロケールの言語で月名を返します。ユーザーの言語で国名を表示するには、Locale.current.localizedString(forRegionCode:)を使用します。
日付の書式設定をロケール対応で行うことは、ユーザーに日付を表示する際の重要なタスクです。ロケールを設定したDateFormatterは、ユーザーの地域に適した日付と時刻の形式を自動的に選択します。dateStyleとtimeStyleの値が.short、.medium、.long、.fullの場合、フォーマッタはロケールのルールに従って日付コンポーネントを構成します。
import Foundation
let date = Date()
// 異なるロケールでの書式設定
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.locale = Locale(identifier: "ru_RU")
print("ロシア語: \(formatter.string(from: date))")
formatter.locale = Locale(identifier: "en_US")
print("英語: \(formatter.string(from: date))")
formatter.locale = Locale(identifier: "ja_JP")
print("日本語: \(formatter.string(from: date))")
// ロケールを使った通貨書式設定
let numFormatter = NumberFormatter()
numFormatter.numberStyle = .currency
numFormatter.locale = Locale(identifier: "de_DE")
print("ドイツの通貨: \(numFormatter.string(from: 1234.56) ?? "nil")")
numFormatter.locale = Locale(identifier: "en_US")
print("アメリカの通貨: \(numFormatter.string(from: 1234.56) ?? "nil")")
サーバーAPIからの日付解析は、固定形式のために常にLocale(identifier: “en_US_POSIX”)を使用する必要があります。サーバーは通常、英語の月名でISO 8601形式の日付を送信し、現在のデバイスのロケールを使用すると、ユーザーが非英語圏にいる場合に解析エラーが発生する可能性があります。en_US_POSIXは解析がデバイス設定に依存しないことを保証します。
import Foundation
// 正しいサーバー日付解析
let isoFormatter = DateFormatter()
isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
isoFormatter.locale = Locale(identifier: "en_US_POSIX")
isoFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let serverDate = "2024-07-21T14:30:00+0000"
if let parsed = isoFormatter.date(from: serverDate) {
print("解析された日付: \(parsed)")
}
// ローカライズされた通貨名
let usLocale = Locale(identifier: "en_US")
let currencyName = usLocale.localizedString(
forCurrencyCode: "RUB"
)
print("アメリカのロケールでのロシアルーブル: \(currencyName ?? "nil")")
追加機能:LocaleはlocalizedString(forRegionCode:)、localizedString(forLanguageCode:)、localizedString(forCurrencyCode:)、localizedString(forCalendarIdentifier:)メソッドを通じて、コンポーネントのローカライズされた説明を提供します。これらのメソッドは、呼び出し元のロケールの言語で名前を返します。例えば、Locale(identifier: “ru_RU”).localizedString(forCountryCode: “DE”)は“ドイツ”を返します。
数値解析時のLocaleの無視は国際化アプリケーションにおける重大なエラーです。明示的なロケールなしのNumberFormatterは現在のデバイスのロケールを使用します。ロシアのユーザーが“1,5”を入力すると、NumberFormatter.number(from: “1,5”)は正しく1.5を返します。しかし、同じコードがen_USロケールのデバイスで実行されると、en_USの小数点区切りはドットであるため、解析はnilを返します。
サーバー日付に対するen_US_POSIXの欠如は微妙なバグを引き起こします。dateFormatとlocale = Locale.currentを持つDateFormatterは、日付形式がアメリカと異なる地域のユーザーで壊れる可能性があります。例えば、ドイツではDateFormatterが“21.07.2024”を期待する一方、サーバーは“07/21/2024”を送信します。en_US_POSIXはユーザーの地域に関係なく、機械解析のための固定形式を保証します。
Dateオブジェクトではなく日付文字列の比較もよくある間違いです。開発者は異なるロケールからの日付の文字列表現を比較して、誤った結果を得ることがあります。Localeは表示のみを変更し、日付の絶対値は変更しません。常にDateオブジェクトを比較し、文字列表現は比較しないでください。日付コンポーネントを比較するには、明示的にロケールを設定したCalendarを使用してください。
WWDC 2023によると、アプリケーションの国際化問題の約30%がLocaleの誤った設定に関連しています。Appleは、サーバーデータを扱う際には常にDateFormatterとNumberFormatterにロケールを明示的に設定し、UI表示にのみLocale.currentを使用することを推奨しています。この簡単な習慣で、地域設定に関連するほとんどのバグが解消されます。
よくある質問
Localeは文化的・言語的な書式ルール(日付形式、数値形式、通貨、測定単位)を表すFoundationクラスです。DateFormatter、NumberFormatter、Calendarと共にローカライズされたデータ表示に使用されます。
Localeは表示形式(言語、地域の慣習)を決定し、TimeZoneはUTCからの絶対的な時刻オフセットを決定します。Localeは文字列表現に影響し、TimeZoneは時刻の数値に影響します。両方とも完全な日付書式設定のために一緒に使用されます。
en_US_POSIXはユーザー設定に関係なく安定した形式を保証する技術的な書式設定のための特別なロケールです。サーバーの日付(ISO 8601)を解析し、形式が予測可能である必要があるAPIを扱うために必須です。
Locale.availableIdentifiersはサポートされているすべてのロケールの識別子の文字列配列を返します。言語でフィルタリングするにはhasPrefixでfilterを使用し、地域を取得するにはLocale(identifier:).regionCodeを使用します。
NumberFormatterはLocaleを使用して小数点区切り(カンマまたはドット)、通貨記号、グループ区切りを決定します。固定形式の場合は、ロケールをen_US_POSIXに設定するか、フォーマッタのプロパティを明示的に設定してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。