Pages

Wednesday, 15 April 2020

Umbraco 8 - ModelsBuilder with Language Fallback


In Umbraco 8, they introduced the much-anticipated multi-language feature called Variants.
I won't describe this in detail, just check the documentation on Variants.

Edit: the approach below is superseded by changing the default fallback behaviour of Umbraco as described here.

Language fallback

In concert with Variants, we can now have language fallback. This basically means that when you have an English and a Dutch version of a page, you do not HAVE to fill in every field on the Dutch version. When left empty it can fall back to the English version (assuming English is marked as Fallback Language in your language settings).


Now, fallback doesn't just magically work, it involves some work on your part.

Assume that message is a contentItem (derived from IPublishedContent) and we want to get the value of the title property.

The traditional way is to get it by calling the Value() method.
var title = message.Value<string>("title");

This will give you the value of the title in the current culture. If this is empty in Dutch, you will get an empty string.

Enable language fallback

To enable language fallback, you would add a fallback parameter.
var title = message.Value<string>("title", fallback: Fallback.ToLanguage);

Verify VariationContext

If - for some reason - the current culture in your context is not set or plain wrong, you can give the culture as a parameter to the Value() method, but this is not the way to go.

I use a lot of API's (derived from Umbraco.Web.WebApi.UmbracoApiController) and they usually don't set the current Umbraco culture.
Given a parameter language containing the culture you need (coming for instance from a request header), you can set the correct culture for Variant fallback as follows:

Current.UmbracoContext.VariationContextAccessor.VariationContext = new VariationContext(language);

You now get the title in the correct language or - when empty - the fallback langauge.

ModelsBuilder

I hear you thinking : "Dude, we've been using ModelsBuilder to have strongly-typed content models for years. Do you really expect us to go back to .Value("propertyName")?"

No, I do not. But it requires some more work.

I won't go into the details of Extending the Modelsbuilder, but here's the gist of it.
The generated class (simplified for brevity) for our Message model would be
[PublishedModel("message")]
public partial class Message : PublishedContentModel
{
    [ImplementPropertyType("title")]
    public string Title => this.Value<string>("title");

    [ImplementPropertyType("someProperty")]
    public string SomeProperty => this.Value<string>("someProperty");
}

We cannot modify this class, because it is generated by the ModelsBuilder. But because it is a partial class, we can extend it.

Below is the custom class you add to your project (same name and namespace as the generated one).
public partial class Message
{
    [ImplementPropertyType("title")]
    public string Title => this.Value<string>("title", fallback: Fallback.ToLanguage);
}

You'll need to remove the implementation for title from the generated class. The next time the generator runs, it will skip the title property because of the ImplementPropertyType-attribute in your custom class.

It is now possible for you to use the following code to get the title (with fallback) from the message item.
var title = message.Title;

Random thoughts

  • The above approach does not work for Grid content, but that's subject for a different post.
  • When creating a new language version of an item in Umbraco, the name of the item is a mandatory field. It would be nice if this could be left empty and also fallback to the English version.
  • If you make a field mandatory, it is mandatory in all languages, so you can't leave it empty and depend on fallback.
    Personally, I made many mandatory fields optional again, because I feel the advantage of fallback outways the need for mandatory fields. (your call)
Feedback or questions? Give me a shout.

No comments:

Post a Comment