Private/Initialize-ColorScriptsLocalization.ps1

function Resolve-InitializedLocalizationDetailRecord {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param()

    if (-not $script:LocalizationDetails) {
        $script:LocalizationDetails = [pscustomobject]@{
            LocalizedDataLoaded = $true
            ModuleRoot          = $script:ModuleRoot
            SearchedPaths       = @()
            Source              = 'Import-LocalizedData'
            FilePath            = $null
        }
    }

    return $script:LocalizationDetails
}

function Get-ColorScriptsPreferredCulture {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [string[]]$CultureFallbackOverride
    )

    if ($CultureFallbackOverride -and $CultureFallbackOverride.Count -gt 0) {
        return $CultureFallbackOverride[0]
    }

    try {
        return [System.Globalization.CultureInfo]::CurrentUICulture.Name
    }
    catch {
        return $null
    }
}

function Test-EmbeddedLocalizationPreferred {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory)][string]$LocalizationMode,
        [Parameter()][AllowNull()][AllowEmptyString()][string]$PreferredCulture,
        [switch]$UseDefaultCandidates
    )

    if ($LocalizationMode -eq 'Embedded') {
        return $true
    }

    if ($LocalizationMode -ne 'Auto' -or -not $UseDefaultCandidates) {
        return $false
    }

    return [string]::IsNullOrWhiteSpace($PreferredCulture) -or
    $PreferredCulture.StartsWith('en', [System.StringComparison]::OrdinalIgnoreCase)
}

function Use-ColorScriptsLocalizationState {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter()][AllowNull()][object]$Messages,
        [Parameter()][AllowNull()][object]$ModuleRoot,
        [Parameter(Mandatory)][bool]$LocalizedDataLoaded,
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$SearchedPath,
        [Parameter(Mandatory)][string]$Source,
        [Parameter()][AllowNull()][object]$FilePath
    )

    $script:Messages = $Messages
    $script:ModuleRoot = $ModuleRoot
    $script:LocalizationInitialized = $true
    $script:LocalizationDetails = [pscustomobject]@{
        LocalizedDataLoaded = $LocalizedDataLoaded
        ModuleRoot          = $ModuleRoot
        SearchedPaths       = $SearchedPath
        Source              = $Source
        FilePath            = $FilePath
    }

    return $script:LocalizationDetails
}

function Resolve-LocalizationRootPath {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)][string]$Root,
        [Parameter(Mandatory)][string]$FailureContext
    )

    try {
        return (Resolve-Path -LiteralPath $Root -ErrorAction Stop).ProviderPath
    }
    catch {
        Write-ModuleTrace ("{0} for '{1}': {2}" -f $FailureContext, $Root, $_.Exception.Message)
        return $Root
    }
}

function Import-ExplicitLocalizationRoot {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][AllowEmptyString()][string]$Root
    )

    if ([string]::IsNullOrWhiteSpace($Root)) {
        return $null
    }

    $resolvedRoot = Resolve-LocalizationRootPath -Root $Root -FailureContext 'Explicit root localization resolution failed'
    try {
        $rootProbe = Join-Path -Path $resolvedRoot -ChildPath 'Messages.psd1'
    }
    catch {
        return $null
    }

    if (-not (Test-Path -LiteralPath $rootProbe -PathType Leaf)) {
        return $null
    }

    try {
        $messages = Import-LocalizedData -BaseDirectory $resolvedRoot -FileName 'Messages.psd1' -ErrorAction Stop
        if (-not $messages -or $messages -isnot [System.Collections.IDictionary]) {
            return $null
        }

        $details = Use-ColorScriptsLocalizationState -Messages $messages -ModuleRoot $resolvedRoot -LocalizedDataLoaded $true -SearchedPath @($resolvedRoot) -Source 'Import-LocalizedData' -FilePath $rootProbe
        Write-ModuleTrace ("Localization loaded from explicit root via Import-LocalizedData: {0}" -f $resolvedRoot)
        return $details
    }
    catch {
        Write-ModuleTrace ("Explicit root localization import failed for '{0}': {1}" -f $resolvedRoot, $_.Exception.Message)
        return $null
    }
}

function Find-ExplicitLocalizationDetailRecord {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$CandidateRoot
    )

    foreach ($root in $CandidateRoot) {
        $details = Import-ExplicitLocalizationRoot -Root $root
        if ($details) {
            return $details
        }
    }

    return $null
}

function Get-EmbeddedLocalizationRoot {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [string[]]$CandidateRoot
    )

    $moduleRootCandidate = $null
    foreach ($candidate in @($CandidateRoot)) {
        if (-not [string]::IsNullOrWhiteSpace($candidate)) {
            $moduleRootCandidate = $candidate
            break
        }
    }

    if (-not $moduleRootCandidate -and $script:ModuleRoot) {
        $moduleRootCandidate = $script:ModuleRoot
    }
    elseif (-not $moduleRootCandidate -and $PSScriptRoot) {
        $moduleRootCandidate = $PSScriptRoot
    }

    if (-not $moduleRootCandidate) {
        return $null
    }

    return Resolve-LocalizationRootPath -Root $moduleRootCandidate -FailureContext 'Embedded defaults module root resolution failed'
}

function Use-EmbeddedLocalizationDefaultState {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter()][AllowNull()][object]$ModuleRoot,
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$SearchedPath
    )

    $messages = if ($script:EmbeddedDefaultMessages) { $script:EmbeddedDefaultMessages.Clone() } else { @{} }
    return Use-ColorScriptsLocalizationState -Messages $messages -ModuleRoot $ModuleRoot -LocalizedDataLoaded $false -SearchedPath $SearchedPath -Source 'EmbeddedDefaults' -FilePath $null
}

function Get-LocalizationCandidateRoot {
    [CmdletBinding()]
    [OutputType([string[]])]
    param(
        [string[]]$CandidateRoot
    )

    $uniqueCandidates = New-Object System.Collections.Generic.List[string]
    foreach ($candidate in @($CandidateRoot)) {
        if ([string]::IsNullOrWhiteSpace($candidate) -or $uniqueCandidates.Contains($candidate)) {
            continue
        }
        $null = $uniqueCandidates.Add($candidate)
    }

    if ($uniqueCandidates.Count -eq 0) {
        if ($script:ModuleRoot) {
            $null = $uniqueCandidates.Add($script:ModuleRoot)
        }
        elseif ($PSScriptRoot) {
            $null = $uniqueCandidates.Add($PSScriptRoot)
        }
    }

    return $uniqueCandidates.ToArray()
}

function Resolve-LocalizationCandidateDirectory {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)][string]$Candidate
    )

    Write-ModuleTrace ("Evaluating localization candidate: {0}" -f $Candidate)
    $candidatePath = Resolve-LocalizationRootPath -Root $Candidate -FailureContext 'Localization candidate resolution failed'
    if ([System.IO.Directory]::Exists($candidatePath) -or
        (Test-Path -LiteralPath $candidatePath -PathType Container)) {
        return $candidatePath
    }

    return $null
}

function Resolve-LocalizationFallbackRoot {
    [CmdletBinding()]
    [OutputType([string])]
    param()

    if ($PSScriptRoot -and (Test-Path -LiteralPath $PSScriptRoot -PathType Container)) {
        return Resolve-LocalizationRootPath -Root $PSScriptRoot -FailureContext 'Localization fallback root resolution failed'
    }

    if ($script:ModuleRoot) {
        return $script:ModuleRoot
    }

    return $null
}

function Get-LocalizationCandidatePath {
    [CmdletBinding()]
    [OutputType([string[]])]
    param(
        [string[]]$CandidateRoot
    )

    $candidatePaths = New-Object System.Collections.Generic.List[string]
    foreach ($candidate in @(Get-LocalizationCandidateRoot -CandidateRoot $CandidateRoot)) {
        $candidatePath = Resolve-LocalizationCandidateDirectory -Candidate $candidate
        if ($candidatePath -and -not $candidatePaths.Contains($candidatePath)) {
            [void]$candidatePaths.Add($candidatePath)
        }
    }

    if ($candidatePaths.Count -eq 0) {
        Write-ModuleTrace 'No localization candidate paths resolved; falling back to module root discovery.'
        $fallbackRoot = Resolve-LocalizationFallbackRoot
        if ($fallbackRoot) {
            [void]$candidatePaths.Add($fallbackRoot)
        }
    }

    if ($candidatePaths.Count -eq 0) {
        throw [System.InvalidOperationException]::new('Unable to resolve a module root for localization resources.')
    }

    return $candidatePaths.ToArray()
}

function Import-LocalizationCandidate {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][string]$CandidatePath,
        [string[]]$CultureFallbackOverride,
        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [System.Collections.Generic.List[string]]$SearchedPath
    )

    $null = $SearchedPath.Add($CandidatePath)
    $importParams = @{ BaseDirectory = $CandidatePath }
    if ($CultureFallbackOverride -and $CultureFallbackOverride.Count -gt 0) {
        $importParams['FallbackUICulture'] = $CultureFallbackOverride
    }

    try {
        $importResult = Import-LocalizedMessagesFromFile @importParams
        if (-not $importResult -or -not $importResult.Messages) {
            return $null
        }

        $source = if ($importResult.Source) { $importResult.Source } else { 'Import-LocalizedData' }
        $filePath = if ($importResult.FilePath) { $importResult.FilePath } else { $null }
        $details = Use-ColorScriptsLocalizationState -Messages $importResult.Messages -ModuleRoot $CandidatePath -LocalizedDataLoaded $true -SearchedPath $SearchedPath.ToArray() -Source $source -FilePath $filePath
        if ($filePath) {
            Write-ModuleTrace ("Localization resolved via {0} from {1} (file {2})" -f $source, $CandidatePath, $filePath)
        }
        else {
            Write-ModuleTrace ("Localization resolved via {0} from {1}" -f $source, $CandidatePath)
        }

        return $details
    }
    catch {
        Write-ModuleTrace ("Localization import failure for '{0}': {1}" -f $CandidatePath, $_.Exception.Message)
        return $null
    }
}

function Find-LocalizationImport {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$CandidatePath,
        [string[]]$CultureFallbackOverride
    )

    $searchedPaths = New-Object System.Collections.Generic.List[string]
    $selectedRoot = $null
    foreach ($currentPath in $CandidatePath) {
        if ([string]::IsNullOrWhiteSpace($currentPath)) {
            continue
        }

        $selectedRoot = $currentPath
        $details = Import-LocalizationCandidate -CandidatePath $currentPath -CultureFallbackOverride $CultureFallbackOverride -SearchedPath $searchedPaths
        if ($details) {
            return [pscustomobject]@{
                Details      = $details
                SelectedRoot = $selectedRoot
                SearchedPath = $searchedPaths.ToArray()
            }
        }
    }

    return [pscustomobject]@{
        Details      = $null
        SelectedRoot = $selectedRoot
        SearchedPath = $searchedPaths.ToArray()
    }
}

function Get-LocalizationFailureRoot {
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter()][AllowNull()][object]$SelectedRoot,
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$CandidatePath
    )

    if ($SelectedRoot) {
        return $SelectedRoot
    }

    if ($CandidatePath.Count -gt 0) {
        return $CandidatePath[0]
    }

    return Resolve-LocalizationFallbackRoot
}

function Test-LocalizationStateReusable {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [string[]]$CandidateRoot,
        [string[]]$CultureFallbackOverride
    )

    return $script:LocalizationInitialized -and
    $script:Messages -and
    -not $CandidateRoot -and
    -not $CultureFallbackOverride
}

function Test-EmbeddedLocalizationFastPath {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory)][bool]$Preferred,
        [Parameter(Mandatory)][string]$LocalizationMode,
        [Parameter(Mandatory)][bool]$ExplicitRootsProvided
    )

    return $Preferred -and $LocalizationMode -ne 'Full' -and -not $ExplicitRootsProvided
}

function Use-EmbeddedLocalizationFastPath {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [string[]]$CandidateRoot,
        [Parameter()][AllowNull()][AllowEmptyString()][string]$PreferredCulture,
        [Parameter(Mandatory)][string]$LocalizationMode
    )

    $moduleRoot = Get-EmbeddedLocalizationRoot -CandidateRoot $CandidateRoot
    $searchedPath = if ($moduleRoot) { @($moduleRoot) } else { @() }
    $details = Use-EmbeddedLocalizationDefaultState -ModuleRoot $moduleRoot -SearchedPath $searchedPath
    $cultureDisplay = if ($PreferredCulture) { $PreferredCulture } else { 'n/a' }
    Write-ModuleTrace ("Localization fast-path using embedded defaults (mode: {0}, culture: {1})" -f $LocalizationMode, $cultureDisplay)
    return $details
}

function Initialize-ColorScriptsLocalizationCore {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [string[]]$CandidateRoot,
        [string[]]$CultureFallbackOverride,
        [switch]$UseDefaultCandidates
    )

    if (Test-LocalizationStateReusable -CandidateRoot $CandidateRoot -CultureFallbackOverride $CultureFallbackOverride) {
        return Resolve-InitializedLocalizationDetailRecord
    }

    $localizationMode = if ($script:LocalizationMode) { $script:LocalizationMode } else { 'Auto' }
    $preferredCulture = Get-ColorScriptsPreferredCulture -CultureFallbackOverride $CultureFallbackOverride
    $preferEmbeddedDefaults = Test-EmbeddedLocalizationPreferred -LocalizationMode $localizationMode -PreferredCulture $preferredCulture -UseDefaultCandidates:$UseDefaultCandidates
    $explicitRootsProvided = $null -ne $CandidateRoot -and $CandidateRoot.Count -gt 0

    if ($explicitRootsProvided) {
        $explicitDetails = Find-ExplicitLocalizationDetailRecord -CandidateRoot $CandidateRoot
        if ($explicitDetails) {
            return $explicitDetails
        }
    }

    $useEmbeddedFastPath = Test-EmbeddedLocalizationFastPath -Preferred $preferEmbeddedDefaults -LocalizationMode $localizationMode -ExplicitRootsProvided $explicitRootsProvided
    if ($useEmbeddedFastPath) {
        return Use-EmbeddedLocalizationFastPath -CandidateRoot $CandidateRoot -PreferredCulture $preferredCulture -LocalizationMode $localizationMode
    }

    $candidatePaths = @(Get-LocalizationCandidatePath -CandidateRoot $CandidateRoot)
    $import = Find-LocalizationImport -CandidatePath $candidatePaths -CultureFallbackOverride $CultureFallbackOverride
    if ($import.Details) {
        return $import.Details
    }

    $effectiveRoot = Get-LocalizationFailureRoot -SelectedRoot $import.SelectedRoot -CandidatePath $candidatePaths
    $details = Use-EmbeddedLocalizationDefaultState -ModuleRoot $effectiveRoot -SearchedPath $import.SearchedPath
    Write-Warning 'Localization resources were not found. Falling back to built-in English messages.'
    return $details
}

function Initialize-ColorScriptsLocalization {
    param(
        [string[]]$CandidateRoots,
        [string[]]$CultureFallbackOverride,
        [switch]$UseDefaultCandidates
    )

    # Capture parameters outside the synchronized scriptblock so ScriptAnalyzer and Windows
    # PowerShell agree that the values are intentionally closed over.
    $candidateRootList = $CandidateRoots
    $cultureFallbackList = $CultureFallbackOverride
    $useDefaultCandidatesFlag = $UseDefaultCandidates.IsPresent

    return Invoke-ModuleSynchronized $script:LocalizationSyncRoot {
        Initialize-ColorScriptsLocalizationCore -CandidateRoot $candidateRootList -CultureFallbackOverride $cultureFallbackList -UseDefaultCandidates:$useDefaultCandidatesFlag
    }
}