PowerShell: Aus NAV-Translationsdatei eine XLIFF erzeugen

16. April 2015 16:08

Mit dieser Funktion kann auf direktem Wege aus einer NAV-Translationsdatei, wie vorhanden mehrsprachig in Kodierung OEM 850 eine XLIFF-Datei (*.xlf, Version 1.2) in Unicode UTF-8 erzeugt werden. Die Sprachdatei aus NAV wird im ersten Schritt in eine Zwischendatei in Unicode UTF-8 konvertiert, die dabei ebenfalls im Arbeitsverzeichnis mit Suffix _UTF8 abgelegt wird.
Die Quell- und Zielsprache (Source- und Target Language) können dabei jeweils vorher angeben werden. In der Translationsdatei werden die daraus resultierenden Suchmuster (Codestring bis zum Doppelpunkt) verwendet, um die Übersetzung zu finden und in der XLIFF unter der ID des Codestrings einzutragen. Nachtrag: Leerzeichen im Dateipfad sind nicht zulässig, wer die braucht, muss das Skript anpassen :wink: .

Ausgangsdatei, über Extras>Translate aus NAV exportiert:
NAVlangtxt.png

Beispiel 1: Bei Eingabe Quelle: A1031 (de) Ziel: A1033 (en)
XLFkonvEingabeDE_EN.png

entsteht diese XLIFF:
XLFde_enCOLOR.png


Beispiel 2: Bei Eingabe Quelle: A1033 (en) Ziel: A1031 (de)
XLFkonvEingabeEN_DE.png

entsteht aus der gleichen Ausgangsdatei diese XLIFF:
XLFen_deCOLOR.png


Weitere Sprachen als im Switchbefehl bereits vorhanden können natürlich jederzeit ergänzt werden.
XLFswitch.png

Für Polnisch als Zielsprache müsste z.B. diese Zeile hinzukommen:
Code:
"A1045" {$ISOTargetCode = 'pl'}

Außerdem wäre hier die Codepage der Quelldatei nicht 850 (Western Europe), sondern 852 (Central Europe), das muss im Skript ggf. angepasst werden, dann also:
Code:
$sourceEncoding = [System.Text.Encoding]::GetEncoding(852)
oder das SourceEncoding als weiteren Eingabeparameter hinzunehmen. Ob die Konvertierung der Sonderzeichen funktioniert hat, kann man sofort an der UTF-8-Zwischendatei kontrollieren, aus der das Skript dann im weiteren Verlauf die XLIFF erstellt.

Je nach Zielsystem muss man aber prüfen, ob die ISO 639-Codes dort bekannt sind.
Die Umsetzung von DES -> A2055 -> gsw (Swiss German) ist zwar korrekt, gsw ist aber im Translator Hub derzeit unbekannt, dort muss also A2055 als 'de' umgesetzt werden.

Außer im Translator Hub kann die XLIFF in Tools wie Virtaal,Transolution usw. genutzt werden oder (Nachtrag aus 2025 :-) ) in Leerstellen direkt mit KI übersetzt werden.
XLFvirtaal.png


[27.04.22]Neue Version v2 (schnell, mit Hashtables statt langsamen Filterungen in der Quelldatei)
Hashtable-Variante mit optional leeren Targetcaptions hier.
Code:
function NAVTransToXLF_v2
 {
   
   #Declare ordered hashtables
   $NAVUTF8sourcehash = [ordered]@{}
   $NAVUTF8targethash = [ordered]@{}   
   
    function WriteToFile
     {
      out-file $XLFoutfile -inputobject $args -force -append -Width 500 -Encoding utf8
     }

 
   function WriteXLFHeader
     {
      $CurrLine = "<xliff version='1.2' xmlns='urn:oasis:names:tc:xliff:document:1.2'>"
      # out-file C:\Temp\Translations.xlf -inputobject $Currline -force -append -Width 500 -Encoding utf8
      writetofile $CurrLine
      $OrigFileName = [IO.Path]::GetFileName($NAVtransfile)
     
      $CurrLine = "    <file original='$OrigFileName' source-language='$ISOsourceCode' target-language='$ISOTargetCode' datatype='plaintext'>"
      writetofile $CurrLine
      $CurrLine = "        <body>"
      writetofile $CurrLine
    }
   function WriteBodyLines
     {
      $SourceCaption = $SourceCaption -replace '&','&amp;'
      $TargetCaption = $TargetCaption -replace '&','&amp;'
      $SourceCaption = $SourceCaption -replace '<','&lt;'
      $TargetCaption = $TargetCaption -replace '<','&lt;'
      $SourceCaption = $SourceCaption -replace '>','&gt;'
      $TargetCaption = $TargetCaption -replace '>','&gt;'
      $SourceCaption = $SourceCaption -replace '"','&quot;'
      $TargetCaption = $TargetCaption -replace '"','&quot;'
      $SourceCaption = $SourceCaption -replace $([char]39),'&apos;'
      $TargetCaption = $TargetCaption -replace $([char]39),'&apos;'
      $CurrLine = "            <trans-unit id=$([char]34)$SourceCodestringID$([char]34)>"
      writetofile $CurrLine
      $CurrLine = "                <source xml:lang=$([char]34)$ISOsourceCode$([char]34)>$SourceCaption</source>"
      writetofile $CurrLine
      $CurrLine = "                <target xml:lang=$([char]34)$ISOtargetCode$([char]34)>$TargetCaption</target>"
      writetofile $CurrLine         
      $CurrLine = '            </trans-unit>'
      writetofile $CurrLine
     
     }
   function WriteXLFfooter
     {
        $CurrLine = '        </body>'
        writetofile $CurrLine
        $CurrLine = '    </file>'
        writetofile $CurrLine
        $CurrLine = '</xliff>'
        writetofile $CurrLine
     }
   $NAVtransfile = Read-host "Existing NAV translation file"
   $XLFoutfile = Read-host "Name of new XLF translation file"
     
   if ($NAVtransfile -eq $XLFoutfile)
     {
       Throw "Source and target file are identical. Source: $NAVtransfile Target: $XLFoutfile"
     }
     
   if (Test-Path $XLFoutfile) {Remove-Item $XLFoutfile}
     
   $lines = Get-Content $NAVtransfile | Measure-Object -Line
   $Nooflines = $lines.Lines
   write-host "File has $Nooflines lines"
 
   $sourceEncoding = [Text.Encoding]::GetEncoding(850)
   $targetEncoding = [Text.Encoding]::GetEncoding(65001)
       
   $convertedFileName = [IO.Path]::GetDirectoryName($NAVtransfile) + "\"+ [IO.Path]::GetFileNameWithoutExtension($NAVtransfile) +"_UTF8" + [IO.Path]::GetExtension($NAVtransfile)
   if (Test-Path $convertedFileName) {Remove-Item $convertedFileName}
       
   $convertedfile = New-Item -path $convertedFileName -type file
       
   $textfile = [IO.File]::ReadAllText($NAVtransfile, $sourceencoding)
   [IO.File]::WriteAllText($convertedfile, $textfile, $targetencoding)
   Write-host $NAVtransfile 'converted to' $convertedFileName
     
   
   $SourceLangCode = Read-host "NAV-Code for source language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   $TargetLangCode = Read-host "NAV-Code for target language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   if ($SourceLangCode -eq $TargetLangCode)
     {
       Throw "Source and target language code are identical. Source: $SourceLangCode Target: $TargetLangCode"
     }

   
   Switch ($SourceLangCode)
     {
       "A1031" {$ISOsourceCode = 'de'}
       "A1033" {$ISOsourceCode = 'en'}
       "A1034" {$ISOsourceCode = 'es'}
       "A1036" {$ISOsourceCode = 'fr'}
       "A1040" {$ISOsourceCode = 'it'}
       "A1046" {$ISOsourceCode = 'pt'}       
       "A2055" {$ISOsourceCode = 'gsw'}
       "A2057" {$ISOsourceCode = 'en-gb'}
       "A3079" {$ISOsourceCode = 'de'}
       default {Throw "Please assign an ISO code for $SourceLangCode in the script"}
     }
   Switch ($TargetLangCode)
     {
             "A1030" {$ISOTargetCode = 'dk'}
             "A1031" {$ISOTargetCode = 'de'}
             "A1033" {$ISOTargetCode = 'en'}
             "A1034" {$ISOTargetCode = 'es'}
             "A1035" {$ISOTargetCode = 'fi'}
             "A1036" {$ISOTargetCode = 'fr'}
             "A1040" {$ISOTargetCode = 'it'}
             "A1043" {$ISOTargetCode = 'nl'}
             "A1044" {$ISOTargetCode = 'no'}
             "A1045" {$ISOTargetCode = 'pl'}
             "A1046" {$ISOTargetCode = 'pt'}
             "A1053" {$ISOTargetCode = 'se'}
             "A2055" {$ISOTargetCode = 'gsw'}
             "A2057" {$ISOTargetCode = 'en-gb'}
             "A3079" {$ISOTargetCode = 'de'}
       default {Throw "Please assign an ISO code for $TargetLangCode in the script"}
     }
 
     Write-host "ISO 639 language codes source: $ISOsourceCode, target: $ISOTargetCode"
     $SourceLangCode = "-"+ $SourceLangCode + "-"
     $TargetLangCode = "-"+ $TargetLangCode + "-"
     
     # Write-host "Writing XLF-Header…"
     WriteXLFHeader

   
     Write-host "Building hash, this may take a while…"
     foreach ($line in [IO.File]::ReadLines($convertedFileName))
     {
       $k += 1
       if ($k % 1000 -eq 0) {Write-host "Line $k of $Nooflines"}
       $SourceTextLine = $line.ToString()
       if ($SourceTextLine.contains($SourceLangCode))
       {
         $pos1 = $SourceTextLine.IndexOf(":")     
         $SourceCodestringID = $SourceTextLine.Substring(0,$pos1)
         $SourceCaption = $SourceTextLine.Substring($pos1 + 1)
         $NAVUTF8sourcehash.add($SourceCodestringID,$SourceCaption)
       }   
       if ($SourceTextLine.contains($TargetLangCode))
         {
         $pos1 = $SourceTextLine.IndexOf(":")     
         $TargetCodestringID = $SourceTextLine.Substring(0,$pos1)
         $TargetCaption = $SourceTextLine.Substring($pos1 + 1)
         $NAVUTF8targethash.add($TargetCodestringID,$TargetCaption)
       }           
     }

     foreach ($key in $NAVUTF8sourcehash.keys)
       {
           
           $SourceCodestringID = $key
           $SourceCaption = $NAVUTF8sourcehash[$key]
           $TargetCodestringID = $SourceCodestringID -replace $SourceLangCode,$TargetLangCode
           $TargetCaption = $NAVUTF8targethash[$TargetCodestringID]
         
             IF (![string]::IsNullOrEmpty($TargetCaption))
                {
                  IF ($TargetCaption -eq 'System.Object[]') {$TargetCaption = ''}
                  WriteBodyLines
                  $i += 1
                  if ($i % 10 -eq 0) {Write-Host "Writing line $i"}
                }
         
      }
   
   WriteXLFfooter
 }
NAVTransToXLF_v2


Erste Version (bei großen Dateien (z.B. 50000 Zeilen) sehr langsam, nur noch zu Vergleichszwecken)
Code:
function NAVTransToXLF
 {
   function WriteToFile
     {
      out-file $XLFoutfile -inputobject $args -force -append -Width 500 -Encoding utf8
     }
 
   function WriteXLFHeader
     {
      $CurrLine = "<xliff version='1.2' xmlns='urn:oasis:names:tc:xliff:document:1.2'>"
      # out-file C:\Temp\Translations.xlf -inputobject $Currline -force -append -Width 500 -Encoding utf8
      writetoFile $CurrLine
      $OrigFileName = [System.IO.Path]::GetFileName($NAVtransfile)
      
      $CurrLine = "    <file original='$OrigFileName' source-language='$ISOsourceCode' target-language='$ISOTargetCode' datatype='plaintext'>"
      writetoFile $CurrLine
      $CurrLine = "        <body>"
      writetoFile $CurrLine
    }
   function WriteBodyLines
     {
      $SourceCaption = $SourceCaption -replace '&','&amp;'
      $TargetCaption = $TargetCaption -replace '&','&amp;'
      $SourceCaption = $SourceCaption -replace '<','&lt;'
      $TargetCaption = $TargetCaption -replace '<','&lt;'
      $SourceCaption = $SourceCaption -replace '>','&gt;'
      $TargetCaption = $TargetCaption -replace '>','&gt;'
      $SourceCaption = $SourceCaption -replace '"','&quot;'
      $TargetCaption = $TargetCaption -replace '"','&quot;'
      $SourceCaption = $SourceCaption -replace $([char]39),'&apos;'
      $TargetCaption = $TargetCaption -replace $([char]39),'&apos;'
      $CurrLine = "            <trans-unit id=$([char]34)$SourceCodestringID$([char]34)>"
      writetoFile $CurrLine
      $CurrLine = "                <source xml:lang=$([char]34)$ISOsourceCode$([char]34)>$SourceCaption</source>"
      writetoFile $CurrLine
      $CurrLine = "                <target xml:lang=$([char]34)$ISOtargetCode$([char]34)>$TargetCaption</target>"
      writetoFile $CurrLine         
      $CurrLine = '            </trans-unit>'
      writetoFile $CurrLine
     
     }
   function WriteXLFfooter
     {
        $CurrLine = '        </body>'
        writetoFile $CurrLine
        $CurrLine = '    </file>'
        writetoFile $CurrLine
        $CurrLine = '</xliff>'
        writetoFile $CurrLine
     }
   $NAVtransfile = Read-host "Existing NAV translation file"
   $XLFoutfile = Read-host "Name of new XLF translation file"
     
   if ($NAVtransfile -eq $XLFoutfile)
     {
       Throw "Source and target file are identical. Source: $NAVtransfile Target: $XLFoutfile"
     }
     
   if (Test-Path $XLFoutfile) {Remove-Item $XLFoutfile}
     
   $lines = Get-Content $NAVtransfile | Measure-Object –Line
   $Nooflines = $lines.Lines
   write-host "File has $Nooflines lines"
 
   $sourceEncoding = [System.Text.Encoding]::GetEncoding(850)
   $targetEncoding = [System.Text.Encoding]::GetEncoding(65001)
       
   $convertedFileName = [System.IO.Path]::GetDirectoryName($NAVtransfile) + "\"+ [System.IO.Path]::GetFileNameWithoutExtension($NAVtransfile) +"_UTF8" + [System.IO.Path]::GetExtension($NAVtransfile)
   if (Test-Path $convertedFileName) {Remove-Item $convertedFileName}
       
   $convertedfile = New-Item -path $convertedFileName -type file
       
   $textfile = [System.IO.File]::ReadAllText($NAVtransfile, $sourceencoding)
   [System.IO.File]::WriteAllText($convertedfile, $textfile, $targetencoding)
   Write-host $NAVtransfile 'converted to' $convertedFileName
     
   $SourceLangCode = Read-host "NAV-Code for source language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   $TargetLangCode = Read-host "NAV-Code for target language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   if ($SourceLangCode -eq $TargetLangCode)
     {
       Throw "Source and target language code are identical. Source: $SourceLangCode Target: $TargetLangCode"
     }

   
   Switch ($SourceLangCode)
     {
       "A1031" {$ISOsourceCode = 'de'}
       "A1033" {$ISOsourceCode = 'en'}
       "A1034" {$ISOsourceCode = 'es'}
       "A1036" {$ISOsourceCode = 'fr'}
       "A1040" {$ISOsourceCode = 'it'}
       "A1046" {$ISOsourceCode = 'pt'}       
       "A2055" {$ISOsourceCode = 'gsw'}
       "A2057" {$ISOsourceCode = 'en-gb'}
       "A3079" {$ISOsourceCode = 'de'}
       default {Throw "Please assign an ISO code for $SourceLangCode in the script"}
     }
   Switch ($TargetLangCode)
     {
             "A1030" {$ISOTargetCode = 'dk'}
             "A1031" {$ISOTargetCode = 'de'}
             "A1033" {$ISOTargetCode = 'en'}
             "A1034" {$ISOTargetCode = 'es'}
             "A1035" {$ISOTargetCode = 'fi'}
             "A1036" {$ISOTargetCode = 'fr'}
             "A1040" {$ISOTargetCode = 'it'}
             "A1043" {$ISOTargetCode = 'nl'}
             "A1044" {$ISOTargetCode = 'no'}
             "A1045" {$ISOTargetCode = 'pl'}
             "A1046" {$ISOTargetCode = 'pt'}
             "A1053" {$ISOTargetCode = 'se'}
             "A2055" {$ISOTargetCode = 'gsw'}
             "A2057" {$ISOTargetCode = 'en-gb'}
             "A3079" {$ISOTargetCode = 'de'}
       default {Throw "Please assign an ISO code for $TargetLangCode in the script"}
     }
 
     Write-host "ISO 639 language codes source: $ISOsourceCode, target: $ISOTargetCode"
     $SourceLangCode = "-"+ $SourceLangCode + "-"
     $TargetLangCode = "-"+ $TargetLangCode + "-"
     
     # Write-host "Writing XLF-Header…"
     WriteXLFHeader
     # Write-host "UTF8 file: $convertedFileName"
   
     foreach ($line in [System.IO.File]::ReadLines($convertedFileName))
       {
       $SourceTextLine = $line.ToString()
         if ($SourceTextLine.contains($SourceLangCode))
       {
         $TargetLine = ''
       # Write-host "Source line $SourceTextLine"
 
       $pos1 = $SourceTextLine.IndexOf(":")
     
       $SourceCodestringID = $SourceTextLine.Substring(0,$pos1)
     
       
        $SourceCaption = $SourceTextLine.Substring($pos1 + 1)
        # Write-host "Source Caption $SourceCaption"
        $TargetCodestringID = $SourceCodestringID -replace $SourceLangCode,$TargetLangCode
        # Write-host "Token Target $TargetCodestringID"
        $TargetTextLine = ''
        $TargetLine = select-string -path $convertedFileName -pattern $TargetCodestringID -SimpleMatch
     
     
        IF (![string]::IsNullOrEmpty($TargetLine))
           {
             $TargetTextLine = $TargetLine.ToString()
             $posFileNameEnd = $TargetTextLine.IndexOf(":")
             $TargetTextLine = $TargetTextLine.Substring($posFileNameEnd + 1)
             $poslineno = $TargetTextLine.IndexOf(":")
             $TargetTextLine = $TargetTextLine.Substring($poslineno + 1)
             $posNAVcodeEnd = $TargetTextLine.IndexOf(":")
             $TargetTextLine = $TargetTextLine.Substring($posNAVcodeEnd + 1)
             # Write-host "Target line $TargetTextLine"
     
             IF (![string]::IsNullOrEmpty($TargetTextLine))
                {
                  $pos2 = $TargetTextLine.IndexOf(":")
                  $TargetCaption = $TargetTextLine.Substring($pos2 + 1)
                  IF ($TargetCaption -eq 'System.Object[]') {$TargetCaption = ''}
                  WriteBodyLines
                  $i = $i + 1
                  if ($i%10 -eq 0) {Write-Host "Writing line $i"}
                }
            }
        }
      }
   WriteXLFfooter
}



Edit 28.08.15:

Mein Skript zur Rückumwandlung nach erfolgter Bearbeitung liegt hier :greenarrow: Aus XLIFF eine NAV-Translationsdatei erzeugen
Tags: Sprachdatei, translations file
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Variante: XLIFF eingrenzen auf Add-on-Captions

16. April 2015 17:37

In dieser Variante ist am Anfang eine Zusatzfunktion IsAddonCaption, die anhand der im Add-on vorkommenden Teilen von IDs innerhalb des ID-Codestrings prüft (die müssen natürlich jeweils an das eigene Add-on angepasst werden), ob es sich um eine Add-on-Caption handelt. Nur diese werden dann in die XLIFF übernommen.

XLF_OPPcaptions.png


Code:
function NAVTransOPPToXLF
 {
   function IsAddonCaption($Codestring)
     {
      return($Codestring.contains('51578') -or $Codestring.contains('51579') -or $Codestring.contains('51580') -or $Codestring.StartsWith('M1055') -or $Codestring.StartsWith('M55'))
     }
 
   function WriteToFile
     {
      out-file $XLFoutfile -inputobject $args -force -append -Width 500 -Encoding utf8
     }
 
   function WriteXLFHeader
     {
      $CurrLine = "<xliff version='1.2' xmlns='urn:oasis:names:tc:xliff:document:1.2'>"
      # out-file C:\Temp\Translations.xlf -inputobject $Currline -force -append -Width 500 -Encoding utf8
      writetoFile $CurrLine
      $OrigFileName = [System.IO.Path]::GetFileName($NAVtransfile)
      
      $CurrLine = "    <file original='$OrigFileName' source-language='$ISOsourceCode' target-language='$ISOTargetCode' datatype='plaintext'>"
      writetoFile $CurrLine
      $CurrLine = "        <body>"
      writetoFile $CurrLine
    }
   function WriteBodyLines
     {
      $SourceCaption = $SourceCaption -replace '&','&amp;'
      $TargetCaption = $TargetCaption -replace '&','&amp;'
      $SourceCaption = $SourceCaption -replace '<','&lt;'
      $TargetCaption = $TargetCaption -replace '<','&lt;'
      $SourceCaption = $SourceCaption -replace '>','&gt;'
      $TargetCaption = $TargetCaption -replace '>','&gt;'
      $SourceCaption = $SourceCaption -replace '"','&quot;'
      $TargetCaption = $TargetCaption -replace '"','&quot;'
      $SourceCaption = $SourceCaption -replace $([char]39),'&apos;'
      $TargetCaption = $TargetCaption -replace $([char]39),'&apos;'
      $CurrLine = "            <trans-unit id=$([char]34)$SourceCodestringID$([char]34)>"
      writetoFile $CurrLine
      $CurrLine = "                <source xml:lang=$([char]34)$ISOsourceCode$([char]34)>$SourceCaption</source>"
      writetoFile $CurrLine
      $CurrLine = "                <target xml:lang=$([char]34)$ISOtargetCode$([char]34)>$TargetCaption</target>"
      writetoFile $CurrLine         
      $CurrLine = '            </trans-unit>'
      writetoFile $CurrLine
     
     }
   function WriteXLFfooter
     {
        $CurrLine = '        </body>'
        writetoFile $CurrLine
        $CurrLine = '    </file>'
        writetoFile $CurrLine
        $CurrLine = '</xliff>'
        writetoFile $CurrLine
     }
   $NAVtransfile = Read-host "Existing NAV translation file"
   $XLFoutfile = Read-host "Name of new XLF translation file"
     
   if ($NAVtransfile -eq $XLFoutfile)
     {
       Throw "Source and target file are identical. Source: $NAVtransfile Target: $XLFoutfile"
     }
     
   if (Test-Path $XLFoutfile) {Remove-Item $XLFoutfile}
     
   $lines = Get-Content $NAVtransfile | Measure-Object –Line
   $Nooflines = $lines.Lines
   write-host "File has $Nooflines lines"
 
   $sourceEncoding = [System.Text.Encoding]::GetEncoding(850)
   $targetEncoding = [System.Text.Encoding]::GetEncoding(65001)
       
   $convertedFileName = [System.IO.Path]::GetDirectoryName($NAVtransfile) + "\"+ [System.IO.Path]::GetFileNameWithoutExtension($NAVtransfile) +"_UTF8" + [System.IO.Path]::GetExtension($NAVtransfile)
   if (Test-Path $convertedFileName) {Remove-Item $convertedFileName}
       
   $convertedfile = New-Item -path $convertedFileName -type file
       
   $textfile = [System.IO.File]::ReadAllText($NAVtransfile, $sourceencoding)
   [System.IO.File]::WriteAllText($convertedfile, $textfile, $targetencoding)
   Write-host $NAVtransfile 'converted to' $convertedFileName
     
   $SourceLangCode = Read-host "NAV-Code for source language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   $TargetLangCode = Read-host "NAV-Code for target language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   if ($SourceLangCode -eq $TargetLangCode)
     {
       Throw "Source and target language code are identical. Source: $SourceLangCode Target: $TargetLangCode"
     }

   
   Switch ($SourceLangCode)
     {
       "A1031" {$ISOsourceCode = 'de'}
       "A1033" {$ISOsourceCode = 'en'}
       "A1034" {$ISOsourceCode = 'es'}
       "A1036" {$ISOsourceCode = 'fr'}
       "A2055" {$ISOsourceCode = 'gsw'}
       "A2057" {$ISOsourceCode = 'en-gb'}
       "A3079" {$ISOsourceCode = 'de'}
       default {Throw "Please assign an ISO code for $SourceLangCode in the script"}
     }
   Switch ($TargetLangCode)
     {
       "A1031" {$ISOTargetCode = 'de'}
       "A1033" {$ISOTargetCode = 'en'}
       "A1034" {$ISOTargetCode = 'es'}
       "A1036" {$ISOTargetCode = 'fr'}
       "A2055" {$ISOTargetCode = 'gsw'}
       "A2057" {$ISOTargetCode = 'en-gb'}
       "A3079" {$ISOTargetCode = 'de'}
       default {Throw "Please assign an ISO code for $TargetLangCode in the script"}
     }
 
     Write-host "ISO 639 language codes source: $ISOsourceCode, target: $ISOTargetCode"
     $SourceLangCode = "-"+ $SourceLangCode + "-"
     $TargetLangCode = "-"+ $TargetLangCode + "-"
     
     # Write-host "Writing XLF-Header…"
     WriteXLFHeader
     # Write-host "UTF8 file: $convertedFileName"
   
     foreach ($line in [System.IO.File]::ReadLines($convertedFileName))
       {
       $SourceTextLine = $line.ToString()
         if ($SourceTextLine.contains($SourceLangCode))
       {
       $TargetLine = ''
       # Write-host "Source line $SourceTextLine"
 
       $pos1 = $SourceTextLine.IndexOf(":")
     
     $SourceCodestringID = $SourceTextLine.Substring(0,$pos1)
     IF (IsAddonCaption($SourceCodestringID))
        {
        $SourceCaption = $SourceTextLine.Substring($pos1 + 1)
        # Write-host "Source Caption $SourceCaption"
        $TargetCodestringID = $SourceCodestringID -replace $SourceLangCode,$TargetLangCode
        # Write-host "Token Target $TargetCodestringID"
        $TargetTextLine = ''
        $TargetLine = select-string -path $convertedFileName -pattern $TargetCodestringID -SimpleMatch
     
     
        IF (![string]::IsNullOrEmpty($TargetLine))
           {
             $TargetTextLine = $TargetLine.ToString()
             $posFileNameEnd = $TargetTextLine.IndexOf(":")
             $TargetTextLine = $TargetTextLine.Substring($posFileNameEnd + 1)
             $poslineno = $TargetTextLine.IndexOf(":")
             $TargetTextLine = $TargetTextLine.Substring($poslineno + 1)
             $posNAVcodeEnd = $TargetTextLine.IndexOf(":")
             $TargetTextLine = $TargetTextLine.Substring($posNAVcodeEnd + 1)
             # Write-host "Target line $TargetTextLine"
     
             IF (![string]::IsNullOrEmpty($TargetTextLine))
                {
                  $pos2 = $TargetTextLine.IndexOf(":")
                  $TargetCaption = $TargetTextLine.Substring($pos2 + 1)
                  IF ($TargetCaption -eq 'System.Object[]') {$TargetCaption = ''}
                  WriteBodyLines
                  $i = $i + 1
                  if ($i%10 -eq 0) {Write-Host "Writing line $i"}
                }
            }
        }
      }
    }
   WriteXLFfooter
}
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Leere Targetcaptions optional zulassen

19. Mai 2015 17:24

Mit diesen erweiterten Funktionen ist es optional möglich (Zusatzfrage: Allow empty target captions in XLIFF if no caption translation is available (yes,no) ?), bei fehlenden Übersetzungen für der Zielsprache in der NAV-Translationsdatei leere Targetcaptions in der XLIFF zu exportieren (im Bild rot eingekreist). Nachtrag 19.05.25: Hier eine Weiterentwicklung dieses Skripts mit optional ausschließlich leeren Targetcaptions, welche dann als Block innerhalb der XLIFF einfach mittels KI übersetzt werden können.
XLFwithEmptyCaptions2.png

Bei der Ansicht mit Virtaal werden die Lücken dann deutlich dargestellt:
XLFwithEmptyCaptions.png


Neue Version v2, mit Hashtables deutlich schneller.
Code:
function NAVTransToXLF_v2_AllowEmptyTargetCaptions
 {
   
   #Declare ordered hashtables
   $NAVUTF8sourcehash = [ordered]@{}
   $NAVUTF8targethash = [ordered]@{}
     
    function WriteToFile
     {
      out-file $XLFoutfile -inputobject $args -force -append -Width 500 -Encoding utf8
     }

 
   function WriteXLFHeader
     {
      $CurrLine = "<xliff version='1.2' xmlns='urn:oasis:names:tc:xliff:document:1.2'>"
      # out-file C:\Temp\Translations.xlf -inputobject $Currline -force -append -Width 500 -Encoding utf8
      writetofile $CurrLine
      $OrigFileName = [IO.Path]::GetFileName($NAVtransfile)
     
      $CurrLine = "    <file original='$OrigFileName' source-language='$ISOsourceCode' target-language='$ISOTargetCode' datatype='plaintext'>"
      writetofile $CurrLine
      $CurrLine = "        <body>"
      writetofile $CurrLine
    }
   function WriteBodyLines
     {
      $SourceCaption = $SourceCaption -replace '&','&amp;'
      $TargetCaption = $TargetCaption -replace '&','&amp;'
      $SourceCaption = $SourceCaption -replace '<','&lt;'
      $TargetCaption = $TargetCaption -replace '<','&lt;'
      $SourceCaption = $SourceCaption -replace '>','&gt;'
      $TargetCaption = $TargetCaption -replace '>','&gt;'
      $SourceCaption = $SourceCaption -replace '"','&quot;'
      $TargetCaption = $TargetCaption -replace '"','&quot;'
      $SourceCaption = $SourceCaption -replace $([char]39),'&apos;'
      $TargetCaption = $TargetCaption -replace $([char]39),'&apos;'
      $CurrLine = "            <trans-unit id=$([char]34)$SourceCodestringID$([char]34)>"
      writetofile $CurrLine
      $CurrLine = "                <source xml:lang=$([char]34)$ISOsourceCode$([char]34)>$SourceCaption</source>"
      writetofile $CurrLine
      $CurrLine = "                <target xml:lang=$([char]34)$ISOtargetCode$([char]34)>$TargetCaption</target>"
      writetofile $CurrLine         
      $CurrLine = '            </trans-unit>'
      writetofile $CurrLine
     
     }
   function WriteXLFfooter
     {
        $CurrLine = '        </body>'
        writetofile $CurrLine
        $CurrLine = '    </file>'
        writetofile $CurrLine
        $CurrLine = '</xliff>'
        writetofile $CurrLine
     }
   $NAVtransfile = Read-host "Existing NAV translation file"
   $XLFoutfile = Read-host "Name of new XLF translation file"
   
   [ValidateSet('yes','no')]$AllowEmptyTargetCaptions = Read-Host "Allow empty target captions in XLIFF if no caption translation is available (yes,no) ?"
                 
     
   if ($NAVtransfile -eq $XLFoutfile)
     {
       Throw "Source and target file are identical. Source: $NAVtransfile Target: $XLFoutfile"
     }
     
   if (Test-Path $XLFoutfile) {Remove-Item $XLFoutfile}
     
   $lines = Get-Content $NAVtransfile | Measure-Object -Line
   $Nooflines = $lines.Lines
   write-host "File has $Nooflines lines"
 
   $sourceEncoding = [Text.Encoding]::GetEncoding(850)
   $targetEncoding = [Text.Encoding]::GetEncoding(65001)
       
   $convertedFileName = [IO.Path]::GetDirectoryName($NAVtransfile) + "\"+ [IO.Path]::GetFileNameWithoutExtension($NAVtransfile) +"_UTF8" + [IO.Path]::GetExtension($NAVtransfile)
   if (Test-Path $convertedFileName) {Remove-Item $convertedFileName}
       
   $convertedfile = New-Item -path $convertedFileName -type file
       
   $textfile = [IO.File]::ReadAllText($NAVtransfile, $sourceencoding)
   [IO.File]::WriteAllText($convertedfile, $textfile, $targetencoding)
   Write-host $NAVtransfile 'converted to' $convertedFileName
     
   
   $SourceLangCode = Read-host "NAV-Code for source language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   $TargetLangCode = Read-host "NAV-Code for target language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   if ($SourceLangCode -eq $TargetLangCode)
     {
       Throw "Source and target language code are identical. Source: $SourceLangCode Target: $TargetLangCode"
     }

   
   Switch ($SourceLangCode)
     {
       "A1031" {$ISOsourceCode = 'de'}
       "A1033" {$ISOsourceCode = 'en'}
       "A1034" {$ISOsourceCode = 'es'}
       "A1036" {$ISOsourceCode = 'fr'}
       "A1040" {$ISOsourceCode = 'it'}
       "A1046" {$ISOsourceCode = 'pt'}       
       "A2055" {$ISOsourceCode = 'gsw'}
       "A2057" {$ISOsourceCode = 'en-gb'}
       "A3079" {$ISOsourceCode = 'de'}
       default {Throw "Please assign an ISO code for $SourceLangCode in the script"}
     }
   Switch ($TargetLangCode)
     {
             "A1030" {$ISOTargetCode = 'dk'}
             "A1031" {$ISOTargetCode = 'de'}
             "A1033" {$ISOTargetCode = 'en'}
             "A1034" {$ISOTargetCode = 'es'}
             "A1035" {$ISOTargetCode = 'fi'}
             "A1036" {$ISOTargetCode = 'fr'}
             "A1040" {$ISOTargetCode = 'it'}
             "A1043" {$ISOTargetCode = 'nl'}
             "A1044" {$ISOTargetCode = 'no'}
             "A1045" {$ISOTargetCode = 'pl'}
             "A1046" {$ISOTargetCode = 'pt'}
             "A1053" {$ISOTargetCode = 'se'}
             "A2055" {$ISOTargetCode = 'gsw'}
             "A2057" {$ISOTargetCode = 'en-gb'}
             "A3079" {$ISOTargetCode = 'de'}
       default {Throw "Please assign an ISO code for $TargetLangCode in the script"}
     }
 
     Write-host "ISO 639 language codes source: $ISOsourceCode, target: $ISOTargetCode"
     $SourceLangCode = "-"+ $SourceLangCode + "-"
     $TargetLangCode = "-"+ $TargetLangCode + "-"
     
     # Write-host "Writing XLF-Header…"
     WriteXLFHeader

   
     Write-host "Building hash, this may take a while…"
     foreach ($line in [IO.File]::ReadLines($convertedFileName))
     {
       $k += 1
       if ($k % 1000 -eq 0) {Write-host "Line $k of $Nooflines"}
       $SourceTextLine = $line.ToString()
       if ($SourceTextLine.contains($SourceLangCode))
       {
         $pos1 = $SourceTextLine.IndexOf(":")     
         $SourceCodestringID = $SourceTextLine.Substring(0,$pos1)
         $SourceCaption = $SourceTextLine.Substring($pos1 + 1)
         $NAVUTF8sourcehash.add($SourceCodestringID,$SourceCaption)
       }   
       if ($SourceTextLine.contains($TargetLangCode))
         {
         $pos1 = $SourceTextLine.IndexOf(":")     
         $TargetCodestringID = $SourceTextLine.Substring(0,$pos1)
         $TargetCaption = $SourceTextLine.Substring($pos1 + 1)
         $NAVUTF8targethash.add($TargetCodestringID,$TargetCaption)
       }           
     }

     foreach ($key in $NAVUTF8sourcehash.keys)
       {
           
           $SourceCodestringID = $key
           $SourceCaption = $NAVUTF8sourcehash[$key]
           $TargetCodestringID = $SourceCodestringID -replace $SourceLangCode,$TargetLangCode
           $TargetCaption = $NAVUTF8targethash[$TargetCodestringID]
           
         
             IF (![string]::IsNullOrEmpty($TargetCaption) -or ($AllowEmptyTargetCaptions -eq 'yes'))
                {
                  IF ($TargetCaption -eq 'System.Object[]') {$TargetCaption = ''}
                  WriteBodyLines
                  $i += 1
                  if ($i % 100 -eq 0) {Write-Host "Writing line $i"}
                }
         
      }
   
   WriteXLFfooter
   Write-host $NAVtransfile 'processed to' $XLFoutfile
 }
NAVTransToXLF_v2_AllowEmptyTargetCaptions


Die alte Beispielfunktion ohne Hashtables ist inklusive der AddOn-Erweiterung. Nur noch zu Vergleichszwecken verwenden.
Code:
       
function NAVTransOPPToXLF_OptionalEmptyTargetCaptions
         {

         function IsAddonCaption($Codestring)
         {
         return($Codestring.contains('51578') -or $Codestring.contains('51579') -or $Codestring.contains('51580') -or $Codestring.StartsWith('M1055') -or $Codestring.StartsWith('M55'))
         }
         
         function WriteToFile
         {
         out-file $XLFoutfile -inputobject $args -force -append -Width 500 -Encoding utf8
         }
         
         function WriteXLFHeader
             {

             $CurrLine = "<xliff version='1.2' xmlns='urn:oasis:names:tc:xliff:document:1.2'>"

             # out-file C:\Temp\Translations.xlf -inputobject $Currline -force -append -Width 500 -Encoding utf8

             writetoFile $CurrLine
             $OrigFileName = [System.IO.Path]::GetFileName($NAVtransfile)
             
             $CurrLine = "    <file original='$OrigFileName' source-language='$ISOsourceCode' target-language='$ISOTargetCode' datatype='plaintext'>"

             writetoFile $CurrLine

             $CurrLine = "        <body>"

             writetoFile $CurrLine
             }

          function WriteBodyLines
             {

             
              $SourceCaption = $SourceCaption -replace '&','&amp;'
              $TargetCaption = $TargetCaption -replace '&','&amp;'
              $SourceCaption = $SourceCaption -replace '<','&lt;'
              $TargetCaption = $TargetCaption -replace '<','&lt;'
              $SourceCaption = $SourceCaption -replace '>','&gt;'
              $TargetCaption = $TargetCaption -replace '>','&gt;'
              $SourceCaption = $SourceCaption -replace '"','&quot;'
              $TargetCaption = $TargetCaption -replace '"','&quot;'
              $SourceCaption = $SourceCaption -replace $([char]39),'&apos;'
              $TargetCaption = $TargetCaption -replace $([char]39),'&apos;'

              $CurrLine = "            <trans-unit id=$([char]34)$SourceCodestringID$([char]34)>"
              writetoFile $CurrLine
              $CurrLine = "                <source xml:lang=$([char]34)$ISOsourceCode$([char]34)>$SourceCaption</source>"
              writetoFile $CurrLine
              $CurrLine = "                <target xml:lang=$([char]34)$ISOtargetCode$([char]34)>$TargetCaption</target>"
              writetoFile $CurrLine         
              $CurrLine = '            </trans-unit>'
              writetoFile $CurrLine
             
              }

              Function WriteXLFfooter
                {
                $CurrLine = '        </body>'
                writetoFile $CurrLine
                $CurrLine = '    </file>'
                writetoFile $CurrLine
                $CurrLine = '</xliff>'
                writetoFile $CurrLine
                }

             $NAVtransfile = Read-host "Existing NAV translation file"
             $XLFoutfile = Read-host "Name of new XLF translation file"
             
             [ValidateSet('yes','no')]$AllowEmptyTargetCaptions = Read-Host "Allow empty target captions in XLIFF if no caption translation is available (yes,no) ?"
             
             if ($NAVtransfile -eq $XLFoutfile)
               {
                 Throw "Source and target file are identical. Source: $NAVtransfile Target: $XLFoutfile"
               }
             
             if (Test-Path $XLFoutfile) {Remove-Item $XLFoutfile}
             
             $lines = Get-Content $NAVtransfile | Measure-Object –Line
             $Nooflines = $lines.Lines
             write-host "File has $Nooflines lines"
         
            $sourceEncoding = [System.Text.Encoding]::GetEncoding(850)
            $targetEncoding = [System.Text.Encoding]::GetEncoding(65001)
               
            $convertedFileName = [System.IO.Path]::GetDirectoryName($NAVtransfile) + "\"+ [System.IO.Path]::GetFileNameWithoutExtension($NAVtransfile) +"_UTF8" + [System.IO.Path]::GetExtension($NAVtransfile)
            if (Test-Path $convertedFileName) {Remove-Item $convertedFileName}
               
            $convertedfile = New-Item -path $convertedFileName -type file
               
            $textfile = [System.IO.File]::ReadAllText($NAVtransfile, $sourceencoding)
            [System.IO.File]::WriteAllText($convertedfile, $textfile, $targetencoding)
            Write-host $NAVtransfile 'converted to' $convertedFileName
             
             $SourceLangCode = Read-host "NAV-Code for source language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
             $TargetLangCode = Read-host "NAV-Code for target language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"

             if ($SourceLangCode -eq $TargetLangCode)
              {
              Throw "Source and target language code are identical. Source: $SourceLangCode Target: $TargetLangCode"
              }
       
           
            Switch ($SourceLangCode)
             {
             "A1031" {$ISOsourceCode = 'de'}
             "A1033" {$ISOsourceCode = 'en'}
             "A1034" {$ISOsourceCode = 'es'}
             "A1036" {$ISOsourceCode = 'fr'}
             "A1040" {$ISOsourceCode = 'it'}
             "A1046" {$ISOsourceCode = 'pt'}
             "A2055" {$ISOsourceCode = 'gsw'}
             "A2057" {$ISOsourceCode = 'en-gb'}
             "A3079" {$ISOsourceCode = 'de'}

             default {Throw "Please assign an ISO code for $SourceLangCode in the script"}
             }

            Switch ($TargetLangCode)
             {
             "A1030" {$ISOTargetCode = 'dk'}
             "A1031" {$ISOTargetCode = 'de'}
             "A1033" {$ISOTargetCode = 'en'}
             "A1034" {$ISOTargetCode = 'es'}
             "A1035" {$ISOTargetCode = 'fi'}
             "A1036" {$ISOTargetCode = 'fr'}
             "A1040" {$ISOTargetCode = 'it'}
             "A1043" {$ISOTargetCode = 'nl'}
             "A1044" {$ISOTargetCode = 'no'}
             "A1045" {$ISOTargetCode = 'pl'}
             "A1046" {$ISOTargetCode = 'pt'}
             "A1053" {$ISOTargetCode = 'se'}
             "A2055" {$ISOTargetCode = 'gsw'}
             "A2057" {$ISOTargetCode = 'en-gb'}
             "A3079" {$ISOTargetCode = 'de'}
             default {Throw "Please assign an ISO code for $TargetLangCode in the script"}
             }
         
             Write-host "ISO 639 language codes source: $ISOsourceCode, target: $ISOTargetCode"
             $SourceLangCode = "-"+ $SourceLangCode + "-"
             $TargetLangCode = "-"+ $TargetLangCode + "-"
             
             # Write-host "Writing XLF-Header…"
             WriteXLFHeader
             # Write-host "UTF8 file: $convertedFileName"
           
             foreach ($line in [System.IO.File]::ReadLines($convertedFileName))
             {
             $SourceTextLine = $line.ToString()
             if ($SourceTextLine.contains($SourceLangCode))
             {
               $TargetLine = ''
               # Write-host "Source line $SourceTextLine"
         
             
              $pos1 = $SourceTextLine.IndexOf(":")

             
              $SourceCodestringID = $SourceTextLine.Substring(0,$pos1)

              IF (IsAddonCaption($SourceCodestringID))
              {
                $SourceCaption = $SourceTextLine.Substring($pos1 + 1)
                # Write-host "Source Caption $SourceCaption"
                $TargetCodestringID = $SourceCodestringID -replace $SourceLangCode,$TargetLangCode
                # Write-host "Token Target $TargetCodestringID"
                $TargetTextLine = ''
                $TargetLine = select-string -path $convertedFileName -pattern $TargetCodestringID -SimpleMatch
             
             if (([string]::IsNullOrEmpty($TargetLine)) -and ($AllowEmptyTargetCaptions -eq 'yes'))
              {
               $TargetLine = '::::'
              }

               
               if (![string]::IsNullOrEmpty($TargetLine))
               {
                $TargetTextLine = $TargetLine.ToString()
                $posFileNameEnd = $TargetTextLine.IndexOf(":")
                $TargetTextLine = $TargetTextLine.Substring($posFileNameEnd + 1)
                $poslineno = $TargetTextLine.IndexOf(":")
                $TargetTextLine = $TargetTextLine.Substring($poslineno + 1)
                $posNAVcodeEnd = $TargetTextLine.IndexOf(":")
                $TargetTextLine = $TargetTextLine.Substring($posNAVcodeEnd + 1)
                # Write-host "Target line $TargetTextLine"
             
                if (![string]::IsNullOrEmpty($TargetTextLine))
                {
                $pos2 = $TargetTextLine.IndexOf(":")
               
               
                $TargetCaption = $TargetTextLine.Substring($pos2 + 1)
                If ($TargetCaption -eq 'System.Object[]') {$TargetCaption = ''}
               
                WriteBodyLines
                $i = $i + 1
                if ($i%10 -eq 0) {Write-Host "Writing line $i"}
                }
               }
              }
             }
           }
          WriteXLFfooter
        }
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: Aus NAV-Translationsdatei eine XLIFF erzeuge

27. Januar 2021 22:09

Der Dynamics Translation Service (DTS) kann (wie bisher schon der ältere stillgelegte Translator Hub) mit XLIFFs ggf. mittels Translation Memory Files vorab trainiert und für genauere Übersetzungen definierter Begriffe und Phrasen genutzt werden.

Re: PowerShell: Aus NAV-Translationsdatei eine XLIFF erzeuge

27. April 2023 14:38

Im Startbeitrag gibt es jetzt eine neue Version des Skripts, die geordnete Hashtables verwendet, damit die bislang notwendigen langsamen Filterungen auf der Quelldatei umgeht, und somit erheblich schneller die XLIFF erzeugt. Selbst große Dateien werden damit in ein paar Sekunden abgearbeitet (Test mit 51598 Zeilen in einer Datei mit Captions für ENU,DEU und FRA dauerte 12,5 sec, mit der alten Version 34 Minuten :!: ).
Nachtrag 28.04.23: Die Variante mit optional leeren Targetcaptions ist jetzt auch als Hashtable-Version hier vorhanden.

Variante: Optional nur mit EmptyTargetCaptions

19. September 2025 16:20

2 neue Varianten zum obigen Skript AllowEmptyTargetCaptions, v2 mit einer Zusatzfrage
Write only trans-unit IDs with missing target captions (yes,no) ?
falls die mit "yes" beantwortet wird, dann enthält die XLIFF nur noch Trans-Unit-IDs mit fehlenden Übersetzungen.
Mittlerweile kann man ja über Copilot oder andere KI-Tools die Übersetzungen direkt in der XLIFF erzeugen lassen, und das geht schneller wenn kein Ballast dort enthalten ist :wink: . Außerdem besteht dann keine Gefahr, dass vorhandene Übersetzungen versehentlich mitmarkiert und u.U. erneut von der KI übersetzt werden. Gewünschten Bereich der Trans-Unit-IDs markieren und z.B. "Add French translations for the marked lines" reicht als Prompt aus.
Mein Skript zur Rückumwandlung nach erfolgter Bearbeitung wie bisher hier: Aus XLIFF eine NAV-Translationsdatei erzeugen.

Vorher muss dabei ggf. die Maskierung der fünf reservierten Zeichen in XML beachtet werden XLIFF-Datei erstellen (.xlf), besonders der Apostroph ' (Single Quote) wird in FRA ständig auftauchen. Statt dieses zu Maskieren kann man hier alternativ auf Akut ´ (Alt+239 oder Alt+0180), Gravis ` (Alt+96, in der Informatik auch als backtick bzw. backquote bekannt) oder Ejektiv ʼ ausweichen. Das hat auch den Vorteil, dass diese Texte bei der späteren Umwandlung nach AL unverändert erhalten bleiben und nicht an dieser Stelle wieder verarbeitet werden, weil das Single Quote ' dort auch als Begrenzungszeichen dient und diese dann zwei '' enthalten.

Andere Option: Wie im obigen Link beschrieben alles als ![CDATA[…]] (Character Data) behandeln. Das kann man gleich von Copilot mit erledigen lassen mittels anderem Prompt: Add French translations for the marked lines and enclose them as character data with ![CDATA[…]] oder als zweiten Schritt nach der Übersetzung separat mit: Enclose all French translations as character data with ![CDATA[…]]. Das obige Skript ist nicht für CDATA ausgelegt, damit klappt es nur mit Maskieren durch Escapezeichen.

Nach Umwandlung der XLIFF zurück in die Textdatei für den Import in C/SIDE kann man vorher in dieser ggf. die einzelnen diakritischen Hilfszeichen wie Akut, Gravis usw. wieder in ein Apostroph ändern, wenn diese nicht von der Darstellung im Standard abweichen sollen. Lesbar sind die Wörter aber auch, wenn diese dort verbleiben :wink: .


CopilotTranslationsFRA.png
.

Code:
function NAVTransToXLF_v2_AllowEmptyTargetCaptions_v2
 {
   
   #Declare ordered hashtables
   $NAVUTF8sourcehash = [ordered]@{}
   $NAVUTF8targethash = [ordered]@{}
     
    function WriteToFile
     {
      out-file $XLFoutfile -inputobject $args -force -append -Width 500 -Encoding utf8
     }

 
   function WriteXLFHeader
     {
      $CurrLine = "<xliff version='1.2' xmlns='urn:oasis:names:tc:xliff:document:1.2'>"
      # out-file C:\Temp\Translations.xlf -inputobject $Currline -force -append -Width 500 -Encoding utf8
      writetofile $CurrLine
      $OrigFileName = [IO.Path]::GetFileName($NAVtransfile)
     
      $CurrLine = "    <file original='$OrigFileName' source-language='$ISOsourceCode' target-language='$ISOTargetCode' datatype='plaintext'>"
      writetofile $CurrLine
      $CurrLine = "        <body>"
      writetofile $CurrLine
    }
   function WriteBodyLines
     {
      $SourceCaption = $SourceCaption -replace '&','&amp;'
      $TargetCaption = $TargetCaption -replace '&','&amp;'
      $SourceCaption = $SourceCaption -replace '<','&lt;'
      $TargetCaption = $TargetCaption -replace '<','&lt;'
      $SourceCaption = $SourceCaption -replace '>','&gt;'
      $TargetCaption = $TargetCaption -replace '>','&gt;'
      $SourceCaption = $SourceCaption -replace '"','&quot;'
      $TargetCaption = $TargetCaption -replace '"','&quot;'
      $SourceCaption = $SourceCaption -replace $([char]39),'&apos;'
      $TargetCaption = $TargetCaption -replace $([char]39),'&apos;'
      $CurrLine = "            <trans-unit id=$([char]34)$SourceCodestringID$([char]34)>"
      writetofile $CurrLine
      $CurrLine = "                <source xml:lang=$([char]34)$ISOsourceCode$([char]34)>$SourceCaption</source>"
      writetofile $CurrLine
      $CurrLine = "                <target xml:lang=$([char]34)$ISOtargetCode$([char]34)>$TargetCaption</target>"
      writetofile $CurrLine         
      $CurrLine = '            </trans-unit>'
      writetofile $CurrLine
     
     }
   function WriteXLFfooter
     {
        $CurrLine = '        </body>'
        writetofile $CurrLine
        $CurrLine = '    </file>'
        writetofile $CurrLine
        $CurrLine = '</xliff>'
        writetofile $CurrLine
     }
   $NAVtransfile = Read-host "Existing NAV translation file"
   $XLFoutfile = Read-host "Name of new XLF translation file"
   
   [ValidateSet('yes','no')]$AllowEmptyTargetCaptions = Read-Host "Allow empty target captions in XLIFF if no caption translation is available (yes,no) ?"
   [ValidateSet('yes','no')]$WriteOnlyMissingTargetCaptions = Read-Host "Write only trans-unit IDs with missing target captions (yes,no) ?"
                 
     
   if ($NAVtransfile -eq $XLFoutfile)
     {
       Throw "Source and target file are identical. Source: $NAVtransfile Target: $XLFoutfile"
     }
     
   if (Test-Path $XLFoutfile) {Remove-Item $XLFoutfile}
     
   $lines = Get-Content $NAVtransfile | Measure-Object -Line
   $Nooflines = $lines.Lines
   write-host "File has $Nooflines lines"
 
   $sourceEncoding = [Text.Encoding]::GetEncoding(850)
   $targetEncoding = [Text.Encoding]::GetEncoding(65001)
       
   $convertedFileName = [IO.Path]::GetDirectoryName($NAVtransfile) + "\"+ [IO.Path]::GetFileNameWithoutExtension($NAVtransfile) +"_UTF8" + [IO.Path]::GetExtension($NAVtransfile)
   if (Test-Path $convertedFileName) {Remove-Item $convertedFileName}
       
   $convertedfile = New-Item -path $convertedFileName -type file
       
   $textfile = [IO.File]::ReadAllText($NAVtransfile, $sourceencoding)
   [IO.File]::WriteAllText($convertedfile, $textfile, $targetencoding)
   Write-host $NAVtransfile 'converted to' $convertedFileName
     
   
   $SourceLangCode = Read-host "NAV-Code for source language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   $TargetLangCode = Read-host "NAV-Code for target language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
   if ($SourceLangCode -eq $TargetLangCode)
     {
       Throw "Source and target language code are identical. Source: $SourceLangCode Target: $TargetLangCode"
     }

   
   Switch ($SourceLangCode)
     {
       "A1031" {$ISOsourceCode = 'de'}
       "A1033" {$ISOsourceCode = 'en'}
       "A1034" {$ISOsourceCode = 'es'}
       "A1036" {$ISOsourceCode = 'fr'}
       "A1040" {$ISOsourceCode = 'it'}
       "A1046" {$ISOsourceCode = 'pt'}       
       "A2055" {$ISOsourceCode = 'gsw'}
       "A2057" {$ISOsourceCode = 'en-gb'}
       "A3079" {$ISOsourceCode = 'de'}
       default {Throw "Please assign an ISO code for $SourceLangCode in the script"}
     }
   Switch ($TargetLangCode)
     {
             "A1030" {$ISOTargetCode = 'dk'}
             "A1031" {$ISOTargetCode = 'de'}
             "A1033" {$ISOTargetCode = 'en'}
             "A1034" {$ISOTargetCode = 'es'}
             "A1035" {$ISOTargetCode = 'fi'}
             "A1036" {$ISOTargetCode = 'fr'}
             "A1040" {$ISOTargetCode = 'it'}
             "A1043" {$ISOTargetCode = 'nl'}
             "A1044" {$ISOTargetCode = 'no'}
             "A1045" {$ISOTargetCode = 'pl'}
             "A1046" {$ISOTargetCode = 'pt'}
             "A1053" {$ISOTargetCode = 'se'}
             "A2055" {$ISOTargetCode = 'gsw'}
             "A2057" {$ISOTargetCode = 'en-gb'}
             "A3079" {$ISOTargetCode = 'de'}
       default {Throw "Please assign an ISO code for $TargetLangCode in the script"}
     }
 
     Write-host "ISO 639 language codes source: $ISOsourceCode, target: $ISOTargetCode"
     $SourceLangCode = "-"+ $SourceLangCode + "-"
     $TargetLangCode = "-"+ $TargetLangCode + "-"
     
     # Write-host "Writing XLF-Header…"
     WriteXLFHeader

   
     Write-host "Building hash, this may take a while…"
     foreach ($line in [IO.File]::ReadLines($convertedFileName))
     {
       $k += 1
       if ($k % 1000 -eq 0) {Write-host "Line $k of $Nooflines"}
       $SourceTextLine = $line.ToString()
       if ($SourceTextLine.contains($SourceLangCode))
       {
         $pos1 = $SourceTextLine.IndexOf(":")     
         $SourceCodestringID = $SourceTextLine.Substring(0,$pos1)
         $SourceCaption = $SourceTextLine.Substring($pos1 + 1)
         $NAVUTF8sourcehash.add($SourceCodestringID,$SourceCaption)
       }   
       if ($SourceTextLine.contains($TargetLangCode))
         {
         $pos1 = $SourceTextLine.IndexOf(":")     
         $TargetCodestringID = $SourceTextLine.Substring(0,$pos1)
         $TargetCaption = $SourceTextLine.Substring($pos1 + 1)
         $NAVUTF8targethash.add($TargetCodestringID,$TargetCaption)
       }           
     }

     foreach ($key in $NAVUTF8sourcehash.keys)
       {
           
           $SourceCodestringID = $key
           $SourceCaption = $NAVUTF8sourcehash[$key]
           $TargetCodestringID = $SourceCodestringID -replace $SourceLangCode,$TargetLangCode
           $TargetCaption = $NAVUTF8targethash[$TargetCodestringID]
           $WriteToFile = ($WriteOnlyMissingTargetCaptions -eq 'no') -or (($WriteOnlyMissingTargetCaptions -eq 'yes') -and ([string]::IsNullOrEmpty($TargetCaption)))
           
         
             IF (($writetofile) -and ((![string]::IsNullOrEmpty($TargetCaption) -or ($AllowEmptyTargetCaptions -eq 'yes'))))
                {
                  IF ($TargetCaption -eq 'System.Object[]') {$TargetCaption = ''}
                  WriteBodyLines
                  $i += 1
                  if ($i % 100 -eq 0) {Write-Host "Writing line $i"}
                }
         
      }
   
   WriteXLFfooter
   Write-host $NAVtransfile 'processed to' $XLFoutfile
 }
NAVTransToXLF_v2_AllowEmptyTargetCaptions_v2

In dieser Version v3 kommt zusätzlich zu v2 noch die Frage
"Delete all target caption contents in XLIFF e.g. to recreate these (yes,no) ?" wodurch auch vorhandene Captions in der Zielsprache in der XLIFF entfernt werden, damit man diese bei Bedarf komplett neu erzeugen kann. Hilfreich auch bei alten Datenbanken aus der 2.x.-Zeit oder davor, wo bei Erweiterungen gelegentlich der deutsche Feldname als deutsche Caption DEU und dann unverändert auch als Englisch ENU eingetragen wurde, weil es so schneller ging :roll: .
Code:
    function NAVTransToXLF_v2_AllowEmptyTargetCaptions_v3
     {
       
       #Declare ordered hashtables
       $NAVUTF8sourcehash = [ordered]@{}
       $NAVUTF8targethash = [ordered]@{}
         
        function WriteToFile
         {
          out-file $XLFoutfile -inputobject $args -force -append -Width 500 -Encoding utf8
         }

     
       function WriteXLFHeader
         {
          $CurrLine = "<xliff version='1.2' xmlns='urn:oasis:names:tc:xliff:document:1.2'>"
          # out-file C:\Temp\Translations.xlf -inputobject $Currline -force -append -Width 500 -Encoding utf8
          writetofile $CurrLine
          $OrigFileName = [IO.Path]::GetFileName($NAVtransfile)
         
          $CurrLine = "    <file original='$OrigFileName' source-language='$ISOsourceCode' target-language='$ISOTargetCode' datatype='plaintext'>"
          writetofile $CurrLine
          $CurrLine = "        <body>"
          writetofile $CurrLine
        }
       function WriteBodyLines
         {
          $SourceCaption = $SourceCaption -replace '&','&amp;'
          $TargetCaption = $TargetCaption -replace '&','&amp;'
          $SourceCaption = $SourceCaption -replace '<','&lt;'
          $TargetCaption = $TargetCaption -replace '<','&lt;'
          $SourceCaption = $SourceCaption -replace '>','&gt;'
          $TargetCaption = $TargetCaption -replace '>','&gt;'
          $SourceCaption = $SourceCaption -replace '"','&quot;'
          $TargetCaption = $TargetCaption -replace '"','&quot;'
          $SourceCaption = $SourceCaption -replace $([char]39),'&apos;'
          $TargetCaption = $TargetCaption -replace $([char]39),'&apos;'
          $CurrLine = "            <trans-unit id=$([char]34)$SourceCodestringID$([char]34)>"
          writetofile $CurrLine
          $CurrLine = "                <source xml:lang=$([char]34)$ISOsourceCode$([char]34)>$SourceCaption</source>"
          writetofile $CurrLine
          $CurrLine = "                <target xml:lang=$([char]34)$ISOtargetCode$([char]34)>$TargetCaption</target>"
          writetofile $CurrLine         
          $CurrLine = '            </trans-unit>'
          writetofile $CurrLine
         
         }
       function WriteXLFfooter
         {
            $CurrLine = '        </body>'
            writetofile $CurrLine
            $CurrLine = '    </file>'
            writetofile $CurrLine
            $CurrLine = '</xliff>'
            writetofile $CurrLine
         }
       $NAVtransfile = Read-host "Existing NAV translation file"
       $XLFoutfile = Read-host "Name of new XLF translation file"
       
       [ValidateSet('yes','no')]$AllowEmptyTargetCaptions = Read-Host "Allow empty target captions in XLIFF if no caption translation is available (yes,no) ?"
       [ValidateSet('yes','no')]$WriteOnlyMissingTargetCaptions = Read-Host "Write only trans-unit IDs with missing target captions (yes,no) ?"
       [ValidateSet('yes','no')]$DeleteAllTargetCaptions = Read-Host "Delete all target caption contents in XLIFF e.g. to recreate these (yes,no) ?"
                     
         
       if ($NAVtransfile -eq $XLFoutfile)
         {
           Throw "Source and target file are identical. Source: $NAVtransfile Target: $XLFoutfile"
         }
         
       if (Test-Path $XLFoutfile) {Remove-Item $XLFoutfile}
         
       $lines = Get-Content $NAVtransfile | Measure-Object -Line
       $Nooflines = $lines.Lines
       write-host "File has $Nooflines lines"
     
       $sourceEncoding = [Text.Encoding]::GetEncoding(850)
       $targetEncoding = [Text.Encoding]::GetEncoding(65001)
           
       $convertedFileName = [IO.Path]::GetDirectoryName($NAVtransfile) + "\"+ [IO.Path]::GetFileNameWithoutExtension($NAVtransfile) +"_UTF8" + [IO.Path]::GetExtension($NAVtransfile)
       if (Test-Path $convertedFileName) {Remove-Item $convertedFileName}
           
       $convertedfile = New-Item -path $convertedFileName -type file
           
       $textfile = [IO.File]::ReadAllText($NAVtransfile, $sourceencoding)
       [IO.File]::WriteAllText($convertedfile, $textfile, $targetencoding)
       Write-host $NAVtransfile 'converted to' $convertedFileName
         
       
       $SourceLangCode = Read-host "NAV-Code for source language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
       $TargetLangCode = Read-host "NAV-Code for target language (e.g. A1031 for DEU, A1033 for ENU, A1036 for FRA)"
       if ($SourceLangCode -eq $TargetLangCode)
         {
           Throw "Source and target language code are identical. Source: $SourceLangCode Target: $TargetLangCode"
         }

       
       Switch ($SourceLangCode)
         {
           "A1031" {$ISOsourceCode = 'de'}
           "A1033" {$ISOsourceCode = 'en'}
           "A1034" {$ISOsourceCode = 'es'}
           "A1036" {$ISOsourceCode = 'fr'}
           "A1040" {$ISOsourceCode = 'it'}
           "A1046" {$ISOsourceCode = 'pt'}       
           "A2055" {$ISOsourceCode = 'gsw'}
           "A2057" {$ISOsourceCode = 'en-gb'}
           "A3079" {$ISOsourceCode = 'de'}
           default {Throw "Please assign an ISO code for $SourceLangCode in the script"}
         }
       Switch ($TargetLangCode)
         {
                 "A1030" {$ISOTargetCode = 'dk'}
                 "A1031" {$ISOTargetCode = 'de'}
                 "A1033" {$ISOTargetCode = 'en'}
                 "A1034" {$ISOTargetCode = 'es'}
                 "A1035" {$ISOTargetCode = 'fi'}
                 "A1036" {$ISOTargetCode = 'fr'}
                 "A1040" {$ISOTargetCode = 'it'}
                 "A1043" {$ISOTargetCode = 'nl'}
                 "A1044" {$ISOTargetCode = 'no'}
                 "A1045" {$ISOTargetCode = 'pl'}
                 "A1046" {$ISOTargetCode = 'pt'}
                 "A1053" {$ISOTargetCode = 'se'}
                 "A2055" {$ISOTargetCode = 'gsw'}
                 "A2057" {$ISOTargetCode = 'en-gb'}
                 "A3079" {$ISOTargetCode = 'de'}
           default {Throw "Please assign an ISO code for $TargetLangCode in the script"}
         }
     
         Write-host "ISO 639 language codes source: $ISOsourceCode, target: $ISOTargetCode"
         $SourceLangCode = "-"+ $SourceLangCode + "-"
         $TargetLangCode = "-"+ $TargetLangCode + "-"
         
         # Write-host "Writing XLF-Header…"
         WriteXLFHeader

       
         Write-host "Building hash, this may take a while…"
         foreach ($line in [IO.File]::ReadLines($convertedFileName))
         {
           $k += 1
           if ($k % 1000 -eq 0) {Write-host "Line $k of $Nooflines"}
           $SourceTextLine = $line.ToString()
           if ($SourceTextLine.contains($SourceLangCode))
           {
             $pos1 = $SourceTextLine.IndexOf(":")     
             $SourceCodestringID = $SourceTextLine.Substring(0,$pos1)
             $SourceCaption = $SourceTextLine.Substring($pos1 + 1)
             $NAVUTF8sourcehash.add($SourceCodestringID,$SourceCaption)
           }   
           if ($SourceTextLine.contains($TargetLangCode))
             {
             $pos1 = $SourceTextLine.IndexOf(":")     
             $TargetCodestringID = $SourceTextLine.Substring(0,$pos1)
             $TargetCaption = $SourceTextLine.Substring($pos1 + 1)
             $NAVUTF8targethash.add($TargetCodestringID,$TargetCaption)
           }           
         }

         foreach ($key in $NAVUTF8sourcehash.keys)
           {
               
               $SourceCodestringID = $key
               $SourceCaption = $NAVUTF8sourcehash[$key]
               $TargetCodestringID = $SourceCodestringID -replace $SourceLangCode,$TargetLangCode
               $TargetCaption = $NAVUTF8targethash[$TargetCodestringID]
               $WriteToFile = ($WriteOnlyMissingTargetCaptions -eq 'no') -or (($WriteOnlyMissingTargetCaptions -eq 'yes') -and ([string]::IsNullOrEmpty($TargetCaption)))
               if ($DeleteAllTargetCaptions)
                {$TargetCaption = ''
                $WriteToFile = $true}
               
             
                 IF (($writetofile) -and ((![string]::IsNullOrEmpty($TargetCaption) -or ($AllowEmptyTargetCaptions -eq 'yes'))))
                    {
                      IF ($TargetCaption -eq 'System.Object[]') {$TargetCaption = ''}
                      WriteBodyLines
                      $i += 1
                      if ($i % 100 -eq 0) {Write-Host "Writing line $i"}
                    }
             
          }
       
       WriteXLFfooter
       Write-host $NAVtransfile 'processed to' $XLFoutfile
     }
    NAVTransToXLF_v2_AllowEmptyTargetCaptions_v3

Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.