如何通过覆盖 MSBuild 目标来防止外语资源生成?
How do I prevent foreign language resource generation by overriding MSBuild targets?
我正在努力减少大型 C#/ASP.NET 解决方案的编译时间。我们的解决方案使用通常的 resx 文件方法翻译成大约十几种外语。这些资源文件的解析和编译,大大拖慢了我们的编译时间,是每天的挫折。
我知道可以创建自定义资源提供程序并摆脱 .resx 文件。现在,请假设我们必须坚持使用 .resx 文件。
通过从我们的 .csproj 文件中排除除默认区域设置 .resx 文件之外的所有文件,我能够将编译时间缩短一半。我们的开发人员不需要在日常开发过程中编译其他十几种语言。
我正在寻找防止编译外语 .resx 文件的方法。我想出了两种方法,我正在寻找一种方法是否更好,或者是否有其他更好的方法。
我想到的两个:
- 编写可以去除非默认 .resx 文件并重新添加到各种 .csproj 文件中的脚本。也许我们会在版本控制中保留最小的 .csproj 文件,并有一个单独的构建过程来递归地重新添加 .resx 文件,以便重新集成新的翻译、执行测试和进行我们的部署构建。
- 找出一种方法来覆盖执行 resx 解析和编译的内置 MSBuild 目标,有效地禁用除默认语言之外的所有语言。开发人员可能 enable/disable 使用简单的编译标志或构建开关来实现此行为。我还没有深入研究 Microsoft 提供的 .target 文件来了解这个解决方案实际上有多合理或可维护。
更新
我编写了以下 Powershell 脚本,将我所有的外语 EmbeddedResources 和 Compile 元素移动到一个具有 Conditional 属性的新 ItemGroup 中。
$root = "C:\Code\solution_root\"
# Find all csproj files.
$projects = Get-ChildItem -Path $root -Recurse -ErrorAction SilentlyContinue -Filter *.csproj | Where-Object { $_.Extension -eq '.csproj' }
# Use a hashtable to gather a unique list of suffixes we moved for sanity checking - make sure we didn't
# relocate anything we didn't intend to. This is how I caught I was moving javascript files I didn't intend to.
$suffixes = @{}
# Find foreign resources ending in .resx and .Designer.cs
# Use a regex capture to so we can count uniques.
$pattern = "\.(?<locale>\w{2}(-\w{2,3})?\.(Designer.cs|resx))$"
foreach ($project in $projects)
{
"Processing {0}" -f $project.FullName
# Load the csproj file as XML
$xmlDoc = new-object XML
$xmlDoc.Load($project.FullName)
# Set namespace for XPath queries
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
$ns.AddNamespace("ns", $xmlDoc.DocumentElement.NamespaceURI)
$count = 0
$embeds = $xmlDoc.SelectNodes("//ns:EmbeddedResource",$ns)
$compiles = $xmlDoc.SelectNodes("//ns:Compile",$ns)
# Create new conditional ItemGroup node if it does not exist.
# Side-effect - every csproj will get this new element regardless of whether it
# contains foreign resources. That works for us, might not for you.
$moveToNode = $xmlDoc.SelectSingleNode("//ns:ItemGroup[@Condition=`" '`$(Configuration)'=='Release' `"]", $ns)
if ($moveToNode -eq $null) {
# When creating new elements, pass in the NamespaceURI from the parent node.
# If we don't do this, elements will get a blank namespace like xmlns="", and this will break compilation.
# Hat tip to
$conditionAtt = $xmlDoc.CreateAttribute("Condition")
$conditionAtt.Value = " '`$(Configuration)'=='Release' "
$moveToNode = $xmlDoc.CreateElement("ItemGroup", $xmlDoc.Project.NamespaceURI)
$ignore = $moveToNode.Attributes.Append($conditionAtt)
$ignore = $xmlDoc.LastChild.AppendChild($moveToNode)
}
# Loop over the EmbeddedResource and Compile elements.
foreach ($resource in ($embeds += $compiles)) {
# Exclude javascript files which I found in our Web project.
# These look like *.js.resx or *.js.Designer.cs and were getting picked up by my regex.
# Yeah, I could make a better regex, but I'd like to see my kids today.
if ($resource.Include -notmatch "js\.(Designer.cs|resx)$" -and $resource.Include -match $pattern ) {
# We have a foreign-language resource.
# Track unique suffixes for reporting later.
$suffix = $matches['locale']
if (!$suffixes.ContainsKey($suffix)) {
$ignore = $suffixes.Add($suffix,"")
}
$ignore = $moveToNode.InsertBefore($resource, $null)
# Count how many we moved per project.
$count += 1
}
}
"Moved {0} resources in {1}.`n" -f $count, $project.Name
$xmlDoc.Save($project.FullName)
}
echo "The following unique suffixes were processed."
$suffixes.Keys | sort
您可以利用 MSBuild 中的现有机制来处理这种情况,例如 <Choose>
元素 documentated here 和引入您自己的构建配置的选项。如果您的开发人员在日常工作中只构建 Debug 配置,您甚至可能不需要自己的构建配置:您可以设置您的项目,以便所有外语资源仅包含在 Release 构建中。
此项目文件部分将确保仅在 Release 配置中构建波兰语资源(语言代码可能有误,但您明白了):
<Choose>
<When Condition=" '$(Configuration)'=='Release' ">
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.pl.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.pl.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.pl.Designer.cs">
<DependentUpon>Resources.pl.resx</DependentUpon>
<AutoGen>True</AutoGen>
</Compile>
<!-- TODO: add other language resources as necessary-->
</ItemGroup>
</When>
</Choose>
如果您的开发人员需要构建发布配置,您可以创建自己的配置 ReleaseResx 并仅在该配置中包含外语 .resx 文件。
你的两个建议也行,我只是觉得这种方法更干净。
我正在努力减少大型 C#/ASP.NET 解决方案的编译时间。我们的解决方案使用通常的 resx 文件方法翻译成大约十几种外语。这些资源文件的解析和编译,大大拖慢了我们的编译时间,是每天的挫折。
我知道可以创建自定义资源提供程序并摆脱 .resx 文件。现在,请假设我们必须坚持使用 .resx 文件。
通过从我们的 .csproj 文件中排除除默认区域设置 .resx 文件之外的所有文件,我能够将编译时间缩短一半。我们的开发人员不需要在日常开发过程中编译其他十几种语言。
我正在寻找防止编译外语 .resx 文件的方法。我想出了两种方法,我正在寻找一种方法是否更好,或者是否有其他更好的方法。
我想到的两个:
- 编写可以去除非默认 .resx 文件并重新添加到各种 .csproj 文件中的脚本。也许我们会在版本控制中保留最小的 .csproj 文件,并有一个单独的构建过程来递归地重新添加 .resx 文件,以便重新集成新的翻译、执行测试和进行我们的部署构建。
- 找出一种方法来覆盖执行 resx 解析和编译的内置 MSBuild 目标,有效地禁用除默认语言之外的所有语言。开发人员可能 enable/disable 使用简单的编译标志或构建开关来实现此行为。我还没有深入研究 Microsoft 提供的 .target 文件来了解这个解决方案实际上有多合理或可维护。
更新
我编写了以下 Powershell 脚本,将我所有的外语 EmbeddedResources 和 Compile 元素移动到一个具有 Conditional 属性的新 ItemGroup 中。
$root = "C:\Code\solution_root\"
# Find all csproj files.
$projects = Get-ChildItem -Path $root -Recurse -ErrorAction SilentlyContinue -Filter *.csproj | Where-Object { $_.Extension -eq '.csproj' }
# Use a hashtable to gather a unique list of suffixes we moved for sanity checking - make sure we didn't
# relocate anything we didn't intend to. This is how I caught I was moving javascript files I didn't intend to.
$suffixes = @{}
# Find foreign resources ending in .resx and .Designer.cs
# Use a regex capture to so we can count uniques.
$pattern = "\.(?<locale>\w{2}(-\w{2,3})?\.(Designer.cs|resx))$"
foreach ($project in $projects)
{
"Processing {0}" -f $project.FullName
# Load the csproj file as XML
$xmlDoc = new-object XML
$xmlDoc.Load($project.FullName)
# Set namespace for XPath queries
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
$ns.AddNamespace("ns", $xmlDoc.DocumentElement.NamespaceURI)
$count = 0
$embeds = $xmlDoc.SelectNodes("//ns:EmbeddedResource",$ns)
$compiles = $xmlDoc.SelectNodes("//ns:Compile",$ns)
# Create new conditional ItemGroup node if it does not exist.
# Side-effect - every csproj will get this new element regardless of whether it
# contains foreign resources. That works for us, might not for you.
$moveToNode = $xmlDoc.SelectSingleNode("//ns:ItemGroup[@Condition=`" '`$(Configuration)'=='Release' `"]", $ns)
if ($moveToNode -eq $null) {
# When creating new elements, pass in the NamespaceURI from the parent node.
# If we don't do this, elements will get a blank namespace like xmlns="", and this will break compilation.
# Hat tip to
$conditionAtt = $xmlDoc.CreateAttribute("Condition")
$conditionAtt.Value = " '`$(Configuration)'=='Release' "
$moveToNode = $xmlDoc.CreateElement("ItemGroup", $xmlDoc.Project.NamespaceURI)
$ignore = $moveToNode.Attributes.Append($conditionAtt)
$ignore = $xmlDoc.LastChild.AppendChild($moveToNode)
}
# Loop over the EmbeddedResource and Compile elements.
foreach ($resource in ($embeds += $compiles)) {
# Exclude javascript files which I found in our Web project.
# These look like *.js.resx or *.js.Designer.cs and were getting picked up by my regex.
# Yeah, I could make a better regex, but I'd like to see my kids today.
if ($resource.Include -notmatch "js\.(Designer.cs|resx)$" -and $resource.Include -match $pattern ) {
# We have a foreign-language resource.
# Track unique suffixes for reporting later.
$suffix = $matches['locale']
if (!$suffixes.ContainsKey($suffix)) {
$ignore = $suffixes.Add($suffix,"")
}
$ignore = $moveToNode.InsertBefore($resource, $null)
# Count how many we moved per project.
$count += 1
}
}
"Moved {0} resources in {1}.`n" -f $count, $project.Name
$xmlDoc.Save($project.FullName)
}
echo "The following unique suffixes were processed."
$suffixes.Keys | sort
您可以利用 MSBuild 中的现有机制来处理这种情况,例如 <Choose>
元素 documentated here 和引入您自己的构建配置的选项。如果您的开发人员在日常工作中只构建 Debug 配置,您甚至可能不需要自己的构建配置:您可以设置您的项目,以便所有外语资源仅包含在 Release 构建中。
此项目文件部分将确保仅在 Release 配置中构建波兰语资源(语言代码可能有误,但您明白了):
<Choose>
<When Condition=" '$(Configuration)'=='Release' ">
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.pl.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.pl.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.pl.Designer.cs">
<DependentUpon>Resources.pl.resx</DependentUpon>
<AutoGen>True</AutoGen>
</Compile>
<!-- TODO: add other language resources as necessary-->
</ItemGroup>
</When>
</Choose>
如果您的开发人员需要构建发布配置,您可以创建自己的配置 ReleaseResx 并仅在该配置中包含外语 .resx 文件。
你的两个建议也行,我只是觉得这种方法更干净。