Best practice for fetching language context in an API controller

Vote:
 

What is the best practice in EPiServer for fetching the language context for an API controller?

In the controller 

LanguageSelector.AutoDetect()

defaults to the master language - I'm guessing it's because the route does not include a language variable.

[Route("api/[controller]")]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true, Duration = 0)]
[Produces("application/json")]
public class LocalizationController : ControllerBase
{
    [Route("[action]")]
    [HttpGet]
    public IActionResult Translate()
    {
        var siteSettings = SiteDefinition.Current.StartPage.GetSiteSettings();
        var translationList = siteSettings?.TranslationArea.OfBlockType<TranslationBlock>(CultureInfo.CurrentUICulture);
        return translationList is null ? NotFound() : Ok(translationList.ToDictionary(x => x.Key, x => x.Value));
    }
}

I've added UseRequestLocalization In Startup.cs but that enables 'CultureInfo.CurrentUICulture' to be correct - LanguageSelector.AutoDetect() is still on a master language.

var languageBranchRepository = app.ApplicationServices.GetRequiredService<ILanguageBranchRepository>();
var supportedLanguages = languageBranchRepository.ListEnabled().Select(x => x.Culture.Name).ToArray();

app.UseRequestLocalization(options => options
                .AddSupportedCultures(supportedLanguages)
                .AddSupportedUICultures(supportedLanguages)
                .SetDefaultCulture(supportedLanguages[0]));
#288371
Edited, Sep 30, 2022 5:44
Andreas J - Sep 30, 2022 13:06
Have you tried making use of the Accept-Language header?
Carl S - Sep 30, 2022 13:10
Yes. That is why 'CultureInfo.CurrentUICulture' gets the correct culture. - this way is the .NETx preferred way - I just was curious if there was another way i EPi.
Johan Petersson - Oct 03, 2022 8:00
You have to provide the language in one way or another. If you don't want to use a header, then you have to provide it in the URL somehow, either as part of the path or a querystring.
Andreas J - Oct 03, 2022 20:14
Okay, so .AutoDetect() uses IContentLanguageAccessor. I have actually seen that this interface does not return the same culture as ICurrentCultureContext. The latter returns ContextCache.Current["EPiServer:CurrentCulture"] and the former returns ContextCache.Current["EPiServer:ContentLanguage"].

@Johan, do you know if this is expected?
Vote:
 

Note that there is a difference between CurrentCulture and CurrentUICulture in dotnet. Same thing in the CMS. Looking at your controller name, I assume you're interested in CurrentUICulture.

Since you're not using our routing, you're responsible for settings the correct values for these static properties. You can update these by calling IUpdateContentLanguage.ResolveCulture(string culture) and/or IUpdateUserInterfaceLanguage.ResolveCulture(string culture) depending on if you just want to load content in correct language or just load other translated resources.

You can probably just update the cultures on the current thread too (Thread.CurrentThread.CurrentUICulture and Thread.CurrentThread.CurrentCulture), but I would recommend using the above methods instead.

You also have the option to be explicit in each method and provide the language manually, e.g. IContentLoader accepts a CultureInfo object.

#288609
Oct 04, 2022 7:47
Vote:
 

How do I resolve IUpdateContentLanguage.ResolveCulture(string culture)?

It doesn't exist as a DI?

#288612
Oct 04, 2022 9:09
Johan Petersson - Oct 04, 2022 9:12
The naming of the method is a bit misleading. You're not actually resolving, but rather setting the language. You resolve the service with regular constructor injection.
Vote:
 

I think that would be over-complicated. I would recommend binding a parameter to the Accept-Language header. Or just use attribute routing:

private readonly IUpdateUserInterfaceLanguage updateUserInterfaceLanguage;
private readonly IUpdateContentLanguage updateContentLanguage;

// Resolve interfaces with constructor injection.

[Route("{language}/api/[controller]/[action]")]
[HttpGet]
public IActionResult Translate(string language)
{
    updateUserInterfaceLanguage.ResolveCulture(language) 
    updateContentLanguage.ResolveCulture(language)
}

You probably want to add this in a custom binder and/or filter.

#288613
Edited, Oct 04, 2022 9:09
Vote:
 

private readonly IUpdateUserInterfaceLanguage updateUserInterfaceLanguage; is a later feature than 12.6.0? :)

It doesn't exist for me. Nuget package?

#288614
Oct 04, 2022 9:11
Vote:
 

These interfaces exists in the EPiServer.Cms.AspNetCore package. They have existed as long as I can remember, but in earlier versions they were in a different package.

#288617
Oct 04, 2022 9:15
Andreas J - Oct 04, 2022 9:19
Bro, the interfaces are internal in 12.9.2
Johan Petersson - Oct 04, 2022 9:23
That "just" means they might not follow semantic versioning. Bro :P
Andreas J - Oct 06, 2022 7:25
Hmm, care to elaborate? How do you propose to use them if they are internal, not public?
Johan Petersson - Oct 06, 2022 7:46
Sorry, thought they were just in an internal namespace (pubternal). They were made internal in CMS12 for some unknown, to me, reason. I will make a note that we should open up these.
Vote:
 

=) so best way is still the .NETx way and 'Accept-Language' header

#288619
Oct 04, 2022 9:23
Johan Petersson - Oct 04, 2022 9:25
That's not "the .NET way". Accept-Language is a standard http header to negotiate resource language.
Neither are "better", it's more a matter of taste I guess.
Vote:
 

@Johan, I find it difficult to handle cultures for non-content requests. ContentLanguageImplementation is internal and can't be overridden. The method LanguagePreferenceList in ContentLanguageImplementation is also very difficult to affect. Accept Language is ONLY used if UseBrowserLanguagePreferences is true, which is not optimal. For content requests, I don't want to use UseBrowserLanguagePreferences but for API requests I do want to use the Accept Language header. Or some other header, but I can't because I can't override the behaviour. I could not use ContentLanguageImplementation at all but then I won't be able to take part of DetermineCulture, which is sad because it's a good way to determine the most suitable culture to use given the preferences.

Please provide a way to change the behaviour. Simply giving us a way to affect LanguagePreferenceList would probably be enough.

#294056
Jan 03, 2023 9:06
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.