Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Alin Răuțoiu 28 posts 146 karma points
    Aug 23, 2022 @ 10:51
    Alin Răuțoiu
    0

    Localization problem using ViewComponents with AJAX in Umbraco9/10

    I am building a paginated view where I'm calling a light surface controller from the front-end, the surface controller is invoking a view component, which returns the rendered html that I replace back on the front-end. So far so good. The problem is that it's a multilanguage website and on the AJAX call to the controller, the AssignedContentItem property on the UmbracoHelper cannot be bound and throws an error, which seems to reset the current culture to "en-us", instead of the correct one.

    Can I bind the current page on the surface controller (which, I presume would also fix the problem in the helper)?

    Is there anyway to call GetDictionaryValue from the View Component without using the UmbracoHelper?

    Or is there something fundamentally wrong to my approach that I should be aware of and start from there.

  • Huw Reddick 1929 posts 6717 karma points MVP 2x c-trib
    Aug 23, 2022 @ 13:27
    Huw Reddick
    1

    you can inject ILocalizationService

  • Alin Răuțoiu 28 posts 146 karma points
    Aug 23, 2022 @ 15:01
    Alin Răuțoiu
    0

    Thanks! Figured this out by myself almost right after I posted the question, but it feels like a not that great workaround.

  • Huw Reddick 1929 posts 6717 karma points MVP 2x c-trib
    Aug 23, 2022 @ 19:14
    Huw Reddick
    0

    Why do you think that?

  • Alin Răuțoiu 28 posts 146 karma points
    Aug 25, 2022 @ 07:37
    Alin Răuțoiu
    0

    Well, ILocalizationService is much more powerful, allowing the manipulation of the Translations list. Thankfully, the Components views aren't exposed in the backoffice. Then there's no method that returns a string value for a particular key. So I have to retrieve the current culture id in Umbraco with GetLanguageIdByIsoCode. I do this in the views "global" scope. Then when I need to get a translated string I have to get the DictionaryItem with GetDictionaryItemByKey, on which I have to call GetTranslatedValue for my previously retrieved culture Id. Finally, I have to check if there's any translated value, if not place a default string.

    Of course, I could make my own wrapper service that only returns a string for a key and culture and encapsulate all those steps, but then I'd just reimplement UmbracoHelper's GetDictionaryValue.

    This is why I see this solution more like a workaround to what's either my improper use of SurfaceControllers/ViewComponents or maybe a bug in Umbraco. I don't see why I shouldn't be able to bind the CurrentPage property, at least manually, which I believe is the base problem for this situation.

  • Dan 13 posts 113 karma points
    Aug 24, 2022 @ 21:33
    Dan
    0

    I've a kind similar issue with Ajax and Culture.

    I'm trying to implement "Load more" button via Ajax. It sends an Ajax request to SurfaceController and than it pass parameters to the ViewComponent (including current culture). But it always returns content in default language.

    What am I doing wrong? And how to get content in current culture?

    public IViewComponentResult Invoke(string tag, int currentPage, int pageSize,  string culture)
        {
    
            if (_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? context) == false)
            {
                throw new NotImplementedException();
            }
    
            var personNodes = context.Content.GetAtRoot()
                .First()
                .FirstChild<DoctorsAll>()
                .Children<Doctor>(culture) ?? Array.Empty<Doctor>();
    
            if (!String.IsNullOrEmpty(tag))
            {
                personNodes = personNodes.Where(x => x.ServiceTag.Contains(tag));
            }
    
            var result = personNodes
                .Select(p => new FilterViewModel()
                {
                    Title = p.HeroTitle,
                    Description = p.HeroText,
                    ImgUrl = p.HeroImage.Src,
                    Link = p.Url()
                })
                .Skip(pageSize * currentPage)
                .Take(pageSize)
                .ToList();
    
            if(!result.Any())
                return Content(string.Empty);
    
            return View(result);
        }
    
  • Huw Reddick 1929 posts 6717 karma points MVP 2x c-trib
    Aug 25, 2022 @ 00:18
  • Alin Răuțoiu 28 posts 146 karma points
    Aug 25, 2022 @ 07:16
    Alin Răuțoiu
    0

    In my case I'm not managing to bind the current page to the surface controller . Don't really know why, it respects the naming convention and all. I get that it behaves differently from how it did in Umbraco8, but I don't quite get how.

    Anyway, what I ended up doing is that in the master page I render a hidden input containing the current culture string. Then in the pagination component I grab that with JS and pass it as a query parameter in the call to the SurfaceController method that invokes the ViewComponent. Finally, before invoking the View Component I set a new VariationContext

    _variationContextAccessor.VariationContext = new VariationContext(culture);
    

    I imagine it should also work how you're doing it, passing the culture to the ViewComponent. I'd imagine it'd be more performant like that, but I don't want to manually keep track of each's document culture over various services. At this point it doesn't seem to be causing any issues.

  • Salomons 15 posts 107 karma points c-trib
    Nov 09, 2023 @ 14:11
    Salomons
    1

    In case anyone is still searching, take a look at the UmbracoPublishedContentCultureProvider. This sets the default culture at some points in the application. Leading to unexpected behaviour of the UICulture in the View rendering.

    You can override this by creating your own RequestCultureProvider, this example takes the cultureinfo of a request header. The order of inserting is important. The Umbraco providers are inserted before the original microsoft providers (QueryStringRequestCultureProvider for example)

    /// <summary>
    ///     Sets the request culture to the culture of the header
    /// </summary>
    public class HeaderCultureProvider : RequestCultureProvider
    {
        private readonly RequestLocalizationOptions _localizationOptions;
        private readonly object _locker = new();
    
        /// <summary>
        ///     Initializes a new instance of the HeaderCultureProvider class.
        /// </summary>
        public HeaderCultureProvider(RequestLocalizationOptions localizationOptions) =>
            _localizationOptions = localizationOptions;
    
        /// <inheritdoc />
        public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
        {
            var header = httpContext.Request.Headers["CultureCode"];
            if (header.Count > 0)
            {
                var culture = header[0];
                if (culture != null)
                {
                    lock (_locker)
                    {
                        // We need to dynamically change the supported cultures since we won't ever know what languages are used since
                        // they are dynamic within Umbraco. We have to handle this for both UI and Region cultures, in case people run different region and UI languages
                        // This code to check existence is borrowed from aspnetcore to avoid creating a CultureInfo
                        // https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Middleware/Localization/src/RequestLocalizationMiddleware.cs#L165
                        CultureInfo? existingCulture = _localizationOptions.SupportedCultures?.FirstOrDefault(
                            supportedCulture =>
                                StringSegment.Equals(supportedCulture.Name, culture, StringComparison.OrdinalIgnoreCase));
    
                        if (existingCulture == null)
                        {
                            // add this as a supporting culture
                            var ci = CultureInfo.GetCultureInfo(culture);
                            _localizationOptions.SupportedCultures?.Add(ci);
                        }
    
                        CultureInfo? existingUICulture = _localizationOptions.SupportedUICultures?.FirstOrDefault(
                            supportedCulture =>
                                StringSegment.Equals(supportedCulture.Name, culture, StringComparison.OrdinalIgnoreCase));
    
                        if (existingUICulture == null)
                        {
                            // add this as a supporting culture
                            var ci = CultureInfo.GetCultureInfo(culture);
                            _localizationOptions.SupportedUICultures?.Add(ci);
                        }
                    }
    
                    return Task.FromResult<ProviderCultureResult>(new ProviderCultureResult(culture));
                }
            }
    
            return NullProviderCultureResult;
        }
    }
    

    Then add the provider like this:

    public class CultureAttributeLocalizationOptions : IConfigureOptions<RequestLocalizationOptions>
      {
          public void Configure(RequestLocalizationOptions options)
          {
              options.RequestCultureProviders.Insert(0, new HeaderCultureProvider(options));
          }
      }
    

    Eventualy in the composer, you can add the options

    // add header culture attribute
    services.ConfigureOptions<CultureAttributeLocalizationOptions>();
    

    Hope this helps.

Please Sign in or register to post replies

Write your reply to:

Draft