Modules/businessdev.ALbuild.Containers/Private/Resolve-BcWebClientUiString.ps1

function Resolve-BcWebClientUiString {
    <#
    .SYNOPSIS
        Resolves the accessible names the capture engine looks for, for one UI culture.
 
    .DESCRIPTION
        The URL parameters the capture command relies on ('page', 'report', 'filter', 'mode') are
        documented and stable. The BUTTON CAPTIONS are not: "Verwerfen", "Vorschau" and the rest are UI
        text tied to a BC version and a language, and they are the first thing that breaks.
 
        So they live in a file - Resources/webclient-ui.json - and a workspace can override it:
 
            "screenshot": { "uiStrings": "./albuild-webclient-ui.json" }
 
        The override is MERGED over the defaults per key, so a workspace that only has to correct one
        caption writes one caption, not the whole table. Resolution per culture: the exact culture
        ('de-DE'), then its language ('de'), then 'default'.
 
        What it deliberately does NOT do is guess. A culture with no entry falls back to 'default'
        rather than to "whatever the last one was", and a caption that matches nothing at capture time
        is a named failure in the engine (UI_ELEMENT_NOT_FOUND) rather than a screenshot of the wrong
        thing.
 
    .PARAMETER Language
        UI culture, e.g. 'de-DE'. An empty value resolves the defaults.
 
    .PARAMETER OverridePath
        A JSON file merged over the shipped table. Missing file = terminating error: a caller that
        configured an override and silently got the defaults would debug the wrong thing.
 
    .OUTPUTS
        Hashtable of name -> string[], ready to serialise into the engine's job file.
 
    .EXAMPLE
        Resolve-BcWebClientUiString -Language 'de-DE'
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [string] $Language,
        [string] $OverridePath
    )

    $resource = Get-BcCaptureResource
    $tablePath = Join-Path -Path (Split-Path -Path $resource.Folder -Parent) -ChildPath 'webclient-ui.json'
    if (-not (Test-Path -LiteralPath $tablePath)) {
        throw "The web client UI string table is missing from the module ('$tablePath'). Reinstall businessdev.ALbuild."
    }

    $table = Get-Content -LiteralPath $tablePath -Raw | ConvertFrom-Json

    if ($OverridePath) {
        if (-not (Test-Path -LiteralPath $OverridePath)) {
            throw ("The UI string override '$OverridePath' does not exist. Remove 'screenshot.uiStrings' " +
                'from albuild.json, or point it at a file that is there - falling back to the defaults ' +
                'silently would hide the very change you configured.')
        }
        $override = Get-Content -LiteralPath $OverridePath -Raw | ConvertFrom-Json
    }
    else {
        $override = $null
    }

    # Most specific first: 'de-DE' beats 'de' beats 'default'. Applied low to high so the specific
    # entry is the one left standing.
    $language = "$Language".Trim()
    $scopes = @('default')
    if ($language) {
        $primary = ($language -split '-')[0]
        if ($primary -and $primary -ne 'default') { $scopes += $primary }
        if ($language -ne $primary) { $scopes += $language }
    }

    $resolved = @{}
    foreach ($source in @($table, $override)) {
        if (-not $source) { continue }
        foreach ($scope in $scopes) {
            if (-not $source.PSObject.Properties[$scope]) { continue }
            $entry = $source.$scope
            foreach ($property in $entry.PSObject.Properties) {
                if ($property.Name -like '$*') { continue }   # the '$comment' block is documentation
                $resolved[$property.Name] = @($property.Value)
            }
        }
    }

    return $resolved
}