From dd111226090642cd6777630ff45e432ce8349656 Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Sun, 17 Jan 2021 21:33:35 +1300 Subject: [PATCH] Huge improvement in speed and memory allocations for localized text service. --- .../Compose/RelateOnTrashComponent.cs | 5 +- .../Services/ILocalizedTextService.cs | 14 + .../Implement/LocalizedTextService.cs | 241 ++++---- .../LocalizedTextServiceExtensions.cs | 67 ++- ...TextServiceGetAllStoredValuesBenchmarks.cs | 533 ++++++++++++++++++ .../Umbraco.Tests.Benchmarks.csproj | 1 + .../Mapping/ContentWebModelMappingTests.cs | 4 +- .../Compose/NotificationsComponent.cs | 8 +- .../Editors/AuthenticationController.cs | 2 +- .../Editors/BackOfficeController.cs | 2 +- src/Umbraco.Web/Editors/CodeFileController.cs | 10 +- src/Umbraco.Web/Editors/ContentController.cs | 134 ++--- .../Editors/ContentTypeController.cs | 6 +- .../Editors/ContentTypeControllerBase.cs | 6 +- .../Editors/CurrentUserController.cs | 2 +- src/Umbraco.Web/Editors/DataTypeController.cs | 4 +- src/Umbraco.Web/Editors/MediaController.cs | 26 +- .../Editors/MediaTypeController.cs | 2 +- src/Umbraco.Web/Editors/MemberController.cs | 2 +- .../Editors/MemberGroupController.cs | 2 +- .../Editors/MemberTypeController.cs | 2 +- .../Editors/PackageInstallController.cs | 10 +- .../Editors/TemplateQueryController.cs | 40 +- .../Editors/UserGroupsController.cs | 6 +- src/Umbraco.Web/Editors/UsersController.cs | 24 +- .../Checks/Config/AbstractConfigCheck.cs | 16 +- .../Checks/Config/CompilationDebugCheck.cs | 6 +- .../Checks/Config/ConfigurationService.cs | 12 +- .../Checks/Config/CustomErrorsCheck.cs | 6 +- .../Checks/Config/MacroErrorsCheck.cs | 6 +- .../Checks/Config/NotificationEmailCheck.cs | 4 +- .../HealthCheck/Checks/Config/TraceCheck.cs | 6 +- .../Config/TrySkipIisCustomErrorsCheck.cs | 6 +- .../FolderAndFilePermissionsCheck.cs | 20 +- .../Checks/Security/BaseHttpHeaderCheck.cs | 14 +- .../Checks/Security/ExcessiveHeadersCheck.cs | 6 +- .../HealthCheck/Checks/Security/HttpsCheck.cs | 28 +- .../HealthCheck/Checks/Services/SmtpCheck.cs | 8 +- .../EmailNotificationMethod.cs | 4 +- src/Umbraco.Web/Macros/MacroRenderer.cs | 4 +- .../Models/Mapping/CommonMapper.cs | 2 +- .../Models/Mapping/ContentVariantMapper.cs | 2 +- .../Mapping/MemberTabsAndPropertiesMapper.cs | 16 +- .../Models/Mapping/SectionMapDefinition.cs | 2 +- .../Models/Mapping/TabsAndPropertiesMapper.cs | 2 +- .../Models/Mapping/UserMapDefinition.cs | 26 +- src/Umbraco.Web/Models/Trees/MenuItem.cs | 7 +- src/Umbraco.Web/Models/Trees/MenuItemList.cs | 6 +- .../BlockEditorPropertyEditor.cs | 4 +- .../DropDownFlexibleConfigurationEditor.cs | 2 +- .../UploadFileTypeValidator.cs | 2 +- .../ValueListConfigurationEditor.cs | 2 +- .../Routing/UrlProviderExtensions.cs | 14 +- .../Trees/ApplicationTreeController.cs | 6 +- .../Trees/ContentTreeControllerBase.cs | 2 +- .../Trees/DataTypeTreeController.cs | 2 +- .../Trees/MediaTypeTreeController.cs | 2 +- src/Umbraco.Web/Trees/MemberTreeController.cs | 2 +- src/Umbraco.Web/Trees/Tree.cs | 2 +- 59 files changed, 1002 insertions(+), 400 deletions(-) create mode 100644 src/Umbraco.Tests.Benchmarks/LocalizedTextServiceGetAllStoredValuesBenchmarks.cs diff --git a/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs b/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs index a216a584ba..27eb559462 100644 --- a/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs +++ b/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs @@ -93,7 +93,8 @@ namespace Umbraco.Core.Compose item.Entity.Id, ObjectTypes.GetName(UmbracoObjectTypes.Document), string.Format(textService.Localize( - "recycleBin/contentTrashed"), + "recycleBin","contentTrashed"), + item.Entity.Id, originalParentId)); } } @@ -132,7 +133,7 @@ namespace Umbraco.Core.Compose item.Entity.Id, ObjectTypes.GetName(UmbracoObjectTypes.Media), string.Format(textService.Localize( - "recycleBin/mediaTrashed"), + "recycleBin", "mediaTrashed"), item.Entity.Id, originalParentId)); } } diff --git a/src/Umbraco.Core/Services/ILocalizedTextService.cs b/src/Umbraco.Core/Services/ILocalizedTextService.cs index f167c64e0f..a4e5141a42 100644 --- a/src/Umbraco.Core/Services/ILocalizedTextService.cs +++ b/src/Umbraco.Core/Services/ILocalizedTextService.cs @@ -4,6 +4,20 @@ using System.Globalization; namespace Umbraco.Core.Services { + + public interface ILocalizedTextService2 : ILocalizedTextService + { + /// + /// Localize a key with variables + /// + /// + /// + /// + /// This can be null + /// + string Localize(string area, string alias, CultureInfo culture, IDictionary tokens = null); + } + /// /// The entry point to localize any key in the text storage source for a given culture /// diff --git a/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs b/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs index 4f5121def7..c099960397 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizedTextService.cs @@ -10,12 +10,13 @@ namespace Umbraco.Core.Services.Implement { // TODO: Convert all of this over to Niels K's localization framework one day - public class LocalizedTextService : ILocalizedTextService + public class LocalizedTextService : ILocalizedTextService2 { private readonly ILogger _logger; private readonly Lazy _fileSources; private readonly IDictionary>> _dictionarySource; - private readonly IDictionary> _xmlSource; + private readonly IDictionary> _noAreaDictionarySource; + private readonly char[] _splitter = new[] { '/' }; /// /// Initializes with a file sources instance @@ -27,9 +28,40 @@ namespace Umbraco.Core.Services.Implement if (logger == null) throw new ArgumentNullException("logger"); _logger = logger; if (fileSources == null) throw new ArgumentNullException("fileSources"); + var dictionaries = FileSourcesToDictionarySources(fileSources.Value); + _dictionarySource = dictionaries.WithArea; + _noAreaDictionarySource = dictionaries.WithoutArea; _fileSources = fileSources; } + private (IDictionary>> WithArea, IDictionary> WithoutArea) FileSourcesToDictionarySources(LocalizedTextServiceFileSources fileSources) + { + var xmlSources = fileSources.GetXmlSources(); + return XmlSourcesToDictionarySources(xmlSources); + } + private (IDictionary>> WithArea, IDictionary> WithoutArea) XmlSourcesToDictionarySources(IDictionary> source) + { + var cultureDictionary = new Dictionary>>(); + var cultureNoAreaDictionary = new Dictionary>(); + foreach (var xmlSource in source) + { + var areaAliaValue = GetAreaStoredTranslations(source, xmlSource.Key); + cultureDictionary.Add(xmlSource.Key, areaAliaValue); + var aliasValue = new Dictionary(); + foreach (var area in areaAliaValue) + { + foreach (var alias in area.Value) + { + if (!aliasValue.ContainsKey(alias.Key)) + { + aliasValue.Add(alias.Key, alias.Value); + } + } + } + cultureNoAreaDictionary.Add(xmlSource.Key, aliasValue); + } + return (cultureDictionary, cultureNoAreaDictionary); + } /// /// Initializes with an XML source /// @@ -39,10 +71,13 @@ namespace Umbraco.Core.Services.Implement { if (source == null) throw new ArgumentNullException("source"); if (logger == null) throw new ArgumentNullException("logger"); - _xmlSource = source; _logger = logger; + var dictionaries = XmlSourcesToDictionarySources(source); + _dictionarySource = dictionaries.WithArea; + _noAreaDictionarySource = dictionaries.WithoutArea; } + /// /// Initializes with a source of a dictionary of culture -> areas -> sub dictionary of keys/values /// @@ -52,35 +87,50 @@ namespace Umbraco.Core.Services.Implement { _dictionarySource = source ?? throw new ArgumentNullException(nameof(source)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + var cultureNoAreaDictionary = new Dictionary>(); + foreach (var cultureDictionary in _dictionarySource) + { + var areaAliaValue = GetAreaStoredTranslations(source, cultureDictionary.Key); + var aliasValue = new Dictionary(); + foreach (var area in areaAliaValue) + { + foreach (var alias in area.Value) + { + if (!aliasValue.ContainsKey(alias.Key)) + { + aliasValue.Add(alias.Key, alias.Value); + } + } + } + cultureNoAreaDictionary.Add(cultureDictionary.Key, aliasValue); + } + _noAreaDictionarySource = cultureNoAreaDictionary; } public string Localize(string key, CultureInfo culture, IDictionary tokens = null) { if (culture == null) throw new ArgumentNullException(nameof(culture)); - - // TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode - culture = ConvertToSupportedCultureWithRegionCode(culture); - - //This is what the legacy ui service did - if (string.IsNullOrEmpty(key)) - return string.Empty; - - var keyParts = key.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); + var keyParts = key.Split(_splitter, StringSplitOptions.RemoveEmptyEntries); var area = keyParts.Length > 1 ? keyParts[0] : null; var alias = keyParts.Length > 1 ? keyParts[1] : keyParts[0]; - - var xmlSource = _xmlSource ?? (_fileSources != null - ? _fileSources.Value.GetXmlSources() - : null); - - if (xmlSource != null) - { - return GetFromXmlSource(xmlSource, culture, area, alias, tokens); - } - else + return Localize(area, alias, culture, tokens); + } + public string Localize(string area, string alias, CultureInfo culture, IDictionary tokens = null) + { + if (culture == null) throw new ArgumentNullException(nameof(culture)); + var sw = System.Diagnostics.Stopwatch.StartNew(); + try { + // TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode + culture = ConvertToSupportedCultureWithRegionCode(culture); + return GetFromDictionarySource(culture, area, alias, tokens); } + finally + { + sw.Stop(); + System.Diagnostics.Debug.WriteLine($"Localize {area}/{alias} ({tokens?.Count}) ({sw.ElapsedTicks})"); + } } /// @@ -96,49 +146,23 @@ namespace Umbraco.Core.Services.Implement var result = new Dictionary(); - var xmlSource = _xmlSource ?? (_fileSources != null - ? _fileSources.Value.GetXmlSources() - : null); - if (xmlSource != null) + if (_dictionarySource.ContainsKey(culture) == false) { - if (xmlSource.ContainsKey(culture) == false) - { - _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); - return result; - } - - //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"); - 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); - } + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); + return result; } - else - { - if (_dictionarySource.ContainsKey(culture) == false) - { - _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); - return result; - } - //convert all areas + keys to a single key with a '/' - foreach (var area in _dictionarySource[culture]) + //convert all areas + keys to a single key with a '/' + foreach (var area in _dictionarySource[culture]) + { + foreach (var key in area.Value) { - 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. + if (result.ContainsKey(dictionaryKey) == false) { - 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. - if (result.ContainsKey(dictionaryKey) == false) - { - result.Add(dictionaryKey, key.Value); - } + result.Add(dictionaryKey, key.Value); } } } @@ -146,23 +170,44 @@ namespace Umbraco.Core.Services.Implement return result; } - private Dictionary GetStoredTranslations(IDictionary> xmlSource, CultureInfo cult) + private Dictionary> GetAreaStoredTranslations(IDictionary> xmlSource, CultureInfo cult) { - var result = new Dictionary(); + var overallResult = new Dictionary>(); var areas = xmlSource[cult].Value.XPathSelectElements("//area"); foreach (var area in areas) { + var result = new Dictionary(); var keys = area.XPathSelectElements("./key"); foreach (var key in keys) { - var dictionaryKey = string.Format("{0}/{1}", (string)area.Attribute("alias"), - (string)key.Attribute("alias")); + var dictionaryKey = + (string)key.Attribute("alias"); //there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files if (result.ContainsKey(dictionaryKey) == false) result.Add(dictionaryKey, key.Value); } + overallResult.Add(area.Attribute("alias").Value, result); } - return result; + return overallResult; + } + private Dictionary> GetAreaStoredTranslations(IDictionary>> dictionarySource, CultureInfo cult) + { + var overallResult = new Dictionary>(); + var areaDict = dictionarySource[cult]; + + foreach (var area in areaDict) + { + var result = new Dictionary(); + var keys = area.Value.Keys; + foreach (var key in keys) + { + //there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files + if (result.ContainsKey(key) == false) + result.Add(key, area.Value[key]); + } + overallResult.Add(area.Key, result); + } + return overallResult; } /// @@ -171,11 +216,7 @@ namespace Umbraco.Core.Services.Implement /// public IEnumerable GetSupportedCultures() { - var xmlSource = _xmlSource ?? (_fileSources != null - ? _fileSources.Value.GetXmlSources() - : null); - - return xmlSource != null ? xmlSource.Keys : _dictionarySource.Keys; + return _dictionarySource.Keys; } /// @@ -211,27 +252,21 @@ namespace Umbraco.Core.Services.Implement return "[" + key + "]"; } - var cultureSource = _dictionarySource[culture]; - string found; - if (area.IsNullOrWhiteSpace()) + string found = null; + if (string.IsNullOrWhiteSpace(area)) { - found = cultureSource - .SelectMany(x => x.Value) - .Where(keyvals => keyvals.Key.InvariantEquals(key)) - .Select(x => x.Value) - .FirstOrDefault(); + _noAreaDictionarySource[culture].TryGetValue(key, out found); } else { - found = cultureSource - .Where(areas => areas.Key.InvariantEquals(area)) - .SelectMany(a => a.Value) - .Where(keyvals => keyvals.Key.InvariantEquals(key)) - .Select(x => x.Value) - .FirstOrDefault(); + if (_dictionarySource[culture].TryGetValue(area, out var areaDictionary)) + { + areaDictionary.TryGetValue(key, out found); + } } + if (found != null) { return ParseTokens(found, tokens); @@ -240,44 +275,6 @@ namespace Umbraco.Core.Services.Implement //NOTE: Based on how legacy works, the default text does not contain the area, just the key return "[" + key + "]"; } - - private string GetFromXmlSource(IDictionary> xmlSource, CultureInfo culture, string area, string key, IDictionary tokens) - { - if (xmlSource.ContainsKey(culture) == false) - { - _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); - return "[" + key + "]"; - } - - var found = FindTranslation(xmlSource, culture, area, key); - - if (found != null) - { - return ParseTokens(found.Value, tokens); - } - - // Fall back to English by default if we can't find the key - found = FindTranslation(xmlSource, new CultureInfo("en-US"), area, key); - if (found != null) - return ParseTokens(found.Value, tokens); - - // If it can't be found in either file, fall back to the default, showing just the key in square brackets - // NOTE: Based on how legacy works, the default text does not contain the area, just the key - return "[" + key + "]"; - } - - private XElement FindTranslation(IDictionary> xmlSource, CultureInfo culture, string area, string key) - { - var cultureSource = xmlSource[culture].Value; - - var xpath = area.IsNullOrWhiteSpace() - ? string.Format("//key [@alias = '{0}']", key) - : string.Format("//area [@alias = '{0}']/key [@alias = '{1}']", area, key); - - var found = cultureSource.XPathSelectElement(xpath); - return found; - } - /// /// Parses the tokens in the value /// @@ -301,7 +298,7 @@ namespace Umbraco.Core.Services.Implement foreach (var token in tokens) { - value = value.Replace(string.Format("{0}{1}{0}", "%", token.Key), token.Value); + value = value.Replace(string.Concat("%", token.Key, "%"), token.Value); } return value; diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs index ce5b3ef8c4..c6254f7baa 100644 --- a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs +++ b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs @@ -12,12 +12,75 @@ namespace Umbraco.Core.Services /// public static class LocalizedTextServiceExtensions { - public static string Localize(this ILocalizedTextService manager, string area, string key) + public static string Localize(this ILocalizedTextService manager, string area, string alias, CultureInfo culture) { - var fullKey = string.Join("/", area, key); + if(manager is ILocalizedTextService2 manager2) + { + return manager2.Localize(area, alias, culture); + } + var fullKey = string.Concat(area, "/", alias); + return manager.Localize(fullKey, culture); + } + public static string Localize(this ILocalizedTextService manager, string area, string alias) + { + if (manager is ILocalizedTextService2 manager2) + { + return manager2.Localize(area, alias, Thread.CurrentThread.CurrentUICulture); + } + var fullKey = string.Concat(area, "/", alias); return manager.Localize(fullKey, Thread.CurrentThread.CurrentUICulture); } + /// + /// Localize using the current thread culture + /// + /// + /// + /// + /// + /// + public static string Localize(this ILocalizedTextService manager, string area, string alias, string[] tokens) + { + if (manager is ILocalizedTextService2 manager2) + { + return manager2.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, ConvertToDictionaryVars(tokens)); + } + return manager.Localize(string.Join("/",area, alias), Thread.CurrentThread.CurrentUICulture, tokens); + } + /// + /// Localize using the current thread culture + /// + /// + /// + /// + /// + /// + public static string Localize(this ILocalizedTextService manager, string area, string alias, IDictionary tokens = null) + { + if (manager is ILocalizedTextService2 manager2) + { + return manager2.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, tokens); + } + return manager.Localize(string.Join("/", area, alias), Thread.CurrentThread.CurrentUICulture, tokens); + } + + /// + /// Localize a key without any variables + /// + /// + /// + /// + /// + /// + /// + public static string Localize(this ILocalizedTextService manager, string area, string alias, CultureInfo culture, string[] tokens) + { + if (manager is ILocalizedTextService2 manager2) + { + return manager2.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, tokens); + } + return manager.Localize(string.Join("/", area, alias), culture, ConvertToDictionaryVars(tokens)); + } /// /// Localize using the current thread culture /// diff --git a/src/Umbraco.Tests.Benchmarks/LocalizedTextServiceGetAllStoredValuesBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/LocalizedTextServiceGetAllStoredValuesBenchmarks.cs new file mode 100644 index 0000000000..e084931d9e --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/LocalizedTextServiceGetAllStoredValuesBenchmarks.cs @@ -0,0 +1,533 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Loggers; +using Moq; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Services.Implement; +using System.Xml.Linq; +using Umbraco.Core.Logging; +using Umbraco.Tests.Benchmarks.Config; +using Umbraco.Core.Services; +using Umbraco.Core; +using System.Xml.XPath; +using ILogger = Umbraco.Core.Logging.ILogger; + +namespace Umbraco.Tests.Benchmarks +{ + [QuickRunWithMemoryDiagnoserConfig] + public class LocalizedTextServiceGetAllStoredValuesBenchmarks + { + private CultureInfo culture; + private OldLocalizedTextService _dictionaryService; + private OldLocalizedTextService _xmlService; + + private LocalizedTextService _optimized; + private LocalizedTextService _optimizedDict; + [GlobalSetup] + public void Setup() + { + culture = CultureInfo.GetCultureInfo("en-US"); + _dictionaryService = GetDictionaryLocalizedTextService(culture); + _xmlService = GetXmlService(culture); + _optimized = GetOptimizedService(culture); + _optimizedDict = GetOptimizedServiceDict(culture); + var result1 = _dictionaryService.Localize("language", culture); + var result2 = _xmlService.Localize("language", culture); + var result3 = _dictionaryService.GetAllStoredValues(culture); + var result4 = _xmlService.GetAllStoredValues(culture); + var result5 = _optimized.GetAllStoredValues(culture); + var result6 = _xmlService.GetAllStoredValues(culture); + } + + [Benchmark] + public void OriginalDictionaryGetAll() + { + for (int i = 0; i < 10000; i++) + { + var result = _dictionaryService.GetAllStoredValues(culture); + } + + } + + [Benchmark] + public void OriginalXmlGetAll() + { + for (int i = 0; i < 10000; i++) + { + var result = _xmlService.GetAllStoredValues(culture); + } + + } + + [Benchmark] + public void OriginalDictionaryLocalize() + { + for (int i = 0; i < 10000; i++) + { + var result = _dictionaryService.Localize("language", culture); + } + + } + + + [Benchmark(Baseline = true)] + public void OriginalXmlLocalize() + { + for (int i = 0; i < 10000; i++) + { + var result = _xmlService.Localize("language", culture); + } + } + [Benchmark()] + public void OptimizedXmlLocalize() + { + for (int i = 0; i < 10000; i++) + { + var result = _optimized.Localize(null, "language", culture); + } + } + [Benchmark()] + public void OptimizedDictLocalize() + { + for (int i = 0; i < 10000; i++) + { + var result = _optimizedDict.Localize(null, "language", culture); + } + } + + private static LocalizedTextService GetOptimizedServiceDict(CultureInfo culture) + { + return new LocalizedTextService( + new Dictionary>> + { + { + culture, new Dictionary> + { + { + "testArea1", new Dictionary + { + {"testKey1", "testValue1"}, + {"testKey2", "testValue2"} + } + }, + { + "testArea2", new Dictionary + { + {"blah1", "blahValue1"}, + {"blah2", "blahValue2"} + } + }, + } + } + }, Mock.Of()); + } + private static LocalizedTextService GetOptimizedService(CultureInfo culture) + { + var txtService = new LocalizedTextService(new Dictionary> + { + { + culture, new Lazy(() => new XDocument( + new XElement("language", + new XElement("area", new XAttribute("alias", "testArea1"), + new XElement("key", new XAttribute("alias", "testKey1"), "testValue1"), + new XElement("key", new XAttribute("alias", "testKey2"), "testValue2")), + new XElement("area", new XAttribute("alias", "testArea2"), + new XElement("key", new XAttribute("alias", "blah1"), "blahValue1"), + new XElement("key", new XAttribute("alias", "blah2"), "blahValue2"))))) + } + }, Mock.Of()); + return txtService; + } + + private static OldLocalizedTextService GetXmlService(CultureInfo culture) + { + var txtService = new OldLocalizedTextService(new Dictionary> + { + { + culture, new Lazy(() => new XDocument( + new XElement("language", + new XElement("area", new XAttribute("alias", "testArea1"), + new XElement("key", new XAttribute("alias", "testKey1"), "testValue1"), + new XElement("key", new XAttribute("alias", "testKey2"), "testValue2")), + new XElement("area", new XAttribute("alias", "testArea2"), + new XElement("key", new XAttribute("alias", "blah1"), "blahValue1"), + new XElement("key", new XAttribute("alias", "blah2"), "blahValue2"))))) + } + }, Mock.Of()); + return txtService; + } + + private static OldLocalizedTextService GetDictionaryLocalizedTextService(CultureInfo culture) + { + return new OldLocalizedTextService( + new Dictionary>> + { + { + culture, new Dictionary> + { + { + "testArea1", new Dictionary + { + {"testKey1", "testValue1"}, + {"testKey2", "testValue2"} + } + }, + { + "testArea2", new Dictionary + { + {"blah1", "blahValue1"}, + {"blah2", "blahValue2"} + } + }, + } + } + }, Mock.Of()); + } + } + + //Original + public class OldLocalizedTextService : ILocalizedTextService + { + private readonly ILogger _logger; + private readonly Lazy _fileSources; + private readonly IDictionary>> _dictionarySource; + private readonly IDictionary> _xmlSource; + + /// + /// Initializes with a file sources instance + /// + /// + /// + public OldLocalizedTextService(Lazy fileSources, ILogger logger) + { + if (logger == null) throw new ArgumentNullException("logger"); + _logger = logger; + if (fileSources == null) throw new ArgumentNullException("fileSources"); + _fileSources = fileSources; + } + + /// + /// Initializes with an XML source + /// + /// + /// + public OldLocalizedTextService(IDictionary> source, ILogger logger) + { + if (source == null) throw new ArgumentNullException("source"); + if (logger == null) throw new ArgumentNullException("logger"); + _xmlSource = source; + _logger = logger; + } + + /// + /// Initializes with a source of a dictionary of culture -> areas -> sub dictionary of keys/values + /// + /// + /// + public OldLocalizedTextService(IDictionary>> source, ILogger logger) + { + _dictionarySource = source ?? throw new ArgumentNullException(nameof(source)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public string Localize(string key, CultureInfo culture, IDictionary tokens = null) + { + if (culture == null) throw new ArgumentNullException(nameof(culture)); + + // TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode + culture = ConvertToSupportedCultureWithRegionCode(culture); + + //This is what the legacy ui service did + if (string.IsNullOrEmpty(key)) + return string.Empty; + + var keyParts = key.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + var area = keyParts.Length > 1 ? keyParts[0] : null; + var alias = keyParts.Length > 1 ? keyParts[1] : keyParts[0]; + + var xmlSource = _xmlSource ?? (_fileSources != null + ? _fileSources.Value.GetXmlSources() + : null); + + if (xmlSource != null) + { + return GetFromXmlSource(xmlSource, culture, area, alias, tokens); + } + else + { + return GetFromDictionarySource(culture, area, alias, tokens); + } + } + + /// + /// Returns all key/values in storage for the given culture + /// + /// + public IDictionary GetAllStoredValues(CultureInfo culture) + { + if (culture == null) throw new ArgumentNullException("culture"); + + // TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode + culture = ConvertToSupportedCultureWithRegionCode(culture); + + var result = new Dictionary(); + + var xmlSource = _xmlSource ?? (_fileSources != null + ? _fileSources.Value.GetXmlSources() + : null); + + if (xmlSource != null) + { + if (xmlSource.ContainsKey(culture) == false) + { + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); + return result; + } + + //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"); + 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 + { + if (_dictionarySource.ContainsKey(culture) == false) + { + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); + return result; + } + + //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. + if (result.ContainsKey(dictionaryKey) == false) + { + result.Add(dictionaryKey, key.Value); + } + } + } + } + + return result; + } + + private Dictionary GetStoredTranslations(IDictionary> xmlSource, CultureInfo cult) + { + var result = new Dictionary(); + var areas = xmlSource[cult].Value.XPathSelectElements("//area"); + foreach (var area in areas) + { + var keys = area.XPathSelectElements("./key"); + foreach (var key in keys) + { + var dictionaryKey = string.Format("{0}/{1}", (string)area.Attribute("alias"), + (string)key.Attribute("alias")); + //there could be duplicates if the language file isn't formatted nicely - which is probably the case for quite a few lang files + if (result.ContainsKey(dictionaryKey) == false) + result.Add(dictionaryKey, key.Value); + } + } + return result; + } + + /// + /// Returns a list of all currently supported cultures + /// + /// + public IEnumerable GetSupportedCultures() + { + var xmlSource = _xmlSource ?? (_fileSources != null + ? _fileSources.Value.GetXmlSources() + : null); + + return xmlSource != null ? xmlSource.Keys : _dictionarySource.Keys; + } + + /// + /// Tries to resolve a full 4 letter culture from a 2 letter culture name + /// + /// + /// The culture to determine if it is only a 2 letter culture, if so we'll try to convert it, otherwise it will just be returned + /// + /// + /// + /// TODO: This is just a hack due to the way we store the language files, they should be stored with 4 letters since that + /// is what they reference but they are stored with 2, further more our user's languages are stored with 2. So this attempts + /// to resolve the full culture if possible. + /// + /// This only works when this service is constructed with the LocalizedTextServiceFileSources + /// + public CultureInfo ConvertToSupportedCultureWithRegionCode(CultureInfo currentCulture) + { + if (currentCulture == null) throw new ArgumentNullException("currentCulture"); + + if (_fileSources == null) return currentCulture; + if (currentCulture.Name.Length > 2) return currentCulture; + + var attempt = _fileSources.Value.TryConvert2LetterCultureTo4Letter(currentCulture.TwoLetterISOLanguageName); + return attempt ? attempt.Result : currentCulture; + } + + private string GetFromDictionarySource(CultureInfo culture, string area, string key, IDictionary tokens) + { + if (_dictionarySource.ContainsKey(culture) == false) + { + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); + return "[" + key + "]"; + } + + var cultureSource = _dictionarySource[culture]; + + string found; + if (area.IsNullOrWhiteSpace()) + { + found = cultureSource + .SelectMany(x => x.Value) + .Where(keyvals => keyvals.Key.InvariantEquals(key)) + .Select(x => x.Value) + .FirstOrDefault(); + } + else + { + found = cultureSource + .Where(areas => areas.Key.InvariantEquals(area)) + .SelectMany(a => a.Value) + .Where(keyvals => keyvals.Key.InvariantEquals(key)) + .Select(x => x.Value) + .FirstOrDefault(); + } + + if (found != null) + { + return ParseTokens(found, tokens); + } + + //NOTE: Based on how legacy works, the default text does not contain the area, just the key + return "[" + key + "]"; + } + + private string GetFromXmlSource(IDictionary> xmlSource, CultureInfo culture, string area, string key, IDictionary tokens) + { + if (xmlSource.ContainsKey(culture) == false) + { + _logger.Warn("The culture specified {Culture} was not found in any configured sources for this service", culture); + return "[" + key + "]"; + } + + var found = FindTranslation(xmlSource, culture, area, key); + + if (found != null) + { + return ParseTokens(found.Value, tokens); + } + + // Fall back to English by default if we can't find the key + found = FindTranslation(xmlSource, new CultureInfo("en-US"), area, key); + if (found != null) + return ParseTokens(found.Value, tokens); + + // If it can't be found in either file, fall back to the default, showing just the key in square brackets + // NOTE: Based on how legacy works, the default text does not contain the area, just the key + return "[" + key + "]"; + } + + private XElement FindTranslation(IDictionary> xmlSource, CultureInfo culture, string area, string key) + { + var cultureSource = xmlSource[culture].Value; + + var xpath = area.IsNullOrWhiteSpace() + ? string.Format("//key [@alias = '{0}']", key) + : string.Format("//area [@alias = '{0}']/key [@alias = '{1}']", area, key); + + var found = cultureSource.XPathSelectElement(xpath); + return found; + } + + /// + /// Parses the tokens in the value + /// + /// + /// + /// + /// + /// This is based on how the legacy ui localized text worked, each token was just a sequential value delimited with a % symbol. + /// For example: hello %0%, you are %1% ! + /// + /// Since we're going to continue using the same language files for now, the token system needs to remain the same. With our new service + /// we support a dictionary which means in the future we can really have any sort of token system. + /// Currently though, the token key's will need to be an integer and sequential - though we aren't going to throw exceptions if that is not the case. + /// + internal static string ParseTokens(string value, IDictionary tokens) + { + if (tokens == null || tokens.Any() == false) + { + return value; + } + + foreach (var token in tokens) + { + value = value.Replace(string.Format("{0}{1}{0}", "%", token.Key), token.Value); + } + + return value; + } + + } + +// // * Summary * + +// BenchmarkDotNet=v0.11.3, OS=Windows 10.0.18362 +//Intel Core i5-8265U CPU 1.60GHz(Kaby Lake R), 1 CPU, 8 logical and 4 physical cores +// [Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4250.0 +// Job-JIATTD : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4250.0 + +//IterationCount=3 IterationTime=100.0000 ms LaunchCount = 1 +//WarmupCount=3 + +// Method | Mean | Error | StdDev | Ratio | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op | +//---------------------- |----------:|-----------:|----------:|------:|------------:|------------:|------------:|--------------------:| +// DictionaryGetAll | 11.199 ms | 1.8170 ms | 0.0996 ms | 0.14 | 1888.8889 | - | - | 5868.59 KB | +// XmlGetAll | 62.963 ms | 24.0615 ms | 1.3189 ms | 0.81 | 13500.0000 | - | - | 42448.71 KB | +// DictionaryLocalize | 9.757 ms | 1.6966 ms | 0.0930 ms | 0.13 | 1100.0000 | - | - | 3677.65 KB | +// XmlLocalize | 77.725 ms | 14.6069 ms | 0.8007 ms | 1.00 | 14000.0000 | - | - | 43032.8 KB | +// OptimizedXmlLocalize | 2.402 ms | 0.4256 ms | 0.0233 ms | 0.03 | 187.5000 | - | - | 626.01 KB | +// OptimizedDictLocalize | 2.345 ms | 0.2411 ms | 0.0132 ms | 0.03 | 187.5000 | - | - | 626.01 KB | + +//// * Warnings * +//MinIterationTime +// LocalizedTextServiceGetAllStoredValuesBenchmarks.DictionaryGetAll: IterationCount= 3, IterationTime= 100.0000 ms, LaunchCount= 1, WarmupCount= 3->MinIterationTime = 99.7816 ms which is very small. It's recommended to increase it. +// LocalizedTextServiceGetAllStoredValuesBenchmarks.DictionaryLocalize: IterationCount= 3, IterationTime= 100.0000 ms, LaunchCount= 1, WarmupCount= 3->MinIterationTime = 96.7415 ms which is very small. It's recommended to increase it. +// LocalizedTextServiceGetAllStoredValuesBenchmarks.XmlLocalize: IterationCount= 3, IterationTime= 100.0000 ms, LaunchCount= 1, WarmupCount= 3->MinIterationTime = 76.8151 ms which is very small. It's recommended to increase it. + +//// * Legends * +// Mean : Arithmetic mean of all measurements +// Error : Half of 99.9% confidence interval +// StdDev : Standard deviation of all measurements +// Ratio : Mean of the ratio distribution ([Current]/[Baseline]) +// Gen 0/1k Op : GC Generation 0 collects per 1k Operations +// Gen 1/1k Op : GC Generation 1 collects per 1k Operations +// Gen 2/1k Op : GC Generation 2 collects per 1k Operations +// Allocated Memory/Op : Allocated memory per single operation(managed only, inclusive, 1KB = 1024B) +// 1 ms : 1 Millisecond(0.001 sec) + +//// * Diagnostic Output - MemoryDiagnoser * + + +// // ***** BenchmarkRunner: End ***** +// Run time: 00:00:09 (9.15 sec), executed benchmarks: 6 +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 48d69cf757..4bb7faa22d 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -54,6 +54,7 @@ + diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index 6a4054d5ae..eebc3a0e8b 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -255,8 +255,8 @@ namespace Umbraco.Tests.Models.Mapping } Assert.AreEqual(contentType.CompositionPropertyGroups.Count(), invariantContent.Tabs.Count() - 1); - Assert.IsTrue(invariantContent.Tabs.Any(x => x.Label == Current.Services.TextService.Localize("general/properties"))); - Assert.AreEqual(2, invariantContent.Tabs.Where(x => x.Label == Current.Services.TextService.Localize("general/properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count()); + Assert.IsTrue(invariantContent.Tabs.Any(x => x.Label == Current.Services.TextService.Localize("general", "properties"))); + Assert.AreEqual(2, invariantContent.Tabs.Where(x => x.Label == Current.Services.TextService.Localize("general", "properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count()); } #region Assertions diff --git a/src/Umbraco.Web/Compose/NotificationsComponent.cs b/src/Umbraco.Web/Compose/NotificationsComponent.cs index ea5df65f74..42984cef16 100644 --- a/src/Umbraco.Web/Compose/NotificationsComponent.cs +++ b/src/Umbraco.Web/Compose/NotificationsComponent.cs @@ -257,12 +257,12 @@ namespace Umbraco.Web.Compose siteUri, ((IUser user, NotificationEmailSubjectParams subject) x) => _textService.Localize( - "notifications/mailSubject", + "notifications", "mailSubject", x.user.GetUserCulture(_textService, _globalSettings), new[] { x.subject.SiteUrl, x.subject.Action, x.subject.ItemName }), ((IUser user, NotificationEmailBodyParams body, bool isHtml) x) - => _textService.Localize( - x.isHtml ? "notifications/mailBodyHtml" : "notifications/mailBody", + => _textService.Localize("notifications", + x.isHtml ? "mailBodyHtml" : "mailBody", x.user.GetUserCulture(_textService, _globalSettings), new[] { @@ -274,7 +274,7 @@ namespace Umbraco.Web.Compose x.body.ItemId, //format the summary depending on if it's variant or not contentVariantGroup.Key == ContentVariation.Culture - ? (x.isHtml ? _textService.Localize("notifications/mailBodyVariantHtmlSummary", new[]{ x.body.Summary }) : _textService.Localize("notifications/mailBodyVariantSummary", new []{ x.body.Summary })) + ? (x.isHtml ? _textService.Localize("notifications","mailBodyVariantHtmlSummary", new[]{ x.body.Summary }) : _textService.Localize("notifications","mailBodyVariantSummary", new []{ x.body.Summary })) : x.body.Summary, x.body.ItemUrl })); diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index e2ab829427..d5f4ef9fe8 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -337,7 +337,7 @@ namespace Umbraco.Web.Editors new[] { identityUser.UserName, callbackUrl }); await UserManager.SendEmailAsync(identityUser.Id, - Services.TextService.Localize("login/resetPasswordEmailCopySubject", + Services.TextService.Localize("login", "resetPasswordEmailCopySubject", // Ensure the culture of the found user is used for the email! UserExtensions.GetUserCulture(identityUser.Culture, Services.TextService, GlobalSettings)), message); diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 18740d41fc..2959a983bd 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -347,7 +347,7 @@ namespace Umbraco.Web.Editors } //Add error and redirect for it to be displayed - TempData[ViewDataExtensions.TokenPasswordResetCode] = new[] { Services.TextService.Localize("login/resetCodeExpired") }; + TempData[ViewDataExtensions.TokenPasswordResetCode] = new[] { Services.TextService.Localize("login", "resetCodeExpired") }; return RedirectToLocal(Url.Action("Default", "BackOffice")); } diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 0e1c4b3e60..967d0d14c7 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -88,7 +88,7 @@ namespace Umbraco.Web.Editors if (string.IsNullOrWhiteSpace(parentId)) throw new ArgumentException("Value cannot be null or whitespace.", "parentId"); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name"); if (name.ContainsAny(Path.GetInvalidPathChars())) { - return Request.CreateNotificationValidationErrorResponse(Services.TextService.Localize("codefile/createFolderIllegalChars")); + return Request.CreateNotificationValidationErrorResponse(Services.TextService.Localize("codefile", "createFolderIllegalChars")); } // if the parentId is root (-1) then we just need an empty string as we are @@ -388,8 +388,8 @@ namespace Umbraco.Web.Editors } display.AddErrorNotification( - Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), - Services.TextService.Localize("speechBubbles/partialViewErrorText")); + Services.TextService.Localize("speechBubbles", "partialViewErrorHeader"), + Services.TextService.Localize("speechBubbles", "partialViewErrorText")); break; case Core.Constants.Trees.PartialViewMacros: @@ -403,8 +403,8 @@ namespace Umbraco.Web.Editors } display.AddErrorNotification( - Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), - Services.TextService.Localize("speechBubbles/partialViewErrorText")); + Services.TextService.Localize("speechBubbles", "partialViewErrorHeader"), + Services.TextService.Localize("speechBubbles", "partialViewErrorText")); break; case Core.Constants.Trees.Scripts: diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 6f85a08751..ed629d746a 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -242,7 +242,7 @@ namespace Umbraco.Web.Editors new ContentVariantDisplay { CreateDate = DateTime.Now, - Name = Services.TextService.Localize("general/recycleBin") + Name = Services.TextService.Localize("general","recycleBin") } }, ContentApps = apps @@ -567,8 +567,8 @@ namespace Umbraco.Web.Editors var notificationModel = new SimpleNotificationModel(); notificationModel.AddSuccessNotification( - Services.TextService.Localize("blueprints/createdBlueprintHeading"), - Services.TextService.Localize("blueprints/createdBlueprintMessage", new[] { content.Name }) + Services.TextService.Localize("blueprints", "createdBlueprintHeading"), + Services.TextService.Localize("blueprints", "createdBlueprintMessage", new[] { content.Name }) ); return notificationModel; @@ -579,7 +579,7 @@ namespace Umbraco.Web.Editors var existing = Services.ContentService.GetBlueprintsForContentTypes(content.ContentTypeId); if (existing.Any(x => x.Name == name && x.Id != content.Id)) { - ModelState.AddModelError(modelName, Services.TextService.Localize("blueprints/duplicateBlueprintMessage")); + ModelState.AddModelError(modelName, Services.TextService.Localize("blueprints", "duplicateBlueprintMessage")); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); } } @@ -741,15 +741,15 @@ namespace Umbraco.Web.Editors var variantName = GetVariantName(culture, segment); AddSuccessNotification(notifications, culture, segment, - Services.TextService.Localize("speechBubbles/editContentSendToPublish"), - Services.TextService.Localize("speechBubbles/editVariantSendToPublishText", new[] { variantName })); + Services.TextService.Localize("speechBubbles", "editContentSendToPublish"), + Services.TextService.Localize("speechBubbles", "editVariantSendToPublishText", new[] { variantName })); } } else if (ModelState.IsValid) { globalNotifications.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/editContentSendToPublish"), - Services.TextService.Localize("speechBubbles/editContentSendToPublishText")); + Services.TextService.Localize("speechBubbles", "editContentSendToPublish"), + Services.TextService.Localize("speechBubbles", "editContentSendToPublishText")); } } break; @@ -766,8 +766,8 @@ namespace Umbraco.Web.Editors if (!ValidatePublishBranchPermissions(contentItem, out var noAccess)) { globalNotifications.AddErrorNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/invalidPublishBranchPermissions")); + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "invalidPublishBranchPermissions")); wasCancelled = false; break; } @@ -782,8 +782,8 @@ namespace Umbraco.Web.Editors if (!ValidatePublishBranchPermissions(contentItem, out var noAccess)) { globalNotifications.AddErrorNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/invalidPublishBranchPermissions")); + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "invalidPublishBranchPermissions")); wasCancelled = false; break; } @@ -873,7 +873,7 @@ namespace Umbraco.Web.Editors //if there's more than 1 variant, then we need to add the culture specific error //messages based on the variants in error so that the messages show in the publish/save dialog if (variants.Count > 1) - AddVariantValidationError(variant.Culture, variant.Segment, "publish/contentPublishedFailedByMissingName"); + AddVariantValidationError(variant.Culture, variant.Segment, "publish","contentPublishedFailedByMissingName"); else return false; //It's invariant and is missing critical data, it cannot be saved } @@ -911,14 +911,14 @@ namespace Umbraco.Web.Editors /// /// /// - /// + /// /// /// /// Method is used for normal Saving and Scheduled Publishing /// private void SaveAndNotify(ContentItemSave contentItem, Func saveMethod, int variantCount, Dictionary notifications, SimpleNotificationModel globalNotifications, - string invariantSavedLocalizationKey, string variantSavedLocalizationKey, string cultureForInvariantErrors, + string invariantSavedLocalizationKey, string variantSavedLocalizationAlias, string cultureForInvariantErrors, out bool wasCancelled) { var saveResult = saveMethod(contentItem.PersistedContent); @@ -938,14 +938,14 @@ namespace Umbraco.Web.Editors var variantName = GetVariantName(culture, segment); AddSuccessNotification(notifications, culture, segment, - Services.TextService.Localize("speechBubbles/editContentSavedHeader"), - Services.TextService.Localize(variantSavedLocalizationKey, new[] { variantName })); + Services.TextService.Localize("speechBubbles", "editContentSavedHeader"), + Services.TextService.Localize(null,variantSavedLocalizationAlias, new[] { variantName })); } } else if (ModelState.IsValid) { globalNotifications.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/editContentSavedHeader"), + Services.TextService.Localize("speechBubbles", "editContentSavedHeader"), Services.TextService.Localize(invariantSavedLocalizationKey)); } } @@ -1073,7 +1073,7 @@ namespace Umbraco.Web.Editors { //can't continue, a mandatory variant is not published and not scheduled for publishing // TODO: Add segment - AddVariantValidationError(culture, null, "speechBubbles/scheduleErrReleaseDate2"); + AddVariantValidationError(culture, null, "speechBubbles", "scheduleErrReleaseDate2"); isValid = false; continue; } @@ -1081,7 +1081,7 @@ namespace Umbraco.Web.Editors { //can't continue, a mandatory variant is not published and it's scheduled for publishing after a non-mandatory // TODO: Add segment - AddVariantValidationError(culture, null, "speechBubbles/scheduleErrReleaseDate3"); + AddVariantValidationError(culture, null, "speechBubbles", "scheduleErrReleaseDate3"); isValid = false; continue; } @@ -1095,7 +1095,7 @@ namespace Umbraco.Web.Editors //1) release date cannot be less than now if (variant.ReleaseDate.HasValue && variant.ReleaseDate < DateTime.Now) { - AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles/scheduleErrReleaseDate1"); + AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles", "scheduleErrReleaseDate1"); isValid = false; continue; } @@ -1103,7 +1103,7 @@ namespace Umbraco.Web.Editors //2) expire date cannot be less than now if (variant.ExpireDate.HasValue && variant.ExpireDate < DateTime.Now) { - AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles/scheduleErrExpireDate1"); + AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles", "scheduleErrExpireDate1"); isValid = false; continue; } @@ -1111,7 +1111,7 @@ namespace Umbraco.Web.Editors //3) expire date cannot be less than release date if (variant.ExpireDate.HasValue && variant.ReleaseDate.HasValue && variant.ExpireDate <= variant.ReleaseDate) { - AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles/scheduleErrExpireDate2"); + AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles", "scheduleErrExpireDate2"); isValid = false; continue; } @@ -1360,19 +1360,19 @@ namespace Umbraco.Web.Editors if (r.publishing && !r.isValid) { //flagged for publishing but the mandatory culture is invalid - AddVariantValidationError(r.model.Culture, r.model.Segment, "publish/contentPublishedFailedReqCultureValidationError"); + AddVariantValidationError(r.model.Culture, r.model.Segment, "publish", "contentPublishedFailedReqCultureValidationError"); canPublish = false; } else if (r.publishing && r.isValid && firstInvalidMandatoryCulture != null) { //in this case this culture also cannot be published because another mandatory culture is invalid - AddVariantValidationError(r.model.Culture, r.model.Segment, "publish/contentPublishedFailedReqCultureValidationError", firstInvalidMandatoryCulture); + AddVariantValidationError(r.model.Culture, r.model.Segment, "publish", "contentPublishedFailedReqCultureValidationError", firstInvalidMandatoryCulture); canPublish = false; } else if (!r.publishing) { //cannot continue publishing since a required culture that is not currently being published isn't published - AddVariantValidationError(r.model.Culture, r.model.Segment, "speechBubbles/contentReqCulturePublishError"); + AddVariantValidationError(r.model.Culture, r.model.Segment, "speechBubbles", "contentReqCulturePublishError"); canPublish = false; } } @@ -1397,7 +1397,7 @@ namespace Umbraco.Web.Editors var valid = persistentContent.PublishCulture(CultureImpact.Explicit(variant.Culture, defaultCulture.InvariantEquals(variant.Culture))); if (!valid) { - AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles/contentCultureValidationError"); + AddVariantValidationError(variant.Culture, variant.Segment, "speechBubbles", "contentCultureValidationError"); return false; } } @@ -1414,12 +1414,12 @@ namespace Umbraco.Web.Editors /// /// The culture used in the localization message, null by default which means will be used. /// - private void AddVariantValidationError(string culture, string segment, string localizationKey, string cultureToken = null) + private void AddVariantValidationError(string culture, string segment, string localizationArea,string localizationAlias, string cultureToken = null) { var cultureToUse = cultureToken ?? culture; var variantName = GetVariantName(cultureToUse, segment); - var errMsg = Services.TextService.Localize(localizationKey, new[] { variantName }); + var errMsg = Services.TextService.Localize(localizationArea, localizationAlias, new[] { variantName }); ModelState.AddVariantValidationError(culture, segment, errMsg); } @@ -1554,7 +1554,7 @@ namespace Umbraco.Web.Editors { Services.ContentService.EmptyRecycleBin(Security.GetUserId().ResultOr(Constants.Security.SuperUserId)); - return Request.CreateNotificationSuccessResponse(Services.TextService.Localize("defaultdialogs/recycleBinIsEmpty")); + return Request.CreateNotificationSuccessResponse(Services.TextService.Localize("defaultdialogs", "recycleBinIsEmpty")); } /// @@ -1662,8 +1662,8 @@ namespace Umbraco.Web.Editors else { content.AddSuccessNotification( - Services.TextService.Localize("content/unpublish"), - Services.TextService.Localize("speechBubbles/contentUnpublished")); + Services.TextService.Localize("content", "unpublish"), + Services.TextService.Localize("speechBubbles", "contentUnpublished")); return content; } } @@ -1688,8 +1688,8 @@ namespace Umbraco.Web.Editors if (results.Any(x => x.Value.Result == PublishResultType.SuccessUnpublishMandatoryCulture)) { content.AddSuccessNotification( - Services.TextService.Localize("content/unpublish"), - Services.TextService.Localize("speechBubbles/contentMandatoryCultureUnpublished")); + Services.TextService.Localize("content", "unpublish"), + Services.TextService.Localize("speechBubbles", "contentMandatoryCultureUnpublished")); return content; } @@ -1697,8 +1697,8 @@ namespace Umbraco.Web.Editors foreach (var r in results) { content.AddSuccessNotification( - Services.TextService.Localize("content/unpublish"), - Services.TextService.Localize("speechBubbles/contentCultureUnpublished", new[] { _allLangs.Value[r.Key].CultureName })); + Services.TextService.Localize("conten", "unpublish"), + Services.TextService.Localize("speechBubbles", "contentCultureUnpublished", new[] { _allLangs.Value[r.Key].CultureName })); } return content; @@ -1729,7 +1729,7 @@ namespace Umbraco.Web.Editors } catch (UriFormatException) { - var response = Request.CreateValidationErrorResponse(Services.TextService.Localize("assignDomain/invalidDomain")); + var response = Request.CreateValidationErrorResponse(Services.TextService.Localize("assignDomain", "invalidDomain")); throw new HttpResponseException(response); } } @@ -1887,7 +1887,7 @@ namespace Umbraco.Web.Editors foreach (var (culture, segment) in variantErrors) { - AddVariantValidationError(culture, segment, "speechBubbles/contentCultureValidationError"); + AddVariantValidationError(culture, segment, "speechBubbles", "contentCultureValidationError"); } } @@ -2012,7 +2012,7 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse( - Services.TextService.Localize("moveOrCopy/notAllowedAtRoot"))); + Services.TextService.Localize("moveOrCopy", "notAllowedAtRoot"))); } } else @@ -2030,7 +2030,7 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse( - Services.TextService.Localize("moveOrCopy/notAllowedByContentType"))); + Services.TextService.Localize("moveOrCopy", "notAllowedByContentType"))); } // Check on paths @@ -2038,7 +2038,7 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse( - Services.TextService.Localize("moveOrCopy/notAllowedByPath"))); + Services.TextService.Localize("moveOrCopy", "notAllowedByPath"))); } } @@ -2107,16 +2107,16 @@ namespace Umbraco.Web.Editors { //either invariant single publish, or bulk publish where all statuses are already published display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/editContentPublishedHeader"), - Services.TextService.Localize("speechBubbles/editContentPublishedText")); + Services.TextService.Localize("speechBubbles", "editContentPublishedHeader"), + Services.TextService.Localize("speechBubbles", "editContentPublishedText")); } else { foreach (var c in successfulCultures) { display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/editContentPublishedHeader"), - Services.TextService.Localize("speechBubbles/editVariantPublishedText", new[] { _allLangs.Value[c].CultureName })); + Services.TextService.Localize("speechBubbles", "editContentPublishedHeader"), + Services.TextService.Localize("speechBubbles", "editVariantPublishedText", new[] { _allLangs.Value[c].CultureName })); } } } @@ -2132,20 +2132,20 @@ namespace Umbraco.Web.Editors if (successfulCultures == null) { display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/editContentPublishedHeader"), + Services.TextService.Localize("speechBubbles", "editContentPublishedHeader"), totalStatusCount > 1 - ? Services.TextService.Localize("speechBubbles/editMultiContentPublishedText", new[] { itemCount.ToInvariantString() }) - : Services.TextService.Localize("speechBubbles/editContentPublishedText")); + ? Services.TextService.Localize("speechBubbles", "editMultiContentPublishedText", new[] { itemCount.ToInvariantString() }) + : Services.TextService.Localize("speechBubbles", "editContentPublishedText")); } else { foreach (var c in successfulCultures) { display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/editContentPublishedHeader"), + Services.TextService.Localize("speechBubbles", "editContentPublishedHeader"), totalStatusCount > 1 - ? Services.TextService.Localize("speechBubbles/editMultiVariantPublishedText", new[] { itemCount.ToInvariantString(), _allLangs.Value[c].CultureName }) - : Services.TextService.Localize("speechBubbles/editVariantPublishedText", new[] { _allLangs.Value[c].CultureName })); + ? Services.TextService.Localize("speechBubbles", "editMultiVariantPublishedText", new[] { itemCount.ToInvariantString(), _allLangs.Value[c].CultureName }) + : Services.TextService.Localize("speechBubbles", "editVariantPublishedText", new[] { _allLangs.Value[c].CultureName })); } } } @@ -2155,8 +2155,8 @@ namespace Umbraco.Web.Editors //TODO: This doesn't take into account variations with the successfulCultures param var names = string.Join(", ", status.Select(x => $"'{x.Content.Name}'")); display.AddWarningNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/contentPublishedFailedByParent", + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "contentPublishedFailedByParent", new[] { names }).Trim()); } break; @@ -2172,8 +2172,8 @@ namespace Umbraco.Web.Editors //TODO: This doesn't take into account variations with the successfulCultures param var names = string.Join(", ", status.Select(x => $"'{x.Content.Name}'")); display.AddWarningNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/contentPublishedFailedAwaitingRelease", + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "contentPublishedFailedAwaitingRelease", new[] { names }).Trim()); } break; @@ -2182,8 +2182,8 @@ namespace Umbraco.Web.Editors //TODO: This doesn't take into account variations with the successfulCultures param var names = string.Join(", ", status.Select(x => $"'{x.Content.Name}'")); display.AddWarningNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/contentPublishedFailedExpired", + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "contentPublishedFailedExpired", new[] { names }).Trim()); } break; @@ -2192,8 +2192,8 @@ namespace Umbraco.Web.Editors //TODO: This doesn't take into account variations with the successfulCultures param var names = string.Join(", ", status.Select(x => $"'{x.Content.Name}'")); display.AddWarningNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/contentPublishedFailedIsTrashed", + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "contentPublishedFailedIsTrashed", new[] { names }).Trim()); } break; @@ -2203,8 +2203,8 @@ namespace Umbraco.Web.Editors { var names = string.Join(", ", status.Select(x => $"'{x.Content.Name}'")); display.AddWarningNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/contentPublishedFailedInvalid", + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "contentPublishedFailedInvalid", new[] { names }).Trim()); } else @@ -2213,8 +2213,8 @@ namespace Umbraco.Web.Editors { var names = string.Join(", ", status.Select(x => $"'{(x.Content.ContentType.VariesByCulture() ? x.Content.GetCultureName(c) : x.Content.Name)}'")); display.AddWarningNotification( - Services.TextService.Localize("publish"), - Services.TextService.Localize("publish/contentPublishedFailedInvalid", + Services.TextService.Localize(null,"publish"), + Services.TextService.Localize("publish", "contentPublishedFailedInvalid", new[] { names }).Trim()); } } @@ -2222,7 +2222,7 @@ namespace Umbraco.Web.Editors break; case PublishResultType.FailedPublishMandatoryCultureMissing: display.AddWarningNotification( - Services.TextService.Localize("publish"), + Services.TextService.Localize(null,"publish"), "publish/contentPublishedFailedByCulture"); break; default: @@ -2355,13 +2355,13 @@ namespace Umbraco.Web.Editors case OperationResultType.NoOperation: default: notificationModel.AddErrorNotification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), + Services.TextService.Localize("speechBubbles", "operationFailedHeader"), null); // TODO: There is no specific failed to save error message AFAIK break; case OperationResultType.FailedCancelledByEvent: notificationModel.AddErrorNotification( - Services.TextService.Localize("speechBubbles/operationCancelledHeader"), - Services.TextService.Localize("speechBubbles/operationCancelledText")); + Services.TextService.Localize("speechBubbles", "operationCancelledHeader"), + Services.TextService.Localize("speechBubbles", "operationCancelledText")); break; } diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index d426cb1f56..89f0b99d7f 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -384,7 +384,7 @@ namespace Umbraco.Web.Editors var display = Mapper.Map(savedCt); display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/contentTypeSavedHeader"), + Services.TextService.Localize("speechBubbles", "contentTypeSavedHeader"), string.Empty); return display; @@ -666,8 +666,8 @@ namespace Umbraco.Web.Editors else { model.Notifications.Add(new Notification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), - Services.TextService.Localize("media/disallowedFileType"), + Services.TextService.Localize("speechBubbles", "operationFailedHeader"), + Services.TextService.Localize("media", "disallowedFileType"), NotificationStyle.Warning)); } diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index d56820a549..8eefbb616b 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -256,7 +256,7 @@ namespace Umbraco.Web.Editors var exists = allAliases.InvariantContains(contentTypeSave.Alias); if (exists && (ctId == 0 || !ct.Alias.InvariantEquals(contentTypeSave.Alias))) { - ModelState.AddModelError("Alias", Services.TextService.Localize("editcontenttype/aliasAlreadyExists")); + ModelState.AddModelError("Alias", Services.TextService.Localize("editcontenttype", "aliasAlreadyExists")); } // execute the external validators @@ -388,7 +388,7 @@ namespace Umbraco.Web.Editors return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); case MoveOperationStatusType.FailedNotAllowedByPath: var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy", "notAllowedByPath"), ""); return Request.CreateValidationErrorResponse(notificationModel); default: throw new ArgumentOutOfRangeException(); @@ -432,7 +432,7 @@ namespace Umbraco.Web.Editors return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); case MoveOperationStatusType.FailedNotAllowedByPath: var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy", "notAllowedByPath"), ""); return Request.CreateValidationErrorResponse(notificationModel); default: throw new ArgumentOutOfRangeException(); diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index 46151f2a9e..8da2243255 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -183,7 +183,7 @@ namespace Umbraco.Web.Editors //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword); - result.AddSuccessNotification(Services.TextService.Localize("user/password"), Services.TextService.Localize("user/passwordChanged")); + result.AddSuccessNotification(Services.TextService.Localize("user", "password"), Services.TextService.Localize("user", "passwordChanged")); return result; } diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index ad92d40ecf..76b8d590f2 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -281,7 +281,7 @@ namespace Umbraco.Web.Editors // map back to display model, and return var display = Mapper.Map(dataType.PersistedDataType); - display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/dataTypeSaved"), ""); + display.AddSuccessNotification(Services.TextService.Localize("speechBubbles", "dataTypeSaved"), ""); return display; } @@ -316,7 +316,7 @@ namespace Umbraco.Web.Editors return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); case MoveOperationStatusType.FailedNotAllowedByPath: var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy", "notAllowedByPath"), ""); return Request.CreateValidationErrorResponse(notificationModel); default: throw new ArgumentOutOfRangeException(); diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index dfe6939552..758fb85898 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -106,7 +106,7 @@ namespace Umbraco.Web.Editors Id = Constants.System.RecycleBinMedia, Alias = "recycleBin", ParentId = -1, - Name = Services.TextService.Localize("general/recycleBin"), + Name = Services.TextService.Localize("general", "recycleBin"), ContentTypeAlias = "recycleBin", CreateDate = DateTime.Now, IsContainer = true, @@ -431,7 +431,7 @@ namespace Umbraco.Web.Editors if (sourceParentID == destinationParentID) { - return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new Notification("",Services.TextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error))); + return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new Notification("",Services.TextService.Localize("media", "moveToSameFolderFailed"),NotificationStyle.Error))); } if (moveResult == false) { @@ -519,8 +519,8 @@ namespace Umbraco.Web.Editors if (saveStatus.Success) { display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/editMediaSaved"), - Services.TextService.Localize("speechBubbles/editMediaSavedText")); + Services.TextService.Localize("speechBubbles", "editMediaSaved"), + Services.TextService.Localize("speechBubbles", "editMediaSavedText")); } else { @@ -551,7 +551,7 @@ namespace Umbraco.Web.Editors { Services.MediaService.EmptyRecycleBin(Security.GetUserId().ResultOr(Constants.Security.SuperUserId)); - return Request.CreateNotificationSuccessResponse(Services.TextService.Localize("defaultdialogs/recycleBinIsEmpty")); + return Request.CreateNotificationSuccessResponse(Services.TextService.Localize("defaultdialogs", "recycleBinIsEmpty")); } /// @@ -730,7 +730,7 @@ namespace Umbraco.Web.Editors if (saveResult == false) { AddCancelMessage(tempFiles, - message: Services.TextService.Localize("speechBubbles/operationCancelledText") + " -- " + mediaItemName); + message: Services.TextService.Localize("speechBubbles", "operationCancelledText") + " -- " + mediaItemName); } else { @@ -745,8 +745,8 @@ namespace Umbraco.Web.Editors else { tempFiles.Notifications.Add(new Notification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), - Services.TextService.Localize("media/disallowedFileType"), + Services.TextService.Localize("speechBubbles", "operationFailedHeader"), + Services.TextService.Localize("media", "disallowedFileType"), NotificationStyle.Warning)); } } @@ -840,8 +840,8 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateResponse( HttpStatusCode.Forbidden, new SimpleNotificationModel(new Notification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), - Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"), + Services.TextService.Localize("speechBubbles", "operationFailedHeader"), + Services.TextService.Localize("speechBubbles", "invalidUserPermissionsText"), NotificationStyle.Warning)))); } @@ -874,7 +874,7 @@ namespace Umbraco.Web.Editors if (toMove.ContentType.AllowedAsRoot == false && mediaTypeService.GetAll().Any(ct => ct.AllowedAsRoot)) { var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedAtRoot"), ""); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy", "notAllowedAtRoot"), ""); throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel)); } } @@ -892,7 +892,7 @@ namespace Umbraco.Web.Editors .Any(x => x.Value == toMove.ContentType.Id) == false) { var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByContentType"), ""); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy", "notAllowedByContentType"), ""); throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel)); } @@ -900,7 +900,7 @@ namespace Umbraco.Web.Editors if ((string.Format(",{0},", parent.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1) { var notificationModel = new SimpleNotificationModel(); - notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy", "notAllowedByPath"), ""); throw new HttpResponseException(Request.CreateValidationErrorResponse(notificationModel)); } } diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index d816f13c92..3b7c3fb4d9 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -250,7 +250,7 @@ namespace Umbraco.Web.Editors var display = Mapper.Map(savedCt); display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/mediaTypeSavedHeader"), + Services.TextService.Localize("speechBubbles", "mediaTypeSavedHeader"), string.Empty); return display; diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 477dced7d0..2f6fb621b0 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -377,7 +377,7 @@ namespace Umbraco.Web.Editors { case ContentSaveAction.Save: case ContentSaveAction.SaveNew: - display.AddSuccessNotification(localizedTextService.Localize("speechBubbles/editMemberSaved"), localizedTextService.Localize("speechBubbles/editMemberSaved")); + display.AddSuccessNotification(localizedTextService.Localize("speechBubbles", "editMemberSaved"), localizedTextService.Localize("speechBubbles", "editMemberSaved")); break; } diff --git a/src/Umbraco.Web/Editors/MemberGroupController.cs b/src/Umbraco.Web/Editors/MemberGroupController.cs index ed11139e56..19adf17a2a 100644 --- a/src/Umbraco.Web/Editors/MemberGroupController.cs +++ b/src/Umbraco.Web/Editors/MemberGroupController.cs @@ -152,7 +152,7 @@ namespace Umbraco.Web.Editors var display = Mapper.Map(memberGroup); display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/memberGroupSavedHeader"), + Services.TextService.Localize("speechBubbles", "memberGroupSavedHeader"), string.Empty); return display; diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs index 4bfea76eda..d55afd5e95 100644 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ b/src/Umbraco.Web/Editors/MemberTypeController.cs @@ -231,7 +231,7 @@ namespace Umbraco.Web.Editors var display = Mapper.Map(savedCt); display.AddSuccessNotification( - Services.TextService.Localize("speechBubbles/memberTypeSavedHeader"), + Services.TextService.Localize("speechBubbles", "memberTypeSavedHeader"), string.Empty); return display; diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 1030498734..d12308aeee 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -180,7 +180,7 @@ namespace Umbraco.Web.Editors { //this package is already installed throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( - Services.TextService.Localize("packager/packageAlreadyInstalled"))); + Services.TextService.Localize("packager", "packageAlreadyInstalled"))); } model.OriginalVersion = installType == PackageInstallType.Upgrade ? alreadyInstalled.Version : null; @@ -189,8 +189,8 @@ namespace Umbraco.Web.Editors else { model.Notifications.Add(new Notification( - Services.TextService.Localize("speechBubbles/operationFailedHeader"), - Services.TextService.Localize("media/disallowedFileType"), + Services.TextService.Localize("speechBubbles", "operationFailedHeader"), + Services.TextService.Localize("media", "disallowedFileType"), NotificationStyle.Warning)); } @@ -234,7 +234,7 @@ namespace Umbraco.Web.Editors if (installType == PackageInstallType.AlreadyInstalled) { throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( - Services.TextService.Localize("packager/packageAlreadyInstalled"))); + Services.TextService.Localize("packager", "packageAlreadyInstalled"))); } model.OriginalVersion = installType == PackageInstallType.Upgrade ? alreadyInstalled.Version : null; @@ -260,7 +260,7 @@ namespace Umbraco.Web.Editors var packageMinVersion = packageInfo.UmbracoVersion; if (UmbracoVersion.Current < packageMinVersion) throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( - Services.TextService.Localize("packager/targetVersionMismatch", new[] {packageMinVersion.ToString()}))); + Services.TextService.Localize("packager", "targetVersionMismatch", new[] {packageMinVersion.ToString()}))); } var installType = Services.PackagingService.GetPackageInstallType(packageInfo.Name, SemVersion.Parse(packageInfo.Version), out var alreadyInstalled); diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index 1f07267802..a3b19cc9fe 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -21,28 +21,28 @@ namespace Umbraco.Web.Editors { private IEnumerable Terms => new List { - new OperatorTerm(Services.TextService.Localize("template/is"), Operator.Equals, new [] {"string"}), - new OperatorTerm(Services.TextService.Localize("template/isNot"), Operator.NotEquals, new [] {"string"}), - new OperatorTerm(Services.TextService.Localize("template/before"), Operator.LessThan, new [] {"datetime"}), - new OperatorTerm(Services.TextService.Localize("template/beforeIncDate"), Operator.LessThanEqualTo, new [] {"datetime"}), - new OperatorTerm(Services.TextService.Localize("template/after"), Operator.GreaterThan, new [] {"datetime"}), - new OperatorTerm(Services.TextService.Localize("template/afterIncDate"), Operator.GreaterThanEqualTo, new [] {"datetime"}), - new OperatorTerm(Services.TextService.Localize("template/equals"), Operator.Equals, new [] {"int"}), - new OperatorTerm(Services.TextService.Localize("template/doesNotEqual"), Operator.NotEquals, new [] {"int"}), - new OperatorTerm(Services.TextService.Localize("template/contains"), Operator.Contains, new [] {"string"}), - new OperatorTerm(Services.TextService.Localize("template/doesNotContain"), Operator.NotContains, new [] {"string"}), - new OperatorTerm(Services.TextService.Localize("template/greaterThan"), Operator.GreaterThan, new [] {"int"}), - new OperatorTerm(Services.TextService.Localize("template/greaterThanEqual"), Operator.GreaterThanEqualTo, new [] {"int"}), - new OperatorTerm(Services.TextService.Localize("template/lessThan"), Operator.LessThan, new [] {"int"}), - new OperatorTerm(Services.TextService.Localize("template/lessThanEqual"), Operator.LessThanEqualTo, new [] {"int"}) + new OperatorTerm(Services.TextService.Localize("template","is"), Operator.Equals, new [] {"string"}), + new OperatorTerm(Services.TextService.Localize("template","isNot"), Operator.NotEquals, new [] {"string"}), + new OperatorTerm(Services.TextService.Localize("template","before"), Operator.LessThan, new [] {"datetime"}), + new OperatorTerm(Services.TextService.Localize("template","beforeIncDate"), Operator.LessThanEqualTo, new [] {"datetime"}), + new OperatorTerm(Services.TextService.Localize("template","after"), Operator.GreaterThan, new [] {"datetime"}), + new OperatorTerm(Services.TextService.Localize("template","afterIncDate"), Operator.GreaterThanEqualTo, new [] {"datetime"}), + new OperatorTerm(Services.TextService.Localize("template","equals"), Operator.Equals, new [] {"int"}), + new OperatorTerm(Services.TextService.Localize("template","doesNotEqual"), Operator.NotEquals, new [] {"int"}), + new OperatorTerm(Services.TextService.Localize("template","contains"), Operator.Contains, new [] {"string"}), + new OperatorTerm(Services.TextService.Localize("template","doesNotContain"), Operator.NotContains, new [] {"string"}), + new OperatorTerm(Services.TextService.Localize("template","greaterThan"), Operator.GreaterThan, new [] {"int"}), + new OperatorTerm(Services.TextService.Localize("template","greaterThanEqual"), Operator.GreaterThanEqualTo, new [] {"int"}), + new OperatorTerm(Services.TextService.Localize("template","lessThan"), Operator.LessThan, new [] {"int"}), + new OperatorTerm(Services.TextService.Localize("template","lessThanEqual"), Operator.LessThanEqualTo, new [] {"int"}) }; private IEnumerable Properties => new List { - new PropertyModel { Name = Services.TextService.Localize("template/id"), Alias = "Id", Type = "int" }, - new PropertyModel { Name = Services.TextService.Localize("template/name"), Alias = "Name", Type = "string" }, - new PropertyModel { Name = Services.TextService.Localize("template/createdDate"), Alias = "CreateDate", Type = "datetime" }, - new PropertyModel { Name = Services.TextService.Localize("template/lastUpdatedDate"), Alias = "UpdateDate", Type = "datetime" } + new PropertyModel { Name = Services.TextService.Localize("template","id"), Alias = "Id", Type = "int" }, + new PropertyModel { Name = Services.TextService.Localize("template","name"), Alias = "Name", Type = "string" }, + new PropertyModel { Name = Services.TextService.Localize("template","createdDate"), Alias = "CreateDate", Type = "datetime" }, + new PropertyModel { Name = Services.TextService.Localize("template","lastUpdatedDate"), Alias = "UpdateDate", Type = "datetime" } }; public QueryResultModel PostTemplateQuery(QueryModel model) @@ -206,10 +206,10 @@ namespace Umbraco.Web.Editors public IEnumerable GetContentTypes() { var contentTypes = Services.ContentTypeService.GetAll() - .Select(x => new ContentTypeModel { Alias = x.Alias, Name = Services.TextService.Localize("template/contentOfType", tokens: new string[] { x.Name }) }) + .Select(x => new ContentTypeModel { Alias = x.Alias, Name = Services.TextService.Localize("template", "contentOfType", tokens: new string[] { x.Name }) }) .OrderBy(x => x.Name).ToList(); - contentTypes.Insert(0, new ContentTypeModel { Alias = string.Empty, Name = Services.TextService.Localize("template/allContent") }); + contentTypes.Insert(0, new ContentTypeModel { Alias = string.Empty, Name = Services.TextService.Localize("template", "allContent") }); return contentTypes; } diff --git a/src/Umbraco.Web/Editors/UserGroupsController.cs b/src/Umbraco.Web/Editors/UserGroupsController.cs index b081ca6137..03e6c5b8b7 100644 --- a/src/Umbraco.Web/Editors/UserGroupsController.cs +++ b/src/Umbraco.Web/Editors/UserGroupsController.cs @@ -78,7 +78,7 @@ namespace Umbraco.Web.Editors var display = Mapper.Map(userGroupSave.PersistedUserGroup); - display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserGroupSaved")); + display.AddSuccessNotification(Services.TextService.Localize("speechBubbles", "operationSavedHeader"), Services.TextService.Localize("speechBubbles", "editUserGroupSaved")); return display; } @@ -163,9 +163,9 @@ namespace Umbraco.Web.Editors } if (userGroups.Length > 1) return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/deleteUserGroupsSuccess", new[] {userGroups.Length.ToString()})); + Services.TextService.Localize("speechBubbles", "deleteUserGroupsSuccess", new[] {userGroups.Length.ToString()})); return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/deleteUserGroupSuccess", new[] {userGroups[0].Name})); + Services.TextService.Localize("speechBubbles", "deleteUserGroupSuccess", new[] {userGroups[0].Name})); } } } diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index b022e6f27a..72162dd179 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -470,7 +470,7 @@ namespace Umbraco.Web.Editors await SendUserInviteEmailAsync(display, Security.CurrentUser.Name, Security.CurrentUser.Email, user, userSave.Message); } - display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/resendInviteHeader"), Services.TextService.Localize("speechBubbles/resendInviteSuccess", new[] { user.Name })); + display.AddSuccessNotification(Services.TextService.Localize("speechBubbles", "resendInviteHeader"), Services.TextService.Localize("speechBubbles", "resendInviteSuccess", new[] { user.Name })); return display; } @@ -529,10 +529,10 @@ namespace Umbraco.Web.Editors var applicationUri = RuntimeState.ApplicationUrl; var inviteUri = new Uri(applicationUri, action); - var emailSubject = Services.TextService.Localize("user/inviteEmailCopySubject", + var emailSubject = Services.TextService.Localize("user", "inviteEmailCopySubject", //Ensure the culture of the found user is used for the email! UserExtensions.GetUserCulture(to.Language, Services.TextService, GlobalSettings)); - var emailBody = Services.TextService.Localize("user/inviteEmailCopyFormat", + var emailBody = Services.TextService.Localize("user", "inviteEmailCopyFormat", //Ensure the culture of the found user is used for the email! UserExtensions.GetUserCulture(to.Language, Services.TextService, GlobalSettings), new[] { userDisplay.Name, from, message, inviteUri.ToString(), fromEmail }); @@ -678,7 +678,7 @@ namespace Umbraco.Web.Editors if (passwordChangeResult.Success) { var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword); - result.AddSuccessNotification(Services.TextService.Localize("general/success"), Services.TextService.Localize("user/passwordChangedGeneric")); + result.AddSuccessNotification(Services.TextService.Localize("general", "success"), Services.TextService.Localize("user", "passwordChangedGeneric")); return result; } @@ -716,11 +716,11 @@ namespace Umbraco.Web.Editors if (users.Length > 1) { return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/disableUsersSuccess", new[] {userIds.Length.ToString()})); + Services.TextService.Localize("speechBubbles", "disableUsersSuccess", new[] {userIds.Length.ToString()})); } return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/disableUserSuccess", new[] { users[0].Name })); + Services.TextService.Localize("speechBubbles", "disableUserSuccess", new[] { users[0].Name })); } /// @@ -740,11 +740,11 @@ namespace Umbraco.Web.Editors if (users.Length > 1) { return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/enableUsersSuccess", new[] { userIds.Length.ToString() })); + Services.TextService.Localize("speechBubbles", "enableUsersSuccess", new[] { userIds.Length.ToString() })); } return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/enableUserSuccess", new[] { users[0].Name })); + Services.TextService.Localize("speechBubbles", "enableUserSuccess", new[] { users[0].Name })); } /// @@ -767,7 +767,7 @@ namespace Umbraco.Web.Editors } var user = await UserManager.FindByIdAsync(userIds[0]); return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/unlockUserSuccess", new[] { user.Name })); + Services.TextService.Localize("speechBubbles", "unlockUserSuccess", new[] { user.Name })); } foreach (var u in userIds) @@ -781,7 +781,7 @@ namespace Umbraco.Web.Editors } return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/unlockUsersSuccess", new[] { userIds.Length.ToString() })); + Services.TextService.Localize("speechBubbles", "unlockUsersSuccess", new[] { userIds.Length.ToString() })); } [AdminUsersAuthorize("userIds")] @@ -799,7 +799,7 @@ namespace Umbraco.Web.Editors } Services.UserService.Save(users); return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/setUserGroupOnUsersSuccess")); + Services.TextService.Localize("speechBubbles", "setUserGroupOnUsersSuccess")); } /// @@ -830,7 +830,7 @@ namespace Umbraco.Web.Editors Services.UserService.Delete(user, true); return Request.CreateNotificationSuccessResponse( - Services.TextService.Localize("speechBubbles/deleteUserSuccess", new[] { userName })); + Services.TextService.Localize("speechBubbles", "deleteUserSuccess", new[] { userName })); } public class PagedUserResult : PagedResult diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/AbstractConfigCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/AbstractConfigCheck.cs index 01863dac0c..f9e4c85f31 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/AbstractConfigCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/AbstractConfigCheck.cs @@ -74,7 +74,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/checkSuccessMessage", + return TextService.Localize("healthcheck", "checkSuccessMessage", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath }); } } @@ -87,9 +87,9 @@ namespace Umbraco.Web.HealthCheck.Checks.Config get { return ValueComparisonType == ValueComparisonType.ShouldEqual - ? TextService.Localize("healthcheck/checkErrorMessageDifferentExpectedValue", + ? TextService.Localize("healthcheck", "checkErrorMessageDifferentExpectedValue", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath }) - : TextService.Localize("healthcheck/checkErrorMessageUnexpectedValue", + : TextService.Localize("healthcheck", "checkErrorMessageUnexpectedValue", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath }); } } @@ -105,7 +105,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config var rectifiedValue = recommendedValue != null ? recommendedValue.Value : ProvidedValue; - return TextService.Localize("healthcheck/rectifySuccessMessage", + return TextService.Localize("healthcheck", "rectifySuccessMessage", new[] { CurrentValue, @@ -156,7 +156,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config // Declare the action for rectifying the config value var rectifyAction = new HealthCheckAction("rectify", Id) { - Name = TextService.Localize("healthcheck/rectifyButton"), + Name = TextService.Localize("healthcheck", "rectifyButton"), ValueRequired = CanRectifyWithValue, }; @@ -178,7 +178,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config public virtual HealthCheckStatus Rectify() { if (ValueComparisonType == ValueComparisonType.ShouldNotEqual) - throw new InvalidOperationException(TextService.Localize("healthcheck/cannotRectifyShouldNotEqual")); + throw new InvalidOperationException(TextService.Localize("healthcheck", "cannotRectifyShouldNotEqual")); var recommendedValue = Values.First(v => v.IsRecommended).Value; return UpdateConfigurationValue(recommendedValue); @@ -192,10 +192,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Config public virtual HealthCheckStatus Rectify(string value) { if (ValueComparisonType == ValueComparisonType.ShouldEqual) - throw new InvalidOperationException(TextService.Localize("healthcheck/cannotRectifyShouldEqualWithValue")); + throw new InvalidOperationException(TextService.Localize("healthcheck", "cannotRectifyShouldEqualWithValue")); if (string.IsNullOrWhiteSpace(value)) - throw new InvalidOperationException(TextService.Localize("healthcheck/valueToRectifyNotProvided")); + throw new InvalidOperationException(TextService.Localize("healthcheck", "valueToRectifyNotProvided")); // Need to track provided value in order to correctly put together the rectify message ProvidedValue = value; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/CompilationDebugCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/CompilationDebugCheck.cs index 951609f91f..959a0cdbae 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/CompilationDebugCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/CompilationDebugCheck.cs @@ -25,10 +25,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Config new AcceptableConfiguration { IsRecommended = true, Value = bool.FalseString.ToLower() } }; - public override string CheckSuccessMessage => TextService.Localize("healthcheck/compilationDebugCheckSuccessMessage"); + public override string CheckSuccessMessage => TextService.Localize("healthcheck", "compilationDebugCheckSuccessMessage"); - public override string CheckErrorMessage => TextService.Localize("healthcheck/compilationDebugCheckErrorMessage"); + public override string CheckErrorMessage => TextService.Localize("healthcheck", "compilationDebugCheckErrorMessage"); - public override string RectifySuccessMessage => TextService.Localize("healthcheck/compilationDebugCheckRectifySuccessMessage"); + public override string RectifySuccessMessage => TextService.Localize("healthcheck", "compilationDebugCheckRectifySuccessMessage"); } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationService.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationService.cs index 95b458b142..8801ab0030 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationService.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/ConfigurationService.cs @@ -36,7 +36,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config return new ConfigurationServiceResult { Success = false, - Result = _textService.Localize("healthcheck/configurationServiceFileNotFound", new[] { _configFilePath }) + Result = _textService.Localize("healthcheck", "configurationServiceFileNotFound", new[] { _configFilePath }) }; var xmlDocument = new XmlDocument(); @@ -47,7 +47,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config return new ConfigurationServiceResult { Success = false, - Result = _textService.Localize("healthcheck/configurationServiceNodeNotFound", new[] { _xPath, _configFilePath }) + Result = _textService.Localize("healthcheck", "configurationServiceNodeNotFound", new[] { _xPath, _configFilePath }) }; return new ConfigurationServiceResult @@ -62,7 +62,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config return new ConfigurationServiceResult { Success = false, - Result = _textService.Localize("healthcheck/configurationServiceError", new[] { ex.Message }) + Result = _textService.Localize("healthcheck", "configurationServiceError", new[] { ex.Message }) }; } } @@ -80,7 +80,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config return new ConfigurationServiceResult { Success = false, - Result = _textService.Localize("healthcheck/configurationServiceFileNotFound", new[] { _configFilePath }) + Result = _textService.Localize("healthcheck", "configurationServiceFileNotFound", new[] { _configFilePath }) }; var xmlDocument = new XmlDocument { PreserveWhitespace = true }; @@ -91,7 +91,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config return new ConfigurationServiceResult { Success = false, - Result = _textService.Localize("healthcheck/configurationServiceNodeNotFound", new[] { _xPath, _configFilePath }) + Result = _textService.Localize("healthcheck", "configurationServiceNodeNotFound", new[] { _xPath, _configFilePath }) }; if (node.NodeType == XmlNodeType.Element) @@ -108,7 +108,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config return new ConfigurationServiceResult { Success = false, - Result = _textService.Localize("healthcheck/configurationServiceError", new[] { ex.Message }) + Result = _textService.Localize("healthcheck", "configurationServiceError", new[] { ex.Message }) }; } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/CustomErrorsCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/CustomErrorsCheck.cs index 63986b6c62..9b52e9b8e9 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/CustomErrorsCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/CustomErrorsCheck.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/customErrorsCheckSuccessMessage", new[] { CurrentValue }); + return TextService.Localize("healthcheck", "customErrorsCheckSuccessMessage", new[] { CurrentValue }); } } @@ -38,7 +38,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/customErrorsCheckErrorMessage", + return TextService.Localize("healthcheck", "customErrorsCheckErrorMessage", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value }); } } @@ -47,7 +47,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/customErrorsCheckRectifySuccessMessage", + return TextService.Localize("healthcheck", "customErrorsCheckRectifySuccessMessage", new[] { Values.First(v => v.IsRecommended).Value }); } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/MacroErrorsCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/MacroErrorsCheck.cs index 09c041998e..1e0283321f 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/MacroErrorsCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/MacroErrorsCheck.cs @@ -45,7 +45,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/macroErrorModeCheckSuccessMessage", + return TextService.Localize("healthcheck", "macroErrorModeCheckSuccessMessage", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value }); } } @@ -54,7 +54,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/macroErrorModeCheckErrorMessage", + return TextService.Localize("healthcheck", "macroErrorModeCheckErrorMessage", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value }); } } @@ -63,7 +63,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/macroErrorModeCheckRectifySuccessMessage", + return TextService.Localize("healthcheck", "macroErrorModeCheckRectifySuccessMessage", new[] { Values.First(v => v.IsRecommended).Value }); } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/NotificationEmailCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/NotificationEmailCheck.cs index 8c52a5686b..af9494ced7 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/NotificationEmailCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/NotificationEmailCheck.cs @@ -25,8 +25,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Config new AcceptableConfiguration { IsRecommended = false, Value = DefaultFromEmail } }; - public override string CheckSuccessMessage => TextService.Localize("healthcheck/notificationEmailsCheckSuccessMessage", new [] { CurrentValue } ); + public override string CheckSuccessMessage => TextService.Localize("healthcheck", "notificationEmailsCheckSuccessMessage", new [] { CurrentValue } ); - public override string CheckErrorMessage => TextService.Localize("healthcheck/notificationEmailsCheckErrorMessage", new[] { DefaultFromEmail }); + public override string CheckErrorMessage => TextService.Localize("healthcheck", "notificationEmailsCheckErrorMessage", new[] { DefaultFromEmail }); } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/TraceCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/TraceCheck.cs index b8e4e51eb9..47c88bf250 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/TraceCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/TraceCheck.cs @@ -24,10 +24,10 @@ namespace Umbraco.Web.HealthCheck.Checks.Config new AcceptableConfiguration { IsRecommended = true, Value = bool.FalseString.ToLower() } }; - public override string CheckSuccessMessage => TextService.Localize("healthcheck/traceModeCheckSuccessMessage"); + public override string CheckSuccessMessage => TextService.Localize("healthcheck", "traceModeCheckSuccessMessage"); - public override string CheckErrorMessage => TextService.Localize("healthcheck/traceModeCheckErrorMessage"); + public override string CheckErrorMessage => TextService.Localize("healthcheck", "traceModeCheckErrorMessage"); - public override string RectifySuccessMessage => TextService.Localize("healthcheck/traceModeCheckRectifySuccessMessage"); + public override string RectifySuccessMessage => TextService.Localize("healthcheck", "traceModeCheckRectifySuccessMessage"); } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Config/TrySkipIisCustomErrorsCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Config/TrySkipIisCustomErrorsCheck.cs index 4a467d7120..2c4e59091e 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Config/TrySkipIisCustomErrorsCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Config/TrySkipIisCustomErrorsCheck.cs @@ -39,7 +39,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckSuccessMessage", + return TextService.Localize("healthcheck", "trySkipIisCustomErrorsCheckSuccessMessage", new[] { Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() }); } } @@ -48,7 +48,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckErrorMessage", + return TextService.Localize("healthcheck", "trySkipIisCustomErrorsCheckErrorMessage", new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() }); } } @@ -57,7 +57,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config { get { - return TextService.Localize("healthcheck/trySkipIisCustomErrorsCheckRectifySuccessMessage", + return TextService.Localize("healthcheck", "trySkipIisCustomErrorsCheckRectifySuccessMessage", new[] { Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() }); } } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs index ee6f254235..218cb2cc9c 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Permissions/FolderAndFilePermissionsCheck.cs @@ -135,22 +135,20 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions { // Return error if any required paths fail the check, or warning if any optional ones do var resultType = StatusResultType.Success; - var messageKey = string.Format("healthcheck/{0}PermissionsCheckMessage", - checkingFor == PermissionCheckFor.Folder ? "folder" : "file"); - var message = _textService.Localize(messageKey); + var messageArea = "healthcheck"; + var messageAlias = string.Concat(checkingFor == PermissionCheckFor.Folder ? "folder" : "file", "PermissionsCheckMessage"); + var message = _textService.Localize(messageArea, messageAlias); if (requiredPathCheckResult == false) { resultType = StatusResultType.Error; - messageKey = string.Format("healthcheck/required{0}PermissionFailed", - checkingFor == PermissionCheckFor.Folder ? "Folder" : "File"); - message = GetMessageForPathCheckFailure(messageKey, requiredFailedPaths); + messageAlias = string.Concat("required", checkingFor == PermissionCheckFor.Folder ? "Folder" : "File", "PermissionFailed"); + message = GetMessageForPathCheckFailure(messageArea, messageAlias, requiredFailedPaths); } else if (optionalPathCheckResult == false) { resultType = StatusResultType.Warning; - messageKey = string.Format("healthcheck/optional{0}PermissionFailed", - checkingFor == PermissionCheckFor.Folder ? "Folder" : "File"); - message = GetMessageForPathCheckFailure(messageKey, optionalFailedPaths); + messageAlias = string.Concat("optional", checkingFor == PermissionCheckFor.Folder ? "Folder" : "File", "PermissionFailed"); + message = GetMessageForPathCheckFailure(messageArea, messageAlias, optionalFailedPaths); } var actions = new List(); @@ -162,12 +160,12 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions }; } - private string GetMessageForPathCheckFailure(string messageKey, IEnumerable failedPaths) + private string GetMessageForPathCheckFailure(string messageArea,string messageAlias, IEnumerable failedPaths) { var rootFolder = IOHelper.MapPath("/"); var failedFolders = failedPaths .Select(x => ParseFolderFromFullPath(rootFolder, x)); - return _textService.Localize(messageKey, + return _textService.Localize(messageArea, messageAlias, new[] { string.Join(", ", failedFolders) }); } diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs index 5e66bc47b1..6bb32d9d74 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/BaseHttpHeaderCheck.cs @@ -87,12 +87,12 @@ namespace Umbraco.Web.HealthCheck.Checks.Security } message = success - ? TextService.Localize($"healthcheck/{_localizedTextPrefix}CheckHeaderFound") - : TextService.Localize($"healthcheck/{_localizedTextPrefix}CheckHeaderNotFound"); + ? TextService.Localize($"healthcheck", "{_localizedTextPrefix}CheckHeaderFound") + : TextService.Localize($"healthcheck", "{_localizedTextPrefix}CheckHeaderNotFound"); } catch (Exception ex) { - message = TextService.Localize("healthcheck/healthCheckInvalidUrl", new[] { url.ToString(), ex.Message }); + message = TextService.Localize("healthcheck", "healthCheckInvalidUrl", new[] { url.ToString(), ex.Message }); } var actions = new List(); @@ -100,8 +100,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security { actions.Add(new HealthCheckAction(SetHeaderInConfigAction, Id) { - Name = TextService.Localize("healthcheck/setHeaderInConfig"), - Description = TextService.Localize($"healthcheck/{_localizedTextPrefix}SetHeaderInConfigDescription") + Name = TextService.Localize("healthcheck", "setHeaderInConfig"), + Description = TextService.Localize($"healthcheck", "{_localizedTextPrefix}SetHeaderInConfigDescription") }); } @@ -149,14 +149,14 @@ namespace Umbraco.Web.HealthCheck.Checks.Security if (success) { return - new HealthCheckStatus(TextService.Localize(string.Format("healthcheck/{0}SetHeaderInConfigSuccess", _localizedTextPrefix))) + new HealthCheckStatus(TextService.Localize("healthcheck", _localizedTextPrefix + "SetHeaderInConfigSuccess")) { ResultType = StatusResultType.Success }; } return - new HealthCheckStatus(TextService.Localize("healthcheck/setHeaderInConfigError", new [] { errorMessage })) + new HealthCheckStatus(TextService.Localize("healthcheck", "setHeaderInConfigError", new [] { errorMessage })) { ResultType = StatusResultType.Error }; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs index fd76b9d486..28904cc6bd 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/ExcessiveHeadersCheck.cs @@ -64,12 +64,12 @@ namespace Umbraco.Web.HealthCheck.Checks.Security .ToArray(); success = headersFound.Any() == false; message = success - ? _textService.Localize("healthcheck/excessiveHeadersNotFound") - : _textService.Localize("healthcheck/excessiveHeadersFound", new [] { string.Join(", ", headersFound) }); + ? _textService.Localize("healthcheck", "excessiveHeadersNotFound") + : _textService.Localize("healthcheck", "excessiveHeadersFound", new [] { string.Join(", ", headersFound) }); } catch (Exception ex) { - message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { url.ToString(), ex.Message }); + message = _textService.Localize("healthcheck", "httpsCheckInvalidUrl", new[] { url.ToString(), ex.Message }); } var actions = new List(); diff --git a/src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs index 98f8a83c1d..51b253fe94 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Security/HttpsCheck.cs @@ -84,23 +84,23 @@ namespace Umbraco.Web.HealthCheck.Checks.Security if (daysToExpiry <= 0) { result = StatusResultType.Error; - message = _textService.Localize("healthcheck/httpsCheckExpiredCertificate"); + message = _textService.Localize("healthcheck", "httpsCheckExpiredCertificate"); } else if (daysToExpiry < NumberOfDaysForExpiryWarning) { result = StatusResultType.Warning; - message = _textService.Localize("healthcheck/httpsCheckExpiringCertificate", new[] { daysToExpiry.ToString() }); + message = _textService.Localize("healthcheck", "httpsCheckExpiringCertificate", new[] { daysToExpiry.ToString() }); } else { result = StatusResultType.Success; - message = _textService.Localize("healthcheck/httpsCheckValidCertificate"); + message = _textService.Localize("healthcheck", "httpsCheckValidCertificate"); } } else { result = StatusResultType.Error; - message = _textService.Localize("healthcheck/healthCheckInvalidUrl", new[] { url, response.StatusDescription }); + message = _textService.Localize("healthcheck", "healthCheckInvalidUrl", new[] { url, response.StatusDescription }); } } catch (Exception ex) @@ -109,12 +109,12 @@ namespace Umbraco.Web.HealthCheck.Checks.Security if (exception != null) { message = exception.Status == WebExceptionStatus.TrustFailure - ? _textService.Localize("healthcheck/httpsCheckInvalidCertificate", new [] { exception.Message }) - : _textService.Localize("healthcheck/healthCheckInvalidUrl", new [] { url, exception.Message }); + ? _textService.Localize("healthcheck", "httpsCheckInvalidCertificate", new [] { exception.Message }) + : _textService.Localize("healthcheck", "healthCheckInvalidUrl", new [] { url, exception.Message }); } else { - message = _textService.Localize("healthcheck/healthCheckInvalidUrl", new[] { url, ex.Message }); + message = _textService.Localize("healthcheck", "healthCheckInvalidUrl", new[] { url, ex.Message }); } result = StatusResultType.Error; @@ -138,7 +138,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security var actions = new List(); return - new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckIsCurrentSchemeHttps", new[] { success ? string.Empty : "not" })) + new HealthCheckStatus(_textService.Localize("healthcheck", "httpsCheckIsCurrentSchemeHttps", new[] { success ? string.Empty : "not" })) { ResultType = success ? StatusResultType.Success : StatusResultType.Error, Actions = actions @@ -155,7 +155,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security StatusResultType resultType; if (uri.Scheme != "https") { - resultMessage = _textService.Localize("healthcheck/httpsCheckConfigurationRectifyNotPossible"); + resultMessage = _textService.Localize("healthcheck", "httpsCheckConfigurationRectifyNotPossible"); resultType = StatusResultType.Info; } else @@ -163,11 +163,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Security if (httpsSettingEnabled == false) actions.Add(new HealthCheckAction(FixHttpsSettingAction, Id) { - Name = _textService.Localize("healthcheck/httpsCheckEnableHttpsButton"), - Description = _textService.Localize("healthcheck/httpsCheckEnableHttpsDescription") + Name = _textService.Localize("healthcheck", "httpsCheckEnableHttpsButton"), + Description = _textService.Localize("healthcheck", "httpsCheckEnableHttpsDescription") }); - resultMessage = _textService.Localize("healthcheck/httpsCheckConfigurationCheckResult", + resultMessage = _textService.Localize("healthcheck", "httpsCheckConfigurationCheckResult", new[] {httpsSettingEnabled.ToString(), httpsSettingEnabled ? string.Empty : "not"}); resultType = httpsSettingEnabled ? StatusResultType.Success: StatusResultType.Error; } @@ -190,14 +190,14 @@ namespace Umbraco.Web.HealthCheck.Checks.Security if (updateConfigFile.Success) { return - new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckEnableHttpsSuccess")) + new HealthCheckStatus(_textService.Localize("healthcheck", "httpsCheckEnableHttpsSuccess")) { ResultType = StatusResultType.Success }; } return - new HealthCheckStatus(_textService.Localize("healthcheck/httpsCheckEnableHttpsError", new [] { updateConfigFile.Result })) + new HealthCheckStatus(_textService.Localize("healthcheck", "httpsCheckEnableHttpsError", new [] { updateConfigFile.Result })) { ResultType = StatusResultType.Error }; diff --git a/src/Umbraco.Web/HealthCheck/Checks/Services/SmtpCheck.cs b/src/Umbraco.Web/HealthCheck/Checks/Services/SmtpCheck.cs index d6f7cef497..2627e24fb8 100644 --- a/src/Umbraco.Web/HealthCheck/Checks/Services/SmtpCheck.cs +++ b/src/Umbraco.Web/HealthCheck/Checks/Services/SmtpCheck.cs @@ -56,7 +56,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Services var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings"); if (settings == null) { - message = _textService.Localize("healthcheck/smtpMailSettingsNotFound"); + message = _textService.Localize("healthcheck", "smtpMailSettingsNotFound"); } else { @@ -64,14 +64,14 @@ namespace Umbraco.Web.HealthCheck.Checks.Services var port = settings.Smtp.Network.Port == 0 ? DefaultSmtpPort : settings.Smtp.Network.Port; if (string.IsNullOrEmpty(host)) { - message = _textService.Localize("healthcheck/smtpMailSettingsHostNotConfigured"); + message = _textService.Localize("healthcheck", "smtpMailSettingsHostNotConfigured"); } else { success = CanMakeSmtpConnection(host, port); message = success - ? _textService.Localize("healthcheck/smtpMailSettingsConnectionSuccess") - : _textService.Localize("healthcheck/smtpMailSettingsConnectionFail", new [] { host, port.ToString() }); + ? _textService.Localize("healthcheck", "smtpMailSettingsConnectionSuccess") + : _textService.Localize("healthcheck", "smtpMailSettingsConnectionFail", new [] { host, port.ToString() }); } } diff --git a/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs b/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs index 873b356214..54e4f2c52e 100644 --- a/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs +++ b/src/Umbraco.Web/HealthCheck/NotificationMethods/EmailNotificationMethod.cs @@ -48,7 +48,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods return; } - var message = _textService.Localize("healthcheck/scheduledHealthCheckEmailBody", new[] + var message = _textService.Localize("healthcheck", "scheduledHealthCheckEmailBody", new[] { DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString(), @@ -59,7 +59,7 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods // you can identify the site that these results are for. var host = _runtimeState.ApplicationUrl; - var subject = _textService.Localize("healthcheck/scheduledHealthCheckEmailSubject", new[] { host.ToString() }); + var subject = _textService.Localize("healthcheck", "scheduledHealthCheckEmailSubject", new[] { host.ToString() }); var mailSender = new EmailSender(); using (var mailMessage = CreateMailMessage(subject, message)) diff --git a/src/Umbraco.Web/Macros/MacroRenderer.cs b/src/Umbraco.Web/Macros/MacroRenderer.cs index b4fd8c0d86..4538dc03c6 100755 --- a/src/Umbraco.Web/Macros/MacroRenderer.cs +++ b/src/Umbraco.Web/Macros/MacroRenderer.cs @@ -340,14 +340,14 @@ namespace Umbraco.Web.Macros $"Executing PartialView: MacroSource=\"{model.MacroSource}\".", "Executed PartialView.", () => ExecutePartialView(model, content), - () => textService.Localize("errors/macroErrorLoadingPartialView", new[] { model.MacroSource })); + () => textService.Localize("errors", "macroErrorLoadingPartialView", new[] { model.MacroSource })); default: return ExecuteMacroWithErrorWrapper(model, $"Execute macro with unsupported type \"{model.MacroType}\".", "Executed.", () => { throw new Exception("Unsupported macro type."); }, - () => textService.Localize("errors/macroErrorUnsupportedType")); + () => textService.Localize("errors", "macroErrorUnsupportedType")); } } diff --git a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs b/src/Umbraco.Web/Models/Mapping/CommonMapper.cs index f7db17ff74..d04579d5ee 100644 --- a/src/Umbraco.Web/Models/Mapping/CommonMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CommonMapper.cs @@ -80,7 +80,7 @@ namespace Umbraco.Web.Models.Mapping // localize content app names foreach (var app in apps) { - var localizedAppName = _localizedTextService.Localize($"apps/{app.Alias}"); + var localizedAppName = _localizedTextService.Localize($"apps", "{app.Alias}"); if (localizedAppName.Equals($"[{app.Alias}]", StringComparison.OrdinalIgnoreCase) == false) { app.Name = localizedAppName; diff --git a/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs index 0b6be53045..7b240d2981 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentVariantMapper.cs @@ -153,7 +153,7 @@ namespace Umbraco.Web.Models.Mapping if(!isCultureVariant && !isSegmentVariant) { - return _localizedTextService.Localize("general/default"); + return _localizedTextService.Localize("general", "default"); } var parts = new List(); diff --git a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs index 8744b068a7..61c6684093 100644 --- a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesMapper.cs @@ -61,7 +61,7 @@ namespace Umbraco.Web.Models.Mapping if (isLockedOutProperty?.Value != null && isLockedOutProperty.Value.ToString() != "1") { isLockedOutProperty.View = "readonlyvalue"; - isLockedOutProperty.Value = _localizedTextService.Localize("general/no"); + isLockedOutProperty.Value = _localizedTextService.Localize("general", "no"); } } else @@ -75,7 +75,7 @@ namespace Umbraco.Web.Models.Mapping if (isLockedOutProperty?.Value != null && isLockedOutProperty.Value.ToString() != "1") { isLockedOutProperty.View = "readonlyvalue"; - isLockedOutProperty.Value = _localizedTextService.Localize("general/no"); + isLockedOutProperty.Value = _localizedTextService.Localize("general", "no"); } } @@ -115,14 +115,14 @@ namespace Umbraco.Web.Models.Mapping new ContentPropertyDisplay { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}id", - Label = _localizedTextService.Localize("general/id"), + Label = _localizedTextService.Localize("general","id"), Value = new List {member.Id.ToString(), member.Key.ToString()}, View = "idwithguid" }, new ContentPropertyDisplay { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}doctype", - Label = _localizedTextService.Localize("content/membertype"), + Label = _localizedTextService.Localize("content","membertype"), Value = _localizedTextService.UmbracoDictionaryTranslate(member.ContentType.Name), View = Current.PropertyEditors[Constants.PropertyEditors.Aliases.Label].GetValueEditor().View }, @@ -130,7 +130,7 @@ namespace Umbraco.Web.Models.Mapping new ContentPropertyDisplay { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email", - Label = _localizedTextService.Localize("general/email"), + Label = _localizedTextService.Localize("general","email"), Value = member.Email, View = "email", Validation = {Mandatory = true} @@ -138,7 +138,7 @@ namespace Umbraco.Web.Models.Mapping new ContentPropertyDisplay { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password", - Label = _localizedTextService.Localize("password"), + Label = _localizedTextService.Localize(null,"password"), // NOTE: The value here is a json value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists // only when creating a new member and we want to have a generated password pre-filled. Value = new Dictionary @@ -159,7 +159,7 @@ namespace Umbraco.Web.Models.Mapping new ContentPropertyDisplay { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}membergroup", - Label = _localizedTextService.Localize("content/membergroup"), + Label = _localizedTextService.Localize("content","membergroup"), Value = GetMemberGroupValue(member.Username), View = "membergroups", Config = new Dictionary {{"IsRequired", true}} @@ -223,7 +223,7 @@ namespace Umbraco.Web.Models.Mapping var prop = new ContentPropertyDisplay { Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login", - Label = localizedText.Localize("login"), + Label = localizedText.Localize(null,"login"), Value = member.Username }; diff --git a/src/Umbraco.Web/Models/Mapping/SectionMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/SectionMapDefinition.cs index e05e6e5c84..de6a92549a 100644 --- a/src/Umbraco.Web/Models/Mapping/SectionMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/SectionMapDefinition.cs @@ -35,7 +35,7 @@ namespace Umbraco.Web.Models.Mapping private void Map(ISection source, Section target, MapperContext context) { target.Alias = source.Alias; - target.Name = _textService.Localize("sections/" + source.Alias); + target.Name = _textService.Localize("sections", source.Alias); } // Umbraco.Code.MapAll diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs index b8d76572fb..85af1dbe9e 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesMapper.cs @@ -58,7 +58,7 @@ namespace Umbraco.Web.Models.Mapping tabs.Add(new Tab { Id = 0, - Label = LocalizedTextService.Localize("general/properties"), + Label = LocalizedTextService.Localize("general", "properties"), Alias = "Generic properties", Properties = genericproperties }); diff --git a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs index aa158799cb..924ffda554 100644 --- a/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/UserMapDefinition.cs @@ -273,8 +273,8 @@ namespace Umbraco.Web.Models.Mapping { target.AvailableCultures = _textService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName); target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache); - target.CalculatedStartContentIds = GetStartNodes(source.CalculateContentStartNodeIds(_entityService), UmbracoObjectTypes.Document, "content/contentRoot", context); - target.CalculatedStartMediaIds = GetStartNodes(source.CalculateMediaStartNodeIds(_entityService), UmbracoObjectTypes.Media, "media/mediaRoot", context); + target.CalculatedStartContentIds = GetStartNodes(source.CalculateContentStartNodeIds(_entityService), UmbracoObjectTypes.Document, "content","contentRoot", context); + target.CalculatedStartMediaIds = GetStartNodes(source.CalculateMediaStartNodeIds(_entityService), UmbracoObjectTypes.Media, "media","mediaRoot", context); target.CreateDate = source.CreateDate; target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; @@ -289,8 +289,8 @@ namespace Umbraco.Web.Models.Mapping target.Navigation = CreateUserEditorNavigation(); target.ParentId = -1; target.Path = "-1," + source.Id; - target.StartContentIds = GetStartNodes(source.StartContentIds.ToArray(), UmbracoObjectTypes.Document, "content/contentRoot", context); - target.StartMediaIds = GetStartNodes(source.StartMediaIds.ToArray(), UmbracoObjectTypes.Media, "media/mediaRoot", context); + target.StartContentIds = GetStartNodes(source.StartContentIds.ToArray(), UmbracoObjectTypes.Document, "content","contentRoot", context); + target.StartMediaIds = GetStartNodes(source.StartMediaIds.ToArray(), UmbracoObjectTypes.Media, "media","mediaRoot", context); target.UpdateDate = source.UpdateDate; target.UserGroups = context.MapEnumerable(source.Groups); target.Username = source.Username; @@ -347,12 +347,12 @@ namespace Umbraco.Web.Models.Mapping if (sourceStartMediaId > 0) target.MediaStartNode = context.Map(_entityService.Get(sourceStartMediaId.Value, UmbracoObjectTypes.Media)); else if (sourceStartMediaId == -1) - target.MediaStartNode = CreateRootNode(_textService.Localize("media/mediaRoot")); + target.MediaStartNode = CreateRootNode(_textService.Localize("media", "mediaRoot")); if (sourceStartContentId > 0) target.ContentStartNode = context.Map(_entityService.Get(sourceStartContentId.Value, UmbracoObjectTypes.Document)); else if (sourceStartContentId == -1) - target.ContentStartNode = CreateRootNode(_textService.Localize("content/contentRoot")); + target.ContentStartNode = CreateRootNode(_textService.Localize("content", "contentRoot")); if (target.Icon.IsNullOrWhiteSpace()) target.Icon = Constants.Icons.UserGroup; @@ -364,10 +364,10 @@ namespace Umbraco.Web.Models.Mapping => new Permission { Category = action.Category.IsNullOrWhiteSpace() - ? _textService.Localize($"actionCategories/{Constants.Conventions.PermissionCategories.OtherCategory}") - : _textService.Localize($"actionCategories/{action.Category}"), - Name = _textService.Localize($"actions/{action.Alias}"), - Description = _textService.Localize($"actionDescriptions/{action.Alias}"), + ? _textService.Localize("actionCategories",Constants.Conventions.PermissionCategories.OtherCategory) + : _textService.Localize("actionCategories", action.Category), + Name = _textService.Localize("actions", action.Alias), + Description = _textService.Localize("actionDescriptions", action.Alias), Icon = action.Icon, Checked = source.Permissions != null && source.Permissions.Contains(action.Letter.ToString(CultureInfo.InvariantCulture)), PermissionCode = action.Letter.ToString(CultureInfo.InvariantCulture) @@ -383,14 +383,14 @@ namespace Umbraco.Web.Models.Mapping private static string MapContentTypeIcon(IEntitySlim entity) => entity is IContentEntitySlim contentEntity ? contentEntity.ContentTypeIcon : null; - private IEnumerable GetStartNodes(int[] startNodeIds, UmbracoObjectTypes objectType, string localizedKey, MapperContext context) + private IEnumerable GetStartNodes(int[] startNodeIds, UmbracoObjectTypes objectType, string localizedArea,string localizedAlias, MapperContext context) { if (startNodeIds.Length <= 0) return Enumerable.Empty(); var startNodes = new List(); if (startNodeIds.Contains(-1)) - startNodes.Add(CreateRootNode(_textService.Localize(localizedKey))); + startNodes.Add(CreateRootNode(_textService.Localize(localizedArea, localizedAlias))); var mediaItems = _entityService.GetAll(objectType, startNodeIds); startNodes.AddRange(context.MapEnumerable(mediaItems)); @@ -406,7 +406,7 @@ namespace Umbraco.Web.Models.Mapping Active = true, Alias = "details", Icon = "icon-umb-users", - Name = _textService.Localize("general/user"), + Name = _textService.Localize("general","user"), View = "views/users/views/user/details.html" } }; diff --git a/src/Umbraco.Web/Models/Trees/MenuItem.cs b/src/Umbraco.Web/Models/Trees/MenuItem.cs index fb4dfb836c..9d4d146596 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItem.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItem.cs @@ -32,12 +32,9 @@ namespace Umbraco.Web.Models.Trees public MenuItem(string alias, ILocalizedTextService textService) : this() { - var values = textService.GetAllStoredValues(Thread.CurrentThread.CurrentUICulture); - values.TryGetValue($"visuallyHiddenTexts/{alias}_description", out var textDescription); - Alias = alias; - Name = textService.Localize($"actions/{Alias}"); - TextDescription = textDescription; + Name = textService.Localize("actions", Alias); + TextDescription = textService.Localize("visuallyHiddenTexts", alias + "_description", Thread.CurrentThread.CurrentUICulture); } /// diff --git a/src/Umbraco.Web/Models/Trees/MenuItemList.cs b/src/Umbraco.Web/Models/Trees/MenuItemList.cs index 4aaf0632ab..edcfd24584 100644 --- a/src/Umbraco.Web/Models/Trees/MenuItemList.cs +++ b/src/Umbraco.Web/Models/Trees/MenuItemList.cs @@ -97,14 +97,12 @@ namespace Umbraco.Web.Models.Trees var item = Current.Actions.GetAction(); if (item == null) return null; - var values = textService.GetAllStoredValues(Thread.CurrentThread.CurrentUICulture); - values.TryGetValue($"visuallyHiddenTexts/{item.Alias}Description", out var textDescription); - var menuItem = new MenuItem(item, textService.Localize($"actions/{item.Alias}")) + var menuItem = new MenuItem(item, textService.Localize("actions",item.Alias)) { SeparatorBefore = hasSeparator, OpensDialog = opensDialog, - TextDescription = textDescription, + TextDescription = textService.Localize("visuallyHiddenTexts", item.Alias+"Description", Thread.CurrentThread.CurrentUICulture), }; return menuItem; diff --git a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs index 8c4ebf49c3..705d4a706d 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockEditorPropertyEditor.cs @@ -249,7 +249,7 @@ namespace Umbraco.Web.PropertyEditors || (blockEditorData != null && validationLimit.Min.HasValue && blockEditorData.Layout.Count() < validationLimit.Min)) { yield return new ValidationResult( - _textService.Localize("validation/entriesShort", new[] + _textService.Localize("validation", "entriesShort", new[] { validationLimit.Min.ToString(), (validationLimit.Min - blockEditorData.Layout.Count()).ToString() @@ -260,7 +260,7 @@ namespace Umbraco.Web.PropertyEditors if (blockEditorData != null && validationLimit.Max.HasValue && blockEditorData.Layout.Count() > validationLimit.Max) { yield return new ValidationResult( - _textService.Localize("validation/entriesExceed", new[] + _textService.Localize("validation", "entriesExceed", new[] { validationLimit.Max.ToString(), (blockEditorData.Layout.Count() - validationLimit.Max).ToString() diff --git a/src/Umbraco.Web/PropertyEditors/DropDownFlexibleConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownFlexibleConfigurationEditor.cs index dbdedebd0a..cf0aca7ba0 100644 --- a/src/Umbraco.Web/PropertyEditors/DropDownFlexibleConfigurationEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DropDownFlexibleConfigurationEditor.cs @@ -14,7 +14,7 @@ namespace Umbraco.Web.PropertyEditors var items = Fields.First(x => x.Key == "items"); // customize the items field - items.Name = textService.Localize("editdatatype/addPrevalue"); + items.Name = textService.Localize("editdatatype", "addPrevalue"); items.Validators.Add(new ValueListUniqueValueValidator()); } diff --git a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs b/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs index 8d7bd29889..f691feba52 100644 --- a/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs +++ b/src/Umbraco.Web/PropertyEditors/UploadFileTypeValidator.cs @@ -40,7 +40,7 @@ namespace Umbraco.Web.PropertyEditors { //we only store a single value for this editor so the 'member' or 'field' // we'll associate this error with will simply be called 'value' - yield return new ValidationResult(Current.Services.TextService.Localize("errors/dissallowedMediaType"), new[] { "value" }); + yield return new ValidationResult(Current.Services.TextService.Localize("errors", "dissallowedMediaType"), new[] { "value" }); } } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueListConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/ValueListConfigurationEditor.cs index abfde3646a..b4f600dbf3 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueListConfigurationEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueListConfigurationEditor.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.PropertyEditors var items = Fields.First(x => x.Key == "items"); // customize the items field - items.Name = textService.Localize("editdatatype/addPrevalue"); + items.Name = textService.Localize("editdatatype", "addPrevalue"); items.Validators.Add(new ValueListUniqueValueValidator()); } diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index afa0bc96cc..7d5fba7df5 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -35,7 +35,7 @@ namespace Umbraco.Web.Routing if (content.Published == false) { - yield return UrlInfo.Message(textService.Localize("content/itemNotPublished")); + yield return UrlInfo.Message(textService.Localize("content", "itemNotPublished")); yield break; } @@ -126,7 +126,7 @@ namespace Umbraco.Web.Routing // deal with exceptions case "#ex": - yield return UrlInfo.Message(textService.Localize("content/getUrlException"), culture); + yield return UrlInfo.Message(textService.Localize("content", "getUrlException"), culture); break; // got a URL, deal with collisions, add URL @@ -152,13 +152,13 @@ namespace Umbraco.Web.Routing while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture))); if (parent == null) // oops, internal error - return UrlInfo.Message(textService.Localize("content/parentNotPublishedAnomaly"), culture); + return UrlInfo.Message(textService.Localize("content", "parentNotPublishedAnomaly"), culture); else if (!parent.Published) // totally not published - return UrlInfo.Message(textService.Localize("content/parentNotPublished", new[] {parent.Name}), culture); + return UrlInfo.Message(textService.Localize("content", "parentNotPublished", new[] {parent.Name}), culture); else // culture not published - return UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] {parent.Name}), culture); + return UrlInfo.Message(textService.Localize("content", "parentCultureNotPublished", new[] {parent.Name}), culture); } private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo) @@ -174,7 +174,7 @@ namespace Umbraco.Web.Routing if (pcr.HasPublishedContent == false) { - urlInfo = UrlInfo.Message(textService.Localize("content/routeErrorCannotRoute"), culture); + urlInfo = UrlInfo.Message(textService.Localize("content", "routeErrorCannotRoute"), culture); return true; } @@ -193,7 +193,7 @@ namespace Umbraco.Web.Routing l.Reverse(); var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; - urlInfo = UrlInfo.Message(textService.Localize("content/routeError", new[] { s }), culture); + urlInfo = UrlInfo.Message(textService.Localize("content", "routeError", new[] { s }), culture); return true; } diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index ad9e3ca172..82853bace3 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -70,7 +70,7 @@ namespace Umbraco.Web.Trees { //if there are no trees defined for this section but the section is defined then we can have a simple //full screen section without trees - var name = Services.TextService.Localize("sections/" + application); + var name = Services.TextService.Localize("sections", application); return TreeRootNode.CreateSingleTreeRoot(Constants.System.RootString, null, null, name, TreeNodeCollection.Empty, true); } @@ -103,7 +103,7 @@ namespace Umbraco.Web.Trees nodes.Add(node); } - var name = Services.TextService.Localize("sections/" + application); + var name = Services.TextService.Localize("sections", application); if (nodes.Count > 0) { @@ -138,7 +138,7 @@ namespace Umbraco.Web.Trees var name = groupName.IsNullOrWhiteSpace() ? "thirdPartyGroup" : groupName; var groupRootNode = TreeRootNode.CreateGroupNode(nodes, application); - groupRootNode.Name = Services.TextService.Localize("treeHeaders/" + name); + groupRootNode.Name = Services.TextService.Localize("treeHeaders", name); treeRootNodes.Add(groupRootNode); } diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 95de72b7bf..66e9fbf50c 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -331,7 +331,7 @@ namespace Umbraco.Web.Trees RecycleBinId.ToInvariantString(), id, queryStrings, - Services.TextService.Localize("general/recycleBin"), + Services.TextService.Localize("general", "recycleBin"), "icon-trash", RecycleBinSmells, queryStrings.GetRequiredValue("application") + TreeAlias.EnsureStartsWith('/') + "/recyclebin")); diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 6a7fb7f5ad..c0727d6e78 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -135,7 +135,7 @@ namespace Umbraco.Web.Trees menu.Items.Add(Services.TextService, opensDialog: true); - menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) + menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions", "rename")) { Icon = "icon icon-edit" }); diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index 7a9a80c8fc..d3989e54bb 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -99,7 +99,7 @@ namespace Umbraco.Web.Trees menu.Items.Add(Services.TextService, opensDialog: true); - menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions/rename")) + menu.Items.Add(new MenuItem("rename", Services.TextService.Localize("actions", "rename")) { Icon = "icon icon-edit" }); diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index c0a9d15cfa..0bf1787402 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -124,7 +124,7 @@ namespace Umbraco.Web.Trees if (id == Constants.System.RootString) { nodes.Add( - CreateTreeNode(Constants.Conventions.MemberTypes.AllMembersListId, id, queryStrings, Services.TextService.Localize("member/allMembers"), Constants.Icons.MemberType, true, + CreateTreeNode(Constants.Conventions.MemberTypes.AllMembersListId, id, queryStrings, Services.TextService.Localize("member", "allMembers"), Constants.Icons.MemberType, true, queryStrings.GetRequiredValue("application") + TreeAlias.EnsureStartsWith('/') + "/list/" + Constants.Conventions.MemberTypes.AllMembersListId)); if (_isUmbracoProvider) diff --git a/src/Umbraco.Web/Trees/Tree.cs b/src/Umbraco.Web/Trees/Tree.cs index 4747d2495b..bc0511fe1e 100644 --- a/src/Umbraco.Web/Trees/Tree.cs +++ b/src/Umbraco.Web/Trees/Tree.cs @@ -50,7 +50,7 @@ namespace Umbraco.Web.Trees var label = $"[{tree.TreeAlias}]"; // try to look up a the localized tree header matching the tree alias - var localizedLabel = textService.Localize("treeHeaders/" + tree.TreeAlias); + var localizedLabel = textService.Localize("treeHeader", tree.TreeAlias); // if the localizedLabel returns [alias] then return the title if it's defined if (localizedLabel != null && localizedLabel.Equals(label, StringComparison.InvariantCultureIgnoreCase))