Handling translations in Business Central can be very time consuming, so to help you a little bit I will share with you the way that I handle translations in AL 😊.

First off you will need to install the Visual Studio Code extension called XLIFF Sync, this is a great tool which allows you easily to keep all your Xliff files in sync.

To use this tool simply press F1 or CTRL+SHIFT+P and here you will have the ability to create a new Xliff file for any language based on your existing xliff file.

Once you add new fields, actions etc. and you wish to add the new translations to your xliff files you can run the command

This will update all your xliff files with the new translations, which you can then translate in the xliff file. However sometimes we would like to use the translations from the base application, or from another xliff file 🤔, to help you with this I have created some simple PowerShell scripts to make it easier, you can find all the source code here XliffTranslationTool.ps1 – Repos (azure.com) everything is provided as is, and you can do with it what you like 🤷‍♂️. The fist function is called UpdateTranslationsBasedOnAnotherXliffFile and looks as follows:

function UpdateTranslationsBasedOnAnotherXliffFile
{
    Param (
        [Parameter(ValueFromPipelineByPropertyName=$True)]
        $baseXliff = '',
        [Parameter(ValueFromPipelineByPropertyName=$True)]
        $targetXliff = '',
        [Parameter(ValueFromPipelineByPropertyName=$True)]
        $output = ''

    )
    [System.Xml.XmlDocument]$file = new-object System.Xml.XmlDocument

    [XML]$units = Get-Content $baseXliff

    $hashBase = @{}


     ForEach ($unit in $units.xliff.file.body.group.'trans-unit') 
     {
         if(-not $hashBase.ContainsKey($unit.source))
         {
            $hashBase.Add($unit.source,$unit.target.InnerText) 
         
          }

     }

    [System.Xml.XmlDocument]$file = new-object System.Xml.XmlDocument

    [XML]$units = Get-Content $targetXliff


     ForEach ($unit in $units.xliff.file.body.group.'trans-unit') 
     {
     
        if($unit.target.State -eq "needs-translation")
        {
            if($hashBase.ContainsKey($unit.source))
            {
                $target = $hashBase.Item($unit.source);
                $unit.target.InnerText = $target; 
                $unit.target.State = "";              
            }
            
        }             

     }

     $units.Save($output)

}

This function takes three parameters, the first which points to the xliff file from which you wish to copy your translations from, the second is your own xliff file, and the last is where you would like to place the newly merged translation file the call could look something like this:

UpdateTranslationsBasedOnAnotherXliffFile -baseXliff "C:\temp\Base Application.da-DK.xlf" -targetXliff "C:\Users\denni\Documents\AL\ALProject1\Translations\ALProject1.da-DK.xlf" -output "C:\Temp\Out\ALProject1.da-DK.xlf"

The next function is called ExportTranslationsToCSV and this will export all your translations into a CSV file with two columns the source and the target, this takes two parameters the first is your Xliff file and the second is the location where you wish to create your csv file:

function ExportTranslationsToCSV
{
    Param (
        [Parameter(ValueFromPipelineByPropertyName=$True)]
        $FromXliff = '',
        [Parameter(ValueFromPipelineByPropertyName=$True)]
        $output = ''
    )
    
    [System.Xml.XmlDocument]$file = new-object System.Xml.XmlDocument

    [XML]$units = Get-Content $FromXliff

    $hashBase = @{}


    $out = ForEach ($unit in $units.xliff.file.body.group.'trans-unit') 
        {

            $target = $unit.target.ToString();     

            if($target -eq "target")
                {
                    $target = $unit.target.InnerText;
                }

            New-Object -TypeName PSObject -Property @{
                Source = $unit.source
                Target = $target;

              } | Select-Object Source,Target
         
        }

    $out | Export-Csv $output -Encoding UTF8 -Delimiter ';' -NoTypeInformation
}

This could be called with something like this:

ExportTranslationsToCSV -FromXliff "C:\Temp\Out\ALProject1.da-DK.xlf" -output C:\temp\test.csv 

The last function is called ImportToXliffFromCSV and this function will create a new xliff file based on a CSV and your own xliff file, it takes the following parameters the out put where you wish your new xliff file to be created, from csv which will be your csv file holding the translations and target which is your current xliff file:

function ImportToXliffFromCSV
{

    Param (
            [Parameter(ValueFromPipelineByPropertyName=$True)]
            $output = '',
            [Parameter(ValueFromPipelineByPropertyName=$True)]
            $FromCSV = '',        
            [Parameter(ValueFromPipelineByPropertyName=$True)]
            $targetXliff = ''
          )
    
    $imported = import-csv -Delimiter ';' -Path $FromCSV

    $hashBase = @{}
    
     ForEach ($item in $imported) 
     {
         if(-not $hashBase.ContainsKey($item.source))
             {
                $hashBase.Add($item.source,$item.target) 
             }

     }

    [System.Xml.XmlDocument]$file = new-object System.Xml.XmlDocument

    [XML]$units = Get-Content $targetXliff


     ForEach ($unit in $units.xliff.file.body.group.'trans-unit') 
     {

      if($unit.target.State -eq "needs-translation")
        {
            if($hashBase.ContainsKey($unit.source))
                {          
                    $target = $hashBase.Item($unit.source);
                    $unit.target.InnerText = $target; 
                    $unit.target.State = "";               
                }         
        }            

     }

    $units.Save($output)
}

The function could be called with something like this:

ImportToXliffFromCSV -output "C:\Temp\Out\ALProject1.da-DK.xlf" -FromCSV 'C:\temp\test.csv' -targetXliff "C:\Temp\Out\ALProject1.da-DK.xlf"

And that is it, I will give you a much more inadept look and demo in the video below, and since everything is written in PowerShell you could also use this tool in your pipelines; where you for example could let your pipeline create the CSV fil and upload or send it to your customers for them to translate it, and then your pipeline could also import the CSV file from a webservice which holds the translated CSV file, but I will leave that part to you to play with 😉

And until next time stay safe 😷💉

Leave a Reply