如何通过覆盖MSBuild目标来防止外语资源生成?

Lar*_*man 5 c# asp.net msbuild localization resx

我正在努力减少大型C#/ ASP.NET解决方案的编译时间.我们的解决方案使用通常的resx文件方法翻译成大约十几种外语.解析和编译这些资源文件会大大减慢编译时间,并且每天都会受挫.

我知道可以创建自定义资源提供程序并远离.resx文件.现在,请假设我们必须坚持使用.resx文件.

通过从.csproj文件中排除除默认语言环境.resx文件之外的所有文件,我可以将编译时间缩短一半.我们的开发人员无需在日常开发过程中编译其他十几种语言.

我正在寻找防止编译外语.resx文件的方法.我想出了两种方法,我正在寻找关于一个是否优越的建议,或者是否有其他更好的方法.

我想出了两个:

  • 编写可以在各种.csproj文件中删除并添加回非默认.resx文件的脚本.也许我们将最小的.csproj文件保留在版本控制中,并且有一个单独的构建过程,用于递归地重新添加.resx文件,以便重新集成新的翻译,执行测试并执行我们的部署构建.
  • 图一种覆盖执行resx解析和编译的内置MSBuild目标的方法,有效地禁用除默认语言之外的所有目标.开发人员可以使用简单的编译标志或构建开关来启用/禁用此行为.我还没有深入研究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 https://stackoverflow.com/questions/135000/how-to-prevent-blank-xmlns-attributes-in-output-from-nets-xmldocument

        $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
Run Code Online (Sandbox Code Playgroud)

Jen*_*zcz 2

您可以利用 MSBuild 中的现有机制来处理这种情况,例如此处记录的<Choose>元素以及引入您自己的构建配置的选项。如果您的开发人员在日常工作中仅构建调试配置,您甚至可能不需要自己的构建配置:您可以设置您的项目,以便所有外语资源仅包含在发布版本中。

此项目文件部分将确保波兰语资源仅在发布配置中构建(语言代码可能是错误的,但您明白了):

<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>
Run Code Online (Sandbox Code Playgroud)

如果您的开发人员需要构建发布配置,您可以创建自己的配置 ReleaseResx 并仅在该配置中包含外语 .resx 文件。

您的建议也都有效,我只是认为这种方法更干净。