Windows10 MUI as SelfService-Application in ConfigMgr

Windows10 Language-Packs (MUI's) are helpful in multi-lingual environments as you can have a single base OS Image with additional Languages on top.

But whatever Language you enforce, you will always have Users who prefer another Language... so why not let the User choose the Language by him self ?!

Let's create Applications for the MUI's in Configuration Manager and make them available in Software Center so a User can Pick his favourite Language:

Note: You can assign these Applications also from a Task-Sequence during OSD.

required Files:

We need the Language-Pack itself (.CAB File), some configuration-Files for Keyboard and other Locale-Settings and an installation script (Install.ps1):

Note: The Language-Pack does depend on the Windows10 Build, if you are using Win10 1607, you also need the Language-Pack for build 1607.. To verify if you have the right File, start lpksetup.exe and select your .CAB File. You will get an Error if the CAB does not match with your OS..

The .XML Files are used to configure the Default-Profile and the Welcome-Screen. You can find an example on my Blog: https://rzander.azurewebsites.net/how-to-change-the-welcome-screen-language-in-win10/
In the current example, I've created a Folder with the German MUI (.CAB) and an XML File for every Locale that depends on the German MUI.

The Installation-Script (Install.ps1) does look like:

param(  
    [string]$Lang = 'de-CH', #Default Language if no parameter is specified
    [string]$UILang = 'de-DE' #Default UILanguage if no parameter is specified
)

cd $PSScriptRoot  
$CoreLang = $lang.Split('-')[0]
$reboot = $false #no reboot per default 

#Check if running as SYSTEM
if([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.value -eq "S-1-5-18"))  
    {
        #Check if MUI is already installed
        if(-NOT (Test-Path "HKLM:\SYSTEM\CurrentControlSet\Control\MUI\UILanguages\$($UILang)"))
        {
            try
            {
                #Install MUI Components
                $proc = (start-process "Dism" -ArgumentList "/online /Add-Package /PackagePath:.\LP_$($CoreLang).cab" -Wait -PassThru);$proc.WaitForExit()
                $proc = (start-process "Dism" -ArgumentList "/online /Add-Package /PackagePath:.\Handwriting-$($UILang)-Package.cab" -Wait -PassThru);$proc.WaitForExit()
                $proc = (start-process "Dism" -ArgumentList "/online /Add-Package /PackagePath:.\OCR-$($UILang)-Package.cab" -Wait -PassThru);$proc.WaitForExit()
                $proc = (start-process "Dism" -ArgumentList "/online /Add-Package /PackagePath:.\Speech-$($UILang)-Package.cab" -Wait -PassThru);$proc.WaitForExit()
                $proc = (start-process "Dism" -ArgumentList "/online /Add-Package /PackagePath:.\TextToSpeech-$($UILang)-Package.cab" -Wait -PassThru);$proc.WaitForExit()
                $proc = (start-process "Dism" -ArgumentList "/online /Add-Package /PackagePath:.\Basic-$($UILang)-Package.cab" -Wait -PassThru);$proc.WaitForExit()
            } catch {}
            $reboot = $true
        }
    }

#Set Default Language
try  
{
    $proc = (start-process -FilePath "control.exe" -ArgumentList "intl.cpl,, /f:`"$($Lang).xml`"" -Wait -PassThru);$proc.WaitForExit()
} catch{}


#Just in case if there is no XML-File for the specified Language...
try {  
    set-culture $Lang  
    Set-WinUserLanguageList (New-WinUserLanguageList $Lang) -Force
    Set-WinUILanguageOverride -Language $UILang
} catch {}

#Check if running from a Task-Sequence
if([bool] (get-process tsmanager -ea 0))  
{
    if($reboot)
        { Exit 3010 }
    else
        { Exit 0 }
}

The Script requires the Locale-Name (e.g. "de-CH" for "German - Switzerland") as Input Parameter so we do not have to change that script for every Language.
The .CAB File-name must start with "LP_" followed by the core Language-Code; in our example it's: "lp_de.cab"

The script does only install the .CAB if running as SYSTEM. The reason is that we want to use the same script to configure the Settings for the current logged on user.
If we want to assign the Application during OSD, the Application returns a Soft-Reboot exit code only when running outside of a Task-Sequence. This prevents an automatic reboot during the TS (you can still manually add a reboot action in the TS).

The Application(s):

As we want to configure also the Locale-Settings during the LP installation, an Application per Language and Locale is required.
In my example, a have a different Application for "German-Germany" and another one for "German-Switzerland". Both are using the same source-directory and .CAB File but the Locale-Settings are different..

Deployment Type "USER":

The first deployment Type we create is called "USER" as it's running under the Users context. We just have to call the Install.PS1 with the Language as Parameter:

powershell.exe -ExecutionPolicy Bypass -File "Install.ps1" de-CH

As Detection Rule, we check if the UI Language is set correctly:

Note: This does just check if the UI Language is set but not if the Locale-Settings are correct.

In the User Experience settings, we have to specify to run the DT as User ("Only when no user is logged on") as this DT will set the settings for the current User

Deployment Type "SYSTEM":

The second deployment Type is called "SYSTEM" as it has to run with Admin rights to install the MUI. As program, we use the same command as on the USER DT:

powershell.exe -ExecutionPolicy Bypass -File "Install.ps1" de-CH

As Detection Rule, we check if the UI Language is set correctly on HKEYLOCALMACHINE:

Note: even if we want to deploy "de-CH" , the UI Language is always "de-DE". Only the Locale-Settings will be configured with "de-CH"

This DT will install as System whether or not a user is logged on...

Make sure that the Deployment-Type "User" has priority 1 and the DT "System" is set to priority 2. This will trigger the DT "User" is a User is logged on...

Dependencies

If a User triggers the Installation, we want to install the MUI (as SYSTEM) before the User Settings are applied. For this case, we have to create a dependency on the USER Deployment-Type and link the SYSTEM Deployment-Type (from the same Application):

Deployment

You should always deploy this Application as "Available" and not "Required"... just to prevent an installation loop if multiple MUI's are assigned.

Important: After the Installation, the computer has to reboot and it can take a while until the Application Evaluation cycle detects the App as installed. Do not switch between different MUI's until the App is recognized as installed, otherwise you will end in a loop where different MUI's are trying to install...