From 7f189aa7413a9b201b279ea982fcbca8362a0792 Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Thu, 9 Apr 2026 16:03:02 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B6=D0=B5=D1=81=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=81=D1=86=D0=B5=D0=BD=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20AI-=D0=B0=D0=B3=D0=B5=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACTIONS.md | 3 ++- scripts/apply-translation-edits.ps1 | 23 +++++++++++++++++++++++ scripts/compare-translation.ps1 | 26 ++++++++++++++++---------- scripts/update-translation.ps1 | 9 +++++---- 4 files changed, 46 insertions(+), 15 deletions(-) diff --git a/ACTIONS.md b/ACTIONS.md index c02f429..6568866 100644 --- a/ACTIONS.md +++ b/ACTIONS.md @@ -80,9 +80,10 @@ ACTIONS: plan: - run_translation:diff_sequentially - if_summary_has_no_missing_no_version_mismatch_no_stale_report_translation_up_to_date_and_stop + - if_diff_exists_stop_after_generating_build/translation-diff/candidates.json_until_prepared_edits_are_provided_explicitly - review_build/translation-diff/candidates.json_before_apply - reuse_glossary_for_term_consistency_when_preparing_texts - - run_translation:apply_only_after_candidate_texts_are_filled + - run_translation:apply_only_after_candidate_texts_are_filled_and_explicit_edits_path_is_passed checks: - xml_valid - glossary_consistency diff --git a/scripts/apply-translation-edits.ps1 b/scripts/apply-translation-edits.ps1 index 9344c27..9ca53bb 100644 --- a/scripts/apply-translation-edits.ps1 +++ b/scripts/apply-translation-edits.ps1 @@ -51,6 +51,21 @@ function Get-ContentNodeMap { return $map } +function Assert-UniqueEditContentUid { + param( + [Parameter(Mandatory = $true)] + [System.Collections.Generic.HashSet[string]]$Seen, + [Parameter(Mandatory = $true)] + [string]$ContentUid, + [Parameter(Mandatory = $true)] + [string]$Section + ) + + if (-not $Seen.Add($ContentUid)) { + throw "Edits file contains duplicate contentuid '$ContentUid' in '$Section'." + } +} + $russianDocument = Read-XmlDocument -Path $RussianPath $temporaryRussianPath = "$($russianDocument.Path).tmp" $validateScriptPath = Join-Path $PSScriptRoot "validate-translation-xml.ps1" @@ -83,6 +98,7 @@ if ($null -eq $contentListNode) { $nodeMap = Get-ContentNodeMap -Xml $russianDocument.Xml $updatedEntries = New-Object System.Collections.Generic.List[string] $addedEntries = New-Object System.Collections.Generic.List[string] +$seenEditContentUids = [System.Collections.Generic.HashSet[string]]::new() $updates = @() if ($edits.PSObject.Properties.Name -contains "updates" -and $null -ne $edits.updates) { @@ -95,6 +111,8 @@ foreach ($edit in $updates) { throw "Each update entry must contain non-empty 'contentuid'." } + Assert-UniqueEditContentUid -Seen $seenEditContentUids -ContentUid $contentUid -Section "updates" + if (-not $nodeMap.ContainsKey($contentUid)) { throw "Target russian.xml does not contain contentuid '$contentUid' for update." } @@ -105,6 +123,9 @@ foreach ($edit in $updates) { } if ($edit.PSObject.Properties.Name -contains "text") { + if ([string]::IsNullOrWhiteSpace([string]$edit.text)) { + throw "Update entry '$contentUid' must contain non-empty 'text' when provided." + } $node.InnerText = [string]$edit.text } @@ -133,6 +154,8 @@ foreach ($edit in $adds) { throw "Each add entry must contain non-empty 'contentuid'." } + Assert-UniqueEditContentUid -Seen $seenEditContentUids -ContentUid $contentUid -Section "adds" + if ($nodeMap.ContainsKey($contentUid)) { throw "Target russian.xml already contains contentuid '$contentUid'; use 'updates' instead of 'adds'." } diff --git a/scripts/compare-translation.ps1 b/scripts/compare-translation.ps1 index 5ace86f..206d81f 100644 --- a/scripts/compare-translation.ps1 +++ b/scripts/compare-translation.ps1 @@ -27,12 +27,21 @@ function Get-LocalizationEntries { foreach ($node in $nodes) { $contentUid = [string]$node.GetAttribute("contentuid") if ([string]::IsNullOrWhiteSpace($contentUid)) { - continue + throw "Localization XML contains a content node without 'contentuid': '$resolvedPath'." + } + + if ($entries.ContainsKey($contentUid)) { + throw "Localization XML contains duplicate contentuid '$contentUid': '$resolvedPath'." + } + + $version = [string]$node.GetAttribute("version") + if ([string]::IsNullOrWhiteSpace($version)) { + throw "Localization XML contains contentuid '$contentUid' with empty 'version': '$resolvedPath'." } $entries[$contentUid] = [ordered]@{ contentuid = $contentUid - version = [string]$node.GetAttribute("version") + version = $version text = [string]$node.InnerText } } @@ -46,9 +55,6 @@ New-Item -ItemType Directory -Path $resolvedOutputDir -Force | Out-Null $englishEntries = Get-LocalizationEntries -Path $EnglishPath $russianEntries = Get-LocalizationEntries -Path $RussianPath -$englishKeys = [System.Collections.Generic.HashSet[string]]::new([string[]]$englishEntries.Keys) -$russianKeys = [System.Collections.Generic.HashSet[string]]::new([string[]]$russianEntries.Keys) - $missingInRussian = New-Object System.Collections.Generic.List[object] $versionMismatch = New-Object System.Collections.Generic.List[object] $staleOnlyInRussian = New-Object System.Collections.Generic.List[object] @@ -156,11 +162,11 @@ if ($isUpToDate) { $mdLines += "Перевод уже актуален, дополнительные действия не требуются." } else { $mdLines += "" - $mdLines += "## Local workflow" - $mdLines += "1. Update upstream cache: ``scripts/get-upstream-english.ps1``" - $mdLines += "2. Refresh diff: ``scripts/compare-translation.ps1``" - $mdLines += "3. Edit ``build/translation-diff/candidates.json``" - $mdLines += "4. Apply changes: ``scripts/apply-translation-edits.ps1 -EditsPath build/translation-diff/candidates.json``" + $mdLines += "## Agent workflow" + $mdLines += "1. Refresh upstream cache: ``scripts/get-upstream-english.ps1``" + $mdLines += "2. Refresh diff reports: ``scripts/compare-translation.ps1``" + $mdLines += "3. Fill translated texts in ``build/translation-diff/candidates.json``" + $mdLines += "4. Apply only prepared edits: ``scripts/apply-translation-edits.ps1 -EditsPath build/translation-diff/candidates.json``" } $mdLines += "" diff --git a/scripts/update-translation.ps1 b/scripts/update-translation.ps1 index 3fca16f..450c2a4 100644 --- a/scripts/update-translation.ps1 +++ b/scripts/update-translation.ps1 @@ -51,13 +51,14 @@ try { return } - $effectiveEditsPath = $EditsPath - if ([string]::IsNullOrWhiteSpace($effectiveEditsPath)) { - $effectiveEditsPath = Join-Path $resolvedOutputDir "candidates.json" + if ([string]::IsNullOrWhiteSpace($EditsPath)) { + Write-Host "[update-translation.ps1] Найдены изменения перевода. Подготовьте правки в '$([System.IO.Path]::GetFullPath((Join-Path $resolvedOutputDir "candidates.json")))' и затем запустите повторно с '-EditsPath'." + return } + $effectiveEditsPath = [System.IO.Path]::GetFullPath($EditsPath) if (-not (Test-Path -LiteralPath $effectiveEditsPath)) { - throw "Translation changes were found. Prepare edits in '$([System.IO.Path]::GetFullPath((Join-Path $resolvedOutputDir "candidates.json")))' and rerun update with '-EditsPath'." + throw "Prepared edits file was not found: '$effectiveEditsPath'." } & $applyScriptPath -RussianPath $RussianPath -EditsPath $effectiveEditsPath