在撰寫 ASP.NET 時,.NET 程式部分可用 Resource 去做多語的部分,JavaScript 這邊雖然也有 L10N 的解決方案,但是若走不同的解決方案,難以避免有些詞彙會重複定義。

這邊筆者嘗試使用 T4 來解決這樣的問題。


/**
* Resources
* ---------
* This file is auto-generated by a tool
* 2012 Jochen van Wylick
* 2016 LarryNung
**/

var  = {};

. = "";

 __savedOutputs = new List();
    Engine __engine = new Engine();

    void DeleteOldOutputs()
    {
        EnvDTE.ProjectItem templateProjectItem = __getTemplateProjectItem();
        foreach (EnvDTE.ProjectItem childProjectItem in templateProjectItem.ProjectItems)
        {
            if (!__savedOutputs.Contains(childProjectItem.Name))
                childProjectItem.Delete();
        }
    }

    void ProcessTemplate(string templateFileName, string outputFileName)
    {
        string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
        string outputFilePath = Path.Combine(templateDirectory, outputFileName);

        string template = File.ReadAllText(Host.ResolvePath(templateFileName));
        string output = __engine.ProcessTemplate(template, Host);
        File.WriteAllText(outputFilePath, output);

        EnvDTE.ProjectItem templateProjectItem = __getTemplateProjectItem();
        templateProjectItem.ProjectItems.AddFromFile(outputFilePath);

        __savedOutputs.Add(outputFileName);
    }

    void SaveOutput(string outputFileName)
    {
        string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
        string outputFilePath = Path.Combine(templateDirectory, outputFileName);

        File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString(), Encoding.UTF8);
        this.GenerationEnvironment = new StringBuilder();

        EnvDTE.ProjectItem templateProjectItem = __getTemplateProjectItem();
        templateProjectItem.ProjectItems.AddFromFile(outputFilePath);

        __savedOutputs.Add(outputFileName);
    }

    EnvDTE.ProjectItem __getTemplateProjectItem()
    {
        EnvDTE.Project dteProject = __getTemplateProject();

        IVsProject vsProject = __dteProjectToVsProject(dteProject);

        int iFound = 0;
        uint itemId = 0;
        VSDOCUMENTPRIORITY[] pdwPriority = new VSDOCUMENTPRIORITY[1];
        int result = vsProject.IsDocumentInProject(Host.TemplateFile, out iFound, pdwPriority, out itemId);
        if (result != VSConstants.S_OK)
            throw new Exception("Unexpected error calling IVsProject.IsDocumentInProject");
        if (iFound == 0)
            throw new Exception("Cannot retrieve ProjectItem for template file");
        if (itemId == 0)
            throw new Exception("Cannot retrieve ProjectItem for template file");

        Microsoft.VisualStudio.OLE.Interop.IServiceProvider itemContext = null;
        result = vsProject.GetItemContext(itemId, out itemContext);
        if (result != VSConstants.S_OK)
            throw new Exception("Unexpected error calling IVsProject.GetItemContext");
        if (itemContext == null)
            throw new Exception("IVsProject.GetItemContext returned null");
    
        ServiceProvider itemContextService = new ServiceProvider(itemContext);
        EnvDTE.ProjectItem templateItem = (EnvDTE.ProjectItem)itemContextService.GetService(typeof(EnvDTE.ProjectItem));
        Debug.Assert(templateItem != null, "itemContextService.GetService returned null");

        return templateItem;
    }

    EnvDTE.Project __getTemplateProject()
    {
        IServiceProvider hostServiceProvider = (IServiceProvider)Host;
        if (hostServiceProvider == null)
            throw new Exception("Host property returned unexpected value (null)");

        EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
        if (dte == null)
            throw new Exception("Unable to retrieve EnvDTE.DTE");

        Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects;
        if (activeSolutionProjects == null)
            throw new Exception("DTE.ActiveSolutionProjects returned null");

        EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0);
        if (dteProject == null)
            throw new Exception("DTE.ActiveSolutionProjects[0] returned null");

        return dteProject;
    }

    static IVsProject __dteProjectToVsProject(EnvDTE.Project project)
    {
        if (project == null) 
            throw new ArgumentNullException("project");
            
        string projectGuid = null;        

        // DTE does not expose the project GUID that exists at in the msbuild project file.        
        // Cannot use MSBuild object model because it uses a static instance of the Engine,         
        // and using the Project will cause it to be unloaded from the engine when the         
        // GC collects the variable that we declare.       
        using (XmlReader projectReader = XmlReader.Create(project.FileName))
        {
            projectReader.MoveToContent();
            object nodeName = projectReader.NameTable.Add("ProjectGuid");
            while (projectReader.Read())
            {
                if (Object.Equals(projectReader.LocalName, nodeName))
                {
                    projectGuid = (string)projectReader.ReadElementContentAsString(); 
                    break;
                }
            }
        }
        if (string.IsNullOrEmpty(projectGuid))
            throw new Exception("Unable to find ProjectGuid element in the project file");

        Microsoft.VisualStudio.OLE.Interop.IServiceProvider dteServiceProvider = 
            (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)project.DTE;
        IServiceProvider serviceProvider = new ServiceProvider(dteServiceProvider); 
        IVsHierarchy vsHierarchy = VsShellUtilities.GetHierarchy(serviceProvider, new Guid(projectGuid));
            
        IVsProject vsProject = (IVsProject)vsHierarchy;
        if (vsProject == null)
            throw new ArgumentException("Project is not a VS project.");
        return vsProject;
    }
#>

像是這邊筆者準備了不同的資源檔。

{% img /images/posts/T4JSResource/1.png %}

透過 T4 會產生對應的 js 檔。

{% img /images/posts/T4JSResource/2.png %}

只要將 js 檔引用進來就可以直接使用。

{% img /images/posts/T4JSResource/3.png %}

{% img /images/posts/T4JSResource/4.png %}