jdhitsolutions / WingetTools

A set of PowerShell tools for working with the winget package manager.
MIT License
146 stars 14 forks source link

[Bug]: German language Windows 11 - issue with Get-WGInstalled Function #8

Closed SebCT closed 2 years ago

SebCT commented 2 years ago

Describe the problem

On a german language Windows 11 Installation the module will fail because of the german output of winget.

With the Function "Get-WGInstalled" on a german language Windows the winget output has to be parsed with german instead of english.

In the attached screen with "Get-WGInstalled -Verbose" you will get this result because of the german output of winget:

image

Expectation

A list of installed software with Get-WGInstalled Function.

Additional Information

I made a workaround for my german language with these lines (Line 59-69) in the Get-WGInstalled Function:

Name = $rxname.match($($show | Select-String "Gefunden\s")).value ID = $pkg.PackageIdentifier InstalledVersion = $installed OnlineVersion = $online Publisher = $(Try { (($show | Select-String "Herausgeber:") -split "Herausgeber: ")[1].trim() } Catch { $null }) PublisherURL = $(Try { (($show | Select-String "Herausgeber-Url:") -split "Herausgeber-Url: ")[1].trim() } Catch { $null }) Author = $(Try { (($show | Select-String "Autor:") -split "Autor: ")[1].trim() } Catch { $null }) Moniker = $(Try { (($show | Select-String "Moniker:") -split "Moniker: ")[1].trim() } Catch {$null }) Description = $(Try { (($show | Select-String "Beschreibung:") -split "Beschreibung: ")[1].trim() } Catch { $null }) Homepage = $(Try { (($show | Select-String "Startseite:") -split "Startseite: ")[1].trim() } Catch { $null }) Source = $Source

PowerShell version

7.2

Platform

Windows 11 Pro or Enterprise

Additional Checks

jdhitsolutions commented 2 years ago

I'm not too surprised there is a problem with non-English cultures. Winget doesn't leave many options to make it PowerShell-friendly and I've resorted to regex patterns which don't always translate well across cultures. I think I have another option with Get-WGInstalled I'll have to look at.

jdhitsolutions commented 2 years ago

I'm working on adding a YAML dependency which I think will help. I'm trying to duplicate your output. Can you run winget show gimp.gimp --source winget and paste the text output?

SebCT commented 2 years ago

I'm working on adding a YAML dependency which I think will help. I'm trying to duplicate your output. Can you run winget show gimp.gimp --source winget and paste the text output?

Here's the output from a german OS -> winget show gimp.gimp --source winget

Gefunden GIMP [GIMP.GIMP] Version: 2.10.32 Herausgeber: The GIMP Team Herausgeber-Support-URL: https://docs.gimp.org/en/ Autor: The GIMP Team Moniker: gimp Beschreibung: GIMP is an acronym for GNU Image Manipulation Program. It is a freely distributed program for such tasks as photo retouching, image composition and image authoring. Startseite: https://www.gimp.org Lizenz: GPL-3.0 Lizenz-URL: https://www.gimp.org/about/COPYING Installationsprogramm: Typ: inno Download-URL: https://download.gimp.org/pub/gimp/v2.10/windows/gimp-2.10.32-setup-1.exe SHA256: e4410b5695cfc83bc2a33a124e8689a50c942978d0164e77724407d2a5cefb0d

jdhitsolutions commented 2 years ago

Thanks. This is what I was afraid of. Using YAML conversion still won't solve the problem. I'll need to use ConvertFrom-StringData with localized property names. It just means this will take a bit longer to resolve.

SebCT commented 2 years ago

Thanks for trying to support german/other language, too - amazing work and a really excellent PS-Module :-)

jdhitsolutions commented 2 years ago

I can't find any way to duplicate the German results. But I think I have a solution. The solution requires another module dependency. I'll have to ask you to install the psyml module. This is a wrapper for the PSYamlDotNet class and I'm using it to parse winget output. Once you have the module installed, save this to a script file and run it.

#requires -version 5.1
#requires -module ThreadJob
#requires -module psyml

[cmdletbinding()]
Param([string]$ID = "microsoft.edge", [string]$Source = "winget")

$localized = ConvertFrom-StringData -StringData @"
Found               = Gefunden
Name                = Name
ID                  = ID
Version             = Version
Publisher           = Herausgeber
PublisherUrl        = Herausgeber-URL
PublisherSupportUrl = Herausgeber-Support-URL
Author              = Autor
Moniker             = Moniker
Description         = Beschreibung
Homepage            = Startseite
License             = Lizenz
LicenseURL          = Lizenz-URL
Source              = Source
Computername        = Computername
"@

Function _parseVersion {
    #parse out odd characters from version strings
    [cmdletbinding()]
    Param([string]$VersionString)

    if ($versionString -match "unknown") {
        $out = $null
    }
    elseif ($VersionString -match "[\<\>]") {
        $out = ($VersionString -replace $matches.values, "").Trim()
    }
    else {
        $out = $VersionString.Trim()
    }

    $out
}
$show = Start-ThreadJob { Param([string]$ID, [string]$source) winget show --id $id --source $source } -ArgumentList $id, $source |
Wait-Job | Receive-Job -Keep |
Where-Object { $_ -notmatch "(\d+%|\d MB|\s+)$" -and $_.length -gt 0 }

#write the winget result to the host
$show | Out-String | Write-Host -ForegroundColor cyan

Try {
    $yml = $show.replace(" - ", " ") | Select-Object -Skip 1 | Where-Object { $_ -match "^(\s+)?(\b(\w+)\b).*:" } | ConvertFrom-Yaml -ErrorAction stop
}
Catch {
    Write-Warning "Failed to convert to YAML"
    $show | Out-String | Write-Warning
}

[regex]$rxname = "(?<name>(?<=\w\s).*)(?=\s\[(?<id>[\S\.]+)\])"
$online = _parseVersion $yml.$($localized.version)
$installed = "to be determined"
[pscustomobject]@{
    PSTypeName          = "WGInstalled"
    Name                = $rxname.match($show[0]).groups["name"].value
    ID                  = $rxname.match($show[0]).groups["id"].value
    InstalledVersion    = $installed
    OnlineVersion       = $online
    Publisher           = $yml.$($localized.Publisher)
    PublisherUrl        = $yml.$($localized.PublisherURL)
    PublisherSupportUrl = $yml.$($localized.PublisherSupportUrl)
    Author              = $yml.$($localized.Author)
    Moniker             = $yml.$($localized.Moniker)
    Description         = $yml.$($localized.Description)
    Homepage            = $yml.$($Localized.homepage)
    Source              = $Source
}

I'm using a default package id, but you can try something else you have installed like gimp.gimp. I want to test localized data. The output object will still have English property names. Maybe later, I can see what it takes to create an object with localized property names.

SebCT commented 2 years ago

I can't find any way to duplicate the German results. But I think I have a solution. The solution requires another module dependency. I'll have to ask you to install the psyml module. This is a wrapper for the PSYamlDotNet class and I'm using it to parse winget output. Once you have the module installed, save this to a script file and run it.

#requires -version 5.1
#requires -module ThreadJob
#requires -module psyml

[cmdletbinding()]
Param([string]$ID = "microsoft.edge", [string]$Source = "winget")

$localized = ConvertFrom-StringData -StringData @"
Found               = Gefunden
Name                = Name
ID                  = ID
Version             = Version
Publisher           = Herausgeber
PublisherUrl        = Herausgeber-URL
PublisherSupportUrl = Herausgeber-Support-URL
Author              = Autor
Moniker             = Moniker
Description         = Beschreibung
Homepage            = Startseite
License             = Lizenz
LicenseURL          = Lizenz-URL
Source              = Source
Computername        = Computername
"@

Function _parseVersion {
    #parse out odd characters from version strings
    [cmdletbinding()]
    Param([string]$VersionString)

    if ($versionString -match "unknown") {
        $out = $null
    }
    elseif ($VersionString -match "[\<\>]") {
        $out = ($VersionString -replace $matches.values, "").Trim()
    }
    else {
        $out = $VersionString.Trim()
    }

    $out
}
$show = Start-ThreadJob { Param([string]$ID, [string]$source) winget show --id $id --source $source } -ArgumentList $id, $source |
Wait-Job | Receive-Job -Keep |
Where-Object { $_ -notmatch "(\d+%|\d MB|\s+)$" -and $_.length -gt 0 }

#write the winget result to the host
$show | Out-String | Write-Host -ForegroundColor cyan

Try {
    $yml = $show.replace(" - ", " ") | Select-Object -Skip 1 | Where-Object { $_ -match "^(\s+)?(\b(\w+)\b).*:" } | ConvertFrom-Yaml -ErrorAction stop
}
Catch {
    Write-Warning "Failed to convert to YAML"
    $show | Out-String | Write-Warning
}

[regex]$rxname = "(?<name>(?<=\w\s).*)(?=\s\[(?<id>[\S\.]+)\])"
$online = _parseVersion $yml.$($localized.version)
$installed = "to be determined"
[pscustomobject]@{
    PSTypeName          = "WGInstalled"
    Name                = $rxname.match($show[0]).groups["name"].value
    ID                  = $rxname.match($show[0]).groups["id"].value
    InstalledVersion    = $installed
    OnlineVersion       = $online
    Publisher           = $yml.$($localized.Publisher)
    PublisherUrl        = $yml.$($localized.PublisherURL)
    PublisherSupportUrl = $yml.$($localized.PublisherSupportUrl)
    Author              = $yml.$($localized.Author)
    Moniker             = $yml.$($localized.Moniker)
    Description         = $yml.$($localized.Description)
    Homepage            = $yml.$($Localized.homepage)
    Source              = $Source
}

I'm using a default package id, but you can try something else you have installed like gimp.gimp. I want to test localized data. The output object will still have English property names. Maybe later, I can see what it takes to create an object with localized property names.

Works perfect, thanks!

image
jdhitsolutions commented 2 years ago

Thanks. I'll finish integrating this approach into the module.

jdhitsolutions commented 2 years ago

I've published v1.5.0 to the PowerShell Gallery. Update the module and test in a new PowerShell session. I even added German versions of command help. Translations are from Google, so feel free to submit a PR for the German markdown files if they can be improved.

SebCT commented 2 years ago

I can confirm that Version 1.5.0 is now working with german winget, too - thank you!