System.Globalizationでグローバル指向プログラミング

注意:
この文書は以前「.NETでいきまっしょい!」で公開していたものですが、公開以降メンテナンスされていません。 今や古い情報となった内容が記載されている場合があるのでご注意ください。

1.現在のカルチャ情報を取得


 System.Globalization名前空間にあるクラスを利用すれば、カレンダーや書式などの部分を国際対応することができます。 そのまえに、ひとまず国際化というのを置いておいて、自分の国(現在のカルチャ)の情報を取得することから始めます。 CultureInfoクラスは、カルチャに関する様々な情報を扱うためのクラスです。 このクラスの共有読み取り専用プロパティであるCurrentCultureを参照すれば、現在のカルチャ(OSが使用しているカルチャ)の情報を取得することができます。
現在のカルチャ情報を取得
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        ' 現在のカルチャ
        Dim culture As CultureInfo = CultureInfo.CurrentCulture

        ' カルチャ名
        Console.WriteLine(culture.Name)
        Console.WriteLine(culture.DisplayName)
        Console.WriteLine(culture.EnglishName)
        Console.WriteLine(culture.NativeName)
        Console.WriteLine(culture.ThreeLetterISOLanguageName)

        ' カルチャ情報
        Console.WriteLine(culture.Calendar)
        Console.WriteLine(culture.DateTimeFormat.LongDatePattern)
        Console.WriteLine(culture.DateTimeFormat.LongTimePattern)
        Console.WriteLine(culture.DateTimeFormat.ShortDatePattern)
        Console.WriteLine(culture.DateTimeFormat.ShortTimePattern)
        Console.WriteLine(culture.TextInfo.ANSICodePage)
        Console.WriteLine(culture.NumberFormat.CurrencySymbol)

    End Sub

End Module
出力結果
ja-JP
日本語 (日本)
Japanese (Japan)
日本語 (日本)
jpn
System.Globalization.GregorianCalendar
yyyy'年'M'月'd'日'
H:mm:ss
yyyy/MM/dd
H:mm
932
\
Press any key to continue

 前半のコードではカルチャ名に関する文字列を出力しています。 基本的なカルチャの名前は一行目の出力のように「xx-XX」となっています。 前の二文字は言語を表し、後の二文字は国(地域)を表します。 たとえば英語-米国の場合は「en-US」となります。 そのほかにも表記方法が数種類あり、前半の出力がそれを出力したものです。
 後半のコードは、カルチャ特有の情報を取得し、表示するものです。 Calenderプロパティは使用するカレンダーを表し、Calendarクラスの派生クラスになります。 この場合ではGregorianCalendarクラス、つまりグレゴリオ暦を使用していることがわかります。 DateTimeFormat、NumberFormatからは日時・数値などの表記に関する情報、TextInfoからはコードページなどに関する情報が取得できます。

2.他のカルチャ情報を取得


 このように現在のカルチャについての情報を取得できたわけですが、当然他のカルチャの情報を取得することもできます。 CultureInfoのインスタンスを生成する際に、コンストラクタにカルチャ名を指定することでそのカルチャの情報を記述したCultureInfoオブジェクトを取得できます。
他のカルチャ情報を取得
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        ' 他のカルチャ情報 (英語 - 米国)
        Dim culture As New CultureInfo("en-US")

        ' カルチャ名
        Console.WriteLine("{0}, {1}", culture.Name, culture.NativeName)

        ' カルチャ情報
        Console.WriteLine(culture.Calendar)
        Console.WriteLine(culture.DateTimeFormat.LongDatePattern)
        Console.WriteLine(culture.DateTimeFormat.LongTimePattern)
        Console.WriteLine(culture.DateTimeFormat.ShortDatePattern)
        Console.WriteLine(culture.DateTimeFormat.ShortTimePattern)
        Console.WriteLine(culture.TextInfo.ANSICodePage)
        Console.WriteLine(culture.NumberFormat.CurrencySymbol)

    End Sub

End Module
出力結果
en-US, English (United States)
System.Globalization.GregorianCalendar
dddd, MMMM dd, yyyy
h:mm:ss tt
M/d/yyyy
h:mm tt
1252
$
Press any key to continue

 この例では特定のカルチャの情報を取得していますが、使用可能なすべてのカルチャ情報を取得するにはCultureInfo.GetCultures()メソッドを使用します。 この例では出力される文字の関係上、Debugに出力しています。 また、実行結果はCultureTypes.AllCulturesではなくCultureTypes.SpecificCulturesを指定して出力したものです。
すべてのカルチャ情報を取得
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        Dim cultures() As CultureInfo = CultureInfo.GetCultures(CultureTypes.AllCultures)
        Dim culture As CultureInfo

        Debug.WriteLine("Cultures: " + cultures.Length.ToString())

        For Each culture In cultures

            Debug.WriteLine(culture.Name + ", " + culture.NativeName)

        Next

    End Sub

End Module
実行結果
実行結果

3.カルチャに対応した書式


 様々な国・地域のカルチャ情報を取得できたら、後はそれを活用するだけです。 次の例は、Decimal型の値を通貨として文字列化し、その際の書式をカルチャに依存した書式にするようにしているものです。
カルチャに対応した書式 (通貨)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        Dim currency As Decimal = 123456.78D

        ' 現在のカルチャ(日本)
        OutputCurrency(currency, CultureInfo.CurrentCulture)

        ' アメリカ
        OutputCurrency(currency, New CultureInfo("en-US"))

        ' カナダ
        OutputCurrency(currency, New CultureInfo("en-CA"))

        ' イギリス
        OutputCurrency(currency, New CultureInfo("en-GB"))

        ' フランス
        OutputCurrency(currency, New CultureInfo("fr-FR"))

        ' ドイツ
        OutputCurrency(currency, New CultureInfo("de-DE"))

        ' イタリア
        OutputCurrency(currency, New CultureInfo("it-IT"))

        ' ロシア
        OutputCurrency(currency, New CultureInfo("ru-RU"))

    End Sub

    Sub OutputCurrency(ByVal currency As Decimal, ByVal culture As CultureInfo)

        Debug.WriteLine(culture.DisplayName + ": " + currency.ToString("C", culture.NumberFormat))

    End Sub

End Module
実行結果
実行結果

 この例ではG8各国のカルチャ情報から通貨形式の書式を取得しています。 ToString()メソッドには、カルチャ情報に依存した形式で出力するバージョンがあります。 今回はそれを用いています。 CultureInfo.NumberFormatプロパティはそのカルチャの数値に関する書式を記述したクラスです。 これをToString()メソッドに渡すことによってその書式に従って数値が文字列化されます。 ちなみに、一つ目の引数である"C"は、その数値を通貨として出力するように指定する数値書式指定文字列というものです。 この実行結果を見てわかるとおり、単なるDecimal型の数値を渡したにも関わらず、通貨文字がその国で用いられているものになったり、通貨文字が先頭に来るか末尾に来るかが変わっています。 また、日本円では通常小数点以下(銭に相当する部分)は扱わないので、この書式では小数点以下が四捨五入され出力されていません。
 さらに、数値に関しても各国で表記方法が異なるので、その場合についても見てみることにします。
カルチャに対応した書式 (数値)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        Dim intValue As Integer = 12345678
        Dim realValue As Double = 12345.678

        ' 現在のカルチャ(日本)
        OutputValue(intValue, realValue, CultureInfo.CurrentCulture)

        ' アメリカ
        OutputValue(intValue, realValue, New CultureInfo("en-US"))

        ' カナダ
        OutputValue(intValue, realValue, New CultureInfo("en-CA"))

        ' イギリス
        OutputValue(intValue, realValue, New CultureInfo("en-GB"))

        ' フランス
        OutputValue(intValue, realValue, New CultureInfo("fr-FR"))

        ' ドイツ
        OutputValue(intValue, realValue, New CultureInfo("de-DE"))

        ' イタリア
        OutputValue(intValue, realValue, New CultureInfo("it-IT"))

        ' ロシア
        OutputValue(intValue, realValue, New CultureInfo("ru-RU"))

    End Sub

    Sub OutputValue(ByVal i As Integer, ByVal r As Double, ByVal culture As CultureInfo)

        Debug.WriteLine(culture.DisplayName + ": " + _
         i.ToString("D", culture.NumberFormat) + _
         "  " + r.ToString("E", culture.NumberFormat) + _
         "  " + r.ToString("F", culture.NumberFormat) + _
         "  " + r.ToString("G", culture.NumberFormat) + _
         "  " + r.ToString("N", culture.NumberFormat))

    End Sub

End Module
実行結果
実行結果 (見やすさのために多少体裁を整えてあります)

 まず始めに、数値書式指定文字列について説明すると、「D」は十進、「E」は指数形式、「F」は固定小数点、「G」は一般表記、「N」は数値として書式指定するものです。 この実行結果のように、小数点の表記について「 . (ピリオド)」を用いたり「 , (カンマ)」と国によって小数点の表記が異なります。 さらに、三桁毎の区切り文字も、使用したり使用しなかったりする場合があります。
 通貨や数値だけでなく、日付・時刻の表記もローカライズすることができます。
カルチャに対応した書式 (日付・時刻)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        Dim dtmNow As DateTime = DateTime.Now

        ' 現在のカルチャ(日本)
        OutputDateTime(dtmNow, CultureInfo.CurrentCulture)

        ' アメリカ
        OutputDateTime(dtmNow, New CultureInfo("en-US"))

        ' カナダ
        OutputDateTime(dtmNow, New CultureInfo("en-CA"))

        ' イギリス
        OutputDateTime(dtmNow, New CultureInfo("en-GB"))

        ' フランス
        OutputDateTime(dtmNow, New CultureInfo("fr-FR"))

        ' ドイツ
        OutputDateTime(dtmNow, New CultureInfo("de-DE"))

        ' イタリア
        OutputDateTime(dtmNow, New CultureInfo("it-IT"))

        ' ロシア
        OutputDateTime(dtmNow, New CultureInfo("ru-RU"))

        ' 韓国
        OutputDateTime(dtmNow, New CultureInfo("ko-KR"))

        ' 中国
        OutputDateTime(dtmNow, New CultureInfo("zh-CN"))

    End Sub

    Sub OutputDateTime(ByVal dtm As DateTime, ByVal culture As CultureInfo)

        Debug.WriteLine(culture.DisplayName + ": " + dtm.ToString(culture.DateTimeFormat))

    End Sub

End Module
実行結果
実行結果 (見やすさのために多少体裁を整えてあります)

4.カルチャに対応した月・曜日


 書式や通貨単位だけがカルチャに依存するものではありません。 暦もカルチャに依存します。 日本がそのよい例です。 日本では基本的にはグレゴリオ歴を採用していますが、同時に和暦というものも存在し、年号を用いて「平成〜年」とか「昭和〜年」という表記をします。
 さらに、曜日の呼び方や月の名前などもカルチャによって異なります。 次の例ではカルチャ情報から日時に関する情報を取得し、そこから様々なカルチャ依存の情報を取り出しています。
日時に関する情報
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        ' カルチャのインスタンスを生成
        Dim culture As New CultureInfo("ja-JP")

        ' カルチャから日時のフォーマット情報を取得
        Dim format As DateTimeFormatInfo = culture.DateTimeFormat

        ' 現在のカレンダ
        Debug.WriteLine(format.Calendar.ToString())

        ' 曜日名を列挙
        EnumerateDayNames(format)

        ' 月の名前を列挙
        EnumerateMonthNames(format)

        ' 時代(年号)を列挙
        EnumerateEras(format)



        ' 暦を和暦に設定
        format.Calendar = New JapaneseCalendar()

        ' 現在のカレンダ
        Debug.WriteLine(format.Calendar.ToString())

        ' 時代(年号)を列挙
        EnumerateEras(format)

    End Sub

    ' 曜日を表す文字列を列挙するメソッド
    Sub EnumerateDayNames(ByVal format As DateTimeFormatInfo)

        Dim dayName As String

        For Each dayName In format.DayNames

            Debug.Write(dayName + ", ")

        Next

        Debug.WriteLine("")

    End Sub

    ' 月を表す文字列を列挙するメソッド
    Sub EnumerateMonthNames(ByVal format As DateTimeFormatInfo)

        Dim monthName As String

        For Each monthName In format.MonthNames

            Debug.Write(monthName + ", ")

        Next

        Debug.WriteLine("")

    End Sub

    ' 時代(年号)を列挙するメソッド
    Sub EnumerateEras(ByVal format As DateTimeFormatInfo)

        Dim era As Integer

        For Each era In format.Calendar.Eras

            Debug.WriteLine(era.ToString() + " : " + format.GetEraName(era))

        Next

    End Sub

End Module
実行結果
実行結果

 この例では曜日名、月名、年号などを取得しています。 またDateTimeFormatInfoクラスのCalendarプロパティに和暦を表すJapanezeCalendarクラスのインスタンスを代入してその違いを見ています。 実行結果から分かるとおり、初期状態ではGregorianCalendarが代入されています。 グレゴリオ暦で年号を列挙した場合は「西暦」だけですが、和暦の場合は「平成」、「昭和」などが出力されます。 Calendarクラスについてはこの次以降で説明します。 
 これ以外の情報として、曜日名と月名を取得していますが、月名は十三項目存在しているかのような出力になっています。 これは、閏月などで第十三月が存在する暦を考慮したものと考えられます。 次のコードでは、各カルチャでは曜日・月の名前をどのように記述するかを調べることができます。
各カルチャでの曜日・月の名前を取得
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        ' カルチャのインスタンスを生成
        Dim cultures() As CultureInfo = { _
         New CultureInfo("ja-JP"), _
         New CultureInfo("en-US"), _
         New CultureInfo("de-DE"), _
         New CultureInfo("fr-FR"), _
         New CultureInfo("ru-RU"), _
         New CultureInfo("ko-KR"), _
         New CultureInfo("zh-CN")}

        Dim culture As CultureInfo

        Dim format As DateTimeFormatInfo

        For Each culture In cultures

            ' カルチャから日時のフォーマット情報を取得
            format = culture.DateTimeFormat

            Debug.WriteLine(culture.ToString())

            ' 曜日名を列挙
            EnumerateDayNames(format)

            ' 月の名前を列挙
            EnumerateMonthNames(format)

        Next

    End Sub

    ' 曜日を表す文字列を列挙するメソッド
    Sub EnumerateDayNames(ByVal format As DateTimeFormatInfo)

        ' 省略

    End Sub

    ' 月を表す文字列を列挙するメソッド
    Sub EnumerateMonthNames(ByVal format As DateTimeFormatInfo)

        ' 省略

    End Sub

End Module
実行結果
実行結果

5.カルチャに対応した暦(こよみ)


 先程述べたように、曜日・月の名前のほかにも暦も当然カルチャに依存します。 世界にはグレゴリオ暦ではなくヘブライ暦や回教暦を採用している国・地域もあります。 そのために必ずしも「2003/01/01」が西暦(グレゴリオ暦)を表すとは限らなくなります。 そこで使用されるのがCalendarクラスです。 このクラス自体は抽象クラスですが、この派生クラスである GregorianCalendar や HebrewCalendar では一年の日数、一月の日数などの暦に関する処理が実装されています。
 次のコードは和暦で表した日付をシステムが現在使用している暦(このコードを実行した環境ではグレゴリオ暦)になおして表示するコードです。 システムの暦を調べるには、「コントロール パネル」の「地域と言語のオプション」にある「地域オプション」タブで「カスタマイズ」をクリックし、表示されるダイアログの日付タブで調べることができます。 また、ここを変更することで異なる実行結果を得ることもできます。
暦の確認・変更
暦の確認・変更

和暦からシステムの暦(グレゴリオ暦)への日付の変換
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        Dim japanese As New JapaneseCalendar()

        Dim gregorian As New GregorianCalendar()

        Dim gregDateTime As DateTime

        ' 和暦の平成15年2月7日をグレゴリオ暦(西暦)に直す
        gregDateTime = japanese.ToDateTime(15, 2, 7, 0, 0, 0, 0, japanese.CurrentEra)

        Console.WriteLine(gregDateTime)

        ' 西暦でその年を取得する
        Console.WriteLine("Gregorian: {0}", gregorian.GetYear(gregDateTime))

        ' 和暦でその年を取得する
        Console.WriteLine("Japanese: {0}", japanese.GetYear(gregDateTime))

    End Sub

End Module
出力結果
2003/02/07 0:00:00
Gregorian: 2003
Japanese: 15
Press any key to continue

 実行した環境では西暦を使用していたので、ToDateTime()メソッドの戻り値は西暦になります。 次のコードは、システムの暦での2001年1月1日から、各暦での一年後および十年後の日付をシステムの暦で取得するものです。
各暦での一年・十年の加算
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
Imports System
Imports System.Globalization

Module Globalization

    Sub Main()

        ' システムの暦での2001年1月1日
        Dim dtm As DateTime = New DateTime(2001, 1, 1)

        ' 和暦
        Dim japanese As New JapaneseCalendar()

        ' ヘブライ暦
        Dim hebrew As New HebrewCalendar()

        ' 回教暦
        Dim hijri As New HijriCalendar()

        ' 西暦
        Dim gregorian As New GregorianCalendar()


        ' システムの暦を表示
        Console.WriteLine("Using {0}", CultureInfo.CurrentCulture.Calendar)

        ' 日付を表示
        Console.WriteLine(dtm)

        ' 和暦での一年後、十年後
        Console.WriteLine("Japanese:  {0}, {1}", japanese.AddYears(dtm, 1), japanese.AddYears(dtm, 10))

        ' ヘブライ暦での一年後、十年後
        Console.WriteLine("Hebrew:    {0}, {1}", hebrew.AddYears(dtm, 1), hebrew.AddYears(dtm, 10))

        ' 回教暦での一年後、十年後
        Console.WriteLine("Hijri:     {0}, {1}", hijri.AddYears(dtm, 1), hijri.AddYears(dtm, 10))

        ' 西暦での一年後、十年後
        Console.WriteLine("Gregorian: {0}, {1}", gregorian.AddYears(dtm, 1), gregorian.AddYears(dtm, 10))

    End Sub

End Module
出力結果
Using System.Globalization.GregorianCalendar
2001/01/01 0:00:00
Japanese:  2002/01/01 0:00:00, 2011/01/01 0:00:00
Hebrew:    2001/12/21 0:00:00, 2010/12/13 0:00:00
Hijri:     2001/12/21 0:00:00, 2010/09/14 0:00:00
Gregorian: 2002/01/01 0:00:00, 2011/01/01 0:00:00
Press any key to continue

 このように、ヘブライ暦や回教暦では一年の日数・一月の日数などがグレゴリオ暦とは異なるので、グレゴリオ暦での一年後・十年後とは異なった値になります。 また、和暦については年号だけが異なり、基本的にはグレゴリオ暦と同じなので日付のずれはありません。 この例ではAddYears()メソッドを使用しましたが、場合によっては月数や日数を加減算することもできます。

6.現在のスレッドのカルチャを変更する


 今まではシステムのカルチャ設定に基づいたコーディングをしてきましたが、場合によっては現在のカルチャを変更したくなる場合があります。 そのような場合に対応するため、現在のスレッドのカルチャを変えることが可能になっています。 次のコードではカルチャを途中で「en-US (英語・米国)」に変えています。
現在のスレッドのカルチャを変更する
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
Option Strict On

Imports System
Imports System.Globalization
Imports System.Threading

Module Globalization

    Sub Main()

        Console.WriteLine("現在のカルチャ: {0}", CultureInfo.CurrentCulture.NativeName)

        ' 通貨の表示
        Console.WriteLine("{0:C}", 123456.789)

        ' 日時の表示
        Console.WriteLine("{0}", DateTime.Now)


        ' 現在のスレッドのカルチャを変更
        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")


        Console.WriteLine("現在のカルチャ: {0}", CultureInfo.CurrentCulture.NativeName)

        ' 通貨の表示
        Console.WriteLine("{0:C}", 123456.789)

        ' 日時の表示
        Console.WriteLine("{0}", DateTime.Now)

    End Sub

End Module
出力結果
現在のカルチャ: 日本語 (日本)
\123,457
2003/12/14 2:40:53
現在のカルチャ: English (United States)
$123,456.79
12/14/2003 2:40:53 AM
Press any key to continue

 実際にカルチャを変更しているコードが21行目です。 CurrentThread.CurrentCultureプロパティに新しいCultureInfoクラスのインスタンスを指定することでカルチャを変更しています。 さらに、これを利用して暦を変えることも可能です。
現在のスレッドのカルチャを変更する (暦を変更)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
Imports System
Imports System.Globalization
Imports System.Threading

Module Globalization

    Sub Main()

        ' 日時の表示
        Console.WriteLine("{0}", DateTime.Now)

        ' 新しいカルチャのインスタンスを生成
        Dim culture As New CultureInfo("ja-JP")

        ' カルチャに新しく和暦を指定 
        culture.DateTimeFormat.Calendar = New JapaneseCalendar()

        ' 現在のスレッドのカルチャを変更
        Thread.CurrentThread.CurrentCulture = culture

        ' 日時の表示
        Console.WriteLine("{0}", DateTime.Now)

    End Sub

End Module
出力結果
2003/12/14 2:41:23
平成 15/12/14 2:41:23
Press any key to continue