Skip to content

Commit a071aec

Browse files
[Xamarin.Android.Build.Tasks] Make XA4214 warning localizable
Context: https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1009374/ This commit prepares one MSBuild message for localization, establishing the general steps to follow for other messages. The general steps are: 1. Add the message to `src/Xamarin.Android.Build.Tasks/Properties/Resources.resx`. Use the message code as the name of the resource. For example, for the message text associated with code `XA4214`, use `XA4214` as the name. 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. In the call to `LogCodedError()` or `LogCodedWarning()`, reference the message string using the generated C# property name like `Properties.Resources.XA4214`. 3. After adding the new message, build `Xamarin.Android.Build.Tasks.csproj` locally. This will run the targets from [dotnet/xliff-tasks][0] to update the `.xlf` [XLIFF][1] 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. This first example also establishes a few guidelines for specific scenarios: * If a message code is used in multiple calls to `LogCodedError()` or `LogCodedWarning()` that each logs a different message, append short descriptive phrases to the end of the code to create additional resource names as needed. * To include values of variables in the message, use format items like `{0}` and `{1}` rather than string interpolation or string concatenation. String interpolation won't work because string resources are not subject to interpolation. String concatenation should also be avoided because it means the message text will be split across multiple string resources, which makes it more complicated to provide appropriate context about the message for the translation team. * Use the comments field in the `.resx` file to provide additional context to the translation team about the message. For example, for a message that includes a format item like `{0}`, it can sometimes be helpful to add a comment about what will appear for that item in the final formatted string: {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 Implementation details ====================== The `.resx` file is named `Resources.resx` and located in the `src/Xamarin.Android.Build.Tasks/Properties/` directory. This follows what the nonpublic xamarin/designer and xamarin/XamarinVS repositories do. The `.resx` file is stored with LF line endings in the repo but set to `text` mode in `.gitattributes` so it can be checked out with CRLF line endings on Windows to match what the Visual Studio `.resx` editor expects. The `.resx` file currently uses the `<Generator>ResXFileCodeGenerator</Generator>` Visual Studio IDE mechanism to generate a `Resources.Designer.cs` file that provides C# property names for the string resources. Visual Studio updates the file each time a change is saved to the corresponding `.resx` file. This is the approach used by the nonpublic xamarin/designer and xamarin/XamarinVS repos. In the future, if `Xamarin.Android.Build.Tasks.csproj` is converted to a [short-form project][2], it would probably be best to add a dependency on dotnet/arcade and switch to using the [`GenerateResxSource` mechanism][3] instead of `ResXFileCodeGenerator`. That would match what dotnet/sdk does. This commit leaves the message 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`, and for the existing messages in Xamarin.Android, the message codes don't seem like they will be too useful for the translation team anyway. The [dotnet/xliff-tasks][0] tasks and targets find `.resx` files in a project, generate or update corresponding `.xlf` [XLIFF][1] files as needed, and then build satellite assemblies from the `.xlf` files. Using xliff-tasks involves a few steps: 1. Add the dotnet-eng Azure DevOps NuGet package feed or the dotnet-core MyGet NuGet package feed to `NuGet.config`. This commit uses dotnet-eng because that seems to be the more up-to-date recommendation, based on the [dotnet/arcade README][4]. 2. Add the XliffTasks NuGet package to the project to localize. 3. Set `$(UpdateXlfOnBuild)` to `true` for the project when building locally, but not when building on CI. This follows the strategy suggested in the [xliff-tasks README][5]: Many teams using `XliffTasks` default `UpdateXlfOnBuild` to true for local developer builds, but leave it off for CI builds. This way the .xlf files are automatically updated as the developer works, and the CI build will fail if the developer forgets to include the changes to the .xlf files as part of their PR. This way the .xlf files are always in sync with the source files, and can be handed off to a localization team at any time. This commit sets the property globally via `Configuration.props` to help make it easily visible to contributors. 4. Build the project to generate the initial set of `.xlf` files. 5. Add the generated `.xlf` files to source control. 6. Update the targets for the installer packages so that they include the satellite assemblies generated by XliffTasks. In the future, if `Xamarin.Android.Build.Tasks.csproj` is converted to a short-form project, it would probably be best to add a dependency on dotnet/arcade, set `$(UsingToolXliff)` to `true`, and then undo steps 1-3 since dotnet/arcade takes care of those steps automatically. I locally verified that the satellite assemblies were included in the expected locations in both the Windows and macOS installers with this commit in place. I also locally verified that MSBuild used the text from `Resources.fr.xlf` for the XA4214 warning if I modified `GenerateJavaStubs.Run()` to set both `Thread.CurrentThread.CurrentCulture()` and `Thread.CurrentThread.CurrentUICulture()` to `new CultureInfo ("fr-FR")`. [0]: https://github.com/dotnet/xliff-tasks [1]: http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html [2]: https://docs.microsoft.com/visualstudio/msbuild/how-to-use-project-sdk [3]: https://github.com/dotnet/arcade/blob/e67d9f098029ebecedf11012a749b532d68ad2a9/Documentation/ArcadeSdk.md#generateresxsource-bool [4]: https://github.com/dotnet/arcade/blob/2c6db6ee8d8adeb2e8ccc1485e6780635890e419/README.md#getting-started [5]: https://github.com/dotnet/xliff-tasks/blob/91369f6f0585fa092eddfa6c169b7f68174eb763/README.md#updating-xlf-files
1 parent 60363ef commit a071aec

22 files changed

+522
-7
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*.Designer.cs eol=crlf
2020

2121
*.cs text
22+
*.resx text
23+
*.xlf text
2224
*.xml text
2325
*.md text
2426
Makefile eol=lf

Configuration.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@
174174
<RemapAssemblyRefTool>$(ManagedRuntime) $(ManagedRuntimeArgs) &quot;$(RemapAssemblyRefToolExecutable)&quot;</RemapAssemblyRefTool>
175175
</PropertyGroup>
176176

177+
<PropertyGroup>
178+
<UpdateXlfOnBuild Condition="'$(RunningOnCI)' != 'true'">true</UpdateXlfOnBuild>
179+
</PropertyGroup>
180+
177181
<!-- Unit Test Properties -->
178182
<PropertyGroup>
179183
<_Runtime Condition=" '$(HostOS)' != 'Windows' ">$(ManagedRuntime) $(ManagedRuntimeArgs)</_Runtime>

NuGet.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<packageSources>
44
<clear />
55
<!-- ensure only the sources defined below are used -->
6+
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" protocolVersion="3" />
67
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
78
</packageSources>
89
</configuration>

build-tools/installers/create-installers.targets

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,19 @@
194194
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Bindings.targets" />
195195
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Build.Tasks.dll" />
196196
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Build.Tasks.pdb" />
197+
<_MSBuildFiles Include="$(MSBuildSrcDir)\cs\Xamarin.Android.Build.Tasks.resources.dll" />
198+
<_MSBuildFiles Include="$(MSBuildSrcDir)\de\Xamarin.Android.Build.Tasks.resources.dll" />
199+
<_MSBuildFiles Include="$(MSBuildSrcDir)\es\Xamarin.Android.Build.Tasks.resources.dll" />
200+
<_MSBuildFiles Include="$(MSBuildSrcDir)\fr\Xamarin.Android.Build.Tasks.resources.dll" />
201+
<_MSBuildFiles Include="$(MSBuildSrcDir)\it\Xamarin.Android.Build.Tasks.resources.dll" />
202+
<_MSBuildFiles Include="$(MSBuildSrcDir)\ja\Xamarin.Android.Build.Tasks.resources.dll" />
203+
<_MSBuildFiles Include="$(MSBuildSrcDir)\ko\Xamarin.Android.Build.Tasks.resources.dll" />
204+
<_MSBuildFiles Include="$(MSBuildSrcDir)\pl\Xamarin.Android.Build.Tasks.resources.dll" />
205+
<_MSBuildFiles Include="$(MSBuildSrcDir)\pt-BR\Xamarin.Android.Build.Tasks.resources.dll" />
206+
<_MSBuildFiles Include="$(MSBuildSrcDir)\ru\Xamarin.Android.Build.Tasks.resources.dll" />
207+
<_MSBuildFiles Include="$(MSBuildSrcDir)\tr\Xamarin.Android.Build.Tasks.resources.dll" />
208+
<_MSBuildFiles Include="$(MSBuildSrcDir)\zh-Hans\Xamarin.Android.Build.Tasks.resources.dll" />
209+
<_MSBuildFiles Include="$(MSBuildSrcDir)\zh-Hant\Xamarin.Android.Build.Tasks.resources.dll" />
197210
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.BuildInfo.txt" />
198211
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Cecil.dll" />
199212
<_MSBuildFiles Include="$(MSBuildSrcDir)\Xamarin.Android.Cecil.pdb" />

src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root>
3+
<!--
4+
Microsoft ResX Schema
5+
6+
Version 2.0
7+
8+
The primary goals of this format is to allow a simple XML format
9+
that is mostly human readable. The generation and parsing of the
10+
various data types are done through the TypeConverter classes
11+
associated with the data types.
12+
13+
Example:
14+
15+
... ado.net/XML headers & schema ...
16+
<resheader name="resmimetype">text/microsoft-resx</resheader>
17+
<resheader name="version">2.0</resheader>
18+
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
19+
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
20+
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
21+
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
22+
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
23+
<value>[base64 mime encoded serialized .NET Framework object]</value>
24+
</data>
25+
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
26+
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
27+
<comment>This is a comment</comment>
28+
</data>
29+
30+
There are any number of "resheader" rows that contain simple
31+
name/value pairs.
32+
33+
Each data row contains a name, and value. The row also contains a
34+
type or mimetype. Type corresponds to a .NET class that support
35+
text/value conversion through the TypeConverter architecture.
36+
Classes that don't support this are serialized and stored with the
37+
mimetype set.
38+
39+
The mimetype is used for serialized objects, and tells the
40+
ResXResourceReader how to depersist the object. This is currently not
41+
extensible. For a given mimetype the value must be set accordingly:
42+
43+
Note - application/x-microsoft.net.object.binary.base64 is the format
44+
that the ResXResourceWriter will generate, however the reader can
45+
read any of the formats listed below.
46+
47+
mimetype: application/x-microsoft.net.object.binary.base64
48+
value : The object must be serialized with
49+
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
50+
: and then encoded with base64 encoding.
51+
52+
mimetype: application/x-microsoft.net.object.soap.base64
53+
value : The object must be serialized with
54+
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
55+
: and then encoded with base64 encoding.
56+
57+
mimetype: application/x-microsoft.net.object.bytearray.base64
58+
value : The object must be serialized into a byte array
59+
: using a System.ComponentModel.TypeConverter
60+
: and then encoded with base64 encoding.
61+
-->
62+
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
63+
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
64+
<xsd:element name="root" msdata:IsDataSet="true">
65+
<xsd:complexType>
66+
<xsd:choice maxOccurs="unbounded">
67+
<xsd:element name="metadata">
68+
<xsd:complexType>
69+
<xsd:sequence>
70+
<xsd:element name="value" type="xsd:string" minOccurs="0" />
71+
</xsd:sequence>
72+
<xsd:attribute name="name" use="required" type="xsd:string" />
73+
<xsd:attribute name="type" type="xsd:string" />
74+
<xsd:attribute name="mimetype" type="xsd:string" />
75+
<xsd:attribute ref="xml:space" />
76+
</xsd:complexType>
77+
</xsd:element>
78+
<xsd:element name="assembly">
79+
<xsd:complexType>
80+
<xsd:attribute name="alias" type="xsd:string" />
81+
<xsd:attribute name="name" type="xsd:string" />
82+
</xsd:complexType>
83+
</xsd:element>
84+
<xsd:element name="data">
85+
<xsd:complexType>
86+
<xsd:sequence>
87+
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
88+
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
89+
</xsd:sequence>
90+
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
91+
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
92+
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
93+
<xsd:attribute ref="xml:space" />
94+
</xsd:complexType>
95+
</xsd:element>
96+
<xsd:element name="resheader">
97+
<xsd:complexType>
98+
<xsd:sequence>
99+
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
100+
</xsd:sequence>
101+
<xsd:attribute name="name" type="xsd:string" use="required" />
102+
</xsd:complexType>
103+
</xsd:element>
104+
</xsd:choice>
105+
</xsd:complexType>
106+
</xsd:element>
107+
</xsd:schema>
108+
<resheader name="resmimetype">
109+
<value>text/microsoft-resx</value>
110+
</resheader>
111+
<resheader name="version">
112+
<value>2.0</value>
113+
</resheader>
114+
<resheader name="reader">
115+
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
116+
</resheader>
117+
<resheader name="writer">
118+
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119+
</resheader>
120+
<data name="XA4214" xml:space="preserve">
121+
<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>
122+
<comment>{0} - The managed type name
123+
{1} - Comma-separated list of all the assemblies where the managed type exists</comment>
124+
</data>
125+
<data name="XA4214_Result" xml:space="preserve">
126+
<value>References to the type `{0}` will refer to `{0}, {1}`.</value>
127+
<comment>The phrase "`{0}, {1}`" does not need to be translated.
128+
{0} - The managed type name
129+
{1} - The the name of the library that contains the type</comment>
130+
</data>
131+
</root>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<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">
3+
<file datatype="xml" source-language="en" target-language="cs" original="../Resources.resx">
4+
<body>
5+
<trans-unit id="XA4214">
6+
<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>
7+
<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>
8+
<note>{0} - The managed type name
9+
{1} - Comma-separated list of all the assemblies where the managed type exists</note>
10+
</trans-unit>
11+
<trans-unit id="XA4214_Result">
12+
<source>References to the type `{0}` will refer to `{0}, {1}`.</source>
13+
<target state="new">References to the type `{0}` will refer to `{0}, {1}`.</target>
14+
<note>The phrase "`{0}, {1}`" does not need to be translated.
15+
{0} - The managed type name
16+
{1} - The the name of the library that contains the type</note>
17+
</trans-unit>
18+
</body>
19+
</file>
20+
</xliff>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<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">
3+
<file datatype="xml" source-language="en" target-language="de" original="../Resources.resx">
4+
<body>
5+
<trans-unit id="XA4214">
6+
<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>
7+
<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>
8+
<note>{0} - The managed type name
9+
{1} - Comma-separated list of all the assemblies where the managed type exists</note>
10+
</trans-unit>
11+
<trans-unit id="XA4214_Result">
12+
<source>References to the type `{0}` will refer to `{0}, {1}`.</source>
13+
<target state="new">References to the type `{0}` will refer to `{0}, {1}`.</target>
14+
<note>The phrase "`{0}, {1}`" does not need to be translated.
15+
{0} - The managed type name
16+
{1} - The the name of the library that contains the type</note>
17+
</trans-unit>
18+
</body>
19+
</file>
20+
</xliff>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<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">
3+
<file datatype="xml" source-language="en" target-language="es" original="../Resources.resx">
4+
<body>
5+
<trans-unit id="XA4214">
6+
<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>
7+
<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>
8+
<note>{0} - The managed type name
9+
{1} - Comma-separated list of all the assemblies where the managed type exists</note>
10+
</trans-unit>
11+
<trans-unit id="XA4214_Result">
12+
<source>References to the type `{0}` will refer to `{0}, {1}`.</source>
13+
<target state="new">References to the type `{0}` will refer to `{0}, {1}`.</target>
14+
<note>The phrase "`{0}, {1}`" does not need to be translated.
15+
{0} - The managed type name
16+
{1} - The the name of the library that contains the type</note>
17+
</trans-unit>
18+
</body>
19+
</file>
20+
</xliff>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<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">
3+
<file datatype="xml" source-language="en" target-language="fr" original="../Resources.resx">
4+
<body>
5+
<trans-unit id="XA4214">
6+
<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>
7+
<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>
8+
<note>{0} - The managed type name
9+
{1} - Comma-separated list of all the assemblies where the managed type exists</note>
10+
</trans-unit>
11+
<trans-unit id="XA4214_Result">
12+
<source>References to the type `{0}` will refer to `{0}, {1}`.</source>
13+
<target state="new">References to the type `{0}` will refer to `{0}, {1}`.</target>
14+
<note>The phrase "`{0}, {1}`" does not need to be translated.
15+
{0} - The managed type name
16+
{1} - The the name of the library that contains the type</note>
17+
</trans-unit>
18+
</body>
19+
</file>
20+
</xliff>

0 commit comments

Comments
 (0)