File: Tasks\TransformTemplates.cs
Web Access
Project: 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;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using XliffTasks.Model;

namespace XliffTasks.Tasks;

[MSBuildMultiThreadableTask]
public sealed class TransformTemplates : XlfTask
{
    [Required]
    public ITaskItem[] Templates { get; set; }

    [Required]
    public ITaskItem[] UnstructuredResources { get; set; }

    [Required]
    public string[] Languages { get; set; }

    [Required]
    public string TranslatedOutputDirectory { get; set; }

    [Output]
    public ITaskItem[] TransformedTemplates { get; set; }

    protected override void ExecuteCore()
    {
        List<ITaskItem> transformedTemplates = new();
        Dictionary<string, ITaskItem> resourceMap = UnstructuredResources.ToDictionary(item => item.GetMetadata("FullPath"));
        foreach (ITaskItem template in Templates)
        {
            // special-case the default template items as the 1033 culture
            ITaskItem defaultTemplate = TransformTemplate(template, language: null, resourceMap: null);
            transformedTemplates.Add(defaultTemplate);

            // and process other languages like normal
            foreach (string language in Languages)
            {
                // The neutral .xlf file carries no translations.
                if (XlfTask.IsNeutralLanguage(language))
                {
                    continue;
                }

                ITaskItem item = TransformTemplate(template, language, resourceMap);
                transformedTemplates.Add(item);
            }
        }

        TransformedTemplates = transformedTemplates.ToArray();
    }

    private ITaskItem TransformTemplate(ITaskItem template, string language, IDictionary<string, ITaskItem> resourceMap)
    {
        if ((language == null) ^ (resourceMap == null))
        {
            throw new ArgumentException($"Either both '{nameof(language)}' and '{nameof(resourceMap)}' must be specified, or they both must be 'null'.");
        }

        bool transformingDefaultTemplate = language == null;
        string templateCulture = transformingDefaultTemplate ? "1033" : language;

        string templateName = Path.GetFileNameWithoutExtension(template.ItemSpec);
        string templatePath = template.GetMetadata("FullPath");
        string templateDirectory = Path.GetDirectoryName(templatePath);
        XDocument templateXml = XDocument.Load(templatePath);

        // create a copy of the .vstemplate and all files
        string localizedTemplateDirectory = transformingDefaultTemplate
            ? Path.Combine(TranslatedOutputDirectory, $"{templateName}.default.1033")
            : Path.Combine(TranslatedOutputDirectory, $"{templateName}.{language}");
        Directory.CreateDirectory(TaskEnvironment.GetAbsolutePath(localizedTemplateDirectory));
        string cultureSpecificTemplateFile = Path.Combine(localizedTemplateDirectory, Path.GetFileName(template.ItemSpec));
        File.Copy(TaskEnvironment.GetAbsolutePath(templatePath), TaskEnvironment.GetAbsolutePath(cultureSpecificTemplateFile), overwrite: true);

        // copy the template project files
        foreach (XElement projectNode in templateXml.Descendants().Where(d => d.Name.LocalName == "Project"))
        {
            string projectFileFullPath = Path.Combine(templateDirectory, projectNode.Attribute("File").Value);
            File.Copy(TaskEnvironment.GetAbsolutePath(projectFileFullPath), TaskEnvironment.GetAbsolutePath(Path.Combine(localizedTemplateDirectory, Path.GetFileName(projectNode.Attribute("File").Value))), overwrite: true);
        }

        // copy the template project items
        foreach (XElement templateItem in templateXml.Descendants().Where(d => d.Name.LocalName == "ProjectItem"))
        {
            string templateItemFullPath = Path.Combine(templateDirectory, templateItem.Value);
            string templateItemDestinationPath = Path.Combine(localizedTemplateDirectory, templateItem.Value);
            AbsolutePath templateItemDestinationAbsolutePath = TaskEnvironment.GetAbsolutePath(templateItemDestinationPath);
            if (transformingDefaultTemplate)
            {
                // if not localizing anything, simply strip out the translation markers
                UnstructuredDocument document = new();
                document.Load(new FileInfo(TaskEnvironment.GetAbsolutePath(templateItemFullPath)));
                Dictionary<string, string> defaultTranslation = document.Nodes.ToDictionary(node => node.Id, node => node.Source);
                document.Translate(defaultTranslation);
                document.Save(new FileInfo(templateItemDestinationAbsolutePath));
            }
            else
            {
                // try to localize the template items
                if (resourceMap.TryGetValue(templateItemFullPath, out ITaskItem unstructuredResource))
                {
                    // copy a localized file
                    string localizedFileName = string.Concat(
                        Path.GetFileNameWithoutExtension(unstructuredResource.ItemSpec),
                        ".",
                        language,
                        Path.GetExtension(unstructuredResource.ItemSpec));
                    File.Copy(TaskEnvironment.GetAbsolutePath(Path.Combine(TranslatedOutputDirectory, localizedFileName)), templateItemDestinationAbsolutePath, overwrite: true);
                }
                else
                {
                    // copy the original unaltered file
                    File.Copy(TaskEnvironment.GetAbsolutePath(templateItemFullPath), templateItemDestinationAbsolutePath, overwrite: true);
                }
            }
        }

        TaskItem item = new(cultureSpecificTemplateFile);
        item.SetMetadata("Culture", templateCulture);
        return item;
    }
}