Browse Source

add localization stub

Luke Pulverenti 11 years ago
parent
commit
5a014b093c

+ 15 - 0
MediaBrowser.Api/LocalizationService.cs

@@ -31,6 +31,14 @@ namespace MediaBrowser.Api
     {
     {
     }
     }
 
 
+    /// <summary>
+    /// Class ParentalRatings
+    /// </summary>
+    [Route("/Localization/Options", "GET", Summary = "Gets localization options")]
+    public class GetLocalizationOptions : IReturn<List<LocalizatonOption>>
+    {
+    }
+
     /// <summary>
     /// <summary>
     /// Class CulturesService
     /// Class CulturesService
     /// </summary>
     /// </summary>
@@ -62,6 +70,13 @@ namespace MediaBrowser.Api
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
 
 
+        public object Get(GetLocalizationOptions request)
+        {
+            var result = _localization.GetLocalizationOptions().ToList();
+
+            return ToOptimizedSerializedResultUsingCache(result);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the specified request.
         /// Gets the specified request.
         /// </summary>
         /// </summary>

+ 1 - 1
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Playback.Hls
                                        ? 0
                                        ? 0
                                        : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
                                        : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
 
 
-            var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds);
+            var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
 
 
             var threads = GetNumberOfThreads(state, false);
             var threads = GetNumberOfThreads(state, false);
 
 

+ 31 - 1
MediaBrowser.Controller/Localization/ILocalizationManager.cs

@@ -1,7 +1,7 @@
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Controller.Localization
 namespace MediaBrowser.Controller.Localization
 {
 {
@@ -31,5 +31,35 @@ namespace MediaBrowser.Controller.Localization
         /// <param name="rating">The rating.</param>
         /// <param name="rating">The rating.</param>
         /// <returns>System.Int32.</returns>
         /// <returns>System.Int32.</returns>
         int? GetRatingLevel(string rating);
         int? GetRatingLevel(string rating);
+
+        /// <summary>
+        /// Gets the localized string.
+        /// </summary>
+        /// <param name="phrase">The phrase.</param>
+        /// <param name="culture">The culture.</param>
+        /// <returns>System.String.</returns>
+        string GetLocalizedString(string phrase, string culture);
+
+        /// <summary>
+        /// Gets the localized string.
+        /// </summary>
+        /// <param name="phrase">The phrase.</param>
+        /// <returns>System.String.</returns>
+        string GetLocalizedString(string phrase);
+
+        /// <summary>
+        /// Localizes the document.
+        /// </summary>
+        /// <param name="document">The document.</param>
+        /// <param name="culture">The culture.</param>
+        /// <param name="tokenBuilder">The token builder.</param>
+        /// <returns>System.String.</returns>
+        string LocalizeDocument(string document, string culture, Func<string, string> tokenBuilder);
+
+        /// <summary>
+        /// Gets the localization options.
+        /// </summary>
+        /// <returns>IEnumerable{LocalizatonOption}.</returns>
+        IEnumerable<LocalizatonOption> GetLocalizationOptions();
     }
     }
 }
 }

+ 4 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -218,6 +218,8 @@ namespace MediaBrowser.Model.Configuration
         public string ServerName { get; set; }
         public string ServerName { get; set; }
         public string WanDdns { get; set; }
         public string WanDdns { get; set; }
 
 
+        public string UICulture { get; set; }
+        
         public DlnaOptions DlnaOptions { get; set; }
         public DlnaOptions DlnaOptions { get; set; }
 
 
         /// <summary>
         /// <summary>
@@ -281,6 +283,8 @@ namespace MediaBrowser.Model.Configuration
             MetadataOptions = options.ToArray();
             MetadataOptions = options.ToArray();
 
 
             DlnaOptions = new DlnaOptions();
             DlnaOptions = new DlnaOptions();
+
+            UICulture = "en-us";
         }
         }
     }
     }
 
 

+ 6 - 0
MediaBrowser.Model/Globalization/CountryInfo.cs

@@ -30,4 +30,10 @@ namespace MediaBrowser.Model.Globalization
         /// <value>The name of the three letter ISO region.</value>
         /// <value>The name of the three letter ISO region.</value>
         public string ThreeLetterISORegionName { get; set; }
         public string ThreeLetterISORegionName { get; set; }
     }
     }
+
+    public class LocalizatonOption
+    {
+        public string Name { get; set; }
+        public string Value { get; set; }
+    }
 }
 }

+ 3 - 0
MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json

@@ -0,0 +1,3 @@
+{
+    "LabelTest": "Text"
+}

+ 110 - 3
MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs

@@ -1,9 +1,9 @@
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Serialization;
 using MoreLinq;
 using MoreLinq;
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
@@ -11,6 +11,8 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Reflection;
+using MediaBrowser.Common.Extensions;
 
 
 namespace MediaBrowser.Server.Implementations.Localization
 namespace MediaBrowser.Server.Implementations.Localization
 {
 {
@@ -33,15 +35,17 @@ namespace MediaBrowser.Server.Implementations.Localization
             new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
             new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
 
 
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
-        
+        private readonly IJsonSerializer _jsonSerializer;
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="LocalizationManager"/> class.
         /// Initializes a new instance of the <see cref="LocalizationManager"/> class.
         /// </summary>
         /// </summary>
         /// <param name="configurationManager">The configuration manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
-        public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem)
+        public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
         {
         {
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
+            _jsonSerializer = jsonSerializer;
 
 
             ExtractAll();
             ExtractAll();
         }
         }
@@ -241,5 +245,108 @@ namespace MediaBrowser.Server.Implementations.Localization
 
 
             return value == null ? (int?)null : value.Value;
             return value == null ? (int?)null : value.Value;
         }
         }
+
+        public string GetLocalizedString(string phrase)
+        {
+            return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture);
+        }
+
+        public string GetLocalizedString(string phrase, string culture)
+        {
+            var dictionary = GetLocalizationDictionary(culture);
+
+            string value;
+
+            if (dictionary.TryGetValue(phrase, out value))
+            {
+                return value;
+            }
+
+            return phrase;
+        }
+
+        private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
+            new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
+
+        public Dictionary<string, string> GetLocalizationDictionary(string culture)
+        {
+            const string prefix = "Server";
+            var key = prefix + culture;
+
+            return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "server.json"));
+        }
+
+        public Dictionary<string, string> GetJavaScriptLocalizationDictionary(string culture)
+        {
+            const string prefix = "JavaScript";
+            var key = prefix + culture;
+
+            return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "javascript.json"));
+        }
+
+        private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
+        {
+            var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+            var assembly = GetType().Assembly;
+            var namespaceName = GetType().Namespace + "." + prefix;
+
+            CopyInto(dictionary, namespaceName + "." + baseFilename, assembly);
+            CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture), assembly);
+
+            return dictionary;
+        }
+
+        private void CopyInto(IDictionary<string, string> dictionary, string resourcePath, Assembly assembly)
+        {
+            using (var stream = assembly.GetManifestResourceStream(resourcePath))
+            {
+                if (stream != null)
+                {
+                    var dict = _jsonSerializer.DeserializeFromStream<Dictionary<string, string>>(stream);
+
+                    foreach (var key in dict.Keys)
+                    {
+                        dictionary[key] = dict[key];
+                    }
+                }
+            }
+        }
+
+        private string GetResourceFilename(string culture)
+        {
+            var parts = culture.Split('-');
+
+            if (parts.Length == 2)
+            {
+                culture = parts[0].ToLower() + "_" + parts[1].ToUpper();
+            }
+            else
+            {
+                culture = culture.ToLower();
+            }
+
+            return culture + ".json";
+        }
+
+        public IEnumerable<LocalizatonOption> GetLocalizationOptions()
+        {
+            return new List<LocalizatonOption>
+            {
+                new LocalizatonOption{ Name="English (United States)", Value="en-us"}
+            };
+        }
+
+        public string LocalizeDocument(string document, string culture, Func<string,string> tokenBuilder)
+        {
+            foreach (var pair in GetLocalizationDictionary(culture).ToList())
+            {
+                var token = tokenBuilder(pair.Key);
+
+                document = document.Replace(token, pair.Value, StringComparison.Ordinal);
+            }
+
+            return document;
+        }
     }
     }
 }
 }

+ 17 - 0
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -0,0 +1,17 @@
+{
+    "LabelExit": "Exit",
+	"LabelVisitCommunity": "Visit Community",
+	"LabelGithubWiki": "Github Wiki",
+	"LabelSwagger": "Swagger",
+	"LabelStandard": "Standard",
+	"LabelViewApiDocumentation": "View Api Documentation",
+	"LabelBrowseLibrary": "Browse Library",
+	"LabelConfigureMediaBrowser": "Configure Media Browser",
+	"LabelOpenLibraryViewer": "Open Library Viewer",
+	"LabelRestartServer": "Restart Server",
+	"LabelShowLogWindow": "Show Log Window",
+	"LabelPrevious": "Previous",
+	"LabelFinish": "Finish",
+	"LabelNext": "Next",
+	"LabelYoureDone": "You're Done!"
+}

+ 3 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -282,6 +282,8 @@
     <EmbeddedResource Include="Localization\Ratings\ru.txt" />
     <EmbeddedResource Include="Localization\Ratings\ru.txt" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <EmbeddedResource Include="Localization\JavaScript\javascript.json" />
+    <EmbeddedResource Include="Localization\Server\server.json" />
     <None Include="packages.config" />
     <None Include="packages.config" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
@@ -386,6 +388,7 @@
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
   </ItemGroup>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(SolutionDir)\.nuget\nuget.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
   <Import Project="$(SolutionDir)\.nuget\nuget.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 4 - 4
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -170,7 +170,7 @@ namespace MediaBrowser.ServerApplication
 
 
         private ILiveTvManager LiveTvManager { get; set; }
         private ILiveTvManager LiveTvManager { get; set; }
 
 
-        private ILocalizationManager LocalizationManager { get; set; }
+        internal ILocalizationManager LocalizationManager { get; set; }
 
 
         private IEncodingManager EncodingManager { get; set; }
         private IEncodingManager EncodingManager { get; set; }
         private IChannelManager ChannelManager { get; set; }
         private IChannelManager ChannelManager { get; set; }
@@ -421,6 +421,9 @@ namespace MediaBrowser.ServerApplication
 
 
             RegisterSingleInstance(ServerConfigurationManager);
             RegisterSingleInstance(ServerConfigurationManager);
 
 
+            LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer);
+            RegisterSingleInstance(LocalizationManager);
+
             RegisterSingleInstance<IWebSocketServer>(() => new AlchemyServer(Logger));
             RegisterSingleInstance<IWebSocketServer>(() => new AlchemyServer(Logger));
 
 
             RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer());
             RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer());
@@ -472,9 +475,6 @@ namespace MediaBrowser.ServerApplication
             ServerManager = new ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager);
             ServerManager = new ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager);
             RegisterSingleInstance(ServerManager);
             RegisterSingleInstance(ServerManager);
 
 
-            LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager);
-            RegisterSingleInstance(LocalizationManager);
-
             var innerProgress = new ActionableProgress<double>();
             var innerProgress = new ActionableProgress<double>();
             innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15));
             innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15));
 
 

+ 2 - 4
MediaBrowser.ServerApplication/LibraryViewer.cs

@@ -16,18 +16,16 @@ namespace MediaBrowser.ServerApplication
     {
     {
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
-        private readonly IDisplayPreferencesRepository _displayPreferencesManager;
         private readonly IItemRepository _itemRepository;
         private readonly IItemRepository _itemRepository;
 
 
         private User _currentUser;
         private User _currentUser;
 
 
-        public LibraryViewer(IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IDisplayPreferencesRepository displayPreferencesManager, IItemRepository itemRepo)
+        public LibraryViewer(IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IItemRepository itemRepo)
         {
         {
             InitializeComponent();
             InitializeComponent();
 
 
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
-            _displayPreferencesManager = displayPreferencesManager;
             _itemRepository = itemRepo;
             _itemRepository = itemRepo;
 
 
             foreach (var user in userManager.Users)
             foreach (var user in userManager.Users)
@@ -44,7 +42,7 @@ namespace MediaBrowser.ServerApplication
             if (e.Node != null)
             if (e.Node != null)
             {
             {
                 var item = (BaseItem)e.Node.Tag;
                 var item = (BaseItem)e.Node.Tag;
-                lblType.Text = "Type: " + item.GetType().Name;
+                lblType.Text = item.GetType().Name;
 
 
                 var json = FormatJson(_jsonSerializer.SerializeToString(item));
                 var json = FormatJson(_jsonSerializer.SerializeToString(item));
 
 

+ 1 - 1
MediaBrowser.ServerApplication/MainStartup.cs

@@ -253,7 +253,7 @@ namespace MediaBrowser.ServerApplication
         {
         {
             //Application.EnableVisualStyles();
             //Application.EnableVisualStyles();
             //Application.SetCompatibleTextRenderingDefault(false);
             //Application.SetCompatibleTextRenderingDefault(false);
-            _serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.UserManager, _appHost.LibraryManager, _appHost.JsonSerializer, _appHost.DisplayPreferencesRepository, _appHost.ItemRepository);
+            _serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.UserManager, _appHost.LibraryManager, _appHost.JsonSerializer, _appHost.DisplayPreferencesRepository, _appHost.ItemRepository, _appHost.LocalizationManager);
             Application.Run();
             Application.Run();
         }
         }
 
 

+ 37 - 13
MediaBrowser.ServerApplication/ServerNotifyIcon.cs

@@ -4,6 +4,7 @@ using System.Windows.Forms;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
@@ -41,6 +42,7 @@ namespace MediaBrowser.ServerApplication
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IDisplayPreferencesRepository _displayPreferencesManager;
         private readonly IDisplayPreferencesRepository _displayPreferencesManager;
         private readonly IItemRepository _itemRepository;
         private readonly IItemRepository _itemRepository;
+        private readonly ILocalizationManager _localization;
         private LogForm _logForm;
         private LogForm _logForm;
 
 
         public bool Visible
         public bool Visible
@@ -56,10 +58,17 @@ namespace MediaBrowser.ServerApplication
             }
             }
         }
         }
 
 
-        public ServerNotifyIcon(ILogManager logManager, IServerApplicationHost appHost, IServerConfigurationManager configurationManager, IUserManager userManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer, IDisplayPreferencesRepository displayPreferencesManager, IItemRepository itemRepo)
+        public ServerNotifyIcon(ILogManager logManager, 
+            IServerApplicationHost appHost, 
+            IServerConfigurationManager configurationManager, 
+            IUserManager userManager, ILibraryManager libraryManager, 
+            IJsonSerializer jsonSerializer, 
+            IDisplayPreferencesRepository displayPreferencesManager, 
+            IItemRepository itemRepo, ILocalizationManager localization)
         {
         {
             _logger = logManager.GetLogger("MainWindow");
             _logger = logManager.GetLogger("MainWindow");
             _itemRepository = itemRepo;
             _itemRepository = itemRepo;
+            _localization = localization;
             _appHost = appHost;
             _appHost = appHost;
             _logManager = logManager;
             _logManager = logManager;
             _configurationManager = configurationManager;
             _configurationManager = configurationManager;
@@ -118,20 +127,17 @@ namespace MediaBrowser.ServerApplication
             // 
             // 
             cmdExit.Name = "cmdExit";
             cmdExit.Name = "cmdExit";
             cmdExit.Size = new System.Drawing.Size(208, 22);
             cmdExit.Size = new System.Drawing.Size(208, 22);
-            cmdExit.Text = "Exit";
             // 
             // 
             // cmdCommunity
             // cmdCommunity
             // 
             // 
             cmdCommunity.Name = "cmdCommunity";
             cmdCommunity.Name = "cmdCommunity";
             cmdCommunity.Size = new System.Drawing.Size(208, 22);
             cmdCommunity.Size = new System.Drawing.Size(208, 22);
-            cmdCommunity.Text = "Visit Community";
             // 
             // 
             // cmdLogWindow
             // cmdLogWindow
             // 
             // 
             cmdLogWindow.CheckOnClick = true;
             cmdLogWindow.CheckOnClick = true;
             cmdLogWindow.Name = "cmdLogWindow";
             cmdLogWindow.Name = "cmdLogWindow";
             cmdLogWindow.Size = new System.Drawing.Size(208, 22);
             cmdLogWindow.Size = new System.Drawing.Size(208, 22);
-            cmdLogWindow.Text = "Show Log Window";
             // 
             // 
             // toolStripSeparator1
             // toolStripSeparator1
             // 
             // 
@@ -142,13 +148,11 @@ namespace MediaBrowser.ServerApplication
             // 
             // 
             cmdRestart.Name = "cmdRestart";
             cmdRestart.Name = "cmdRestart";
             cmdRestart.Size = new System.Drawing.Size(208, 22);
             cmdRestart.Size = new System.Drawing.Size(208, 22);
-            cmdRestart.Text = "Restart Server";
             // 
             // 
             // cmdLibraryExplorer
             // cmdLibraryExplorer
             // 
             // 
             cmdLibraryExplorer.Name = "cmdLibraryExplorer";
             cmdLibraryExplorer.Name = "cmdLibraryExplorer";
             cmdLibraryExplorer.Size = new System.Drawing.Size(208, 22);
             cmdLibraryExplorer.Size = new System.Drawing.Size(208, 22);
-            cmdLibraryExplorer.Text = "Open Library Explorer";
             // 
             // 
             // toolStripSeparator2
             // toolStripSeparator2
             // 
             // 
@@ -159,13 +163,11 @@ namespace MediaBrowser.ServerApplication
             // 
             // 
             cmdConfigure.Name = "cmdConfigure";
             cmdConfigure.Name = "cmdConfigure";
             cmdConfigure.Size = new System.Drawing.Size(208, 22);
             cmdConfigure.Size = new System.Drawing.Size(208, 22);
-            cmdConfigure.Text = "Configure Media Browser";
             // 
             // 
             // cmdBrowse
             // cmdBrowse
             // 
             // 
             cmdBrowse.Name = "cmdBrowse";
             cmdBrowse.Name = "cmdBrowse";
             cmdBrowse.Size = new System.Drawing.Size(208, 22);
             cmdBrowse.Size = new System.Drawing.Size(208, 22);
-            cmdBrowse.Text = "Browse Library";
             // 
             // 
             // cmdApiDocs
             // cmdApiDocs
             // 
             // 
@@ -175,25 +177,21 @@ namespace MediaBrowser.ServerApplication
             cmdGtihub});
             cmdGtihub});
             cmdApiDocs.Name = "cmdApiDocs";
             cmdApiDocs.Name = "cmdApiDocs";
             cmdApiDocs.Size = new System.Drawing.Size(208, 22);
             cmdApiDocs.Size = new System.Drawing.Size(208, 22);
-            cmdApiDocs.Text = "View Api Documentation";
             // 
             // 
             // cmdStandardDocs
             // cmdStandardDocs
             // 
             // 
             cmdStandardDocs.Name = "cmdStandardDocs";
             cmdStandardDocs.Name = "cmdStandardDocs";
             cmdStandardDocs.Size = new System.Drawing.Size(136, 22);
             cmdStandardDocs.Size = new System.Drawing.Size(136, 22);
-            cmdStandardDocs.Text = "Standard";
             // 
             // 
             // cmdSwagger
             // cmdSwagger
             // 
             // 
             cmdSwagger.Name = "cmdSwagger";
             cmdSwagger.Name = "cmdSwagger";
             cmdSwagger.Size = new System.Drawing.Size(136, 22);
             cmdSwagger.Size = new System.Drawing.Size(136, 22);
-            cmdSwagger.Text = "Swagger";
             // 
             // 
             // cmdGtihub
             // cmdGtihub
             // 
             // 
             cmdGtihub.Name = "cmdGtihub";
             cmdGtihub.Name = "cmdGtihub";
             cmdGtihub.Size = new System.Drawing.Size(136, 22);
             cmdGtihub.Size = new System.Drawing.Size(136, 22);
-            cmdGtihub.Text = "Github Wiki";
 
 
             cmdExit.Click += cmdExit_Click;
             cmdExit.Click += cmdExit_Click;
             cmdRestart.Click += cmdRestart_Click;
             cmdRestart.Click += cmdRestart_Click;
@@ -211,6 +209,8 @@ namespace MediaBrowser.ServerApplication
             _logManager.LoggerLoaded += LoadLogWindow;
             _logManager.LoggerLoaded += LoadLogWindow;
             _configurationManager.ConfigurationUpdated += Instance_ConfigurationUpdated;
             _configurationManager.ConfigurationUpdated += Instance_ConfigurationUpdated;
 
 
+            LocalizeText();
+
             if (_appHost.IsFirstRun)
             if (_appHost.IsFirstRun)
             {
             {
                 Action action = () => notifyIcon1.ShowBalloonTip(5000, "Media Browser", "Welcome to Media Browser Server!", ToolTipIcon.Info);
                 Action action = () => notifyIcon1.ShowBalloonTip(5000, "Media Browser", "Welcome to Media Browser Server!", ToolTipIcon.Info);
@@ -219,6 +219,24 @@ namespace MediaBrowser.ServerApplication
             }
             }
         }
         }
 
 
+        private void LocalizeText()
+        {
+            _uiCulture = _configurationManager.Configuration.UICulture;
+
+            cmdExit.Text = _localization.GetLocalizedString("LabelExit");
+            cmdCommunity.Text = _localization.GetLocalizedString("LabelVisitCommunity");
+            cmdGtihub.Text = _localization.GetLocalizedString("LabelGithubWiki");
+            cmdSwagger.Text = _localization.GetLocalizedString("LabelSwagger");
+            cmdStandardDocs.Text = _localization.GetLocalizedString("LabelStandard");
+            cmdApiDocs.Text = _localization.GetLocalizedString("LabelViewApiDocumentation");
+            cmdBrowse.Text = _localization.GetLocalizedString("LabelBrowseLibrary");
+            cmdConfigure.Text = _localization.GetLocalizedString("LabelConfigureMediaBrowser");
+            cmdLibraryExplorer.Text = _localization.GetLocalizedString("LabelOpenLibraryViewer");
+            cmdRestart.Text = _localization.GetLocalizedString("LabelRestartServer");
+            cmdLogWindow.Text = _localization.GetLocalizedString("LabelShowLogWindow");
+        }
+
+        private string _uiCulture;
         /// <summary>
         /// <summary>
         /// Handles the ConfigurationUpdated event of the Instance control.
         /// Handles the ConfigurationUpdated event of the Instance control.
         /// </summary>
         /// </summary>
@@ -226,6 +244,12 @@ namespace MediaBrowser.ServerApplication
         /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
         /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
         void Instance_ConfigurationUpdated(object sender, EventArgs e)
         void Instance_ConfigurationUpdated(object sender, EventArgs e)
         {
         {
+            if (!string.Equals(_configurationManager.Configuration.UICulture, _uiCulture,
+                    StringComparison.OrdinalIgnoreCase))
+            {
+                LocalizeText();
+            }
+
             Action action = () =>
             Action action = () =>
             {
             {
                 var isLogWindowOpen = _logForm != null;
                 var isLogWindowOpen = _logForm != null;
@@ -307,7 +331,7 @@ namespace MediaBrowser.ServerApplication
 
 
         void cmdLibraryExplorer_Click(object sender, EventArgs e)
         void cmdLibraryExplorer_Click(object sender, EventArgs e)
         {
         {
-            new LibraryViewer(_jsonSerializer, _userManager, _libraryManager, _displayPreferencesManager, _itemRepository).Show();
+            new LibraryViewer(_jsonSerializer, _userManager, _libraryManager, _itemRepository).Show();
         }
         }
 
 
         void cmdRestart_Click(object sender, EventArgs e)
         void cmdRestart_Click(object sender, EventArgs e)

+ 2 - 2
MediaBrowser.ServerApplication/Splash/SplashForm.Designer.cs

@@ -121,9 +121,9 @@
             this.lblStatus.Location = new System.Drawing.Point(3, 0);
             this.lblStatus.Location = new System.Drawing.Point(3, 0);
             this.lblStatus.MaximumSize = new System.Drawing.Size(0, 100);
             this.lblStatus.MaximumSize = new System.Drawing.Size(0, 100);
             this.lblStatus.Name = "lblStatus";
             this.lblStatus.Name = "lblStatus";
-            this.lblStatus.Size = new System.Drawing.Size(599, 59);
+            this.lblStatus.Size = new System.Drawing.Size(469, 59);
             this.lblStatus.TabIndex = 0;
             this.lblStatus.TabIndex = 0;
-            this.lblStatus.Text = "Loading Media Browser Server";
+            this.lblStatus.Text = "Loading Media Browser";
             this.lblStatus.UseWaitCursor = true;
             this.lblStatus.UseWaitCursor = true;
             // 
             // 
             // panel1
             // panel1

+ 257 - 171
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
@@ -15,6 +16,8 @@ using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using WebMarkupMin.Core.Minifiers;
+using WebMarkupMin.Core.Settings;
 
 
 namespace MediaBrowser.WebDashboard.Api
 namespace MediaBrowser.WebDashboard.Api
 {
 {
@@ -96,6 +99,7 @@ namespace MediaBrowser.WebDashboard.Api
         private readonly IServerConfigurationManager _serverConfigurationManager;
         private readonly IServerConfigurationManager _serverConfigurationManager;
 
 
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
+        private readonly ILocalizationManager _localization;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="DashboardService" /> class.
         /// Initializes a new instance of the <see cref="DashboardService" /> class.
@@ -103,11 +107,12 @@ namespace MediaBrowser.WebDashboard.Api
         /// <param name="appHost">The app host.</param>
         /// <param name="appHost">The app host.</param>
         /// <param name="serverConfigurationManager">The server configuration manager.</param>
         /// <param name="serverConfigurationManager">The server configuration manager.</param>
         /// <param name="fileSystem">The file system.</param>
         /// <param name="fileSystem">The file system.</param>
-        public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem)
+        public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization)
         {
         {
             _appHost = appHost;
             _appHost = appHost;
             _serverConfigurationManager = serverConfigurationManager;
             _serverConfigurationManager = serverConfigurationManager;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
+            _localization = localization;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -148,7 +153,7 @@ namespace MediaBrowser.WebDashboard.Api
         {
         {
             var page = ServerEntryPoint.Instance.PluginConfigurationPages.First(p => p.Name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
             var page = ServerEntryPoint.Instance.PluginConfigurationPages.First(p => p.Name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
 
 
-            return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), page.Plugin.AssemblyDateLastModified, null, MimeTypes.GetMimeType("page.html"), () => ModifyHtml(page.GetHtmlStream()));
+            return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), page.Plugin.AssemblyDateLastModified, null, MimeTypes.GetMimeType("page.html"), () => ModifyHtml(page.GetHtmlStream(), null));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -210,13 +215,16 @@ namespace MediaBrowser.WebDashboard.Api
 
 
             var contentType = MimeTypes.GetMimeType(path);
             var contentType = MimeTypes.GetMimeType(path);
 
 
+            var isHtml = IsHtml(path);
+            var localizationCulture = isHtml ? GetLocalizationCulture() : null;
+
             // Don't cache if not configured to do so
             // Don't cache if not configured to do so
             // But always cache images to simulate production
             // But always cache images to simulate production
-            if (!_serverConfigurationManager.Configuration.EnableDashboardResponseCaching && 
-                !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) && 
+            if (!_serverConfigurationManager.Configuration.EnableDashboardResponseCaching &&
+                !contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) &&
                 !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
                 !contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
             {
             {
-                return ResultFactory.GetResult(GetResourceStream(path).Result, contentType);
+                return ResultFactory.GetResult(GetResourceStream(path, isHtml, localizationCulture).Result, contentType);
             }
             }
 
 
             TimeSpan? cacheDuration = null;
             TimeSpan? cacheDuration = null;
@@ -230,17 +238,24 @@ namespace MediaBrowser.WebDashboard.Api
 
 
             var assembly = GetType().Assembly.GetName();
             var assembly = GetType().Assembly.GetName();
 
 
-            var cacheKey = (assembly.Version + path).GetMD5();
+            var cacheKey = (assembly.Version + (localizationCulture ?? string.Empty) + path).GetMD5();
 
 
-            return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path));
+            return ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, isHtml, localizationCulture));
+        }
+
+        private string GetLocalizationCulture()
+        {
+            return _serverConfigurationManager.Configuration.UICulture;
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets the resource stream.
         /// Gets the resource stream.
         /// </summary>
         /// </summary>
         /// <param name="path">The path.</param>
         /// <param name="path">The path.</param>
+        /// <param name="isHtml">if set to <c>true</c> [is HTML].</param>
+        /// <param name="localizationCulture">The localization culture.</param>
         /// <returns>Task{Stream}.</returns>
         /// <returns>Task{Stream}.</returns>
-        private async Task<Stream> GetResourceStream(string path)
+        private async Task<Stream> GetResourceStream(string path, bool isHtml, string localizationCulture)
         {
         {
             Stream resourceStream;
             Stream resourceStream;
 
 
@@ -259,13 +274,11 @@ namespace MediaBrowser.WebDashboard.Api
 
 
             if (resourceStream != null)
             if (resourceStream != null)
             {
             {
-                var isHtml = IsHtml(path);
-
                 // Don't apply any caching for html pages
                 // Don't apply any caching for html pages
                 // jQuery ajax doesn't seem to handle if-modified-since correctly
                 // jQuery ajax doesn't seem to handle if-modified-since correctly
                 if (isHtml)
                 if (isHtml)
                 {
                 {
-                    resourceStream = await ModifyHtml(resourceStream).ConfigureAwait(false);
+                    resourceStream = await ModifyHtml(resourceStream, localizationCulture).ConfigureAwait(false);
                 }
                 }
             }
             }
 
 
@@ -297,26 +310,48 @@ namespace MediaBrowser.WebDashboard.Api
         /// </summary>
         /// </summary>
         /// <param name="sourceStream">The source stream.</param>
         /// <param name="sourceStream">The source stream.</param>
         /// <returns>Task{Stream}.</returns>
         /// <returns>Task{Stream}.</returns>
-        internal async Task<Stream> ModifyHtml(Stream sourceStream)
+        private async Task<Stream> ModifyHtml(Stream sourceStream, string localizationCulture)
         {
         {
-            string html;
-
-            using (var memoryStream = new MemoryStream())
+            using (sourceStream)
             {
             {
-                await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
+                string html;
 
 
-                html = Encoding.UTF8.GetString(memoryStream.ToArray());
-            }
+                using (var memoryStream = new MemoryStream())
+                {
+                    await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
 
 
-            var version = GetType().Assembly.GetName().Version;
+                    html = Encoding.UTF8.GetString(memoryStream.ToArray());
 
 
-            html = html.Replace("<head>", "<head>" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version));
+                    if (!string.IsNullOrWhiteSpace(localizationCulture))
+                    {
+                        html = _localization.LocalizeDocument(html, localizationCulture, GetLocalizationToken);
+                    }
 
 
-            var bytes = Encoding.UTF8.GetBytes(html);
+                    try
+                    {
+                        var minifier = new HtmlMinifier(new HtmlMinificationSettings(true));
+
+                        html = minifier.Minify(html).MinifiedContent;
+                    }
+                    catch (Exception ex)
+                    {
+                        Logger.ErrorException("Error minifying html", ex);
+                    }
+                }
 
 
-            sourceStream.Dispose();
+                var version = GetType().Assembly.GetName().Version;
 
 
-            return new MemoryStream(bytes);
+                html = html.Replace("<head>", "<head>" + GetMetaTags() + GetCommonCss(version) + GetCommonJavascript(version));
+
+                var bytes = Encoding.UTF8.GetBytes(html);
+
+                return new MemoryStream(bytes);
+            }
+        }
+
+        private string GetLocalizationToken(string phrase)
+        {
+            return "${" + phrase + "}";
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -393,154 +428,200 @@ namespace MediaBrowser.WebDashboard.Api
         /// <returns>Task{Stream}.</returns>
         /// <returns>Task{Stream}.</returns>
         private async Task<Stream> GetAllJavascript()
         private async Task<Stream> GetAllJavascript()
         {
         {
-            var scriptFiles = new[]
-                                  {
-                                      "extensions.js",
-                                      "site.js",
-                                      "librarybrowser.js",
-                                      "librarylist.js",
-                                      "editorsidebar.js",
-                                      "librarymenu.js",
-                                      "chromecast.js",
-                                      "contextmenu.js",
-
-                                      "mediacontroller.js",
-                                      "mediaplayer.js",
-                                      "mediaplayer-video.js",
-
-                                      "ratingdialog.js",
-                                      "aboutpage.js",
-                                      "allusersettings.js",
-                                      "alphapicker.js",
-                                      "addpluginpage.js",
-                                      "advancedconfigurationpage.js",
-                                      "advancedpaths.js",
-                                      "advancedserversettings.js",
-                                      "metadataadvanced.js",
-                                      "appsplayback.js",
-                                      "appsweather.js",
-                                      "autoorganizetv.js",
-                                      "autoorganizelog.js",
-                                      "channels.js",
-                                      "channelitems.js",
-                                      "dashboardinfo.js",
-                                      "dashboardpage.js",
-                                      "directorybrowser.js",
-                                      "dlnaprofile.js",
-                                      "dlnaprofiles.js",
-                                      "dlnasettings.js",
-                                      "editcollectionitems.js",
-                                      "edititemmetadata.js",
-                                      "edititempeople.js",
-                                      "edititemimages.js",
-                                      "encodingsettings.js",
-                                      "gamesrecommendedpage.js",
-                                      "gamesystemspage.js",
-                                      "gamespage.js",
-                                      "gamegenrepage.js",
-                                      "gamestudiospage.js",
-                                      "indexpage.js",
-                                      "itembynamedetailpage.js",
-                                      "itemdetailpage.js",
-                                      "itemgallery.js",
-                                      "itemlistpage.js",
-                                      "librarypathmapping.js",
-                                      "libraryreport.js",
-                                      "librarysettings.js",
-                                      "livetvchannel.js",
-                                      "livetvchannels.js",
-                                      "livetvguide.js",
-                                      "livetvnewrecording.js",
-                                      "livetvprogram.js",
-                                      "livetvrecording.js",
-                                      "livetvrecordinglist.js",
-                                      "livetvrecordings.js",
-                                      "livetvtimer.js",
-                                      "livetvseriestimer.js",
-                                      "livetvseriestimers.js",
-                                      "livetvsettings.js",
-                                      "livetvsuggested.js",
-                                      "livetvstatus.js",
-                                      "livetvtimers.js",
-                                      "loginpage.js",
-                                      "logpage.js",
-                                      "medialibrarypage.js",
-                                      "metadataconfigurationpage.js",
-                                      "metadataimagespage.js",
-                                      "moviegenres.js",
-                                      "moviecollections.js",
-                                      "movies.js",
-                                      "movieslatest.js",
-                                      "moviepeople.js",
-                                      "moviesrecommended.js",
-                                      "moviestudios.js",
-                                      "movietrailers.js",
-                                      "musicalbums.js",
-                                      "musicalbumartists.js",
-                                      "musicartists.js",
-                                      "musicgenres.js",
-                                      "musicrecommended.js",
-                                      "musicvideos.js",
-                                      "notifications.js",
-                                      "playlist.js",
-                                      "plugincatalogpage.js",
-                                      "pluginspage.js",
-                                      "pluginupdatespage.js",
-                                      "remotecontrol.js",
-                                      "scheduledtaskpage.js",
-                                      "scheduledtaskspage.js",
-                                      "search.js",
-                                      "songs.js",
-                                      "supporterkeypage.js",
-                                      "supporterpage.js",
-                                      "episodes.js",
-                                      "tvgenres.js",
-                                      "tvlatest.js",
-                                      "tvpeople.js",
-                                      "tvrecommended.js",
-                                      "tvshows.js",
-                                      "tvstudios.js",
-                                      "tvupcoming.js",
-                                      "useredit.js",
-                                      "userpassword.js",
-                                      "userimagepage.js",
-                                      "userprofilespage.js",
-                                      "usersettings.js",
-                                      "userparentalcontrol.js",
-                                      "wizardfinishpage.js",
-                                      "wizardimagesettings.js",
-                                      "wizardservice.js",
-                                      "wizardstartpage.js",
-                                      "wizardsettings.js",
-                                      "wizarduserpage.js"
-                                  };
-
             var memoryStream = new MemoryStream();
             var memoryStream = new MemoryStream();
             var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
             var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
 
 
+            // jQuery + jQuery mobile
             await AppendResource(memoryStream, "thirdparty/jquery-2.0.3.min.js", newLineBytes).ConfigureAwait(false);
             await AppendResource(memoryStream, "thirdparty/jquery-2.0.3.min.js", newLineBytes).ConfigureAwait(false);
             await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.2/jquery.mobile-1.4.2.min.js", newLineBytes).ConfigureAwait(false);
             await AppendResource(memoryStream, "thirdparty/jquerymobile-1.4.2/jquery.mobile-1.4.2.min.js", newLineBytes).ConfigureAwait(false);
 
 
+            await AppendLocalization(memoryStream).ConfigureAwait(false);
+            await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
+
+            // Write the version string for the dashboard comparison function
             var versionString = string.Format("window.dashboardVersion='{0}';", _appHost.ApplicationVersion);
             var versionString = string.Format("window.dashboardVersion='{0}';", _appHost.ApplicationVersion);
             var versionBytes = Encoding.UTF8.GetBytes(versionString);
             var versionBytes = Encoding.UTF8.GetBytes(versionString);
 
 
             await memoryStream.WriteAsync(versionBytes, 0, versionBytes.Length).ConfigureAwait(false);
             await memoryStream.WriteAsync(versionBytes, 0, versionBytes.Length).ConfigureAwait(false);
             await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
             await memoryStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
 
 
-            await AppendResource(memoryStream, "thirdparty/autonumeric/autoNumeric.min.js", newLineBytes).ConfigureAwait(false);
-            
+            var builder = new StringBuilder();
             var assembly = GetType().Assembly;
             var assembly = GetType().Assembly;
-            await AppendResource(assembly, memoryStream, "MediaBrowser.WebDashboard.ApiClient.js", newLineBytes).ConfigureAwait(false);
+            using (var stream = assembly.GetManifestResourceStream("MediaBrowser.WebDashboard.ApiClient.js"))
+            {
+                using (var streamReader = new StreamReader(stream))
+                {
+                    var text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
+                    builder.Append(text);
+                    builder.Append(Environment.NewLine);
+                }
+            }
+
+            foreach (var file in GetScriptFiles())
+            {
+                var path = GetDashboardResourcePath("scripts/" + file);
+
+                using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+                {
+                    using (var streamReader = new StreamReader(fs))
+                    {
+                        var text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
+                        builder.Append(text);
+                        builder.Append(Environment.NewLine);
+                    }
+                }
+            }
+
+            var js = builder.ToString();
+
+            try
+            {
+                var result = new CrockfordJsMinifier().Minify(js, false, Encoding.UTF8);
 
 
-            foreach (var file in scriptFiles)
+                js = result.MinifiedContent;
+            }
+            catch (Exception ex)
             {
             {
-                await AppendResource(memoryStream, "scripts/" + file, newLineBytes).ConfigureAwait(false);
+                Logger.ErrorException("Error minifying javascript", ex);
             }
             }
+            
+            var bytes = Encoding.UTF8.GetBytes(js);
+            await memoryStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
 
 
             memoryStream.Position = 0;
             memoryStream.Position = 0;
             return memoryStream;
             return memoryStream;
         }
         }
 
 
+        private IEnumerable<string> GetScriptFiles()
+        {
+            return new[]
+                            {
+                                "extensions.js",
+                                "site.js",
+                                "librarybrowser.js",
+                                "librarylist.js",
+                                "editorsidebar.js",
+                                "librarymenu.js",
+                                "chromecast.js",
+                                "contextmenu.js",
+
+                                "mediacontroller.js",
+                                "mediaplayer.js",
+                                "mediaplayer-video.js",
+
+                                "ratingdialog.js",
+                                "aboutpage.js",
+                                "allusersettings.js",
+                                "alphapicker.js",
+                                "addpluginpage.js",
+                                "advancedconfigurationpage.js",
+                                "advancedpaths.js",
+                                "advancedserversettings.js",
+                                "metadataadvanced.js",
+                                "appsplayback.js",
+                                "appsweather.js",
+                                "autoorganizetv.js",
+                                "autoorganizelog.js",
+                                "channels.js",
+                                "channelitems.js",
+                                "dashboardinfo.js",
+                                "dashboardpage.js",
+                                "directorybrowser.js",
+                                "dlnaprofile.js",
+                                "dlnaprofiles.js",
+                                "dlnasettings.js",
+                                "editcollectionitems.js",
+                                "edititemmetadata.js",
+                                "edititempeople.js",
+                                "edititemimages.js",
+                                "encodingsettings.js",
+                                "gamesrecommendedpage.js",
+                                "gamesystemspage.js",
+                                "gamespage.js",
+                                "gamegenrepage.js",
+                                "gamestudiospage.js",
+                                "indexpage.js",
+                                "itembynamedetailpage.js",
+                                "itemdetailpage.js",
+                                "itemgallery.js",
+                                "itemlistpage.js",
+                                "librarypathmapping.js",
+                                "libraryreport.js",
+                                "librarysettings.js",
+                                "livetvchannel.js",
+                                "livetvchannels.js",
+                                "livetvguide.js",
+                                "livetvnewrecording.js",
+                                "livetvprogram.js",
+                                "livetvrecording.js",
+                                "livetvrecordinglist.js",
+                                "livetvrecordings.js",
+                                "livetvtimer.js",
+                                "livetvseriestimer.js",
+                                "livetvseriestimers.js",
+                                "livetvsettings.js",
+                                "livetvsuggested.js",
+                                "livetvstatus.js",
+                                "livetvtimers.js",
+                                "loginpage.js",
+                                "logpage.js",
+                                "medialibrarypage.js",
+                                "metadataconfigurationpage.js",
+                                "metadataimagespage.js",
+                                "moviegenres.js",
+                                "moviecollections.js",
+                                "movies.js",
+                                "movieslatest.js",
+                                "moviepeople.js",
+                                "moviesrecommended.js",
+                                "moviestudios.js",
+                                "movietrailers.js",
+                                "musicalbums.js",
+                                "musicalbumartists.js",
+                                "musicartists.js",
+                                "musicgenres.js",
+                                "musicrecommended.js",
+                                "musicvideos.js",
+                                "notifications.js",
+                                "playlist.js",
+                                "plugincatalogpage.js",
+                                "pluginspage.js",
+                                "pluginupdatespage.js",
+                                "remotecontrol.js",
+                                "scheduledtaskpage.js",
+                                "scheduledtaskspage.js",
+                                "search.js",
+                                "songs.js",
+                                "supporterkeypage.js",
+                                "supporterpage.js",
+                                "episodes.js",
+                                "tvgenres.js",
+                                "tvlatest.js",
+                                "tvpeople.js",
+                                "tvrecommended.js",
+                                "tvshows.js",
+                                "tvstudios.js",
+                                "tvupcoming.js",
+                                "useredit.js",
+                                "userpassword.js",
+                                "userimagepage.js",
+                                "userprofilespage.js",
+                                "usersettings.js",
+                                "userparentalcontrol.js",
+                                "wizardfinishpage.js",
+                                "wizardimagesettings.js",
+                                "wizardservice.js",
+                                "wizardstartpage.js",
+                                "wizardsettings.js",
+                                "wizarduserpage.js"
+                            };
+        }
+
+        private async Task AppendLocalization(Stream stream)
+        {
+
+        }
+
         /// <summary>
         /// <summary>
         /// Gets all CSS.
         /// Gets all CSS.
         /// </summary>
         /// </summary>
@@ -568,35 +649,40 @@ namespace MediaBrowser.WebDashboard.Api
                                       "icons.css"
                                       "icons.css"
                                   };
                                   };
 
 
-            var memoryStream = new MemoryStream();
-
-            var newLineBytes = Encoding.UTF8.GetBytes(Environment.NewLine);
+            var builder = new StringBuilder();
 
 
             foreach (var file in files)
             foreach (var file in files)
             {
             {
-                await AppendResource(memoryStream, "css/" + file, newLineBytes).ConfigureAwait(false);
+                var path = GetDashboardResourcePath("css/" + file);
+
+                using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+                {
+                    using (var streamReader = new StreamReader(fs))
+                    {
+                        var text = await streamReader.ReadToEndAsync().ConfigureAwait(false);
+                        builder.Append(text);
+                        builder.Append(Environment.NewLine);
+                    }
+                }
             }
             }
-            
-            memoryStream.Position = 0;
-            return memoryStream;
-        }
 
 
-        /// <summary>
-        /// Appends the resource.
-        /// </summary>
-        /// <param name="assembly">The assembly.</param>
-        /// <param name="outputStream">The output stream.</param>
-        /// <param name="path">The path.</param>
-        /// <param name="newLineBytes">The new line bytes.</param>
-        /// <returns>Task.</returns>
-        private async Task AppendResource(Assembly assembly, Stream outputStream, string path, byte[] newLineBytes)
-        {
-            using (var stream = assembly.GetManifestResourceStream(path))
+            var css = builder.ToString();
+
+            try
             {
             {
-                await stream.CopyToAsync(outputStream).ConfigureAwait(false);
+                var result = new KristensenCssMinifier().Minify(builder.ToString(), false, Encoding.UTF8);
 
 
-                await outputStream.WriteAsync(newLineBytes, 0, newLineBytes.Length).ConfigureAwait(false);
+                //css = result.MinifiedContent;
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error minifying css", ex);
             }
             }
+
+            var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(css));
+
+            memoryStream.Position = 0;
+            return memoryStream;
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 7 - 6
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -57,6 +57,9 @@
     <Reference Include="ServiceStack.Interfaces">
     <Reference Include="ServiceStack.Interfaces">
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
     </Reference>
     </Reference>
+    <Reference Include="WebMarkupMin.Core">
+      <HintPath>..\packages\WebMarkupMin.Core.0.8.18\lib\net40\WebMarkupMin.Core.dll</HintPath>
+    </Reference>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">
     <Compile Include="..\SharedVersion.cs">
@@ -641,9 +644,6 @@
     <Content Include="dashboard-ui\livetvseriestimers.html">
     <Content Include="dashboard-ui\livetvseriestimers.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\autonumeric\autoNumeric.min.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\thirdparty\jquery-2.0.3.min.js">
     <Content Include="dashboard-ui\thirdparty\jquery-2.0.3.min.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -1477,9 +1477,6 @@
     <Content Include="dashboard-ui\scripts\tvstudios.js">
     <Content Include="dashboard-ui\scripts\tvstudios.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\autonumeric\autoNumeric.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\thirdparty\jstree1.0\jquery.jstree.js">
     <Content Include="dashboard-ui\thirdparty\jstree1.0\jquery.jstree.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -1970,6 +1967,7 @@
     </Content>
     </Content>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
+    <None Include="app.config" />
     <None Include="dashboard-ui\css\fonts\OpenSans-ExtraBold.woff">
     <None Include="dashboard-ui\css\fonts\OpenSans-ExtraBold.woff">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
     </None>
@@ -1983,6 +1981,9 @@
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
     </None>
     <None Include="packages.config" />
     <None Include="packages.config" />
+    <None Include="WebMarkupMin.Configuration.xsd">
+      <SubType>Designer</SubType>
+    </None>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup />
   <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

+ 30 - 0
MediaBrowser.WebDashboard/app.config

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <configSections>
+		<sectionGroup name="webMarkupMin">
+			<section name="core" type="WebMarkupMin.Core.Configuration.CoreConfiguration, WebMarkupMin.Core" />
+		</sectionGroup>
+	</configSections>
+  <webMarkupMin xmlns="http://tempuri.org/WebMarkupMin.Configuration.xsd">
+		<core>
+			<css>
+				<minifiers>
+					<add name="NullCssMinifier" displayName="Null CSS Minifier" type="WebMarkupMin.Core.Minifiers.NullCssMinifier, WebMarkupMin.Core" />
+					<add name="KristensenCssMinifier" displayName="Mads Kristensen's CSS minifier" type="WebMarkupMin.Core.Minifiers.KristensenCssMinifier, WebMarkupMin.Core" />
+				</minifiers>
+			</css>
+			<js>
+				<minifiers>
+					<add name="NullJsMinifier" displayName="Null JS Minifier" type="WebMarkupMin.Core.Minifiers.NullJsMinifier, WebMarkupMin.Core" />
+					<add name="CrockfordJsMinifier" displayName="Douglas Crockford's JS Minifier" type="WebMarkupMin.Core.Minifiers.CrockfordJsMinifier, WebMarkupMin.Core" />
+				</minifiers>
+			</js>
+			<logging>
+				<loggers>
+				  <add name="NullLogger" displayName="Null Logger" type="WebMarkupMin.Core.Loggers.NullLogger, WebMarkupMin.Core" />
+				  <add name="ThrowExceptionLogger" displayName="Throw exception logger" type="WebMarkupMin.Core.Loggers.ThrowExceptionLogger, WebMarkupMin.Core" />
+				</loggers>
+			</logging>
+		</core>
+	</webMarkupMin>
+</configuration>

+ 1 - 0
MediaBrowser.WebDashboard/packages.config

@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
 <packages>
   <package id="MediaBrowser.ApiClient.Javascript" version="3.0.249" targetFramework="net45" />
   <package id="MediaBrowser.ApiClient.Javascript" version="3.0.249" targetFramework="net45" />
+  <package id="WebMarkupMin.Core" version="0.8.18" targetFramework="net45" />
 </packages>
 </packages>