How to use default language as default culture for threads in Umbraco

Explore a solution for aligning thread culture with the default language settings in Umbraco's backoffice. This approach addresses the complex challenge of maintaining consistent language across various operational threads, ensuring that every thread in your Umbraco application adheres to the configured default language

Introduction

Let's imagine a situation when you need to perform some tasks in a separate thread e.g. in the RecurringHostedService.

If you use the build-in ContentService in the separate thread to perform some CRUD operations, then be aware of warnings in the logs as below:

The culture specified '' was not found in any configured sources for this service

The culture specified '' was not found in any configured sources for this service Umbraco

The issue is caused by a fact that any new thread doesn't know which exactly culture should be used.

You can solve it easily be a few lines of code at the beginning of any thread as below:

var cultrue = new CultureInfo("en");

Thread.CurrentThread.CurrentCulture = cultrue;
Thread.CurrentThread.CurrentUICulture = cultrue;

Languages configuration in Umbraco's backoffice

As you probably know, we can configure languages in the Settings section of the Umbraco backoffice.

It's worth to mention that a default language has to be set as shown below:

Laguages Dashboard In Settings Section Of Umbraco

Tricky custom solution

Of course, as soon as I encountered the previously described problem, I realized that I had to use this default language as the default language for threads.

Below, you will find the solution that should remove 'The culture specified '' was not found in any configured sources for this service' warnings.

You need to implement a new Component with Composer as follows:

public class DefaultCultureComposer : ComponentComposer<DefaultCultureComponent> { }

public class DefaultCultureComponent : IComponent
{
    private readonly IRuntimeState _runtimeState;

    private readonly IScopeProvider _scopeProvider;

    private readonly ILanguageRepository _languageRepository;

    public DefaultCultureComponent(
        IRuntimeState runtimeState,
        IScopeProvider scopeProvider,
        ILanguageRepository languageRepository
        )
    {
        _runtimeState = runtimeState;

        _scopeProvider = scopeProvider;

        _languageRepository = languageRepository;
    }

    public void Initialize()
    {
        if (_runtimeState.Level < RuntimeLevel.Run)
            return;

        using (var scope = _scopeProvider.CreateScope())
        {
            string defaultIsoCode = _languageRepository.GetDefaultIsoCode();
            var defaultCultrue = new CultureInfo(defaultIsoCode);

            CultureInfo.DefaultThreadCurrentCulture = defaultCultrue;
            CultureInfo.DefaultThreadCurrentUICulture = defaultCultrue;
        }
    }

    public void Terminate() { }
}

Step 1: Establishing a Custom Component

Firstly, we introduce a new component named DefaultCultureComponent.

This component is designed to automatically set the default culture for any a new threads in Umbraco.

We utilize the ComponentComposer class to initiate this component, creating a dedicated composer - DefaultCultureComposer.

This composer is responsible for registering our custom component with Umbraco's composition.

Step 2: Injection of Dependencies

The DefaultCultureComponent class, adhering to the principles of dependency injection, receives crucial services through its constructor.

These services include IRuntimeState, IScopeProvider, and ILanguageRepository.

These dependencies are pivotal for accessing and manipulating Umbraco's runtime state, managing scope, and interfacing with language settings.

Step 3: Implementing the Initialize Method

Within our component, the Initialize method plays a key role.

It first checks the application's runtime level to ensure it's appropriate for the operation.

If the runtime level is set to RuntimeLevel.Run or higher, the method proceeds.

The scope is created using the IScopeProvider, within which we fetch the default language's ISO code from ILanguageRepository.

This ISO code is then used to construct a new CultureInfo object, setting the stage for our next step.

Step 4: Setting the Default Culture for Threads

Once we have our CultureInfo object, based on the default language of Umbraco, we set it as the default culture for both CurrentCulture and CurrentUICulture on Thread.

This ensures that all newly spawned threads in the application will automatically adopt Umbraco's configured default language.

This seamless integration is crucial for maintaining linguistic consistency across the application, especially in scenarios where multilingual capabilities are a core requirement.

Step 5: Clean Termination

Our component also includes a Terminate method, a standard part of the IComponent interface.

This method is used for cleanup processes when the component is disposed of.

This method performs no specific actions in our current implementation but is included for completeness and future-proofing.

Alternative approaches for setting thread language in Umbraco

While you might consider other strategies, such as setting the LANG environment variable, feedback from some developers suggests varying degrees of success with this approach.

Moreover, please take into account other scenarios where you might be hosting two applications with different language requirements on a single Azure Service plan.

Final words

From now on, you can expect the elimination of warnings, as threads will uniformly utilize the language set a single time within Umbraco.

You don't need to handle any redundant configuration on Azure.

For those interested in the broader context of culture management, check out the raised issue and the entire thread discussed here Umbraco GitHub Issue #13980.

🌐 Explore More: Interested in learning about Umbraco? Explore our blog.

✉️Get in Touch: If you have questions or need assistance with your Umbraco projects, please get in touch with us.

↑ Top ↑