Modules/businessdev.ALbuild.Apps/Public/Merge-BcTranslationMemory.ps1
|
function Merge-BcTranslationMemory { <# .SYNOPSIS Fills untranslated units in a target XLIFF from a translation-memory index (deterministic, exact). .DESCRIPTION For each unit still in a needs-translation / needs-adaptation state (or with an empty target), looks up the index built by Import-BcTranslationMemory: first the context-sensitive "GenNote|source" key, then the plain "source" key. On an exact match it writes the <target>, sets state="translated", and records the origin in an auditable note (<note from-tool='albuild-tm'> origin=<self|app:...|bc-base></note>). Units without a match are left untouched (needs-translation) so a downstream consumer can translate the true new strings. No fuzzy matching, no LLM. .PARAMETER Path The target-language XLIFF file to fill (written in place unless -WhatIf). .PARAMETER Index The index object returned by Import-BcTranslationMemory. .OUTPUTS PSCustomObject { filled, skipped, byOrigin (hashtable origin -> count), file }. #> [CmdletBinding(SupportsShouldProcess)] [OutputType([PSCustomObject])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter(Mandatory)] [PSCustomObject] $Index ) if (-not (Test-Path -LiteralPath $Path)) { throw "XLIFF file not found: '$Path'." } $entries = $Index.Entries $contextAware = [bool]$Index.ContextAware [xml] $doc = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 $filled = 0; $skipped = 0 $byOrigin = @{} foreach ($u in (Get-BcXliffUnit -Document $doc)) { $needs = [string]::IsNullOrWhiteSpace($u.Target) -or ($u.TargetState -in @('needs-translation', 'needs-adaptation')) if (-not $needs) { continue } if ([string]::IsNullOrEmpty($u.Source)) { $skipped++; continue } $hit = $null if ($contextAware -and $u.GenNote -and $entries.ContainsKey("$($u.GenNote)|$($u.Source)")) { $hit = $entries["$($u.GenNote)|$($u.Source)"] } elseif ($entries.ContainsKey("$($u.Source)")) { $hit = $entries["$($u.Source)"] } if (-not $hit) { $skipped++; continue } # Write target + state="translated" on <target>. $targetNode = $u.TargetNode if (-not $targetNode) { $ns = $u.SourceNode.NamespaceURI $targetNode = if ($ns) { $doc.CreateElement('target', $ns) } else { $doc.CreateElement('target') } $null = $u.SourceNode.ParentNode.InsertAfter($targetNode, $u.SourceNode) } $targetNode.InnerText = $hit.Target $stateAttr = $targetNode.Attributes | Where-Object { $_.Name -ieq 'state' } | Select-Object -First 1 if (-not $stateAttr) { $stateAttr = $targetNode.SetAttributeNode($doc.CreateAttribute('state')) } $stateAttr.Value = 'translated' # Auditable origin note (replace any prior albuild-tm note on this unit). $ns = $u.UnitNode.NamespaceURI foreach ($n in @($u.UnitNode.SelectNodes(".//*[local-name()='note']"))) { $fromTool = $null if ($n.Attributes) { foreach ($a in $n.Attributes) { if ($a.Name -ieq 'from-tool') { $fromTool = $a.Value } } } if ($fromTool -eq 'albuild-tm') { $null = $n.ParentNode.RemoveChild($n) } } $note = if ($ns) { $doc.CreateElement('note', $ns) } else { $doc.CreateElement('note') } $fa = $note.SetAttributeNode($doc.CreateAttribute('from-tool')); $fa.Value = 'albuild-tm' $note.InnerText = "origin=$($hit.Origin)" $null = $u.UnitNode.AppendChild($note) $filled++ $o = [string]$hit.Origin if ($byOrigin.ContainsKey($o)) { $byOrigin[$o]++ } else { $byOrigin[$o] = 1 } } if ($filled -gt 0 -and $PSCmdlet.ShouldProcess($Path, "Fill $filled unit(s) from translation memory")) { $doc.Save($Path) } Write-ALbuildLog -Level Success "Translation memory: filled $filled, $skipped still need translation in '$(Split-Path -Leaf $Path)'." return [PSCustomObject]@{ filled = $filled; skipped = $skipped; byOrigin = $byOrigin; file = (Split-Path -Leaf $Path) } } |