using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Xml; using System.Xml.Linq; using System.Xml.XPath; using Umbraco.Core.Cache; namespace Umbraco.Core.Services { //TODO: Convert all of this over to Niels K's localization framework one day /// /// Exposes the XDocument sources from files for the default localization text service and ensure caching is taken care of /// public class LocalizedTextServiceFileSources { private readonly IRuntimeCacheProvider _cache; private readonly DirectoryInfo _fileSourceFolder; public LocalizedTextServiceFileSources(IRuntimeCacheProvider cache, DirectoryInfo fileSourceFolder) { if (cache == null) throw new ArgumentNullException("cache"); if (fileSourceFolder == null) throw new ArgumentNullException("fileSourceFolder"); _cache = cache; _fileSourceFolder = fileSourceFolder; } /// /// returns all xml sources for all culture files found in the folder /// /// public IDictionary> GetXmlSources() { var result = new Dictionary>(); foreach (var fileInfo in _fileSourceFolder.GetFiles("*.xml")) { var localCopy = fileInfo; var filename = Path.GetFileNameWithoutExtension(localCopy.FullName); var culture = CultureInfo.GetCultureInfo(filename); //get the lazy value from cache result.Add(culture, new Lazy(() => _cache.GetCacheItem( string.Format("{0}-{1}", typeof (LocalizedTextServiceFileSources).Name, culture.TwoLetterISOLanguageName), () => { using (var fs = localCopy.OpenRead()) { return XDocument.Load(fs); } }, isSliding: true, timeout: TimeSpan.FromMinutes(10), dependentFiles: new[] {localCopy.FullName}))); } return result; } } public class LocalizedTextService : ILocalizedTextService { private readonly IDictionary>> _dictionarySource; private readonly IDictionary> _xmlSource; /// /// Initializes with a file sources instance /// /// public LocalizedTextService(LocalizedTextServiceFileSources fileSources) { _xmlSource = fileSources.GetXmlSources(); } /// /// Initializes with an XML source /// /// public LocalizedTextService(IDictionary> source) { if (source == null) throw new ArgumentNullException("source"); _xmlSource = source; } /// /// Initializes with a source of a dictionary of culture -> areas -> sub dictionary of keys/values /// /// public LocalizedTextService(IDictionary>> source) { if (source == null) throw new ArgumentNullException("source"); _dictionarySource = source; } public string Localize(string key, CultureInfo culture, object variables) { Mandate.ParameterNotNull(culture, "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]; if (_xmlSource != null) { return GetFromXmlSource(culture, area, alias); } else { return GetFromDictionarySource(culture, area, alias); } } private string GetFromDictionarySource(CultureInfo culture, string area, string key) { if (_dictionarySource.ContainsKey(culture) == false) { throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); } 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(); } //NOTE: Based on how legacy works, the default text does not contain the area, just the key return found ?? "[" + key + "]"; } private string GetFromXmlSource(CultureInfo culture, string area, string key) { if (_xmlSource.ContainsKey(culture) == false) { throw new NullReferenceException("The culture specified " + culture + " was not found in any configured sources for this service"); } 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 == null //NOTE: Based on how legacy works, the default text does not contain the area, just the key ? "[" + key + "]" : found.Value; } } }