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 +