From 4e33552a5c9223652405e2dd1a28a7bafabc0a43 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 17 Dec 2014 16:19:42 +1100 Subject: [PATCH] wip - working on U4-5966 --- .../Services/ILocalizedTextService.cs | 17 ++- .../Services/LocalizedTextService.cs | 107 +++++++++++++-- .../Services/LocalizedTextServiceTests.cs | 124 +++++++++++++++++- src/umbraco.businesslogic/ui.cs | 39 +++--- 4 files changed, 253 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Core/Services/ILocalizedTextService.cs b/src/Umbraco.Core/Services/ILocalizedTextService.cs index dd6f9de883..097f1f689b 100644 --- a/src/Umbraco.Core/Services/ILocalizedTextService.cs +++ b/src/Umbraco.Core/Services/ILocalizedTextService.cs @@ -1,4 +1,6 @@ -using System.Globalization; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; namespace Umbraco.Core.Services { @@ -16,11 +18,14 @@ namespace Umbraco.Core.Services /// /// /// - /// This can be null + /// This can be null /// - string Localize(string key, CultureInfo culture, - - //TODO: Potentially this should be a dictionary to simplify things a little? - object variables); + string Localize(string key, CultureInfo culture, IDictionary tokens); + + /// + /// Returns all key/values in storage for the given culture + /// + /// + IDictionary GetAllStoredValues(CultureInfo culture); } } diff --git a/src/Umbraco.Core/Services/LocalizedTextService.cs b/src/Umbraco.Core/Services/LocalizedTextService.cs index d9a445b482..3610bee6cb 100644 --- a/src/Umbraco.Core/Services/LocalizedTextService.cs +++ b/src/Umbraco.Core/Services/LocalizedTextService.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Services _dictionarySource = source; } - public string Localize(string key, CultureInfo culture, object variables) + public string Localize(string key, CultureInfo culture, IDictionary tokens) { Mandate.ParameterNotNull(culture, "culture"); @@ -102,15 +102,63 @@ namespace Umbraco.Core.Services if (_xmlSource != null) { - return GetFromXmlSource(culture, area, alias); + return GetFromXmlSource(culture, area, alias, tokens); } else { - return GetFromDictionarySource(culture, area, alias); + return GetFromDictionarySource(culture, area, alias, tokens); } } - private string GetFromDictionarySource(CultureInfo culture, string area, string key) + /// + /// Returns all key/values in storage for the given culture + /// + /// + public IDictionary GetAllStoredValues(CultureInfo culture) + { + if (culture == null) throw new ArgumentNullException("culture"); + + var result = new Dictionary(); + + if (_xmlSource != null) + { + if (_xmlSource.ContainsKey(culture) == false) + { + throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); + } + + //convert all areas + keys to a single key with a '/' + var areas = _xmlSource[culture].Value.XPathSelectElements("//area"); + foreach (var area in areas) + { + var keys = area.XPathSelectElements("./key"); + foreach (var key in keys) + { + result.Add(string.Format("{0}/{1}", (string) area.Attribute("alias"), (string) key.Attribute("alias")), key.Value); + } + } + } + else + { + if (_dictionarySource.ContainsKey(culture) == false) + { + throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); + } + + //convert all areas + keys to a single key with a '/' + foreach (var area in _dictionarySource[culture]) + { + foreach (var key in area.Value) + { + result.Add(string.Format("{0}/{1}", area.Key, key.Key), key.Value); + } + } + } + + return result; + } + + private string GetFromDictionarySource(CultureInfo culture, string area, string key, IDictionary tokens) { if (_dictionarySource.ContainsKey(culture) == false) { @@ -138,11 +186,16 @@ namespace Umbraco.Core.Services .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 found ?? "[" + key + "]"; + return "[" + key + "]"; } - private string GetFromXmlSource(CultureInfo culture, string area, string key) + private string GetFromXmlSource(CultureInfo culture, string area, string key, IDictionary tokens) { if (_xmlSource.ContainsKey(culture) == false) { @@ -157,13 +210,43 @@ namespace Umbraco.Core.Services var found = cultureSource.XPathSelectElement(xpath); - return found == null - //NOTE: Based on how legacy works, the default text does not contain the area, just the key - ? "[" + key + "]" - : found.Value; + if (found != null) + { + return ParseTokens(found.Value, tokens); + } + + //NOTE: Based on how legacy works, the default text does not contain the area, just the key + return "[" + key + "]"; + } + + /// + /// 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 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; } - - } } diff --git a/src/Umbraco.Tests/Services/LocalizedTextServiceTests.cs b/src/Umbraco.Tests/Services/LocalizedTextServiceTests.cs index 38f0f1a1ce..ba1ad70b8c 100644 --- a/src/Umbraco.Tests/Services/LocalizedTextServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizedTextServiceTests.cs @@ -13,6 +13,81 @@ namespace Umbraco.Tests.Services [TestFixture] public class LocalizedTextServiceTests { + [Test] + public void Using_Dictionary_Gets_All_Stored_Values() + { + var culture = CultureInfo.GetCultureInfo("en-US"); + var txtService = new LocalizedTextService( + new Dictionary>> + { + { + culture, new Dictionary> + { + { + "testArea1", new Dictionary + { + {"testKey1", "testValue1"}, + {"testKey2", "testValue2"} + } + }, + { + "testArea2", new Dictionary + { + {"blah1", "blahValue1"}, + {"blah2", "blahValue2"} + } + }, + } + } + }); + + var result = txtService.GetAllStoredValues(culture); + + Assert.AreEqual(4, result.Count()); + Assert.AreEqual("testArea1/testKey1", result.ElementAt(0).Key); + Assert.AreEqual("testArea1/testKey2", result.ElementAt(1).Key); + Assert.AreEqual("testArea2/blah1", result.ElementAt(2).Key); + Assert.AreEqual("testArea2/blah2", result.ElementAt(3).Key); + Assert.AreEqual("testValue1", result["testArea1/testKey1"]); + Assert.AreEqual("testValue2", result["testArea1/testKey2"]); + Assert.AreEqual("blahValue1", result["testArea2/blah1"]); + Assert.AreEqual("blahValue2", result["testArea2/blah2"]); + + } + + [Test] + public void Using_XDocument_Gets_All_Stored_Values() + { + var culture = CultureInfo.GetCultureInfo("en-US"); + 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"))))) + } + }); + + var result = txtService.GetAllStoredValues(culture); + + Assert.AreEqual(4, result.Count()); + Assert.AreEqual("testArea1/testKey1", result.ElementAt(0).Key); + Assert.AreEqual("testArea1/testKey2", result.ElementAt(1).Key); + Assert.AreEqual("testArea2/blah1", result.ElementAt(2).Key); + Assert.AreEqual("testArea2/blah2", result.ElementAt(3).Key); + Assert.AreEqual("testValue1", result["testArea1/testKey1"]); + Assert.AreEqual("testValue2", result["testArea1/testKey2"]); + Assert.AreEqual("blahValue1", result["testArea2/blah1"]); + Assert.AreEqual("blahValue2", result["testArea2/blah2"]); + + } + [Test] public void Using_Dictionary_Returns_Text_With_Area() @@ -115,6 +190,32 @@ namespace Umbraco.Tests.Services Assert.AreEqual("[doNotFind]", result); } + [Test] + public void Using_Dictionary_Returns_Tokenized_Text() + { + var culture = CultureInfo.GetCultureInfo("en-US"); + var txtService = new LocalizedTextService( + new Dictionary>> + { + { + culture, new Dictionary> + { + { + "testArea", new Dictionary + { + {"testKey", "Hello %0%, you are such a %1% %2%"} + } + } + } + } + }); + + var result = txtService.Localize("testKey", culture, + new Dictionary { { "0", "world" }, { "1", "great" }, { "2", "planet" } }); + + Assert.AreEqual("Hello world, you are such a great planet", result); + } + [Test] public void Using_XDocument_Returns_Text_With_Area() { @@ -196,6 +297,27 @@ namespace Umbraco.Tests.Services Assert.AreEqual("[doNotFind]", result); } + [Test] + public void Using_XDocument_Returns_Tokenized_Text() + { + var culture = CultureInfo.GetCultureInfo("en-US"); + var txtService = new LocalizedTextService( + new Dictionary> + { + { + culture, new Lazy(() => new XDocument( + new XElement("area", new XAttribute("alias", "testArea"), + new XElement("key", new XAttribute("alias", "testKey"), + "Hello %0%, you are such a %1% %2%")))) + } + }); + + var result = txtService.Localize("testKey", culture, + new Dictionary { { "0", "world" }, { "1", "great" }, { "2", "planet" } }); + + Assert.AreEqual("Hello world, you are such a great planet", result); + } + [Test] public void Using_Dictionary_Throws_When_No_Culture_Found() { @@ -216,7 +338,7 @@ namespace Umbraco.Tests.Services } }); - Assert.Throws(() => txtService.Localize("testArea/testKey", CultureInfo.GetCultureInfo("en-AU"))); + Assert.Throws(() => txtService.Localize("testArea/testKey", CultureInfo.GetCultureInfo("en-AU"))); } [Test] diff --git a/src/umbraco.businesslogic/ui.cs b/src/umbraco.businesslogic/ui.cs index 509df667aa..2ab3528ac3 100644 --- a/src/umbraco.businesslogic/ui.cs +++ b/src/umbraco.businesslogic/ui.cs @@ -1,7 +1,10 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Web; @@ -360,24 +363,25 @@ namespace umbraco //return "[" + key + "]"; } - private static string GetStringWithVars(string stringWithVars, string[] variables) - { - var vars = Regex.Matches(stringWithVars, @"\%(\d)\%", - RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - foreach (Match var in vars) - { - stringWithVars = stringWithVars.Replace( - var.Value, - variables[Convert.ToInt32(var.Groups[0].Value.Replace("%", ""))]); - } - return stringWithVars; - } + //private static string GetStringWithVars(string stringWithVars, string[] variables) + //{ + // var vars = Regex.Matches(stringWithVars, @"\%(\d)\%", + // RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + // foreach (Match var in vars) + // { + // stringWithVars = stringWithVars.Replace( + // var.Value, + // variables[Convert.ToInt32(var.Groups[0].Value.Replace("%", ""))]); + // } + // return stringWithVars; + //} /// /// Gets the language file as a xml document. /// /// The language. /// + [Obsolete("This is no longer used and will be removed from the codebase in future versions")] public static XmlDocument getLanguageFile(string language) { var cacheKey = "uitext_" + language; @@ -414,10 +418,15 @@ namespace umbraco } - - internal static object ConvertToObjectVars(string[] variables) + /// + /// Convert an array of strings to a dictionary of indicies -> values + /// + /// + /// + internal static IDictionary ConvertToObjectVars(string[] variables) { - throw new NotImplementedException(); + return variables.Select((s, i) => new {index = i.ToString(CultureInfo.InvariantCulture), value = s}) + .ToDictionary(keyvals => keyvals.index, keyvals => keyvals.value); } }