Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -174,6 +174,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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this property used by? I see no other references to UpdateXlfOnBuild within this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, whoops. I meant to surface all the notes from the new commit message. For this question, step 3 in particular is the key info.

The dotnet/xliff-tasks tasks and targets find .resx files in a
project, generate or update corresponding .xlf XLIFF 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 appears to be the more up-to-date
    recommendation, based on the dotnet/arcade README.

  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:

    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").

</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 @@ -194,6 +195,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