LocalizationManager.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. using MediaBrowser.Model.Extensions;
  2. using MediaBrowser.Controller.Configuration;
  3. using MediaBrowser.Model.Entities;
  4. using MediaBrowser.Model.Globalization;
  5. using MediaBrowser.Model.Serialization;
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.Generic;
  9. using System.Globalization;
  10. using System.IO;
  11. using System.Linq;
  12. using MediaBrowser.Model.IO;
  13. using MediaBrowser.Model.Logging;
  14. using MediaBrowser.Model.Reflection;
  15. namespace Emby.Server.Implementations.Localization
  16. {
  17. /// <summary>
  18. /// Class LocalizationManager
  19. /// </summary>
  20. public class LocalizationManager : ILocalizationManager
  21. {
  22. /// <summary>
  23. /// The _configuration manager
  24. /// </summary>
  25. private readonly IServerConfigurationManager _configurationManager;
  26. /// <summary>
  27. /// The us culture
  28. /// </summary>
  29. private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
  30. private readonly ConcurrentDictionary<string, Dictionary<string, ParentalRating>> _allParentalRatings =
  31. new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
  32. private readonly IFileSystem _fileSystem;
  33. private readonly IJsonSerializer _jsonSerializer;
  34. private readonly ILogger _logger;
  35. private readonly IAssemblyInfo _assemblyInfo;
  36. private readonly ITextLocalizer _textLocalizer;
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="LocalizationManager" /> class.
  39. /// </summary>
  40. /// <param name="configurationManager">The configuration manager.</param>
  41. /// <param name="fileSystem">The file system.</param>
  42. /// <param name="jsonSerializer">The json serializer.</param>
  43. public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, IAssemblyInfo assemblyInfo, ITextLocalizer textLocalizer)
  44. {
  45. _configurationManager = configurationManager;
  46. _fileSystem = fileSystem;
  47. _jsonSerializer = jsonSerializer;
  48. _logger = logger;
  49. _assemblyInfo = assemblyInfo;
  50. _textLocalizer = textLocalizer;
  51. ExtractAll();
  52. }
  53. private void ExtractAll()
  54. {
  55. var type = GetType();
  56. var resourcePath = type.Namespace + ".Ratings.";
  57. var localizationPath = LocalizationPath;
  58. _fileSystem.CreateDirectory(localizationPath);
  59. var existingFiles = GetRatingsFiles(localizationPath)
  60. .Select(Path.GetFileName)
  61. .ToList();
  62. // Extract from the assembly
  63. foreach (var resource in _assemblyInfo
  64. .GetManifestResourceNames(type)
  65. .Where(i => i.StartsWith(resourcePath)))
  66. {
  67. var filename = "ratings-" + resource.Substring(resourcePath.Length);
  68. if (!existingFiles.Contains(filename))
  69. {
  70. using (var stream = _assemblyInfo.GetManifestResourceStream(type, resource))
  71. {
  72. var target = Path.Combine(localizationPath, filename);
  73. _logger.Info("Extracting ratings to {0}", target);
  74. using (var fs = _fileSystem.GetFileStream(target, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
  75. {
  76. stream.CopyTo(fs);
  77. }
  78. }
  79. }
  80. }
  81. foreach (var file in GetRatingsFiles(localizationPath))
  82. {
  83. LoadRatings(file);
  84. }
  85. }
  86. private List<string> GetRatingsFiles(string directory)
  87. {
  88. return _fileSystem.GetFilePaths(directory, false)
  89. .Where(i => string.Equals(Path.GetExtension(i), ".txt", StringComparison.OrdinalIgnoreCase))
  90. .Where(i => Path.GetFileName(i).StartsWith("ratings-", StringComparison.OrdinalIgnoreCase))
  91. .ToList();
  92. }
  93. /// <summary>
  94. /// Gets the localization path.
  95. /// </summary>
  96. /// <value>The localization path.</value>
  97. public string LocalizationPath
  98. {
  99. get
  100. {
  101. return Path.Combine(_configurationManager.ApplicationPaths.ProgramDataPath, "localization");
  102. }
  103. }
  104. public string RemoveDiacritics(string text)
  105. {
  106. return _textLocalizer.RemoveDiacritics(text);
  107. }
  108. public string NormalizeFormKD(string text)
  109. {
  110. return _textLocalizer.NormalizeFormKD(text);
  111. }
  112. /// <summary>
  113. /// Gets the cultures.
  114. /// </summary>
  115. /// <returns>IEnumerable{CultureDto}.</returns>
  116. public List<CultureDto> GetCultures()
  117. {
  118. var type = GetType();
  119. var path = type.Namespace + ".iso6392.txt";
  120. var list = new List<CultureDto>();
  121. using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
  122. {
  123. using (var reader = new StreamReader(stream))
  124. {
  125. while (!reader.EndOfStream)
  126. {
  127. var line = reader.ReadLine();
  128. if (!string.IsNullOrWhiteSpace(line))
  129. {
  130. var parts = line.Split('|');
  131. if (parts.Length == 5)
  132. {
  133. list.Add(new CultureDto
  134. {
  135. DisplayName = parts[3],
  136. Name = parts[3],
  137. ThreeLetterISOLanguageName = parts[0],
  138. TwoLetterISOLanguageName = parts[2]
  139. });
  140. }
  141. }
  142. }
  143. }
  144. }
  145. return list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
  146. !string.IsNullOrWhiteSpace(i.DisplayName) &&
  147. !string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) &&
  148. !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToList();
  149. }
  150. /// <summary>
  151. /// Gets the countries.
  152. /// </summary>
  153. /// <returns>IEnumerable{CountryInfo}.</returns>
  154. public List<CountryInfo> GetCountries()
  155. {
  156. var type = GetType();
  157. var path = type.Namespace + ".countries.json";
  158. using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
  159. {
  160. return _jsonSerializer.DeserializeFromStream<List<CountryInfo>>(stream);
  161. }
  162. }
  163. /// <summary>
  164. /// Gets the parental ratings.
  165. /// </summary>
  166. /// <returns>IEnumerable{ParentalRating}.</returns>
  167. public IEnumerable<ParentalRating> GetParentalRatings()
  168. {
  169. return GetParentalRatingsDictionary().Values.ToList();
  170. }
  171. /// <summary>
  172. /// Gets the parental ratings dictionary.
  173. /// </summary>
  174. /// <returns>Dictionary{System.StringParentalRating}.</returns>
  175. private Dictionary<string, ParentalRating> GetParentalRatingsDictionary()
  176. {
  177. var countryCode = _configurationManager.Configuration.MetadataCountryCode;
  178. if (string.IsNullOrEmpty(countryCode))
  179. {
  180. countryCode = "us";
  181. }
  182. var ratings = GetRatings(countryCode);
  183. if (ratings == null)
  184. {
  185. ratings = GetRatings("us");
  186. }
  187. return ratings;
  188. }
  189. /// <summary>
  190. /// Gets the ratings.
  191. /// </summary>
  192. /// <param name="countryCode">The country code.</param>
  193. private Dictionary<string, ParentalRating> GetRatings(string countryCode)
  194. {
  195. Dictionary<string, ParentalRating> value;
  196. _allParentalRatings.TryGetValue(countryCode, out value);
  197. return value;
  198. }
  199. /// <summary>
  200. /// Loads the ratings.
  201. /// </summary>
  202. /// <param name="file">The file.</param>
  203. /// <returns>Dictionary{System.StringParentalRating}.</returns>
  204. private void LoadRatings(string file)
  205. {
  206. var dict = _fileSystem.ReadAllLines(file).Select(i =>
  207. {
  208. if (!string.IsNullOrWhiteSpace(i))
  209. {
  210. var parts = i.Split(',');
  211. if (parts.Length == 2)
  212. {
  213. int value;
  214. if (int.TryParse(parts[1], NumberStyles.Integer, UsCulture, out value))
  215. {
  216. return new ParentalRating { Name = parts[0], Value = value };
  217. }
  218. }
  219. }
  220. return null;
  221. })
  222. .Where(i => i != null)
  223. .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
  224. var countryCode = _fileSystem.GetFileNameWithoutExtension(file)
  225. .Split('-')
  226. .Last();
  227. _allParentalRatings.TryAdd(countryCode, dict);
  228. }
  229. private readonly string[] _unratedValues = {"n/a", "unrated", "not rated"};
  230. /// <summary>
  231. /// Gets the rating level.
  232. /// </summary>
  233. public int? GetRatingLevel(string rating)
  234. {
  235. if (string.IsNullOrEmpty(rating))
  236. {
  237. throw new ArgumentNullException("rating");
  238. }
  239. if (_unratedValues.Contains(rating, StringComparer.OrdinalIgnoreCase))
  240. {
  241. return null;
  242. }
  243. // Fairly common for some users to have "Rated R" in their rating field
  244. rating = rating.Replace("Rated ", string.Empty, StringComparison.OrdinalIgnoreCase);
  245. var ratingsDictionary = GetParentalRatingsDictionary();
  246. ParentalRating value;
  247. if (!ratingsDictionary.TryGetValue(rating, out value))
  248. {
  249. // If we don't find anything check all ratings systems
  250. foreach (var dictionary in _allParentalRatings.Values)
  251. {
  252. if (dictionary.TryGetValue(rating, out value))
  253. {
  254. return value.Value;
  255. }
  256. }
  257. }
  258. return value == null ? (int?)null : value.Value;
  259. }
  260. public string GetLocalizedString(string phrase)
  261. {
  262. return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture);
  263. }
  264. public string GetLocalizedString(string phrase, string culture)
  265. {
  266. var dictionary = GetLocalizationDictionary(culture);
  267. string value;
  268. if (dictionary.TryGetValue(phrase, out value))
  269. {
  270. return value;
  271. }
  272. return phrase;
  273. }
  274. private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
  275. new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
  276. public Dictionary<string, string> GetLocalizationDictionary(string culture)
  277. {
  278. const string prefix = "Core";
  279. var key = prefix + culture;
  280. return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "core.json"));
  281. }
  282. private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
  283. {
  284. var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  285. var namespaceName = GetType().Namespace + "." + prefix;
  286. CopyInto(dictionary, namespaceName + "." + baseFilename);
  287. CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture));
  288. return dictionary;
  289. }
  290. private void CopyInto(IDictionary<string, string> dictionary, string resourcePath)
  291. {
  292. using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), resourcePath))
  293. {
  294. if (stream != null)
  295. {
  296. var dict = _jsonSerializer.DeserializeFromStream<Dictionary<string, string>>(stream);
  297. foreach (var key in dict.Keys)
  298. {
  299. dictionary[key] = dict[key];
  300. }
  301. }
  302. }
  303. }
  304. private string GetResourceFilename(string culture)
  305. {
  306. var parts = culture.Split('-');
  307. if (parts.Length == 2)
  308. {
  309. culture = parts[0].ToLower() + "-" + parts[1].ToUpper();
  310. }
  311. else
  312. {
  313. culture = culture.ToLower();
  314. }
  315. return culture + ".json";
  316. }
  317. public IEnumerable<LocalizatonOption> GetLocalizationOptions()
  318. {
  319. return new List<LocalizatonOption>
  320. {
  321. new LocalizatonOption{ Name="Arabic", Value="ar"},
  322. new LocalizatonOption{ Name="Bulgarian (Bulgaria)", Value="bg-BG"},
  323. new LocalizatonOption{ Name="Catalan", Value="ca"},
  324. new LocalizatonOption{ Name="Chinese Simplified", Value="zh-CN"},
  325. new LocalizatonOption{ Name="Chinese Traditional", Value="zh-TW"},
  326. new LocalizatonOption{ Name="Croatian", Value="hr"},
  327. new LocalizatonOption{ Name="Czech", Value="cs"},
  328. new LocalizatonOption{ Name="Danish", Value="da"},
  329. new LocalizatonOption{ Name="Dutch", Value="nl"},
  330. new LocalizatonOption{ Name="English (United Kingdom)", Value="en-GB"},
  331. new LocalizatonOption{ Name="English (United States)", Value="en-us"},
  332. new LocalizatonOption{ Name="Finnish", Value="fi"},
  333. new LocalizatonOption{ Name="French", Value="fr"},
  334. new LocalizatonOption{ Name="French (Canada)", Value="fr-CA"},
  335. new LocalizatonOption{ Name="German", Value="de"},
  336. new LocalizatonOption{ Name="Greek", Value="el"},
  337. new LocalizatonOption{ Name="Hebrew", Value="he"},
  338. new LocalizatonOption{ Name="Hungarian", Value="hu"},
  339. new LocalizatonOption{ Name="Indonesian", Value="id"},
  340. new LocalizatonOption{ Name="Italian", Value="it"},
  341. new LocalizatonOption{ Name="Kazakh", Value="kk"},
  342. new LocalizatonOption{ Name="Norwegian Bokmål", Value="nb"},
  343. new LocalizatonOption{ Name="Persian", Value="fa"},
  344. new LocalizatonOption{ Name="Polish", Value="pl"},
  345. new LocalizatonOption{ Name="Portuguese (Brazil)", Value="pt-BR"},
  346. new LocalizatonOption{ Name="Portuguese (Portugal)", Value="pt-PT"},
  347. new LocalizatonOption{ Name="Russian", Value="ru"},
  348. new LocalizatonOption{ Name="Slovenian (Slovenia)", Value="sl-SI"},
  349. new LocalizatonOption{ Name="Spanish", Value="es-ES"},
  350. new LocalizatonOption{ Name="Spanish (Mexico)", Value="es-MX"},
  351. new LocalizatonOption{ Name="Swedish", Value="sv"},
  352. new LocalizatonOption{ Name="Turkish", Value="tr"},
  353. new LocalizatonOption{ Name="Ukrainian", Value="uk"},
  354. new LocalizatonOption{ Name="Vietnamese", Value="vi"}
  355. }.OrderBy(i => i.Name);
  356. }
  357. }
  358. public interface ITextLocalizer
  359. {
  360. string RemoveDiacritics(string text);
  361. string NormalizeFormKD(string text);
  362. }
  363. }