DEV Community

Cover image for Upgrade Umbraco 13 to 16: Localization
Luuk Peters
Luuk Peters

Posted on

Upgrade Umbraco 13 to 16: Localization

This is part three in a series of blogs about common tasks you'll encounter when updating Umbraco 13 to 16. In this part, we'll look into updating localization to work with Umbraco 16. First, we'll cover updating backoffice localization, then we'll cover changes to localization in C# code, and finally, I'll briefly touch on using localization in Umbraco backoffice extensions.

Backoffice localization (UI Localization)

Probably the most common localization feature is the ability to localize the backoffice. Think of the names and descriptions of content types and their properties, but also things like sections, dashboards, and message popups.

Backoffice localization works in roughly the same way in Umbraco 13 and 16: you have a file for each language you want to support in the backoffice, and each file defines a set of keys with localized values. You then use those keys in various places in the Umbraco backoffice. Because this blog focuses on upgrading Umbraco, I’ll assume you already know how localization works in the Umbraco 13 backoffice. If not, read the localization documentation of Umbraco 13 first.

The main difference between Umbraco 13 and 16 is the file format and how the files are registered, so for the most part, backoffice localization is not difficult to update.

Update language files

Let’s start by updating the language files to the new format. In Umbraco 13, language files were XML, but in Umbraco 16 they are — like almost everything in the backoffice — extensions. This means they work with JavaScript and must be registered using a manifest. And because they are JavaScript, you can actually do some useful things with them, but I’ll come back to that later.

It’s also good to note that XML language files haven’t completely disappeared; they are still used for localization in backend C# code. However, everything in the backoffice now uses JavaScript.

Here’s an example Umbraco 13 language file, located in wwwroot/App_Plugins/Example/Lang/en.xml:

<?xml version="1.0" encoding="utf-8" ?> 
<language>
    <area alias="exampledocype">
        <key alias="doctype_name">Example</key>
        <key alias="doctype_description">This is an example doc type</key>
        <key alias="property1_name">Property name</key>
        <key alias="property1_description">Use this property for something</key>
    </area>
</language>
Enter fullscreen mode Exit fullscreen mode

We now need to convert this file to JavaScript. The idea remains the same, but unlike Umbraco 13 — where language files had to be in a specific folder to be automatically loaded — in Umbraco 16 you can name your file anything and place it anywhere. The only requirement is that you register the file in a manifest. Just make sure to use the same aliases for your keys.

For example, let's update the Umbraco 13 example and place it at wwwroot/App_Plugins/Example/localization-en.js:

export default {
    exampledocype: {
        doctype_name: "Example",
        doctype_description: "This is an example doc type",
        property1_name: "Property name",
        property1_description: "Use this property for something"
    }
}
Enter fullscreen mode Exit fullscreen mode

And that’s it! For large files, simply let AI do the conversion for you — it’s surprisingly good at it. Now we just need to register the localizations.

Register localization in 16

As mentioned, localizations are backoffice extensions. This means they need to be registered using an Extension Manifest and are not automatically loaded by convention. There are multiple ways to register manifests, but in this example, we’ll register them directly in the umbraco-package.json file.

Create or update wwwroot/App_Plugins/Example/umbraco-package.json and add localization manifests. You need a manifest for each language:

{
    "$schema": "../../umbraco-package-schema.json",
    "id": "Example.Localization",
    "name": "A localization example",
    "version": "1.0.0",
    "extensions": [
        {
            "type": "localization",
            "alias": "Example.Localization.Localize.En",
            "name": "English",
            "meta": {
                "culture": "en"
            },
            "js": "/App_Plugins/Example/localization-en.js"
        },
        {
            "type": "localization",
            "alias": "Example.Localization.Localize.Dk",
            "name": "Danish",
            "meta": {
                "culture": "dk"
            },
            "js": "/App_Plugins/Example/localization-dk.js"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Now, most backoffice localization should work again.

Fix property description fields

The bulk of the work to make your backoffice localizations from Umbraco 13 work in 16 is now done, but we’re not finished yet. Not all localizations work out of the box. Specifically, description fields on properties do not localize as they did in Umbraco 13. If you use a key in a description field the same way as before, the description will not be localized:

Instead, you need to wrap the description key in curly brackets:

This is because descriptions are passed through the Umbraco Flavoured Markdown pipeline, which requires the correct formatting.

See the Umbraco 16 documentation on backoffice localization for more details.

Backend localizations (.NET localization)

Backend localizations in .NET are useful when you need to localize texts for things like sending emails, handling errors, or running health checks.

No more ILocalizationService

The ILocalizationService no longer exists. It was a bit of an odd service with too many responsibilities. You could use it to manage content languages in Umbraco as well as manage the Umbraco dictionary. This functionality has now been split into two services: ILanguageService and IDictionaryService. It’s straightforward to use them, so you shouldn’t have much difficulty updating code that uses the IDictionaryService.

The ILocalizeTextService still exists and works the same as in Umbraco 13. It is the backend service for reading localization files using keys. This service still uses XML files and not the JavaScript files from the backoffice.

See the Umbraco 16 documentation on .NET localization for more details.

Localization in your extensions

I also want to briefly touch on localization in custom Umbraco extensions and provide some links to the documentation.

You can also reference localization keys directly in your extension manifests. For instance, this is an example of the title of a dashboard in the backoffice:

const UmbExtensionManifest: UmbExtensionManifest = {
    type: 'dashboard',
    ...
    meta: {
        label: "#dashboardTabs_contentExpirationDashboard"
        ...
    }
};
Enter fullscreen mode Exit fullscreen mode

If you are building a custom Umbraco web component, make sure it inherits from UmbLitElement or uses the UmbElementMixin on the LitElement. If you do, you’ll get a helper for localization in your component:

import { LitElement, css, html } from "lit";
import { customElement } from "@umbraco-cms/backoffice/external/lit";
import { UmbElementMixin } from "@umbraco-cms/backoffice/element-api";

export default class MyElement extends UmbElementMixin(LitElement) {
    render() {
        return html`<uui-button .label=${this.localize.term('general_close')}>
        </uui-button>`;
    }
}
Enter fullscreen mode Exit fullscreen mode

You can also use the umb-localize element in your component like this:

<button>
    <umb-localize key="dialog_myKey"></umb-localize>
</button>
Enter fullscreen mode Exit fullscreen mode

This works the same way as the <localize> element in AngularJS back in Umbraco 13.

Read the documentation on using localization in Umbraco 16 for more information.

Because localizations are now in JavaScript, this opens up new possibilities. You can use arguments and placeholders in your localizations. For example, you can define a localization for singular and plural using the same key and an argument:

export default {
    section: {
        numberOfItems: (count) => {
            count = parseInt(count, 10);
            if (count === 0) return 'Showing nothing';
            if (count === 1) return 'Showing only one item';
            return `Showing ${count} items`;
        },
    },
};
Enter fullscreen mode Exit fullscreen mode

Read the documentation on using arguments and placeholders in Umbraco 16 for more information.

Top comments (0)