Src/Private/Resolve-Culture.ps1

# Function to resolve culture with fallback
function Resolve-Culture {
    <#
    .SYNOPSIS
        Resolves a culture name to a fallback chain of culture names.
    .DESCRIPTION
        Returns an array of culture names representing the fallback chain for a given culture.
        The function provides intelligent fallbacks for regional language variants.
 
        For example, 'fr-CA' (French-Canada) will fall back to 'fr-FR' (French-France), then 'en-US'.
        This ensures that if a specific regional translation is not available, the function will try
        the parent language, and finally fall back to en-US.
 
        The function includes comprehensive mappings for:
        - English variants (en-AU, en-GB, en-CA, etc.)
        - Chinese variants (simplified and traditional)
        - French, Spanish, German, Italian variants
        - Japanese, Korean, Arabic, and many other languages
    .PARAMETER CultureName
        The culture name to resolve (e.g., 'en-US', 'fr-CA', 'zh-CN').
    .OUTPUTS
        System.String[]
        Returns an array of culture names in fallback order.
    .EXAMPLE
        Resolve-Culture -CultureName 'fr-CA'
 
        Returns: @('fr-CA', 'fr-FR', 'en-US')
    .EXAMPLE
        Resolve-Culture -CultureName 'en-AU'
 
        Returns: @('en-AU', 'en-GB', 'en-US')
    .EXAMPLE
        Resolve-Culture -CultureName 'es-MX'
 
        Returns: @('es-MX', 'es-ES', 'en-US')
    .NOTES
        This is a private function used internally by Initialize-LocalizedData.
        Supports over 60 culture variants with intelligent fallback chains.
        If a culture is not explicitly mapped, the function will attempt to fall back to
        the parent language family (e.g., 'de-XX' -> 'de-DE' -> 'en-US').
    #>

    param([string]$CultureName)

    # Define culture mapping with fallback chain
    $CultureMappings = @{
        # English variants
        'en-AU' = @('en-AU', 'en-GB', 'en-US')
        'en-CA' = @('en-CA', 'en-GB', 'en-US')
        'en-IE' = @('en-IE', 'en-GB', 'en-US')
        'en-IN' = @('en-IN', 'en-GB', 'en-US')
        'en-NZ' = @('en-NZ', 'en-GB', 'en-US')
        'en-SG' = @('en-SG', 'en-GB', 'en-US')
        'en-ZA' = @('en-ZA', 'en-GB', 'en-US')
        'en-GB' = @('en-GB', 'en-US')
        'en-US' = @('en-US')

        # Chinese variants
        'zh-CN' = @('zh-CN', 'zh-Hans', 'en-US')     # Simplified Chinese (China)
        'zh-TW' = @('zh-TW', 'zh-Hant', 'en-US')     # Traditional Chinese (Taiwan)
        'zh-HK' = @('zh-HK', 'zh-Hant', 'zh-TW', 'en-US')  # Chinese (Hong Kong)
        'zh-SG' = @('zh-SG', 'zh-Hans', 'zh-CN', 'en-US')  # Chinese (Singapore)
        'zh-MO' = @('zh-MO', 'zh-Hant', 'zh-HK', 'en-US')  # Chinese (Macau)
        'zh-Hans' = @('zh-Hans', 'zh-CN', 'en-US')   # Simplified Chinese
        'zh-Hant' = @('zh-Hant', 'zh-TW', 'en-US')   # Traditional Chinese

        # French variants
        'fr-FR' = @('fr-FR', 'en-US')                # French (France)
        'fr-CA' = @('fr-CA', 'fr-FR', 'en-US')       # French (Canada)
        'fr-BE' = @('fr-BE', 'fr-FR', 'en-US')       # French (Belgium)
        'fr-CH' = @('fr-CH', 'fr-FR', 'en-US')       # French (Switzerland)
        'fr-LU' = @('fr-LU', 'fr-FR', 'en-US')       # French (Luxembourg)
        'fr-MC' = @('fr-MC', 'fr-FR', 'en-US')       # French (Monaco)

        # Spanish variants
        'es-ES' = @('es-ES', 'en-US')                # Spanish (Spain)
        'es-MX' = @('es-MX', 'es-ES', 'en-US')       # Spanish (Mexico)
        'es-AR' = @('es-AR', 'es-ES', 'en-US')       # Spanish (Argentina)
        'es-CL' = @('es-CL', 'es-ES', 'en-US')       # Spanish (Chile)
        'es-CO' = @('es-CO', 'es-ES', 'en-US')       # Spanish (Colombia)
        'es-PE' = @('es-PE', 'es-ES', 'en-US')       # Spanish (Peru)
        'es-VE' = @('es-VE', 'es-ES', 'en-US')       # Spanish (Venezuela)
        'es-UY' = @('es-UY', 'es-AR', 'es-ES', 'en-US')  # Spanish (Uruguay)
        'es-PY' = @('es-PY', 'es-AR', 'es-ES', 'en-US')  # Spanish (Paraguay)
        'es-BO' = @('es-BO', 'es-ES', 'en-US')       # Spanish (Bolivia)
        'es-EC' = @('es-EC', 'es-ES', 'en-US')       # Spanish (Ecuador)
        'es-US' = @('es-US', 'es-MX', 'es-ES', 'en-US')  # Spanish (United States)

        # German variants
        'de-DE' = @('de-DE', 'en-US')                # German (Germany)
        'de-AT' = @('de-AT', 'de-DE', 'en-US')       # German (Austria)
        'de-CH' = @('de-CH', 'de-DE', 'en-US')       # German (Switzerland)
        'de-LI' = @('de-LI', 'de-CH', 'de-DE', 'en-US')  # German (Liechtenstein)
        'de-LU' = @('de-LU', 'de-DE', 'en-US')       # German (Luxembourg)

        # Italian variants
        'it-IT' = @('it-IT', 'en-US')                # Italian (Italy)
        'it-CH' = @('it-CH', 'it-IT', 'en-US')       # Italian (Switzerland)
        'it-SM' = @('it-SM', 'it-IT', 'en-US')       # Italian (San Marino)
        'it-VA' = @('it-VA', 'it-IT', 'en-US')       # Italian (Vatican)

        # Japanese variants
        'ja-JP' = @('ja-JP', 'en-US')                # Japanese (Japan)

        # Portuguese variants
        'pt-PT' = @('pt-PT', 'en-US')                # Portuguese (Portugal)
        'pt-BR' = @('pt-BR', 'pt-PT', 'en-US')       # Portuguese (Brazil)
        'pt-AO' = @('pt-AO', 'pt-PT', 'en-US')       # Portuguese (Angola)
        'pt-MZ' = @('pt-MZ', 'pt-PT', 'en-US')       # Portuguese (Mozambique)

        # Russian variants
        'ru-RU' = @('ru-RU', 'en-US')                # Russian (Russia)
        'ru-BY' = @('ru-BY', 'ru-RU', 'en-US')       # Russian (Belarus)
        'ru-KZ' = @('ru-KZ', 'ru-RU', 'en-US')       # Russian (Kazakhstan)

        # Arabic variants
        'ar-SA' = @('ar-SA', 'en-US')                # Arabic (Saudi Arabia)
        'ar-EG' = @('ar-EG', 'ar-SA', 'en-US')       # Arabic (Egypt)
        'ar-AE' = @('ar-AE', 'ar-SA', 'en-US')       # Arabic (UAE)
        'ar-JO' = @('ar-JO', 'ar-SA', 'en-US')       # Arabic (Jordan)
        'ar-LB' = @('ar-LB', 'ar-SA', 'en-US')       # Arabic (Lebanon)

        # Korean variants
        'ko-KR' = @('ko-KR', 'en-US')                # Korean (South Korea)
        'ko-KP' = @('ko-KP', 'ko-KR', 'en-US')       # Korean (North Korea)

        # Dutch variants
        'nl-NL' = @('nl-NL', 'en-US')                # Dutch (Netherlands)
        'nl-BE' = @('nl-BE', 'nl-NL', 'en-US')       # Dutch (Belgium)

        # Swedish variants
        'sv-SE' = @('sv-SE', 'en-US')                # Swedish (Sweden)
        'sv-FI' = @('sv-FI', 'sv-SE', 'en-US')       # Swedish (Finland)

        # Norwegian variants
        'nb-NO' = @('nb-NO', 'en-US')                # Norwegian Bokmål
        'nn-NO' = @('nn-NO', 'nb-NO', 'en-US')       # Norwegian Nynorsk
        'no-NO' = @('no-NO', 'nb-NO', 'en-US')       # Norwegian (legacy)

        # Danish variants
        'da-DK' = @('da-DK', 'en-US')                # Danish (Denmark)
        'da-GL' = @('da-GL', 'da-DK', 'en-US')       # Danish (Greenland)

        # Finnish variants
        'fi-FI' = @('fi-FI', 'en-US')                # Finnish (Finland)

        # Polish variants
        'pl-PL' = @('pl-PL', 'en-US')                # Polish (Poland)

        # Czech variants
        'cs-CZ' = @('cs-CZ', 'en-US')                # Czech (Czech Republic)

        # Hungarian variants
        'hu-HU' = @('hu-HU', 'en-US')                # Hungarian (Hungary)

        # Turkish variants
        'tr-TR' = @('tr-TR', 'en-US')                # Turkish (Turkey)
        'tr-CY' = @('tr-CY', 'tr-TR', 'en-US')       # Turkish (Cyprus)

        # Greek variants
        'el-GR' = @('el-GR', 'en-US')                # Greek (Greece)
        'el-CY' = @('el-CY', 'el-GR', 'en-US')       # Greek (Cyprus)

        # Hebrew variants
        'he-IL' = @('he-IL', 'en-US')                # Hebrew (Israel)

        # Hindi variants
        'hi-IN' = @('hi-IN', 'en-US')                # Hindi (India)

        # Thai variants
        'th-TH' = @('th-TH', 'en-US')                # Thai (Thailand)

        # Vietnamese variants
        'vi-VN' = @('vi-VN', 'en-US')                # Vietnamese (Vietnam)
    }

    # Automatic fallback for language families not explicitly mapped
    $LanguageCode = $CultureName.Split('-')[0]
    $LanguageFallbacks = @{
        'en' = @('en-GB', 'en-US')
        'zh' = @('zh-CN', 'zh-Hans', 'en-US')
        'fr' = @('fr-FR', 'en-US')
        'es' = @('es-ES', 'en-US')
        'de' = @('de-DE', 'en-US')
        'it' = @('it-IT', 'en-US')
        'ja' = @('ja-JP', 'en-US')
        'pt' = @('pt-PT', 'en-US')
        'ru' = @('ru-RU', 'en-US')
        'ar' = @('ar-SA', 'en-US')
        'ko' = @('ko-KR', 'en-US')
        'nl' = @('nl-NL', 'en-US')
        'sv' = @('sv-SE', 'en-US')
        'no' = @('nb-NO', 'en-US')
        'nb' = @('nb-NO', 'en-US')
        'nn' = @('nn-NO', 'nb-NO', 'en-US')
        'da' = @('da-DK', 'en-US')
        'fi' = @('fi-FI', 'en-US')
        'pl' = @('pl-PL', 'en-US')
        'cs' = @('cs-CZ', 'en-US')
        'hu' = @('hu-HU', 'en-US')
        'tr' = @('tr-TR', 'en-US')
        'el' = @('el-GR', 'en-US')
        'he' = @('he-IL', 'en-US')
        'hi' = @('hi-IN', 'en-US')
        'th' = @('th-TH', 'en-US')
        'vi' = @('vi-VN', 'en-US')
    }

    # Return explicit mapping if exists
    if ($CultureMappings.ContainsKey($CultureName)) {
        return $CultureMappings[$CultureName]
    }

    # Return language family fallback if exists
    if ($LanguageFallbacks.ContainsKey($LanguageCode)) {
        return @($CultureName) + $LanguageFallbacks[$LanguageCode]
    }

    # Final fallback
    return @($CultureName, 'en-US')
}