ククログ

株式会社クリアコード > ククログ > PowerShellで現在のセッションのカルチャを一時的に変更する方法

PowerShellで現在のセッションのカルチャを一時的に変更する方法

主にWindows環境での開発を行っている橋田です。

今回、PowerShellで現在のセッションのカルチャを変更する方法を調査しました。 これは、PowerShellのスクリプトがシステムのカルチャの影響を受けないようにしたい場合などに便利です。

バージョンによって対応方法が異なることや、その背景など、調査をして初めて知ったこともあったため、その調査結果を紹介します。

デフォルトのコマンドレット

PowerShellには、以下のようなカルチャを変更するコマンドレットが存在します。

しかしこれらのコマンドレットは、システム全体やユーザー単位の設定を書き換えるため、現在のセッションのカルチャのみを一時的に変更したいという目的では使用できません。

セッションのカルチャの動作

セッションのカルチャの動作については、PowerShell 6で仕様変更が入っています。

https://learn.microsoft.com/ja-jp/powershell/scripting/whats-new/differences-from-windows-powershell?view=powershell-7.5#make-psculture-consistently-reflect-in-session-culture-changes

$PSCulture がセッション内カルチャの変更を一貫して反映

Windows PowerShell では、現在のカルチャの値がキャッシュされるため、セッションの開始後にカルチャが変更されると値が同期しなくなる場合があります。 このキャッシュ動作が、PowerShell Core で修正されています。

PowerShell 6以降では[System.Threading.Thread]::CurrentThread.CurrentCultureを変更すると現在のセッションに反映されます。

一方、PowerShell 5.1以下では[System.Threading.Thread]::CurrentThread.CurrentCultureを変更しても、キャッシュされた値が使われるため現在のセッションには反映されません。 キャッシュされているカルチャの値を変更する必要があります。

$culture = [System.Globalization.CultureInfo]::CreateSpecificCulture("en-US")
$assembly = [System.Reflection.Assembly]::Load("System.Management.Automation")
$type = $assembly.GetType("Microsoft.PowerShell.NativeCultureResolver")
$type.GetField('m_Culture', 'NonPublic, Static').SetValue($null, $culture)
$type.GetField('m_uiCulture', 'NonPublic, Static').SetValue($null, $culture)

参考: https://github.com/PowerShell/PowerShell/issues/19860

結論

以上の調査結果をまとめると、以下のような関数を作成することでPowerShell 5.1以下とPowerShell 6以降のどちらでも現在のセッションのカルチャを変更することができます。

function Set-CurrentCultures {
    param([Parameter(Mandatory=$true)]
          [string]$name)

    $culture = [System.Globalization.CultureInfo]::CreateSpecificCulture($name)
    $psVer = $PSVersionTable.PSVersion.Major

    if ($psVer -ge 6) {
        [System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture
        [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
    }
    else {
        $assembly = [System.Reflection.Assembly]::Load("System.Management.Automation")
        $type = $assembly.GetType("Microsoft.PowerShell.NativeCultureResolver")
        $type.GetField('m_Culture', 'NonPublic, Static').SetValue($null, $culture)
        $type.GetField('m_uiCulture', 'NonPublic, Static').SetValue($null, $culture)
    }
}

使用方法は以下の通りです。

> [System.Threading.Thread]::CurrentThread.CurrentUICulture

LCID             Name             DisplayName
----             ----             -----------
1041             ja-JP            日本語 (日本)

> [System.Threading.Thread]::CurrentThread.CurrentCulture

LCID             Name             DisplayName
----             ----             -----------
1041             ja-JP            日本語 (日本)

> Set-CurrentCultures "en-US"
> [System.Threading.Thread]::CurrentThread.CurrentUICulture

LCID             Name             DisplayName
----             ----             -----------
1033             en-US            English (United States)

>  [System.Threading.Thread]::CurrentThread.CurrentCulture

LCID             Name             DisplayName
----             ----             -----------
1033             en-US            English (United States)

なお、このカルチャの変更は、現在のPowerShellのセッションにのみ影響し、セッションから呼び出した外部プログラム(exeなどの新規プロセス)には影響しません。

まとめ

今回はPowerShellで現在のセッションのカルチャを変更する方法を紹介しました。

本記事が、同様の問題で悩まれている方に少しでも役に立てば幸いです。