Modules/businessdev.ALbuild.Apps/Public/Set-BcTranslationUnit.ps1

function Set-BcTranslationUnit {
    <#
    .SYNOPSIS
        Sets the target text (and state) of a single translation unit in an AL XLIFF file.
 
    .DESCRIPTION
        The supported, auditable single-unit write path behind `albuild translation set` - no raw XML
        editing. Selects the unit by id or by exact source text, writes the <target>, and sets its state:
        'translated' (a completed translation) or 'needs-work' (mapped to the XLIFF 'needs-adaptation'
        state). Namespace-agnostic (XLIFF 1.2/2.0) via the shared Get-BcXliffUnit reader.
 
    .PARAMETER Path
        The target-language XLIFF file to edit.
 
    .PARAMETER Id
        The trans-unit id to set. Either -Id or -Source is required.
 
    .PARAMETER Source
        The exact source text to match. Either -Id or -Source is required.
 
    .PARAMETER Target
        The translated text to write into <target>.
 
    .PARAMETER State
        'translated' (default) or 'needs-work'.
 
    .OUTPUTS
        PSCustomObject { id, source, target, state, file }.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path,
        [string] $Id,
        [string] $Source,
        [Parameter(Mandatory)] [AllowEmptyString()] [string] $Target,
        [ValidateSet('translated', 'needs-work')] [string] $State = 'translated'
    )

    if (-not (Test-Path -LiteralPath $Path)) { throw "XLIFF file not found: '$Path'." }
    if ([string]::IsNullOrEmpty($Id) -and [string]::IsNullOrEmpty($Source)) {
        throw 'Specify -Id or -Source to select the translation unit.'
    }

    [xml] $doc = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
    $units = @(Get-BcXliffUnit -Document $doc)
    $match = if ($Id) { $units | Where-Object { $_.Id -eq $Id } | Select-Object -First 1 }
    else { $units | Where-Object { $_.Source -eq $Source } | Select-Object -First 1 }
    if (-not $match) {
        $sel = if ($Id) { "id '$Id'" } else { "source '$Source'" }
        throw "No translation unit found for $sel in '$Path'."
    }

    $sourceNode = $match.SourceNode
    $targetNode = $match.TargetNode
    if (-not $targetNode) {
        $ns = $sourceNode.NamespaceURI
        $targetNode = if ($ns) { $doc.CreateElement('target', $ns) } else { $doc.CreateElement('target') }
        $null = $sourceNode.ParentNode.InsertAfter($targetNode, $sourceNode)
    }
    $targetNode.InnerText = $Target

    # State lives on <target>. A completed translation carries state="translated"; needs-work maps to the
    # XLIFF "needs-adaptation" state that Test-BcTranslation flags as incomplete.
    $xlfState = if ($State -eq 'needs-work') { 'needs-adaptation' } else { 'translated' }
    $stateAttr = $targetNode.Attributes | Where-Object { $_.Name -ieq 'state' } | Select-Object -First 1
    if (-not $stateAttr) { $stateAttr = $targetNode.SetAttributeNode($doc.CreateAttribute('state')) }
    $stateAttr.Value = $xlfState

    if ($PSCmdlet.ShouldProcess($Path, "Set translation unit '$($match.Id)'")) {
        $doc.Save($Path)
        Write-ALbuildLog -Level Success "Set '$($match.Id)' in '$(Split-Path -Leaf $Path)' -> $State."
    }
    return [PSCustomObject]@{ id = $match.Id; source = $match.Source; target = $Target; state = $State; file = (Split-Path -Leaf $Path) }
}