From bd4006c577035bf907f69092d5b9d8d692582b84 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 11 Jan 2021 13:39:09 +1100 Subject: [PATCH] Fixes the custom RequestCultureProvider to dynamically add cultures to the supported cultures list, changes the request/builder to not reference a ICultureInfo and instead just a string to avoid allocations and confusion since the handlers will end up as a string anyways. Removes the unnecessary cultureinfo concurrentdictionary because CultureInfo.GetCultureInfo does the same thing. --- src/Umbraco.Core/Routing/AliasUrlProvider.cs | 8 ++-- .../Routing/ContentFinderByIdPath.cs | 2 +- .../Routing/ContentFinderByRedirectUrl.cs | 2 +- .../Routing/ContentFinderByUrl.cs | 2 +- .../Routing/ContentFinderByUrlAlias.cs | 2 +- .../Routing/DefaultUrlProvider.cs | 12 ++++-- src/Umbraco.Core/Routing/Domain.cs | 6 +-- src/Umbraco.Core/Routing/DomainUtilities.cs | 14 +++---- src/Umbraco.Core/Routing/IPublishedRequest.cs | 6 ++- .../Routing/IPublishedRequestBuilder.cs | 4 +- src/Umbraco.Core/Routing/PublishedRequest.cs | 4 +- .../Routing/PublishedRequestBuilder.cs | 4 +- src/Umbraco.Core/Routing/PublishedRouter.cs | 14 +++---- src/Umbraco.Core/Routing/SiteDomainHelper.cs | 12 +++--- .../Security/AuthenticationExtensions.cs | 13 +----- .../Routing/ContentFinderByConfigured404.cs | 4 +- .../Routing/NotFoundHandlerHelper.cs | 4 +- .../Implement/LocalizedTextService.cs | 15 +++---- .../PublishedSnapshotService.cs | 4 +- .../CoreThings/ObjectExtensionsTests.cs | 2 +- .../Routing/SiteDomainHelperTests.cs | 40 +++++++++---------- .../Routing/PublishedRequestBuilderTests.cs | 6 +-- .../LegacyXmlPublishedCache/DomainCache.cs | 16 +++----- .../PublishedContent/PublishedRouterTests.cs | 2 +- .../ContentFinderByAliasWithDomainsTests.cs | 4 +- .../Routing/ContentFinderByUrlTests.cs | 6 +-- .../ContentFinderByUrlWithDomainsTests.cs | 2 +- .../Routing/DomainsAndCulturesTests.cs | 6 +-- ...derWithoutHideTopLevelNodeFromPathTests.cs | 10 ++--- .../Controllers/LanguageController.cs | 3 +- ...mbracoBackOfficeIdentityCultureProvider.cs | 20 ++++++++++ .../UmbracoPublishedContentCultureProvider.cs | 34 ++++++++++++++-- .../UmbracoRequestLocalizationOptions.cs | 4 +- .../Templates/TemplateRenderer.cs | 4 +- 34 files changed, 168 insertions(+), 123 deletions(-) diff --git a/src/Umbraco.Core/Routing/AliasUrlProvider.cs b/src/Umbraco.Core/Routing/AliasUrlProvider.cs index 1e6056942f..65e094690e 100644 --- a/src/Umbraco.Core/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Core/Routing/AliasUrlProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Options; @@ -112,10 +112,10 @@ namespace Umbraco.Web.Routing // if the property varies, get the variant value, URL is "/" // but! only if the culture is published, else ignore - if (varies && !node.HasCulture(domainUri.Culture.Name)) continue; + if (varies && !node.HasCulture(domainUri.Culture)) continue; var umbracoUrlName = varies - ? node.Value(_publishedValueFallback,Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name) + ? node.Value(_publishedValueFallback,Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture) : node.Value(_publishedValueFallback, Constants.Conventions.Content.UrlAlias); var aliases = umbracoUrlName?.Split(new [] {','}, StringSplitOptions.RemoveEmptyEntries); @@ -127,7 +127,7 @@ namespace Umbraco.Web.Routing { var path = "/" + alias; var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); - yield return UrlInfo.Url(_uriUtility.UriFromUmbraco(uri, _requestConfig).ToString(), domainUri.Culture.Name); + yield return UrlInfo.Url(_uriUtility.UriFromUmbraco(uri, _requestConfig).ToString(), domainUri.Culture); } } } diff --git a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs index 414d1da871..46571f5d65 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs @@ -77,7 +77,7 @@ namespace Umbraco.Web.Routing if (!string.IsNullOrEmpty(cultureFromQuerystring)) { // we're assuming it will match a culture, if an invalid one is passed in, an exception will throw (there is no TryGetCultureInfo method), i think this is ok though - frequest.SetCulture(CultureInfo.GetCultureInfo(cultureFromQuerystring)); + frequest.SetCulture(cultureFromQuerystring); } frequest.SetPublishedContent(node); diff --git a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs index b887ff11be..38f04d1ddb 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs @@ -54,7 +54,7 @@ namespace Umbraco.Web.Routing ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded()) : frequest.Uri.GetAbsolutePathDecoded(); - IRedirectUrl redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(route, frequest.Culture.Name); + IRedirectUrl redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(route, frequest.Culture); if (redirectUrl == null) { diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs index 44ae4335e2..27893cd3de 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs @@ -74,7 +74,7 @@ namespace Umbraco.Web.Routing _logger.LogDebug("Test route {Route}", route); - IPublishedContent node = umbCtx.Content.GetByRoute(umbCtx.InPreviewMode, route, culture: docreq.Culture?.Name); + IPublishedContent node = umbCtx.Content.GetByRoute(umbCtx.InPreviewMode, route, culture: docreq.Culture); if (node != null) { docreq.SetPublishedContent(node); diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs index f7ebd6bbc5..770fdf4003 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs @@ -59,7 +59,7 @@ namespace Umbraco.Web.Routing node = FindContentByAlias( umbCtx.Content, frequest.Domain != null ? frequest.Domain.ContentId : 0, - frequest.Culture.Name, + frequest.Culture, frequest.Uri.GetAbsolutePathDecoded()); if (node != null) diff --git a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 51c212aa3c..d739f851ad 100644 --- a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; @@ -83,7 +83,9 @@ namespace Umbraco.Web.Routing var umbracoContext = _umbracoContextAccessor.UmbracoContext; var node = umbracoContext.Content.GetById(id); if (node == null) + { yield break; + } // look for domains, walking up the tree var n = node; @@ -96,17 +98,19 @@ namespace Umbraco.Web.Routing // no domains = exit if (domainUris ==null) + { yield break; + } foreach (var d in domainUris) { - var culture = d?.Culture?.Name; + var culture = d?.Culture; - //although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok + // although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok var route = umbracoContext.Content.GetRouteById(id, culture); if (route == null) continue; - //need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned) + // need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned) var pos = route.IndexOf('/'); var path = pos == 0 ? route : route.Substring(pos); diff --git a/src/Umbraco.Core/Routing/Domain.cs b/src/Umbraco.Core/Routing/Domain.cs index b9116c6b51..7d1808ef97 100644 --- a/src/Umbraco.Core/Routing/Domain.cs +++ b/src/Umbraco.Core/Routing/Domain.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; namespace Umbraco.Web.Routing { @@ -15,7 +15,7 @@ namespace Umbraco.Web.Routing /// The identifier of the content which supports the domain. /// The culture of the domain. /// A value indicating whether the domain is a wildcard domain. - public Domain(int id, string name, int contentId, CultureInfo culture, bool isWildcard) + public Domain(int id, string name, int contentId, string culture, bool isWildcard) { Id = id; Name = name; @@ -55,7 +55,7 @@ namespace Umbraco.Web.Routing /// /// Gets the culture of the domain. /// - public CultureInfo Culture { get; } + public string Culture { get; } /// /// Gets a value indicating whether the domain is a wildcard domain. diff --git a/src/Umbraco.Core/Routing/DomainUtilities.cs b/src/Umbraco.Core/Routing/DomainUtilities.cs index 85347abb42..fa5d84836d 100644 --- a/src/Umbraco.Core/Routing/DomainUtilities.cs +++ b/src/Umbraco.Core/Routing/DomainUtilities.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core; @@ -51,8 +51,8 @@ namespace Umbraco.Web.Routing var rootContentId = domain?.ContentId ?? -1; var wcDomain = FindWildcardDomainInPath(umbracoContext.Domains.GetAll(true), contentPath, rootContentId); - if (wcDomain != null) return wcDomain.Culture.Name; - if (domain != null) return domain.Culture.Name; + if (wcDomain != null) return wcDomain.Culture; + if (domain != null) return domain.Culture; return umbracoContext.Domains.DefaultCulture; } @@ -233,13 +233,13 @@ namespace Umbraco.Web.Routing if (culture != null) // try the supplied culture { - var cultureDomains = domainsAndUris.Where(x => x.Culture.Name.InvariantEquals(culture)).ToList(); + var cultureDomains = domainsAndUris.Where(x => x.Culture.InvariantEquals(culture)).ToList(); if (cultureDomains.Count > 0) return cultureDomains; } if (defaultCulture != null) // try the defaultCulture culture { - var cultureDomains = domainsAndUris.Where(x => x.Culture.Name.InvariantEquals(defaultCulture)).ToList(); + var cultureDomains = domainsAndUris.Where(x => x.Culture.InvariantEquals(defaultCulture)).ToList(); if (cultureDomains.Count > 0) return cultureDomains; } @@ -254,13 +254,13 @@ namespace Umbraco.Web.Routing if (culture != null) // try the supplied culture { - domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)); + domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(culture)); if (domainAndUri != null) return domainAndUri; } if (defaultCulture != null) // try the defaultCulture culture { - domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(defaultCulture)); + domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(defaultCulture)); if (domainAndUri != null) return domainAndUri; } diff --git a/src/Umbraco.Core/Routing/IPublishedRequest.cs b/src/Umbraco.Core/Routing/IPublishedRequest.cs index 57b38dbff8..fedfd69dc3 100644 --- a/src/Umbraco.Core/Routing/IPublishedRequest.cs +++ b/src/Umbraco.Core/Routing/IPublishedRequest.cs @@ -45,7 +45,11 @@ namespace Umbraco.Web.Routing /// /// Gets the content request's culture. /// - CultureInfo Culture { get; } + /// + /// This will get mapped to a CultureInfo eventually but CultureInfo are expensive to create so we want to leave that up to the + /// localization middleware to do. See https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Middleware/Localization/src/RequestLocalizationMiddleware.cs#L165. + /// + string Culture { get; } /// /// Gets the url to redirect to, when the content request triggers a redirect. diff --git a/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs b/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs index f8e5837838..ced443a89c 100644 --- a/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs +++ b/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web.Routing /// /// Gets the assigned (if any) /// - CultureInfo Culture { get; } + string Culture { get; } /// /// Gets a value indicating whether the current published content has been obtained @@ -64,7 +64,7 @@ namespace Umbraco.Web.Routing /// /// Sets the culture for the request /// - IPublishedRequestBuilder SetCulture(CultureInfo culture); + IPublishedRequestBuilder SetCulture(string culture); /// /// Sets the found for the request diff --git a/src/Umbraco.Core/Routing/PublishedRequest.cs b/src/Umbraco.Core/Routing/PublishedRequest.cs index bb1c28cab1..badfb27dd2 100644 --- a/src/Umbraco.Core/Routing/PublishedRequest.cs +++ b/src/Umbraco.Core/Routing/PublishedRequest.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Routing /// /// Initializes a new instance of the class. /// - public PublishedRequest(Uri uri, IPublishedContent publishedContent, bool isInternalRedirect, ITemplate template, DomainAndUri domain, CultureInfo culture, string redirectUrl, int? responseStatusCode, IReadOnlyList cacheExtensions, IReadOnlyDictionary headers, bool cacheabilityNoCache, bool ignorePublishedContentCollisions) + public PublishedRequest(Uri uri, IPublishedContent publishedContent, bool isInternalRedirect, ITemplate template, DomainAndUri domain, string culture, string redirectUrl, int? responseStatusCode, IReadOnlyList cacheExtensions, IReadOnlyDictionary headers, bool cacheabilityNoCache, bool ignorePublishedContentCollisions) { Uri = uri ?? throw new ArgumentNullException(nameof(uri)); PublishedContent = publishedContent; @@ -48,7 +48,7 @@ namespace Umbraco.Web.Routing public DomainAndUri Domain { get; } /// - public CultureInfo Culture { get; } + public string Culture { get; } /// public string RedirectUrl { get; } diff --git a/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs b/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs index 4230e73a78..faa793c7ff 100644 --- a/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs +++ b/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.Routing public DomainAndUri Domain { get; private set; } /// - public CultureInfo Culture { get; private set; } + public string Culture { get; private set; } /// public ITemplate Template { get; private set; } @@ -89,7 +89,7 @@ namespace Umbraco.Web.Routing } /// - public IPublishedRequestBuilder SetCulture(CultureInfo culture) + public IPublishedRequestBuilder SetCulture(string culture) { Culture = culture; return this; diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index b61baa1990..4d7f0eef82 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -121,15 +121,15 @@ namespace Umbraco.Web.Routing return request.Build(); } - private void SetVariationContext(CultureInfo culture) + private void SetVariationContext(string culture) { VariationContext variationContext = _variationContextAccessor.VariationContext; - if (variationContext != null && variationContext.Culture == culture?.Name) + if (variationContext != null && variationContext.Culture == culture) { return; } - _variationContextAccessor.VariationContext = new VariationContext(culture?.Name); + _variationContextAccessor.VariationContext = new VariationContext(culture); } /// @@ -272,7 +272,7 @@ namespace Umbraco.Web.Routing } // variant, ensure that the culture corresponding to the domain's language is published - return domainDocument.Cultures.ContainsKey(domain.Culture.Name); + return domainDocument.Cultures.ContainsKey(domain.Culture); } domains = domains.Where(IsPublishedContentDomain).ToList(); @@ -302,10 +302,10 @@ namespace Umbraco.Web.Routing // not matching any existing domain _logger.LogDebug("{TracePrefix}Matches no domain", tracePrefix); - request.SetCulture(defaultCulture == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultCulture)); + request.SetCulture(defaultCulture ?? CultureInfo.CurrentUICulture.Name); } - _logger.LogDebug("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture.Name); + _logger.LogDebug("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture); return request.Domain != null; } @@ -331,7 +331,7 @@ namespace Umbraco.Web.Routing if (domain != null) { request.SetCulture(domain.Culture); - _logger.LogDebug("{TracePrefix}Got domain on node {DomainContentId}, set culture to {CultureName}", tracePrefix, domain.ContentId, request.Culture.Name); + _logger.LogDebug("{TracePrefix}Got domain on node {DomainContentId}, set culture to {CultureName}", tracePrefix, domain.ContentId, request.Culture); } else { diff --git a/src/Umbraco.Core/Routing/SiteDomainHelper.cs b/src/Umbraco.Core/Routing/SiteDomainHelper.cs index 35475ae292..bf43514f4a 100644 --- a/src/Umbraco.Core/Routing/SiteDomainHelper.cs +++ b/src/Umbraco.Core/Routing/SiteDomainHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -284,9 +284,11 @@ namespace Umbraco.Web.Routing // we do our best, but can't do the impossible // get the "default" domain ie the first one for the culture, else the first one (exists, length > 0) if (qualifiedSites == null) - return domainAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)) ?? - domainAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(defaultCulture)) ?? - domainAndUris.First(); + { + return domainAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(culture)) + ?? domainAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(defaultCulture)) + ?? domainAndUris.First(); + } // find a site that contains the current authority var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority)); @@ -308,7 +310,7 @@ namespace Umbraco.Web.Routing .FirstOrDefault(domainAndUri => domainAndUri != null); // random, really - ret = ret ?? domainAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture)) ?? domainAndUris.First(); + ret = ret ?? domainAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(culture)) ?? domainAndUris.First(); return ret; } diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index 607c4748cc..13eab4ff80 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Globalization; using System.Security.Principal; -using System.Text; using System.Threading; namespace Umbraco.Core.Security @@ -13,7 +9,6 @@ namespace Umbraco.Core.Security /// /// Ensures that the thread culture is set based on the back office user's culture /// - /// public static void EnsureCulture(this IIdentity identity) { var culture = GetCulture(identity); @@ -27,16 +22,10 @@ namespace Umbraco.Core.Security { if (identity is UmbracoBackOfficeIdentity umbIdentity && umbIdentity.IsAuthenticated) { - return UserCultures.GetOrAdd(umbIdentity.Culture, s => new CultureInfo(s)); + return CultureInfo.GetCultureInfo(umbIdentity.Culture); } return null; } - - - /// - /// Used so that we aren't creating a new CultureInfo object for every single request - /// - private static readonly ConcurrentDictionary UserCultures = new ConcurrentDictionary(); } } diff --git a/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs index 231a68df58..5634fa4a93 100644 --- a/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs +++ b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs @@ -54,7 +54,7 @@ namespace Umbraco.Web.Routing _logger.LogDebug("Looking for a page to handle 404."); // try to find a culture as best as we can - CultureInfo errorCulture = CultureInfo.CurrentUICulture; + string errorCulture = CultureInfo.CurrentUICulture.Name; if (frequest.Domain != null) { errorCulture = frequest.Domain.Culture; @@ -67,7 +67,7 @@ namespace Umbraco.Web.Routing while (pos > 1) { route = route.Substring(0, pos); - node = umbCtx.Content.GetByRoute(route, culture: frequest?.Culture?.Name); + node = umbCtx.Content.GetByRoute(route, culture: frequest?.Culture); if (node != null) { break; diff --git a/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs index 74ce0979f6..d73b780974 100644 --- a/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs +++ b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs @@ -21,12 +21,12 @@ namespace Umbraco.Web.Routing ContentErrorPage[] error404Collection, IEntityService entityService, IPublishedContentQuery publishedContentQuery, - CultureInfo errorCulture) + string errorCulture) { if (error404Collection.Length > 1) { // test if a 404 page exists with current culture thread - ContentErrorPage cultureErr = error404Collection.FirstOrDefault(x => x.Culture == errorCulture.Name) + ContentErrorPage cultureErr = error404Collection.FirstOrDefault(x => x.Culture.InvariantEquals(errorCulture)) ?? error404Collection.FirstOrDefault(x => x.Culture == "default"); // there should be a default one! if (cultureErr != null) diff --git a/src/Umbraco.Infrastructure/Services/Implement/LocalizedTextService.cs b/src/Umbraco.Infrastructure/Services/Implement/LocalizedTextService.cs index 4d12f111e3..8547830ac7 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/LocalizedTextService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/LocalizedTextService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -86,7 +86,6 @@ namespace Umbraco.Core.Services.Implement /// /// Returns all key/values in storage for the given culture /// - /// public IDictionary GetAllStoredValues(CultureInfo culture) { if (culture == null) throw new ArgumentNullException("culture"); @@ -108,16 +107,18 @@ namespace Umbraco.Core.Services.Implement return result; } - //convert all areas + keys to a single key with a '/' + // convert all areas + keys to a single key with a '/' result = GetStoredTranslations(xmlSource, culture); - //merge with the English file in case there's keys in there that don't exist in the local file - var englishCulture = new CultureInfo("en-US"); + // merge with the English file in case there's keys in there that don't exist in the local file + var englishCulture = CultureInfo.GetCultureInfo("en-US"); if (culture.Equals(englishCulture) == false) { var englishResults = GetStoredTranslations(xmlSource, englishCulture); foreach (var englishResult in englishResults.Where(englishResult => result.ContainsKey(englishResult.Key) == false)) + { result.Add(englishResult.Key, englishResult.Value); + } } } else @@ -128,13 +129,13 @@ namespace Umbraco.Core.Services.Implement return result; } - //convert all areas + keys to a single key with a '/' + // convert all areas + keys to a single key with a '/' foreach (var area in _dictionarySource[culture]) { foreach (var key in area.Value) { var dictionaryKey = string.Format("{0}/{1}", area.Key, key.Key); - //i don't think it's possible to have duplicates because we're dealing with a dictionary in the first place, but we'll double check here just in case. + // i don't think it's possible to have duplicates because we're dealing with a dictionary in the first place, but we'll double check here just in case. if (result.ContainsKey(dictionaryKey) == false) { result.Add(dictionaryKey, key.Value); diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 49283de276..5068e52b49 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -461,7 +461,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var domains = _serviceContext.DomainService.GetAll(true); foreach (var domain in domains .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard))) + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, x.LanguageIsoCode, x.IsWildcard))) { _domainStore.SetLocked(domain.Id, domain); } @@ -865,7 +865,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (domain == null) continue; if (domain.RootContentId.HasValue == false) continue; // anomaly if (domain.LanguageIsoCode.IsNullOrWhiteSpace()) continue; // anomaly - var culture = CultureInfo.GetCultureInfo(domain.LanguageIsoCode); + var culture = domain.LanguageIsoCode; _domainStore.SetLocked(domain.Id, new Domain(domain.Id, domain.DomainName, domain.RootContentId.Value, culture, domain.IsWildcard)); break; } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs index 848edddf1c..b1ff2c8fbe 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/ObjectExtensionsTests.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.CoreThings public void TestSetup() { _savedCulture = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB"); // make sure the dates parse correctly + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB"); // make sure the dates parse correctly } [TearDown] diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/SiteDomainHelperTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/SiteDomainHelperTests.cs index 2aed3e0216..13f6794a5d 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/SiteDomainHelperTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/SiteDomainHelperTests.cs @@ -19,8 +19,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing [TearDown] public void TearDown() => SiteDomainHelper.Clear(); // assuming this works! - private static readonly CultureInfo s_cultureFr = CultureInfo.GetCultureInfo("fr-fr"); - private static readonly CultureInfo s_cultureGb = CultureInfo.GetCultureInfo("en-gb"); + private static readonly string s_cultureFr = "fr-fr"; + private static readonly string s_cultureGb = "en-gb"; [Test] public void AddSites() @@ -185,7 +185,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new Domain(1, "domain1.com", -1, s_cultureGb, false), }; DomainAndUri[] domainAndUris = DomainAndUris(current, domains); - string output = helper.MapDomain(domainAndUris, current, s_cultureFr.Name, s_cultureFr.Name).Uri.ToString(); + string output = helper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); // will pick it all right @@ -196,7 +196,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new Domain(1, "https://domain2.com", -1, s_cultureGb, false) }; domainAndUris = DomainAndUris(current, domains); - output = helper.MapDomain(domainAndUris, current, s_cultureFr.Name, s_cultureFr.Name).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); current = new Uri("https://domain1.com/foo/bar"); @@ -206,7 +206,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new Domain(1, "https://domain4.com", -1, s_cultureGb, false) }; domainAndUris = DomainAndUris(current, domains); - output = helper.MapDomain(domainAndUris, current, s_cultureFr.Name, s_cultureFr.Name).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); current = new Uri("https://domain4.com/foo/bar"); @@ -216,7 +216,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new Domain(1, "https://domain4.com", -1, s_cultureGb, false) }; domainAndUris = DomainAndUris(current, domains); - output = helper.MapDomain(domainAndUris, current, s_cultureFr.Name, s_cultureFr.Name).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, s_cultureFr, s_cultureFr).Uri.ToString(); Assert.AreEqual("https://domain4.com/", output); } @@ -240,8 +240,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new DomainAndUri(new Domain(1, "domain1.com", -1, s_cultureFr, false), current), new DomainAndUri(new Domain(1, "domain2.com", -1, s_cultureGb, false), current), }, current, - s_cultureFr.Name, - s_cultureFr.Name).Uri.ToString(); + s_cultureFr, + s_cultureFr).Uri.ToString(); Assert.AreEqual("http://domain1.com/", output); // current is a site1 uri, domains do not contain current @@ -253,8 +253,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureFr, false), current), new DomainAndUri(new Domain(1, "domain2.net", -1, s_cultureGb, false), current) }, current, - s_cultureFr.Name, - s_cultureFr.Name).Uri.ToString(); + s_cultureFr, + s_cultureFr).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); // current is a site1 uri, domains do not contain current @@ -267,8 +267,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new DomainAndUri(new Domain(1, "domain2.net", -1, s_cultureFr, false), current), new DomainAndUri(new Domain(1, "domain1.net", -1, s_cultureGb, false), current) }, current, - s_cultureFr.Name, - s_cultureFr.Name).Uri.ToString(); + s_cultureFr, + s_cultureFr).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); } @@ -301,8 +301,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing }, current, true, - s_cultureFr.Name, - s_cultureFr.Name).ToArray(); + s_cultureFr, + s_cultureFr).ToArray(); Assert.AreEqual(1, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -320,8 +320,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing }, current, true, - s_cultureFr.Name, - s_cultureFr.Name).ToArray(); + s_cultureFr, + s_cultureFr).ToArray(); Assert.AreEqual(1, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -343,8 +343,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing }, current, true, - s_cultureFr.Name, - s_cultureFr.Name).ToArray(); + s_cultureFr, + s_cultureFr).ToArray(); Assert.AreEqual(3, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -364,8 +364,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing new DomainAndUri(new Domain(1, "domain1.org", -1, s_cultureGb, false), current), // yes: same site (though bogus setup) }, current, true, - s_cultureFr.Name, - s_cultureFr.Name).ToArray(); + s_cultureFr, + s_cultureFr).ToArray(); Assert.AreEqual(3, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Web/Routing/PublishedRequestBuilderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Web/Routing/PublishedRequestBuilderTests.cs index cf4ca44f10..234226c3c7 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Web/Routing/PublishedRequestBuilderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Web/Routing/PublishedRequestBuilderTests.cs @@ -48,7 +48,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Web.Routing sut.SetDomain( new DomainAndUri( - new Domain(1, "test", 2, CultureInfo.GetCultureInfo("en-AU"), false), new Uri("https://example.com/en-au"))); + new Domain(1, "test", 2, "en-AU", false), new Uri("https://example.com/en-au"))); Assert.IsNotNull(sut.Domain); Assert.IsNotNull(sut.Culture); @@ -62,8 +62,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Web.Routing IPublishedContent content = Mock.Of(x => x.Id == 1); ITemplate template = Mock.Of(x => x.Id == 1); string[] cacheExt = new[] { "must-revalidate" }; - var auCulture = CultureInfo.GetCultureInfo("en-AU"); - var usCulture = CultureInfo.GetCultureInfo("en-US"); + var auCulture = "en-AU"; + var usCulture = "en-US"; var domain = new DomainAndUri( new Domain(1, "test", 2, auCulture, false), new Uri("https://example.com/en-au")); IReadOnlyDictionary headers = new Dictionary { ["Hello"] = "world" }; diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs index abaa239598..92c2691f90 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Linq; using Umbraco.Core; @@ -19,20 +19,14 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache } /// - public IEnumerable GetAll(bool includeWildcards) - { - return _domainService.GetAll(includeWildcards) + public IEnumerable GetAll(bool includeWildcards) => _domainService.GetAll(includeWildcards) .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard)); - } + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, x.LanguageIsoCode, x.IsWildcard)); /// - public IEnumerable GetAssigned(int documentId, bool includeWildcards = false) - { - return _domainService.GetAssignedDomains(documentId, includeWildcards) + public IEnumerable GetAssigned(int documentId, bool includeWildcards = false) => _domainService.GetAssignedDomains(documentId, includeWildcards) .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, CultureInfo.GetCultureInfo(x.LanguageIsoCode), x.IsWildcard)); - } + .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, x.LanguageIsoCode, x.IsWildcard)); /// public bool HasAssigned(int documentId, bool includeWildcards = false) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index 52b76a0021..39fc49a9db 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.PublishedContent var request = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); var content = GetPublishedContentMock(); request.SetPublishedContent(content.Object); - request.SetCulture(new CultureInfo("en-AU")); + request.SetCulture("en-AU"); request.SetRedirect("/hello"); var result = publishedRouter.BuildRequest(request); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs index 4746720329..13f5e8b214 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -64,7 +64,9 @@ namespace Umbraco.Tests.Routing publishedRouter.FindDomain(request); if (expectedNode > 0) - Assert.AreEqual(expectedCulture, request.Culture.Name); + { + Assert.AreEqual(expectedCulture, request.Culture); + } var finder = new ContentFinderByUrlAlias(LoggerFactory.CreateLogger(), Mock.Of(), VariationContextAccessor, GetUmbracoContextAccessor(umbracoContext)); var result = finder.TryFindContent(request); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs index 2b5364c22a..c86fd0fe1c 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Routing var snapshotService = CreatePublishedSnapshotService(globalSettings); var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings, snapshotService: snapshotService); var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); Assert.IsTrue(globalSettings.HideTopLevelNodeFromPath); @@ -119,7 +119,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings); var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/"))); + frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite", -1, "en-US", false), new Uri("http://mysite/"))); var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); var result = lookup.TryFindContent(frequest); @@ -147,7 +147,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings); var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/æøå"))); + frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite/æøå", -1, "en-US", false), new Uri("http://mysite/æøå"))); var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs index 12115ba3ad..df8d372b98 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -180,7 +180,7 @@ namespace Umbraco.Tests.Routing // must lookup domain else lookup by URL fails publishedRouter.FindDomain(frequest); - Assert.AreEqual(expectedCulture, frequest.Culture.Name); + Assert.AreEqual(expectedCulture, frequest.Culture); var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index dd5fd6351d..cd4fdc6fdd 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -275,7 +275,7 @@ namespace Umbraco.Tests.Routing // lookup domain publishedRouter.FindDomain(frequest); - Assert.AreEqual(expectedCulture, frequest.Culture.Name); + Assert.AreEqual(expectedCulture, frequest.Culture); var finder = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); var result = finder.TryFindContent(frequest); @@ -331,7 +331,7 @@ namespace Umbraco.Tests.Routing publishedRouter.HandleWildcardDomains(frequest); Assert.IsTrue(result); - Assert.AreEqual(expectedCulture, frequest.Culture.Name); + Assert.AreEqual(expectedCulture, frequest.Culture); Assert.AreEqual(frequest.PublishedContent.Id, expectedNode); } // domains such as "/en" are natively supported, and when instanciating @@ -377,7 +377,7 @@ namespace Umbraco.Tests.Routing publishedRouter.FindDomain(frequest); Assert.IsNotNull(frequest.Domain); - Assert.AreEqual(expectedCulture, frequest.Culture.Name); + Assert.AreEqual(expectedCulture, frequest.Culture); var finder = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); var result = finder.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs b/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs index dca0caadc4..597a7e223e 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -186,8 +186,8 @@ namespace Umbraco.Tests.Routing if (contentId != 9876) return Enumerable.Empty(); return new[] { - new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false), //default - new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false) + new Domain(2, "example.us", 9876, "en-US", false), //default + new Domain(3, "example.fr", 9876, "fr-FR", false) }; }); domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); @@ -240,8 +240,8 @@ namespace Umbraco.Tests.Routing if (contentId != 9876) return Enumerable.Empty(); return new[] { - new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false), //default - new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false) + new Domain(2, "example.us", 9876, "en-US", false), //default + new Domain(3, "example.fr", 9876, "fr-FR", false) }; }); domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); diff --git a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs index 21b205de0f..969c267821 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -50,6 +50,7 @@ namespace Umbraco.Web.BackOffice.Controllers // get cultures - new-ing instances to get proper display name, // in the current culture, and not the cached one // (see notes in Language class about culture info names) + // TODO: Fix this requirement, see https://github.com/umbraco/Umbraco-CMS/issues/3623 return CultureInfo.GetCultures(CultureTypes.AllCultures) .Where(x => !x.Name.IsNullOrWhiteSpace()) .Select(x => new CultureInfo(x.Name)) // important! diff --git a/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs b/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs index a09230a3fc..741583413c 100644 --- a/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs +++ b/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs @@ -1,7 +1,9 @@ using System.Globalization; using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Localization; +using Microsoft.Extensions.Options; using Umbraco.Core.Security; namespace Umbraco.Web.Common.Localization @@ -12,6 +14,13 @@ namespace Umbraco.Web.Common.Localization /// public class UmbracoBackOfficeIdentityCultureProvider : RequestCultureProvider { + private readonly RequestLocalizationOptions _localizationOptions; + + /// + /// Initializes a new instance of the class. + /// + public UmbracoBackOfficeIdentityCultureProvider(RequestLocalizationOptions localizationOptions) => _localizationOptions = localizationOptions; + /// public override Task DetermineProviderCultureResult(HttpContext httpContext) { @@ -22,6 +31,17 @@ namespace Umbraco.Web.Common.Localization return NullProviderCultureResult; } + // We need to dynamically change the supported cultures since we won't ever know what languages are used since + // they are dynamic within Umbraco. + var cultureExists = _localizationOptions.SupportedCultures.Contains(culture); + + if (!cultureExists) + { + // add this as a supporting culture + _localizationOptions.SupportedCultures.Add(culture); + _localizationOptions.SupportedUICultures.Add(culture); + } + return Task.FromResult(new ProviderCultureResult(culture.Name)); } } diff --git a/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs b/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs index cc683848c3..bedf5e73a7 100644 --- a/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs +++ b/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs @@ -1,8 +1,13 @@ -using System.Globalization; +using System; +using System.Globalization; +using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Umbraco.Web.Common.Routing; using Umbraco.Web.Routing; @@ -13,19 +18,42 @@ namespace Umbraco.Web.Common.Localization /// public class UmbracoPublishedContentCultureProvider : RequestCultureProvider { + private readonly RequestLocalizationOptions _localizationOptions; + + /// + /// Initializes a new instance of the class. + /// + public UmbracoPublishedContentCultureProvider(RequestLocalizationOptions localizationOptions) => _localizationOptions = localizationOptions; + /// public override Task DetermineProviderCultureResult(HttpContext httpContext) { if (httpContext.GetRouteValue(Core.Constants.Web.UmbracoRouteDefinitionDataToken) is UmbracoRouteValues routeValues) { - CultureInfo culture = routeValues.PublishedRequest?.Culture; + string culture = routeValues.PublishedRequest?.Culture; if (culture != null) { - return Task.FromResult(new ProviderCultureResult(culture.Name)); + // We need to dynamically change the supported cultures since we won't ever know what languages are used since + // they are dynamic within Umbraco. + // 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); + _localizationOptions.SupportedUICultures.Add(ci); + } + + return Task.FromResult(new ProviderCultureResult(culture)); } } return NullProviderCultureResult; } + } } diff --git a/src/Umbraco.Web.Common/Localization/UmbracoRequestLocalizationOptions.cs b/src/Umbraco.Web.Common/Localization/UmbracoRequestLocalizationOptions.cs index 1a27798c35..a4c6d117ca 100644 --- a/src/Umbraco.Web.Common/Localization/UmbracoRequestLocalizationOptions.cs +++ b/src/Umbraco.Web.Common/Localization/UmbracoRequestLocalizationOptions.cs @@ -30,8 +30,8 @@ namespace Umbraco.Web.Common.Localization options.RequestCultureProviders = new List(); } - options.RequestCultureProviders.Insert(0, new UmbracoBackOfficeIdentityCultureProvider()); - options.RequestCultureProviders.Insert(1, new UmbracoPublishedContentCultureProvider()); + options.RequestCultureProviders.Insert(0, new UmbracoBackOfficeIdentityCultureProvider(options)); + options.RequestCultureProviders.Insert(1, new UmbracoPublishedContentCultureProvider(options)); } } } diff --git a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs index d9b7bf95a4..23b2ba6466 100644 --- a/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs +++ b/src/Umbraco.Web.Common/Templates/TemplateRenderer.cs @@ -87,8 +87,8 @@ namespace Umbraco.Web.Common.Templates var defaultLanguage = _languageService.GetAllLanguages().FirstOrDefault(); requestBuilder.SetCulture(defaultLanguage == null - ? CultureInfo.CurrentUICulture - : defaultLanguage.CultureInfo); + ? CultureInfo.CurrentUICulture.Name + : defaultLanguage.CultureInfo.Name); } else {