Skip to main content

Localize .NET applications with machine-translation

In this post, I’m going to introduce you to a GitHub Action that creates machine-translations for .NET localization. GitHub Actions allow you to build, test, and deploy your code right from GitHub, but they also allow for other workflows. You can perform nearly any action imaginable against your source code as it evolves. With the Machine Translator GitHub Action, you configure a workflow to automatically create pull requests as translation source file change.

You can use localization with Blazor WebAssembly (Wasm) to change the displayed language of a rendered website. Localization support in .NET is nothing new. It’s possible with translation files, for example, *.{locale}.resx, *.{locale}.xliff, or *.{locale}.restext to name a few. The CultureInfo class is used along with these translation files and various other .NET employed mechanics. However, maintaining translation files can be tedious and time-consuming. With GitHub Actions and Azure Cognitive Services Translator, you can set up a workflow to automatically create pull requests that provide machine-translated files.

Azure Cognitive Services Translator

Cognitive Services Translator is a cloud-based machine translation service from Azure. It powers the GitHub Action, providing the root translation functionality. To use the action, you will need a Cognitive Services Translator resource. You can use an existing one, or create a new one. If you do not have an Azure account, you can create one for free. This resource is used to perform the translations from the GitHub Action through the Translator API v3. In other words, as you push code changes to your GitHub repository that include *.en.resx files, this action runs when correctly specified in the workflow.

For more information on filtering when actions run due to changes in specific files, see Workflow syntax for GitHub Actions.

Machine Translator GitHub Action

The Machine Translator GitHub Action is available on the GitHub action marketplace. This GitHub Action does the work of marrying the functionality of the Cognitive Services Translator with your source files. To use this action, you’ll need to create a GitHub workflow. There are a few required inputs (and some optional), most of which are from your Azure Translator resource:

Type Input name Example / Description
Required sourceLocale 'en' The source locale to translate from.
Required subscriptionKey 'c571d5d8xxxxxxxxxxxxxxxxxx56bac3' Cognitive Services Translator subscription key, ideally stored as secret.
Required endpoint 'https://api.cognitive.microsofttranslator.com/' Cognitive Services Translator endpoint, ideally stored as secret.
Optional region 'canadacentral' Cognitive Services Translator region, ideally stored as secret. Optional when using a global translator resource.
Optional toLocales '"es,de,fr"' or '["es","de","fr"]' Limit the scope of the translation targets. If not provided, uses all possible translation targets.

Additionally, the action requires a GITHUB_TOKEN as an environment variable. GitHub automatically creates a GITHUB_TOKEN secret to use in your workflow as an encrypted secret. To define this environment variable, use the following YAML within your workflow (more on this later):

env:
  GITHUB_TOKEN: $

For more information, see GitHub Actions: Authentication in a workflow .

Machine Translator design

The Machine Translator action is entirely open source. It is written in TypeScript, and designed to accomplish several key objectives:

  • Determine which languages are available for translation from the translator API
  • Read all translation files (*.resx for example) as source inputs for translation
  • Translate all inputs into the available languages, or configured targets
  • Create (or update existing) translation files

To see how the action is actually implemented, feel free to view the source on the GitHub repository.

The scope of this action is limited to reading input translation files, translating new ones from these sources, and then writing the translation files back to the workspace. Part of the intended workflow composition is to pair this action with two others. The first is the actions/checkout@v2 action, which checks out your repository under the workspace so that the action can access it. The second is the peter-evans/create-pull-request@v3.4.1 action, which will create a pull request if files are changed. Special shout-out to Peter Evans 🎉 for his work here!

Open and active development

This action is actively being developed. It now has full support for the RESX, RESTEXT, and INI file formats, and basic support of XLIFF and PO file formats. XLIFF is another common industry-standard for resource management, while RESTEXT is a simpler INI-based key-value-pair alternative. The Machine Translator automatically handles batching of rate-limited API calls to Cognitive Services Translator. Many thanks to Tim Heuer 🤘🏼 for his collaboration and feedback! To propose ideas, feature requests, or post issues – please do so on the GitHub repository.

“Blazing Translations” demo app

This GitHub Action is based on the notion of resource files and localization in .NET. Any .NET application that follows this localization paradigm is free to use this action. In this way, the action is not limited to just ASP.NET Core Blazor Wasm apps – that is just the demo app of choice for this post. The GitHub repository for the demo app is available at IEvangelist/IEvangelist.BlazingTranslations. The repository has several Secrets, which store encrypted values that can be accessed from a workflow. For more information, see GitHub Action reference: Encrypted Secrets.

The demo app was inspired by Pranav Krishnamoorthy’s LocSample but adds a bit more exemplary source code. Some of the key components are:

  • In the Client project, call the IServiceCollection.AddLocalization() extension method to register localization services
  • Using dependency injection, inject IStringLocalizer<T> where string literals are used
  • Add resource files that shadow Razor components and pages. For example, Index.razor should be accompanied by an Index.en.resx file
  • Replace string literals with resource key-value pairs

Example Razor component

@page "/"

<h1>@HelloWorld</h1>

@Greeting

<SurveyPrompt Title="@SurveyTitle" />

In the preceding Razor markup, there are a few @ directives that call into the code-behind, which simply access their property values. Consider the following Index.razor.cs file, which shadows the Razor component.

using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;

namespace IEvangelist.BlazingTranslations.Client.Pages
{
    public partial class Index
    {
        [Inject]
        public IStringLocalizer<Index> Localizer { get; set; }

        public string SurveyTitle => Localizer[nameof(SurveyTitle)];
        public string Greeting => Localizer[nameof(Greeting)];
        public string HelloWorld => Localizer[nameof(HelloWorld)];
    }
}

The partial class is shared with the Index Razor component’s generated class. As such, this is thought of as the “code-behind”. It uses the [Inject] attribute to inject the IStringLocalizer<Index>. There are three readonly properties which are expressed as localizer indexer accessors. In this example, the corresponding Index.en.resx resource file is similar to the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
  <data name="Greeting" xml:space="preserve">
    <value>Welcome to your new app.</value>
  </data>
  <data name="HelloWorld" xml:space="preserve">
    <value>Hello, world!</value>
  </data>
  <data name="SurveyTitle" xml:space="preserve">
    <value>How is Blazor working for you?</value>
  </data>
</root>

Now that you have your resource file, and Razor components defined, you need to create the workflow.

Create workflow

Workflows are defined within the .github/workflows directory from the root of the repository and are written as YAML files. Consider the following:

name: Create translation pull request
on:
  push:
    branches: [ main ]
    paths:
    - '**.en.resx' # only take action when *.en.resx files change

env:
  GITHUB_TOKEN: $ # Available by default, contextual to the action

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # Checks-out repository under the workspace, so that the action can access it
      - uses: actions/checkout@v2

      # Use the machine-translator to automatically translate resource files
      - name: Machine Translator
        id: translator
        uses: IEvangelist/resource-translator@v2.1.1
        with:
          subscriptionKey: $
          endpoint: $
          region: $
          sourceLocale: 'en'

      # Creates a pull request of all translated resource files
      - name: Create pull request
        uses: peter-evans/create-pull-request@v3.4.1
        if: $ == 'true'
        with:
          title: '$'
          body: '$'

The preceding workflow definition will run when any *.en.resx file is either created or changed.

Putting it all together

With all the moving pieces in place, you the developer, are empowered to develop as you normally would. As you create and update Razor components and corresponding resource files, pull requests are automatically created for your review with translated resource files. Since pull requests are created, they can be updated by translation specialists if need be – but this will serve as a great starting point nonetheless.

Example pull request

Here is a link to an example automated pull request. The automated pull request was triggered by a commit that simply updated several *.en.resx files. The pull request details a summary of translations.

GitHub pull request #20 from IEvangelist/IEvangelist.BlazingTranslations

Summary

The Azure Cognitive Services Translator API serves as the backbone to the Machine Translator GitHub Action. With great power, comes great responsibility. As a developer, you must wear your ethics-hat at all times, especially when working with artificial intelligence (AI). It might seem as though this GitHub Action can easily elevate your .NET apps to be inclusive of nearly 80 languages, it is irresponsible to make such claims. These are machine translations and should be treated as such. Without direct intervention from a human review, the machine translations may not be conversationally accurate or culturally specific to what you are trying to convey in the text being translated.

While I’m hopeful that you will use this action, I encourage you to also consider limiting the scope of translations to known locales with the toLocales input. Work closely with the stakeholders during the app development, and incrementally solicit feedback on translations from those who are consuming that app.

See also

The post Localize .NET applications with machine-translation appeared first on .NET Blog.



source https://devblogs.microsoft.com/dotnet/localize-net-applications-with-machine-translation/

Comments

Popular posts from this blog

Fake CVR Generator Denmark

What Is Danish CVR The Central Business Register (CVR) is the central register of the state with information on all Danish companies. Since 1999, the Central Business Register has been the authoritative register for current and historical basic data on all registered companies in Denmark. Data comes from the companies' own registrations on Virk Report. There is also information on associations and public authorities in the CVR. As of 2018, CVR also contains information on Greenlandic companies, associations and authorities. In CVR at Virk you can do single lookups, filtered searches, create extracts and subscriptions, and retrieve a wide range of company documents and transcripts. Generate Danish CVR For Test (Fake) Click the button below to generate the valid CVR number for Denmark. You can click multiple times to generate several numbers. These numbers can be used to Test your sofware application that uses CVR, or Testing CVR APIs that Danish Govt provide. Generate

How To Iterate Dictionary Object

Dictionary is a object that can store values in Key-Value pair. its just like a list, the only difference is: List can be iterate using index(0-n) but not the Dictionary . Generally when we try to iterate the dictionary we get below error: " Collection was modified; enumeration operation may not execute. " So How to parse a dictionary and modify its values?? To iterate dictionary we must loop through it's keys or key - value pair. Using keys

How To Append Data to HTML5 localStorage or sessionStorage?

The localStorage property allows you to access a local Storage object. localStorage is similar to sessionStorage. The only difference is that, while data stored in localStorage has no expiration time untill unless user deletes his cache, data stored in sessionStorage gets cleared when the originating window or tab get closed. These are new HTML5 objects and provide these methods to deal with it: The following snippet accesses the current domain's local Storage object and adds a data item to it using Storage.setItem() . localStorage.setItem('myFav', 'Taylor Swift'); or you can use the keyname directly as : localStorage.myFav = 'Taylor Swift'; To grab the value set in localStorage or sessionStorage, we can use localStorage.getItem("myFav"); or localStorage.myFav There's no append function for localStorage or sessionStorage objects. It's not hard to write one though.The simplest solution goes here: But we can kee