Introduction

08 Version 1.2 Shows language and translation serviceI made a lot of PowerShell scripts that are multi-lingual. Based on the current Windows language, the GUI is shown in that Windows language. The entries are all stored in a JSON file. The GUI is shown in English if the current Windows language is not in the JSON file.

I made that JSON file with all the translations manually with Google Translate. That was very time intensive. So, time to automate that process.

Background information

See this JSON content:

{
 "en" :
   {
    "Text 1"     : "one",
    "Text 2"     : "You can change this to whatever you like.",
    "Example 3"  : "three",
    "Example 4"  : "The part is for test.",
    "something 5": "This is the last line to be translated.",
    "extra"      : "An extra line."
   }
}

If you add the German language, you will get:

{
 "en" :
   {
    "Text 1"     : "one",
    "Text 2"     : "You can change this to whatever you like.",
    "Example 3"  : "three",
    "Example 4"  : "The part is for test.",
    "something 5": "This is the last line to be translated.",
    "extra"      : "An extra line."
   },
 "de": 
   {
    "Text 1"     : "eines",
    "Text 2"     : "Sie können dies beliebig ändern.",
    "Example 3"  : "drei",
    "Example 4"  : "Das Teil ist zum testen.",
    "something 5": "Dies ist die letzte zu übersetzende Zeile.",
    "extra"      : "Eine zusätzliche Zeile."
   }
}

If you read the JSON file and store it in the object $JSONObject, you can use $JSONObject.de."Example 4" to show the correct sentence in German.

Challenges

Headers

I do not know if it is the correct name, but I call "Text 1", "Text 2, "Example 3", "Example 4", "Something 5" and "extra" headers. But these headers differ per JSON file, so I must read them dynamically. 

The only way to extract the headers was to convert the JSON object to a CSV object and read the column headers:

$CSVObject                 = @($JSONObject.psobject.Properties.Value | ConvertTo-CSV -NoTypeInformation)
$Headers                   = $CSVObject[0].Split(",").Replace("""","")

In version v11 this has been changed to:

$Headers                   = ($JSONObject.en | Get-Member -type NoteProperty).Name

Then it was very easy to read all the English values that belong to a header:

$NewJSONObject    = @{}  
$Array            = [Ordered]@{}
ForEach ($Header in $Headers)
 {
  $Array += @{$Header=$JSONObject.en.$Header}
 }
  
$NewJSONObject.Add("en",$Array)

The whole code:

$JSONObject = @'
{
 "en" :
   {
    "Text 1"     : "one",
    "Text 2"     : "You can change this to whatever you like.",
    "Example 3"  : "three",
    "Example 4"  : "The part is for test.",
    "something 5": "This is the last line to be translated.",
    "extra"      : "An extra line."
   }
}
'@ | ConvertFrom-Json

$Headers             = ($JSONObject.en | Get-Member -type NoteProperty).Name
$NewJSONObject       = @{}  
$Array               = [Ordered]@{}
ForEach ($Header in $Headers)
 {
  $Array += @{$Header=$JSONObject.en.$Header}
 }
  
$NewJSONObject.Add("en",$Array)
$NewJSONObject.en."Example 4"

Translation services

The default translation service is the Google Free API. You can use that API free of charge. Unfortunately, the translations are not always 100% accurate. So there are four translation services included in this script:

  • Google Free API
  • Google Cloud API
  • DeepL
  • Microsoft Translator Service

You have to sign up for the last three. Google Cloud needs to be configured via Google. DeepL can be used free of charge with a maximum of 500.000 characters. The Microsoft Translator service needs to be configured in Azure. 

Code to test Google Free translation API:

Clear-Host
Add-Type -AssemblyName System.Web
$Text                = "This is not a complicated sentence, but good enough to test the translation API."
$TargetLanguage      = "de"
$Uri                 = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=$($TargetLanguage)&dt=t&q=$Text"
$Response            = Invoke-RestMethod -Uri $Uri -Method Get

$Translation         = $Response[0].SyncRoot | foreach { $_[0] }
$Translation         = [System.Web.HttpUtility]::HtmlDecode($Translation)
$Translation

Code to test Google Cloud API:

Clear-Host
Add-Type -AssemblyName System.Web
$key                 = "Your API Key"
$Text                = "This is not a complicated sentence, but good enough to test the translation API."
$SourceLanguage      = "en"
$TargetLanguage      = "de"
$url                 = "https://www.googleapis.com/language/translate/v2?key=$($Key)&target=$($TargetLanguage)&source=$($SourceLanguage)&q=$($Text)"
$result              = Invoke-RestMethod $url
$Translation         = [System.Web.HttpUtility]::HtmlDecode($result.data.translations.translatedText)
$Translation

Code to test DeepL:

Clear-Host
Add-Type -AssemblyName System.Web
$APIKey              =" Your API Key"
$textToConvert       = "This item is used by other items, therefore not removed."
$translateBaseURI    = "https://api-free.deepl.com/v2/translate"
$TargetLang          = "fr"


$headers             = @{Authorization = "DeepL-Auth-Key $APIKey"}
$Body                = @{target_lang = $TargetLang}
$Body               += @{source_lang = "EN"}
$Body               += @{split_sentences = 0}
$Body               += @{text = $textToConvert}

$Response            = Invoke-RestMethod -Uri "https://api-free.deepl.com/v2/translate" -Method Post -Body $Body -Headers $headers

$Translation         = [System.Web.HttpUtility]::HtmlDecode([Text.Encoding]::UTF8.GetString([Text.Encoding]::GetEncoding(28591).GetBytes($Response.translations.text)))
$Translation

Code to test Microsoft Translator Service:

Clear-Host
# API Key from https://azure.microsoft.com/en-us/services/cognitive-services/translator-text-api/
Add-Type -AssemblyName System.Web

$apiKey              = "Your API Key"
$location            = "Your location"
$fromLang            = "en"
$toLang              = "de"

$headers             = @{}
$headers.Add("Ocp-Apim-Subscription-Key",$apiKey)
$headers.Add("Ocp-Apim-Subscription-Region",$location)
$headers.Add("Content-Type","application/json")

$translateBaseURI    = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&from=$($fromLang)&to=$($toLang)"

$textToConvert       = "This is not a complicated sentence, but good enough to test the translation API."

$text                = @{'Text' = $($textToConvert)} | ConvertTo-Json

$conversionResult    = Invoke-RestMethod -Method POST -Uri $translateBaseURI -Headers $headers -Body "[$($text)]"
$Translation         = [System.Web.HttpUtility]::HtmlDecode($($conversionResult.translations[0].text))
$translation

02 CheckedListBoxI had one issue with DeepL: the English sentence 'This item is used by other items, therefore not removed.' was translated in French to 'Cet élément est utilisé par d'autres éléments, il n'est donc pas supprimé.' Some characters were not in the proper UTF8. This was finally solved by changing the line

[System.Web.HttpUtility]::HtmlDecode($Response.translations.text)

to

[System.Web.HttpUtility]::HtmlDecode([Text.Encoding]::UTF8.GetString([Text.Encoding]::GetEncoding(28591).GetBytes($Response.translations.text)))

Source: this article on StackOverflow

Translation languages

DeepL is the most restrictive if it comes to supported languages. So I grabbed all the supported languages from their website and put them in an array. then I used:

[CultureInfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) | Where {$_.Name -like "de*"} | Select-Object -Property Name,DisplayName

to find the real language name instead of the ISO code. All that information is stored in a hashtable. That hashtable is used to fill the CheckedListBox with the real language name.

Clear-Host

$Table = @()

$SupportedLanguages               = @("bg","cs","da","de","es","fr","id","blah","it","nl","sv","el","et","fi","hu","lt","pl","ro","sk","sl","uk","zz")
ForEach ($SupportedLanguage in $SupportedLanguages)
 {
  $Language = ([CultureInfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) | Where {$_.Name -like "$SupportedLanguage*"} | Select-Object -Property Name,DisplayName)
  if($Language)
   {
     $Record  = [ordered] @{"Code"            = "";
                            "Display Name"    = "";}

     $Record."Code"         = $SupportedLanguage
     $Record."Display Name" = ($Language[0].DisplayName).Split(" ")[0]
     $Table                += New-Object PSObject -Property $Record
   }
    else
   {
    Write-Host "Don't fool me. The ISO code '$SupportedLanguage' does not exists."
   }
 }

$Table = $Table | Sort-Object "Display Name"

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$formTesting   = New-Object 'System.Windows.Forms.Form'
$btnOk         = New-Object 'System.Windows.Forms.Button'
$chkListBox    = New-Object 'System.Windows.Forms.CheckedListBox'

#
# formTesting
#
$formTesting.Controls.Add($btnOk)
$formTesting.Controls.Add($chkListBox)
$formTesting.AutoScaleDimensions   = New-Object System.Drawing.SizeF(6, 13)
$formTesting.AutoScaleMode         = 'Font'
$formTesting.ClientSize            = New-Object System.Drawing.Size(284, 242)
$formTesting.Name                  = 'formTesting'
$formTesting.Text                  = 'Testing '
#
# btnOk
#
$btnOk.Location                    = New-Object System.Drawing.Point(197, 203)
$btnOk.Name                        = 'btnOk'
$btnOk.Size                        = New-Object System.Drawing.Size(75, 23)
$btnOk.TabIndex                    = 1
$btnOk.Text                        = '&Ok'
$btnOk.UseVisualStyleBackColor     = $True
#
# chkListBox
#
$chkListBox.FormattingEnabled      = $True
$chkListBox.Location               = New-Object System.Drawing.Point(12, 12)
$chkListBox.Name                   = 'chkListBox'
$chkListBox.Size                   = New-Object System.Drawing.Size(120, 214)
$chkListBox.TabIndex               = 0
$chkListBox.CheckOnClick           = $true

ForEach ($SupportedLanguage in $Table)
 {
  [void]$chkListBox.Items.Add($SupportedLanguage."Display Name")
 }

$btnOk.add_Click({
   [void]$formTesting.Close()
   [void]$formTesting.Dispose()})


[void]$formTesting.ShowDialog()

$Result = @()
ForEach ($CheckedItem in ($chkListBox.CheckedItems))
 {
  $Result += $Table | Where {$_."Display Name" -eq $CheckedItem} | Select-Object -Property Code
 }

 ForEach ($item in $Result)
  {
   $FullName = ($Table | Where {$_.Code -eq $($Item.Code)} | Select-Object "Display Name")."Display Name"
   Write-Host "$($Item.Code) -> $FullName"
  }

Proxy

You can detect a proxy server with the command:

[System.Net.WebRequest]::GetSystemWebProxy().GetProxy([uri]"http://www.google.com/").AbsoluteUri

But... if no proxy is used, the return address is the same as the address that is mentioned after GetProxy.

$CheckServer      = "http://www.google.com/"

$Result           = $null
$DefaultWebProxy  = [System.Net.WebRequest]::DefaultWebProxy.GetProxy([uri]($CheckServer)).AbsoluteUri
$SystemWebProxy   = [System.Net.WebRequest]::GetSystemWebProxy().GetProxy([uri]$CheckServer).AbsoluteUri

if($DefaultWebProxy -ne $CheckServer)
 {
  $Result = $DefaultWebProxy
 }
 elseif($SystemWebProxy -ne $CheckServer)
 {
  $Result = $SystemWebProxy     
 }
    
if($Result)
 {
  Write-Host "The proxy server '$Result' has been found and is used."
 }
 else
 {
  Write-Host "No proxy server has been found."
 }

Skip during translation and test the translation

I use variables in my scripts. And variable names may not be translated. You can recognise them as there are between less than and greater than signs. An example: <VariableName>. So it is mandatory that these items are not translated. DeepL has a non-translate class, but the free Google API has not. So I made one by myself.

Also, the generated JSON file should be checked. Sometimes, characters are handled differently per country. The English sentence "The screen height of '<DetectedHeight>' is greater than the allowed height of '<AllowedHeight>'." was translated in the Ukrain language to "Висота екрана "<DetectedHeight>" більша за дозволену висоту "<AllowedHeight>"."

Maybe you see it already, but there are double quotes where the original text has single quotes. 

The code:

$Translation = [System.Web.HttpUtility]::HtmlDecode($Translation)

has been changed to:

$Translation = [System.Web.HttpUtility]::HtmlDecode($Translation).Replace([char](34),"'")

And no issues anymore.

And now, the minimized version with the above-mentioned items included. If you need a minimal version, then this is a good starting point!

# =============================================================================================================================================
# Function block
# =============================================================================================================================================
  
  Function Get-Proxy
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       21-Sept-20222
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-Proxy
    =============================================================================================================================================
    .SYNOPSIS

    This function gets the proxy.

    If a proxy server is used, the returned server name is different from the server that is checked.

    #>

    $CheckServer      = "http://www.google.com/"

    $Result = $null
    $ThisFunctionName = $MyInvocation.InvocationName
    $DefaultWebProxy  = [System.Net.WebRequest]::DefaultWebProxy.GetProxy([uri]($CheckServer)).AbsoluteUri
    $SystemWebProxy   = [System.Net.WebRequest]::GetSystemWebProxy().GetProxy([uri]$CheckServer).AbsoluteUri

    if($DefaultWebProxy -ne $CheckServer)
     {
      $Result = $DefaultWebProxy
     }
     elseif($SystemWebProxy -ne $CheckServer)
     {
      $Result = $SystemWebProxy     
     }
    
    if($Result)
     {
      Write-Host "The proxy server '$Result' has been found and is used."
     }
     else
     {
      Write-Host "No proxy server has been found."
     }

    Return $Result
   }


  Function TranslateVia-GoogleFREEApi
   {
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-September-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     TranslateVia-GoogleFREEApi
    =============================================================================================================================================
    .SYNOPSIS

    This translates text from the source language to the target language, using the free Google Translation Service
    #>

    Param
    (
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $SourceLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TargetLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TextToTranslate
    )

    $Uri = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=$($SourceLanguage)&tl=$($TargetLanguage)&dt=t&q=$($TextToTranslate)"

    # =============================================================================================================================================
    # If a proxy server is used used, add the proxy details to the Invoke-Restmethod.
    # =============================================================================================================================================
          
      if ($ProxyServer) 
       {
        $Response = Invoke-RestMethod -Uri $Uri -Method Get -Proxy $ProxyServer -UseDefaultCredentials -ProxyUseDefaultCredentials
       }
        else
       {
        $Response = Invoke-RestMethod -Uri $Uri -Method Get
       }

    $Translation = $Response[0].SyncRoot | foreach { $_[0] }
    $Translation = [System.Web.HttpUtility]::HtmlDecode($Translation).Replace([char](34),"'")

    Write-Host "[Function TranslateVia-GoogleFREEApi] The text '$TextToTranslate' is translated from $SourceLanguage to $TargetLanguage"
    Write-Host "[Function TranslateVia-GoogleFREEApi] Result: $Translation`n"

    Return $Translation
   }

# =============================================================================================================================================
# End function block
# =============================================================================================================================================

  Clear-Host

  $JSONFile    = "$Env:TEMP\JasonData.json"
  $NewJSONFile = "$Env:TEMP\JasonData_New.json"
    
@'
     {
      "en" :
        {
         "Text 1"       : "one",
         "Text 2"       : "You can change this to whatever you like.",
         "Example 3"    : "three",
         "Example 4"    : "The part is for test.",
         "something 5"  : "This is the last line to be translated.",
         "extra"        : "An extra line.",
         "error"        : "The screen height of '<DetectedHeight>' is greater than the allowed height of '<AllowedHeight>'.",
         "LongVariable" : "You can <Replace> <This> with <Whatever> <You> <Like> and it <will> <not> break the <translation>. Only a <weird> translation."
        }
     }
'@ | Out-File -FilePath $JSONFile
  $JSONObject  = Get-Content -Path $JSONFile -Raw -Encoding UTF8 | ConvertFrom-Json
  $ProxyServer = Get-Proxy

# =============================================================================================================================================
# Grab all the headers. These might be different for each JSON file. So that has to dynamic.
# =============================================================================================================================================
    
  $Headers                   = ($JSONObject.en | Get-Member -type NoteProperty).Name
    
# =============================================================================================================================================
# And a new JSON object for the translations, including the English one.
# =============================================================================================================================================
        
  $NewJSONObject             = [ordered] @{}
  $SelectedLanguages         = @("sl","uk","de")

# =============================================================================================================================================
# First, add the English headers to the new JSON File. That is our starting point.
# =============================================================================================================================================

  $Array = [Ordered] @{}
  ForEach ($Header in $Headers)
   {
    $Array += @{$Header=$JSONObject.en.$Header}
   }
  
  $NewJSONObject.Add("en",$Array)

# =============================================================================================================================================
# And now, add all the other selected languages. 
# =============================================================================================================================================

  ForEach ($Language in $SelectedLanguages)
   {
    $Array = [Ordered]@{}
    ForEach ($Header in $Headers)
     {
      $TextToTranslate = $($JSONObject.en.$Header).Replace("&","")
      $Replacement     = @()
      $Original        = @()
      $Counter         = 0

      $OldText = $TextToTranslate
      While ($TextToTranslate.Contains("<") -and $TextToTranslate.Contains(">"))
       {
        $RandomNumber         = Get-Random -Minimum 11111111 -Maximum 99999999
        $Replacement         += $RandomNumber
        
        $Begin                = $TextToTranslate.IndexOf("<") 
        $End                  = $TextToTranslate.IndexOf(">")
        $Variable             = $TextToTranslate.SubString($Begin,$End-$Begin+1)
        $Original            += $Variable
        $TextToTranslate      = $TextToTranslate.Replace($Variable,$($Replacement[$Counter]))
        $Counter++
       }
      
      if($Counter -gt 0)
       {
        Write-Host "Old line: $OldText"
        Write-Host "New line: $TextToTranslate"
       }
      
      $TranslatedText = (TranslateVia-GoogleFREEApi -SourceLanguage "en" -TargetLanguage $Language -TextToTranslate $TextToTranslate)
      For ($a=0;$a -lt $Counter;$a++)
       {
        $TranslatedText = $TranslatedText.Replace($Replacement[$a],$Original[$a])
       }
      Write-Host "Put in the array: $TranslatedText"
      $Array += @{$Header=$TranslatedText}
     }
    
    $NewJSONObject.Add($Language,$Array)
   }
    
  $NewJSONObject | ConvertTo-Json | % { [System.Text.RegularExpressions.Regex]::Unescape($_) } | Out-File -FilePath $NewJSONFile -Encoding utf8
  Write-Host "The file '$NewJSONFile' has been written."

# =============================================================================================================================================
# Check the created JSON File.
# =============================================================================================================================================

  $bolError = $False
  $Message  = ""

  Try
   {
     
  # =============================================================================================================================================
  # If the created JSON file can be read, then there is no problem. 
  # =============================================================================================================================================
          
    $Null = Get-Content -Path $NewJSONFile -Encoding UTF8 | ConvertFrom-Json
    Write-Host "The JSON file '$NewJSONFile' seems to be fine. No further checks needed."
   }
  Catch
   {
      
  # =============================================================================================================================================
  # The created JSON file cannot be read. Now, create a tmp JSON file for each language and check if that one can be read successfully. 
  # =============================================================================================================================================
      
    Write-Host "There seems to be an issue with the JSON file '$NewJSONFile'. Let's check each language."

    ForEach ($Language in $SelectedLanguages)
     {
      $JSONText = $NewJSONObject.$Language
      $TempFile = New-TemporaryFile
      $JSONText | ConvertTo-Json | % { [System.Text.RegularExpressions.Regex]::Unescape($_) } | Out-File -FilePath $TempFile -Encoding utf8
      Try
       {
        $Null = Get-Content -Path $TempFile -Encoding UTF8 | ConvertFrom-Json
        $ErrorText =  "The language $Language has no issues."
        $Message  += "`n$ErrorText"
       }
       Catch
       {
        $ErrorText = "There are issues with the language $Language. Check the created JSON file manually!"
        $Message  += "`n$ErrorText"
       }
      Remove-Item -Path $TempFile -Force
     }
    Write-Host $Message
   }

The input file has an error.Saving settings in the registry

If you enter the authentication key and API Domain under DeepL, Google API key under Google Cloud Translation or APIM subscription key and APIM subscription region under Microsoft Cognitive Services Translator, that information is stored in the registry under HKEY_CURRENT_USER\Software\VroomSoft\AddTranslationsToJSONFile. If you do not want the settings to be saved, start the application with the command line option -NoUserSettings. If you want to clear the registry use the command line option -ClearUserSettings.

Modifications in version 2.0

I made the following changes in version 2.0:

DeepL, Google Cloud Services and Microsoft Azure Cognitive Services Translator can use their API to read the supported languages. That technique has been implemented.

Reading supported languages -> DeepL

Clear-Host
# API Key from https://www.deepl.com/pro-checkout/account?productId=1200&yearly=false&trial=false
# Credit card required in order to prevent misuse of our free API, in particular to prevent fraudulent multiple registrations. 
$APIKey              = "Your API Key"
$translateBaseURI    = "https://api-free.deepl.com/v2/languages?type=target"

$headers             = @{Authorization = "DeepL-Auth-Key $APIKey"}
Try
 {
  $Response            = @((Invoke-RestMethod -Uri $translateBaseURI -Method Post -Headers $headers).Language)
  $Response            = $Response | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique
  $Response
 }
  Catch
 {
  $_.Exception.Message
 }

Reading supported languages -> Google Cloud Services

Clear-Host
$key                 = "Your API Key"
$url                 = "https://translation.googleapis.com/language/translate/v2/languages?key=$($key)"
Try
 {
  $result              = Invoke-WebRequest -Uri $url -Method Get
  $Content             = $result | Select-Object -ExpandProperty Content
  $Response            = ((ConvertFrom-Json -InputObject $Content).data.languages).language
  $Response            = $Response | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique
  $Response
 }
  Catch
 {
  $_.Exception.Message
 }

Reading supported languages -> Microsoft Azure Cognitive Services Translator

Clear-Host

$translateBaseURI    = "https://api.cognitive.microsofttranslator.com/languages?api-version=3.0"
$conversionResult    = Invoke-RestMethod -Method Get -Uri $translateBaseURI
$Response           = ($conversionResult.translation | Get-Member -type NoteProperty).Name | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique
$Response

Reading supported languages -> Google FREE API

Only Google FREE API cannot use the API to read the supported languages. I had to use their website instead:

Google Translator supported languages.
Google Translator-supported languages.

With the use of the inspector I found the class:

CSS code
Classes

Then it was relatively easy to implement as I knew the CSS:

# Source for solution: https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/find-multiple-matches
$Content   = invoke-restmethod https://cloud.google.com/translate/docs/languages
$LookFor   = "<code translate=""no"" dir=""ltr"">.*</code>"
$Languages = @(($Content | Select-String -AllMatches $LookFor | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value).Replace("<code translate=""no"" dir=""ltr"">","").Replace("</code>",""))
$Languages = $Languages | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique
$Languages

Modifications in version 2.1

The function Check-WorkingInternetConnection has been rewritten: when the internet was reachable, it came back with an error. Test-NetConnection and Test-Connection do not work when a proxy server is used, so I had to find an alternative.

And that is $Status = (New-Object System.Net.NetworkInformation.Ping).SendPingAsync("www.google.com") followed by [Threading.Tasks.Task]::WaitAll($Status). There is a working internet connection if the last command is run successfully. 

 Function Check-WorkingInternetConnection
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       09-July-2020 / Modified 04-Oct-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Check-WorkingInternetConnection
    =============================================================================================================================================
    .SYNOPSIS

    Check for a working internet connection.

    (New-Object System.Net.NetworkInformation.Ping).SendPingAsync("www.google.com") works also when a proxy server is used.
    [Threading.Tasks.Task]::WaitAll($Status) will thrown an error if the host cannot be reached.
    Test-NetConnection and Test-Connection do not work if a proxy server is used. 

    #>

    Param
     (
      [String] $HostNameToCheck = "www.google.com"
     )

    $ThisFunctionName         = $MyInvocation.MyCommand.Name
    $HostNameToCheck          = $HostNameToCheck.Replace("https://","").Replace("http://","")  # Remove https:// or http://
    $HostNameToCheck          = $HostNameToCheck.Replace("/","")                               # Remove a trailing / if needed.

    Try
     {
      $Status = (New-Object System.Net.NetworkInformation.Ping).SendPingAsync($HostNameToCheck)
      [Threading.Tasks.Task]::WaitAll($Status)
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Details:"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "  - Result:        $($Status.Result.Status)" 
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "  - Faulted:       $($Status.Isfaulted)"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "  - Is Completed:  $($Status.IsCompleted)"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The host $HostNameToCheck can be reached. Thus there is a working internet connection." 
      $Result = $True
     }
      Catch
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The host $HostNameToCheck cannot be reached. Thus there is no working internet connection."
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Error: $($Status.Exception)"
      $Result = $False
     }
    Return $Result
   }

Modifications in version 2.2

There are three changes:

  1. Changed the date notation of the logfile to yyyy-MM-dd HH-mm-ss, for example, '2022-11-14 22-23-09'.
  2. Changed the date notation of the JSON file with the translations to yyyy-MM-dd HH-mm-ss, for example, '2022-11-14 22-23-09'.
  3. The function 'UserDetails' has been modified.

Background information

You can run this script in two modes:

  • Silent:
    Use the command line options -Silent, -JSONFilename to specify the JSON file with the English headers and -LanguagesToInclude to specify the translation languages. If you use -all then all the languages are selected and used. If you have used this application before and the license information is stored in the registry then you can add the parameter -TranslatorService to specify which translator service you want to use. If there is no license information available in the registry then the Google Free API will be used instead.
  • Non-silent (default mode):
    The GUI is used. However, you can still specify -ClearUserSettings or -NoUserSettings.

Get-Help "AddTranslationsToJSONFile v22.ps1" -Detailed shows the following output:

NAME
    C:\tmp\AddTranslationsToJSONFile v22\AddTranslationsToJSONFile v22.ps1
    
SYNOPSIS
    Translates the contents of a JSON File.
    
    
SYNTAX
    C:\tmp\AddTranslationsToJSONFile v22\AddTranslationsToJSONFile v22.ps1 [-DetailedLogging] [-ClearUserSettings] 
    [-NoUserSettings] [-Silent] [-JSONFilename <String>] [-LanguagesToInclude <String[]>] [-TranslatorService 
    <String>] [<CommonParameters>]
    
    C:\tmp\AddTranslationsToJSONFile v22\AddTranslationsToJSONFile v22.ps1 [-DetailedLogging] [-ClearUserSettings] 
    [-NoUserSettings] -Silent -JSONFilename <String> -LanguagesToInclude <String[]> [-TranslatorService <String>] 
    [<CommonParameters>]
    
    
DESCRIPTION
    Consider the following JSON File with the following content:
     {
      "en" :
        {
         "Text 1"     : "one",
         "Text 2"     : "You can change this to whatever you like.",
         "Example 3"  : "three",
         "Example 4"  : "The part is for test.",
         "something 5": "This is the last line to be translated.",
         "extra"      : "An extra line."
        }
     }
    
    The content of the values "one", "You can change this to whatever you like.", "three" etc. is translated to the 
    selected languages
    using either Google Translate (FREE API), Google Cloud Translate, Microsoft Azure Cognitive Services Translator or 
    DeepL. 
    
    The keys "Test 1", "Test 2" etc. can be whatever you like. You are not sticked to these keys. 
    
    If the -Silent switch is used, then the translation is done via Google Translate FREE API.
    
    This script can only be used when PowerShell FullLanguage is used. You will get an error message if that is not 
    the case.
    

PARAMETERS
    -DetailedLogging [<SwitchParameter>]
        
    -ClearUserSettings [<SwitchParameter>]
        
    -NoUserSettings [<SwitchParameter>]
        
    -Silent [<SwitchParameter>]
        
    -JSONFilename <String>
        
    -LanguagesToInclude <String[]>
        
    -TranslatorService <String>
        
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see 
        about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216). 
    
    -------------------------- EXAMPLE 1 --------------------------
    
    PS C:\>Start the application and show the GUI.
    
    ."AddTranslationsToJSONFile v22.ps1"
    
    
    
    
    -------------------------- EXAMPLE 2 --------------------------
    
    PS C:\>Start the application and show the GUI with verbose logging on the screen.
    
    ."AddTranslationsToJSONFile v22.ps1" -Verbose
    
    
    
    
    -------------------------- EXAMPLE 3 --------------------------
    
    PS C:\>Start the application and show the GUI with detailed logging to a log file in %TEMP%
    
    ."AddTranslationsToJSONFile v22.ps1" -DetailedLogging
    
    
    
    
    -------------------------- EXAMPLE 4 --------------------------
    
    PS C:\>Start the application, show the GUI with detailed logging to a log file in %TEMP% and clear the settings 
    before starting.
    
    ."AddTranslationsToJSONFile v22.ps1" -DetailedLogging -ClearUserSettings
    
    
    
    
    -------------------------- EXAMPLE 5 --------------------------
    
    PS C:\>Start the application in silent mode and translate the JSON file "c:\path\to\jsonfile.json" to Dutch, 
    French, Spanish, Indonesian
    
    and German.
    ."AddTranslationsToJSONFile v22.ps1" -Silent -JSONFilename "c:\path\to\jsonfile.json" -LanguagesToInclude 
    nl,fr,es,id,de
    LanguagesToInclude can be any of these: bg|cs|da|de|es|fr|id|it|nl|sv|el|et|fi|hu|lt|pl|ro|sk|sl|uk
    
    
    
    
    -------------------------- EXAMPLE 6 --------------------------
    
    PS C:\>Start the application in silent mode and translate the JSON file "c:\path\to\jsonfile.json" in all 
    available languages.
    
    ."AddTranslationsToJSONFile v22.ps1" -Silent -JSONFilename "c:\path\to\jsonfile.json" -LanguagesToInclude all
    
    
    
    
    -------------------------- EXAMPLE 7 --------------------------
    
    PS C:\>Start the application in silent mode and translate the JSON file "c:\path\to\jsonfile.json" in all 
    available languages. Use DeepL
    
    as the translation service
    ."AddTranslationsToJSONFile v22.ps1" -Silent -JSONFilename "c:\path\to\jsonfile.json" -LanguagesToInclude all 
    -TranslatorService DeepL
    
    
    
    
REMARKS
    To see the examples, type: "get-help C:\tmp\AddTranslationsToJSONFile v22\AddTranslationsToJSONFile v22.ps1 
    -examples".
    For more information, type: "get-help C:\tmp\AddTranslationsToJSONFile v22\AddTranslationsToJSONFile v22.ps1 
    -detailed".
    For technical information, type: "get-help C:\tmp\AddTranslationsToJSONFile v22\AddTranslationsToJSONFile v22.ps1 
    -full".

The script

<#
.SYNOPSIS
    Translates the contents of a JSON File.

.DESCRIPTION
    Consider the following JSON File with the following content:
     {
      "en" :
        {
         "Text 1"     : "one",
         "Text 2"     : "You can change this to whatever you like.",
         "Example 3"  : "three",
         "Example 4"  : "The part is for test.",
         "something 5": "This is the last line to be translated.",
         "extra"      : "An extra line."
        }
     }

    The content of the values "one", "You can change this to whatever you like.", "three" etc. is translated to the selected languages
    using either Google Translate (FREE API), Google Cloud Translate, Microsoft Azure Cognitive Services Translator or DeepL. 

    The keys "Test 1", "Test 2" etc. can be whatever you like. You are not sticked to these keys. 

    If the -Silent switch is used, then the translation is done via Google Translate FREE API.

    This script can only be used when PowerShell FullLanguage is used. You will get an error message if that is not the case. 

.EXAMPLE
     Start the application and show the GUI.
     ."AddTranslationsToJSONFile v22.ps1" 

.EXAMPLE
     Start the application and show the GUI with verbose logging on the screen.
     ."AddTranslationsToJSONFile v22.ps1" -Verbose

.EXAMPLE
     Start the application and show the GUI with detailed logging to a log file in %TEMP%
     ."AddTranslationsToJSONFile v22.ps1" -DetailedLogging

.EXAMPLE
     Start the application, show the GUI with detailed logging to a log file in %TEMP% and clear the settings before starting.
     ."AddTranslationsToJSONFile v22.ps1" -DetailedLogging -ClearUserSettings 

.EXAMPLE
     Start the application in silent mode and translate the JSON file "c:\path\to\jsonfile.json" to Dutch, French, Spanish, Indonesian
     and German.
     ."AddTranslationsToJSONFile v22.ps1" -Silent -JSONFilename "c:\path\to\jsonfile.json" -LanguagesToInclude nl,fr,es,id,de
     LanguagesToInclude can be any of these: bg|cs|da|de|es|fr|id|it|nl|sv|el|et|fi|hu|lt|pl|ro|sk|sl|uk

.EXAMPLE
     Start the application in silent mode and translate the JSON file "c:\path\to\jsonfile.json" in all available languages.
     ."AddTranslationsToJSONFile v22.ps1" -Silent -JSONFilename "c:\path\to\jsonfile.json" -LanguagesToInclude all

.EXAMPLE
     Start the application in silent mode and translate the JSON file "c:\path\to\jsonfile.json" in all available languages. Use DeepL
     as the translation service
     ."AddTranslationsToJSONFile v22.ps1" -Silent -JSONFilename "c:\path\to\jsonfile.json" -LanguagesToInclude all -TranslatorService DeepL

.NOTES
    Author:  Willem-Jan Vroom
    Website: 
    Twitter: @TheStingPilot

v0.1 Beta 1:
   * Initial version. 

v0.1 (RC1):
   * Language translation service information is stored in the registry under 'HKCU:\Software\VroomSoft\AddTranslationsToJSONFile'.
   * UTF8 decoding when using DeepL.
   * Proxy support
   * Check if the created JSON file is valid.
   * Select or unselect all languages.

v1.0
   * More detailed error message is the input JSON file has an incorrect layout.
   * An ulimited number of variables in one sentence is allowed. Was restricted to three.

v1.1:
   * Silent option includes the option to select the translation service
   * Added command line option TranslatorService. You can select one of:
      - Deepl
      - GoogleFREEAPI
      - GoogleCloudServices
      - MicrosoftCognitive
     There is one requirement: that the login details are in the registry. Fallback: Google FREE API.
   * The stutus bar shows the language instead of the language code.

v1.2:
   * The output file name is created dynamically and includes the name of the translation service.
   * The text boxes with the input JSON file name and the generated JSON filename with the translations show the last part of the filename.

v1.3:
   * Three languages have been added: ru, pt and zh

v2.0:
   * The languages are added dynamically.
   * It is no longer needed to use the radio buttons to specify the translation service.
   * Improved error handing if a key is not found while reading the language files.

v2.1:
   * Function 'WorkingInternetConnection' has been rewritten.
   * In some cases the translate button was not enabled.

v2.2:
   * Issue with -Silent option and the usage of DeepL as the translation service: there is a timeout.
     Tested on two computers and this issue was on one computer.
   * Changed the date notation of the logfile to yyyy-MM-dd HH-mm-ss, for example '2022-11-14 22-23-09'.
   * Changed the date notation of the JSON file with the translations to yyyy-MM-dd HH-mm-ss, for example '2022-11-14 22-23-09'.
   * The function 'UserDetails' has been modified.

Resources:
 - https://cloud.google.com/translate/docs/basic/translating-text?hl=en-GB#powershell
 - https://docs.microsoft.com/en-us/azure/cognitive-services/translator/how-to-create-translator-resource#authentication-keys-and-endpoint-url
 - https://blog.kloud.com.au/2018/08/28/using-azure-cognitive-services-language-text-translation-with-powershell/
 - https://www.alexandrumarin.com/translate-text-with-powershell-and-google-translate/
 - https://www.deepl.com/pro-api?cta=header-pro-api/
 - https://mattou07.net/posts/creating-complex-json-with-powershell/
 - https://stackoverflow.com/questions/47779157/convertto-json-and-convertfrom-json-with-special-characters/47779605#47779605
 - https://stackoverflow.com/questions/69090021/convert-a-string-in-powershell-in-europe-to-utf-8

#>

[CmdletBinding(DefaultParameterSetName = 'Default')]

Param
  (
   [Parameter(HelpMessage='Enable detailed logging to a log file.')]
   [Parameter(ParameterSetName = 'Default',Mandatory=$False)]
   [Parameter(ParameterSetName = 'Silent' ,Mandatory=$False)]
   [Switch]   $DetailedLogging,

   [Parameter(HelpMessage='Clear the settings before starting.')]
   [Parameter(ParameterSetName = 'Default',Mandatory=$False)]
   [Switch]   $ClearUserSettings,

   [Parameter(HelpMessage='Do not load the user settings before starting.')]
   [Parameter(ParameterSetName = 'Default',Mandatory=$False)]
   [Switch]   $NoUserSettings,
   
   [Parameter(HelpMessage='Enable silent mode. The text is translated by the Google FREE API service.')]
   [Parameter(ParameterSetName = 'Silent' ,Mandatory=$True)]
   [Switch]   $Silent,

   [Parameter(HelpMessage='JSON filename with the English text.')]
   [Parameter(ParameterSetName = 'Silent' ,Mandatory=$True)]
   [String] $JSONFilename,

   [Parameter(HelpMessage='Select all the languages you want to use for the translation.')]
   [Parameter(ParameterSetName = 'Silent' ,Mandatory=$True)]
   [ValidateSet("all","bg","cs","da","de","es","fr","id","it","ja","lv","nl","sv","el","et","fi","hu","lt","pl","ro","sk","sl","sv","tr","uk","pt","ru","zh")]
   [String[]] $LanguagesToInclude,

   [Parameter(HelpMessage='Select all the languages you want to use for the translation.')]
   [Parameter(ParameterSetName = 'Silent' ,Mandatory=$False)]
   [ValidateSet("Deepl","GoogleFREEAPI","GoogleCloudServices","MicrosoftCognitive")]
   [String] $TranslatorService = "GoogleFREEAPI"

   )

  Clear-Host

# =============================================================================================================================================
# Function block
# =============================================================================================================================================

  Function Add-EntryToLogFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-May-2020 / Modified 09-May-2022: Includes the function name
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Add-EntryToLogFile
    =============================================================================================================================================
    .SYNOPSIS

    This function adds a line to a log file

    #>

    Param
     (
      [Parameter(Mandatory=$True)]  [string] $Entry,
      [Parameter(Mandatory=$False)] [String] $FunctionName
     )
      
     Write-Verbose "[Function: $FunctionName] - $Entry"
     If ($Global:gblDetailedLogging -and $Global:LogFile)
      {
       $Timestamp = (Get-Date -UFormat "%a %e %b %Y %X").ToString()
       Add-Content $Global:LogFile -Value $($Timestamp + "[Function: $FunctionName] - $Entry") -Force -ErrorAction SilentlyContinue
      }
   }

  Function Check-WorkingInternetConnection
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       09-July-2020 / Modified 04-Oct-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Check-WorkingInternetConnection
    =============================================================================================================================================
    .SYNOPSIS

    Check for a working internet connection.

    (New-Object System.Net.NetworkInformation.Ping).SendPingAsync("www.google.com") works also when a proxy server is used.
    [Threading.Tasks.Task]::WaitAll($Status) will thrown an error if the host cannot be reached.
    Test-NetConnection and Test-Connection do not work if a proxy server is used. 

    #>

    Param
     (
      [Parameter(Mandatory=$False)] [String] $HostNameToCheck = "www.google.com"
     )

    $ThisFunctionName         = $MyInvocation.MyCommand.Name
    $HostNameToCheck          = $HostNameToCheck.Replace("https://","").Replace("http://","")  # Remove https:// or http://
    $HostNameToCheck          = $HostNameToCheck.Replace("/","")                               # Remove a trailing / if needed.

    Try
     {
      $Status = (New-Object System.Net.NetworkInformation.Ping).SendPingAsync($HostNameToCheck)
      [Threading.Tasks.Task]::WaitAll($Status)
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Details:"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "  - Result:        $($Status.Result.Status)" 
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "  - Faulted:       $($Status.Isfaulted)"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "  - Is Completed:  $($Status.IsCompleted)"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The host $HostNameToCheck can be reached. Thus there is a working internet connection." 
      $Result = $True
     }
      Catch
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The host $HostNameToCheck cannot be reached. Thus there is no working internet connection."
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Error: $($Status.Exception)"
      $Result = $False
     }
    Return $Result
   }

  Function Get-Proxy
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       21-Sept-20222
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-Proxy
    =============================================================================================================================================
    .SYNOPSIS

    This function gets the proxy.

    If a proxy server is used, the returned server name is different from the server that is checked.

    #>

    Param
     (
      [Parameter(Mandatory=$False)] [String] $CheckServer      = "http://www.google.com/"
     )
    

    $Result = $null
    $ThisFunctionName = $MyInvocation.MyCommand.Name
    $DefaultWebProxy  = [System.Net.WebRequest]::DefaultWebProxy.GetProxy([uri]($CheckServer)).AbsoluteUri
    $SystemWebProxy   = [System.Net.WebRequest]::GetSystemWebProxy().GetProxy([uri]$CheckServer).AbsoluteUri

    If ($DefaultWebProxy -ne $CheckServer)
     {
      $Result = $DefaultWebProxy
     }
      ElseIf ($SystemWebProxy -ne $CheckServer)
     {
      $Result = $SystemWebProxy     
     }
    
    If ($Result)
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The proxy server '$Result' has been found and is used."
     }
      else
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No proxy server has been found."
     }

    Return $Result
   }

  Function Display-MessageBox
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       24-May-2021 / 24-Apr-2024 / 09-June-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Display-MessageBox
    =============================================================================================================================================
    .SYNOPSIS

    This function displays a message box.

    The return value is:

    None (no result) 	0
    OK 	                1
    Cancel 	            2
    Yes 	            6
    No. 	            7

    #>

    param
     (
      [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()][String] $Text,
      [Parameter(Mandatory=$True)] [ValidateNotNullOrEmpty()][String] $Title,
      [Parameter(Mandatory=$False)][Switch] $GUI,
      [Parameter(Mandatory=$False)][Switch] $Error,
      [Parameter(Mandatory=$False)][ValidateSet('Ok','AbortRetryIgnore','OkCancel','YesNoCancel','YesNo')][String] $Button = "Ok"
     )
         
    If ($GUI)
     {
      If ($Error)
       {
        $Return = [System.Windows.Forms.MessageBox]::Show($this,$Text.Trim(),$Title,$Button,"Error")
       }
        else
       {
        $Return = [System.Windows.Forms.MessageBox]::Show($this,$Text.Trim(),$Title,$Button,"Asterisk")
       }
     }
      else
     {
      Write-Host "$Text`n"
      Return 0
     }

     Return $($Return.value__)
   }

  Function UserDetails
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       03-Jan-21 / Modified on 14-Jan-21 / Modified on 22-Apr-22 / Modified on 01-May-2022 / Modified on 17-May-2022 /  
	                  Modified 21-Nov-2022
    Created by:       Willem-Jan Vroom
    Functionname:     UserDetails
    =============================================================================================================================================
    .SYNOPSIS

    This function returns 4 details of the Current Logged In Usser
    1. The username of the current logged in user
    2. User\Domain of the current logged in user
    3. User SID fo the User\Domain
    4. Account name that is using the script 

    Initially, the scriptAccount was found with the command 
    [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

    But that command throws up an error due to the PowerShell ConstrainedMode.

    Also, the output of whoami /user depends on the language. In the German language the output is different.
    The header information is in the German language, so the header should be read from the CSV file.
    That can be done with PSObject.Properties.Name

    C:\Users\Test>whoami /user

    BENUTZERINFORMATIONEN
     ---------------------

    Benutzername         SID
    ==================== ==============================================
    windows10wkg_02\test S-1-5-21-1753037473-1212035439-1379364385-1001

    On 21-Nov-2022 the function has been changed.

    There are 6 situations that should be handled:
    1. Find the logged on user sid when there is one user logged on on a 'normal' Windows computer
    2. Find the logged on user sid when multiple users are logged on in an RDS / Citrix environment where there is no local admin.
    3. Find the logged on user sid when multiple users are logged on in an RDS / Citrix environment where the users are local admin.
    4. Find the logged on user sid when there is a SYSTEM account, and there is a user logged on on a 'normal' Windows computer. 
       This occurs during a Software Center installation performed under the SYSTEM account.
    5. Find the logged on user sid when there is a SYSTEM account, and there is a user logged on on an RDS / Citrix environment.
    6. Take care of the PowerShell Language Mode.

    While testing I found that the MainWindowHandle is not 0 for the user that is running the explorer process. That is true for all the
    above mentioned situations.

    You can check while running this command with elevated rights:

    get-process -IncludeUserName | Select-Object -Property Username, Id, Name,MainWindowHandle | Where {($_.Name -eq "explorer" -or $_.Name -eq "pfwsmgr")} | Format-Table

    The output will be something like this:

    UserName             Id Name     MainWindowHandle
    --------             -- ----     ----------------
    DEMO\user1         1380 explorer                0
    DEMO\adminuser2    6556 explorer           131134

    So, you have to continue with PID ID 6556.

    So, I used the method of finding the PID, and use that PID as a condition for the Get-WMIObject to search for the explorer or pfwsmgr
    process with that PID.

    #>

  # =============================================================================================================================================
  # Find the current logged on user by checking the rights on the explore.exe (of pfwsmgr.exe with Ivanti Workspace Control) process.
  # If the language mode is FullLanguage then use MainWindowHandle. That is needed when this script is run from SYSTEM context. I assume that
  # the SYSTEM account is not effected by the PowerShell Language Mode.
  # If the language mode is not FullLanguage then use a check on username and the owner of the explorer or pfwsmgr process. 
  # =============================================================================================================================================

    if($Global:gblFullLanguage)
     {
      $PIDID              = (get-process | Select-Object -Property Id, Name, MainWindowHandle | Where {(($_.Name -eq "explorer" -or $_.Name -eq "pfwsmgr") -and $($_.MainWindowHandle).ToInt32() -gt 0)}).Id
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The process 'explorer' or 'pfwsmgr' has the PID $PIDID."
      $Explorer           = (Get-WMIObject -Query "Select * From Win32_Process Where (ProcessId=$PIDID)")
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The process with PID $PIDID has the name $($Explorer.Name)."

      $UserName           = $Explorer.GetOwner()
      $SID                = ($Explorer.GetOwnerSID()).SID
     }
      else
     {
      $Explorer      = @(Get-WMIObject -Query "Select * From Win32_Process Where (Name='explorer.exe' or Name='pfwsmgr.exe')")
      $EnvUSERNAME   = $Env:USERNAME

      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The process 'explorer.exe' or 'pfwsmgr.exe' is running $($Explorer.Count) times."

      For ($a=0; $a -le ($Explorer.Count); $a++)
       {
        $UserName      = ($Explorer[$a]).GetOwner()
        If ($($UserName.User) -eq $EnvUSERNAME)
         {
          $SID           = (($Explorer[$a]).GetOwnerSID()).SID
          Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "A valid SID '$SID' has been found for user '$EnvUSERNAME'." 
          Break
         }
       }
     }

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found domain:        $($UserName.Domain)."
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found username:      $($UserName.User)."
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found SID:           $SID."
        
    $UserAndDomain      = "$($Username.Domain )\$($Username.User)".ToUpper()
    $tmpScriptAccount   = (whoami /user /FO csv | convertfrom-csv)
    $TranslatedUserName = $tmpScriptAccount.PSObject.Properties.Name[0]
    $ScriptAccount      = $($tmpScriptAccount.$TranslatedUserName).ToUpper()

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Found scriptaccount: $ScriptAccount"
    
    Return $($Username.User),$UserAndDomain,$SID,$ScriptAccount
   }

  Function Test-RegistryKeyValue
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-Dec-20
    Created by:       (C) Aaron Jensen 
                      https://stackoverflow.com/questions/5648931/test-if-registry-value-exists
    Organization:     Carbon Module
    Functionname:     Test-RegistryKeyValue
    =============================================================================================================================================
    .SYNOPSIS

    #>

    [CmdletBinding()]
    param 
     (
      [Parameter(Mandatory = $True)][string]  $Path,
      [Parameter(Mandatory = $True)][string]  $Name
     )

    If ( -not(Test-Path -Path $Path -PathType Container))
     {
      return $False
     }

    $properties = Get-ItemProperty -Path $Path
    If (-not $properties)
     {
      return $False
     } 

    $member = Get-Member -InputObject $properties -Name $Name
    If ($member)
     {
      return $True
     }
      else
     {
      return $False
     }
   }

  Function Find-Language
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       30-Dec-20
    Created by:       Willem-Jan Vroom 
    Organisation:                      
    Functionname:     Find-Language
    =============================================================================================================================================
    .SYNOPSIS

    This function works the best, even running under system context.
    Get-UiCulture returns the UICulture details from the account that is used. So when the system account is used, incorrect
    details might be shown.

    #>

    [CmdletBinding()]
    Param
     (
      [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $CurrentUserSID
     )

    $Result           = "en-US"
    $ThisFunctionName = $MyInvocation.MyCommand.Name

    $RegKey = "REGISTRY::HKEY_USERS\$CurrentUserSID\Control Panel\Desktop"
    $Value  = "PreferredUILanguages"
    If (Test-RegistryKeyValue -Path $RegKey -Name $Value)
     {
      $Result = (get-itemproperty $RegKey | Select -ExpandProperty $Value).Split()[0]
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' exists. The data is '$Result'."
      Return $Result
     }
      else
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' does not exists. Skipping to machine settings."
     }

    $RegKey = "REGISTRY::HKEY_USERS\.DEFAULT\Control Panel\Desktop\MuiCached"
    $Value  = "MachinePreferredUILanguages"
    If (Test-RegistryKeyValue -Path $RegKey -Name $Value)
     {
      $Result = (get-itemproperty $RegKey | Select -ExpandProperty $Value).Split()[0]
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Regkey '$RegKey' value '$Value' exists. The data is '$Result'."
      Return $Result
     }

    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There was a problem reading the registry..."
    Return $Result
   }

  Function Set-Registry
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       13-February-2021 / Modified 20-Sept-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Set-Registry
    =============================================================================================================================================
    .SYNOPSIS

    This function sets a registry key
    
    #>

    param
     (
      [Parameter(Mandatory=$True) ] [String]$RegistryKey,
      [Parameter(Mandatory=$True) ] [String]$ValueName,
      [Parameter(Mandatory=$True) ] [String]$Value,
      [Parameter(Mandatory=$False)] [Switch]$Boolean,
      [Parameter(Mandatory=$False)] [Switch]$IsArray
     )

     # Check if the $Registrykey exists
     $ThisFunctionName = $MyInvocation.MyCommand.Name

     If (-not(test-path($RegistryKey)))
      {
       $Key    = $RegistryKey.Split(":")[1]
       $NewKey = $($RegistryKey.Split(":")[0]) + ":"
       foreach($Item in $($Key.Split("\")))
        {
         $NewKey += "\$Item"
         If (-not(test-path($NewKey)))
          {
           New-Item $NewKey -Force | Out-Null
           Add-EntryToLogFile "Created the registry key: $NewKey." -FunctionName $ThisFunctionName
          }
        }

      }
     
     If ($Boolean)
      {
       If ($Value)
        { 
         New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType DWord -Value 1 -Force | Out-Null
         Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):" -FunctionName $ThisFunctionName
         Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"      -FunctionName $ThisFunctionName
         Add-EntryToLogFile -Entry "          Type:      REG_DWORD"       -FunctionName $ThisFunctionName
         Add-EntryToLogFile -Entry "          Value:     1"               -FunctionName $ThisFunctionName
        }
         else
        {
         New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType DWord -Value 0 -Force | Out-Null
         Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):" -FunctionName $ThisFunctionName
         Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"      -FunctionName $ThisFunctionName
         Add-EntryToLogFile -Entry "          Type:      REG_DWORD"       -FunctionName $ThisFunctionName
         Add-EntryToLogFile -Entry "          Value:     0"               -FunctionName $ThisFunctionName
        }
      }
       elseIf ($IsArray)
      {
       New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType String -Value $($Value -join ",") -Force | Out-Null
       Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):"       -FunctionName $ThisFunctionName
       Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"            -FunctionName $ThisFunctionName
       Add-EntryToLogFile -Entry "          Type:      REG_SZ"                -FunctionName $ThisFunctionName
       Add-EntryToLogFile -Entry "          Value:     $($Value -join ",")"   -FunctionName $ThisFunctionName
      }
       else
      {
       New-ItemProperty -Path $RegistryKey -Name $ValueName -PropertyType String -Value $Value -Force | Out-Null
       Add-EntryToLogFile -Entry "Created in the hive $($RegistryKey):"       -FunctionName $ThisFunctionName
       Add-EntryToLogFile -Entry "  Created ValueName: $ValueName"            -FunctionName $ThisFunctionName
       Add-EntryToLogFile -Entry "          Type:      REG_SZ"                -FunctionName $ThisFunctionName
       Add-EntryToLogFile -Entry "          Value:     $Value"                -FunctionName $ThisFunctionName
      }
   }

  Function Read-Registry
   {
    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       13-February-2021 / Modified 14-May-2021 / Modified 20-Sept-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Read-Registry
    =============================================================================================================================================
    .SYNOPSIS

    This function reads a registry key and returns the value
    
    #>

    param
     (
      [Parameter(Mandatory=$True)] [String]$RegistryKey,
      [Parameter(Mandatory=$True)] [String]$ValueName
     )

    $ThisFunctionName = $MyInvocation.MyCommand.Name
    If (Test-Path ($RegistryKey))
     {
      $Value        = (Get-ItemProperty -Path $RegistryKey).$ValueName
      If (($Value -ne $null) -and ($Value.Length -ne 0))
       {
        Add-EntryToLogFile -Entry "The registrykey '$RegistryKey' with valuename '$ValueName' exists." -FunctionName $ThisFunctionName
        $PropertyType = (Get-Item $RegistryKey).GetValueKind($ValueName)
        Add-EntryToLogFile -Entry "Hive:          $RegistryKey"       -FunctionName $ThisFunctionName
        Add-EntryToLogFile -Entry "  - ValueName: $ValueName"         -FunctionName $ThisFunctionName
        Add-EntryToLogFile -Entry "  - Type:      $PropertyType"      -FunctionName $ThisFunctionName
        Add-EntryToLogFile -Entry "  - Value:     $Value"             -FunctionName $ThisFunctionName
        If ($PropertyType -eq "String")
         {
          Return $Value
         }
          elseIf ($PropertyType -eq "DWord")
         {
          If ($Value -eq 1)
           {
            Return $True
           }
            else
           {
            Return $False
           }
         }
       }
     }
     Return [string]::Empty
   }
   
  Function Get-FileFromButton
   {
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-September-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-FileFromButton
    =============================================================================================================================================
    .SYNOPSIS

    You can browse to a file when clicked on the 'Browse' button.
    #>

    Param
    (
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $InitialDirectory,
     [Parameter(Mandatory = $False)]                         [String] $Filter
    )
    
    $ThisFunctionName        = $MyInvocation.MyCommand.Name

    $File                    = New-Object System.Windows.Forms.OpenFileDialog  
    $File.InitialDirectory   = $InitialDirectory 
    $File.Filter             = $Filter
    $File.Multiselect        = $False
    $File.CheckFileExists    = $True
    $File.CheckPathExists    = $True
    $Result = $File.ShowDialog()
    If ($Result -eq [System.Windows.Forms.DialogResult]::OK) 
     {
      $Selected = $File.FileName
     }
    $File.Dispose()
    If ($Selected)
     {
      Add-EntryToLogFile -Entry "The file '$Selected' has been selected as the input JSON file." -FunctionName $ThisFunctionName
      Return $Selected
     }
   }

  Function TranslateVia-GoogleFREEApi
   {
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-September-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     TranslateVia-GoogleFREEApi
    =============================================================================================================================================
    .SYNOPSIS

    This translates text from the source language to the target language, using the free Google Translation Service
    #>

    Param
    (
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $SourceLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TargetLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TextToTranslate
    )

    $ThisFunctionName = $MyInvocation.MyCommand.Name
    Try
     {
      $URL = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=$($SourceLanguage)&tl=$($TargetLanguage)&dt=t&q=$($TextToTranslate)"

    # =============================================================================================================================================
    # If a proxy server is used used, add the proxy details to the Invoke-Restmethod.
    # =============================================================================================================================================
          
      If ($ProxyServer) 
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Using $ProxyServer"
        $Response = Invoke-RestMethod -Uri $URL -Method Get -Proxy $ProxyServer -UseDefaultCredentials -ProxyUseDefaultCredentials -TimeoutSec 10
       }
        else
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No proxy server"
        $Response = Invoke-RestMethod -Uri $URL -Method Get -TimeoutSec 10
       }

      $Translation = $Response[0].SyncRoot | foreach { $_[0] }
      $Translation = [System.Web.HttpUtility]::HtmlDecode($Translation).Replace([char](34),"'")

      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The text '$TextToTranslate' is translated from $SourceLanguage to $TargetLanguage"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Result: $Translation"
     }
      Catch
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There is an error while translating the text."
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Error message: $($_.Exception.Message)`n"
      $Translation = "ERROR: $($_.Exception.Message)"
     }

    Return $Translation
   }

  Function TranslateVia-DeepL
   {
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-September-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     TranslateVia-DeepL
    =============================================================================================================================================
    .SYNOPSIS

    This translates text from the source language to the target language, using DeepL.
    There is a free version available. More info via https://www.deepl.com/pro/change-plan?cta=header-prices#developer
    #>

    Param
    (
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $SourceLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TargetLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TextToTranslate,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $AKey,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $Domain
    )

    $ThisFunctionName = $MyInvocation.MyCommand.Name

    $Headers = @{Authorization = "DeepL-Auth-Key $AKey"}
    $Body    = @{target_lang = $TargetLanguage}
    $Body   += @{source_lang = $SourceLanguage}
    $Body   += @{split_sentences = 0}
    $Body   += @{text = $TextToTranslate}

    Try
     {
      
    # =============================================================================================================================================
    # If a proxy server is used used, add the proxy details to the Invoke-Restmethod.
    # =============================================================================================================================================
          
      If ($ProxyServer) 
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Using $ProxyServer"
        $Response = Invoke-RestMethod -Uri $Domain -Method Post -Body $Body -Headers $Headers -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials -TimeoutSec 10
       }
        else
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No proxy server"
        $Response = Invoke-RestMethod -Uri $Domain -Method Post -Body $Body -Headers $Headers -TimeoutSec 10
       }
      $Translation = [System.Web.HttpUtility]::HtmlDecode([Text.Encoding]::UTF8.GetString([Text.Encoding]::GetEncoding(28591).GetBytes($Response.translations.text))).Replace([char](34),"'")
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The text '$TextToTranslate' is translated from $SourceLanguage to $TargetLanguage"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Result: $Translation"
     }
      Catch
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There is an error while translating the text."
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Error message: $($_.Exception.Message)`n"
      $Translation = "ERROR: $($_.Exception.Message)"
     }

    Return $Translation
   }

  Function TranslateVia-GoogleCloudAPI
   {
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-September-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     TranslateVia-GoogleCloudAPI
    =============================================================================================================================================
    .SYNOPSIS

    This translates text from the source language to the target language, using the Google Cloud API.
    #>

    Param
    (
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $SourceLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TargetLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TextToTranslate,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $AKey
    )

    $ThisFunctionName = $MyInvocation.MyCommand.Name

    Try
     {
      $URL = "https://www.googleapis.com/language/translate/v2?key=$($AKey)&target=$($TargetLanguage)&source=$($SourceLanguage)&q=$($TextToTranslate)"

    # =============================================================================================================================================
    # If a proxy server is used used, add the proxy details to the Invoke-Restmethod.
    # =============================================================================================================================================
          
      If ($ProxyServer) 
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Using $ProxyServer"        
        $result = Invoke-RestMethod $URL -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials -TimeoutSec 10
       }
        else
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No proxy server"        
        $result = Invoke-RestMethod $URL -TimeoutSec 10
       }
      $Translation = $result.data.translations.translatedText
      $Translation = [System.Web.HttpUtility]::HtmlDecode($Translation).Replace([char](34),"'")
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The text '$TextToTranslate' is translated from $SourceLanguage to $TargetLanguage"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Result: $Translation"
     }
      Catch
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There is an error while translating the text."
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Error message: $($_.Exception.Message)`n"
      $Translation = "ERROR: $($_.Exception.Message)"
     }

    Return $Translation
   }

  Function TranslateVia-MicrosoftCognitiveServices
   {
     <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-September-2022
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     TranslateVia-MicrosoftCognitiveServices
    =============================================================================================================================================
    .SYNOPSIS

    This translates text from the source language to the target language, using Microsoft Azure Cognitive Services Translator.
    #>

    Param
    (
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $SourceLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TargetLanguage,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $TextToTranslate,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $AKey,
     [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()][String] $Location
    )

    $ThisFunctionName = $MyInvocation.MyCommand.Name

    $URL              = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&from=$($SourceLanguage)&to=$($TargetLanguage)"
    $Headers          = @{}
    $Headers.Add("Ocp-Apim-Subscription-Key",$Akey)
    $Headers.Add("Ocp-Apim-Subscription-Region",$Location)
    $Headers.Add("Content-Type","application/json")
    $text             = @{'Text' = $($TextToTranslate)} | ConvertTo-Json
    
    Try
     {

    # =============================================================================================================================================
    # If a proxy server is used used, add the proxy details to the Invoke-Restmethod.
    # =============================================================================================================================================
          
      If ($ProxyServer) 
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Using $ProxyServer"
        $result = Invoke-RestMethod -Method POST -Uri $URL -Headers $headers -Body "[$($text)]" -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials -TimeoutSec 10
       }
        else
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No proxy server"        
        $result = Invoke-RestMethod -Method POST -Uri $URL -Headers $headers -Body "[$($text)]" -TimeoutSec 10
       }
      $Translation = $($Result.translations[0].text)
      $Translation = [System.Web.HttpUtility]::HtmlDecode($Translation).Replace([char](34),"'")
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The text '$TextToTranslate' is translated from $SourceLanguage to $TargetLanguage"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Result: $Translation"
     }
      Catch
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There is an error while translating the text."
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Error message: $($_.Exception.Message)`n"
      $Translation = "ERROR: $($_.Exception.Message)"
     }

    Return $Translation
   }

# =============================================================================================================================================
# End function block
# =============================================================================================================================================

# =============================================================================================================================================
# Functions, used in the forms blocks
# =============================================================================================================================================

  Function SelectOrUnselectAllLanguages
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Functionname:     SelectOrUnselectAllLanguages
    =============================================================================================================================================
    .SYNOPSIS

    Selects or unselect all languages (chkLanguages)
        
    #>

    $ThisFunctionName                = $MyInvocation.MyCommand.Name
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Select or unselect all languages."
    For ($Counter = 0; $Counter -lt $chkLanguages.Items.Count; $Counter++)
    {
    If ($chkAllItems.Checked)
     {
      $chkLanguages.SetItemChecked($Counter, $True)
     }
      else
     {
      $chkLanguages.SetItemChecked($Counter, $False)
     }
     }
   }

  Function FilltheCheckedListBoxWithLanguages
   {

    Param
     (
      [String[]] $SupportedLanguages
     )

    $Global:gblTable = @()

    ForEach ($SupportedLanguage in $SupportedLanguages)
    {
      $SupportedLanguage = $SupportedLanguage.Trim().ToLower()
      If ($SupportedLanguage -ne "en")
       {
        $Language = [CultureInfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) | Where {$_.Name -like "$SupportedLanguage*"} | Select-Object -Property Name,DisplayName
        If ($Language)
         {
          $Record  = [ordered] @{"Code"            = "";
                                 "Display Name"    = "";}

          $Record."Code"         = $SupportedLanguage
          $Record."Display Name" = ($Language[0].DisplayName).Split(" ")[0]
          $Global:gblTable      += New-Object PSObject -Property $Record
         }
       }
     }

    $Global:gblTable = $Global:gblTable | Sort-Object "Display Name"
    $chkLanguages.Items.Clear()

    ForEach ($SupportedLanguage in $Global:gblTable)
     {
      [void]$chkLanguages.Items.Add($SupportedLanguage."Display Name")
     }
   }

  Function ValidateDeepL
   {
    
  # =============================================================================================================================================
  # Enable the button 'DeepL' only if the text fields that contain the APIDomain and Authentication Key have been filled in.
  # =============================================================================================================================================
    
    $ThisFunctionName                = $MyInvocation.MyCommand.Name    
    If ($txtAPIDomain.Text -and $txtAuthenticationKey.Text)                 
     {
      $bttnGetDeepLLanguages.Visible = $True
      Add-EntryToLogFile -Entry "The button '$($bttnGetDeepLLanguages.Text)' is visible." -FunctionName $ThisFunctionName 
     } 
      else 
     {
      $bttnGetDeepLLanguages.Visible = $False
      Add-EntryToLogFile -Entry "The button '$($bttnGetDeepLLanguages.Text)' is invisible." -FunctionName $ThisFunctionName
     }
   }

  Function ValidateMicrosoftCognitiveServices
   {
    
  # =============================================================================================================================================
  # Enable the button 'Microsoft Cognitive Services' only if the text fields that contain the subscription key and region have been
  # filled in.
  # =============================================================================================================================================
    
    $ThisFunctionName                = $MyInvocation.MyCommand.Name
    If ($txtAPIMSubscriptionRegion.Text -and $txtAPIMSubscriptionKey.Text)
     {
      $bttnGetMicrosoftLanguages.Visible       = $True
      Add-EntryToLogFile -Entry "The button '$($bttnGetMicrosoftLanguages.Text)' is visible." -FunctionName $ThisFunctionName
     }
      else
     {
      $bttnGetMicrosoftLanguages.Visible       = $False
      Add-EntryToLogFile -Entry "The button '$($bttnGetMicrosoftLanguages.Text)' is invisible." -FunctionName $ThisFunctionName
     }
   }

  Function ValidateGoogleCloud
   {
    
  # =============================================================================================================================================
  # Enable the button 'Google Cloud Translation' only if the text fieldfor the API Key has been filled in.
  # =============================================================================================================================================
  
    $ThisFunctionName                = $MyInvocation.MyCommand.Name
    If ($txtGoogleAPIKey.Text)
     {
      $bttnGetGoogleLanguages.Visible      = $True
      Add-EntryToLogFile -Entry "The button '$($bttnGetGoogleLanguages.Text)' is visible." -FunctionName $ThisFunctionName
     }
      else
     {
      $bttnGetGoogleLanguages.Visible      = $False
      Add-EntryToLogFile -Entry "The button '$($bttnGetGoogleLanguages.Text)' is invisible." -FunctionName $ThisFunctionName
     }
   }

  Function CheckboxIsChanged
   {

  # =============================================================================================================================================
  # Enable translate button only if there are languages selected and there is an language input file.
  # =============================================================================================================================================
    
    $ThisFunctionName                = $MyInvocation.MyCommand.Name
    Add-EntryToLogFile -Entry "There are $(($chkLanguages.CheckedItems).Count) languages selected." -FunctionName $ThisFunctionName
    If ((($chkLanguages.CheckedItems).Count -gt 0) -and ($txtJSONFileWithEnglishText.Text))
     {$bttnTranslate.Enabled = $True
     Add-EntryToLogFile -Entry "The button '$($bttnTranslate.Text)' is enabled." -FunctionName $ThisFunctionName}
      else
     {$bttnTranslate.Enabled = $False
     Add-EntryToLogFile -Entry "The button '$($bttnTranslate.Text)' is disabled." -FunctionName $ThisFunctionName}
   }

  Function JSONFileWithEnglishTextHasBeenSelected
   {

  # =============================================================================================================================================
  # Specify the JSON file that contains the translations.
  # =============================================================================================================================================

    $txtJSONFileWithEnglishText.Enabled       = $True
    CheckboxIsChanged
   }

  Function GetAndTestJSONFile
   {

  # =============================================================================================================================================
  # Select the 'base' JSON file, the file that contains the English text.
  # =============================================================================================================================================

    $ThisFunctionName          = $MyInvocation.MyCommand.Name
    $tmpText = Get-FileFromButton -InitialDirectory $strCurrentDir -Filter "All JSON Files (*.json) | *.json"
    If ($tmpText)
     {
      Try
       {
        $tmpJSONObject = Get-Content -Path $tmpText -Encoding UTF8 | ConvertFrom-Json
        If ($tmpJSONObject.en)
         {
          $txtJSONFileWithEnglishText.Text = $tmpText
          $txtJSONFileWithEnglishText.SelectionStart = $txtJSONFileWithEnglishText.Text.Length
          $txtJSONFileWithEnglishText.ScrollToCaret()
         } 
          else
         {
          $ErrorText = "There is no 'en' entry in the JSON file '$tmpText'. So we cannot process this one."
          Add-EntryToLogFile -Entry $ErrorText -FunctionName $ThisFunctionName
          Display-MessageBox -Title "Error" -Text $ErrorText -GUI -Error -Button Ok
         }
       }
        Catch
       {
        $ErrorMessage  = "There is an error in the JSON file near:`n`n"
        $ErrorText     = $($_.Exception.Message)
        [int]$Number   = ($ErrorText -split '\((.*?)\)')[1]
        $number = $Number -30
        If ($Number -le 1){$Number = 1}
        $ErrorMessage += $ErrorText.SubString($Number)
        $ErrorMessage += "`n`nSelect another JSON file or correct the error in the file '$tmpText'." 
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $ErrorMessage
        $Null = Display-MessageBox -Text $ErrorMessage -GUI -Button Ok -Error -Title "Testing JSON File"
       }
     }
   }

  Function ReadAndProcessJSONFile
   {

  # =============================================================================================================================================
  # Process the given JSON File.
  # =============================================================================================================================================
    
    Param
     (
      [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()][String]     $InputFile,
      [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()][String[]]   $LanguagesToProcess,
      [Parameter(Mandatory = $False)]                          [Switch]     $Silent,
      [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()][ValidateSet("Deepl","GoogleFREEAPI","GoogleCloudServices","MicrosoftCognitive")][String] $TranslationService
     )
    
    $ThisFunctionName          = $MyInvocation.MyCommand.Name
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Reading the file '$InputFile'."
    $JSONObject                = Get-Content -Path $InputFile -Encoding UTF8 -Raw | ConvertFrom-Json

  # =============================================================================================================================================
  # Specify the output file
  # =============================================================================================================================================

    $Mapping = @{
    "Deepl"                 = "Deepl"
    "GoogleFREEAPI"         = "Google FREE API"
    "GoogleCloudServices"   = "Google Cloud Services"
    "MicrosoftCognitive"    = "Microsoft Azure Cognitive Services Translator"}

    $TranslationserviceFullName = $Mapping.Item($TranslationService)

    $tmpText                                  = $InputFile.Replace(".json","")
    $strLastPartOfFileName                    = " ($TranslationserviceFullName) ($((Get-Date -format "yyyy-MM-dd HH-mm-ss").ToString()))"
    $OutputFile                               = $tmpText + $strLastPartOfFileName + ".json"
    If (-not($Silent))
     {
      $txtJSONFileWithTheTranslations.Text            = $OutputFile
      $txtJSONFileWithTheTranslations.Enabled         = $True
      $txtJSONFileWithTheTranslations.Refresh()
      $txtJSONFileWithTheTranslations.SelectionStart  = $txtJSONFileWithTheTranslations.Text.Length
      $txtJSONFileWithTheTranslations.ScrollToCaret()
     }

  # =============================================================================================================================================
  # Grab all the headers. These might be different for each JSON file. So that has to dynamic.
  # =============================================================================================================================================
    
    $Headers                   = ($JSONObject.en | Get-Member -type NoteProperty).Name
    
  # =============================================================================================================================================
  # And a new JSON object for the translations, including the English one.
  # =============================================================================================================================================
        
    $NewJSONObject             = [ordered] @{}
    
  # =============================================================================================================================================
  # First, add the English headers to the new JSON File. That is our starting point.
  # =============================================================================================================================================

    $Array = [ordered] @{}
    ForEach ($Header in $Headers)
     {
      $Array += @{$Header=$JSONObject.en.$Header}
     }
    
    $NewJSONObject.Add("en",$Array)

  # =============================================================================================================================================
  # The second step is reading variables based on the Translation Service.
  # =============================================================================================================================================

    If ($TranslationService -eq "Deepl")
     {
      $AuthenticationKey = $txtAuthenticationKey.text
      If (-not($AuthenticationKey)) {$AuthenticationKey = Read-Registry -RegistryKey $RegistryKey -ValueName "AuthenticationKey"}
      $APIDomain         = $txtAPIDomain.Text
      If (-not($APIDomain))         {$APIDomain         = Read-Registry -RegistryKey $RegistryKey -ValueName "APIDomain"}
     }
        
    If ($TranslationService -eq "GoogleCloudServices")
     {
      $GoogleAPIKey = $txtGoogleAPIKey.text
      If (-not($GoogleAPIKey)) {$GoogleAPIKey = Read-Registry -RegistryKey $RegistryKey -ValueName "GoogleAPIKey"}
     }

    If ($TranslationService -eq "MicrosoftCognitive")
     {
      $APIMSubscriptionKey = $txtAPIMSubscriptionKey.text
      If (-not($APIMSubscriptionKey))    {$APIMSubscriptionKey    = Read-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionKey"}
      $APIMSubscriptionRegion = $txtAPIMSubscriptionRegion.text
      If (-not($APIMSubscriptionRegion)) {$APIMSubscriptionRegion = Read-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionRegion"}
     }

  # =============================================================================================================================================
  # And now, add all the other selected languages. 
  # =============================================================================================================================================

    $LanguageCounter    = 1
    $LanguagesToProcess = $LanguagesToProcess | Sort-Object

    ForEach ($Language in $LanguagesToProcess)
     {
      $Language = $Language.Trim()
      If (-not($NewJSONObject.$Language))
       {
        $LanguageDisplayName = ($Global:gblTable | Where {$_."Code" -eq $Language} | Select-Object -Property "Display name")."Display Name"
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Processing $LanguageDisplayName language."
        $Percentage          = [Math]::Round(($LanguageCounter / $($LanguagesToProcess.Count) * 100),0)
        $StatusText          = "Processing the $LanguageDisplayName language using\b$($Mapping.Item($TranslationService))."
        If ($Silent)
         {
          Write-Progress -Id 1 -Activity "Processing all the languages." -Status $StatusText.Replace("\b"," ")  -PercentComplete $Percentage
         }
          else
         {
          $prgProgressbar.Visible     = $True
          $prgProgressbar.TextOverlay = $StatusText.Replace("\b","`n")
          $prgProgressbar.Value       = $Percentage
          $prgProgressbar.Update()
          $prgProgressbar.Step = (1/$($LanguagesToProcess.Count) * 100)
          $bttnTranslate.Enabled      = $False
          $bttnExit.Enabled           = $False
         }
      
        $Array = [ordered] @{}
        ForEach ($Header in $Headers)
         {
          $TextToTranslate = $($JSONObject.en.$Header).Replace("&","")
          $OldText         = $TextToTranslate
          $Replacement     = @()
          $Original        = @()
          $Counter         = 0
        
          While ($TextToTranslate.Contains("<") -and $TextToTranslate.Contains(">"))
           {
            $RandomNumber         = Get-Random -Minimum 11111111 -Maximum 99999999
            $Replacement         += $RandomNumber
            $Begin                = $TextToTranslate.IndexOf("<") 
            $End                  = $TextToTranslate.IndexOf(">")
            $Variable             = $TextToTranslate.SubString($Begin,$End-$Begin+1)
            $Original            += $Variable
            $TextToTranslate      = $TextToTranslate.Replace($Variable,$($Replacement[$Counter]))
            $Counter++
           }

          If ($TranslationService -eq "GoogleFREEAPI")
           {
            $TranslatedText = (TranslateVia-GoogleFREEApi -SourceLanguage "en" -TargetLanguage $Language -TextToTranslate $TextToTranslate)
           }
        
          If ($TranslationService -eq "Deepl")
           {
            $TranslatedText = (TranslateVia-Deepl -SourceLanguage "en" -TargetLanguage $Language -AKey $AuthenticationKey -Domain $APIDomain -TextToTranslate $TextToTranslate)
           }
        
          If ($TranslationService -eq "GoogleCloudServices")
           {
            $TranslatedText = (TranslateVia-GoogleCloudAPI -SourceLanguage "en" -TargetLanguage $Language -AKey $GoogleAPIKey -TextToTranslate $TextToTranslate)
           }

          If ($TranslationService -eq "MicrosoftCognitive")
           {
            $TranslatedText = (TranslateVia-MicrosoftCognitiveServices -SourceLanguage "en" -TargetLanguage $Language -AKey $APIMSubscriptionKey -Location $APIMSubscriptionRegion -TextToTranslate $TextToTranslate)
           }
        
          If ($Counter -gt 0)
           {
            For ($a = 0;$a -lt $Counter;$a++)
             {
              $TranslatedText = $TranslatedText.Replace($Replacement[$a],$Original[$a])
             }
           }

          $Array += @{$Header=$TranslatedText}
         }
            
        $NewJSONObject.Add($Language,$Array)
        $LanguageCounter++
       }
        else
       {
      # =============================================================================================================================================
      # The language was already present in the translation. Thus skipping this one. But we have to increase the counter with one.
      # =============================================================================================================================================
          
        $LanguageCounter++
       }
     }

    $NewJSONObject | ConvertTo-Json | % { [System.Text.RegularExpressions.Regex]::Unescape($_) } | Out-File -FilePath $OutputFile -Encoding utf8

    $Message = "The JSON file '$OutputFile' has been created."

  # =============================================================================================================================================
  # Check the created JSON File.
  # =============================================================================================================================================

    Try
     {
      
    # =============================================================================================================================================
    # If the created JSON file can be read, then there is no problem. 
    # =============================================================================================================================================
          
      $Null = Get-Content -Path $OutputFile -Encoding UTF8 | ConvertFrom-Json
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The JSON file '$OutputFile' seems to be fine. No further checks needed."
     }
      Catch
     {
      
    # =============================================================================================================================================
    # The created JSON file cannot be read. Now, create a tmp JSON file for each language and check if that one can be read successfully. 
    # =============================================================================================================================================
      
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "There seems to be an issue with the JSON file '$OutputFile'. Let's check each language."

      ForEach ($Language in $LanguagesToProcess)
       {
        $JSONText = $NewJSONObject.$Language
        $TempFile = New-TemporaryFile
        $JSONText | ConvertTo-Json | % { [System.Text.RegularExpressions.Regex]::Unescape($_) } | Out-File -FilePath $TempFile -Encoding utf8
        Try
         {
          $Null = Get-Content -Path $TempFile -Encoding UTF8 | ConvertFrom-Json
          Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The language $Language has no issues."
         }
          Catch
         {
          $ErrorText = "There are issues with the language $Language. Check the created JSON file manually!"
          $Message += "`n$ErrorText"
          Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $ErrorText
         }
        Remove-Item -Path $TempFile -Force
       }
     }
    
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $Message

    $Null = Display-MessageBox -Text $Message -GUI:(-not($Silent)) -Button Ok -Title "Creating JSON File."

    If ($Silent)
     {
      Write-Progress -Id 1 -Completed -Activity " "
     }
      else
     {
      $prgProgressbar.Visible     = $False
      $bttnTranslate.Enabled      = $True
      $bttnExit.Enabled           = $True

     }
   }

  Function bttnTranslate
   {
 
  # =============================================================================================================================================
  # The button bttnTranslate contains the name of the translation service that should be used: for example 
  # 'Start the translation with Google FREE API.'. The last word is 'API'. So, in this case the GoogleFREEAPI is used as the
  # translation service.
  # =============================================================================================================================================   
    
    $ThisFunctionName          = $MyInvocation.MyCommand.Name
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The translation button '$($bttnTranslate.Text)' has been clicked."
    $SelectedLanguages = @()
    $TranslationService = $($bttnTranslate.Text).Split(" ")[-1].Replace(".","")
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The translation service '$TranslationService' is used."
    ForEach ($CheckedItem in ($chkLanguages.CheckedItems))
     {
      $SelectedLanguages += $Global:gblTable | Where {$_."Display Name" -eq $CheckedItem} | Select-Object -Property Code
     }

    If ($TranslationService -eq "Translator")  {ReadAndProcessJSONFile -InputFile $($txtJSONFileWithEnglishText.Text) -LanguagesToProcess $SelectedLanguages.Code -TranslationService MicrosoftCognitive}
    If ($TranslationService -eq "Services")    {ReadAndProcessJSONFile -InputFile $($txtJSONFileWithEnglishText.Text) -LanguagesToProcess $SelectedLanguages.Code -TranslationService GoogleCloudServices}
    If ($TranslationService -eq "API")         {ReadAndProcessJSONFile -InputFile $($txtJSONFileWithEnglishText.Text) -LanguagesToProcess $SelectedLanguages.Code -TranslationService GoogleFREEAPI}
    If ($TranslationService -eq "DeepL")       {ReadAndProcessJSONFile -InputFile $($txtJSONFileWithEnglishText.Text) -LanguagesToProcess $SelectedLanguages.Code -TranslationService Deepl}
   }

  Function GetDeepLLanguages
   {
    $ThisFunctionName          = $MyInvocation.MyCommand.Name
    $URL                       = "https://api-free.deepl.com/v2/languages?type=target"
    Add-EntryToLogFile "Reading the languages from $URL" -FunctionName $ThisFunctionName
    $chkLanguages.Items.Clear()
    $headers   = @{Authorization = "DeepL-Auth-Key $($txtAuthenticationKey.text)"}
    Try
     {
      If ($ProxyServer)
       {
        $Response    = @((Invoke-RestMethod -Uri $URL -Method Post -Headers $headers -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials).Language)
       }
        else
       {
        $Response    = @((Invoke-RestMethod -Uri $URL -Method Post -Headers $headers).Language)
       }
      $Response    = $Response | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique
      FilltheCheckedListBoxWithLanguages -SupportedLanguages $Response
      $chkAllItems.Checked = $False
      $bttnTranslate.Text  = $TranslationButtonBaseText + " DeepL."
     }
      Catch
     {
      $ErrorText = "There is an error reading the DeepL Translation files: $($_.Exception.Message)`nCheck your key."
      Add-EntryToLogFile -Entry $ErrorText -FunctionName $ThisFunctionName
      $Null = Display-MessageBox -Text $ErrorText -Title "Error" -Error -GUI
     
     }
   }

  Function GetGoogleFREEAPILanguagesForWebsite
   {
    $ThisFunctionName          = $MyInvocation.MyCommand.Name
    $URL                       = "https://cloud.google.com/translate/docs/languages"
    Add-EntryToLogFile "Reading the languages from $URL" -FunctionName $ThisFunctionName
    $chkLanguages.Items.Clear()
    $SL     = @()
    If ($ProxyServer)
     {
      $Content                = Invoke-RestMethod -Uri $URL  -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials
     }
      else
     {
      $Content                = Invoke-RestMethod -Uri $URL
     }

    $LookFor      = "<code translate=""no"" dir=""ltr"">.*</code>"
    $Response     = @(($Content | Select-String -AllMatches $LookFor | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Value).Replace("<code translate=""no"" dir=""ltr"">","").Replace("</code>",""))
    $Response     = $Response | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique

    Return $Response
   }

  Function GetGoogleFREEAPILanguages
   {
    $frmProcessing                         = New-Object 'System.Windows.Forms.Form'
    $txtMessage                            = New-Object 'System.Windows.Forms.TextBox'
    $frmProcessing.Controls.Add($txtMessage)
    $frmProcessing.AutoScaleDimensions     = New-Object System.Drawing.SizeF(6, 13)
    $frmProcessing.AutoScaleMode           = 'Font'
    $frmProcessing.BackColor               = [System.Drawing.SystemColors]::ControlDark 
    $frmProcessing.ClientSize              = New-Object System.Drawing.Size(430, 44)
    $frmProcessing.FormBorderStyle         = 'None'
    $frmProcessing.Name                    = 'frmProcessing'
    $frmProcessing.ShowIcon                = $False
    $frmProcessing.StartPosition           = 'CenterParent'
    $frmProcessing.Visible                 = $False
    $frmProcessing.add_Shown({$Lang = GetGoogleFREEAPILanguagesForWebsite 
                              FilltheCheckedListBoxWithLanguages -SupportedLanguages $Lang
                              $bttnTranslate.Text                     = $TranslationButtonBaseText + " Google FREE API."
                              [void]$frmProcessing.Close()
                              [void]$frmProcessing.Dispose()
                              })
    #
    # txtMessage
    #
    $txtMessage.ReadOnly                   = $True
    $txtMessage.Location                   = New-Object System.Drawing.Point(12, 12)
    $txtMessage.MaxLength                  = 50
    $txtMessage.Name                       = 'txtMessage'
    $txtMessage.Size                       = New-Object System.Drawing.Size(400, 20)
    $txtMessage.TabIndex                   = 0
    $txtMessage.TextAlign                  = 'Center'
    $txtMessage.WordWrap                   = $False
    $txtMessage.Text                       = "Please wait...."
    
    [void]$frmProcessing.ShowDialog()
    
   }

  Function GetGoogleLanguages
   {
    $ThisFunctionName    = $MyInvocation.MyCommand.Name
    $URL                 = "https://translation.googleapis.com/language/translate/v2/languages?key=$($txtGoogleAPIKey.text)"
    $chkLanguages.Items.Clear()
    Add-EntryToLogFile "Reading the languages from $URL" -FunctionName $ThisFunctionName
    Try
     {
      If ($ProxyServer)
       {
        $result              = Invoke-WebRequest -Uri $URL -Method Get -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials
       }
        else
       {
        $result              = Invoke-WebRequest -Uri $URL -Method Get
       }

      $Content             = $result | Select-Object -ExpandProperty Content
      $Response            = ((ConvertFrom-Json -InputObject $Content).data.languages).language
      $Response   = $Response | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique

      FilltheCheckedListBoxWithLanguages -SupportedLanguages $Response
      $chkAllItems.Checked                 = $False
      $bttnTranslate.Text                  = $TranslationButtonBaseText + " Google Cloud Services."
     }
      Catch
     {
      $ErrorText = "There is an error reading the Google Cloud Translation files: $($_.Exception.Message)`nCheck your key."
      Add-EntryToLogFile -Entry $ErrorText -FunctionName $ThisFunctionName
      $Null = Display-MessageBox -Text $ErrorText -Title "Error" -Error -GUI
     }
   }
  
  Function GetMicrosoftLanguages
   {
    $ThisFunctionName    = $MyInvocation.MyCommand.Name
    $URL                 = "https://api.cognitive.microsofttranslator.com/languages?api-version=3.0"
    Add-EntryToLogFile "Reading the languages from $URL" -FunctionName $ThisFunctionName
    $chkLanguages.Items.Clear()
    Try
     {
      If ($ProxyServer)
       {
        $conversionResult    = Invoke-RestMethod -Method Get -Uri $URL -Proxy $ProxyServer -ProxyUseDefaultCredentials -UseDefaultCredentials
       }
        else
       {
        $conversionResult    = Invoke-RestMethod -Method Get -Uri $URL
       }

      $Response            = ($conversionResult.translation | Get-Member -type NoteProperty).Name | ForEach {($_ ).Split("-")[0]} | Sort-Object | Select-Object -Unique
      FilltheCheckedListBoxWithLanguages -SupportedLanguages $Response
      $chkAllItems.Checked                      = $False
      $bttnTranslate.Text                       = $TranslationButtonBaseText + " Microsoft Azure Cognitive Services Translator."
     }
      Catch
     {
      $ErrorText = "There is an error reading the Microsoft Languages: $($_.Exception.Message)."
      Add-EntryToLogFile -Entry $ErrorText -FunctionName $ThisFunctionName
      $Null = Display-MessageBox -Text $ErrorText -Title "Error" -Error -GUI
     }
   }

# =============================================================================================================================================
# End Functions, used in the forms blocks
# =============================================================================================================================================

# =============================================================================================================================================
# Form block AddTranslationsToAJSONFile
# =============================================================================================================================================

  Function AddTranslationsToAJSONFile
   { 
    
    $ThisFunctionName = $MyInvocation.MyCommand.Name

    $frmAddTranslationsToJSON                                   = New-Object 'System.Windows.Forms.Form'
    $prgProgressbar                                             = New-Object 'SAPIENTypes.ProgressBarOverlay'
    $bttnTranslate                                              = New-Object 'System.Windows.Forms.Button'
    $bttnExit                                                   = New-Object 'System.Windows.Forms.Button'
    $grpLanguages                                               = New-Object 'System.Windows.Forms.GroupBox'
    $chkAllItems                                                = New-Object 'System.Windows.Forms.CheckBox'
    $chkLanguages                                               = New-Object 'System.Windows.Forms.CheckedListBox'
    $txtJSONFileWithTheTranslations                             = New-Object 'System.Windows.Forms.TextBox'
    $lblJSONFileWithTheTranslations                             = New-Object 'System.Windows.Forms.Label'
    $bttnBrowse                                                 = New-Object 'System.Windows.Forms.Button'
    $txtJSONFileWithEnglishText                                 = New-Object 'System.Windows.Forms.TextBox'
    $lblJSONFileWithEnglishText                                 = New-Object 'System.Windows.Forms.Label'
    $grpTranslationServices                                     = New-Object 'System.Windows.Forms.GroupBox'
    $grpGoogleFreeAPI                                           = New-Object 'System.Windows.Forms.GroupBox'
    $bttnGetGoogleFREEAPILanguages                              = New-Object 'System.Windows.Forms.Button'
    $grpMicrosoftCognitiveTranslator                            = New-Object 'System.Windows.Forms.GroupBox'
    $bttnGetMicrosoftLanguages                                  = New-Object 'System.Windows.Forms.Button'
    $txtAPIMSubscriptionRegion                                  = New-Object 'System.Windows.Forms.TextBox'
    $lblAPIMSubscriptionRegion                                  = New-Object 'System.Windows.Forms.Label'
    $txtAPIMSubscriptionKey                                     = New-Object 'System.Windows.Forms.TextBox'
    $lblAPIMSubscriptionKey                                     = New-Object 'System.Windows.Forms.Label'
    $grpGoogleCloudTranslation                                  = New-Object 'System.Windows.Forms.GroupBox'
    $bttnGetGoogleLanguages                                     = New-Object 'System.Windows.Forms.Button'
    $txtGoogleAPIKey                                            = New-Object 'System.Windows.Forms.TextBox'
    $lblGoogleAPIKey                                            = New-Object 'System.Windows.Forms.Label'
    $grpDeepL                                                   = New-Object 'System.Windows.Forms.GroupBox'
    $bttnGetDeepLLanguages                                      = New-Object 'System.Windows.Forms.Button'
    $txtAPIDomain                                               = New-Object 'System.Windows.Forms.TextBox'
    $lblAPIDomain                                               = New-Object 'System.Windows.Forms.Label'
    $txtAuthenticationKey                                       = New-Object 'System.Windows.Forms.TextBox'
    $lblAuthenticationKey                                       = New-Object 'System.Windows.Forms.Label'

    #
    # frmAddTranslationsToJSON
    #
    $frmAddTranslationsToJSON.Controls.Add($prgProgressbar)
    $frmAddTranslationsToJSON.Controls.Add($bttnTranslate)
    $frmAddTranslationsToJSON.Controls.Add($bttnExit)
    $frmAddTranslationsToJSON.Controls.Add($grpTranslationService)
    $frmAddTranslationsToJSON.Controls.Add($grpLanguages)
    $frmAddTranslationsToJSON.Controls.Add($txtJSONFileWithTheTranslations)
    $frmAddTranslationsToJSON.Controls.Add($lblJSONFileWithTheTranslations)
    $frmAddTranslationsToJSON.Controls.Add($bttnBrowse)
    $frmAddTranslationsToJSON.Controls.Add($txtJSONFileWithEnglishText)
    $frmAddTranslationsToJSON.Controls.Add($lblJSONFileWithEnglishText)
    $frmAddTranslationsToJSON.Controls.Add($grpTranslationServices)
    $frmAddTranslationsToJSON.AutoScaleDimensions               = New-Object System.Drawing.SizeF(6, 13)
    $frmAddTranslationsToJSON.AutoScaleMode                     = 'Font'
    $frmAddTranslationsToJSON.ClientSize                        = New-Object System.Drawing.Size(798, 636)
    $frmAddTranslationsToJSON.FormBorderStyle                   = 'Fixed3D'
    $frmAddTranslationsToJSON.Name                              = 'frmAddTranslationsToJSON'
    $frmAddTranslationsToJSON.StartPosition                     = 'CenterParent'
    #
    # prgProgressbar
    #
    $prgProgressbar.Location                                    = New-Object System.Drawing.Point(365, 488)
    $prgProgressbar.Name                                        = 'prgProgressbar'
    $prgProgressbar.Size                                        = New-Object System.Drawing.Size(415, 75)
    $prgProgressbar.Style                                       = 'Continuous'
    $prgProgressbar.TabIndex                                    = 11
    #
    # bttnTranslate
    #
    $bttnTranslate.Location                                     = New-Object System.Drawing.Point(365, 576)
    $bttnTranslate.Name                                         = 'bttnTranslate'
    $bttnTranslate.Size                                         = New-Object System.Drawing.Size(200, 50)
    $bttnTranslate.TabIndex                                     = 10
    $bttnTranslate.Text                                         = '<< To be modified >>'
    $bttnTranslate.UseVisualStyleBackColor                      = $True
    #
    # bttnExit
    #
    $bttnExit.Location                                          = New-Object System.Drawing.Point(580, 576)
    $bttnExit.Name                                              = 'bttnExit'
    $bttnExit.Size                                              = New-Object System.Drawing.Size(200, 50)
    $bttnExit.TabIndex                                          = 9
    $bttnExit.Text                                              = 'Exit'
    $bttnExit.UseVisualStyleBackColor                           = $True
    #
    # grpLanguages
    #
    $grpLanguages.Controls.Add($chkAllItems)
    $grpLanguages.Controls.Add($chkLanguages)
    $grpLanguages.BackColor                                     = [System.Drawing.Color]::FromArgb(255, 224, 224, 224)
    $grpLanguages.Location                                      = New-Object System.Drawing.Point(577, 12)
    $grpLanguages.Name                                          = 'grpLanguages'
    $grpLanguages.Size                                          = New-Object System.Drawing.Size(209, 408)
    $grpLanguages.TabIndex                                      = 7
    $grpLanguages.TabStop                                       = $False
    $grpLanguages.Text                                          = 'Select the languages'
    #
    # chkAllItems
    #
    $chkAllItems.Location                                       = New-Object System.Drawing.Point(6, 19)
    $chkAllItems.Name                                           = 'chkAllItems'
    $chkAllItems.Size                                           = New-Object System.Drawing.Size(197, 24)
    $chkAllItems.TabIndex                                       = 7
    $chkAllItems.Text                                           = 'Select or unselect all languages'
    $chkAllItems.UseVisualStyleBackColor                        = $True
    #
    # chkLanguages
    #
    $chkLanguages.CheckOnClick                                  = $True
    $chkLanguages.FormattingEnabled                             = $True
    $chkLanguages.Location                                      = New-Object System.Drawing.Point(6, 49)
    $chkLanguages.Name                                          = 'chkLanguages'
    $chkLanguages.Size                                          = New-Object System.Drawing.Size(197, 349)
    $chkLanguages.TabIndex                                      = 6
    #
    # txtJSONFileWithTheTranslations
    #
    $txtJSONFileWithTheTranslations.Location                    = New-Object System.Drawing.Point(174, 452)
    $txtJSONFileWithTheTranslations.Name                        = 'txtJSONFileWithTheTranslations'
    $txtJSONFileWithTheTranslations.Size                        = New-Object System.Drawing.Size(527, 20)
    $txtJSONFileWithTheTranslations.TabIndex                    = 5
    #
    # lblJSONFileWithTheTranslations
    #
    $lblJSONFileWithTheTranslations.AutoSize                    = $True
    $lblJSONFileWithTheTranslations.Location                    = New-Object System.Drawing.Point(14, 452)
    $lblJSONFileWithTheTranslations.Name                        = 'lblJSONFileWithTheTranslations'
    $lblJSONFileWithTheTranslations.Size                        = New-Object System.Drawing.Size(153, 13)
    $lblJSONFileWithTheTranslations.TabIndex                    = 4
    $lblJSONFileWithTheTranslations.Text                        = 'JSON File with the translations:'
    #
    # bttnBrowse
    #
    $bttnBrowse.Location                                        = New-Object System.Drawing.Point(705, 424)
    $bttnBrowse.Name                                            = 'bttnBrowse'
    $bttnBrowse.Size                                            = New-Object System.Drawing.Size(75, 23)
    $bttnBrowse.TabIndex                                        = 3
    $bttnBrowse.Text                                            = 'Browse'
    $bttnBrowse.UseVisualStyleBackColor                         = $True
    #
    # txtJSONFileWithEnglishText
    #
    $txtJSONFileWithEnglishText.Location                        = New-Object System.Drawing.Point(174, 426)
    $txtJSONFileWithEnglishText.Name                            = 'txtJSONFileWithEnglishText'
    $txtJSONFileWithEnglishText.Size                            = New-Object System.Drawing.Size(525, 20)
    $txtJSONFileWithEnglishText.TabIndex                        = 2
    #
    # lblJSONFileWithEnglishText
    #
    $lblJSONFileWithEnglishText.AutoSize                        = $True
    $lblJSONFileWithEnglishText.Location                        = New-Object System.Drawing.Point(12, 429)
    $lblJSONFileWithEnglishText.Name                            = 'lblJSONFileWithEnglishText'
    $lblJSONFileWithEnglishText.Size                            = New-Object System.Drawing.Size(136, 13)
    $lblJSONFileWithEnglishText.TabIndex                        = 1
    $lblJSONFileWithEnglishText.Text                            = 'JSON File with English text:'
    #
    # grpTranslationServices
    #
    $grpTranslationServices.Controls.Add($grpGoogleFreeAPI)
    $grpTranslationServices.Controls.Add($grpMicrosoftCognitiveTranslator)
    $grpTranslationServices.Controls.Add($grpGoogleCloudTranslation)
    $grpTranslationServices.Controls.Add($grpDeepL)
    $grpTranslationServices.BackColor                           = [System.Drawing.Color]::FromArgb(255, 224, 224, 224)
    $grpTranslationServices.Location                            = New-Object System.Drawing.Point(12, 12)
    $grpTranslationServices.Name                                = 'grpTranslationServices'
    $grpTranslationServices.Size                                = New-Object System.Drawing.Size(443, 408)
    $grpTranslationServices.TabIndex                            = 0
    $grpTranslationServices.TabStop                             = $False
    $grpTranslationServices.Text                                = 'Translation services'
    #
    # grpGoogleFreeAPI
    #
    $grpGoogleFreeAPI.Controls.Add($bttnGetGoogleFREEAPILanguages)
    $grpGoogleFreeAPI.BackColor                                 = [System.Drawing.Color]::Silver 
    $grpGoogleFreeAPI.Location                                  = New-Object System.Drawing.Point(6, 141)
    $grpGoogleFreeAPI.Name                                      = 'grpGoogleFreeAPI'
    $grpGoogleFreeAPI.Size                                      = New-Object System.Drawing.Size(429, 59)
    $grpGoogleFreeAPI.TabIndex                                  = 3
    $grpGoogleFreeAPI.TabStop                                   = $False
    $grpGoogleFreeAPI.Text                                      = 'Google FREE API'
    #
    # bttnGetGoogleFREEAPILanguages
    #
    $bttnGetGoogleFREEAPILanguages.Location                     = New-Object System.Drawing.Point(9, 20)
    $bttnGetGoogleFREEAPILanguages.Name                         = 'bttnGetGoogleFREEAPILanguages'
    $bttnGetGoogleFREEAPILanguages.Size                         = New-Object System.Drawing.Size(414, 25)
    $bttnGetGoogleFREEAPILanguages.TabIndex                     = 0
    $bttnGetGoogleFREEAPILanguages.Text                         = 'Get all languages supported by Google FREE API'
    $bttnGetGoogleFREEAPILanguages.UseVisualStyleBackColor      = $True
    #
    # grpMicrosoftCognitiveTranslator
    #
    $grpMicrosoftCognitiveTranslator.Controls.Add($bttnGetMicrosoftLanguages)
    $grpMicrosoftCognitiveTranslator.Controls.Add($txtAPIMSubscriptionRegion)
    $grpMicrosoftCognitiveTranslator.Controls.Add($lblAPIMSubscriptionRegion)
    $grpMicrosoftCognitiveTranslator.Controls.Add($txtAPIMSubscriptionKey)
    $grpMicrosoftCognitiveTranslator.Controls.Add($lblAPIMSubscriptionKey)
    $grpMicrosoftCognitiveTranslator.BackColor                  = [System.Drawing.Color]::Silver 
    $grpMicrosoftCognitiveTranslator.Location                   = New-Object System.Drawing.Point(6, 295)
    $grpMicrosoftCognitiveTranslator.Name                       = 'grpMicrosoftCognitiveTranslator'
    $grpMicrosoftCognitiveTranslator.Size                       = New-Object System.Drawing.Size(429, 106)
    $grpMicrosoftCognitiveTranslator.TabIndex                   = 2
    $grpMicrosoftCognitiveTranslator.TabStop                    = $False
    $grpMicrosoftCognitiveTranslator.Text                       = 'Microsoft Azure Cognitive Services Translator'
    #
    # bttnGetMicrosoftLanguages
    #
    $bttnGetMicrosoftLanguages.Location                         = New-Object System.Drawing.Point(4, 74)
    $bttnGetMicrosoftLanguages.Name                             = 'bttnGetMicrosoftLanguages'
    $bttnGetMicrosoftLanguages.Size                             = New-Object System.Drawing.Size(419, 25)
    $bttnGetMicrosoftLanguages.TabIndex                         = 4
    $bttnGetMicrosoftLanguages.Text                             = 'Get all languages supported by Microsoft Azure Cognitive Services Translator'
    $bttnGetMicrosoftLanguages.UseVisualStyleBackColor          = $True
    #
    # txtAPIMSubscriptionRegion
    #
    $txtAPIMSubscriptionRegion.Location                         = New-Object System.Drawing.Point(152, 46)
    $txtAPIMSubscriptionRegion.Name                             = 'txtAPIMSubscriptionRegion'
    $txtAPIMSubscriptionRegion.Size                             = New-Object System.Drawing.Size(271, 20)
    $txtAPIMSubscriptionRegion.TabIndex                         = 3
    #
    # lblAPIMSubscriptionRegion
    #
    $lblAPIMSubscriptionRegion.AutoSize                         = $True
    $lblAPIMSubscriptionRegion.Location                         = New-Object System.Drawing.Point(7, 49)
    $lblAPIMSubscriptionRegion.Name                             = 'lblAPIMSubscriptionRegion'
    $lblAPIMSubscriptionRegion.Size                             = New-Object System.Drawing.Size(126, 13)
    $lblAPIMSubscriptionRegion.TabIndex                         = 2
    $lblAPIMSubscriptionRegion.Text                             = 'APIM Subscription region'
    #
    # txtAPIMSubscriptionKey
    #
    $txtAPIMSubscriptionKey.Location                            = New-Object System.Drawing.Point(152, 20)
    $txtAPIMSubscriptionKey.Name                                = 'txtAPIMSubscriptionKey'
    $txtAPIMSubscriptionKey.Size                                = New-Object System.Drawing.Size(271, 20)
    $txtAPIMSubscriptionKey.TabIndex                            = 1
    #
    # lblAPIMSubscriptionKey
    #
    $lblAPIMSubscriptionKey.AutoSize                            = $True
    $lblAPIMSubscriptionKey.Location                            = New-Object System.Drawing.Point(7, 23)
    $lblAPIMSubscriptionKey.Name                                = 'lblAPIMSubscriptionKey'
    $lblAPIMSubscriptionKey.Size                                = New-Object System.Drawing.Size(117, 13)
    $lblAPIMSubscriptionKey.TabIndex                            = 0
    $lblAPIMSubscriptionKey.Text                                = 'APIM Subscription key:'
    #
    # grpGoogleCloudTranslation
    #
    $grpGoogleCloudTranslation.Controls.Add($bttnGetGoogleLanguages)
    $grpGoogleCloudTranslation.Controls.Add($txtGoogleAPIKey)
    $grpGoogleCloudTranslation.Controls.Add($lblGoogleAPIKey)
    $grpGoogleCloudTranslation.BackColor                        = [System.Drawing.Color]::Silver 
    $grpGoogleCloudTranslation.Location                         = New-Object System.Drawing.Point(6, 206)
    $grpGoogleCloudTranslation.Name                             = 'grpGoogleCloudTranslation'
    $grpGoogleCloudTranslation.Size                             = New-Object System.Drawing.Size(429, 83)
    $grpGoogleCloudTranslation.TabIndex                         = 1
    $grpGoogleCloudTranslation.TabStop                          = $False
    $grpGoogleCloudTranslation.Text                             = 'Google Cloud Translation'
    #
    # bttnGetGoogleLanguages
    #
    $bttnGetGoogleLanguages.Location                            = New-Object System.Drawing.Point(9, 50)
    $bttnGetGoogleLanguages.Name                                = 'bttnGetGoogleLanguages'
    $bttnGetGoogleLanguages.Size                                = New-Object System.Drawing.Size(414, 25)
    $bttnGetGoogleLanguages.TabIndex                            = 2
    $bttnGetGoogleLanguages.Text                                = 'Get all languages supported by Google Cloud Translation.'
    $bttnGetGoogleLanguages.UseVisualStyleBackColor             = $True
    #
    # txtGoogleAPIKey
    #
    $txtGoogleAPIKey.Location                                   = New-Object System.Drawing.Point(152, 19)
    $txtGoogleAPIKey.Name                                       = 'txtGoogleAPIKey'
    $txtGoogleAPIKey.Size                                       = New-Object System.Drawing.Size(271, 20)
    $txtGoogleAPIKey.TabIndex                                   = 1
    #
    # lblGoogleAPIKey
    #
    $lblGoogleAPIKey.AutoSize                                   = $True
    $lblGoogleAPIKey.Location                                   = New-Object System.Drawing.Point(7, 23)
    $lblGoogleAPIKey.Name                                       = 'lblGoogleAPIKey'
    $lblGoogleAPIKey.Size                                       = New-Object System.Drawing.Size(85, 13)
    $lblGoogleAPIKey.TabIndex                                   = 0
    $lblGoogleAPIKey.Text                                       = 'Google API Key:'
    #
    # grpDeepL
    #
    $grpDeepL.Controls.Add($bttnGetDeepLLanguages)
    $grpDeepL.Controls.Add($txtAPIDomain)
    $grpDeepL.Controls.Add($lblAPIDomain)
    $grpDeepL.Controls.Add($txtAuthenticationKey)
    $grpDeepL.Controls.Add($lblAuthenticationKey)
    $grpDeepL.BackColor                                         = [System.Drawing.Color]::Silver 
    $grpDeepL.Location                                          = New-Object System.Drawing.Point(6, 19)
    $grpDeepL.Name                                              = 'grpDeepL'
    $grpDeepL.Size                                              = New-Object System.Drawing.Size(429, 116)
    $grpDeepL.TabIndex                                          = 0
    $grpDeepL.TabStop                                           = $False
    $grpDeepL.Text                                              = 'DeepL'
    #
    # bttnGetDeepLLanguages
    #
    $bttnGetDeepLLanguages.Location                             = New-Object System.Drawing.Point(9, 77)
    $bttnGetDeepLLanguages.Name                                 = 'bttnGetDeepLLanguages'
    $bttnGetDeepLLanguages.Size                                 = New-Object System.Drawing.Size(414, 25)
    $bttnGetDeepLLanguages.TabIndex                             = 4
    $bttnGetDeepLLanguages.Text                                 = 'Get all languages supported by DeepL.'
    $bttnGetDeepLLanguages.UseVisualStyleBackColor              = $True
    #
    # txtAPIDomain
    #
    $txtAPIDomain.Location                                      = New-Object System.Drawing.Point(154, 46)
    $txtAPIDomain.Name                                          = 'txtAPIDomain'
    $txtAPIDomain.Size                                          = New-Object System.Drawing.Size(269, 20)
    $txtAPIDomain.TabIndex                                      = 3
    #
    # lblAPIDomain
    #
    $lblAPIDomain.AutoSize                                      = $True
    $lblAPIDomain.Location                                      = New-Object System.Drawing.Point(7, 49)
    $lblAPIDomain.Name                                          = 'lblAPIDomain'
    $lblAPIDomain.Size                                          = New-Object System.Drawing.Size(66, 13)
    $lblAPIDomain.TabIndex                                      = 2
    $lblAPIDomain.Text                                          = 'API Domain:'
    #
    # txtAuthenticationKey
    #
    $txtAuthenticationKey.Location                              = New-Object System.Drawing.Point(154, 20)
    $txtAuthenticationKey.Name                                  = 'txtAuthenticationKey'
    $txtAuthenticationKey.Size                                  = New-Object System.Drawing.Size(269, 20)
    $txtAuthenticationKey.TabIndex                              = 1
    #
    # lblAuthenticationKey
    #
    $lblAuthenticationKey.AutoSize                              = $True
    $lblAuthenticationKey.Location                              = New-Object System.Drawing.Point(7, 23)
    $lblAuthenticationKey.Name                                  = 'lblAuthenticationKey'
    $lblAuthenticationKey.Size                                  = New-Object System.Drawing.Size(99, 13)
    $lblAuthenticationKey.TabIndex                              = 0
    $lblAuthenticationKey.Text                                  = 'Authentication Key:'
    
    If (-not($NoUserSettings))
     {
      $tmpValue = Read-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionRegion"
      If ($tmpValue) {$txtAPIMSubscriptionRegion.Text = $tmpValue}
       
      $tmpValue = Read-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionKey"
      If ($tmpValue) {$txtAPIMSubscriptionKey.Text    = $tmpValue}
      
      $tmpValue = Read-Registry -RegistryKey $RegistryKey -ValueName "GoogleAPIKey"
      If ($tmpValue) {$txtGoogleAPIKey.Text           = $tmpValue}
      
      $tmpValue = Read-Registry -RegistryKey $RegistryKey -ValueName "APIDomain"
      If ($tmpValue) {$txtAPIDomain.Text              = $tmpValue}
      
      $tmpValue = Read-Registry -RegistryKey $RegistryKey -ValueName "AuthenticationKey"
      If ($tmpValue) {$txtAuthenticationKey.Text      = $tmpValue}

      $txtAPIMSubscriptionRegion.Refresh()
      $txtAPIMSubscriptionKey.Refresh()
      $txtGoogleAPIKey.Refresh()
      $txtAPIDomain.Refresh()
      $txtAuthenticationKey.Refresh()
     }

    $bttnGetDeepLLanguages.Visible          = $False
    $bttnGetGoogleLanguages.Visible         = $False
    $bttnGetMicrosoftLanguages.Visible      = $False
   
    $bttnTranslate.Text                     = $TranslationButtonBaseText
        
    ValidateDeepL
    ValidateMicrosoftCognitiveServices
    ValidateGoogleCloud

    $bttnTranslate.Enabled                  = $False
    $txtJSONFileWithTheTranslations.Enabled = $False
    $txtJSONFileWithEnglishText.Enabled     = $False
    $prgProgressbar.Visible                 = $False
    $frmAddTranslationsToJSON.Text          = "$ApplicationName $ApplicationVersion"
    $frmAddTranslationsToJSON.TopMost       = $True

    $chkAllItems.Add_CheckedChanged({SelectOrUnselectAllLanguages;CheckboxIsChanged})
    $txtAPIDomain.Add_TextChanged({ValidateDeepL})
    $txtAuthenticationKey.Add_TextChanged({ValidateDeepL})
    $txtAPIMSubscriptionKey.Add_TextChanged({ValidateMicrosoftCognitiveServices})
    $txtAPIMSubscriptionRegion.Add_TextChanged({ValidateMicrosoftCognitiveServices})
    $txtGoogleAPIKey.Add_TextChanged({ValidateGoogleCloud})
    $txtJSONFileWithEnglishText.Add_TextChanged({JSONFileWithEnglishTextHasBeenSelected})
    $chkLanguages.Add_SelectedValueChanged({CheckboxIsChanged;ValidateDeepL;ValidateMicrosoftCognitiveServices;ValidateGoogleCloud})
    $bttnTranslate.add_Click({bttnTranslate})
    $bttnBrowse.add_Click({GetAndTestJSONFile})

    $bttnGetDeepLLanguages.Add_Click({GetDeepLLanguages;ValidateDeepL})
    $bttnGetGoogleFREEAPILanguages.Add_Click({GetGoogleFREEAPILanguages})
    $bttnGetGoogleLanguages.add_Click({GetGoogleLanguages;ValidateGoogleCloud})
    $bttnGetMicrosoftLanguages.add_Click({GetMicrosoftLanguages;ValidateMicrosoftCognitiveServices})

    $bttnExit.add_Click({Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Leaving the application. Bye!"
    If (-not($NoUserSettings))
     {
      $ThisFunctionName = "Leaving"
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Leaving the application and saving settings."
      $tmpValue = $txtAPIMSubscriptionRegion.Text; If ($tmpValue) {Set-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionRegion" -Value $tmpValue}
      $tmpValue = $txtAPIMSubscriptionKey.Text;    If ($tmpValue) {Set-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionKey"    -Value $tmpValue}
      $tmpValue = $txtGoogleAPIKey.Text;           If ($tmpValue) {Set-Registry -RegistryKey $RegistryKey -ValueName "GoogleAPIKey"           -Value $tmpValue}
      $tmpValue = $txtAPIDomain.Text;              If ($tmpValue) {Set-Registry -RegistryKey $RegistryKey -ValueName "APIDomain"              -Value $tmpValue}
      $tmpValue = $txtAuthenticationKey.Text;      If ($tmpValue) {Set-Registry -RegistryKey $RegistryKey -ValueName "AuthenticationKey"      -Value $tmpValue}
     }
    $frmAddTranslationsToJSON.Close()
    $frmAddTranslationsToJSON.Dispose()
    Return 
    })

   [void]$frmAddTranslationsToJSON.ShowDialog()
  }

# =============================================================================================================================================
# End form block AddTranslationsToAJSONFile
# =============================================================================================================================================

  $Global:gblDetailedLogging                     = $DetailedLogging
  $strCurrentDir                                 = Split-Path -parent $MyInvocation.MyCommand.Definition
  $ApplicationName                               = "Add translations to a JSON file"
  $ApplicationVersion                            = "v2.2"
  $PowerShellLanguageMode                        = $ExecutionContext.SessionState.LanguageMode
  $Global:gblFullLanguage                        = $PowerShellLanguageMode -eq "FullLanguage"
  $ErrorNumber                                   = 0
  $ErrorText                                     = ""
  $MinimumWidth                                  = 800
  $MinimumHeight                                 = 600
  $RegistryKey                                   = "HKCU:Software\VroomSoft\AddTranslationsToJSONFile"
  $TranslationButtonBaseText                     = "Start the translation with"

# =============================================================================================================================================
# Find the logpath.
# =============================================================================================================================================

  $OnlyUserName,               `
  $LoggedOnUserInDomainFormat, `
  $UseridSID,                  `
  $InstallAccount                 = UserDetails
  $LogPath                        = $Env:TEMP
  $Global:LogPath                 = $LogPath

# =============================================================================================================================================
# Define the results file. This file contains all the results.
# It is saved as yyyy-MM-dd HH-mm-ss, for example 2022-11-14 22-23-09
# =============================================================================================================================================
 
  $strLastPartOfFileName                         = " ($((Get-Date -format "yyyy-MM-dd HH-mm-ss").ToString()))"
  $PreFixLogFile                                 = $ApplicationName -replace(" ","")
  $ThisFunctionName                              = "[Main - Resultsfile]"
   
  If ($Global:gblDetailedLogging)
   {
    $Global:LogFile = $Global:LogPath + "\"+ $PreFixLogFile + $($strLastPartOfFileName + ".log")
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logfile: $Global:LogFile"
   }

  If ($Message)
   {
    If ($bolError)
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "$Message"
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "This script will now quit."
      Write-Error $Message -Category CloseError
      Exit 3
     }
      else
     {
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "[Warning]: $Message"
     }
   }

# =============================================================================================================================================
# Find all the arguments and put them in the log file
# Source: https://ss64.com/ps/psboundparameters.html
# =============================================================================================================================================

  $ThisFunctionName                              = "[Main - Arguments]"
  If ($Global:bolFullLanguage)
   {
    $Global:gblTableWithParameters  = @()
    $RecordWithParameters = [ordered] @{"Key"     = "";
                                        "Value"   = ""}
  
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** Parameters part           *****"
  
    ForEach($boundparam in $PSBoundParameters.GetEnumerator()) 
     {
      $tmpValue                     = $($boundparam.Value)
      $Value                        = ""
      If ($tmpValue -is [array])
       {
        ForEach ($object in $tmpValue)
         {
          If (-not($value))
           {
            $Value = $object
           }
            else
           {
            $Value +=",$($object)"
           }
         }
       }
        else
       {
        $Value = $tmpValue
       }
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Key: $($boundparam.Key) Value: $Value" 
     }
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** End Parameters part       *****`r`n" 
   }
    else
   {
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The parameters are not written to the logfile due to PowerShell ConstrainedMode."
   }

# =============================================================================================================================================
# Write the logged in user details to the log file. 
# =============================================================================================================================================
  
  $ThisFunctionName                              = "[Main - User Details]"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** User details part         *****" 
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logged on user:                  $LoggedOnUserInDomainFormat"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logged on user (SID):            $UseridSID"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Installation account:            $InstallAccount"
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Language:                        $((Find-Language -CurrentUserSID $UseridSID).SubString(0, 2))"
  If ($Global:gblDetailedLogging)
   {
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Logfile:                         $Global:LogFile"
   }
  Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "***** End User details part     *****`r`n"

# =============================================================================================================================================
# Define the error number and messages
# =============================================================================================================================================

  $Error01 = "There is no working internet connection. The application is quit."
  $Error02 = "The Powershell Language mode '$PowerShellLanguageMode' is not supported." 
  $Error16 = "The file '$JSONFilename' does not exists."

# =============================================================================================================================================
#  Do some error handling:
#    - Check if there is a working internet connection.
#    - Powershell language mode shoudl be FullLanguage.
# =============================================================================================================================================
  
  $InternetConnection  = $True
  if ($Global:gblFullLanguage)
   {
    $InternetConnection  = Check-WorkingInternetConnection -HostNameToCheck www.bing.com
   }
    else
   {
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Checking internetconnection is not possible due to the PowerShell Language Mode $PowerShellLanguageMode." 
   }

  If (-not($InternetConnection))                           {$ErrorNumber += 1}
  If (-not($Global:gblFullLanguage))                       {$ErrorNumber += 2}
  If ($JSONFilename -and -not (Test-Path $JSONFilename))   {$ErrorNumber += 16}
  
# =============================================================================================================================================
# Find the proxy server
# =============================================================================================================================================
  
  If ($InternetConnection)                                 {$ProxyServer = Get-Proxy}

# =============================================================================================================================================
# Add assembly (only FullLanguage and not silent)
# =============================================================================================================================================
  
  If (($Global:gblFullLanguage) -and (-not $Silent))
   {    
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    Add-Type -AssemblyName System.Runtime
    Add-Type -AssemblyName PresentationFramework 
    [System.Windows.Forms.Application]::EnableVisualStyles()
    $DetectedWidth  = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Width
    $DetectedHeight = ([System.Windows.Forms.SystemInformation]::PrimaryMonitorSize).Height
    $Error04 = "The detected screen width of $DetectedWidth is less than the minimum width of $MinimumWidth."
    $Error08 = "The detected screen height of $DetectedHeight is less than of the minimum height of $MinimumHeight."
    If ($DetectedWidth -le $MinimumWidth)   {$ErrorNumber += 4}
    If ($DetectedHeight -le $MinimumHeight) {$ErrorNumber += 8}

    #----------------------------------------------
    #region Define SAPIEN Types
    #----------------------------------------------
    try{
        [SAPIENTypes.ProgressBarOverlay] | Out-Null
    }
     catch
    {
        If ($PSVersionTable.PSVersion.Major -ge 7)
        {
            $Assemblies = 'System.Windows.Forms', 'System.Drawing', 'System.Drawing.Primitives', 'System.ComponentModel.Primitives', 'System.Drawing.Common', 'System.Runtime'
        }
         else
        {
            $Assemblies = 'System.Windows.Forms', 'System.Drawing'  

        }
        Add-Type -ReferencedAssemblies $Assemblies -TypeDefinition @"
        using System;
        using System.Windows.Forms;
        using System.Drawing;
        namespace SAPIENTypes
        {
            public class ProgressBarOverlay : System.Windows.Forms.ProgressBar
            {
                public ProgressBarOverlay() : base() { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); }
                protected override void WndProc(ref Message m)
                { 
                    base.WndProc(ref m);
                    if (m.Msg == 0x000F)// WM_PAINT
                    {
                        if (Style != System.Windows.Forms.ProgressBarStyle.Marquee || !string.IsNullOrEmpty(this.Text))
                        {
                            using (Graphics g = this.CreateGraphics())
                            {
                                using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoWrap))
                                {
                                    stringFormat.Alignment = StringAlignment.Center;
                                    stringFormat.LineAlignment = StringAlignment.Center;
                                    if (!string.IsNullOrEmpty(this.Text))
                                        g.DrawString(this.Text, this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    else
                                    {
                                        int percent = (int)(((double)Value / (double)Maximum) * 100);
                                        g.DrawString(percent.ToString() + "%", this.Font, Brushes.Black, this.ClientRectangle, stringFormat);
                                    }
                                }
                            }
                        }
                    }
                }
              
                public string TextOverlay
                {
                    get
                    {
                        return base.Text;
                    }
                    set
                    {
                        base.Text = value;
                        Invalidate();
                    }
                }
            }
        }
"@ -IgnoreWarnings | Out-Null
    }
    #endregion Define SAPIEN Types
   }

# =============================================================================================================================================
# Error handling.
# =============================================================================================================================================

  If (($ErrorNumber -band 1) -eq 1)    {If ($ErrorText) {$ErrorText += "`n$Error01"} else {$ErrorText = $Error01}}
  If (($ErrorNumber -band 2) -eq 2)    {If ($ErrorText) {$ErrorText += "`n$Error02"} else {$ErrorText = $Error02}}
  If (($ErrorNumber -band 4) -eq 4)    {If ($ErrorText) {$ErrorText += "`n$Error04"} else {$ErrorText = $Error04}}
  If (($ErrorNumber -band 8) -eq 8)    {If ($ErrorText) {$ErrorText += "`n$Error08"} else {$ErrorText = $Error08}}
  If (($ErrorNumber -band 16) -eq 16)  {If ($ErrorText) {$ErrorText += "`n$Error16"} else {$ErrorText = $Error16}}

  If ($ErrorNumber -gt 0)
   {
    $GUI = $True
    if ($Silent -or -not $Global:gblFullLanguage) {$GUI = $False}
    $DummyValue = Display-MessageBox -Text $ErrorText -GUI:$GUI -Error -Title "Error."
    Exit $ErrorNumber
   }

# =============================================================================================================================================
# End Error handling.
# =============================================================================================================================================

# =============================================================================================================================================
# Clear the settings if needed.
# =============================================================================================================================================

  $ThisFunctionName                              = "[Main - Clear User Settings]"

  If ($ClearUserSettings -and (Test-Path $RegistryKey))
   {
    Remove-Item -Path $RegistryKey -Force | Out-Null
    Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "The key '$RegistryKey' has been deleted."
   }

# =============================================================================================================================================
# End cleaning the settings if needed.
# =============================================================================================================================================

 If ($Silent)
  {
    
  # =============================================================================================================================================
  # Check if the input file is a valid JSON file. If not, quit!
  # =============================================================================================================================================
    $ThisFunctionName = "Silent install"

    Try
     {
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "Reading '$JSONFilename'."
      $tmpJSONObject = Get-Content -Path $JSONFilename -Raw -Encoding UTF8 | ConvertFrom-Json
     }
      Catch
     {
      $ErrorMessage  = "There is an error near:`n`n"
      $ErrorText     = $($_.Exception.Message)
      [int]$Number   = ($ErrorText -split '\((.*?)\)')[1]
      $ErrorMessage += $ErrorText.SubString($Number-20)
      Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry $ErrorMessage
      $DummyValue = Display-MessageBox -Text $ErrorMessage -GUI:(-not $Silent) -Error -Title "Error."
      Exit 1
     }

  # =============================================================================================================================================
  # Check if the Input JSON File has an 'en' entry. 
  # =============================================================================================================================================
    
    If (-not($tmpJSONObject.en))
     {
      $ErrorText = "There is no 'en' entry in the JSON file '$JSONFilename'. So we cannot process this one."
      Add-EntryToLogFile -Entry $ErrorText -FunctionName $ThisFunctionName
      Display-MessageBox -Title "Error" -Text $ErrorText
      Exit 1
     }
  
    $Fallback                                 = $False                           

    If ($LanguagesToInclude.Contains("all")) 
     {
      $LanguagesToInclude = @("bg","cs","da","de","es","fr","id","it","ja","lv","nl","sv","el","et","fi","hu","lt","pl","ro","sk","sl","sv","tr","uk","pt","ru","zh")
     }

  # =============================================================================================================================================
  # Remove duplicate languages
  # =============================================================================================================================================
    
    $LanguagesToInclude = $LanguagesToInclude | Select-Object -Unique

    $Global:gblTable = @()
    ForEach ($SupportedLanguage in $LanguagesToInclude)
     {
      $Language = [CultureInfo]::GetCultures([System.Globalization.CultureTypes]::SpecificCultures) | Where {$_.Name -like "$SupportedLanguage*"} | Select-Object -Property Name,DisplayName
      If ($Language)
       {
        $Record  = [ordered] @{"Code"            = "";
                               "Display Name"    = "";}

        $Record."Code"         = $SupportedLanguage.Trim()
        $Record."Display Name" = ($Language[0].DisplayName).Split(" ")[0]
        $Global:gblTable      += New-Object PSObject -Property $Record
        Add-EntryToLogFile -Entry "Adding $($SupportedLanguage.Trim()) -> $(($Language[0].DisplayName).Split(" ")[0]) to the table." -FunctionName $ThisFunctionName
      }
    }

  # =============================================================================================================================================
  # Process the languages.
  # =============================================================================================================================================
  
    If ($TranslatorService -eq "Deepl")
     {
      If ((Read-Registry -RegistryKey $RegistryKey -ValueName "APIDomain") -and (Read-Registry -RegistryKey $RegistryKey -ValueName "AuthenticationKey"))
       {
        Add-EntryToLogFile -Entry "Starting the translation with DeepL." -FunctionName $ThisFunctionName
        ReadAndProcessJSONFile -InputFile $JSONFilename -LanguagesToProcess $LanguagesToInclude -Silent -TranslationService Deepl
       }
        else
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No details for DeepL found in the registry. Falling back to Google FREE API."
        $Fallback = $True
       }
     }

    If ($TranslatorService -eq "GoogleCloudServices")
     {
      If (Read-Registry -RegistryKey $RegistryKey -ValueName "GoogleAPIKey")
       {
        Add-EntryToLogFile -Entry "Starting the translation with Google Cloud Services." -FunctionName $ThisFunctionName
        ReadAndProcessJSONFile -InputFile $JSONFilename -LanguagesToProcess $LanguagesToInclude -Silent -TranslationService GoogleCloudServices
       }
        else
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No details for Google Cloud Services found in the registry. Falling back to Google FREE API."
        $Fallback = $True
       }
     }

    If ($TranslatorService -eq "MicrosoftCognitive")
     {
      If ((Read-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionRegion") -and (Read-Registry -RegistryKey $RegistryKey -ValueName "APIMSubscriptionKey"))
       {
        Add-EntryToLogFile -Entry "Starting the translation with Microsoft Cognitive Services Translator." -FunctionName $ThisFunctionName
        ReadAndProcessJSONFile -InputFile $JSONFilename -LanguagesToProcess $LanguagesToInclude -Silent -TranslationService MicrosoftCognitive
       }
        else
       {
        Add-EntryToLogFile -FunctionName $ThisFunctionName -Entry "No details for Microsoft Cognitive Translator found in the registry. Falling back to Google FREE API."
        $Fallback = $True
       }
     }

    If (($TranslatorService -eq "GoogleFREEAPI") -or $Fallback)
     {
      Add-EntryToLogFile -Entry "Starting the translation withGoogle FREE API." -FunctionName $ThisFunctionName
      ReadAndProcessJSONFile -InputFile $JSONFilename -LanguagesToProcess $LanguagesToInclude -Silent -TranslationService GoogleFREEAPI
     }

  }
   else
  {
   AddTranslationsToAJSONFile
  }

# =============================================================================================================================================
# End application.
# =============================================================================================================================================










 

After extracting the ZIP file, please 'unblock' the Powershell script and/or the batch file. 

Unblock
Unblock