diff --git a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs index d3c036327..6af8abb7b 100644 --- a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs +++ b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs @@ -1,6 +1,9 @@ +using System.Collections.Generic; using FluentAssertions; +using Moq; using NUnit.Framework; using NzbDrone.Core.Annotations; +using NzbDrone.Core.Localization; using NzbDrone.Test.Common; using Sonarr.Http.ClientSchema; @@ -9,6 +12,16 @@ namespace NzbDrone.Api.Test.ClientSchemaTests [TestFixture] public class SchemaBuilderFixture : TestBase { + [SetUp] + public void Setup() + { + Mocker.GetMock() + .Setup(s => s.GetLocalizedString(It.IsAny(), It.IsAny>())) + .Returns>((s, d) => s); + + SchemaBuilder.Initialize(Mocker.Container); + } + [Test] public void should_return_field_for_every_property() { diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs index 9cc35560f..bf609e48c 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs @@ -320,7 +320,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests var result = Subject.Test(); - result.Errors.First().ErrorMessage.Should().Be("Old Hadouken client with unsupported API, need 5.1 or higher"); + result.Errors.Count.Should().Be(1); } } } diff --git a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs index 7b711a37d..a0a1896e0 100644 --- a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs +++ b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs @@ -42,6 +42,23 @@ namespace NzbDrone.Core.Annotations public string RequestAction { get; set; } } + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] + public class FieldTokenAttribute : Attribute + { + public FieldTokenAttribute(TokenField field, string label = "", string token = "", object value = null) + { + Label = label; + Field = field; + Token = token; + Value = value?.ToString(); + } + + public string Label { get; set; } + public TokenField Field { get; set; } + public string Token { get; set; } + public string Value { get; set; } + } + public class FieldSelectOption { public int Value { get; set; } @@ -85,4 +102,11 @@ namespace NzbDrone.Core.Annotations ApiKey, UserName } + + public enum TokenField + { + Label, + HelpText, + HelpTextWarning + } } diff --git a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs index d8ba934a0..223af1286 100644 --- a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs +++ b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -27,8 +28,9 @@ namespace NzbDrone.Core.Download.Clients.Aria2 IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; } @@ -233,14 +235,19 @@ namespace NzbDrone.Core.Download.Clients.Aria2 if (new Version(version) < new Version("1.34.0")) { - return new ValidationFailure(string.Empty, "Aria2 version should be at least 1.34.0. Version reported is {0}", version); + return new ValidationFailure(string.Empty, + _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { + { "clientName", "Aria2" }, { "requiredVersion", "1.34.0" }, { "reportedVersion", version } + })); } } catch (Exception ex) { _logger.Error(ex, "Failed to test Aria2"); - return new NzbDroneValidationFailure("Host", "Unable to connect to Aria2") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", "Aria2" } })) { DetailedDescription = ex.Message }; diff --git a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2Settings.cs b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2Settings.cs index e88bc4cc1..088f06f9e 100644 --- a/src/NzbDrone.Core/Download/Clients/Aria2/Aria2Settings.cs +++ b/src/NzbDrone.Core/Download/Clients/Aria2/Aria2Settings.cs @@ -32,13 +32,13 @@ namespace NzbDrone.Core.Download.Clients.Aria2 [FieldDefinition(1, Label = "Port", Type = FieldType.Number)] public int Port { get; set; } - [FieldDefinition(2, Label = "XML RPC Path", Type = FieldType.Textbox)] + [FieldDefinition(2, Label = "XmlRpcPath", Type = FieldType.Textbox)] public string RpcPath { get; set; } - [FieldDefinition(3, Label = "Use SSL", Type = FieldType.Checkbox)] + [FieldDefinition(3, Label = "UseSsl", Type = FieldType.Checkbox)] public bool UseSsl { get; set; } - [FieldDefinition(4, Label = "Secret token", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] + [FieldDefinition(4, Label = "SecretToken", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string SecretToken { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs index c9b45369e..62a9a4255 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackhole.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; @@ -29,8 +30,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _scanWatchFolder = scanWatchFolder; @@ -79,7 +81,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole return null; } - public override string Name => "Torrent Blackhole"; + public override string Name => _localizationService.GetLocalizedString("TorrentBlackhole"); public override IEnumerable GetItems() { diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs index 49673a562..ad3010a60 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/TorrentBlackholeSettings.cs @@ -28,23 +28,24 @@ namespace NzbDrone.Core.Download.Clients.Blackhole private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator(); - [FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .torrent file")] + [FieldDefinition(0, Label = "TorrentBlackholeTorrentFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")] + [FieldToken(TokenField.HelpText, "TorrentBlackholeTorrentFolder", "extension", ".torrent")] public string TorrentFolder { get; set; } - [FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")] + [FieldDefinition(1, Label = "BlackholeWatchFolder", Type = FieldType.Path, HelpText = "BlackholeWatchFolderHelpText")] public string WatchFolder { get; set; } [DefaultValue(false)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] - [FieldDefinition(2, Label = "Save Magnet Files", Type = FieldType.Checkbox, HelpText = "Save the magnet link if no .torrent file is available (only useful if the download client supports magnets saved to a file)")] + [FieldDefinition(2, Label = "TorrentBlackholeSaveMagnetFiles", Type = FieldType.Checkbox, HelpText = "TorrentBlackholeSaveMagnetFilesHelpText")] public bool SaveMagnetFiles { get; set; } - [FieldDefinition(3, Label = "Save Magnet Files", Type = FieldType.Textbox, HelpText = "Extension to use for magnet links, defaults to '.magnet'")] + [FieldDefinition(3, Label = "TorrentBlackholeSaveMagnetFilesExtension", Type = FieldType.Textbox, HelpText = "TorrentBlackholeSaveMagnetFilesExtensionHelpText")] public string MagnetFileExtension { get; set; } [DefaultValue(false)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] - [FieldDefinition(4, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Sonarr to Copy or Hardlink (depending on settings/system configuration)")] + [FieldDefinition(4, Label = "TorrentBlackholeSaveMagnetFilesReadOnly", Type = FieldType.Checkbox, HelpText = "TorrentBlackholeSaveMagnetFilesReadOnlyHelpText")] public bool ReadOnly { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs index 5fbc74a74..3a7105ba9 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -25,8 +26,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, IValidateNzbs nzbValidationService, - Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) + Logger logger, + ILocalizationService localizationService) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger, localizationService) { _scanWatchFolder = scanWatchFolder; @@ -51,7 +53,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole return null; } - public override string Name => "Usenet Blackhole"; + public override string Name => _localizationService.GetLocalizedString("UsenetBlackhole"); public override IEnumerable GetItems() { diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs index b2ff88149..ae9603b61 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackholeSettings.cs @@ -19,10 +19,11 @@ namespace NzbDrone.Core.Download.Clients.Blackhole { private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator(); - [FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .nzb file")] + [FieldDefinition(0, Label = "UsenetBlackholeNzbFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")] + [FieldToken(TokenField.HelpText, "UsenetBlackholeNzbFolder", "extension", ".nzb")] public string NzbFolder { get; set; } - [FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")] + [FieldDefinition(1, Label = "BlackholeWatchFolder", Type = FieldType.Path, HelpText = "BlackholeWatchFolderHelpText")] public string WatchFolder { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs index 39a191089..725c86521 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/Deluge.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -25,8 +26,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; } @@ -155,7 +157,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge if (torrent.State == DelugeTorrentStatus.Error) { item.Status = DownloadItemStatus.Warning; - item.Message = "Deluge is reporting an error"; + item.Message = _localizationService.GetLocalizedString("DownloadClientDelugeTorrentStateError"); } else if (torrent.IsFinished && torrent.State != DelugeTorrentStatus.Checking) { @@ -248,7 +250,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Password", "Authentication failed"); + return new NzbDroneValidationFailure("Password", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")); } catch (WebException ex) { @@ -256,29 +258,29 @@ namespace NzbDrone.Core.Download.Clients.Deluge switch (ex.Status) { case WebExceptionStatus.ConnectFailure: - return new NzbDroneValidationFailure("Host", "Unable to connect") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { - DetailedDescription = "Please verify the hostname and port." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail") }; case WebExceptionStatus.ConnectionClosed: - return new NzbDroneValidationFailure("UseSsl", "Verify SSL settings") + return new NzbDroneValidationFailure("UseSsl", _localizationService.GetLocalizedString("DownloadClientValidationVerifySsl")) { - DetailedDescription = "Please verify your SSL configuration on both Deluge and Sonarr." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationVerifySslDetail", new Dictionary { { "clientName", Name } }) }; case WebExceptionStatus.SecureChannelFailure: - return new NzbDroneValidationFailure("UseSsl", "Unable to connect through SSL") + return new NzbDroneValidationFailure("UseSsl", _localizationService.GetLocalizedString("DownloadClientValidationSslConnectFailure")) { - DetailedDescription = "Sonarr is unable to connect to Deluge using SSL. This problem could be computer related. Please try to configure both Sonarr and Deluge to not use SSL." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationSslConnectFailureDetail", new Dictionary { { "clientName", Name } }) }; default: - return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } } catch (Exception ex) { _logger.Error(ex, "Failed to test connection"); - return new NzbDroneValidationFailure("Host", "Unable to connect to Deluge") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -298,9 +300,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge if (!enabledPlugins.Contains("Label")) { - return new NzbDroneValidationFailure("TvCategory", "Label plugin not activated") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginInactive")) { - DetailedDescription = "You must have the Label plugin enabled in Deluge to use categories." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginInactiveDetail", new Dictionary { { "clientName", Name } }) }; } @@ -313,9 +315,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge if (!labels.Contains(Settings.TvCategory)) { - return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailure")) { - DetailedDescription = "Sonarr was unable to add the label to Deluge." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailureDetail", new Dictionary { { "clientName", Name } }) }; } } @@ -327,9 +329,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge if (!labels.Contains(Settings.TvImportedCategory)) { - return new NzbDroneValidationFailure("TvImportedCategory", "Configuration of label failed") + return new NzbDroneValidationFailure("TvImportedCategory", _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailure")) { - DetailedDescription = "Sonarr was unable to add the label to Deluge." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDelugeValidationLabelPluginFailureDetail", new Dictionary { { "clientName", Name } }) }; } } @@ -346,7 +348,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge catch (Exception ex) { _logger.Error(ex, "Unable to get torrents"); - return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary { { "exceptionMessage", ex.Message } })); } return null; diff --git a/src/NzbDrone.Core/Download/Clients/Deluge/DelugeSettings.cs b/src/NzbDrone.Core/Download/Clients/Deluge/DelugeSettings.cs index 97aab6a6e..66192d3f8 100644 --- a/src/NzbDrone.Core/Download/Clients/Deluge/DelugeSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Deluge/DelugeSettings.cs @@ -35,28 +35,30 @@ namespace NzbDrone.Core.Download.Clients.Deluge [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Deluge")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Deluge")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the deluge json url, see http://[host]:[port]/[urlBase]/json")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientDelugeSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/json")] public string UrlBase { get; set; } [FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.")] + [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string TvCategory { get; set; } - [FieldDefinition(6, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Sonarr will not remove torrents in that category even if seeding finished. Leave blank to keep same category.")] + [FieldDefinition(6, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")] public string TvImportedCategory { get; set; } - [FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(7, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(8, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } - [FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox)] + [FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)] public bool AddPaused { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/DownloadStationSettings.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/DownloadStationSettings.cs index 1d964377b..6bb2d5a5a 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/DownloadStationSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/DownloadStationSettings.cs @@ -36,7 +36,8 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Download Station")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Download Station")] public bool UseSsl { get; set; } [FieldDefinition(3, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -45,10 +46,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation [FieldDefinition(4, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.. Creates a [category] subdirectory in the output directory.")] + [FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategorySubFolderHelpText")] public string TvCategory { get; set; } - [FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, HelpText = "Optional shared folder to put downloads into, leave blank to use the default Download Station location")] + [FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientDownloadStationSettingsDirectory")] public string TvDirectory { get; set; } public DownloadStationSettings() diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs index 55fed1410..72b3ebbcb 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/TorrentDownloadStation.cs @@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.DownloadStation.Proxies; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -36,8 +37,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _dsInfoProxy = dsInfoProxy; _dsTaskProxySelector = dsTaskProxySelector; @@ -48,7 +50,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation public override string Name => "Download Station"; - public override ProviderMessage Message => new ProviderMessage("Sonarr is unable to connect to Download Station if 2-Factor Authentication is enabled on your DSM account", ProviderMessageType.Warning); + public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientDownloadStationProviderMessage"), ProviderMessageType.Warning); private IDownloadStationTaskProxy DsTaskProxy => _dsTaskProxySelector.GetProxy(Settings); @@ -222,7 +224,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation { if (torrent.Status == DownloadStationTaskStatus.Extracting) { - return $"Extracting: {int.Parse(torrent.StatusExtra["unzip_progress"])}%"; + return _localizationService.GetLocalizedString("DownloadStationStatusExtracting", + new Dictionary + { { "progress", int.Parse(torrent.StatusExtra["unzip_progress"]) } }); } if (torrent.Status == DownloadStationTaskStatus.Error) @@ -308,9 +312,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (downloadDir == null) { - return new NzbDroneValidationFailure(nameof(Settings.TvDirectory), "No default destination") + return new NzbDroneValidationFailure(nameof(Settings.TvDirectory), "DownloadClientDownloadStationValidationNoDefaultDestination") { - DetailedDescription = $"You must login into your Diskstation as {Settings.Username} and manually set it up into DownloadStation settings under BT/HTTP/FTP/NZB -> Location." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationNoDefaultDestinationDetail", new Dictionary { { "username", Settings.Username } }) }; } @@ -325,17 +329,17 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (folderInfo.Additional == null) { - return new NzbDroneValidationFailure(fieldName, $"Shared folder does not exist") + return new NzbDroneValidationFailure(fieldName, _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationSharedFolderMissing")) { - DetailedDescription = $"The Diskstation does not have a Shared Folder with the name '{sharedFolder}', are you sure you specified it correctly?" + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationSharedFolderMissingDetail", new Dictionary { { "sharedFolder", sharedFolder } }) }; } if (!folderInfo.IsDir) { - return new NzbDroneValidationFailure(fieldName, $"Folder does not exist") + return new NzbDroneValidationFailure(fieldName, _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationFolderMissing")) { - DetailedDescription = $"The folder '{downloadDir}' does not exist, it must be created manually inside the Shared Folder '{sharedFolder}'." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationFolderMissingDetail", new Dictionary { { "downloadDir", downloadDir }, { "sharedFolder", sharedFolder } }) }; } } @@ -351,7 +355,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation catch (Exception ex) { _logger.Error(ex, "Error testing Torrent Download Station"); - return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}"); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } } @@ -364,9 +368,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation catch (DownloadClientAuthenticationException ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Username", "Authentication failure") + return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")) { - DetailedDescription = $"Please verify your username and password. Also verify if the host running Sonarr isn't blocked from accessing {Name} by WhiteList limitations in the {Name} configuration." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary { { "clientName", Name } }) }; } catch (WebException ex) @@ -375,19 +379,19 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (ex.Status == WebExceptionStatus.ConnectFailure) { - return new NzbDroneValidationFailure("Host", "Unable to connect") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { - DetailedDescription = "Please verify the hostname and port." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail") }; } - return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}"); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } catch (Exception ex) { _logger.Error(ex, "Error testing Torrent Download Station"); - return new NzbDroneValidationFailure("Host", "Unable to connect to Torrent Download Station") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -402,7 +406,12 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (info.MinVersion > 2 || info.MaxVersion < 2) { - return new ValidationFailure(string.Empty, $"Download Station API version not supported, should be at least 2. It supports from {info.MinVersion} to {info.MaxVersion}"); + return new ValidationFailure(string.Empty, + _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationApiVersion", + new Dictionary + { + { "requiredVersion", 2 }, { "minVersion", info.MinVersion }, { "maxVersion", info.MaxVersion } + })); } return null; @@ -417,7 +426,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation } catch (Exception ex) { - return new NzbDroneValidationFailure(string.Empty, $"Failed to get the list of torrents: {ex.Message}"); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary { { "exceptionMessage", ex.Message } })); } } diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs index 27bc2bbdf..6f89845a9 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.DownloadStation.Proxies; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.ThingiProvider; @@ -34,8 +35,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, IValidateNzbs nzbValidationService, - Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) + Logger logger, + ILocalizationService localizationService) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger, localizationService) { _dsInfoProxy = dsInfoProxy; _dsTaskProxySelector = dsTaskProxySelector; @@ -46,7 +48,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation public override string Name => "Download Station"; - public override ProviderMessage Message => new ProviderMessage("Sonarr is unable to connect to Download Station if 2-Factor Authentication is enabled on your DSM account", ProviderMessageType.Warning); + public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientDownloadStationProviderMessage"), ProviderMessageType.Warning); private IDownloadStationTaskProxy DsTaskProxy => _dsTaskProxySelector.GetProxy(Settings); @@ -213,9 +215,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (downloadDir == null) { - return new NzbDroneValidationFailure(nameof(Settings.TvDirectory), "No default destination") + return new NzbDroneValidationFailure(nameof(Settings.TvDirectory), "DownloadClientDownloadStationValidationNoDefaultDestination") { - DetailedDescription = $"You must login into your Diskstation as {Settings.Username} and manually set it up into DownloadStation settings under BT/HTTP/FTP/NZB -> Location." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationNoDefaultDestinationDetail", new Dictionary { { "username", Settings.Username } }) }; } @@ -230,17 +232,17 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (folderInfo.Additional == null) { - return new NzbDroneValidationFailure(fieldName, $"Shared folder does not exist") + return new NzbDroneValidationFailure(fieldName, _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationSharedFolderMissing")) { - DetailedDescription = $"The Diskstation does not have a Shared Folder with the name '{sharedFolder}', are you sure you specified it correctly?" + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationSharedFolderMissingDetail", new Dictionary { { "sharedFolder", sharedFolder } }) }; } if (!folderInfo.IsDir) { - return new NzbDroneValidationFailure(fieldName, $"Folder does not exist") + return new NzbDroneValidationFailure(fieldName, _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationFolderMissing")) { - DetailedDescription = $"The folder '{downloadDir}' does not exist, it must be created manually inside the Shared Folder '{sharedFolder}'." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationFolderMissingDetail", new Dictionary { { "downloadDir", downloadDir }, { "sharedFolder", sharedFolder } }) }; } } @@ -256,7 +258,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation catch (Exception ex) { _logger.Error(ex, "Error testing Usenet Download Station"); - return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}"); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } } @@ -269,9 +271,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation catch (DownloadClientAuthenticationException ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Username", "Authentication failure") + return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")) { - DetailedDescription = $"Please verify your username and password. Also verify if the host running Sonarr isn't blocked from accessing {Name} by WhiteList limitations in the {Name} configuration." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary { { "clientName", Name } }) }; } catch (WebException ex) @@ -280,19 +282,19 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (ex.Status == WebExceptionStatus.ConnectFailure) { - return new NzbDroneValidationFailure("Host", "Unable to connect") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { - DetailedDescription = "Please verify the hostname and port." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail") }; } - return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } catch (Exception ex) { _logger.Error(ex, "Error testing Torrent Download Station"); - return new NzbDroneValidationFailure("Host", "Unable to connect to Usenet Download Station") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -307,7 +309,12 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation if (info.MinVersion > 2 || info.MaxVersion < 2) { - return new ValidationFailure(string.Empty, $"Download Station API version not supported, should be at least 2. It supports from {info.MinVersion} to {info.MaxVersion}"); + return new ValidationFailure(string.Empty, + _localizationService.GetLocalizedString("DownloadClientDownloadStationValidationApiVersion", + new Dictionary + { + { "requiredVersion", 2 }, { "minVersion", info.MinVersion }, { "maxVersion", info.MaxVersion } + })); } return null; @@ -319,7 +326,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation { if (task.Status == DownloadStationTaskStatus.Extracting) { - return $"Extracting: {int.Parse(task.StatusExtra["unzip_progress"])}%"; + return _localizationService.GetLocalizedString("DownloadStationStatusExtracting", + new Dictionary + { { "progress", int.Parse(task.StatusExtra["unzip_progress"]) } }); } if (task.Status == DownloadStationTaskStatus.Error) @@ -398,7 +407,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation } catch (Exception ex) { - return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of NZBs: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestNzbs", new Dictionary { { "exceptionMessage", ex.Message } })); } } diff --git a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs index abbc62e6f..f101f715d 100644 --- a/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs +++ b/src/NzbDrone.Core/Download/Clients/Flood/Flood.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.Flood.Models; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -28,8 +29,9 @@ namespace NzbDrone.Core.Download.Clients.Flood IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; _downloadSeedConfigProvider = downloadSeedConfigProvider; @@ -81,7 +83,7 @@ namespace NzbDrone.Core.Download.Clients.Flood } public override string Name => "Flood"; - public override ProviderMessage Message => new ProviderMessage("Sonarr will handle automatic removal of torrents based on the current seed criteria in Settings -> Indexers", ProviderMessageType.Info); + public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientFloodSettingsRemovalInfo"), ProviderMessageType.Info); protected override string AddFromTorrentFile(RemoteEpisode remoteEpisode, string hash, string filename, byte[] fileContent) { diff --git a/src/NzbDrone.Core/Download/Clients/Flood/FloodSettings.cs b/src/NzbDrone.Core/Download/Clients/Flood/FloodSettings.cs index 9877b9707..f26e19512 100644 --- a/src/NzbDrone.Core/Download/Clients/Flood/FloodSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Flood/FloodSettings.cs @@ -40,10 +40,12 @@ namespace NzbDrone.Core.Download.Clients.Flood [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Flood")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Flood")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, HelpText = "Optionally adds a prefix to Flood API, such as [protocol]://[host]:[port]/[urlBase]api")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, HelpText = "DownloadClientFloodSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "[protocol]://[host]:[port]/[urlBase]/api")] public string UrlBase { get; set; } [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -52,19 +54,19 @@ namespace NzbDrone.Core.Download.Clients.Flood [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, HelpText = "Manually specifies download destination")] + [FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsDestinationHelpText")] public string Destination { get; set; } - [FieldDefinition(7, Label = "Tags", Type = FieldType.Tag, HelpText = "Initial tags of a download. To be recognized, a download must have all initial tags. This avoids conflicts with unrelated downloads.")] + [FieldDefinition(7, Label = "Tags", Type = FieldType.Tag, HelpText = "DownloadClientFloodSettingsTagsHelpText")] public IEnumerable Tags { get; set; } - [FieldDefinition(8, Label = "Post-Import Tags", Type = FieldType.Tag, HelpText = "Appends tags after a download is imported.", Advanced = true)] + [FieldDefinition(8, Label = "DownloadClientFloodSettingsPostImportTags", Type = FieldType.Tag, HelpText = "DownloadClientFloodSettingsPostImportTagsHelpText", Advanced = true)] public IEnumerable PostImportTags { get; set; } - [FieldDefinition(9, Label = "Additional Tags", Type = FieldType.Select, SelectOptions = typeof(AdditionalTags), HelpText = "Adds properties of media as tags. Hints are examples.", Advanced = true)] + [FieldDefinition(9, Label = "DownloadClientFloodSettingsAdditionalTags", Type = FieldType.Select, SelectOptions = typeof(AdditionalTags), HelpText = "DownloadClientFloodSettingsAdditionalTagsHelpText", Advanced = true)] public IEnumerable AdditionalTags { get; set; } - [FieldDefinition(10, Label = "Start on Add", Type = FieldType.Checkbox)] + [FieldDefinition(10, Label = "DownloadClientFloodSettingsStartOnAdd", Type = FieldType.Checkbox)] public bool StartOnAdd { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/FreeboxDownloadSettings.cs b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/FreeboxDownloadSettings.cs index 45de36ef4..62de8f46c 100644 --- a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/FreeboxDownloadSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/FreeboxDownloadSettings.cs @@ -46,37 +46,42 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload ApiUrl = "/api/v1/"; } - [FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "Hostname or host IP address of the Freebox, defaults to 'mafreebox.freebox.fr' (will only work if on same network)")] + [FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsHostHelpText")] + [FieldToken(TokenField.HelpText, "Host", "url", "mafreebox.freebox.fr")] public string Host { get; set; } - [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "Port used to access Freebox interface, defaults to '443'")] + [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsPortHelpText")] + [FieldToken(TokenField.HelpText, "Port", "port", 443)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secured connection when connecting to Freebox API")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Freebox API")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "API URL", Type = FieldType.Textbox, Advanced = true, HelpText = "Define Freebox API base URL with API version, eg http://[host]:[port]/[api_base_url]/[api_version]/, defaults to '/api/v1/'")] + [FieldDefinition(3, Label = "DownloadClientFreeboxSettingsApiUrl", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientFreeboxSettingsApiUrlHelpText")] + [FieldToken(TokenField.HelpText, "DownloadClientFreeboxSettingsApiUrl", "url", "http://[host]:[port]/[api_base_url]/[api_version]/")] + [FieldToken(TokenField.HelpText, "DownloadClientFreeboxSettingsApiUrl", "defaultApiUrl", "/api/v1/")] public string ApiUrl { get; set; } - [FieldDefinition(4, Label = "App ID", Type = FieldType.Textbox, HelpText = "App ID given when creating access to Freebox API (ie 'app_id')")] + [FieldDefinition(4, Label = "DownloadClientFreeboxSettingsAppId", Type = FieldType.Textbox, HelpText = "DownloadClientFreeboxSettingsAppIdHelpText")] public string AppId { get; set; } - [FieldDefinition(5, Label = "App Token", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "App token retrieved when creating access to Freebox API (ie 'app_token')")] + [FieldDefinition(5, Label = "DownloadClientFreeboxSettingsAppToken", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "DownloadClientFreeboxSettingsAppTokenHelpText")] public string AppToken { get; set; } - [FieldDefinition(6, Label = "Destination Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Freebox download location")] + [FieldDefinition(6, Label = "Destination", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsDestinationHelpText")] public string DestinationDirectory { get; set; } - [FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads (will create a [category] subdirectory in the output directory)")] + [FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategorySubFolderHelpText")] public string Category { get; set; } - [FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentPriority { get; set; } - [FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(FreeboxDownloadPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderPriority { get; set; } - [FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)] + [FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)] public bool AddPaused { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs index 449afa7b3..6f020b57b 100644 --- a/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs +++ b/src/NzbDrone.Core/Download/Clients/FreeboxDownload/TorrentFreeboxDownload.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.FreeboxDownload.Responses; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; } @@ -108,8 +110,9 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload case FreeboxDownloadTaskStatus.Unknown: default: // new status in API? default to downloading - item.Message = "Unknown download state: " + torrent.Status; - _logger.Info(item.Message); + item.Message = _localizationService.GetLocalizedString("UnknownDownloadState", + new Dictionary { { "state", torrent.Status } }); + _logger.Info($"Unknown download state: {torrent.Status}"); item.Status = DownloadItemStatus.Downloading; break; } diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs index 9773065aa..2d50030e7 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/Hadouken.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.Hadouken.Models; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.Hadouken IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; } @@ -159,18 +161,20 @@ namespace NzbDrone.Core.Download.Clients.Hadouken if (version < new Version("5.1")) { return new ValidationFailure(string.Empty, - "Old Hadouken client with unsupported API, need 5.1 or higher"); + _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { { "clientName", Name }, { "requiredVersion", "5.1" }, { "reportedVersion", version } })); } } catch (DownloadClientAuthenticationException ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Password", "Authentication failed"); + return new NzbDroneValidationFailure("Password", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")); } catch (Exception ex) { - return new NzbDroneValidationFailure("Host", "Unable to connect to Hadouken") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect")) { DetailedDescription = ex.Message }; @@ -188,7 +192,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken catch (Exception ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary { { "exceptionMessage", ex.Message } })); } return null; diff --git a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs index 5b0603dd7..8e560720e 100644 --- a/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Hadouken/HadoukenSettings.cs @@ -39,10 +39,13 @@ namespace NzbDrone.Core.Download.Clients.Hadouken [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Hadouken")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Hadouken")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Hadouken url, e.g. http://[host]:[port]/[urlBase]/api")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Hadouken")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")] public string UrlBase { get; set; } [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -51,7 +54,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox)] + [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string Category { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs index 5e5fb3acd..dbdfdb7c4 100644 --- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs +++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs @@ -8,6 +8,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.Validation; @@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, IValidateNzbs nzbValidationService, - Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) + Logger logger, + ILocalizationService localizationService) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger, localizationService) { _proxy = proxy; } @@ -162,7 +164,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex { _logger.Error(ex, "Unable to connect to NZBVortex"); - return new NzbDroneValidationFailure("Host", "Unable to connect to NZBVortex") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -180,13 +182,16 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex if (version.Major < 2 || (version.Major == 2 && version.Minor < 3)) { - return new ValidationFailure("Host", "NZBVortex needs to be updated"); + return new ValidationFailure("Host", + _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { { "clientName", Name }, { "requiredVersion", "2.3" }, { "reportedVersion", version } })); } } catch (Exception ex) { _logger.Error(ex, "Unable to connect to NZBVortex"); - return new ValidationFailure("Host", "Unable to connect to NZBVortex"); + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })); } return null; @@ -200,7 +205,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex } catch (NzbVortexAuthenticationException) { - return new ValidationFailure("ApiKey", "API Key Incorrect"); + return new ValidationFailure("ApiKey", _localizationService.GetLocalizedString("DownloadClientValidationApiKeyIncorrect")); } return null; @@ -214,9 +219,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex { if (Settings.TvCategory.IsNotNullOrWhiteSpace()) { - return new NzbDroneValidationFailure("TvCategory", "Group does not exist") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientValidationGroupMissing")) { - DetailedDescription = "The Group you entered doesn't exist in NzbVortex. Go to NzbVortex to create it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationGroupMissingDetail", new Dictionary { { "clientName", Name } }) }; } } @@ -243,7 +248,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex if (filesResponse.Count > 1) { - var message = string.Format("Download contains multiple files and is not in a job folder: {0}", outputPath); + var message = _localizationService.GetLocalizedString("DownloadClientNzbVortexMultipleFilesMessage", new Dictionary { { "outputPath", outputPath } }); queueItem.Status = DownloadItemStatus.Warning; queueItem.Message = message; diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs index d73c1853e..31169f74d 100644 --- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortexSettings.cs @@ -42,19 +42,21 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the NZBVortex url, e.g. http://[host]:[port]/[urlBase]/api")] + [FieldDefinition(2, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "clientName", "NZBVortex")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")] public string UrlBase { get; set; } - [FieldDefinition(3, Label = "API Key", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)] + [FieldDefinition(3, Label = "ApiKey", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)] public string ApiKey { get; set; } - [FieldDefinition(4, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.")] + [FieldDefinition(4, Label = "Group", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string TvCategory { get; set; } - [FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(5, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(6, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs index 54974815c..d7956318e 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs @@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.Validation; @@ -28,8 +29,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, IValidateNzbs nzbValidationService, - Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) + Logger logger, + ILocalizationService localizationService) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger, localizationService) { _proxy = proxy; } @@ -124,7 +126,13 @@ namespace NzbDrone.Core.Download.Clients.Nzbget historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo); historyItem.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(itemDir)); historyItem.Category = item.Category; - historyItem.Message = $"PAR Status: {item.ParStatus} - Unpack Status: {item.UnpackStatus} - Move Status: {item.MoveStatus} - Script Status: {item.ScriptStatus} - Delete Status: {item.DeleteStatus} - Mark Status: {item.MarkStatus}"; + historyItem.Message = _localizationService.GetLocalizedString("NzbgetHistoryItemMessage", + new Dictionary + { + { "parStatus", item.ParStatus }, { "unpackStatus", item.UnpackStatus }, + { "moveStatus", item.MoveStatus }, { "scriptStaus", item.ScriptStatus }, + { "deleteStatus", item.DeleteStatus }, { "markStatus", item.MarkStatus } + }); historyItem.Status = DownloadItemStatus.Completed; historyItem.RemainingTime = TimeSpan.Zero; historyItem.CanMoveFiles = true; @@ -270,18 +278,23 @@ namespace NzbDrone.Core.Download.Clients.Nzbget if (Version.Parse(version) < Version.Parse("12.0")) { - return new ValidationFailure(string.Empty, "Nzbget version too low, need 12.0 or higher"); + return new ValidationFailure(string.Empty, + _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { { "clientName", Name }, { "requiredVersion", "12.0" }, { "reportedVersion", version } })); } } catch (Exception ex) { if (ex.Message.ContainsIgnoreCase("Authentication failed")) { - return new ValidationFailure("Username", "Authentication failed"); + return new ValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")); } _logger.Error(ex, "Unable to connect to NZBGet"); - return new ValidationFailure("Host", "Unable to connect to NZBGet"); + return new ValidationFailure("Host", + _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", + new Dictionary { { "clientName", Name } })); } return null; @@ -294,10 +307,10 @@ namespace NzbDrone.Core.Download.Clients.Nzbget if (!Settings.TvCategory.IsNullOrWhiteSpace() && !categories.Any(v => v.Name == Settings.TvCategory)) { - return new NzbDroneValidationFailure("TvCategory", "Category does not exist") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissing")) { InfoLink = _proxy.GetBaseUrl(Settings), - DetailedDescription = "The Category your entered doesn't exist in NzbGet. Go to NzbGet to create it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissingDetail", new Dictionary { { "clientName", Name } }) }; } @@ -311,18 +324,18 @@ namespace NzbDrone.Core.Download.Clients.Nzbget var keepHistory = config.GetValueOrDefault("KeepHistory", "7"); if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out var value) || value == 0) { - return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0") + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryZero")) { InfoLink = _proxy.GetBaseUrl(Settings), - DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Sonarr from seeing completed downloads." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryZeroDetail") }; } else if (value > 25000) { - return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be less than 25000") + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryOverMax")) { InfoLink = _proxy.GetBaseUrl(Settings), - DetailedDescription = "NzbGet setting KeepHistory is set too high." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientNzbgetValidationKeepHistoryOverMaxDetail") }; } diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetSettings.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetSettings.cs index 3fe98ef9c..adc8891e5 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetSettings.cs @@ -40,10 +40,13 @@ namespace NzbDrone.Core.Download.Clients.Nzbget [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to NZBGet")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "NZBGet")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the nzbget url, e.g. http://[host]:[port]/[urlBase]/jsonrpc")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "clientName", "NZBGet")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/jsonrpc")] public string UrlBase { get; set; } [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -52,16 +55,16 @@ namespace NzbDrone.Core.Download.Clients.Nzbget [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.")] + [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string TvCategory { get; set; } - [FieldDefinition(7, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(7, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(8, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } - [FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NzbGet version 16.0")] + [FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox, HelpText = "DownloadClientNzbgetSettingsAddPausedHelpText")] public bool AddPaused { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs index f531778d1..920279263 100644 --- a/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs +++ b/src/NzbDrone.Core/Download/Clients/Pneumatic/Pneumatic.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -23,8 +24,9 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(configService, diskProvider, remotePathMappingService, logger, localizationService) { _httpClient = httpClient; } diff --git a/src/NzbDrone.Core/Download/Clients/Pneumatic/PneumaticSettings.cs b/src/NzbDrone.Core/Download/Clients/Pneumatic/PneumaticSettings.cs index 741021a3f..6cd8a2b89 100644 --- a/src/NzbDrone.Core/Download/Clients/Pneumatic/PneumaticSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Pneumatic/PneumaticSettings.cs @@ -19,10 +19,10 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic { private static readonly PneumaticSettingsValidator Validator = new PneumaticSettingsValidator(); - [FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "This folder will need to be reachable from XBMC")] + [FieldDefinition(0, Label = "DownloadClientPneumaticSettingsNzbFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsNzbFolderHelpText")] public string NzbFolder { get; set; } - [FieldDefinition(1, Label = "Strm Folder", Type = FieldType.Path, HelpText = ".strm files in this folder will be import by drone")] + [FieldDefinition(1, Label = "DownloadClientPneumaticSettingsStrmFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsStrmFolderHelpText")] public string StrmFolder { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index c51c99a44..77cbeef17 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -34,8 +35,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, ICacheManager cacheManager, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxySelector = proxySelector; @@ -241,7 +243,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent { case "error": // some error occurred, applies to paused torrents, warning so failed download handling isn't triggered item.Status = DownloadItemStatus.Warning; - item.Message = "qBittorrent is reporting an error"; + item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateError"); break; case "pausedDL": // torrent is paused and has NOT finished downloading @@ -266,24 +268,24 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent case "stalledDL": // torrent is being downloaded, but no connection were made item.Status = DownloadItemStatus.Warning; - item.Message = "The download is stalled with no connections"; + item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateStalled"); break; case "missingFiles": // torrent is missing files item.Status = DownloadItemStatus.Warning; - item.Message = "The download is missing files"; + item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateMissingFiles"); break; case "metaDL": // torrent magnet is being downloaded if (config.DhtEnabled) { item.Status = DownloadItemStatus.Queued; - item.Message = "qBittorrent is downloading metadata"; + item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateMetadata"); } else { item.Status = DownloadItemStatus.Warning; - item.Message = "qBittorrent cannot resolve magnet link with DHT disabled"; + item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateDhtDisabled"); } break; @@ -296,8 +298,8 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent break; default: // new status in API? default to downloading - item.Message = "Unknown download state: " + torrent.State; - _logger.Info(item.Message); + item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStateUnknown", new Dictionary { { "state", torrent.State } }); + _logger.Info($"Unknown download state: {torrent.State}"); item.Status = DownloadItemStatus.Downloading; break; } @@ -311,7 +313,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent else { item.Status = DownloadItemStatus.Warning; - item.Message = "Unable to Import. Path matches client base download directory, it's possible 'Keep top-level folder' is disabled for this torrent or 'Torrent Content Layout' is NOT set to 'Original' or 'Create Subfolder'?"; + item.Message = _localizationService.GetLocalizedString("DownloadClientQbittorrentTorrentStatePathError"); } } @@ -415,29 +417,30 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent if (version < Version.Parse("1.5")) { // API version 5 introduced the "save_path" property in /query/torrents - return new NzbDroneValidationFailure("Host", "Unsupported client version") - { - DetailedDescription = "Please upgrade to qBittorrent version 3.2.4 or higher." - }; + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { + { "clientName", Name }, { "requiredVersion", "3.2.4" }, { "reportedVersion", version } + })); } else if (version < Version.Parse("1.6")) { // API version 6 introduced support for labels if (Settings.TvCategory.IsNotNullOrWhiteSpace()) { - return new NzbDroneValidationFailure("Category", "Category is not supported") + return new NzbDroneValidationFailure("Category", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryUnsupported")) { - DetailedDescription = "Labels are not supported until qBittorrent version 3.3.0. Please upgrade or try again with an empty Category." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryUnsupportedDetail") }; } } else if (Settings.TvCategory.IsNullOrWhiteSpace()) { // warn if labels are supported, but category is not provided - return new NzbDroneValidationFailure("TvCategory", "Category is recommended") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryRecommended")) { IsWarning = true, - DetailedDescription = "Sonarr will not attempt to import completed downloads without a category." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryRecommendedDetail") }; } @@ -445,18 +448,18 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent var config = Proxy.GetConfig(Settings); if ((config.MaxRatioEnabled || config.MaxSeedingTimeEnabled) && (config.MaxRatioAction == QBittorrentMaxRatioAction.Remove || config.MaxRatioAction == QBittorrentMaxRatioAction.DeleteFiles)) { - return new NzbDroneValidationFailure(string.Empty, "qBittorrent is configured to remove torrents when they reach their Share Ratio Limit") + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationRemovesAtRatioLimit")) { - DetailedDescription = "Sonarr will be unable to perform Completed Download Handling as configured. You can fix this in qBittorrent ('Tools -> Options...' in the menu) by changing 'Options -> BitTorrent -> Share Ratio Limiting' from 'Remove them' to 'Pause them'." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationRemovesAtRatioLimitDetail") }; } } catch (DownloadClientAuthenticationException ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Username", "Authentication failure") + return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")) { - DetailedDescription = "Please verify your username and password." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary { { "clientName", Name } }) }; } catch (WebException ex) @@ -464,19 +467,19 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent _logger.Error(ex, "Unable to connect to qBittorrent"); if (ex.Status == WebExceptionStatus.ConnectFailure) { - return new NzbDroneValidationFailure("Host", "Unable to connect") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { - DetailedDescription = "Please verify the hostname and port." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail") }; } - return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } catch (Exception ex) { _logger.Error(ex, "Unable to test qBittorrent"); - return new NzbDroneValidationFailure("Host", "Unable to connect to qBittorrent") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -508,9 +511,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent if (!labels.ContainsKey(Settings.TvCategory)) { - return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailure")) { - DetailedDescription = "Sonarr was unable to add the label to qBittorrent." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailureDetail") }; } } @@ -522,9 +525,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent if (!labels.ContainsKey(Settings.TvImportedCategory)) { - return new NzbDroneValidationFailure("TvImportedCategory", "Configuration of label failed") + return new NzbDroneValidationFailure("TvImportedCategory", _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailure")) { - DetailedDescription = "Sonarr was unable to add the label to qBittorrent." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationCategoryAddFailureDetail") }; } } @@ -550,18 +553,24 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent { if (!recentPriorityDefault) { - return new NzbDroneValidationFailure(nameof(Settings.RecentTvPriority), "Queueing not enabled") { DetailedDescription = "Torrent Queueing is not enabled in your qBittorrent settings. Enable it in qBittorrent or select 'Last' as priority." }; + return new NzbDroneValidationFailure(nameof(Settings.RecentTvPriority), _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabled")) + { + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabledDetail") + }; } else if (!olderPriorityDefault) { - return new NzbDroneValidationFailure(nameof(Settings.OlderTvPriority), "Queueing not enabled") { DetailedDescription = "Torrent Queueing is not enabled in your qBittorrent settings. Enable it in qBittorrent or select 'Last' as priority." }; + return new NzbDroneValidationFailure(nameof(Settings.OlderTvPriority), _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabled")) + { + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientQbittorrentValidationQueueingNotEnabledDetail") + }; } } } catch (Exception ex) { _logger.Error(ex, "Failed to test qBittorrent"); - return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } return null; @@ -576,7 +585,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent catch (Exception ex) { _logger.Error(ex, "Failed to get torrents"); - return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary { { "exceptionMessage", ex.Message } })); } return null; diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs index 8d83e9156..fd12a7e04 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentSettings.cs @@ -36,10 +36,12 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsUseSslHelpText")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the qBittorrent url, e.g. http://[host]:[port]/[urlBase]/api")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "clientName", "qBittorrent")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")] public string UrlBase { get; set; } [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -48,25 +50,25 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.")] + [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string TvCategory { get; set; } - [FieldDefinition(7, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Sonarr will not remove the torrent if seeding has finished. Leave blank to keep same category.")] + [FieldDefinition(7, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")] public string TvImportedCategory { get; set; } - [FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(QBittorrentPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } - [FieldDefinition(10, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent. Note that Forced Torrents do not abide by seed restrictions")] + [FieldDefinition(10, Label = "DownloadClientSettingsInitialState", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "DownloadClientQbittorrentSettingsInitialStateHelpText")] public int InitialState { get; set; } - [FieldDefinition(11, Label = "Sequential Order", Type = FieldType.Checkbox, HelpText = "Download in sequential order (qBittorrent 4.1.0+)")] + [FieldDefinition(11, Label = "DownloadClientQbittorrentSettingsSequentialOrder", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsSequentialOrderHelpText")] public bool SequentialOrder { get; set; } - [FieldDefinition(12, Label = "First and Last First", Type = FieldType.Checkbox, HelpText = "Download first and last pieces first (qBittorrent 4.1.0+)")] + [FieldDefinition(12, Label = "DownloadClientQbittorrentSettingsFirstAndLastFirst", Type = FieldType.Checkbox, HelpText = "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText")] public bool FirstAndLast { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index 95f8942aa..5d9003849 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -10,6 +10,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.Validation; @@ -26,8 +27,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, IValidateNzbs nzbValidationService, - Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) + Logger logger, + ILocalizationService localizationService) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger, localizationService) { _proxy = proxy; } @@ -381,15 +383,15 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd if (version == null) { - return new ValidationFailure("Version", "Unknown Version: " + rawVersion); + return new ValidationFailure("Version", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationUnknownVersion", new Dictionary { { "rawVersion", rawVersion ?? "" } })); } if (rawVersion.Equals("develop", StringComparison.InvariantCultureIgnoreCase)) { - return new NzbDroneValidationFailure("Version", "Sabnzbd develop version, assuming version 3.0.0 or higher.") + return new NzbDroneValidationFailure("Version", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationDevelopVersion")) { IsWarning = true, - DetailedDescription = "Sonarr may not be able to support new features added to SABnzbd when running develop versions." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationDevelopVersionDetail") }; } @@ -403,12 +405,17 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd return null; } - return new ValidationFailure("Version", "Version 0.7.0+ is required, but found: " + version); + return new ValidationFailure("Version", + _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { + { "clientName", Name }, { "requiredVersion", "0.7.0" }, { "reportedVersion", version } + })); } catch (Exception ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Host", "Unable to connect to SABnzbd") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -425,12 +432,12 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd { if (ex.Message.ContainsIgnoreCase("API Key Incorrect")) { - return new ValidationFailure("APIKey", "API Key Incorrect"); + return new ValidationFailure("APIKey", _localizationService.GetLocalizedString("DownloadClientValidationApiKeyIncorrect")); } if (ex.Message.ContainsIgnoreCase("API Key Required")) { - return new ValidationFailure("APIKey", "API Key Required"); + return new ValidationFailure("APIKey", _localizationService.GetLocalizedString("DownloadClientValidationApiKeyRequired")); } throw; @@ -444,10 +451,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd var config = _proxy.GetConfig(Settings); if (config.Misc.pre_check && !HasVersion(1, 1)) { - return new NzbDroneValidationFailure("", "Disable 'Check before download' option in Sabnbzd") + return new NzbDroneValidationFailure("", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationCheckBeforeDownload")) { InfoLink = _proxy.GetBaseUrl(Settings, "config/switches/"), - DetailedDescription = "Using Check before download affects Sonarr ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationCheckBeforeDownloadDetail") }; } @@ -463,10 +470,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd { if (category.Dir.EndsWith("*")) { - return new NzbDroneValidationFailure("TvCategory", "Enable Job folders") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableJobFolders")) { InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"), - DetailedDescription = "Sonarr prefers each download to have a separate folder. With * appended to the Folder/Path Sabnzbd will not create these job folders. Go to Sabnzbd to fix it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableJobFoldersDetail") }; } } @@ -474,10 +481,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd { if (!Settings.TvCategory.IsNullOrWhiteSpace()) { - return new NzbDroneValidationFailure("TvCategory", "Category does not exist") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissing")) { InfoLink = _proxy.GetBaseUrl(Settings, "config/categories/"), - DetailedDescription = "The Category your entered doesn't exist in Sabnzbd. Go to Sabnzbd to create it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationCategoryMissingDetail", new Dictionary { { "clientName", Name } }) }; } } @@ -485,37 +492,37 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd // New in SABnzbd 4.1, but on older versions this will be empty and not apply if (config.Sorters.Any(s => s.is_active && ContainsCategory(s.sort_cats, Settings.TvCategory))) { - return new NzbDroneValidationFailure("TvCategory", "Disable TV Sorting") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSorting")) { InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), - DetailedDescription = "You must disable sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSortingDetail") }; } if (config.Misc.enable_tv_sorting && ContainsCategory(config.Misc.tv_categories, Settings.TvCategory)) { - return new NzbDroneValidationFailure("TvCategory", "Disable TV Sorting") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSorting")) { InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), - DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableTvSortingDetail") }; } if (config.Misc.enable_movie_sorting && ContainsCategory(config.Misc.movie_categories, Settings.TvCategory)) { - return new NzbDroneValidationFailure("TvCategory", "Disable Movie Sorting") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableMovieSorting")) { InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), - DetailedDescription = "You must disable Sabnzbd Movie Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableMovieSortingDetail") }; } if (config.Misc.enable_date_sorting && ContainsCategory(config.Misc.date_categories, Settings.TvCategory)) { - return new NzbDroneValidationFailure("TvCategory", "Disable Date Sorting") + return new NzbDroneValidationFailure("TvCategory", _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableDateSorting")) { InfoLink = _proxy.GetBaseUrl(Settings, "config/sorting/"), - DetailedDescription = "You must disable Sabnzbd Date Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientSabnzbdValidationEnableDisableDateSortingDetail") }; } diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdSettings.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdSettings.cs index 13e6b660c..baa6b3553 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdSettings.cs @@ -51,13 +51,16 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Sabnzbd")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Sabnzbd")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the Sabnzbd url, e.g. http://[host]:[port]/[urlBase]/api")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Sabnzbd")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")] public string UrlBase { get; set; } - [FieldDefinition(4, Label = "API Key", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)] + [FieldDefinition(4, Label = "ApiKey", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey)] public string ApiKey { get; set; } [FieldDefinition(5, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -66,13 +69,13 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd [FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.")] + [FieldDefinition(7, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string TvCategory { get; set; } - [FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs b/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs index 369b9f961..8c9f858bc 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/Transmission.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using FluentValidation.Results; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.RemotePathMappings; @@ -18,8 +20,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { } @@ -34,7 +37,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission if (version < new Version(2, 40)) { - return new ValidationFailure(string.Empty, "Transmission version not supported, should be 2.40 or higher."); + return new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", new Dictionary { { "clientName", Name }, { "requiredVersion", "2.40" }, { "reportedVersion", version } })); } return null; diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs index d0f5a892b..f08ab547c 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionBase.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; } @@ -270,16 +272,16 @@ namespace NzbDrone.Core.Download.Clients.Transmission catch (DownloadClientAuthenticationException ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Username", "Authentication failure") + return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")) { - DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Sonarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name) + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary { { "clientName", Name } }) }; } catch (DownloadClientUnavailableException ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Host", "Unable to connect to Transmission") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -288,7 +290,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission { _logger.Error(ex, "Failed to test"); - return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } } @@ -303,7 +305,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission catch (Exception ex) { _logger.Error(ex, "Failed to get torrents"); - return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary { { "exceptionMessage", ex.Message } })); } return null; diff --git a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionSettings.cs b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionSettings.cs index 40e2528ac..7038ad7af 100644 --- a/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/Transmission/TransmissionSettings.cs @@ -41,10 +41,15 @@ namespace NzbDrone.Core.Download.Clients.Transmission [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to Transmission")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "Transmission")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the transmission rpc url, eg http://[host]:[port]/[urlBase]/rpc, defaults to '/transmission/'")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the transmission rpc url, eg http://[host]:[port]/[urlBase]/rpc, defaults to '/transmission/'")] + [FieldToken(TokenField.HelpText, "UrlBase", "clientName", "Transmission")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/rpc")] + [FieldToken(TokenField.HelpText, "UrlBase", "defaultUrl", "/transmission/")] + public string UrlBase { get; set; } [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -53,19 +58,19 @@ namespace NzbDrone.Core.Download.Clients.Transmission [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.. Creates a [category] subdirectory in the output directory.")] + [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategorySubFolderHelpText")] public string TvCategory { get; set; } - [FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")] + [FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientTransmissionSettingsDirectoryHelpText")] public string TvDirectory { get; set; } - [FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } - [FieldDefinition(10, Label = "Add Paused", Type = FieldType.Checkbox)] + [FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)] public bool AddPaused { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs b/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs index c52a8de88..14ab2534f 100644 --- a/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs +++ b/src/NzbDrone.Core/Download/Clients/Vuze/Vuze.cs @@ -4,6 +4,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.Transmission; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.RemotePathMappings; @@ -19,8 +20,9 @@ namespace NzbDrone.Core.Download.Clients.Vuze IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(proxy, torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { } @@ -57,7 +59,7 @@ namespace NzbDrone.Core.Download.Clients.Vuze if (!int.TryParse(versionString, out var version) || version < MINIMUM_SUPPORTED_PROTOCOL_VERSION) { { - return new ValidationFailure(string.Empty, "Protocol version not supported, use Vuze 5.0.0.0 or higher with Vuze Web Remote plugin."); + return new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientVuzeValidationErrorVersion")); } } diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs index ebfbe8559..bbfb2fe8e 100644 --- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrent.cs @@ -11,6 +11,7 @@ using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.rTorrent; using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -34,8 +35,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent IRemotePathMappingService remotePathMappingService, IDownloadSeedConfigProvider downloadSeedConfigProvider, IRTorrentDirectoryValidator rTorrentDirectoryValidator, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; _rTorrentDirectoryValidator = rTorrentDirectoryValidator; @@ -115,7 +117,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent public override string Name => "rTorrent"; - public override ProviderMessage Message => new ProviderMessage($"rTorrent will not pause torrents when they meet the seed criteria. Sonarr will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers only when Remove Completed is enabled. After importing it will also set \"{_imported_view}\" as an rTorrent view, which can be used in rTorrent scripts to customize behavior.", ProviderMessageType.Info); + public override ProviderMessage Message => new ProviderMessage(_localizationService.GetLocalizedString("DownloadClientRTorrentProviderMessage", new Dictionary { { "importedView", _imported_view } }), ProviderMessageType.Info); public override IEnumerable GetItems() { @@ -250,14 +252,19 @@ namespace NzbDrone.Core.Download.Clients.RTorrent if (new Version(version) < new Version("0.9.0")) { - return new ValidationFailure(string.Empty, "rTorrent version should be at least 0.9.0. Version reported is {0}", version); + return new ValidationFailure(string.Empty, + _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { + { "clientName", Name }, { "requiredVersion", "0.9.0" }, { "reportedVersion", version } + })); } } catch (Exception ex) { _logger.Error(ex, "Failed to test rTorrent"); - return new NzbDroneValidationFailure("Host", "Unable to connect to rTorrent") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -275,7 +282,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent catch (Exception ex) { _logger.Error(ex, "Failed to get torrents"); - return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary { { "exceptionMessage", ex.Message } })); } return null; diff --git a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentSettings.cs b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentSettings.cs index 820932cec..90252c585 100644 --- a/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/rTorrent/RTorrentSettings.cs @@ -37,10 +37,13 @@ namespace NzbDrone.Core.Download.Clients.RTorrent [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to rTorrent")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "rTorrent")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Path", Type = FieldType.Textbox, HelpText = "Path to the XMLRPC endpoint, see http(s)://[host]:[port]/[urlPath]. This is usually RPC2 or [path to ruTorrent]/plugins/rpc/rpc.php when using ruTorrent.")] + [FieldDefinition(3, Label = "DownloadClientRTorrentSettingsUrlPath", Type = FieldType.Textbox, HelpText = "DownloadClientRTorrentSettingsUrlPathHelpText")] + [FieldToken(TokenField.HelpText, "DownloadClientRTorrentSettingsUrlPath", "url", "http(s)://[host]:[port]/[urlPath]")] + [FieldToken(TokenField.HelpText, "DownloadClientRTorrentSettingsUrlPath", "url2", "/plugins/rpc/rpc.php")] public string UrlBase { get; set; } [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -49,22 +52,22 @@ namespace NzbDrone.Core.Download.Clients.RTorrent [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.")] + [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string TvCategory { get; set; } - [FieldDefinition(7, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Sonarr will not remove the torrent if seeding has finished. Leave blank to keep same category.")] + [FieldDefinition(7, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")] public string TvImportedCategory { get; set; } - [FieldDefinition(8, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")] + [FieldDefinition(8, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientRTorrentSettingsDirectoryHelpText")] public string TvDirectory { get; set; } - [FieldDefinition(9, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(9, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(10, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(10, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } - [FieldDefinition(11, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will add torrents and magnets to rTorrent in a stopped state. This may break magnet files.")] + [FieldDefinition(11, Label = "DownloadClientRTorrentSettingsAddStopped", Type = FieldType.Checkbox, HelpText = "DownloadClientRTorrentSettingsAddStoppedHelpText")] public bool AddStopped { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs index 6a7b372e4..0c2c30e31 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrent.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -28,8 +29,9 @@ namespace NzbDrone.Core.Download.Clients.UTorrent IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(torrentFileInfoReader, httpClient, configService, diskProvider, remotePathMappingService, logger, localizationService) { _proxy = proxy; @@ -141,7 +143,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent if (torrent.Status.HasFlag(UTorrentTorrentStatus.Error)) { item.Status = DownloadItemStatus.Warning; - item.Message = "uTorrent is reporting an error"; + item.Message = _localizationService.GetLocalizedString("DownloadClientUTorrentTorrentStateError"); } else if (torrent.Status.HasFlag(UTorrentTorrentStatus.Loaded) && torrent.Status.HasFlag(UTorrentTorrentStatus.Checked) && torrent.Remaining == 0 && torrent.Progress == 1.0) @@ -264,15 +266,20 @@ namespace NzbDrone.Core.Download.Clients.UTorrent if (version < 25406) { - return new ValidationFailure(string.Empty, "Old uTorrent client with unsupported API, need 3.0 or higher"); + return new ValidationFailure(string.Empty, + _localizationService.GetLocalizedString("DownloadClientValidationErrorVersion", + new Dictionary + { + { "clientName", Name }, { "requiredVersion", "3.0" }, { "reportedVersion", version } + })); } } catch (DownloadClientAuthenticationException ex) { _logger.Error(ex, ex.Message); - return new NzbDroneValidationFailure("Username", "Authentication failure") + return new NzbDroneValidationFailure("Username", _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailure")) { - DetailedDescription = "Please verify your username and password." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationAuthenticationFailureDetail", new Dictionary { { "clientName", Name } }) }; } catch (WebException ex) @@ -280,19 +287,19 @@ namespace NzbDrone.Core.Download.Clients.UTorrent _logger.Error(ex, "Unable to connect to uTorrent"); if (ex.Status == WebExceptionStatus.ConnectFailure) { - return new NzbDroneValidationFailure("Host", "Unable to connect") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { - DetailedDescription = "Please verify the hostname and port." + DetailedDescription = _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnectDetail") }; } - return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationUnknownException", new Dictionary { { "exception", ex.Message } })); } catch (Exception ex) { _logger.Error(ex, "Failed to test uTorrent"); - return new NzbDroneValidationFailure("Host", "Unable to connect to uTorrent") + return new NzbDroneValidationFailure("Host", _localizationService.GetLocalizedString("DownloadClientValidationUnableToConnect", new Dictionary { { "clientName", Name } })) { DetailedDescription = ex.Message }; @@ -310,7 +317,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent catch (Exception ex) { _logger.Error(ex, "Failed to get torrents"); - return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message); + return new NzbDroneValidationFailure(string.Empty, _localizationService.GetLocalizedString("DownloadClientValidationTestTorrents", new Dictionary { { "exceptionMessage", ex.Message } })); } return null; diff --git a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs index e6ae23df5..711815bc2 100644 --- a/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs +++ b/src/NzbDrone.Core/Download/Clients/uTorrent/UTorrentSettings.cs @@ -34,10 +34,13 @@ namespace NzbDrone.Core.Download.Clients.UTorrent [FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)] public int Port { get; set; } - [FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to uTorrent")] + [FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "DownloadClientSettingsUseSslHelpText")] + [FieldToken(TokenField.HelpText, "UseSsl", "clientName", "uTorrent")] public bool UseSsl { get; set; } - [FieldDefinition(3, Label = "Url Base", Type = FieldType.Textbox, Advanced = true, HelpText = "Adds a prefix to the uTorrent url, e.g. http://[host]:[port]/[urlBase]/api")] + [FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsUrlBaseHelpText")] + [FieldToken(TokenField.HelpText, "UrlBase", "clientName", "uTorrent")] + [FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/api")] public string UrlBase { get; set; } [FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)] @@ -46,19 +49,20 @@ namespace NzbDrone.Core.Download.Clients.UTorrent [FieldDefinition(5, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)] public string Password { get; set; } - [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated non-Sonarr downloads. Using a category is optional, but strongly recommended.")] + [FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")] public string TvCategory { get; set; } - [FieldDefinition(7, Label = "Post-Import Category", Type = FieldType.Textbox, Advanced = true, HelpText = "Category for Sonarr to set after it has imported the download. Sonarr will not remove the torrent if seeding has finished. Leave blank to keep same category.")] + [FieldDefinition(7, Label = "PostImportCategory", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientSettingsPostImportCategoryHelpText")] public string TvImportedCategory { get; set; } - [FieldDefinition(8, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")] + [FieldDefinition(8, Label = "DownloadClientSettingsRecentPriority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "DownloadClientSettingsRecentPriorityHelpText")] public int RecentTvPriority { get; set; } - [FieldDefinition(9, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")] + [FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "DownloadClientSettingsOlderPriorityHelpText")] public int OlderTvPriority { get; set; } - [FieldDefinition(10, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "Initial state for torrents added to uTorrent")] + [FieldDefinition(10, Label = "DownloadClientSettingsInitialState", Type = FieldType.Select, SelectOptions = typeof(UTorrentState), HelpText = "DownloadClientSettingsInitialStateHelpText")] + [FieldToken(TokenField.HelpText, "DownloadClientSettingsInitialState", "clientName", "uTorrent")] public int IntialState { get; set; } public NzbDroneValidationResult Validate() diff --git a/src/NzbDrone.Core/Download/DownloadClientBase.cs b/src/NzbDrone.Core/Download/DownloadClientBase.cs index c87e30fc0..887091891 100644 --- a/src/NzbDrone.Core/Download/DownloadClientBase.cs +++ b/src/NzbDrone.Core/Download/DownloadClientBase.cs @@ -6,6 +6,7 @@ using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.ThingiProvider; @@ -20,6 +21,7 @@ namespace NzbDrone.Core.Download protected readonly IDiskProvider _diskProvider; protected readonly IRemotePathMappingService _remotePathMappingService; protected readonly Logger _logger; + protected readonly ILocalizationService _localizationService; public abstract string Name { get; } @@ -41,12 +43,14 @@ namespace NzbDrone.Core.Download protected DownloadClientBase(IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, - Logger logger) + Logger logger, + ILocalizationService localizationService) { _configService = configService; _diskProvider = diskProvider; _remotePathMappingService = remotePathMappingService; _logger = logger; + _localizationService = localizationService; } public override string ToString() diff --git a/src/NzbDrone.Core/Download/TorrentClientBase.cs b/src/NzbDrone.Core/Download/TorrentClientBase.cs index a1e921872..872b2e9ba 100644 --- a/src/NzbDrone.Core/Download/TorrentClientBase.cs +++ b/src/NzbDrone.Core/Download/TorrentClientBase.cs @@ -9,6 +9,7 @@ using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.MediaFiles.TorrentInfo; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; @@ -24,12 +25,13 @@ namespace NzbDrone.Core.Download protected readonly ITorrentFileInfoReader _torrentFileInfoReader; protected TorrentClientBase(ITorrentFileInfoReader torrentFileInfoReader, - IHttpClient httpClient, - IConfigService configService, - IDiskProvider diskProvider, - IRemotePathMappingService remotePathMappingService, - Logger logger) - : base(configService, diskProvider, remotePathMappingService, logger) + IHttpClient httpClient, + IConfigService configService, + IDiskProvider diskProvider, + IRemotePathMappingService remotePathMappingService, + Logger logger, + ILocalizationService localizationService) + : base(configService, diskProvider, remotePathMappingService, logger, localizationService) { _httpClient = httpClient; _torrentFileInfoReader = torrentFileInfoReader; diff --git a/src/NzbDrone.Core/Download/UsenetClientBase.cs b/src/NzbDrone.Core/Download/UsenetClientBase.cs index 319f3ad5a..14d87ef09 100644 --- a/src/NzbDrone.Core/Download/UsenetClientBase.cs +++ b/src/NzbDrone.Core/Download/UsenetClientBase.cs @@ -6,6 +6,7 @@ using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Localization; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.RemotePathMappings; @@ -24,8 +25,9 @@ namespace NzbDrone.Core.Download IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, IValidateNzbs nzbValidationService, - Logger logger) - : base(configService, diskProvider, remotePathMappingService, logger) + Logger logger, + ILocalizationService localizationService) + : base(configService, diskProvider, remotePathMappingService, logger, localizationService) { _httpClient = httpClient; _nzbValidationService = nzbValidationService; diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index fc59a7b89..13b227326 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -139,6 +139,9 @@ "BeforeUpdate": "Before update", "BindAddress": "Bind Address", "BindAddressHelpText": "Valid IP address, localhost or '*' for all interfaces", + "BlackholeFolderHelpText": "Folder in which {appName} will store the {extension} file", + "BlackholeWatchFolder": "Watch Folder", + "BlackholeWatchFolderHelpText": "Folder from which {appName} should import completed downloads", "Blocklist": "Blocklist", "BlocklistLoadError": "Unable to load blocklist", "BlocklistRelease": "Blocklist Release", @@ -171,6 +174,7 @@ "Cancel": "Cancel", "CancelPendingTask": "Are you sure you want to cancel this pending task?", "CancelProcessing": "Cancel Processing", + "Category": "Category", "CertificateValidation": "Certificate Validation", "CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.", "Certification": "Certification", @@ -339,11 +343,13 @@ "DeletedReasonMissingFromDisk": "{appName} was unable to find the file on disk so the file was unlinked from the episode in the database", "DeletedReasonUpgrade": "File was deleted to import an upgrade", "DeletedSeriesDescription": "Series was deleted from TheTVDB", + "Destination": "Destination", "DestinationPath": "Destination Path", "DestinationRelativePath": "Destination Relative Path", "DetailedProgressBar": "Detailed Progress Bar", "DetailedProgressBarHelpText": "Show text on progress bar", "Details": "Details", + "Directory": "Directory", "Disabled": "Disabled", "DisabledForLocalAddresses": "Disabled for Local Addresses", "Discord": "Discord", @@ -360,14 +366,136 @@ "DownloadClient": "Download Client", "DownloadClientCheckNoneAvailableHealthCheckMessage": "No download client is available", "DownloadClientCheckUnableToCommunicateWithHealthCheckMessage": "Unable to communicate with {downloadClientName}. {errorMessage}", + "DownloadClientDelugeSettingsUrlBaseHelpText": "Adds a prefix to the deluge json url, see {url}", + "DownloadClientDelugeTorrentStateError": "Deluge is reporting an error", + "DownloadClientDelugeValidationLabelPluginFailure": "Configuration of label failed", + "DownloadClientDelugeValidationLabelPluginFailureDetail": "{appName} was unable to add the label to {clientName}.", + "DownloadClientDelugeValidationLabelPluginInactive": "Label plugin not activated", + "DownloadClientDelugeValidationLabelPluginInactiveDetail": "You must have the Label plugin enabled in {clientName} to use categories.", + "DownloadClientDownloadStationProviderMessage": "{appName} is unable to connect to Download Station if 2-Factor Authentication is enabled on your DSM account", + "DownloadClientDownloadStationSettingsDirectory": "Optional shared folder to put downloads into, leave blank to use the default Download Station location", + "DownloadClientDownloadStationValidationApiVersion": "Download Station API version not supported, should be at least {requiredVersion}. It supports from {minVersion} to {maxVersion}", + "DownloadClientDownloadStationValidationFolderMissing": "Folder does not exist", + "DownloadClientDownloadStationValidationFolderMissingDetail": "The folder '{downloadDir}' does not exist, it must be created manually inside the Shared Folder '{sharedFolder}'.", + "DownloadClientDownloadStationValidationNoDefaultDestination": "No default destination", + "DownloadClientDownloadStationValidationNoDefaultDestinationDetail": "You must login into your Diskstation as {username} and manually set it up into DownloadStation settings under BT/HTTP/FTP/NZB -> Location.", + "DownloadClientDownloadStationValidationSharedFolderMissing": "Shared folder does not exist", + "DownloadClientDownloadStationValidationSharedFolderMissingDetail": "The Diskstation does not have a Shared Folder with the name '{sharedFolder}', are you sure you specified it correctly?", + "DownloadClientFloodSettingsAdditionalTags": "Additional Tags", + "DownloadClientFloodSettingsAdditionalTagsHelpText": "Adds properties of media as tags. Hints are examples.", + "DownloadClientFloodSettingsPostImportTags": "Post-Import Tags", + "DownloadClientFloodSettingsPostImportTagsHelpText": "Appends tags after a download is imported.", + "DownloadClientFloodSettingsRemovalInfo": "{appName} will handle automatic removal of torrents based on the current seed criteria in Settings -> Indexers", + "DownloadClientFloodSettingsStartOnAdd": "Start on Add", + "DownloadClientFloodSettingsTagsHelpText": "Initial tags of a download. To be recognized, a download must have all initial tags. This avoids conflicts with unrelated downloads.", + "DownloadClientFloodSettingsUrlBaseHelpText": "Adds a prefix to the Flood API, such as {url}", + "DownloadClientFreeboxApiError": "Freebox API returned error: {errorDescription}", + "DownloadClientFreeboxAuthenticationError": "Authentication to Freebox API failed. Reason: {errorDescription}", + "DownloadClientFreeboxNotLoggedIn": "Not logged in", + "DownloadClientFreeboxSettingsApiUrl": "API URL", + "DownloadClientFreeboxSettingsApiUrlHelpText": "Define Freebox API base URL with API version, eg '{url}', defaults to '{defaultApiUrl}'", + "DownloadClientFreeboxSettingsAppId": "App ID", + "DownloadClientFreeboxSettingsAppIdHelpText": "App ID given when creating access to Freebox API (ie 'app_id')", + "DownloadClientFreeboxSettingsAppToken": "App Token", + "DownloadClientFreeboxSettingsAppTokenHelpText": "App token retrieved when creating access to Freebox API (ie 'app_token')", + "DownloadClientFreeboxSettingsHostHelpText": "Hostname or host IP address of the Freebox, defaults to '{url}' (will only work if on same network)", + "DownloadClientFreeboxSettingsPortHelpText": "Port used to access Freebox interface, defaults to '{port}'", + "DownloadClientFreeboxUnableToReachFreebox": "Unable to reach Freebox API. Verify 'Host', 'Port' or 'Use SSL' settings. (Error: {exceptionMessage})", + "DownloadClientFreeboxUnableToReachFreeboxApi": "Unable to reach Freebox API. Verify 'API URL' setting for base URL and version.", + "DownloadClientNzbVortexMultipleFilesMessage": "Download contains multiple files and is not in a job folder: {outputPath}", + "DownloadClientNzbgetSettingsAddPausedHelpText": "This option requires at least NzbGet version 16.0", + "DownloadClientNzbgetValidationKeepHistoryOverMax": "NzbGet setting KeepHistory should be less than 25000", + "DownloadClientNzbgetValidationKeepHistoryOverMaxDetail": "NzbGet setting KeepHistory is set too high.", + "DownloadClientNzbgetValidationKeepHistoryZero": "NzbGet setting KeepHistory should be greater than 0", + "DownloadClientNzbgetValidationKeepHistoryZeroDetail": "NzbGet setting KeepHistory is set to 0. Which prevents {appName} from seeing completed downloads.", "DownloadClientOptionsLoadError": "Unable to load download client options", + "DownloadClientPneumaticSettingsNzbFolder": "Nzb Folder", + "DownloadClientPneumaticSettingsNzbFolderHelpText": "This folder will need to be reachable from XBMC", + "DownloadClientPneumaticSettingsStrmFolder": "Strm Folder", + "DownloadClientPneumaticSettingsStrmFolderHelpText": ".strm files in this folder will be import by drone", + "DownloadClientQbittorrentSettingsFirstAndLastFirst": "First and Last First", + "DownloadClientQbittorrentSettingsFirstAndLastFirstHelpText": "Download first and last pieces first (qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsInitialStateHelpText": "Initial state for torrents added to qBittorrent. Note that Forced Torrents do not abide by seed restrictions", + "DownloadClientQbittorrentSettingsSequentialOrder": "Sequential Order", + "DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Download in sequential order (qBittorrent 4.1.0+)", + "DownloadClientQbittorrentSettingsUseSslHelpText": "Use a secure connection. See Options -> Web UI -> 'Use HTTPS instead of HTTP' in qBittorrent.", + "DownloadClientQbittorrentTorrentStateDhtDisabled": "qBittorrent cannot resolve magnet link with DHT disabled", + "DownloadClientQbittorrentTorrentStateError": "qBittorrent is reporting an error", + "DownloadClientQbittorrentTorrentStateMetadata": "qBittorrent is downloading metadata", + "DownloadClientQbittorrentTorrentStatePathError": "Unable to Import. Path matches client base download directory, it's possible 'Keep top-level folder' is disabled for this torrent or 'Torrent Content Layout' is NOT set to 'Original' or 'Create Subfolder'?", + "DownloadClientQbittorrentTorrentStateStalled": "The download is stalled with no connections", + "DownloadClientQbittorrentTorrentStateUnknown": "Unknown download state: {state}", + "DownloadClientQbittorrentValidationCategoryAddFailure": "Configuration of category failed", + "DownloadClientQbittorrentValidationCategoryAddFailureDetail": "{appName} was unable to add the label to qBittorrent.", + "DownloadClientQbittorrentValidationCategoryRecommended": "Category is recommended", + "DownloadClientQbittorrentValidationCategoryRecommendedDetail": "{appName} will not attempt to import completed downloads without a category.", + "DownloadClientQbittorrentValidationCategoryUnsupported": "Category is not supported", + "DownloadClientQbittorrentValidationCategoryUnsupportedDetail": "Categories are not supported until qBittorrent version 3.3.0. Please upgrade or try again with an empty Category.", + "DownloadClientQbittorrentValidationQueueingNotEnabled": "Queueing Not Enabled", + "DownloadClientQbittorrentValidationQueueingNotEnabledDetail": "Torrent Queueing is not enabled in your qBittorrent settings. Enable it in qBittorrent or select 'Last' as priority.", + "DownloadClientQbittorrentValidationRemovesAtRatioLimit": "qBittorrent is configured to remove torrents when they reach their Share Ratio Limit", + "DownloadClientQbittorrentValidationRemovesAtRatioLimitDetail": "{appName} will be unable to perform Completed Download Handling as configured. You can fix this in qBittorrent ('Tools -> Options...' in the menu) by changing 'Options -> BitTorrent -> Share Ratio Limiting' from 'Remove them' to 'Pause them'", + "DownloadClientRTorrentProviderMessage": "rTorrent will not pause torrents when they meet the seed criteria. {appName} will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers only when Remove Completed is enabled. After importing it will also set {importedView} as an rTorrent view, which can be used in rTorrent scripts to customize behavior.", + "DownloadClientRTorrentSettingsAddStopped": "Add Stopped", + "DownloadClientRTorrentSettingsAddStoppedHelpText": "Enabling will add torrents and magnets to rTorrent in a stopped state. This may break magnet files.", + "DownloadClientRTorrentSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default rTorrent location", + "DownloadClientRTorrentSettingsUrlPath": "Url Path", + "DownloadClientRTorrentSettingsUrlPathHelpText": "Path to the XMLRPC endpoint, see {url}. This is usually RPC2 or [path to ruTorrent]{url2} when using ruTorrent.", "DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "Download client {downloadClientName} is set to remove completed downloads. This can result in downloads being removed from your client before {appName} can import them.", "DownloadClientRootFolderHealthCheckMessage": "Download client {downloadClientName} places downloads in the root folder {rootFolderPath}. You should not download to a root folder.", + "DownloadClientSabnzbdValidationCheckBeforeDownload": "Disable 'Check before download' option in Sabnbzd", + "DownloadClientSabnzbdValidationCheckBeforeDownloadDetail": "Using 'Check before download' affects {appName} ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective.", + "DownloadClientSabnzbdValidationDevelopVersion": "Sabnzbd develop version, assuming version 3.0.0 or higher.", + "DownloadClientSabnzbdValidationDevelopVersionDetail": "{appName} may not be able to support new features added to SABnzbd when running develop versions.", + "DownloadClientSabnzbdValidationEnableDisableDateSorting": "Disable Date Sorting", + "DownloadClientSabnzbdValidationEnableDisableDateSortingDetail": "You must disable Date sorting for the category {appName} uses to prevent import issues. Go to Sabnzbd to fix it.", + "DownloadClientSabnzbdValidationEnableDisableMovieSorting": "Disable Movie Sorting", + "DownloadClientSabnzbdValidationEnableDisableMovieSortingDetail": "You must disable Movie sorting for the category {appName} uses to prevent import issues. Go to Sabnzbd to fix it.", + "DownloadClientSabnzbdValidationEnableDisableTvSorting": "Disable TV Sorting", + "DownloadClientSabnzbdValidationEnableDisableTvSortingDetail": "You must disable TV sorting for the category {appName} uses to prevent import issues. Go to Sabnzbd to fix it.", + "DownloadClientSabnzbdValidationEnableJobFolders": "Enable Job folders", + "DownloadClientSabnzbdValidationEnableJobFoldersDetail": "{appName} prefers each download to have a separate folder. With * appended to the Folder/Path Sabnzbd will not create these job folders. Go to Sabnzbd to fix it.", + "DownloadClientSabnzbdValidationUnknownVersion": "Unknown Version: {rawVersion}", "DownloadClientSettings": "Download Client Settings", + "DownloadClientSettingsAddPaused": "Add Paused", + "DownloadClientSettingsCategoryHelpText": "Adding a category specific to {appName} avoids conflicts with unrelated non-{appName} downloads. Using a category is optional, but strongly recommended.", + "DownloadClientSettingsCategorySubFolderHelpText": "Adding a category specific to {appName} avoids conflicts with unrelated non-{appName} downloads. Using a category is optional, but strongly recommended. Creates a [category] subdirectory in the output directory.", + "DownloadClientSettingsDestinationHelpText": "Manually specifies download destination, leave blank to use the default", + "DownloadClientSettingsInitialState": "Initial State", + "DownloadClientSettingsInitialStateHelpText": "Initial state for torrents added to {clientName}", + "DownloadClientSettingsOlderPriority": "Older Priority", + "DownloadClientSettingsOlderPriorityHelpText": "Priority to use when grabbing episodes that aired over 14 days ago", + "DownloadClientSettingsPostImportCategoryHelpText": "Category for {appName} to set after it has imported the download. {appName} will not remove torrents in that category even if seeding finished. Leave blank to keep same category.", + "DownloadClientSettingsRecentPriority": "Recent Priority", + "DownloadClientSettingsRecentPriorityHelpText": "Priority to use when grabbing episodes that aired within the last 14 days", + "DownloadClientSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} url, such as {url}", + "DownloadClientSettingsUseSslHelpText": "Use secure connection when connection to {clientName}", "DownloadClientSortingHealthCheckMessage": "Download client {downloadClientName} has {sortingMode} sorting enabled for {appName}'s category. You should disable sorting in your download client to avoid import issues.", "DownloadClientStatusAllClientHealthCheckMessage": "All download clients are unavailable due to failures", "DownloadClientStatusSingleClientHealthCheckMessage": "Download clients unavailable due to failures: {downloadClientNames}", "DownloadClientTagHelpText": "Only use this download client for series with at least one matching tag. Leave blank to use with all series.", + "DownloadClientTransmissionSettingsDirectoryHelpText": "Optional location to put downloads in, leave blank to use the default Transmission location", + "DownloadClientTransmissionSettingsUrlBaseHelpText": "Adds a prefix to the {clientName} rpc url, eg {url}, defaults to '{defaultUrl}'", + "DownloadClientUTorrentTorrentStateError": "uTorrent is reporting an error", + "DownloadClientValidationApiKeyIncorrect": "API Key Incorrect", + "DownloadClientValidationApiKeyRequired": "API Key Required", + "DownloadClientValidationAuthenticationFailure": "Authentication Failure", + "DownloadClientValidationAuthenticationFailureDetail": "Please verify your username and password. Also verify if the host running {appName} isn't blocked from accessing {clientName} by WhiteList limitations in the {clientName} configuration.", + "DownloadClientValidationCategoryMissing": "Category does not exist", + "DownloadClientValidationCategoryMissingDetail": "The category you entered doesn't exist in {clientName}. Create it in {clientName} first.", + "DownloadClientValidationErrorVersion": "{clientName} version should be at least {requiredVersion}. Version reported is {reportedVersion}", + "DownloadClientValidationGroupMissing": "Group does not exist", + "DownloadClientValidationGroupMissingDetail": "The group you entered doesn't exist in {clientName}. Create it in {clientName} first.", + "DownloadClientValidationSslConnectFailure": "Unable to connect through SSL", + "DownloadClientValidationSslConnectFailureDetail": "{appName} is unable to connect to {clientName} using SSL. This problem could be computer related. Please try to configure both {appName} and {clientName} to not use SSL.", + "DownloadClientValidationTestNzbs": "Failed to get the list of NZBs: {exceptionMessage}", + "DownloadClientValidationTestTorrents": "Failed to get the list of torrents: {exceptionMessage}", + "DownloadClientValidationUnableToConnect": "Unable to connect to {clientName}", + "DownloadClientValidationUnableToConnectDetail": "Please verify the hostname and port.", + "DownloadClientValidationUnknownException": "Unknown exception: {exception}", + "DownloadClientValidationVerifySsl": "Verify SSL settings", + "DownloadClientValidationVerifySslDetail": "Please verify your SSL configuration on both {clientName} and {appName}", + "DownloadClientVuzeValidationErrorVersion": "Protocol version not supported, use Vuze 5.0.0.0 or higher with Vuze Web Remote plugin.", "DownloadClients": "Download Clients", "DownloadClientsLoadError": "Unable to load download clients", "DownloadClientsSettingsSummary": "Download clients, download handling and remote path mappings", @@ -379,6 +507,7 @@ "DownloadPropersAndRepacksHelpText": "Whether or not to automatically upgrade to Propers/Repacks", "DownloadPropersAndRepacksHelpTextCustomFormat": "Use 'Do not Prefer' to sort by custom format score over Propers/Repacks", "DownloadPropersAndRepacksHelpTextWarning": "Use custom formats for automatic upgrades to Propers/Repacks", + "DownloadStationStatusExtracting": "Extracting: {progress}%", "DownloadWarning": "Download warning: {warningMessage}", "Downloaded": "Downloaded", "Downloading": "Downloading", @@ -887,6 +1016,7 @@ "NotificationTriggersHelpText": "Select which events should trigger this notification", "NotificationsLoadError": "Unable to load Notifications", "NotificationsTagsHelpText": "Only send notifications for series with at least one matching tag", + "NzbgetHistoryItemMessage": "PAR Status: {parStatus} - Unpack Status: {unpackStatus} - Move Status: {moveStatus} - Script Status: {scriptStatus} - Delete Status: {deleteStatus} - Mark Status: {markStatus}", "Ok": "Ok", "OnApplicationUpdate": "On Application Update", "OnEpisodeFileDelete": "On Episode File Delete", @@ -956,6 +1086,7 @@ "Permissions": "Permissions", "Port": "Port", "PortNumber": "Port Number", + "PostImportCategory": "Post-Import Category", "PosterOptions": "Poster Options", "PosterSize": "Poster Size", "Posters": "Posters", @@ -1202,6 +1333,7 @@ "SeasonPremiere": "Season Premiere", "SeasonPremieresOnly": "Season Premieres Only", "Seasons": "Seasons", + "SecretToken": "Secret Token", "Security": "Security", "Seeders": "Seeders", "SelectAll": "Select All", @@ -1379,6 +1511,14 @@ "ToggleMonitoredToUnmonitored": "Monitored, click to unmonitor", "ToggleUnmonitoredToMonitored": "Unmonitored, click to monitor", "Tomorrow": "Tomorrow", + "TorrentBlackhole": "Torrent Blackhole", + "TorrentBlackholeSaveMagnetFiles": "Save Magnet Files", + "TorrentBlackholeSaveMagnetFilesExtension": "Save Magnet Files Extension", + "TorrentBlackholeSaveMagnetFilesExtensionHelpText": "Extension to use for magnet links, defaults to '.magnet'", + "TorrentBlackholeSaveMagnetFilesHelpText": "Save the magnet link if no .torrent file is available (only useful if the download client supports magnets saved to a file)", + "TorrentBlackholeSaveMagnetFilesReadOnly": "Read Only", + "TorrentBlackholeSaveMagnetFilesReadOnlyHelpText": "Instead of moving files this will instruct {appName} to Copy or Hardlink (depending on settings/system configuration)", + "TorrentBlackholeTorrentFolder": "Torrent Folder", "TorrentDelay": "Torrent Delay", "TorrentDelayHelpText": "Delay in minutes to wait before grabbing a torrent", "TorrentDelayTime": "Torrent Delay: {torrentDelay}", @@ -1415,6 +1555,7 @@ "Underscore": "Underscore", "Ungroup": "Ungroup", "Unknown": "Unknown", + "UnknownDownloadState": "Unknown download state: {state}", "UnknownEventTooltip": "Unknown event", "Unlimited": "Unlimited", "UnmappedFilesOnly": "Unmapped Files Only", @@ -1459,7 +1600,10 @@ "UseProxy": "Use Proxy", "UseSeasonFolder": "Use Season Folder", "UseSeasonFolderHelpText": "Sort episodes into season folders", + "UseSsl": "Use SSL", "Usenet": "Usenet", + "UsenetBlackhole": "Usenet Blackhole", + "UsenetBlackholeNzbFolder": "Nzb Folder", "UsenetDelay": "Usenet Delay", "UsenetDelayHelpText": "Delay in minutes to wait before grabbing a release from Usenet", "UsenetDelayTime": "Usenet Delay: {usenetDelay}", @@ -1485,6 +1629,7 @@ "Wiki": "Wiki", "WithFiles": "With Files", "WouldYouLikeToRestoreBackup": "Would you like to restore the backup '{name}'?", + "XmlRpcPath": "XML RPC Path", "Year": "Year", "Yes": "Yes", "YesCancel": "Yes, Cancel", diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index a14e9f2ff..0e9ff30a4 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -22,6 +22,7 @@ using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore.Extensions; +using Sonarr.Http.ClientSchema; using LogLevel = Microsoft.Extensions.Logging.LogLevel; using PostgresOptions = NzbDrone.Core.Datastore.PostgresOptions; @@ -145,6 +146,8 @@ namespace NzbDrone.Host .AddNzbDroneLogger() .AddDatabase() .AddStartupContext(context); + + SchemaBuilder.Initialize(c); }) .ConfigureServices(services => { diff --git a/src/NzbDrone.Integration.Test/IntegrationTest.cs b/src/NzbDrone.Integration.Test/IntegrationTest.cs index a6721fdcb..9524d39d3 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTest.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTest.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq; using System.Threading; using NLog; using NUnit.Framework; @@ -7,7 +9,6 @@ using NzbDrone.Core.Datastore.Migration.Framework; using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Test.Common; using NzbDrone.Test.Common.Datastore; -using Sonarr.Http.ClientSchema; namespace NzbDrone.Integration.Test { @@ -50,17 +51,20 @@ namespace NzbDrone.Integration.Test // Make sure tasks have been initialized so the config put below doesn't cause errors WaitForCompletion(() => Tasks.All().SelectList(x => x.TaskName).Contains("RssSync")); - Indexers.Post(new Sonarr.Api.V3.Indexers.IndexerResource + var indexer = Indexers.Schema().FirstOrDefault(i => i.Implementation == nameof(Newznab)); + + if (indexer == null) { - EnableRss = false, - EnableInteractiveSearch = false, - EnableAutomaticSearch = false, - ConfigContract = nameof(NewznabSettings), - Implementation = nameof(Newznab), - Name = "NewznabTest", - Protocol = Core.Indexers.DownloadProtocol.Usenet, - Fields = SchemaBuilder.ToSchema(new NewznabSettings()) - }); + throw new NullReferenceException("Expected valid indexer schema, found null"); + } + + indexer.EnableRss = false; + indexer.EnableInteractiveSearch = false; + indexer.EnableAutomaticSearch = false; + indexer.ConfigContract = nameof(NewznabSettings); + indexer.Implementation = nameof(Newznab); + indexer.Name = "NewznabTest"; + indexer.Protocol = Core.Indexers.DownloadProtocol.Usenet; // Change Console Log Level to Debug so we get more details. var config = HostConfig.Get(1); diff --git a/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs b/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs index 6e6d84666..948994c51 100644 --- a/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs +++ b/src/NzbDrone.Test.Common/AutoMoq/AutoMoqer.cs @@ -23,6 +23,8 @@ namespace NzbDrone.Test.Common.AutoMoq SetupAutoMoqer(CreateTestContainer(new Container())); } + public IContainer Container => _container; + public virtual T Resolve() { var result = _container.Resolve(); diff --git a/src/Sonarr.Http/ClientSchema/SchemaBuilder.cs b/src/Sonarr.Http/ClientSchema/SchemaBuilder.cs index 8d2392f2e..cd088677e 100644 --- a/src/Sonarr.Http/ClientSchema/SchemaBuilder.cs +++ b/src/Sonarr.Http/ClientSchema/SchemaBuilder.cs @@ -3,11 +3,13 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.Json; +using DryIoc; using NzbDrone.Common.EnsureThat; using NzbDrone.Common.Extensions; using NzbDrone.Common.Reflection; using NzbDrone.Common.Serializer; using NzbDrone.Core.Annotations; +using NzbDrone.Core.Localization; namespace Sonarr.Http.ClientSchema { @@ -15,6 +17,12 @@ namespace Sonarr.Http.ClientSchema { private const string PRIVATE_VALUE = "********"; private static Dictionary _mappings = new Dictionary(); + private static ILocalizationService _localizationService; + + public static void Initialize(IContainer container) + { + _localizationService = container.Resolve(); + } public static List ToSchema(object model) { @@ -107,13 +115,27 @@ namespace Sonarr.Http.ClientSchema if (propertyInfo.PropertyType.IsSimpleType()) { var fieldAttribute = property.Item2; + + var label = fieldAttribute.Label.IsNotNullOrWhiteSpace() + ? _localizationService.GetLocalizedString(fieldAttribute.Label, + GetTokens(type, fieldAttribute.Label, TokenField.Label)) + : fieldAttribute.Label; + var helpText = fieldAttribute.HelpText.IsNotNullOrWhiteSpace() + ? _localizationService.GetLocalizedString(fieldAttribute.HelpText, + GetTokens(type, fieldAttribute.Label, TokenField.HelpText)) + : fieldAttribute.HelpText; + var helpTextWarning = fieldAttribute.HelpTextWarning.IsNotNullOrWhiteSpace() + ? _localizationService.GetLocalizedString(fieldAttribute.HelpTextWarning, + GetTokens(type, fieldAttribute.Label, TokenField.HelpTextWarning)) + : fieldAttribute.HelpTextWarning; + var field = new Field { Name = prefix + GetCamelCaseName(propertyInfo.Name), - Label = fieldAttribute.Label, + Label = label, Unit = fieldAttribute.Unit, - HelpText = fieldAttribute.HelpText, - HelpTextWarning = fieldAttribute.HelpTextWarning, + HelpText = helpText, + HelpTextWarning = helpTextWarning, HelpLink = fieldAttribute.HelpLink, Order = fieldAttribute.Order, Advanced = fieldAttribute.Advanced, @@ -173,6 +195,24 @@ namespace Sonarr.Http.ClientSchema .ToArray(); } + private static Dictionary GetTokens(Type type, string label, TokenField field) + { + var tokens = new Dictionary(); + + foreach (var propertyInfo in type.GetProperties()) + { + foreach (var attribute in propertyInfo.GetCustomAttributes(true)) + { + if (attribute is FieldTokenAttribute fieldTokenAttribute && fieldTokenAttribute.Field == field && fieldTokenAttribute.Label == label) + { + tokens.Add(fieldTokenAttribute.Token, fieldTokenAttribute.Value); + } + } + } + + return tokens; + } + private static List GetSelectOptions(Type selectOptions) { if (selectOptions.IsEnum)