diff --git a/MediaBrowser.Api/LocalizationService.cs b/MediaBrowser.Api/LocalizationService.cs
index 60270a3895..be86090ac4 100644
--- a/MediaBrowser.Api/LocalizationService.cs
+++ b/MediaBrowser.Api/LocalizationService.cs
@@ -31,6 +31,14 @@ namespace MediaBrowser.Api
{
}
+ ///
+ /// Class ParentalRatings
+ ///
+ [Route("/Localization/Options", "GET", Summary = "Gets localization options")]
+ public class GetLocalizationOptions : IReturn>
+ {
+ }
+
///
/// Class CulturesService
///
@@ -62,6 +70,13 @@ namespace MediaBrowser.Api
return ToOptimizedSerializedResultUsingCache(result);
}
+ public object Get(GetLocalizationOptions request)
+ {
+ var result = _localization.GetLocalizationOptions().ToList();
+
+ return ToOptimizedSerializedResultUsingCache(result);
+ }
+
///
/// Gets the specified request.
///
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 96b36ac7fc..1a190b75e0 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Playback.Hls
? 0
: ((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);
diff --git a/MediaBrowser.Controller/Localization/ILocalizationManager.cs b/MediaBrowser.Controller/Localization/ILocalizationManager.cs
index dde2f78781..5993c7ac56 100644
--- a/MediaBrowser.Controller/Localization/ILocalizationManager.cs
+++ b/MediaBrowser.Controller/Localization/ILocalizationManager.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
+using System;
using System.Collections.Generic;
-using System.Threading.Tasks;
namespace MediaBrowser.Controller.Localization
{
@@ -31,5 +31,35 @@ namespace MediaBrowser.Controller.Localization
/// The rating.
/// System.Int32.
int? GetRatingLevel(string rating);
+
+ ///
+ /// Gets the localized string.
+ ///
+ /// The phrase.
+ /// The culture.
+ /// System.String.
+ string GetLocalizedString(string phrase, string culture);
+
+ ///
+ /// Gets the localized string.
+ ///
+ /// The phrase.
+ /// System.String.
+ string GetLocalizedString(string phrase);
+
+ ///
+ /// Localizes the document.
+ ///
+ /// The document.
+ /// The culture.
+ /// The token builder.
+ /// System.String.
+ string LocalizeDocument(string document, string culture, Func tokenBuilder);
+
+ ///
+ /// Gets the localization options.
+ ///
+ /// IEnumerable{LocalizatonOption}.
+ IEnumerable GetLocalizationOptions();
}
}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index d0caa3ad20..932d5d63dd 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -218,6 +218,8 @@ namespace MediaBrowser.Model.Configuration
public string ServerName { get; set; }
public string WanDdns { get; set; }
+ public string UICulture { get; set; }
+
public DlnaOptions DlnaOptions { get; set; }
///
@@ -281,6 +283,8 @@ namespace MediaBrowser.Model.Configuration
MetadataOptions = options.ToArray();
DlnaOptions = new DlnaOptions();
+
+ UICulture = "en-us";
}
}
diff --git a/MediaBrowser.Model/Globalization/CountryInfo.cs b/MediaBrowser.Model/Globalization/CountryInfo.cs
index 16aea8436b..9f5f00d80e 100644
--- a/MediaBrowser.Model/Globalization/CountryInfo.cs
+++ b/MediaBrowser.Model/Globalization/CountryInfo.cs
@@ -30,4 +30,10 @@ namespace MediaBrowser.Model.Globalization
/// The name of the three letter ISO region.
public string ThreeLetterISORegionName { get; set; }
}
+
+ public class LocalizatonOption
+ {
+ public string Name { get; set; }
+ public string Value { get; set; }
+ }
}
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
new file mode 100644
index 0000000000..44600aa353
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
@@ -0,0 +1,3 @@
+{
+ "LabelTest": "Text"
+}
\ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
index 0d428742ce..badf5f93c6 100644
--- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
+++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
@@ -1,9 +1,9 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Serialization;
using MoreLinq;
using System;
using System.Collections.Concurrent;
@@ -11,6 +11,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Reflection;
+using MediaBrowser.Common.Extensions;
namespace MediaBrowser.Server.Implementations.Localization
{
@@ -33,15 +35,17 @@ namespace MediaBrowser.Server.Implementations.Localization
new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase);
private readonly IFileSystem _fileSystem;
-
+ private readonly IJsonSerializer _jsonSerializer;
+
///
/// Initializes a new instance of the class.
///
/// The configuration manager.
- public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem)
+ public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
{
_configurationManager = configurationManager;
_fileSystem = fileSystem;
+ _jsonSerializer = jsonSerializer;
ExtractAll();
}
@@ -241,5 +245,108 @@ namespace MediaBrowser.Server.Implementations.Localization
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> _dictionaries =
+ new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase);
+
+ public Dictionary GetLocalizationDictionary(string culture)
+ {
+ const string prefix = "Server";
+ var key = prefix + culture;
+
+ return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "server.json"));
+ }
+
+ public Dictionary GetJavaScriptLocalizationDictionary(string culture)
+ {
+ const string prefix = "JavaScript";
+ var key = prefix + culture;
+
+ return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "javascript.json"));
+ }
+
+ private Dictionary GetDictionary(string prefix, string culture, string baseFilename)
+ {
+ var dictionary = new Dictionary(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 dictionary, string resourcePath, Assembly assembly)
+ {
+ using (var stream = assembly.GetManifestResourceStream(resourcePath))
+ {
+ if (stream != null)
+ {
+ var dict = _jsonSerializer.DeserializeFromStream>(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 GetLocalizationOptions()
+ {
+ return new List
+ {
+ new LocalizatonOption{ Name="English (United States)", Value="en-us"}
+ };
+ }
+
+ public string LocalizeDocument(string document, string culture, Func tokenBuilder)
+ {
+ foreach (var pair in GetLocalizationDictionary(culture).ToList())
+ {
+ var token = tokenBuilder(pair.Key);
+
+ document = document.Replace(token, pair.Value, StringComparison.Ordinal);
+ }
+
+ return document;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
new file mode 100644
index 0000000000..62a4183911
--- /dev/null
+++ b/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!"
+}
\ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 115bc01368..ac8dc94310 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -282,6 +282,8 @@
+
+
@@ -386,6 +388,7 @@
PreserveNewest
+