Translation/Translate-BCDevXliff.ps1

Function Translate-BCDevXliff() {
    Param(
        [Parameter(Position = 0, Mandatory = $true)] [String]$SourceFile,
        [Parameter(Position = 1, Mandatory = $true)] [String]$TargetLanguage,
        [Parameter(Mandatory = $false)] [ValidateSet('bricklead', 'primus-by-bricklead', 'primus-soft')][string]$DevOpsSignature = 'bricklead',
        [switch]$Summary
    )

    if (-not (Test-Path $SourceFile)) {
        throw "Xliff File $SourceFile not found."
    }
    if ($TargetLanguage -notmatch '^[a-z]{2}(-[A-Z]{2})?$') {
        throw "Language $TargetLanguage is not valid. Use format xx-XX."
    }
    if ($SourceFile -notmatch '\.g\.xlf$') {
        throw "Xliff File $SourceFile is not a .g.xlf file."
    }
    Write-Verbose "Translate Xliff File: $SourceFile to $TargetLanguage."

    $DestXliffFile = $SourceFile.Replace('.g.xlf', ".g.$TargetLanguage.xlf")
    $ShortLanguage = $TargetLanguage.split('-')[0]

    $WorkDirectory = Split-Path -Path $SourceFile -Parent

    # Get Microsoft Dictionary
    try {
        $DictionaryFile = (Get-BCDevRef -Type DictionaryURLBase) + "/microsoft/bc-dictionary.$ShortLanguage.json"
        Get-ToolFile -SourceURl $DictionaryFile.ToLowerInvariant() -destination "$WorkDirectory\dictionary.json"
        $Dictionary = Read-BCDevDictionary -DictionaryFile "$WorkDirectory\dictionary.json"
        Remove-Item "$WorkDirectory\dictionary.json" -Force
    }
    catch {
        $Dictionary = @{}
    }
    Write-Verbose "Microsoft Dictionary: $($Dictionary.keys.Count)"
    # Get Signature Dictionary
    try {
        $DictionaryFile = (Get-BCDevRef -Type DictionaryURLBase) + "/$DevOpsSignature/$DevOpsSignature-dictionary.$ShortLanguage.json"
        Get-ToolFile -SourceURl $DictionaryFile.ToLowerInvariant() -destination "$WorkDirectory\dictionary.json"
        $Signature = Read-BCDevDictionary -DictionaryFile "$WorkDirectory\dictionary.json"
        Remove-Item "$WorkDirectory\dictionary.json" -Force
    }
    catch {
        $Signature = @{}
    }
    Write-Verbose "Signature Dictionary: $($Signature.keys.Count)"
    # Get Global Dictionary
    try {
        $DictionaryFile = (Get-BCDevRef -Type DictionaryURLBase) + "/global/global-dictionary.$ShortLanguage.json"
        Get-ToolFile -SourceURl $DictionaryFile.ToLowerInvariant() -destination "$WorkDirectory\dictionary.json"
        $Global = Read-BCDevDictionary -DictionaryFile "$WorkDirectory\dictionary.json"
        Remove-Item "$WorkDirectory\dictionary.json" -Force
    }
    catch {
        $Global = @{}
    }
    Write-Verbose "Global Dictionary: $($Global.keys.Count)"
    # Get Memory Dictionary
    if (test-Path $DestXliffFile) {
        Convert-BCDevXliffToDictionary -XliffFile $DestXliffFile -DictionaryFile "$DestXliffFile.json" -OnlyFinal
        $Memory = Read-BCDevDictionary -DictionaryFile "$DestXliffFile.json"
        Remove-Item "$DestXliffFile.json" -Force
        Remove-Item $DestXliffFile -Force
    }
    else {
        $Memory = @{}
    }
    Write-Verbose "Memory Dictionary : $($Memory.keys.Count)"

    # Initialize
    $FinalCpt = 0
    $DictionaryCpt = 0
    $GlobalCpt = 0
    $SignatureCpt = 0
    $AzureTranslationCpt = 0
    $NeedsReviewCpt = 0
    $TranslatorUrl = (Get-BCDevRef -Type TranslatorURL) + $ShortLanguage.ToLowerInvariant()
    # PT exception
    if ($ShortLanguage -eq "pt") {
        $TranslatorUrl += "-pt"
    }
    $headers = Get-BCDevRef -Type TranslatorHeader

    # Read Xliff
    [xml]$xlf = Get-Content $SourceFile
    $ns = New-Object System.Xml.XmlNamespaceManager($xlf.NameTable)
    $ns.AddNamespace("x", $xlf.DocumentElement.NamespaceURI)
    $sources = $xlf.SelectNodes("//x:source", $ns)
    Write-Verbose "Xliff Sources to migrate: $($sources.Count)"

    $xlf.xliff.file.'target-language' = $TargetLanguage

    # Translation Loop
    for ($i = 0; $i -le ($sources.Count - 1); $i++) {
        $target = $xlf.CreateElement("target")

        $Text = $sources[$i].InnerText
        switch ($true) {
            { ($Text -eq '') -or ($Text -eq ' ') } {
                $target.InnerText = $Text
                $target.SetAttribute("state", "final")
                $target.SetAttribute("state-qualifier", "memory")
                $FinalCpt += 1
                break
            }
            { $Memory.ContainsKey($Text) } {
                $target.InnerText = $Memory[$Text]
                $target.SetAttribute("state", "final")
                $target.SetAttribute("state-qualifier", "memory")
                $FinalCpt += 1
                break
            }
            { $Signature.ContainsKey($Text) } {
                $target.InnerText = $Signature[$Text]
                $target.SetAttribute("state", "translated")
                $target.SetAttribute("state-qualifier", $DevOpsSignature)
                $SignatureCpt += 1
                break
            }
            { $Dictionary.ContainsKey($Text) } {
                $target.InnerText = $Dictionary[$Text]
                $target.SetAttribute("state", "translated")
                $target.SetAttribute("state-qualifier", "microsoft")
                $DictionaryCpt += 1
                break
            }
            { $Global.ContainsKey($Text) } {
                $target.InnerText = $Global[$Text]
                $target.SetAttribute("state", "needs-review-translation")
                $target.SetAttribute("state-qualifier", "tm-suggestion")
                $NeedsReviewCpt += 1
                $GlobalCpt += 1
                break
            }
            default {
                $body = '[' + (@( @{ Text = $Text } ) | ConvertTo-Json -Compress) + ']'
                try {
                    $response = Invoke-RestMethod -Method Post -Uri $TranslatorUrl -Headers $headers -Body $body
                    $NewText = $response[0].translations[0].text
                    $AzureTranslationCpt += 1
                    $Global.add($Text, $NewText)
                }
                catch {
                    $NewText = $Text
                }
                    
                $target.InnerText = $NewText
                $target.SetAttribute("state", "needs-review-translation")
                $target.SetAttribute("state-qualifier", "tm-suggestion")
                $NeedsReviewCpt += 1
            }
        }
            
        $sources[$i].ParentNode.InsertAfter($target, $sources[$i]) | Out-Null
    }

    # Write Translate File
    $settings = New-Object System.Xml.XmlWriterSettings
    $settings.Indent = $true                      # Active l'indentation
    $settings.NewLineHandling = "Replace"
    $settings.NewLineOnAttributes = $false        # Chaque élément sur sa ligne
    $settings.NewLineChars = "`r`n"                 # Saut de ligne = LF
    $settings.Encoding = [System.Text.Encoding]::UTF8

    $writer = [System.Xml.XmlWriter]::Create($DestXliffFile, $settings)
    $xlf.Save($writer)
    $writer.Close()

    # Fix content
    $tempFile = Get-Content $DestXliffFile -Encoding UTF8
    $tempFile = $tempFile.replace(' xmlns=""', '')
    $tempFile = $tempFile.replace('source><target state', "source>`r`n <target state")
    $tempFile | Set-Content $DestXliffFile -Force -Encoding UTF8

    #Update Global Dictionary
    if ($AzureTranslationCpt -gt 0) {        
        $GlobalFile = "$WorkDirectory\global-dictionary.$ShortLanguage.json"
        $Global | ConvertTo-Json | Set-Content $GlobalFile -Encoding UTF8
        Deploy-BCDevDictionary -File $GlobalFile -Type Global
    }

    # Statistics
    Write-Verbose "Summary of translation"
    Write-Verbose "======================"
    Write-Verbose "Memory : $FinalCpt"
    Write-Verbose "Microsoft : $DictionaryCpt"
    Write-Verbose "$DevOpsSignature : $SignatureCpt"
    Write-Verbose "Global : $GlobalCpt"
    Write-Verbose ""
    Write-Verbose "Azure translations : $AzureTranslationCpt"
    Write-Verbose "Needs review translations: $NeedsReviewCpt"

    # Visible Summary
    if ($Summary) {
        Write-Host "Summary of translation $TargetLanguage"
        Write-Host "============================"
        Write-Host "Memory : $FinalCpt"
        Write-Host "Microsoft : $DictionaryCpt"
        Write-Host "$DevOpsSignature : $SignatureCpt"
        Write-Host "Global : $GlobalCpt"
        Write-Host
        Write-Host "Azure translations : $AzureTranslationCpt"
        Write-Host "Needs review translations: $NeedsReviewCpt"
    }
}
Export-ModuleMember -Function Translate-BCDevXliff
# SIG # Begin signature block
# MIIn2wYJKoZIhvcNAQcCoIInzDCCJ8gCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAB9VwRdVZSEMxP
# sEef+wSERV1BIu202Unqd8kj2YO0V6CCIOwwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwgga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0GCSqG
# SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTlaMGkx
# CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4
# RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYg
# MjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHTCphB
# cr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPhof6p
# vF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mAxAHe
# HYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBvMgEd
# gkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps0wjU
# jsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF83bR
# VFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXiUOeS
# LsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOMCZIV
# NSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydPpOjL
# 6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrUG2Zd
# SoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+sdFU
# eEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0TAQH/
# BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYDVR0j
# BBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1Ud
# JQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8E
# PDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
# DQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WIGjB/
# T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+IQhQ
# E7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8Myb9r
# EVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2th9y
# 1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjajV/gx
# dEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2Lr3t
# y9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFzeGxcy
# tL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG7uEB
# YTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+NJpud
# /v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckTetiS
# uEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszWkPZP
# ubdcMIIGvzCCBKegAwIBAgIRAIFOQhehKX/tWszUF/iRrXUwDQYJKoZIhvcNAQEL
# BQAwUzELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExKTAn
# BgNVBAMTIEdsb2JhbFNpZ24gQ29kZSBTaWduaW5nIFJvb3QgUjQ1MB4XDTI0MDYx
# OTAzMjUxMVoXDTM4MDcyODAwMDAwMFowWTELMAkGA1UEBhMCQkUxGTAXBgNVBAoT
# EEdsb2JhbFNpZ24gbnYtc2ExLzAtBgNVBAMTJkdsb2JhbFNpZ24gR0NDIFI0NSBD
# b2RlU2lnbmluZyBDQSAyMDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
# AgEA1kJN+eNPxiP0bB2BpjD3SD3P0OWN5SAilgdENV0Gzw8dcGDmJlT6UyNgAqhf
# AgL3jsluPal4Bb2O9U8ZJJl8zxEWmx97a9Kje2hld6vYsSw/03IGMlxbrFBnLCVN
# VgY2/MFiTH19hhaVml1UulDQsH+iRBnp1m5sPhPCnxHUXzRbUWgxYwr4W9Deullf
# Ma+JaDhAPgjoU2dOY7Yhju/djYVBVZ4cvDfclaDEcacfG6VJbgogWX6Jo1gVlwAl
# ad/ewmpQZU5T+2uhnxgeig5fVF694FvP8gwE0t4IoRAm97Lzei7CjpbBP86l2vRZ
# KIw3ZaExlguOpHZ3FUmEZoIl50MKd1KxmVFC/6Gy3ZzS3BjZwYapQB1Bl2KGvKj/
# osdjFwb9Zno2lAEgiXgfkPR7qVJOak9UBiqAr57HUEL6ZQrjAfSxbqwOqOOBGag4
# yJ4DKIakdKdHlX5yWip7FWocxGnmsL5AGZnL0n1VTiKcEOChW8OzLnqLxN7xSx+M
# KHkwRX9sE7Y9LP8tSooq7CgPLcrUnJiKSm1aNiwv37rL4kFKCHcYiK01YZQS86Ry
# 6+42nqdRJ5E896IazPyH5ZfhUYdp6SLMg8C3D0VsB+FDT9SMSs7PY7G1pBB6+Q0M
# KLBrNP4haCdv7Pj6JoRbdULNiSZ5WZ1rq2NxYpAlDQgg8f8CAwEAAaOCAYYwggGC
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8E
# CDAGAQH/AgEAMB0GA1UdDgQWBBTas43AJJCja3fTDKBZ3SFnZHYLeDAfBgNVHSME
# GDAWgBQfAL9GgAr8eDm3pbRD2VZQu86WOzCBkwYIKwYBBQUHAQEEgYYwgYMwOQYI
# KwYBBQUHMAGGLWh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29tL2NvZGVzaWduaW5n
# cm9vdHI0NTBGBggrBgEFBQcwAoY6aHR0cDovL3NlY3VyZS5nbG9iYWxzaWduLmNv
# bS9jYWNlcnQvY29kZXNpZ25pbmdyb290cjQ1LmNydDBBBgNVHR8EOjA4MDagNKAy
# hjBodHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL2NvZGVzaWduaW5ncm9vdHI0NS5j
# cmwwLgYDVR0gBCcwJTAIBgZngQwBBAEwCwYJKwYBBAGgMgEyMAwGCisGAQQBoDIK
# BAIwDQYJKoZIhvcNAQELBQADggIBADIQ5LwXpYMQQJ3Tqf0nz0VyqcUfSzNZbywy
# MXlxhNY2Z9WrdPzU8gY6brXWy/FCg5a9fd6VLBrtauNBHKbIiTHCWWyJvCojA1lQ
# R0n9b1MOKijMSFTv8yMYW5I2TryjY9TD+wAPgNEgwsrllrrwmluqpCV6Gdv623tT
# T/m2o9lj1XVfAaUo27YYKRRleZzbtOuImBRTUGAxDGazUeNuySkmZPAU0XN4xISN
# PhSlklmreUFG6jTPgXZGOpF4GXO+/gb118GEOaBwTAo1AF7YKjAkHzJ3tuF837NG
# QeH6bY3j4wufL0DZpToNZMm+jNEayWUgOuIA+k56ITdBcJmdUB+Ze3WQdHNNRaVO
# WH/ddmqQWIlmk2Sj/lT3Tarr5SDuddeIsh0MPLyhkqBW5Ef8Zw/qeCnfj6PH2eMx
# eKcLKZRrHCddISeH4qPvyECQLlwXKCXTAUQXq4DafJSoWyP8IJ6bkaGQ/7MN5XJE
# LEcV89SRcib58gXjAWf3abXeBbb+KJCMf6EpO7cs2mQiaZbE9NNXDSqFxrtoaKyL
# 8VJLZG6quLfsTRQc+qgUOM7sJevkYt01+bh7B10bQ2cCCGs9vyUjg4GWcwfu/lha
# PDfaoNtf0pw6RpKcxCYcCTDaJeQOHZBz1B6HTmmEgZHNZX7nNfqDgGrTNB1Gp3gI
# pngyJWZ6MIIG6zCCBNOgAwIBAgIMNY0DQ6d8EhhRyrkRMA0GCSqGSIb3DQEBCwUA
# MFkxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMS8wLQYD
# VQQDEyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0EgMjAyMDAeFw0y
# NTEwMjIxMDQyMjlaFw0yODExMjYwODE1NTlaMFkxCzAJBgNVBAYTAkZSMREwDwYD
# VQQIEwhNb3JiaWhhbjEPMA0GA1UEBxMGVmFubmVzMRIwEAYDVQQKEwlCUklDS0xF
# QUQxEjAQBgNVBAMTCUJSSUNLTEVBRDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBANH89CNXVaQCYu+kwBql0qfG+mWCRiSqIDm3iUF0uI/stq3PhOE8raxs
# xH48emVu5EYVNAQ4UqjA7hCNw+qVChf0PQdJaHbyXNDroEihVT73VwMfL+8FcxjW
# 6sXDmtp4NJJtQHUD3CQpIz74FE/2MydGQ5ZyM+IzXZdqvCmSeoMI5PWnhJHUfHTM
# rI4t4xOwacRCP1sl6FtIZ0vT/hl7BO3lFonKuKb5RL+0uxAoKnjLxbisNK5uRqN4
# A6Gg5swExkG1wqeZBgw2Ay+C4g4J/m21YO7FWkfJposKvwnS+Im+qLljm3wwnu1G
# Bxo9i8bODiDQaG+xfda0huvffYGeSKm4t1y0Cgoy1PkxwxATGEQAvDERT0K1JmVn
# VQO4SaZ0nOR/AFydlCvzsHzWN5KfX3pn4S3+OyVEHyjS7u2FYLgeuw9Vkj3IimWn
# fp7CpZPO9Ild6DkeNcC/fABHHxJ5hSnt6c2D1ilpnAaTdUwnlEqydxQilOCQCY5V
# 54xF+5rV9M6bSnwVMqRUI24Dys8Q2brf+LMoqOkF2qDLTfjDaHEAc2fNMVwtEjyC
# oHninq8aEOwSjAdNYoM4N0e+FfBvhWDGG7eJ/BYap6i5m/+kX2uRGG/fDm16RP4A
# CO76ZrQWoAYuYSfBe31thpSQirSljx+KeyIw01PwZDBJtOA07G3tAgMBAAGjggGx
# MIIBrTAOBgNVHQ8BAf8EBAMCB4AwgZsGCCsGAQUFBwEBBIGOMIGLMEoGCCsGAQUF
# BzAChj5odHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC9nc2djY3I0
# NWNvZGVzaWduY2EyMDIwLmNydDA9BggrBgEFBQcwAYYxaHR0cDovL29jc3AuZ2xv
# YmFsc2lnbi5jb20vZ3NnY2NyNDVjb2Rlc2lnbmNhMjAyMDBWBgNVHSAETzBNMEEG
# CSsGAQQBoDIBMjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu
# LmNvbS9yZXBvc2l0b3J5LzAIBgZngQwBBAEwCQYDVR0TBAIwADBFBgNVHR8EPjA8
# MDqgOKA2hjRodHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL2dzZ2NjcjQ1Y29kZXNp
# Z25jYTIwMjAuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFNqz
# jcAkkKNrd9MMoFndIWdkdgt4MB0GA1UdDgQWBBScz5MAsOazFIMc9PQAOwyeYtJX
# XTANBgkqhkiG9w0BAQsFAAOCAgEAjxOP7JLAHE5dd+OgTzQY/k50WPn9XxxyIzMY
# 1OHJh7qC4CGHk+3N7f/Vp6hwFgNEjgK/EcAbL8BZILFyaRCskMT5yEPh4m+IF8/O
# 1WxUUrItXs7R0VRkh2L4fGaEKgH33W1C5Y9WgLn3Su7zXCyHEuYOFMsEReKheVVc
# sbSle4330aoKp6oiaifjdhr91uMu3Ta1FlG9prlrzVoz/d8EHJu3LAmZzLv53JQ0
# SrjPQrb53KXEOST7opxyCIDZZ090Ji3oThd7TNy+SID7BDkh+ea+HZJTjY/3FuLu
# yFhRxWtIJ6PCMx6a4UDgw2qGcKTlXb71hD7QJJspwWEYftG1hRjf0zMiJk8F4EYb
# 6Xn36uItedM6ETJ4YVisjt79HeYowJQkm/OajZyUrTYs8e9EWkKjO58cSrpFKMgi
# yess0dcvYEz0TZdWTKzhItNtV52r+qtkL7StsLV8JVx1pG/hiHghICrGfaNlYmPk
# a6aDJn/vwJfDvyAgCYCOkIGeFUTbHcBrurdbWR+Ycv4kzeEGsVtRwCCrj8A/Kep5
# T3vTzM3rvXzDqoO5GkO4sfO7fUbyru2EuxNpCHSEPMYARxVKsKF1LSgIJOvoFCC/
# O5j7n9IvnxIPUZohrNBbd9C3jWcm42veAe/zTAKr+Jm2mxhUyi+cJwA3Nb9YJLfI
# hc+McqowggbtMIIE1aADAgECAhAKgO8YS43xBYLRxHanlXRoMA0GCSqGSIb3DQEB
# CwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8G
# A1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBT
# SEEyNTYgMjAyNSBDQTEwHhcNMjUwNjA0MDAwMDAwWhcNMzYwOTAzMjM1OTU5WjBj
# MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMT
# MkRpZ2lDZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFtcCBSZXNwb25kZXIgMjAy
# NSAxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EasLRLGntDqrmBW
# sytXum9R/4ZwCgHfyjfMGUIwYzKomd8U1nH7C8Dr0cVMF3BsfAFI54um8+dnxk36
# +jx0Tb+k+87H9WPxNyFPJIDZHhAqlUPt281mHrBbZHqRK71Em3/hCGC5KyyneqiZ
# 7syvFXJ9A72wzHpkBaMUNg7MOLxI6E9RaUueHTQKWXymOtRwJXcrcTTPPT2V1D/+
# cFllESviH8YjoPFvZSjKs3SKO1QNUdFd2adw44wDcKgH+JRJE5Qg0NP3yiSyi5Mx
# gU6cehGHr7zou1znOM8odbkqoK+lJ25LCHBSai25CFyD23DZgPfDrJJJK77epTwM
# P6eKA0kWa3osAe8fcpK40uhktzUd/Yk0xUvhDU6lvJukx7jphx40DQt82yepyekl
# 4i0r8OEps/FNO4ahfvAk12hE5FVs9HVVWcO5J4dVmVzix4A77p3awLbr89A90/nW
# GjXMGn7FQhmSlIUDy9Z2hSgctaepZTd0ILIUbWuhKuAeNIeWrzHKYueMJtItnj2Q
# +aTyLLKLM0MheP/9w6CtjuuVHJOVoIJ/DtpJRE7Ce7vMRHoRon4CWIvuiNN1Lk9Y
# +xZ66lazs2kKFSTnnkrT3pXWETTJkhd76CIDBbTRofOsNyEhzZtCGmnQigpFHti5
# 8CSmvEyJcAlDVcKacJ+A9/z7eacCAwEAAaOCAZUwggGRMAwGA1UdEwEB/wQCMAAw
# HQYDVR0OBBYEFOQ7/PIx7f391/ORcWMZUEPPYYzoMB8GA1UdIwQYMBaAFO9vU0rp
# 5AZ8esrikFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggr
# BgEFBQcDCDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNI
# QTI1NjIwMjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly9jcmwzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZT
# SEEyNTYyMDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1s
# BwEwDQYJKoZIhvcNAQELBQADggIBAGUqrfEcJwS5rmBB7NEIRJ5jQHIh+OT2Ik/b
# NYulCrVvhREafBYF0RkP2AGr181o2YWPoSHz9iZEN/FPsLSTwVQWo2H62yGBvg7o
# uCODwrx6ULj6hYKqdT8wv2UV+Kbz/3ImZlJ7YXwBD9R0oU62PtgxOao872bOySCI
# LdBghQ/ZLcdC8cbUUO75ZSpbh1oipOhcUT8lD8QAGB9lctZTTOJM3pHfKBAEcxQF
# oHlt2s9sXoxFizTeHihsQyfFg5fxUFEp7W42fNBVN4ueLaceRf9Cq9ec1v5iQMWT
# FQa0xNqItH3CPFTG7aEQJmmrJTV3Qhtfparz+BW60OiMEgV5GWoBy4RVPRwqxv7M
# k0Sy4QHs7v9y69NBqycz0BZwhB9WOfOu/CIJnzkQTwtSSpGGhLdjnQ4eBpjtP+XB
# 3pQCtv4E5UCSDag6+iX8MmB10nfldPF9SVD7weCC3yXZi/uuhqdwkgVxuiMFzGVF
# wYbQsiGnoa9F5AaAyBjFBtXVLcKtapnMG3VH3EmAp/jsJ3FVF3+d1SVDTmjFjLbN
# FZUWMXuZyvgLfgyPehwJVxwC+UpX2MSey2ueIu9THFVkT+um1vshETaWyQo8gmBt
# o/m3acaP9QsuLj3FNwFlTxq25+T4QwX9xa6ILs84ZPvmpovq90K8eWyG2N01c4Ih
# SOxqt81nMYIGRTCCBkECAQEwaTBZMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xv
# YmFsU2lnbiBudi1zYTEvMC0GA1UEAxMmR2xvYmFsU2lnbiBHQ0MgUjQ1IENvZGVT
# aWduaW5nIENBIDIwMjACDDWNA0OnfBIYUcq5ETANBglghkgBZQMEAgEFAKCBhDAY
# BgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3
# AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEi
# BCD2cPTUx9ETkuUXST+Kxk1RnHZivQxDuu5KEenU07s+QDANBgkqhkiG9w0BAQEF
# AASCAgB48cQqytZIC0g+Egk5og2rRFBxGk7cHv9k6aVTJtV+L6OHewiKPLUnKDbz
# K/v/uncgxRX3Vs+KRpGvaKKtfygEbpVZ/DzQNVFxpKNA9gPHJEZKwMdQlujYx2Em
# 36ZeYrCXQThxigvPR8WnJfpX+MLL6cUb5VS9EHNfk0RClnZZsxkWQ+GP9qycmtwM
# VAgNKvRpz7gWDkUxtjUVXCtKbsz61CMhLmoHhMHY9Kudga8GsmC+ptRargcEhpfL
# eRXr//2Qk4oBlWej2G56Ksw5111nIePPE0SOp25loJeMm7s2VPkVKHheUN5sK6ds
# t9aRJT4+VhZkWjswtrXSiel5IZMRDALVYBmA+BHMA6BoD90HE/frXzjgbVeFhXTD
# c9alOPSCtG8oCKcFzhUBa0L1zGwuNzu4BolZAPjGVq3ZZrfIWypm9mN6nkOa8vzg
# zLNolkT24/8zAh0QIVh4zpfETeIcHecOlaQkqBbbNIadB5HpblTKvjKBxQn3EZix
# 0T4a823XDBqt/mHXHH0GXlsoVpXKMjBcgDTbpcz9YL3itMIL3zEARJAqtkxWw3bo
# 9CzIFmDyQlV0Ts7N7fs1kDf/GxpJ7gre2dB4xll4UqXdshf/gBOfNZ5AQ7QqgmqM
# p5lkBiNyEZpecYOeGpOLD4vRYB3Thso+n3NMlBAXJhkOib9I9qGCAyYwggMiBgkq
# hkiG9w0BCQYxggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5E
# aWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1l
# U3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeV
# dGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwG
# CSqGSIb3DQEJBTEPFw0yNTEyMDYwODUyMzBaMC8GCSqGSIb3DQEJBDEiBCANfBQc
# GyDLE2Psme/ptm8WyJUDahr72UE2+XMDQMH7gDANBgkqhkiG9w0BAQEFAASCAgC8
# 93F1xpLJWj6EEPpKRibv/hms/W7Ni0CkOTpLnWozNiRFHffq//Zib8yuZgVGWeK6
# LvmlPBqa39/VoO4Kr3Ui8fF1Jste4idVLERMEIJZeTecfgrWps/RPP/OOn9XvjCF
# eNVeIN58lHw1iueS/xQRAhTqRCMxu/5d7Ww3Fm7HYC+QiuP7qv46cqyKsAX9C5OC
# xCiUUkpVfA76M/U51+5u0Z3HeLt1fExffzvIaagKRl4mIsdF1AQiQSjUy+SBLUCu
# 8qcdgRl4F6ruk/InYg/XDZIQIbMxZZ6FUr3bSPayHbfM6RAL4tXhbw8pPZkUlDAu
# 8jh9zvB4rmqa4T2CP3vYUlxRn4ZKspX+wmVVoeCk0xrrikSLgVo1e+wUq93E6kmU
# 7dkofLZtsNneCIiheZPsOz8pVMgonrTjpXX4IA15DR3iydfVZ7cp0GrNu1KyNRc+
# h8R8r8zJmEnVrM88KBU2nJG++OVO3CJZornsbhtSVqU3ZOOeQuFGbG6U+D5ryGYw
# 9gBd25p+zCUigXm80UlFlBMQmJrPX84EpcFRacRoX57VP3oFkyDHNh5dKhSBF0gD
# ufEWQ8YgTm3pYYYd/icV+bwvy0dn5QyLXUJ4kn14kuXGdwJhR0+7NHQp6nOygwCy
# EPQV7xyJuuviLEKn3p+PHYFmFhUraws5J4CVHhnc7A==
# SIG # End signature block