Src/Private/Initialize-LocalizedData.ps1

function Initialize-LocalizedData {
    <#
    .SYNOPSIS
        Initializes localized data for AsBuiltReport Core and Report modules.
 
    .DESCRIPTION
        This function loads localized strings from language files (.psd1) based on the specified or detected culture.
        It supports both Core and Report modules with intelligent fallback logic:
 
        For Core modules (when no Language parameter specified):
        - Uses the user's OS language (Get-Culture)
        - Searches the Core module's Language folder with culture fallback
 
        For Report modules (when no Language parameter specified):
        - Defaults to en-US
        - Searches the Report module's Language folder with culture fallback
        - Falls back to the Core module's Language folder for shared translations
 
        When Language parameter is explicitly provided:
        - Uses the specified language for both Core and Report modules
 
        Language selection priority for Report modules:
        1. ReportLanguage parameter (if explicitly specified by user)
        2. Report.Language setting from JSON configuration file
        3. Default fallback to 'en-US'
 
    .PARAMETER ModuleBasePath
        The base path of the module containing the Language folder.
        This should point to the module's root directory where the Language subfolder is located.
 
    .PARAMETER LanguageFile
        The name of the language file (without .psd1 extension).
 
    .PARAMETER ModuleType
        The type of module: 'Core' or 'Report'. Defaults to 'Core'.
 
    .PARAMETER ModuleName
        The name of the report module (e.g., 'VMware.vSphere').
        Used for Report modules to help identify the module context.
 
    .PARAMETER Language
        The specific language to use (e.g., 'en-US', 'es-ES', 'fr-FR').
        If not specified:
        - Core modules use the user's OS language
        - Report modules default to 'en-US'
 
    .EXAMPLE
        # Core module usage - uses OS language
        Initialize-LocalizedData -ModuleBasePath $PSScriptRoot -LanguageFile 'New-AsBuiltReport' -ModuleType 'Core'
 
    .EXAMPLE
        # Core module usage with explicit language
        Initialize-LocalizedData -ModuleBasePath $PSScriptRoot -LanguageFile 'New-AsBuiltReport' -ModuleType 'Core' -Language 'fr-FR'
 
    .EXAMPLE
        # Report module usage - defaults to en-US
        Initialize-LocalizedData -ModuleBasePath $PSScriptRoot -LanguageFile 'VMwareSphere' -ModuleType 'Report' -ModuleName 'VMware.vSphere'
 
    .EXAMPLE
        # Report module usage with explicit language
        Initialize-LocalizedData -ModuleBasePath $PSScriptRoot -LanguageFile 'VMwareSphere' -ModuleType 'Report' -ModuleName 'VMware.vSphere' -Language 'es-ES'
 
    .NOTES
        Sets global variable $global:translate with the loaded translations.
        Uses Resolve-Culture function for comprehensive culture fallback.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$ModuleBasePath,

        [Parameter(Mandatory = $true)]
        [string]$LanguageFile,

        [Parameter()]
        [ValidateSet('Core', 'Report')]
        [string]$ModuleType = 'Core',

        [Parameter()]
        [string]$ModuleName,

        [Parameter()]
        [string]$Language
    )

    try {
        # Determine which language to use based on parameters and module type
        if ($Language) {
            # Explicit language parameter provided
            $Culture = [System.Globalization.CultureInfo]::GetCultureInfo($Language)
        } elseif ($ModuleType -eq 'Core') {
            # For Core modules, use the user's OS language
            $Culture = Get-Culture
        } else {
            # For Report modules without explicit language, use en-US as default
            $Culture = [System.Globalization.CultureInfo]::GetCultureInfo('en-US')
        }
        $PSDefaultParameterValues['*:UICulture'] = $Culture.Name
        $PSDefaultParameterValues['*:Culture'] = $Culture.Name
    } catch {
        Write-Error ($_.Exception.Message)
        return
    }

    # Setup all paths required for script to run
    $script:RootPath = $ModuleBasePath

    # Define search paths based on module type
    $SearchPaths = @()
    if ($ModuleType -eq 'Report' -and $ModuleName) {
        # For report modules, search report module first, then core module
        $ReportLangPath = Join-Path $RootPath 'Language'
        $SearchPaths += @{Path = $ReportLangPath; Type = 'Report'}

        # Try to find Core module path for fallback
        try {
            $CoreModule = Get-Module -Name 'AsBuiltReport.Core' -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1
            if ($CoreModule) {
                $CoreLangPath = Join-Path $CoreModule.ModuleBase 'Language'
                $SearchPaths += @{Path = $CoreLangPath; Type = 'Core'}
            }
        } catch {
            Write-Warning "Could not locate AsBuiltReport.Core module for fallback translations"
        }
    } else {
        # For core modules, use the existing logic
        $script:LangPath = Join-Path $RootPath 'Language'
        $SearchPaths += @{Path = $script:LangPath; Type = 'Core'}
    }

    # Get fallback chain for current culture
    $CultureFallbacks = Resolve-Culture -CultureName $Culture.Name

    # Find first available culture where BOTH folder AND file exist
    $TargetCulture = $null
    $Translate = $null
    $FoundPath = $null

    # Search through all search paths and culture fallbacks
    foreach ($SearchPath in $SearchPaths) {
        foreach ($FallbackCulture in $CultureFallbacks) {
            $TestPath = Join-Path $SearchPath.Path $FallbackCulture
            $TestFile = Join-Path $TestPath "$LanguageFile.psd1"
            if ((Test-Path $TestPath) -and (Test-Path $TestFile)) {
                try {
                    # Test if the file can actually be imported
                    $TestImport = Import-LocalizedData -BaseDirectory $SearchPath.Path -UICulture $FallbackCulture -FileName $LanguageFile -ErrorAction Stop
                    $TargetCulture = $FallbackCulture
                    $Translate = $TestImport
                    $FoundPath = $SearchPath.Path
                    $script:LangPath = $SearchPath.Path
                    break
                } catch {
                    # Continue to next fallback if import fails
                    Write-Error "Cannot load $FallbackCulture language file. Ensure $TestFile exists and is valid." -ErrorAction Stop
                }
            }
        }
        # If we found a translation, break out of search paths loop
        if ($TargetCulture) { break }
    }

    # Final safety net - try en-US if nothing else worked
    if (-not $TargetCulture -or -not $Translate) {
        foreach ($SearchPath in $SearchPaths) {
            $TestPath = Join-Path $SearchPath.Path 'en-US'
            $TestFile = Join-Path $TestPath "$LanguageFile.psd1"

            if ((Test-Path $TestPath) -and (Test-Path $TestFile)) {
                try {
                    $Translate = Import-LocalizedData -BaseDirectory $SearchPath.Path -UICulture 'en-US' -FileName $LanguageFile -ErrorAction Stop
                    $TargetCulture = 'en-US'
                    $FoundPath = $SearchPath.Path
                    $script:LangPath = $SearchPath.Path
                    break
                } catch {
                    Write-Error "Cannot load en-US language file. Ensure $TestFile exists and is valid." -ErrorAction Stop
                }
            }
        }

        # If still no translation found, error out
        if (-not $TargetCulture -or -not $Translate) {
            $SearchedPaths = ($SearchPaths | ForEach-Object { Join-Path $_.Path "en-US\$LanguageFile.psd1" }) -join ', '
            Write-Error "en-US language file not found in any of the searched paths: $SearchedPaths" -ErrorAction Stop
        }
    }

    $script:CulturePath = Join-Path $script:LangPath $TargetCulture

    # Output appropriate message (only once per session per culture/module combination)
    # Determine the source of the translations
    $SourceType = $null
    foreach ($SearchPath in $SearchPaths) {
        if ($SearchPath.Path -eq $FoundPath) {
            $SourceType = $SearchPath.Type
            break
        }
    }

    # Determine the appropriate message based on module type
    # Core module = UI language, Report module = Document language
    $LanguageType = if ($ModuleType -eq 'Core') { "UI language" } else { "document language" }


    # Determine the language source for Report modules
    $LanguageSourceText = ""
    if ($ModuleType -eq 'Report' -and $Language) {
        # Check if the language was explicitly specified via command-line parameter
        # by examining if ReportConfig exists and comparing with its language setting
        if ($global:ReportConfig -and $global:ReportConfig.Report.Language) {
            if ($Language -eq $global:ReportConfig.Report.Language) {
                $LanguageSourceText = " (from report configuration file)"
            } else {
                # Language differs from config, so it must be from command-line
                $LanguageSourceText = " (from command-line parameter)"
            }
        } elseif ($Language -eq 'en-US') {
            # Default language
            $LanguageSourceText = " (fallback to default)"
        } else {
            # Explicitly specified via command-line (no config or config doesn't have language)
            $LanguageSourceText = " (from command-line parameter)"
        }
    }
    # Create unique key for this culture fallback scenario
    # When there's a fallback, include module type to show both messages
    # When there's no fallback, just use culture to avoid duplicate messages for same culture
    if ($TargetCulture -ne $Culture.Name) {
        $LocalizationKey = "$TargetCulture-$($Culture.Name)-$ModuleType"
    } else {
        $LocalizationKey = "$TargetCulture"
    }

    # Initialize global tracking variable if it doesn't exist
    if (-not $global:AsBuiltReportLocalizedWarnings) {
        $global:AsBuiltReportLocalizedWarnings = @{}
    }

    # Only show message if we haven't shown it before for this combination
    $ShouldShowMessage = -not $global:AsBuiltReportLocalizedWarnings.ContainsKey($LocalizationKey)

    # Suppress messages for style-related language files (internal implementation detail)
    if ($LanguageFile -eq 'AsBuiltReportCoreStyle') {
        $ShouldShowMessage = $false
    }

    if ($ShouldShowMessage) {
        if ($TargetCulture -ne $Culture.Name) {
            Write-Host ("Setting $LanguageType to '{0}' (fallback from '{1}')$LanguageSourceText." -f $TargetCulture, $Culture.Name) -ForegroundColor Yellow
        } else {
            Write-Host ("Setting $LanguageType to '{0}'$LanguageSourceText." -f $TargetCulture) -ForegroundColor Yellow
        }

        # Mark this combination as already shown
        $global:AsBuiltReportLocalizedWarnings[$LocalizationKey] = $true
    }

    # Set as global variable
    $global:translate = $Translate
}