Dmitry Kostochko

Localization of Office add-ins using forms-based resources

The article describes a clever way to localize an Office add-in by using regular forms-based resources without having to apply them manually to each component, form or control.

Add-in Express solutions are based on native .NET Framework classes, the main module of an Add-in Express project is inherited from the System.ComponentModel.Component class and therefore the Localizible and Language properties can be used and are really used by Office add-in developers. In this post, I will try to show a couple of problems that you may run into when localizing your add-ins and a tricky but efficient way to solve them.

Usually, the developer is faced with the necessity to forcibly change the add-in localization when the language of the installed Office applications is different from the operating system locale.

To have it done, you need to modify the CurrentCulture and CurrentUICulture properties of the System.Threading.Thread.CurrentThread static property and, more importantly, apply the localized resources to each component and each control located on the add-in module or form. To see how Visual Studio itself does it, look at code of the InitializeComponent method:

//...
ComponentResourceManager resources = new ComponentResourceManager(typeof(AddinModule));
//...
resources.ApplyResources(this.MyRibbonTab, "MyRibbonTab");
//...
resources.ApplyResources(this.MyRibbonGroup, "MyRibbonGroup");
//...
resources.ApplyResources(this.MyRibbonButton, "MyRibbonButton");
//...
resources.ApplyResources(this, "$this");
//...

So, at first sight, all is simple – just change the CurrentCulture and CurrentUICulture properties corresponding to the Office language and call the ApplyResources method for each component:

protected override void OnHostApplicationInitialized()
{
    base.OnHostApplicationInitialized();
 
    if (this.ExcelApp != null)
        SwitchLanguage(GetLanguageID(this.ExcelApp));
    else if (this.WordApp != null)
        SwitchLanguage(GetLanguageID(this.WordApp));
}
 
private int GetLanguageID(dynamic app)
{
    Office.LanguageSettings languageSettings = app.LanguageSettings;
    if (languageSettings != null)
        try
        {
            return languageSettings.LanguageID[Office.MsoAppLanguageID.msoLanguageIDUI];
        }
        finally { Marshal.ReleaseComObject(languageSettings); }
    return 0;
}
 
private void SwitchLanguage(int officeLanguageID)
{
    Thread.CurrentThread.CurrentCulture =
        new System.Globalization.CultureInfo(officeLanguageID);
    Thread.CurrentThread.CurrentUICulture =
        new System.Globalization.CultureInfo(officeLanguageID);
 
    ComponentResourceManager resources =
        new ComponentResourceManager(typeof(AddinModule));
    resources.ApplyResources(this.MyRibbonTab, "MyRibbonTab");
    resources.ApplyResources(this.MyRibbonGroup, "MyRibbonGroup");
    resources.ApplyResources(this.MyRibbonButton, "MyRibbonButton");
    resources.ApplyResources(this, "$this");
}

In the above code, I used the OnHostApplicationInitialized virtual method to detect an Office application language, it is called immediately after the HostApplication property is set. In my opinion, this piece of code is quite good, except for one thing irritating for any developer – you should remember to edit it after adding any new component / control to the add-in module.

Why not use the standard code of the InitializeComponent method? Nothing prevents you from doing this, but with one important condition:

private void SwitchLanguage(int officeLanguageID)
{
    Thread.CurrentThread.CurrentCulture = 
        new System.Globalization.CultureInfo(officeLanguageID);
    Thread.CurrentThread.CurrentUICulture = 
        new System.Globalization.CultureInfo(officeLanguageID);
 
    //ComponentResourceManager resources =
    //    new ComponentResourceManager(typeof(AddinModule));
    //resources.ApplyResources(this.MyRibbonTab, "MyRibbonTab");
    //resources.ApplyResources(this.MyRibbonGroup, "MyRibbonGroup");
    //resources.ApplyResources(this.MyRibbonButton, "MyRibbonButton");
    //resources.ApplyResources(this, "$this");
 
    InitializeComponent();
}

And you should add this condition to your module constructor:

public AddinModule()
{
    Application.EnableVisualStyles();
 
    string processName =
        System.Diagnostics.Process.GetCurrentProcess().ProcessName.ToLower();
    if (processName == "devenv" ||          // Visual Studio IDE
        processName == "adxregistrator" ||  // Add-in Express registrator tool
        processName == "msbuild")           // MSBuild.exe
    {
        InitializeComponent();
    }
 
    // Please add any initialization code to the AddinInitialize event handler
}

So, here’s what we get: the InitializeComponent method will be called in the constructor when the module is created by Visual Studio or the adxRegistrator.exe tool or MSBuild.exe. This happens when you register/unregister your add-in during development or when the add-in module is created during the installation of your add-in on the user’s machine.

In all other cases, the add-in module is created when the add-in is being loaded into a host application and the condition in the constructor returns false. The InitializeComponent method will be called after checking the localization and modifying the CurrentCulture and CurrentUICulture properties. Thus, all the components of the add-in module will be initialized according to Culture set in your code. Custom forms and dialogs, advanced Outlook regions and Office task panes as well as other components and controls that may exist in your solution are created after the module and will use the updated localization automatically.

Available downloads:

This sample Excel and Word add-in was developed using Add-in Express for Office and .net:

Localizible Word/Excel add-in (C# and VB.NET)

4 Comments

  • https://secure.gravatar.com/avatar/7bc03fb43e3087d52daa0e4b5eb74808?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Eugene Astafiev says:

    Good to know, Dmitry!

    But the article doesn’t cover how to handle scenarios where users change UI settings on the fly in Office applications with MUI packs installed. The add-in’s UI remains unchanged in that case. You can imagine a situation when users switch from russian to english, but the add-in still show its captions in russian.

    Localizing the UI using ribbon callbacks is the most reliable way because it allows to cover such cases.

  • https://secure.gravatar.com/avatar/ab4ec2858cfdf1e44dadf8c50fae314d?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Dmitry Kostochko (Add-in Express Team) says:

    Hi Eugene,

    Thank you for your feedback! I want to remind you that Office applications do not apply language changes on the fly. Upon changing the Display Language you will get the “Please restart Office so that your language changes can take effect” notification dialog. Please try this yourself.

  • https://secure.gravatar.com/avatar/7bc03fb43e3087d52daa0e4b5eb74808?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Eugene Astafiev says:

    Yes, Office (2016) applications use the following registry key for getting the UI language set:

    > HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Common\LanguageResources::UILanguage

    Unfortunately the value is read at startup only once.

  • https://secure.gravatar.com/avatar/ab4ec2858cfdf1e44dadf8c50fae314d?s=32&d=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G Dmitry Kostochko (Add-in Express Team) says:

    Hi Eugene,

    Thank you for sharing your thoughts!

Post a comment

Have any questions? Ask us right now!