Cleanup Language and Localization code

pull/1057/head
Qstick 2 years ago
parent cad4f3740b
commit 8c10f8b55c

@ -7,7 +7,7 @@ import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
import { fetchIndexers } from 'Store/Actions/indexerActions';
import { fetchIndexerStatus } from 'Store/Actions/indexerStatusActions';
import { fetchAppProfiles, fetchGeneralSettings, fetchIndexerCategories, fetchLanguages, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchAppProfiles, fetchGeneralSettings, fetchIndexerCategories, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import { fetchTags } from 'Store/Actions/tagActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
@ -48,7 +48,6 @@ const selectIsPopulated = createSelector(
(state) => state.tags.isPopulated,
(state) => state.settings.ui.isPopulated,
(state) => state.settings.general.isPopulated,
(state) => state.settings.languages.isPopulated,
(state) => state.settings.appProfiles.isPopulated,
(state) => state.indexers.isPopulated,
(state) => state.indexerStatus.isPopulated,
@ -59,7 +58,6 @@ const selectIsPopulated = createSelector(
tagsIsPopulated,
uiSettingsIsPopulated,
generalSettingsIsPopulated,
languagesIsPopulated,
appProfilesIsPopulated,
indexersIsPopulated,
indexerStatusIsPopulated,
@ -71,7 +69,6 @@ const selectIsPopulated = createSelector(
tagsIsPopulated &&
uiSettingsIsPopulated &&
generalSettingsIsPopulated &&
languagesIsPopulated &&
appProfilesIsPopulated &&
indexersIsPopulated &&
indexerStatusIsPopulated &&
@ -86,7 +83,6 @@ const selectErrors = createSelector(
(state) => state.tags.error,
(state) => state.settings.ui.error,
(state) => state.settings.general.error,
(state) => state.settings.languages.error,
(state) => state.settings.appProfiles.error,
(state) => state.indexers.error,
(state) => state.indexerStatus.error,
@ -97,7 +93,6 @@ const selectErrors = createSelector(
tagsError,
uiSettingsError,
generalSettingsError,
languagesError,
appProfilesError,
indexersError,
indexerStatusError,
@ -109,7 +104,6 @@ const selectErrors = createSelector(
tagsError ||
uiSettingsError ||
generalSettingsError ||
languagesError ||
appProfilesError ||
indexersError ||
indexerStatusError ||
@ -123,7 +117,6 @@ const selectErrors = createSelector(
tagsError,
uiSettingsError,
generalSettingsError,
languagesError,
appProfilesError,
indexersError,
indexerStatusError,
@ -166,9 +159,6 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchTags() {
dispatch(fetchTags());
},
dispatchFetchLanguages() {
dispatch(fetchLanguages());
},
dispatchFetchIndexers() {
dispatch(fetchIndexers());
},
@ -216,7 +206,6 @@ class PageConnector extends Component {
if (!this.props.isPopulated) {
this.props.dispatchFetchCustomFilters();
this.props.dispatchFetchTags();
this.props.dispatchFetchLanguages();
this.props.dispatchFetchAppProfiles();
this.props.dispatchFetchIndexers();
this.props.dispatchFetchIndexerStatus();
@ -242,7 +231,6 @@ class PageConnector extends Component {
isPopulated,
hasError,
dispatchFetchTags,
dispatchFetchLanguages,
dispatchFetchAppProfiles,
dispatchFetchIndexers,
dispatchFetchIndexerStatus,
@ -283,7 +271,6 @@ PageConnector.propTypes = {
isSidebarVisible: PropTypes.bool.isRequired,
dispatchFetchCustomFilters: PropTypes.func.isRequired,
dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchAppProfiles: PropTypes.func.isRequired,
dispatchFetchIndexers: PropTypes.func.isRequired,
dispatchFetchIndexerStatus: PropTypes.func.isRequired,

@ -62,8 +62,6 @@ class UISettings extends Component {
...otherProps
} = this.props;
const uiLanguages = languages.filter((item) => item.value !== 'Original');
const themeOptions = Object.keys(themes)
.map((theme) => ({ key: theme, value: titleCase(theme) }));
@ -172,7 +170,7 @@ class UISettings extends Component {
<FormInputGroup
type={inputTypes.SELECT}
name="uiLanguage"
values={uiLanguages}
values={languages}
helpText={translate('UILanguageHelpText')}
helpTextWarning={translate('UILanguageHelpTextWarning')}
onChange={onInputChange}

@ -3,6 +3,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { clearPendingChanges } from 'Store/Actions/baseActions';
import { fetchLocalizationOptions } from 'Store/Actions/localizationActions';
import { fetchUISettings, saveUISettings, setUISettingsValue } from 'Store/Actions/settingsActions';
import createSettingsSectionSelector from 'Store/Selectors/createSettingsSectionSelector';
import UISettings from './UISettings';
@ -11,18 +12,19 @@ const SECTION = 'ui';
function createLanguagesSelector() {
return createSelector(
(state) => state.settings.languages,
(languages) => {
const items = languages.items;
const filterItems = ['Any', 'Unknown'];
(state) => state.localization,
(localization) => {
console.log(localization);
const items = localization.items;
if (!items) {
return [];
}
const newItems = items.filter((lang) => !filterItems.includes(lang.name)).map((item) => {
const newItems = items.filter((lang) => !items.includes(lang.name)).map((item) => {
return {
key: item.id,
key: item.value,
value: item.name
};
});
@ -51,6 +53,7 @@ const mapDispatchToProps = {
setUISettingsValue,
saveUISettings,
fetchUISettings,
fetchLocalizationOptions,
clearPendingChanges
};
@ -61,6 +64,7 @@ class UISettingsConnector extends Component {
componentDidMount() {
this.props.fetchUISettings();
this.props.fetchLocalizationOptions();
}
componentWillUnmount() {
@ -96,6 +100,7 @@ UISettingsConnector.propTypes = {
setUISettingsValue: PropTypes.func.isRequired,
saveUISettings: PropTypes.func.isRequired,
fetchUISettings: PropTypes.func.isRequired,
fetchLocalizationOptions: PropTypes.func.isRequired,
clearPendingChanges: PropTypes.func.isRequired
};

@ -1,48 +0,0 @@
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import { createThunk } from 'Store/thunks';
//
// Variables
const section = 'settings.languages';
//
// Actions Types
export const FETCH_LANGUAGES = 'settings/languages/fetchLanguages';
//
// Action Creators
export const fetchLanguages = createThunk(FETCH_LANGUAGES);
//
// Details
export default {
//
// State
defaultState: {
isFetching: false,
isPopulated: false,
error: null,
items: []
},
//
// Action Handlers
actionHandlers: {
[FETCH_LANGUAGES]: createFetchHandler(section, '/language')
},
//
// Reducers
reducers: {
}
};

@ -7,6 +7,7 @@ import * as indexers from './indexerActions';
import * as indexerIndex from './indexerIndexActions';
import * as indexerStats from './indexerStatsActions';
import * as indexerStatus from './indexerStatusActions';
import * as localization from './localizationActions';
import * as oAuth from './oAuthActions';
import * as paths from './pathActions';
import * as providerOptions from './providerOptionActions';
@ -25,6 +26,7 @@ export default [
paths,
providerOptions,
releases,
localization,
indexers,
indexerIndex,
indexerStats,

@ -0,0 +1,39 @@
import createFetchHandler from 'Store/Actions/Creators/createFetchHandler';
import { createThunk, handleThunks } from 'Store/thunks';
import createHandleActions from './Creators/createHandleActions';
//
// Variables
export const section = 'localization';
//
// State
export const defaultState = {
isFetching: false,
isPopulated: false,
error: null,
items: []
};
//
// Actions Types
export const FETCH_LOCALIZATION_OPTIONS = 'localization/fetchLocalizationOptions';
//
// Action Creators
export const fetchLocalizationOptions = createThunk(FETCH_LOCALIZATION_OPTIONS);
//
// Action Handlers
export const actionHandlers = handleThunks({
[FETCH_LOCALIZATION_OPTIONS]: createFetchHandler(section, '/localization/options')
});
//
// Reducers
export const reducers = createHandleActions({}, defaultState, section);

@ -8,7 +8,6 @@ import downloadClients from './Settings/downloadClients';
import general from './Settings/general';
import indexerCategories from './Settings/indexerCategories';
import indexerProxies from './Settings/indexerProxies';
import languages from './Settings/languages';
import notifications from './Settings/notifications';
import ui from './Settings/ui';
@ -16,7 +15,6 @@ export * from './Settings/downloadClients';
export * from './Settings/general';
export * from './Settings/indexerCategories';
export * from './Settings/indexerProxies';
export * from './Settings/languages';
export * from './Settings/notifications';
export * from './Settings/applications';
export * from './Settings/appProfiles';
@ -38,7 +36,6 @@ export const defaultState = {
general: general.defaultState,
indexerCategories: indexerCategories.defaultState,
indexerProxies: indexerProxies.defaultState,
languages: languages.defaultState,
notifications: notifications.defaultState,
applications: applications.defaultState,
appProfiles: appProfiles.defaultState,
@ -68,7 +65,6 @@ export const actionHandlers = handleThunks({
...general.actionHandlers,
...indexerCategories.actionHandlers,
...indexerProxies.actionHandlers,
...languages.actionHandlers,
...notifications.actionHandlers,
...applications.actionHandlers,
...appProfiles.actionHandlers,
@ -89,7 +85,6 @@ export const reducers = createHandleActions({
...general.reducers,
...indexerCategories.reducers,
...indexerProxies.reducers,
...languages.reducers,
...notifications.reducers,
...applications.reducers,
...appProfiles.reducers,

@ -1,85 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Languages
{
[TestFixture]
public class LanguageFixture : CoreTest
{
public static object[] FromIntCases =
{
new object[] { 1, Language.English },
new object[] { 2, Language.French },
new object[] { 3, Language.Spanish },
new object[] { 4, Language.German },
new object[] { 5, Language.Italian },
new object[] { 6, Language.Danish },
new object[] { 7, Language.Dutch },
new object[] { 8, Language.Japanese },
new object[] { 9, Language.Icelandic },
new object[] { 10, Language.Chinese },
new object[] { 11, Language.Russian },
new object[] { 12, Language.Polish },
new object[] { 13, Language.Vietnamese },
new object[] { 14, Language.Swedish },
new object[] { 15, Language.Norwegian },
new object[] { 16, Language.Finnish },
new object[] { 17, Language.Turkish },
new object[] { 18, Language.Portuguese },
new object[] { 19, Language.Flemish },
new object[] { 20, Language.Greek },
new object[] { 21, Language.Korean },
new object[] { 22, Language.Hungarian },
new object[] { 23, Language.Hebrew },
new object[] { 24, Language.Lithuanian },
new object[] { 25, Language.Czech }
};
public static object[] ToIntCases =
{
new object[] { Language.English, 1 },
new object[] { Language.French, 2 },
new object[] { Language.Spanish, 3 },
new object[] { Language.German, 4 },
new object[] { Language.Italian, 5 },
new object[] { Language.Danish, 6 },
new object[] { Language.Dutch, 7 },
new object[] { Language.Japanese, 8 },
new object[] { Language.Icelandic, 9 },
new object[] { Language.Chinese, 10 },
new object[] { Language.Russian, 11 },
new object[] { Language.Polish, 12 },
new object[] { Language.Vietnamese, 13 },
new object[] { Language.Swedish, 14 },
new object[] { Language.Norwegian, 15 },
new object[] { Language.Finnish, 16 },
new object[] { Language.Turkish, 17 },
new object[] { Language.Portuguese, 18 },
new object[] { Language.Flemish, 19 },
new object[] { Language.Greek, 20 },
new object[] { Language.Korean, 21 },
new object[] { Language.Hungarian, 22 },
new object[] { Language.Hebrew, 23 },
new object[] { Language.Lithuanian, 24 },
new object[] { Language.Czech, 25 }
};
[Test]
[TestCaseSource("FromIntCases")]
public void should_be_able_to_convert_int_to_languageTypes(int source, Language expected)
{
var language = (Language)source;
language.Should().Be(expected);
}
[Test]
[TestCaseSource("ToIntCases")]
public void should_be_able_to_convert_languageTypes_to_int(Language source, int expected)
{
var i = (int)source;
i.Should().Be(expected);
}
}
}

@ -3,7 +3,6 @@ using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Localization;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
@ -16,7 +15,7 @@ namespace NzbDrone.Core.Test.Localization
[SetUp]
public void Setup()
{
Mocker.GetMock<IConfigService>().Setup(m => m.UILanguage).Returns((int)Language.English);
Mocker.GetMock<IConfigService>().Setup(m => m.UILanguage).Returns("en");
Mocker.GetMock<IAppFolderInfo>().Setup(m => m.StartUpFolder).Returns(TestContext.CurrentContext.TestDirectory);
}

@ -6,7 +6,6 @@ using NLog;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Security;
@ -139,9 +138,9 @@ namespace NzbDrone.Core.Configuration
set { SetValue("EnableColorImpairedMode", value); }
}
public int UILanguage
public string UILanguage
{
get { return GetValueInt("UILanguage", (int)Language.English); }
get { return GetValue("UILanguage", "en"); }
set { SetValue("UILanguage", value); }
}

@ -22,7 +22,7 @@ namespace NzbDrone.Core.Configuration
string TimeFormat { get; set; }
bool ShowRelativeDates { get; set; }
bool EnableColorImpairedMode { get; set; }
int UILanguage { get; set; }
string UILanguage { get; set; }
//Internal
string PlexClientIdentifier { get; }

@ -1,48 +0,0 @@
using System;
using System.Data;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Datastore.Converters
{
public class DapperLanguageIntConverter : SqlMapper.TypeHandler<Language>
{
public override void SetValue(IDbDataParameter parameter, Language value)
{
if (value == null)
{
throw new InvalidOperationException("Attempted to save a language that isn't really a language");
}
else
{
parameter.Value = (int)value;
}
}
public override Language Parse(object value)
{
if (value == null || value is DBNull)
{
return Language.Unknown;
}
return (Language)Convert.ToInt32(value);
}
}
public class LanguageIntConverter : JsonConverter<Language>
{
public override Language Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var item = reader.GetInt32();
return (Language)item;
}
public override void Write(Utf8JsonWriter writer, Language value, JsonSerializerOptions options)
{
writer.WriteNumberValue((int)value);
}
}
}

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(021)]
public class localization_setting_to_string : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(FixLocalizationConfig);
}
private void FixLocalizationConfig(IDbConnection conn, IDbTransaction tran)
{
string uiLanguage;
string uiCulture;
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = tran;
cmd.CommandText = "SELECT \"Value\" FROM \"Config\" WHERE \"Key\" = 'uilanguage'";
uiLanguage = (string)cmd.ExecuteScalar();
}
if (uiLanguage != null && int.TryParse(uiLanguage, out var uiLanguageInt))
{
uiCulture = _uiMapping.GetValueOrDefault(uiLanguageInt) ?? "en";
using (var insertCmd = conn.CreateCommand())
{
insertCmd.Transaction = tran;
insertCmd.CommandText = string.Format("UPDATE \"Config\" SET \"Value\" = '{0}' WHERE \"Key\" = 'uilanguage'", uiCulture);
insertCmd.ExecuteNonQuery();
}
}
}
private readonly Dictionary<int, string> _uiMapping = new Dictionary<int, string>()
{
{ 1, "en" },
{ 2, "fr" },
{ 3, "es" },
{ 4, "de" },
{ 5, "it" },
{ 6, "da" },
{ 7, "nl" },
{ 8, "ja" },
{ 9, "is" },
{ 10, "zh_CN" },
{ 11, "ru" },
{ 12, "pl" },
{ 13, "vi" },
{ 14, "sv" },
{ 15, "nb_NO" },
{ 16, "fi" },
{ 17, "tr" },
{ 18, "pt" },
{ 19, "en" },
{ 20, "el" },
{ 21, "ko" },
{ 22, "hu" },
{ 23, "he" },
{ 24, "lt" },
{ 25, "cs" },
{ 26, "hi" },
{ 27, "ro" },
{ 28, "th" },
{ 29, "bg" },
{ 30, "pt_BR" },
{ 31, "ar" },
{ 32, "uk" },
{ 33, "fa" },
{ 34, "be" },
{ 35, "zh_TW" },
};
}
}

@ -14,7 +14,6 @@ using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerVersions;
using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Jobs;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Notifications;
using NzbDrone.Core.Parser.Model;
@ -113,8 +112,6 @@ namespace NzbDrone.Core.Datastore
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<int>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<KeyValuePair<string, int>>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<KeyValuePair<string, int>>());
SqlMapper.AddTypeHandler(new DapperLanguageIntConverter());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<Language>>(new LanguageIntConverter()));
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<string>>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ReleaseInfo>());
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<HashSet<int>>());

@ -1,193 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Languages
{
public class Language : IEmbeddedDocument, IEquatable<Language>
{
public int Id { get; set; }
public string Name { get; set; }
public Language()
{
}
private Language(int id, string name)
{
Id = id;
Name = name;
}
public override string ToString()
{
return Name;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
public bool Equals(Language other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Id.Equals(other.Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return Equals(obj as Language);
}
public static bool operator ==(Language left, Language right)
{
return Equals(left, right);
}
public static bool operator !=(Language left, Language right)
{
return !Equals(left, right);
}
public static Language Unknown => new Language(0, "Unknown");
public static Language English => new Language(1, "English");
public static Language French => new Language(2, "French");
public static Language Spanish => new Language(3, "Spanish");
public static Language German => new Language(4, "German");
public static Language Italian => new Language(5, "Italian");
public static Language Danish => new Language(6, "Danish");
public static Language Dutch => new Language(7, "Dutch");
public static Language Japanese => new Language(8, "Japanese");
public static Language Icelandic => new Language(9, "Icelandic");
public static Language Chinese => new Language(10, "Chinese (Simplified)");
public static Language Russian => new Language(11, "Russian");
public static Language Polish => new Language(12, "Polish");
public static Language Vietnamese => new Language(13, "Vietnamese");
public static Language Swedish => new Language(14, "Swedish");
public static Language Norwegian => new Language(15, "Norwegian");
public static Language Finnish => new Language(16, "Finnish");
public static Language Turkish => new Language(17, "Turkish");
public static Language Portuguese => new Language(18, "Portuguese");
public static Language Flemish => new Language(19, "Flemish");
public static Language Greek => new Language(20, "Greek");
public static Language Korean => new Language(21, "Korean");
public static Language Hungarian => new Language(22, "Hungarian");
public static Language Hebrew => new Language(23, "Hebrew");
public static Language Lithuanian => new Language(24, "Lithuanian");
public static Language Czech => new Language(25, "Czech");
public static Language Hindi => new Language(26, "Hindi");
public static Language Romanian => new Language(27, "Romanian");
public static Language Thai => new Language(28, "Thai");
public static Language Bulgarian => new Language(29, "Bulgarian");
public static Language PortugueseBR => new Language(30, "Portuguese (Brazil)");
public static Language Arabic => new Language(31, "Arabic");
public static Language Ukrainian => new Language(32, "Ukrainian");
public static Language Persian => new Language(33, "Persian");
public static Language Bengali => new Language(34, "Bengali");
public static Language ChineseTW => new Language(35, "Chinese (Traditional)");
public static Language Any => new Language(-1, "Any");
public static Language Original => new Language(-2, "Original");
public static List<Language> All
{
get
{
return new List<Language>
{
Unknown,
English,
French,
Spanish,
German,
Italian,
Danish,
Dutch,
Japanese,
Icelandic,
Chinese,
Russian,
Polish,
Vietnamese,
Swedish,
Norwegian,
Finnish,
Turkish,
Portuguese,
Flemish,
Greek,
Korean,
Hungarian,
Hebrew,
Lithuanian,
Czech,
Romanian,
Hindi,
Thai,
Bulgarian,
Any,
Original
};
}
}
public static Language FindById(int id)
{
if (id == 0)
{
return Unknown;
}
Language language = All.FirstOrDefault(v => v.Id == id);
if (language == null)
{
throw new ArgumentException("ID does not match a known language", nameof(id));
}
return language;
}
public static explicit operator Language(int id)
{
return FindById(id);
}
public static explicit operator int(Language language)
{
return language.Id;
}
public static explicit operator Language(string lang)
{
var language = All.FirstOrDefault(v => v.Name.Equals(lang, StringComparison.InvariantCultureIgnoreCase));
if (language == null)
{
throw new ArgumentException("Language does not match a known language", nameof(lang));
}
return language;
}
}
}

@ -1,13 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace NzbDrone.Core.Languages
{
public static class LanguageExtensions
{
public static string ToExtendedString(this IEnumerable<Language> languages)
{
return string.Join(", ", languages.Select(l => l.ToString()));
}
}
}

@ -1,13 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Languages
{
public class LanguageFieldConverter : ISelectOptionsConverter
{
public List<SelectOption> GetSelectOptions()
{
return Language.All.ConvertAll(v => new SelectOption { Value = v.Id, Name = v.Name });
}
}
}

@ -1,53 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace NzbDrone.Core.Languages
{
public class LanguagesComparer : IComparer<List<Language>>
{
public int Compare(List<Language> x, List<Language> y)
{
if (!x.Any() && !y.Any())
{
return 0;
}
if (!x.Any() && y.Any())
{
return 1;
}
if (x.Any() && !y.Any())
{
return -1;
}
if (x.Count > 1 && y.Count > 1 && x.Count > y.Count)
{
return 1;
}
if (x.Count > 1 && y.Count > 1 && x.Count < y.Count)
{
return -1;
}
if (x.Count > 1 && y.Count == 1)
{
return 1;
}
if (x.Count == 1 && y.Count > 1)
{
return -1;
}
if (x.Count == 1 && y.Count == 1)
{
return x.First().Name.CompareTo(y.First().Name);
}
return 0;
}
}
}

@ -1,17 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Languages
{
public class RealLanguageFieldConverter : ISelectOptionsConverter
{
public List<SelectOption> GetSelectOptions()
{
return Language.All
.Where(l => l != Language.Unknown && l != Language.Any)
.ToList()
.ConvertAll(v => new SelectOption { Value = v.Id, Name = v.Name });
}
}
}

@ -0,0 +1,14 @@
namespace NzbDrone.Core.Localization
{
public class LocalizationOption
{
public LocalizationOption(string name, string value)
{
Name = name;
Value = value;
}
public string Name { get; set; }
public string Value { get; set; }
}
}

@ -9,7 +9,6 @@ using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Languages;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
@ -20,6 +19,7 @@ namespace NzbDrone.Core.Localization
Dictionary<string, string> GetLocalizationDictionary();
string GetLocalizedString(string phrase);
string GetLocalizedString(string phrase, string language);
IEnumerable<LocalizationOption> GetLocalizationOptions();
}
public class LocalizationService : ILocalizationService, IHandleAsync<ConfigSavedEvent>
@ -45,14 +45,14 @@ namespace NzbDrone.Core.Localization
public Dictionary<string, string> GetLocalizationDictionary()
{
var language = GetSetLanguageFileName();
var language = _configService.UILanguage;
return GetLocalizationDictionary(language);
}
public string GetLocalizedString(string phrase)
{
var language = GetSetLanguageFileName();
var language = _configService.UILanguage;
return GetLocalizedString(phrase, language);
}
@ -66,7 +66,7 @@ namespace NzbDrone.Core.Localization
if (language.IsNullOrWhiteSpace())
{
language = GetSetLanguageFileName();
language = _configService.UILanguage;
}
if (language == null)
@ -84,17 +84,44 @@ namespace NzbDrone.Core.Localization
return phrase;
}
private string GetSetLanguageFileName()
public IEnumerable<LocalizationOption> GetLocalizationOptions()
{
var isoLanguage = IsoLanguages.Get((Language)_configService.UILanguage);
var language = isoLanguage.TwoLetterCode;
if (isoLanguage.CountryCode.IsNotNullOrWhiteSpace())
{
language = string.Format("{0}_{1}", language, isoLanguage.CountryCode);
}
return language;
yield return new LocalizationOption("العربية", "ar");
yield return new LocalizationOption("Български", "bg");
yield return new LocalizationOption("বাংলা (বাংলাদেশ)", "bn");
yield return new LocalizationOption("Català", "ca");
yield return new LocalizationOption("Čeština", "cs");
yield return new LocalizationOption("Dansk", "da");
yield return new LocalizationOption("Deutsch", "de");
yield return new LocalizationOption("English", "en");
yield return new LocalizationOption("Ελληνικά", "el");
yield return new LocalizationOption("Español", "es");
yield return new LocalizationOption("فارسی", "fa");
yield return new LocalizationOption("Suomi", "fi");
yield return new LocalizationOption("Français", "fr");
yield return new LocalizationOption("עִבְרִית", "he");
yield return new LocalizationOption("हिन्दी", "hi");
yield return new LocalizationOption("Magyar", "hu");
yield return new LocalizationOption("Íslenska", "is");
yield return new LocalizationOption("Italiano", "it");
yield return new LocalizationOption("日本語", "ja");
yield return new LocalizationOption("한국어", "ko");
yield return new LocalizationOption("Lietuvių", "lt");
yield return new LocalizationOption("Norsk bokmål", "nb_NO");
yield return new LocalizationOption("Nederlands", "nl");
yield return new LocalizationOption("Polski", "pl");
yield return new LocalizationOption("Português", "pt");
yield return new LocalizationOption("Português (Brasil)", "pt_BR");
yield return new LocalizationOption("Românește", "ro");
yield return new LocalizationOption("Русский", "ru");
yield return new LocalizationOption("Slovenčina", "sk");
yield return new LocalizationOption("Svenska", "sv");
yield return new LocalizationOption("ภาษาไทย", "th");
yield return new LocalizationOption("Türkçe", "tr");
yield return new LocalizationOption("Українська", "uk");
yield return new LocalizationOption("Tiếng Việt", "vi");
yield return new LocalizationOption("汉语 (简化字)", "zh_CN");
yield return new LocalizationOption("漢語 (繁体字)", "zh_TW");
}
private Dictionary<string, string> GetLocalizationDictionary(string language)

@ -1,22 +0,0 @@
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser
{
public class IsoLanguage
{
public string TwoLetterCode { get; set; }
public string ThreeLetterCode { get; set; }
public string CountryCode { get; set; }
public string EnglishName { get; set; }
public Language Language { get; set; }
public IsoLanguage(string twoLetterCode, string countryCode, string threeLetterCode, string englishName, Language language)
{
TwoLetterCode = twoLetterCode;
ThreeLetterCode = threeLetterCode;
CountryCode = countryCode;
EnglishName = englishName;
Language = language;
}
}
}

@ -1,85 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser
{
public static class IsoLanguages
{
private static readonly HashSet<IsoLanguage> All = new HashSet<IsoLanguage>
{
new IsoLanguage("en", "", "eng", "English", Language.English),
new IsoLanguage("fr", "fr", "fra", "French", Language.French),
new IsoLanguage("es", "", "spa", "Spanish", Language.Spanish),
new IsoLanguage("de", "", "deu", "German", Language.German),
new IsoLanguage("it", "", "ita", "Italian", Language.Italian),
new IsoLanguage("da", "", "dan", "Danish", Language.Danish),
new IsoLanguage("nl", "", "nld", "Dutch", Language.Dutch),
new IsoLanguage("ja", "", "jpn", "Japanese", Language.Japanese),
new IsoLanguage("is", "", "isl", "Icelandic", Language.Icelandic),
new IsoLanguage("zh", "cn", "zho", "Chinese (Simplified)", Language.Chinese),
new IsoLanguage("zh", "tw", "zho", "Chinese (Traditional)", Language.ChineseTW),
new IsoLanguage("ru", "", "rus", "Russian", Language.Russian),
new IsoLanguage("pl", "", "pol", "Polish", Language.Polish),
new IsoLanguage("vi", "", "vie", "Vietnamese", Language.Vietnamese),
new IsoLanguage("sv", "", "swe", "Swedish", Language.Swedish),
new IsoLanguage("no", "", "nor", "Norwegian", Language.Norwegian),
new IsoLanguage("nb", "", "nob", "Norwegian Bokmal", Language.Norwegian),
new IsoLanguage("fi", "", "fin", "Finnish", Language.Finnish),
new IsoLanguage("tr", "", "tur", "Turkish", Language.Turkish),
new IsoLanguage("pt", "pt", "por", "Portuguese", Language.Portuguese),
new IsoLanguage("el", "", "ell", "Greek", Language.Greek),
new IsoLanguage("ko", "", "kor", "Korean", Language.Korean),
new IsoLanguage("hu", "", "hun", "Hungarian", Language.Hungarian),
new IsoLanguage("he", "", "heb", "Hebrew", Language.Hebrew),
new IsoLanguage("cs", "", "ces", "Czech", Language.Czech),
new IsoLanguage("hi", "", "hin", "Hindi", Language.Hindi),
new IsoLanguage("th", "", "tha", "Thai", Language.Thai),
new IsoLanguage("bg", "", "bul", "Bulgarian", Language.Bulgarian),
new IsoLanguage("ro", "", "ron", "Romanian", Language.Romanian),
new IsoLanguage("pt", "br", "", "Portuguese (Brazil)", Language.PortugueseBR),
new IsoLanguage("ar", "", "ara", "Arabic", Language.Arabic),
new IsoLanguage("uk", "", "ukr", "Ukrainian", Language.Ukrainian),
new IsoLanguage("fa", "", "fas", "Persian", Language.Persian),
new IsoLanguage("be", "", "ben", "Bengali", Language.Bengali)
};
public static IsoLanguage Find(string isoCode)
{
var isoArray = isoCode.Split('-');
var langCode = isoArray[0].ToLower();
if (langCode.Length == 2)
{
//Lookup ISO639-1 code
var isoLanguages = All.Where(l => l.TwoLetterCode == langCode).ToList();
if (isoArray.Length > 1)
{
isoLanguages = isoLanguages.Any(l => l.CountryCode == isoArray[1].ToLower()) ?
isoLanguages.Where(l => l.CountryCode == isoArray[1].ToLower()).ToList() : isoLanguages.Where(l => string.IsNullOrEmpty(l.CountryCode)).ToList();
}
return isoLanguages.FirstOrDefault();
}
else if (langCode.Length == 3)
{
//Lookup ISO639-2T code
return All.FirstOrDefault(l => l.ThreeLetterCode == langCode);
}
return null;
}
public static IsoLanguage FindByName(string name)
{
return All.FirstOrDefault(l => l.EnglishName == name.Trim());
}
public static IsoLanguage Get(Language language)
{
return All.FirstOrDefault(l => l.Language == language);
}
}
}

@ -1,272 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser
{
public static class LanguageParser
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(LanguageParser));
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_|^)(?<italian>\b(?:ita|italian)\b)|(?<german>\b(?:german|videomann|ger)\b)|(?<flemish>flemish)|(?<bulgarian>bgaudio)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VO|VFF|VFQ|VFI|VF2|TRUEFRENCH)(?:\W|_))|(?<russian>\brus\b)|(?<english>\beng\b)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)|(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex CaseSensitiveLanguageRegex = new Regex(@"(?<lithuanian>\bLT\b)|(?<czech>\bCZ\b)",
RegexOptions.Compiled);
private static readonly Regex SubtitleLanguageRegex = new Regex(".+?[-_. ](?<iso_code>[a-z]{2,3})(?:[-_. ]forced)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static List<Language> ParseLanguages(string title)
{
var lowerTitle = title.ToLower();
var languages = new List<Language>();
if (lowerTitle.Contains("english"))
{
languages.Add(Language.English);
}
if (lowerTitle.Contains("french"))
{
languages.Add(Language.French);
}
if (lowerTitle.Contains("spanish"))
{
languages.Add(Language.Spanish);
}
if (lowerTitle.Contains("danish"))
{
languages.Add(Language.Danish);
}
if (lowerTitle.Contains("dutch"))
{
languages.Add(Language.Dutch);
}
if (lowerTitle.Contains("japanese"))
{
languages.Add(Language.Japanese);
}
if (lowerTitle.Contains("icelandic"))
{
languages.Add(Language.Icelandic);
}
if (lowerTitle.Contains("mandarin") || lowerTitle.Contains("cantonese") || lowerTitle.Contains("chinese"))
{
languages.Add(Language.Chinese);
}
if (lowerTitle.Contains("korean"))
{
languages.Add(Language.Korean);
}
if (lowerTitle.Contains("russian"))
{
languages.Add(Language.Russian);
}
if (lowerTitle.Contains("romanian"))
{
languages.Add(Language.Romanian);
}
if (lowerTitle.Contains("hindi"))
{
languages.Add(Language.Hindi);
}
if (lowerTitle.Contains("thai"))
{
languages.Add(Language.Thai);
}
if (lowerTitle.Contains("bulgarian"))
{
languages.Add(Language.Bulgarian);
}
if (lowerTitle.Contains("polish"))
{
languages.Add(Language.Polish);
}
if (lowerTitle.Contains("vietnamese"))
{
languages.Add(Language.Vietnamese);
}
if (lowerTitle.Contains("swedish"))
{
languages.Add(Language.Swedish);
}
if (lowerTitle.Contains("norwegian"))
{
languages.Add(Language.Norwegian);
}
if (lowerTitle.Contains("finnish"))
{
languages.Add(Language.Finnish);
}
if (lowerTitle.Contains("turkish"))
{
languages.Add(Language.Turkish);
}
if (lowerTitle.Contains("portuguese"))
{
languages.Add(Language.Portuguese);
}
if (lowerTitle.Contains("hungarian"))
{
languages.Add(Language.Hungarian);
}
if (lowerTitle.Contains("hebrew"))
{
languages.Add(Language.Hebrew);
}
// Case sensitive
var caseSensitiveMatch = CaseSensitiveLanguageRegex.Match(title);
if (caseSensitiveMatch.Groups["lithuanian"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Lithuanian);
}
if (caseSensitiveMatch.Groups["czech"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Czech);
}
var matches = LanguageRegex.Matches(title);
foreach (Match match in matches)
{
if (match.Groups["italian"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Italian);
}
if (match.Groups["german"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.German);
}
if (match.Groups["flemish"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Flemish);
}
if (match.Groups["greek"].Captures.Cast<Capture>().Any())
{
languages.Add(Language.Greek);
}
if (match.Groups["french"].Success)
{
languages.Add(Language.French);
}
if (match.Groups["russian"].Success)
{
languages.Add(Language.Russian);
}
if (match.Groups["english"].Success)
{
languages.Add(Language.English);
}
if (match.Groups["bulgarian"].Success)
{
languages.Add(Language.Bulgarian);
}
if (match.Groups["dutch"].Success)
{
languages.Add(Language.Dutch);
}
if (match.Groups["hungarian"].Success)
{
languages.Add(Language.Hungarian);
}
if (match.Groups["hebrew"].Success)
{
languages.Add(Language.Hebrew);
}
if (match.Groups["chinese"].Success)
{
languages.Add(Language.Chinese);
}
}
if (title.ToLower().Contains("multi"))
{
//Let's add english language to multi release as a safe guard.
if (!languages.Contains(Language.English) && languages.Count < 2)
{
languages.Add(Language.English);
}
}
if (!languages.Any())
{
languages.Add(Language.Unknown);
}
return languages.DistinctBy(l => (int)l).ToList();
}
public static Language ParseSubtitleLanguage(string fileName)
{
try
{
#if !LIBRARY
Logger.Debug("Parsing language from subtitle file: {0}", fileName);
#endif
var simpleFilename = Path.GetFileNameWithoutExtension(fileName);
var languageMatch = SubtitleLanguageRegex.Match(simpleFilename);
if (languageMatch.Success)
{
var isoCode = languageMatch.Groups["iso_code"].Value;
var isoLanguage = IsoLanguages.Find(isoCode);
return isoLanguage?.Language ?? Language.Unknown;
}
#if !LIBRARY
Logger.Debug("Unable to parse langauge from subtitle file: {0}", fileName);
#endif
}
catch (Exception)
{
#if !LIBRARY
Logger.Debug("Failed parsing langauge from subtitle file: {0}", fileName);
#endif
}
return Language.Unknown;
}
}
}

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser.Model
{

@ -16,7 +16,7 @@ namespace Prowlarr.Api.V1.Config
public bool ShowRelativeDates { get; set; }
public bool EnableColorImpairedMode { get; set; }
public int UILanguage { get; set; }
public string UILanguage { get; set; }
public string Theme { get; set; }
}

@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.Languages;
using Prowlarr.Http;
using Prowlarr.Http.REST;
namespace Prowlarr.Api.V1.Languages
{
[V1ApiController()]
public class LanguageController : RestController<LanguageResource>
{
public override LanguageResource GetResourceById(int id)
{
var language = (Language)id;
return new LanguageResource
{
Id = (int)language,
Name = language.ToString()
};
}
[HttpGet]
[Produces("application/json")]
public List<LanguageResource> GetAll()
{
return Language.All.Select(l => new LanguageResource
{
Id = (int)l,
Name = l.ToString()
})
.OrderBy(l => l.Name)
.ToList();
}
}
}

@ -1,13 +0,0 @@
using Newtonsoft.Json;
using Prowlarr.Http.REST;
namespace Prowlarr.Api.V1.Languages
{
public class LanguageResource : RestResource
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
public new int Id { get; set; }
public string Name { get; set; }
public string NameLower => Name.ToLowerInvariant();
}
}

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Serializer;
@ -26,5 +27,12 @@ namespace Prowlarr.Api.V1.Localization
{
return Json(_localizationService.GetLocalizationDictionary().ToResource(), _serializerSettings);
}
[HttpGet("Options")]
[Produces("application/json")]
public ActionResult<IEnumerable<LocalizationOption>> GetLocalizationOptions()
{
return Ok(_localizationService.GetLocalizationOptions());
}
}
}

Loading…
Cancel
Save