T4 Template - JsResource.tt

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ assembly name="System.Windows.Forms" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="Microsoft.VisualStudio.OLE.Interop" #>
<#@ assembly name="Microsoft.VisualStudio.Shell" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.VisualStudio.Shell" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ output extension=".js"#>

<#
var path = Path.GetDirectoryName(Host.TemplateFile) + "/App_GlobalResources/";
var resourceFiles= Directory.GetFiles(path, "*.resx");
foreach (var resourceFile in resourceFiles) {
var fileName = Path.GetFileNameWithoutExtension(resourceFile);
var resourceName = Regex.Match(fileName, "([^.]*)").Groups[1].Value;
#>
/**
* Resources
* ---------
* This file is auto-generated by a tool
* 2012 Jochen van Wylick
* 2016 LarryNung
**/

var <#=resourceName #> = {};

<#
var resxSet = new ResXResourceSet(Host.ResolvePath(resourceFile));
foreach (DictionaryEntry item in resxSet) {
#>
<#=resourceName#>.<#=item.Key.ToString() #> = "<#=resxSet.GetString(item.Key.ToString()).Replace("\r\n", string.Empty).Replace("'","\\'")#>";
<#
 }
#>
<#
SaveOutput(fileName + ".js");
}
DeleteOldOutputs();
#>
<#+

List<string> __savedOutputs = new List<string>();
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;
}
#>


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


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


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