New: Localization Framework

pull/2414/head
Qstick 3 years ago
parent 99ccaab6a6
commit 729a876fc7

@ -6,7 +6,7 @@ import { createSelector } from 'reselect';
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
import { fetchArtist } from 'Store/Actions/artistActions';
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
import { fetchImportLists, fetchMetadataProfiles, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchImportLists, fetchLanguages, fetchMetadataProfiles, fetchQualityProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions';
import { fetchTags } from 'Store/Actions/tagActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
@ -46,6 +46,7 @@ const selectIsPopulated = createSelector(
(state) => state.customFilters.isPopulated,
(state) => state.tags.isPopulated,
(state) => state.settings.ui.isPopulated,
(state) => state.settings.languages.isPopulated,
(state) => state.settings.qualityProfiles.isPopulated,
(state) => state.settings.metadataProfiles.isPopulated,
(state) => state.settings.importLists.isPopulated,
@ -54,6 +55,7 @@ const selectIsPopulated = createSelector(
customFiltersIsPopulated,
tagsIsPopulated,
uiSettingsIsPopulated,
languagesIsPopulated,
qualityProfilesIsPopulated,
metadataProfilesIsPopulated,
importListsIsPopulated,
@ -63,6 +65,7 @@ const selectIsPopulated = createSelector(
customFiltersIsPopulated &&
tagsIsPopulated &&
uiSettingsIsPopulated &&
languagesIsPopulated &&
qualityProfilesIsPopulated &&
metadataProfilesIsPopulated &&
importListsIsPopulated &&
@ -75,6 +78,7 @@ const selectErrors = createSelector(
(state) => state.customFilters.error,
(state) => state.tags.error,
(state) => state.settings.ui.error,
(state) => state.settings.languages.error,
(state) => state.settings.qualityProfiles.error,
(state) => state.settings.metadataProfiles.error,
(state) => state.settings.importLists.error,
@ -83,6 +87,7 @@ const selectErrors = createSelector(
customFiltersError,
tagsError,
uiSettingsError,
languagesError,
qualityProfilesError,
metadataProfilesError,
importListsError,
@ -92,6 +97,7 @@ const selectErrors = createSelector(
customFiltersError ||
tagsError ||
uiSettingsError ||
languagesError ||
qualityProfilesError ||
metadataProfilesError ||
importListsError ||
@ -103,6 +109,7 @@ const selectErrors = createSelector(
customFiltersError,
tagsError,
uiSettingsError,
languagesError,
qualityProfilesError,
metadataProfilesError,
importListsError,
@ -147,6 +154,9 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchTags() {
dispatch(fetchTags());
},
dispatchFetchLanguages() {
dispatch(fetchLanguages());
},
dispatchFetchQualityProfiles() {
dispatch(fetchQualityProfiles());
},
@ -189,6 +199,7 @@ class PageConnector extends Component {
this.props.dispatchFetchArtist();
this.props.dispatchFetchCustomFilters();
this.props.dispatchFetchTags();
this.props.dispatchFetchLanguages();
this.props.dispatchFetchQualityProfiles();
this.props.dispatchFetchMetadataProfiles();
this.props.dispatchFetchImportLists();
@ -213,6 +224,7 @@ class PageConnector extends Component {
hasError,
dispatchFetchArtist,
dispatchFetchTags,
dispatchFetchLanguages,
dispatchFetchQualityProfiles,
dispatchFetchMetadataProfiles,
dispatchFetchImportLists,
@ -252,6 +264,7 @@ PageConnector.propTypes = {
dispatchFetchArtist: PropTypes.func.isRequired,
dispatchFetchCustomFilters: PropTypes.func.isRequired,
dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchLanguages: PropTypes.func.isRequired,
dispatchFetchQualityProfiles: PropTypes.func.isRequired,
dispatchFetchMetadataProfiles: PropTypes.func.isRequired,
dispatchFetchImportLists: PropTypes.func.isRequired,

@ -10,6 +10,7 @@ import PageContent from 'Components/Page/PageContent';
import PageContentBody from 'Components/Page/PageContentBody';
import { inputTypes } from 'Helpers/Props';
import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector';
import translate from 'Utilities/String/translate';
import styles from './UISettings.css';
export const firstDayOfWeekOptions = [
@ -56,9 +57,12 @@ class UISettings extends Component {
hasSettings,
onInputChange,
onSavePress,
languages,
...otherProps
} = this.props;
const uiLanguages = languages.filter((item) => item.value !== 'Original');
return (
<PageContent title="UI Settings">
<SettingsToolbarConnector
@ -220,6 +224,20 @@ class UISettings extends Component {
</div>
</FormGroup>
</FieldSet>
<FieldSet legend={translate('Language')}>
<FormGroup>
<FormLabel>{translate('UILanguage')}</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="uiLanguage"
values={uiLanguages}
helpText={translate('UILanguageHelpText')}
helpTextWarning={translate('UILanguageHelpTextWarning')}
onChange={onInputChange}
{...settings.uiLanguage}
/>
</FormGroup>
</FieldSet>
</Form>
}
</PageContentBody>
@ -235,6 +253,7 @@ UISettings.propTypes = {
settings: PropTypes.object.isRequired,
hasSettings: PropTypes.bool.isRequired,
onSavePress: PropTypes.func.isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
onInputChange: PropTypes.func.isRequired
};

@ -9,13 +9,38 @@ import UISettings from './UISettings';
const SECTION = 'ui';
function createLanguagesSelector() {
return createSelector(
(state) => state.settings.languages,
(languages) => {
const items = languages.items;
const filterItems = ['Any', 'Unknown'];
if (!items) {
return [];
}
const newItems = items.filter((lang) => !filterItems.includes(lang.name)).map((item) => {
return {
key: item.id,
value: item.name
};
});
return newItems;
}
);
}
function createMapStateToProps() {
return createSelector(
(state) => state.settings.advancedSettings,
createSettingsSectionSelector(SECTION),
(advancedSettings, sectionSettings) => {
createLanguagesSelector(),
(advancedSettings, sectionSettings, languages) => {
return {
advancedSettings,
languages,
...sectionSettings
};
}

@ -0,0 +1,48 @@
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: {
}
};

@ -9,6 +9,7 @@ import importListExclusions from './Settings/importListExclusions';
import importLists from './Settings/importLists';
import indexerOptions from './Settings/indexerOptions';
import indexers from './Settings/indexers';
import languages from './Settings/languages';
import mediaManagement from './Settings/mediaManagement';
import metadata from './Settings/metadata';
import metadataProfiles from './Settings/metadataProfiles';
@ -31,6 +32,7 @@ export * from './Settings/importLists';
export * from './Settings/importListExclusions';
export * from './Settings/indexerOptions';
export * from './Settings/indexers';
export * from './Settings/languages';
export * from './Settings/metadataProfiles';
export * from './Settings/mediaManagement';
export * from './Settings/metadata';
@ -64,6 +66,7 @@ export const defaultState = {
indexers: indexers.defaultState,
importLists: importLists.defaultState,
importListExclusions: importListExclusions.defaultState,
languages: languages.defaultState,
metadataProfiles: metadataProfiles.defaultState,
mediaManagement: mediaManagement.defaultState,
metadata: metadata.defaultState,
@ -105,6 +108,7 @@ export const actionHandlers = handleThunks({
...indexers.actionHandlers,
...importLists.actionHandlers,
...importListExclusions.actionHandlers,
...languages.actionHandlers,
...metadataProfiles.actionHandlers,
...mediaManagement.actionHandlers,
...metadata.actionHandlers,
@ -137,6 +141,7 @@ export const reducers = createHandleActions({
...indexers.reducers,
...importLists.reducers,
...importListExclusions.reducers,
...languages.reducers,
...metadataProfiles.reducers,
...mediaManagement.reducers,
...metadata.reducers,

@ -0,0 +1,34 @@
import $ from 'jquery';
function getTranslations() {
let localization = null;
const ajaxOptions = {
async: false,
type: 'GET',
global: false,
dataType: 'json',
url: `${window.Lidarr.apiRoot}/localization`,
success: function(data) {
localization = data.Strings;
}
};
ajaxOptions.headers = ajaxOptions.headers || {};
ajaxOptions.headers['X-Api-Key'] = window.Lidarr.apiKey;
$.ajax(ajaxOptions);
return localization;
}
const translations = getTranslations();
export default function translate(key, args = '') {
if (args) {
const translatedKey = translate(key);
return translatedKey.replace(/\{(\d+)\}/g, (match, index) => {
return args[index];
});
}
return translations[key] || key;
}

@ -16,6 +16,7 @@ namespace Lidarr.Api.V1.Config
public bool ShowRelativeDates { get; set; }
public bool EnableColorImpairedMode { get; set; }
public int UILanguage { get; set; }
public bool ExpandAlbumByDefault { get; set; }
public bool ExpandSingleByDefault { get; set; }
@ -39,6 +40,7 @@ namespace Lidarr.Api.V1.Config
ShowRelativeDates = model.ShowRelativeDates,
EnableColorImpairedMode = model.EnableColorImpairedMode,
UILanguage = model.UILanguage,
ExpandAlbumByDefault = model.ExpandAlbumByDefault,
ExpandSingleByDefault = model.ExpandSingleByDefault,

@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Linq;
using Lidarr.Http;
using Lidarr.Http.REST;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.Languages;
namespace Lidarr.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]
public List<LanguageResource> GetAll()
{
return Language.All.Select(l => new LanguageResource
{
Id = (int)l,
Name = l.ToString()
})
.OrderBy(l => l.Name)
.ToList();
}
}
}

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
using Lidarr.Http.REST;
namespace Lidarr.Api.V1.Languages
{
public class LanguageResource : RestResource
{
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public new int Id { get; set; }
public string Name { get; set; }
public string NameLower => Name.ToLowerInvariant();
}
}

@ -0,0 +1,29 @@
using System.Text.Json;
using Lidarr.Http;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Localization;
namespace Lidarr.Api.V1.Localization
{
[V1ApiController]
public class LocalizationController : Controller
{
private readonly ILocalizationService _localizationService;
private readonly JsonSerializerOptions _serializerSettings;
public LocalizationController(ILocalizationService localizationService)
{
_localizationService = localizationService;
_serializerSettings = STJson.GetSerializerSettings();
_serializerSettings.DictionaryKeyPolicy = null;
_serializerSettings.PropertyNamingPolicy = null;
}
[HttpGet]
public string GetLocalizationDictionary()
{
return JsonSerializer.Serialize(_localizationService.GetLocalizationDictionary().ToResource(), _serializerSettings);
}
}
}

@ -0,0 +1,26 @@
using System.Collections.Generic;
using Lidarr.Http.REST;
namespace Lidarr.Api.V1.Localization
{
public class LocalizationResource : RestResource
{
public Dictionary<string, string> Strings { get; set; }
}
public static class LocalizationResourceMapper
{
public static LocalizationResource ToResource(this Dictionary<string, string> localization)
{
if (localization == null)
{
return null;
}
return new LocalizationResource
{
Strings = localization,
};
}
}
}

@ -6,6 +6,7 @@ using NLog;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Languages;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities;
@ -339,6 +340,13 @@ namespace NzbDrone.Core.Configuration
set { SetValue("EnableColorImpairedMode", value); }
}
public int UILanguage
{
get { return GetValueInt("UILanguage", (int)Language.English); }
set { SetValue("UILanguage", value); }
}
public bool ExpandAlbumByDefault
{
get { return GetValueBoolean("ExpandAlbumByDefault", false); }

@ -60,6 +60,7 @@ namespace NzbDrone.Core.Configuration
string TimeFormat { get; set; }
bool ShowRelativeDates { get; set; }
bool EnableColorImpairedMode { get; set; }
int UILanguage { get; set; }
bool ExpandAlbumByDefault { get; set; }
bool ExpandSingleByDefault { get; set; }

@ -0,0 +1,191 @@
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");
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 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,
PortugueseBR,
Arabic,
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;
}
}
}

@ -0,0 +1,13 @@
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()));
}
}
}

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

@ -0,0 +1,53 @@
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;
}
}
}

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

@ -28,6 +28,11 @@
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Common\Lidarr.Common.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Localization\Core\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\..\Logo\64.png">
<Link>Resources\Logo\64.png</Link>

@ -0,0 +1,4 @@
{
"Language": "Language",
"UILanguage": "UI Language"
}

@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Cache;
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;
namespace NzbDrone.Core.Localization
{
public interface ILocalizationService
{
Dictionary<string, string> GetLocalizationDictionary();
string GetLocalizedString(string phrase);
string GetLocalizedString(string phrase, string language);
}
public class LocalizationService : ILocalizationService, IHandleAsync<ConfigSavedEvent>
{
private const string DefaultCulture = "en";
private readonly ICached<Dictionary<string, string>> _cache;
private readonly IConfigService _configService;
private readonly IAppFolderInfo _appFolderInfo;
private readonly Logger _logger;
public LocalizationService(IConfigService configService,
IAppFolderInfo appFolderInfo,
ICacheManager cacheManager,
Logger logger)
{
_configService = configService;
_appFolderInfo = appFolderInfo;
_cache = cacheManager.GetCache<Dictionary<string, string>>(typeof(Dictionary<string, string>), "localization");
_logger = logger;
}
public Dictionary<string, string> GetLocalizationDictionary()
{
var language = GetSetLanguageFileName();
return GetLocalizationDictionary(language);
}
public string GetLocalizedString(string phrase)
{
var language = GetSetLanguageFileName();
return GetLocalizedString(phrase, language);
}
public string GetLocalizedString(string phrase, string language)
{
if (string.IsNullOrEmpty(phrase))
{
throw new ArgumentNullException(nameof(phrase));
}
if (language.IsNullOrWhiteSpace())
{
language = GetSetLanguageFileName();
}
if (language == null)
{
language = DefaultCulture;
}
var dictionary = GetLocalizationDictionary(language);
if (dictionary.TryGetValue(phrase, out var value))
{
return value;
}
return phrase;
}
private string GetSetLanguageFileName()
{
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;
}
private Dictionary<string, string> GetLocalizationDictionary(string language)
{
if (string.IsNullOrEmpty(language))
{
throw new ArgumentNullException(nameof(language));
}
var startupFolder = _appFolderInfo.StartUpFolder;
var prefix = Path.Combine(startupFolder, "Localization", "Core");
var key = prefix + language;
return _cache.Get("localization", () => GetDictionary(prefix, language, DefaultCulture + ".json").GetAwaiter().GetResult());
}
private async Task<Dictionary<string, string>> GetDictionary(string prefix, string culture, string baseFilename)
{
if (string.IsNullOrEmpty(culture))
{
throw new ArgumentNullException(nameof(culture));
}
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var baseFilenamePath = Path.Combine(prefix, baseFilename);
var alternativeFilenamePath = Path.Combine(prefix, GetResourceFilename(culture));
await CopyInto(dictionary, baseFilenamePath).ConfigureAwait(false);
if (culture.Contains("_"))
{
var languageBaseFilenamePath = Path.Combine(prefix, GetResourceFilename(culture.Split('_')[0]));
await CopyInto(dictionary, languageBaseFilenamePath).ConfigureAwait(false);
}
await CopyInto(dictionary, alternativeFilenamePath).ConfigureAwait(false);
return dictionary;
}
private async Task CopyInto(IDictionary<string, string> dictionary, string resourcePath)
{
if (!File.Exists(resourcePath))
{
_logger.Error("Missing translation/culture resource: {0}", resourcePath);
return;
}
using (var fs = File.OpenRead(resourcePath))
{
if (fs != null)
{
var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(fs);
foreach (var key in dict.Keys)
{
dictionary[key] = dict[key];
}
}
else
{
_logger.Error("Missing translation/culture resource: {0}", resourcePath);
}
}
}
private static string GetResourceFilename(string culture)
{
var parts = culture.Split('_');
if (parts.Length == 2)
{
culture = parts[0].ToLowerInvariant() + "_" + parts[1].ToUpperInvariant();
}
else
{
culture = culture.ToLowerInvariant();
}
return culture + ".json";
}
public void HandleAsync(ConfigSavedEvent message)
{
_cache.Clear();
}
}
}

@ -1,14 +1,22 @@
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 threeLetterCode)
public IsoLanguage(string twoLetterCode, string countryCode, string threeLetterCode, string englishName, Language language)
{
TwoLetterCode = twoLetterCode;
ThreeLetterCode = threeLetterCode;
CountryCode = countryCode;
EnglishName = englishName;
Language = language;
}
}
}

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.Parser
{
@ -7,47 +8,74 @@ namespace NzbDrone.Core.Parser
{
private static readonly HashSet<IsoLanguage> All = new HashSet<IsoLanguage>
{
new IsoLanguage("en", "eng"),
new IsoLanguage("fr", "fra"),
new IsoLanguage("es", "spa"),
new IsoLanguage("de", "deu"),
new IsoLanguage("it", "ita"),
new IsoLanguage("da", "dan"),
new IsoLanguage("nl", "nld"),
new IsoLanguage("ja", "jpn"),
new IsoLanguage("is", "isl"),
new IsoLanguage("zh", "zho"),
new IsoLanguage("ru", "rus"),
new IsoLanguage("pl", "pol"),
new IsoLanguage("vi", "vie"),
new IsoLanguage("sv", "swe"),
new IsoLanguage("no", "nor"),
new IsoLanguage("nb", "nob"), // Norwegian Bokmål
new IsoLanguage("fi", "fin"),
new IsoLanguage("tr", "tur"),
new IsoLanguage("pt", "por"),
new IsoLanguage("el", "ell"),
new IsoLanguage("ko", "kor"),
new IsoLanguage("hu", "hun"),
new IsoLanguage("he", "heb"),
new IsoLanguage("lt", "lit"),
new IsoLanguage("cs", "ces")
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", "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", Language.Chinese),
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)
};
public static IsoLanguage Find(string isoCode)
{
if (isoCode.Length == 2)
var isoArray = isoCode.Split('-');
var langCode = isoArray[0].ToLower();
if (langCode.Length == 2)
{
//Lookup ISO639-1 code
return All.SingleOrDefault(l => l.TwoLetterCode == isoCode);
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 (isoCode.Length == 3)
else if (langCode.Length == 3)
{
//Lookup ISO639-2T code
return All.SingleOrDefault(l => l.ThreeLetterCode == isoCode);
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);
}
}
}

Loading…
Cancel
Save