Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Move XA4214 warning text into .resx file (
Browse files Browse the repository at this point in the history
dotnet#3900)

Context: https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1009374/

This is a first step toward localizing the MSBuild error and warning
messages produced by `Xamarin.Android.Build.Tasks.dll`.

We will be following the [.NET Resource Localization pattern][0] and
generating satellite assemblies using [`.resx` files][1], in particular
`src/Xamarin.Android.Build.Tasks/Properties/Resources.resx`.

`Resources.resx` is an XML file, and will contain `/root/data`
elements in which `//data/@name` will start with the Xamarin.Android
error or warning code, and `//data/value` will be the error or
warning message:

        <root>
          <data name="XA4214" xml:space="preserve">
            <value>The managed type `{0}` exists in multiple assemblies: {1}. Please refactor the managed type names in these assemblies so that they are not identical.</value>
          </data>
        </root>

An optional `//data/comment` element may be provided to describe the
meaning within the `//data/value` element to translators:

        <data name="XA4214" xml:space="preserve">
          <value>The managed type `{0}` exists in multiple assemblies: {1}. Please refactor the managed type names in these assemblies so that they are not identical.</value>
          <comment>
            {0} - The managed type name
            {1} - Comma-separated list of all the assemblies where the managed type exists
          </comment>
        </data>

During the build, `Resources.resx` will be translated into a
`Resources.Designer.cs` file:

        namespace Xamarin.Android.Tasks.Properties {
          internal partial class Resources {
            internal static string XA4214 {
              get => ...
            }
          }
        }

The `Resources` members should be used to obtain all strings for use
in `LogCodedError()` and `LogCodedWarning()` calls:

        Log.LogCodedWarning ("XA4214", Properties.Resources.XA4214, kvp.Key, string.Join (", ", kvp.Value));

When an MSBuild error or warning code is used with more than one
output string, then a semantically meaningful suffix should be used
to distinguish between the two:

        <data name="XA4214_Result" xml:space="preserve">
          <value>References to the type `{0}` will refer to `{0}, {1}`.</value>
        </data>

Note that this infrastructure does not interoperate with C#6 string
interpolation.  Any error or warning messages currently using C#6
string interpolation will need to use .NET 1.0-style format strings.

Our translation team doesn't work directly with `.resx` files.
Instead, the translation team works with [XLIFF files][2].
`Resources.resx` is converted into a set of
`src/Xamarin.Android.Build.Tasks/Properties/xlf/Resources.*.xlf`
files via `XliffTasks.targets` from the [dotnet/xliff-tasks][3] repo.
The `Resources.*.xlf` files should be automatically updated whenever
`Resources.resx` is updated.


Other:

  * This approach leaves the error code `XA4214` as a string literal
    for now.  This differs from what dotnet/sdk and microsoft/msbuild
    do; they instead include the message code as part of the string
    resource in the `.resx` file.  That might sometimes provide useful
    additional context for the translation team, but it also requires
    using a different set of logging methods from
    `Microsoft.Build.Utilities.TaskLoggingHelper`.

  * Fix the Test MSBuild Azure Pipelines build

    Specify the `feedsToUse` and `nugetConfigPath` inputs for the
    [`NuGetCommand@2`][6] Azure Pipelines task so that the NuGet
    restore step will be able to restore XliffTasks successfully from
    the dotnet-eng Azure DevOps NuGet package feed.

    This resolves the following error:

        The nuget command failed with exit code(1) and error(Errors in packages.config projects
            Unable to find version '1.0.0-beta.19252.1' of package 'XliffTasks'.
              C:\Users\dlab14\.nuget\packages\: Package 'XliffTasks.1.0.0-beta.19252.1' is not found on source 'C:\Users\dlab14\.nuget\packages\'.
              https://api.nuget.org/v3/index.json: Package 'XliffTasks.1.0.0-beta.19252.1' is not found on source 'https://api.nuget.org/v3/index.json'.)

TODO:

  * When `Xamarin.Android.Build.Tasks.csproj` is converted into a
    [short-form project][4], add a dependency on dotnet/arcade and
    switch to using the [`GenerateResxSource` mechanism][5] instead
    of using `%(EmbeddedResource.Generator)`=ResXFileCodeGenerator
    and set `$(UsingToolXliff)`=True.  This would match dotnet/sdk.

[0]: https://docs.microsoft.com/dotnet/framework/resources/index
[1]: https://docs.microsoft.com/dotnet/framework/resources/creating-resource-files-for-desktop-apps#resources-in-resx-files
[2]: http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
[3]: https://github.com/dotnet/xliff-tasks
[4]: https://docs.microsoft.com/visualstudio/msbuild/how-to-use-project-sdk
[5]: https://github.com/dotnet/arcade/blob/e67d9f098029ebecedf11012a749b532d68ad2a9/Documentation/ArcadeSdk.md#generateresxsource-bool
[6]: https://docs.microsoft.com/azure/devops/pipelines/tasks/package/nuget
  • Loading branch information
brendanzagaeski authored and jonpryor committed Dec 7, 2019
1 parent eff77a4 commit 0342fe5
Show file tree
Hide file tree
Showing 27 changed files with 582 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
*.Designer.cs eol=crlf

*.cs text
*.resx text
*.xlf text
*.xml text
*.md text
Makefile eol=lf
Expand Down
5 changes: 5 additions & 0 deletions Configuration.props
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@
<RemapAssemblyRefTool>$(ManagedRuntime) $(ManagedRuntimeArgs) &quot;$(RemapAssemblyRefToolExecutable)&quot;</RemapAssemblyRefTool>
</PropertyGroup>

<PropertyGroup>
<XlfLanguages>cs;de;es;fr;it;ja;ko;pl;pt-BR;ru;tr;zh-Hans;zh-Hant</XlfLanguages>
<UpdateXlfOnBuild Condition="'$(RunningOnCI)' != 'true'">true</UpdateXlfOnBuild>
</PropertyGroup>

<!-- Unit Test Properties -->
<PropertyGroup>
<_Runtime Condition=" '$(HostOS)' != 'Windows' ">$(ManagedRuntime) $(ManagedRuntimeArgs)</_Runtime>
Expand Down
1 change: 1 addition & 0 deletions Documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
* [Using Your Build](workflow/UsingYourBuild.md)
* [Jenkins Build Artifacts](workflow/JenkinsBuildArtifacts.md)
* [Development tips and native debugging](workflow/DevelopmentTips.md)
* [Localization](workflow/Localization.md)


# Coding Guidelines
Expand Down
Binary file added Documentation/images/resources-editor-xa0000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions Documentation/workflow/Localization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Localization

All new Xamarin.Android MSBuild error or warning messages should be localizable,
so when adding a new message, follow these steps:

1. Add the new message to
`src/Xamarin.Android.Build.Tasks/Properties/Resources.resx`. Use the error
or warning code as the resource name. For example, for `XA0000`, use
`XA0000` as the name:

![Managed Resources Editor with XA0000 as the name for a
resource][resources-editor]

Be sure to use Visual Studio or Visual Studio for Mac to edit the `.resx`
file so that the `ResXFileCodeGenerator` tool will run and update the
corresponding `Resources.Designer.cs` file.

2. Use the generated property from `Resources.Designer.cs` in the
`LogCodedError()` and `LogCodedWarning()` calls:

```csharp
Log.LogCodedError ("XA0000", Properties.Resources.XA0000);
```

3. After adding the new message, build `Xamarin.Android.Build.Tasks.csproj`
locally. This will run the targets from [dotnet/xliff-tasks][xliff-tasks]
to update the `.xlf` [XLIFF][xliff] localization files with the latest
changes from the `.resx` file.

4. Include the changes to the`.resx` file as well as the generated changes to
the `Resources.Designer.cs` file and the `.xlf` files in the commit.

## Guidelines

* When an error or warning code is used with more than one output string, use
semantically meaningful suffixes to distinguish the resource names. As a
made-up example:

```xml
<data name="XA0000_Files" xml:space="preserve">
<value>Invalid files.</value>
</data>
<data name="XA0000_Directories" xml:space="preserve">
<value>Invalid directories.</value>
</data>
```

* To include values of variables in the message, use numbered format items
like `{0}` and `{1}` rather than string interpolation or string
concatenation.

The `.resx` infrastructure does not interoperate with C# 6 string
interpolation.

String concatenation should also be avoided because it means splitting up
the message across multiple string resources, which makes it more
complicated to provide appropriate context to the translators.

* Use the comments field in the `.resx` file to provide additional context to
the translators. For example, if a format item like `{0}` needs additional
explanation, add a comment:

```
{0} - The managed type name
```

For a few more examples, see the dotnet/sdk repo:

https://github.com/dotnet/sdk/blob/master/src/Tasks/Common/Resources/Strings.resx
[resources-editor]: ../images/resources-editor-xa0000.png
[xliff-tasks]: https://github.com/dotnet/xliff-tasks
[xliff]: http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
1 change: 1 addition & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<packageSources>
<clear />
<!-- ensure only the sources defined below are used -->
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" protocolVersion="3" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ steps:
displayName: nuget restore Xamarin.Android solutions
inputs:
restoreSolution: '**/Xamarin.Android*.sln'
feedsToUse: config
nugetConfigPath: NuGet.config

- task: MSBuild@1
displayName: build Xamarin.Android.Tools.BootstrapTasks.csproj
Expand Down
2 changes: 2 additions & 0 deletions build-tools/installers/create-installers.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\scripts\XAVersionInfo.targets" />
<Import Project="..\scripts\LocalizationLanguages.projitems" />
<Import Project="..\..\bin\Build$(Configuration)\ProfileAssemblies.projitems" />
<Import Project="..\..\bin\Build$(Configuration)\Mono.Android.Apis.projitems" />
<UsingTask AssemblyFile="..\..\bin\Build$(Configuration)\xa-prep-tasks.dll" TaskName="Xamarin.Android.BuildTools.PrepTasks.ReplaceFileContents" />
Expand Down Expand Up @@ -195,6 +196,7 @@
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Bindings.targets" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Build.Tasks.dll" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Build.Tasks.pdb" />
<_MSBuildFiles Include="@(_LocalizationLanguages->'$(MSBuildSrcDir)\%(Identity)\Xamarin.Android.Build.Tasks.resources.dll')" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.BuildInfo.txt" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Cecil.dll" />
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Cecil.pdb" />
Expand Down
5 changes: 5 additions & 0 deletions build-tools/scripts/LocalizationLanguages.projitems
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<_LocalizationLanguages Include="$(XlfLanguages)" />
</ItemGroup>
</Project>
81 changes: 81 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

131 changes: 131 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/Resources.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="XA4214" xml:space="preserve">
<value>The managed type `{0}` exists in multiple assemblies: {1}. Please refactor the managed type names in these assemblies so that they are not identical.</value>
<comment>{0} - The managed type name
{1} - Comma-separated list of all the assemblies where the managed type exists</comment>
</data>
<data name="XA4214_Result" xml:space="preserve">
<value>References to the type `{0}` will refer to `{0}, {1}`.</value>
<comment>The phrase "`{0}, {1}`" does not need to be translated.
{0} - The managed type name
{1} - The the name of the library that contains the type</comment>
</data>
</root>
20 changes: 20 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Properties/xlf/Resources.cs.xlf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="cs" original="../Resources.resx">
<body>
<trans-unit id="XA4214">
<source>The managed type `{0}` exists in multiple assemblies: {1}. Please refactor the managed type names in these assemblies so that they are not identical.</source>
<target state="new">The managed type `{0}` exists in multiple assemblies: {1}. Please refactor the managed type names in these assemblies so that they are not identical.</target>
<note>{0} - The managed type name
{1} - Comma-separated list of all the assemblies where the managed type exists</note>
</trans-unit>
<trans-unit id="XA4214_Result">
<source>References to the type `{0}` will refer to `{0}, {1}`.</source>
<target state="new">References to the type `{0}` will refer to `{0}, {1}`.</target>
<note>The phrase "`{0}, {1}`" does not need to be translated.
{0} - The managed type name
{1} - The the name of the library that contains the type</note>
</trans-unit>
</body>
</file>
</xliff>
Loading

0 comments on commit 0342fe5

Please sign in to comment.