Custom batch task deletes all segments in file just by looking at them

Hi guys,

I am developing a batch task. My goal is to write a placeholder string into all targets of segments that have a unspecified confirmation level.

Before that i tried to just print all the sub items of a target segment for practice.

I was able to create and run the batch task on a file but after reopening the file all the segments were gone.

I use the  OnFileComplete method because I want to change stuff later.

public override bool OnFileComplete(ProjectFile projectFile, IMultiFileConverter multiFileConverter)
{
    return true;
}

My gut feeling tells me that this is the culprit and I need to write the segments manually again after looking at them? 

Here is my complete code:

using Sdl.FileTypeSupport.Framework.IntegrationApi;
using Sdl.ProjectAutomation.AutomaticTasks;
using Sdl.ProjectAutomation.Core;
using KI18NBatchTask.KI18NBatchTask;

namespace KI18NBatchTask
{
    [AutomaticTask("Test",
                   "Test",
                   "Test",
                   GeneratedFileType = AutomaticTaskFileType.BilingualTarget)]
    [AutomaticTaskSupportedFileType(AutomaticTaskFileType.BilingualTarget)]
    [RequiresSettings(typeof(KI18NBatchTaskSettings), typeof(KI18NBatchTaskSettingsPage))]
    internal class KI18NBatchTaskClass : AbstractFileContentProcessingAutomaticTask
    {

        protected override void OnInitializeTask()
        { 
            base.OnInitializeTask();
        }
        protected override void ConfigureConverter(ProjectFile projectFile, IMultiFileConverter multiFileConverter)
        {
                
            FileProcessor _task = new FileProcessor();
            multiFileConverter.AddBilingualProcessor(_task);
            multiFileConverter.Parse();
        }

        public override void TaskComplete()
        {
            base.TaskComplete();
        }

        public override bool OnFileComplete(ProjectFile projectFile, IMultiFileConverter multiFileConverter)
        {
            return true;
        }

    }
}

using Sdl.FileTypeSupport.Framework.BilingualApi;
using System.Diagnostics;
using System.Linq;

namespace KI18NBatchTask.KI18NBatchTask
{
    internal class FileProcessor : AbstractBilingualContentProcessor
    {

        public override void ProcessParagraphUnit(IParagraphUnit paragraphUnit)
        {
            //base.ProcessParagraphUnit(paragraphUnit);
            if (paragraphUnit.IsStructure) { return; }

            foreach (var segmentPair in paragraphUnit.SegmentPairs.ToList())
            {
                segmentPair.Target.AllSubItems.ToList().ForEach(item => {
                    Debug.WriteLine(item);
                });
            }
        }
    }
}




Addtional question:
Why is it sometimes necessary to call "base.ProcessParagraphUnit(paragraphUnit);" in my AbstractBilingualContentProcessor?
The Anonymizer Batch Task that is often referenced as an example does it. 
The Clean Up Batch task does not do it. But this one is messy and I also don't understand why it uses a combination of AbstractBilingualContentHandler wraped into a BilingualContentHandlerAdapter instead of a AbstractBilingualContentProcessor.

Sorry for the messy question. Hope anybody could help me with the deleting issue!
Best
Lukas



changed wording
[edited by: Lukas Ley at 3:23 PM (GMT 1) on 30 Jul 2025]
Parents
  • Hi  , The issue is in your OnFileComplete override. You're not calling the base implementation, which is crucial for the framework to properly save the processed content.

    public override bool OnFileComplete(ProjectFile projectFile, IMultiFileConverter multiFileConverter)
    {
        // Always call the base implementation to ensure proper file processing completion
        return base.OnFileComplete(projectFile, multiFileConverter);
    }

    When you override OnFileComplete without calling the base method, you're essentially bypassing the framework's internal logic that handles saving the processed bilingual content back to the file. That's why your segments disappear - they're being processed but never saved.

    Note: If you're not doing any custom logic in OnFileComplete, you can actually remove this override entirely. The base implementation will handle everything correctly.

    Missing base call in ProcessParagraphUnit: You should call the base method to ensure the content flows through the processing pipeline:

    public override void ProcessParagraphUnit(IParagraphUnit paragraphUnit)
    {
        if (paragraphUnit.IsStructure) 
        { 
            base.ProcessParagraphUnit(paragraphUnit);
            return; 
        }
    
        foreach (var segmentPair in paragraphUnit.SegmentPairs.ToList())
        {
            segmentPair.Target.AllSubItems.ToList().ForEach(item => {
                Debug.WriteLine(item);
            });
        }
        
        // Always call base to ensure the paragraph unit continues through the pipeline
        base.ProcessParagraphUnit(paragraphUnit);
    }

    Regarding your additional question about base.ProcessParagraphUnit():

    The reason for calling base.ProcessParagraphUnit(paragraphUnit) depends on your inheritance chain and what you want to achieve:

    • AbstractBilingualContentProcessor: When you inherit from this, calling the base method ensures that the processed content continues through the pipeline to any subsequent processors. If you don't call it, you're essentially "consuming" the content and it won't flow to the next processor in the chain.
    • Different implementations in examples:
      • The Anonymizer calls the base method because it wants to modify content AND pass it along
      • Some tasks might not call it if they're the final processor in the chain or if they handle the pipeline flow differently
      • AbstractBilingualContentHandler vs AbstractBilingualContentProcessor: The Clean Up task might use AbstractBilingualContentHandler wrapped in BilingualContentHandlerAdapter for different processing patterns or legacy reasons. AbstractBilingualContentProcessor is generally the preferred approach for newer implementations.

    Best practice: Always call the base method unless you have a specific reason not to and you fully understand the implications.

    Simplified working version of your task:

    [AutomaticTask("Test", "Test", "Test", GeneratedFileType = AutomaticTaskFileType.BilingualTarget)]
    [AutomaticTaskSupportedFileType(AutomaticTaskFileType.BilingualTarget)]
    [RequiresSettings(typeof(KI18NBatchTaskSettings), typeof(KI18NBatchTaskSettingsPage))]
    internal class KI18NBatchTaskClass : AbstractFileContentProcessingAutomaticTask
    {
        protected override void ConfigureConverter(ProjectFile projectFile, IMultiFileConverter multiFileConverter)
        {
            FileProcessor task = new FileProcessor();
            multiFileConverter.AddBilingualProcessor(task);
            multiFileConverter.Parse();
        }
        
        // Remove OnFileComplete override entirely - let base handle it
    }
    
    internal class FileProcessor : AbstractBilingualContentProcessor
    {
        public override void ProcessParagraphUnit(IParagraphUnit paragraphUnit)
        {
            if (paragraphUnit.IsStructure) 
            { 
                base.ProcessParagraphUnit(paragraphUnit);
                return; 
            }
    
            foreach (var segmentPair in paragraphUnit.SegmentPairs)
            {
                // Your processing logic here
                foreach (var item in segmentPair.Target.AllSubItems)
                {
                    Debug.WriteLine(item);
                }
            }
            
            base.ProcessParagraphUnit(paragraphUnit);
        }
    }

    Patrick Andrew Hartnett | RWS Group

  •  
    After my test setup ran as expected I tried to change the content of a SegmentPair Target in a PragraphUnit and it had not changed anything when reopening the file.
    Then I changed OnFileComplete back to return just "true" again and now it works and it does not delete the segments like before.
    This is pretty confusing behavior. Maybe there should be a guide.

  • Hi  ,  thx for reminding me that there is also come dev tip 'n tricks on the community location; I'll update or remove these as they are aren't accurate examples.  Probably better sticking to the public api documentation here: https://developers.rws.com/studio-api-docs/apiconcepts/batchtasks/what_our_sample_batch_task_should_do.html

    You can also find more complete examples of how to fully implement these overrides from the batch task from the plugins for multilingual xml and excel here:

    https://github.com/RWS/Sdl-Community/tree/master/Multilingual%20Excel%20FileType/Multilingual.Excel.FileType/BatchTasks

    https://github.com/RWS/Sdl-Community/tree/master/Multilingual%20XML%20FileType/Multilingual.XML.FileType/BatchTasks

    of from the public API examples repo here:

    https://github.com/RWS/trados-studio-api-samples/tree/master/ProjectAutomation/BatchTaskSamples

    have fun!

    Patrick Andrew Hartnett | RWS Group

Reply Children
No Data