Does anyone know how to get a dictionary item in a controller?!

hey out there,

bit stumped with this one… we’ve a site we’re building in v17 and we need to get dictionary items in a api controller.

this code works:

public class MyApiController : Controller
{
	private readonly IVariationContextAccessor _variationContextAccessor;
	private readonly ILocalizationService _localizationService;

	public MyApiController(
		IVariationContextAccessor variationContextAccessor,
		ILocalizationService localizationService)
	{
		_variationContextAccessor = variationContextAccessor;
		_localizationService = localizationService;
	}

	[HttpGet]
	public IActionResult GetContent(string culture = "en-US")
	{
		_variationContextAccessor.VariationContext = new VariationContext(culture);

		// Get a dictionary item by key
		var dictionaryItem = _localizationService.GetDictionaryItemByKey("YourDictionaryKey");
		var translation = dictionaryItem?.GetTranslatedValue(culture);

		return Ok(new { translation });
	}
}

but the ILocalizationService has been marked as obsolete… it does say it’ll be removed in v15 (it clearly hasn’t!)

Please use ILanguageService and IDictionaryItemService for localization. Will be removed in V15.

we’ve tried the IDictionaryItemService with this (untested) code but it feels really clunky:

var dictionaryItem = await _dictionaryItemService.GetAsync(“YourDictionaryKey”);
var translation = dictionaryItem.Translations.First(x => x.LanguageIsoCode == “ISOcode”).Value;

there’s gotta be a better way?!?!

any suggestions are (as ever) greatly apprciated :wink:

Hi Jake

The easy way is to simply inject UmbracoHelper into your controller, and use the GetDictionaryValue methods from there.

If you want to use the dictionary directly, you can inject ICultureDictionaryFactory into your controller, and then create the culture dictionary using _cultureDictionaryFactory.CreateDictionary(specificCulture). From there you can access dictionary items, like a normal C# dictionary.

You can see how it’s done in UmbracoHelper here:

that is amazing @skttl thank you - dunno why i didn’t think of using the umbraco helper (tbh i don’t use it a lot…) but it works a treat :wink:

just in case anyone else stumbles across this…

the helper works great in a controller but becomes tricker in an api controller…

the way we’ve solved it is to create a filter:

using Microsoft.AspNetCore.Mvc.Filters;
using System.Globalization;

namespace YourSite.Core.Filters
{
	public class SetCultureFromHeaderFilter : ActionFilterAttribute
	{
		public override void OnActionExecuting(ActionExecutingContext context)
		{
			var cultureHeader = context.HttpContext.Request.Headers["Accept-Language"].FirstOrDefault();

			if (!string.IsNullOrWhiteSpace(cultureHeader))
			{
				try
				{
					var culture = new CultureInfo(cultureHeader);
					Thread.CurrentThread.CurrentCulture = culture;
					Thread.CurrentThread.CurrentUICulture = culture;
				}
				catch (CultureNotFoundException)
				{
					//we could set a default culture here if we wanted
				}
			}

			base.OnActionExecuting(context);
		}
	}
}

then that gets applied to all the api controllers:

namespace YourSite.Core.Controllers.Api
{
	[ApiController]
	[SetCultureFromHeaderFilter]
	public class YourApiController : Controller
	{
...

we set the language on the html tag:

<!doctype html>
<html lang="es">
<head>
...

and pass the header in the javascript that calls any of the endpoints:

const currentCulture = document.documentElement.lang;

const response = await fetch('/api/your-end-point', {
	method: 'POST',
	headers: {
		'Content-Type': 'application/json',
		'Accept-Language': currentCulture,
	}
	...
});

the api controller and any of the code it consumes now knows the correct culture. nice.

Thanks for sharing!

I have something similar, also an attribute for setting the culture based on the referer :slight_smile:

public class SetCultureFromUmbracoReferrer : BaseSetCultureFilterAttribute
{
    private readonly IPublishedRouter _publishedRouter;

    /// <summary>
    /// Initializes a new instance of the SetCultureFromUmbracoReferrer class.
    /// </summary>
    public SetCultureFromUmbracoReferrer()
    {
        _publishedRouter = StaticServiceProvider.Instance.GetRequiredService<IPublishedRouter>();
    }
    
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)    
    {        
        base.OnActionExecuting(context);        
        SetCulture(await GetCulture(context.HttpContext.Request));
        await next(); 
    }
    
    public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)    
    {        
        base.OnResultExecuting(context);        
        SetCulture(await GetCulture(context.HttpContext.Request));
        await next();    
    }

    private async Task<string?> GetCulture(HttpRequest request)
    {
        if (!Uri.TryCreate(request.Headers.Referer, UriKind.Absolute, out var referer)) return null;
        
        // Create a request from the Umbraco referrer.
        var publishedRequest = await _publishedRouter.CreateRequestAsync(referer);
        // Route the request and check if it has published content.
        var routedRequest = await _publishedRouter.RouteRequestAsync(publishedRequest, new RouteRequestOptions(RouteDirection.Inbound));

        if (!routedRequest.HasPublishedContent()) return null;
        
        // Get the culture from domains associated with the Umbraco referrer.
        return routedRequest.PublishedContent?.GetCultureFromDomains(referer);
    }
}

public class BaseSetCultureFilterAttribute : ActionFilterAttribute
{
    private readonly IVariationContextAccessor _variationContextAccessor = StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>();

    private VariationContext? _previousVariationContext;
    private CultureInfo? _previousCulture;
    private CultureInfo? _previousUiCulture;

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        SaveCurrentCulture();
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        ResetCulture();
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        SaveCurrentCulture();
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {
        ResetCulture();
    }


    internal void SetCulture(string? cultureName)
    {
        if (cultureName.HasValue())
        {
            _variationContextAccessor.VariationContext = new VariationContext(cultureName);
            var culture = new CultureInfo(cultureName);
            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }
    }

    internal void SaveCurrentCulture()
    {
        _previousCulture = CultureInfo.CurrentCulture;
        _previousUiCulture = CultureInfo.CurrentUICulture;
        _previousVariationContext = _variationContextAccessor.VariationContext;
    }

    internal void ResetCulture()
    {
        if (_previousVariationContext != null)
        {
            _variationContextAccessor.VariationContext = _previousVariationContext;
            _previousVariationContext = null;
        }

        if (_previousCulture != null)
        {
            Thread.CurrentThread.CurrentCulture = _previousCulture;
            _previousCulture = null;
        }

        if (_previousUiCulture != null)
        {
            Thread.CurrentThread.CurrentUICulture = _previousUiCulture;
            _previousUiCulture = null;
        }
    }
}