File: Model\UnstructuredDocument.cs
Web Access
Project: src\src\Microsoft.DotNet.XliffTasks\Microsoft.DotNet.XliffTasks.csproj (Microsoft.DotNet.XliffTasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.IO;
 
namespace XliffTasks.Model
{
    internal sealed class UnstructuredDocument : TranslatableDocument
    {
        const string TranslatableSpanMarker = "@@@";
        const string TranslatableSpanSeparator = "|";
 
        private List<string> _fragments = new();
        private List<UnstructuredTranslatableNode> _nodes = new();
 
        protected override IEnumerable<TranslatableNode> GetTranslatableNodes()
        {
            return _nodes;
        }
 
        protected override void LoadCore(TextReader reader)
        {
            string text = reader.ReadToEnd();
            int lastSpanEnd = 0;
            int spanStart = text.IndexOf(TranslatableSpanMarker);
            while (spanStart >= 0)
            {
                // the previous span of text is untranslatable and is simply copied
                string plainSpan = text.Substring(lastSpanEnd, spanStart - lastSpanEnd);
                _fragments.Add(plainSpan);
 
                // next, find the translatable span
                lastSpanEnd = text.IndexOf(TranslatableSpanMarker, spanStart + 1);
                if (lastSpanEnd < 0)
                {
                    throw new InvalidOperationException($"No end of span marker '{TranslatableSpanMarker}' found.");
                }
 
                lastSpanEnd += TranslatableSpanMarker.Length; // account for the length of the end marker
                int spanLength = lastSpanEnd - spanStart - TranslatableSpanMarker.Length * 2; // trim off the marker start/end length
                string translatableSpan = text.Substring(spanStart + TranslatableSpanMarker.Length, spanLength);
                int separatorIndex = translatableSpan.IndexOf(TranslatableSpanSeparator);
                if (separatorIndex < 0)
                {
                    throw new InvalidOperationException($"No span separator '{TranslatableSpanSeparator}' found.");
                }
 
                string id = translatableSpan.Substring(0, separatorIndex);
                string source = translatableSpan.Substring(separatorIndex + TranslatableSpanSeparator.Length);
 
                // keep the original span's text
                _nodes.Add(new UnstructuredTranslatableNode(_fragments, _fragments.Count, id, source));
                _fragments.Add(source);
 
                spanStart = lastSpanEnd >= text.Length
                    ? -1 // don't search beyond the end of the text
                    : text.IndexOf(TranslatableSpanMarker, lastSpanEnd + 1);
            }
 
            if (lastSpanEnd < text.Length)
            {
                // add final span
                _fragments.Add(text.Substring(lastSpanEnd));
            }
        }
 
        protected override void SaveCore(TextWriter writer)
        {
            foreach (string fragment in _fragments)
            {
                writer.Write(fragment);
            }
        }
 
        private sealed class UnstructuredTranslatableNode : TranslatableNode
        {
            private IList<string> _fragments;
            private int _fragmentIndex;
 
            public UnstructuredTranslatableNode(IList<string> fragments, int fragmentIndex, string id, string source)
                : base(id, source, null)
            {
                _fragments = fragments;
                _fragmentIndex = fragmentIndex;
            }
 
            public override void Translate(string translation)
            {
                _fragments[_fragmentIndex] = translation;
            }
        }
    }
}