File: Model\VsctDocument.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.Collections.Generic;
using System.IO;
using System.Xml.Linq;
 
namespace XliffTasks.Model
{
    /// <summary>
    /// A <see cref="TranslatableDocument"/> for files in .vsct format.
    /// See https://msdn.microsoft.com/en-us/library/bb164699.aspx
    /// </summary>
    internal sealed class VsctDocument : TranslatableXmlDocument
    {
        protected override IEnumerable<TranslatableNode> GetTranslatableNodes()
        {
            HashSet<string> nonUniqueIds = FindNonUniqueIds();
 
            foreach (XElement strings in Document.Descendants(Document.Root.Name.Namespace + "Strings"))
            {
                string id = strings.Parent.Attribute("id").Value;
                if (nonUniqueIds.Contains(id))
                {
                    // The ID by itself is not unique in this document; we must include
                    // the GUID as well.
                    string guid = strings.Parent.Attribute("guid").Value;
                    id = guid + "|" + id;
                }
 
                foreach (XElement child in strings.Elements())
                {
                    XName name = child.Name;
 
                    if (name.LocalName == "CanonicalName")
                    {
                        // CanonicalName is never localized.
                        // LocCanonicalName can be used to specify a localized alternative.
                        // See https://msdn.microsoft.com/en-us/library/bb491712.aspx
                        continue;
                    }
 
                    yield return new TranslatableXmlElement(
                        id: $"{id}|{name.LocalName}",
                        source: child.Value,
                        note: null,
                        element: child);
                }
            }
        }
 
        /// <summary>
        /// Finds the set of IDs that do not uniquely identify a single set of strings.
        /// Within a .vsct file only the combination of GUID and ID needs to be unique,
        /// but for brevity we'd prefer to use just the ID when we can. By finding the
        /// set of non-unique IDs we can identify when this will not be sufficient.
        /// </summary>
        /// <returns></returns>
        private HashSet<string> FindNonUniqueIds()
        {
            HashSet<string> idsAlreadySeen = new();
            HashSet<string> conflictingIds = new();
 
            foreach (XElement strings in Document.Descendants(Document.Root.Name.Namespace + "Strings"))
            {
                string id = strings.Parent.Attribute("id").Value;
 
                if (!idsAlreadySeen.Add(id))
                {
                    conflictingIds.Add(id);
                }
            }
 
            return conflictingIds;
        }
 
        public override void RewriteRelativePathsToAbsolute(string sourceFullPath)
        {
            foreach (XElement imageTag in Document.Descendants(Document.Root.Name.Namespace + "Bitmap"))
            {
                XAttribute hrefAttribute = imageTag.Attribute("href");
 
                if (hrefAttribute != null)
                {
                    string resourceRelativePath = hrefAttribute.Value.Replace('\\', Path.DirectorySeparatorChar);
 
                    string absolutePath = Path.Combine(Path.GetDirectoryName(sourceFullPath), resourceRelativePath);
 
                    imageTag.Attribute("href").Value = absolutePath;
                }
            }
        }
    }
}