Localizing a .NET office extension (desktop)

This article summarizes my experience on translating office add-in to multiple language. It is not an official guide, but just how you could do it. It assumes you have an office extension (VSTO, desktop) in a single language, and want to provide it in some other languages for users in other countries.

GitHub demo repository:
https://github.com/nbelyh/VstoMultiLanguageDemo

For generic information on localization on application localization in .NET, please refer the official Microsoft documentation: https://docs.microsoft.com/en-us/dotnet/framework/resources/index

The steps I suggest:

  • Create localized versions of your resources
  • Move all strings to resources
  • Move ribbon strings to the resources
  • Translate the resources
  • Use the translation

Create localized versions of your resources.

The easiest is to install Visual Studio extension ResXManager, and then add the languages you want. It will create the localized version of the files for you, and you will be able to translate everything from one place. Also it supports import/export, so you can send your texts to translators. It is a free extension, highly recommended.

The ResXManager View

Move all strings to be translated to resources.

JetBrains Resharper can help with that (it has a nice function "Move to resource", along with other localization assistance).

You can do that "by hand" as well (just for each resource file create new localized verison, like for main Resources.resx create Resources.de.resx for example)

Move ribbon strings to the resources

If your extension has text labels on the ribbon, you may want to translate those. The ribbons designer does not support localization, so you can go with XML ribbon definition (if you use built-in project you could use "Export as XML" option in the designer. To translate labels you now need to move hardcoded strings from this xml to resources, and replace them with callback methods.

In pictures:

Ribbon.xml

<button
	label="Click Me"
	description="Click this button to see a message"
	onAction="Command1"...>

Ribbon.cs

class RibbonHandler
{
    public void Command1(IRibbonControl control) { ... }
}

transforms to:
Ribbon.xml

<button tag="OnCommand"
	getLabel="GetLabel"
	getDescription="GetDescription"
	action="Command1"...>

Ribbon.cs

class RibbonHandler
{
	public void OnCommand(IRibbonControl control)
	{
		if (control.Tag == "Command1") { ... }
	}

	public string GetLabel(IRibbonControl control) =>
		Resources.ResourceManager.GetString($"{control.Tag}_Label");

	public string GetDescription(IRibbonControl control) =>
		Resources.ResourceManager.GetString($"{control.Tag}_Description");
}

Resources:

Command1_Label => "Click Me"
Command1_Description => "Click this button to see a message"

I would not recommend to move the whole Ribbon.xml file to the resources though. For example, you could face a problem when switching add-in UI to match Visio language (see below), because the ribbon XML is required even before the Add-in startup is called by the runtime.

Translating the resources

You can always translate yourself, if you know the target language. if you don’t I would suggest to go with ResXManager to export the resources you know and send to the translator(s).

Using the translation

Assuming you have translated the application, you need to use the translation. Most probably you will want to pick the language that corresponds to the Visio language. You can do that on the add-in’s startup, by checking what kind of language Visio has:
Application.LanguageSettings.LanguageID will return the LCID (number) that defines the language. Then you cna use that to make your application use specific localization by setting Resources.Culture property. If you do have all localization Visio has, in this case you may want to use only supported ones (and default to English). Below is a code sample:

var lcid = Application.LanguageSettings.LanguageID[Office.MsoAppLanguageID.msoLanguageIDUI];
var culture = new CultureInfo(lcid); // selected in the Office

var languages = new[] { "de", "ru" };
if (languages.Any(language => language == culture.TwoLetterISOLanguageName))
{
    Resources.Culture = culture;
    // _ribbonHandler.Invalidate();
}

To test that the translation actually works, you can select Visio UI language (for that you need to install corresponding language packs):

Leave a Reply