Public/system/Set-DisplayLanguage.ps1
|
#Requires -Version 5.1 function Set-DisplayLanguage { <# .SYNOPSIS Sets the Windows UI display language on local or remote computers .DESCRIPTION Changes the Windows UI display language for the current user via Set-WinUILanguageOverride, installing the language pack automatically through the LanguagePackManagement module when it is not already present. Optionally propagates the settings to the system account (Welcome Screen) and/or new user profiles via Copy-UserInternationalSettingsToSystem. The LanguagePackManagement module ships built-in on Windows 11 22H2+ and Windows Server 2022+; on earlier OS versions (e.g. Windows Server 2019) the module is not available and the function fails with clear guidance instead of the underlying "Get-InstalledLanguage is not recognized" error. A session restart is required for changes to take effect. .PARAMETER Language The BCP-47 language tag to apply (e.g. 'en-US', 'fr-FR'). Must follow the format: two lowercase letters, a hyphen, two uppercase letters. .PARAMETER NoInstall Disables automatic installation of the language pack if it is not already present. By default, the function installs the pack automatically when missing. .PARAMETER ApplyToSystem Copies the language settings to the system account (Welcome Screen / login screen). Requires elevated privileges. .PARAMETER ApplyToNewUsers Copies the language settings to all new user profiles created after this change. Requires elevated privileges. .PARAMETER ComputerName One or more computer names to target. Defaults to the local computer. Accepts pipeline input by value and by property name. On a remote computer, the user-scoped override applies to the executing account's profile (the WinRM logon identity), not the interactive console user. .PARAMETER Credential Optional PSCredential for authenticating to remote computers. Not used for local operations. .EXAMPLE Set-DisplayLanguage -Language en-US Changes the display language to English (United States) for the current user on the local computer, installing the language pack automatically. .EXAMPLE Set-DisplayLanguage -Language fr-FR -ComputerName 'SRV01' -ApplyToSystem -ApplyToNewUsers Changes the display language to French on SRV01 for the current user, the system account, and all future new user profiles. Requires an elevated session. .EXAMPLE 'SRV01', 'SRV02' | Set-DisplayLanguage -Language en-US -NoInstall Changes the display language on both servers via pipeline, without installing the language pack if it is missing. .OUTPUTS PSWinOps.DisplayLanguageResult Returns one object per computer describing the pack action taken and whether the user/system/new-user settings were applied. .NOTES Author: Franck SALLET Version: 1.0.0 Last Modified: 2026-09-10 Requires: PowerShell 5.1+ / Windows only Requires: LanguagePackManagement module (built-in on Windows 11 22H2+ / Windows Server 2022+) Requires: Administrator privileges for -ApplyToSystem / -ApplyToNewUsers .LINK https://github.com/k9fr4n/PSWinOps .LINK https://learn.microsoft.com/en-us/powershell/module/languagepackmanagement/ #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType('PSWinOps.DisplayLanguageResult')] param( [Parameter(Mandatory = $true, HelpMessage = 'BCP-47 language tag to apply, e.g. en-US or fr-FR')] [ValidatePattern('^[a-z]{2}-[A-Z]{2}$')] [string]$Language, [Parameter(Mandatory = $false, HelpMessage = 'Skip automatic installation of a missing language pack')] [switch]$NoInstall, [Parameter(Mandatory = $false, HelpMessage = 'Copy settings to the system account (Welcome Screen)')] [switch]$ApplyToSystem, [Parameter(Mandatory = $false, HelpMessage = 'Copy settings to new user profiles')] [switch]$ApplyToNewUsers, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [Alias('CN', 'Name', 'DNSHostName')] [string[]]$ComputerName = @($env:COMPUTERNAME), [Parameter(Mandatory = $false)] [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential ) begin { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Starting with Language='$Language', NoInstall=$($NoInstall.IsPresent), ApplyToSystem=$($ApplyToSystem.IsPresent), ApplyToNewUsers=$($ApplyToNewUsers.IsPresent)" if ($ApplyToSystem -or $ApplyToNewUsers) { if (-not (Test-IsAdministrator)) { $adminException = [System.Security.SecurityException]::new( 'Applying settings to the system account or new user profiles requires administrator privileges.' ) $adminRecord = [System.Management.Automation.ErrorRecord]::new( $adminException, 'InsufficientPrivilege', [System.Management.Automation.ErrorCategory]::PermissionDenied, $null ) $PSCmdlet.ThrowTerminatingError($adminRecord) } } $scriptBlock = { param( [string]$TargetLanguage, [bool]$SkipInstall, [bool]$ToSystem, [bool]$ToNewUsers ) $languagePackModule = Get-Module -ListAvailable -Name 'LanguagePackManagement' if (-not $languagePackModule) { throw "The 'LanguagePackManagement' module is not available on this computer. It ships built-in on Windows 11 22H2+ and Windows Server 2022+; on earlier OS versions (e.g. Windows Server 2019) install the language pack via 'lpksetup.exe' or Server Manager and use 'Set-WinUILanguageOverride' instead." } $installed = Get-InstalledLanguage -ErrorAction Stop | Where-Object -FilterScript { $_.LanguageId -eq $TargetLanguage } if (-not $installed) { if ($SkipInstall) { return [PSCustomObject]@{ PackAction = 'Skipped' UserLanguageSet = $false AppliedToSystem = $false AppliedToNewUsers = $false Status = 'Skipped' } } Install-Language -Language $TargetLanguage -ErrorAction Stop $packAction = 'Installed' } else { $packAction = 'AlreadyPresent' } Set-WinUILanguageOverride -Language $TargetLanguage -ErrorAction Stop $appliedToSystem = $false $appliedToNewUsers = $false if ($ToSystem -or $ToNewUsers) { Copy-UserInternationalSettingsToSystem -WelcomeScreen:$ToSystem -NewUser:$ToNewUsers -ErrorAction Stop $appliedToSystem = $ToSystem $appliedToNewUsers = $ToNewUsers } [PSCustomObject]@{ PackAction = $packAction UserLanguageSet = $true AppliedToSystem = $appliedToSystem AppliedToNewUsers = $appliedToNewUsers Status = 'Success' } } } process { foreach ($targetComputer in $ComputerName) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Processing '$targetComputer'" $actionDescription = if ($ApplyToSystem -or $ApplyToNewUsers) { "Set display language to '$Language' and propagate to System/NewUsers" } else { "Set display language to '$Language'" } try { if ($PSCmdlet.ShouldProcess($targetComputer, $actionDescription)) { $innerResult = Invoke-RemoteOrLocal -ComputerName $targetComputer -ScriptBlock $scriptBlock -ArgumentList @($Language, $NoInstall.IsPresent, $ApplyToSystem.IsPresent, $ApplyToNewUsers.IsPresent) -Credential $Credential [PSCustomObject]@{ PSTypeName = 'PSWinOps.DisplayLanguageResult' ComputerName = $targetComputer Language = $Language PackAction = $innerResult.PackAction UserLanguageSet = $innerResult.UserLanguageSet AppliedToSystem = $innerResult.AppliedToSystem AppliedToNewUsers = $innerResult.AppliedToNewUsers RestartRequired = ($innerResult.Status -eq 'Success') Status = $innerResult.Status ErrorMessage = $null Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' } if ($innerResult.Status -eq 'Success') { Write-Warning -Message "[$($MyInvocation.MyCommand)] A session restart on '$targetComputer' is required for changes to take effect." } } } catch { [PSCustomObject]@{ PSTypeName = 'PSWinOps.DisplayLanguageResult' ComputerName = $targetComputer Language = $Language PackAction = 'None' UserLanguageSet = $false AppliedToSystem = $false AppliedToNewUsers = $false RestartRequired = $false Status = 'Failed' ErrorMessage = $_.Exception.Message Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' } Write-Error -Message "[$($MyInvocation.MyCommand)] Failed to set display language on '${targetComputer}': $_" continue } } } end { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Completed" } } |