Modules/businessdev.ALbuild.Apps/Public/Test-BcTranslation.ps1
|
function Test-BcTranslation { <# .SYNOPSIS Checks AL XLIFF translation files for missing and "needs-work" translations. .DESCRIPTION A CI gate for translations. For each XLIFF file it reports units with a missing translation (no/empty target, the configured missing placeholder, or a needs-translation/-adaptation state) and units that fail the technical needs-work rules (placeholder mismatch, option member count, consecutive spaces, source=target for same-language files). Use -FailOnIssue to fail the build when any issue is found. .PARAMETER Path One or more XLIFF files, folders or wildcards. The generated base file (*.g.xlf) is skipped. .PARAMETER MissingPlaceholder Text that marks an untranslated target (e.g. '[NAB: NOT TRANSLATED]'). Default: none. .PARAMETER SkipNeedsWork Only check for missing translations, not the needs-work rules. .PARAMETER FailOnIssue Throw if any issue is found (for CI). .EXAMPLE Test-BcTranslation -Path ./app/Translations -FailOnIssue .OUTPUTS PSCustomObject per issue (File, UnitId, Type, Message). #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [string[]] $Path, [string] $MissingPlaceholder = '', [switch] $SkipNeedsWork, [switch] $FailOnIssue ) begin { $allIssues = [System.Collections.Generic.List[object]]::new() } process { $files = foreach ($p in $Path) { if (Test-Path -LiteralPath $p -PathType Container) { Get-ChildItem -LiteralPath $p -Filter '*.xlf' -Recurse -File } elseif (Test-Path -LiteralPath $p) { Get-Item -LiteralPath $p } else { Get-ChildItem -Path $p -File -ErrorAction SilentlyContinue } } foreach ($file in ($files | Where-Object { $_.Name -notlike '*.g.xlf' })) { [xml] $doc = Get-Content -LiteralPath $file.FullName -Raw -Encoding UTF8 $fileNode = $doc.SelectSingleNode("//*[local-name()='file']") $sourceLang = '' $targetLang = '' if ($fileNode -and $fileNode.Attributes) { foreach ($a in $fileNode.Attributes) { if ($a.Name -ieq 'source-language' -or $a.Name -ieq 'srcLang') { $sourceLang = $a.Value } if ($a.Name -ieq 'target-language' -or $a.Name -ieq 'trgLang') { $targetLang = $a.Value } } } $sameLanguage = $sourceLang -and $targetLang -and ($sourceLang -ieq $targetLang) foreach ($unit in (Get-BcXliffUnit -Document $doc)) { $missingTarget = [string]::IsNullOrWhiteSpace($unit.Target) $isPlaceholder = $MissingPlaceholder -and ($unit.Target -eq $MissingPlaceholder) $needsState = $unit.TargetState -in @('needs-translation', 'needs-adaptation') $isMissing = $missingTarget -or $isPlaceholder -or $needsState if ($isMissing) { $allIssues.Add([PSCustomObject]@{ File = $file.Name; UnitId = $unit.Id; Type = 'Missing'; Message = 'Missing or incomplete translation.' }) continue } if (-not $SkipNeedsWork) { foreach ($problem in (Test-BcXliffNeedsWork -Source $unit.Source -Target $unit.Target -SameLanguage:$sameLanguage)) { $allIssues.Add([PSCustomObject]@{ File = $file.Name; UnitId = $unit.Id; Type = 'NeedsWork'; Message = $problem }) } } } } } end { foreach ($issue in $allIssues) { $issue } if ($allIssues.Count -gt 0) { Write-ALbuildLog -Level Warning "Found $($allIssues.Count) translation issue(s)." if ($FailOnIssue) { throw "Translation check failed with $($allIssues.Count) issue(s)." } } else { Write-ALbuildLog -Level Success 'No translation issues found.' } } } |