Browse Source

add ui localization framework (#2809)

* move dark/light mode toggle to ui settings page

* separate current culture (formatting) and ui culture (language)

* add some more sample translations

* update changelog

* fix cancellation token
pull/2810/head
Jason Dove 6 months ago committed by GitHub
parent
commit
607d9b0662
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      CHANGELOG.md
  2. 5
      ErsatzTV.Application/Configuration/Commands/UpdateUiSettings.cs
  3. 28
      ErsatzTV.Application/Configuration/Commands/UpdateUiSettingsHandler.cs
  4. 3
      ErsatzTV.Application/Configuration/Queries/GetUiSettings.cs
  5. 25
      ErsatzTV.Application/Configuration/Queries/GetUiSettingsHandler.cs
  6. 8
      ErsatzTV.Application/Configuration/UiSettingsViewModel.cs
  7. 2
      ErsatzTV.Application/Playouts/Queries/GetFuturePlayoutItemsByIdHandler.cs
  8. 1
      ErsatzTV.Core/Domain/ConfigElementKey.cs
  9. 27
      ErsatzTV/DatabaseRequestCultureProvider.cs
  10. 5
      ErsatzTV/Locals/Resources.cs
  11. 35
      ErsatzTV/Locals/Resources.pt-br.resx
  12. 24
      ErsatzTV/Locals/Resources.resx
  13. 4
      ErsatzTV/Pages/DecoTemplateEditor.razor
  14. 2
      ErsatzTV/Pages/Logs.razor
  15. 2
      ErsatzTV/Pages/PlayoutAlternateSchedulesEditor.razor
  16. 2
      ErsatzTV/Pages/PlayoutTemplatesEditor.razor
  17. 2
      ErsatzTV/Pages/Playouts.razor
  18. 84
      ErsatzTV/Pages/Settings/UISettings.razor
  19. 4
      ErsatzTV/Pages/TemplateEditor.razor
  20. 2
      ErsatzTV/Pages/Troubleshooting/BlockPlayoutHistory.razor
  21. 2
      ErsatzTV/Pages/Troubleshooting/PlaybackTroubleshooting.razor
  22. 31
      ErsatzTV/Shared/MainLayout.razor
  23. 9
      ErsatzTV/Startup.cs

6
CHANGELOG.md

@ -7,6 +7,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -7,6 +7,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Add log warnings when actual transcoding speed is potentially insufficient to support smooth playback
- Log messages will include media item id, channel number and transcoding speed
- Add UI language setting to **Settings** > **UI**
- A small number of menu translations have been added for `Português (Brasil)`
- Translation contributions are always welcome!
### Changed
- Move dark/light mode toggle to **Settings** > **UI**
## [26.2.0] - 2026-02-02
### Added

5
ErsatzTV.Application/Configuration/Commands/UpdateUiSettings.cs

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
using ErsatzTV.Core;
namespace ErsatzTV.Application.Configuration;
public record UpdateUiSettings(UiSettingsViewModel UiSettings) : IRequest<Either<BaseError, Unit>>;

28
ErsatzTV.Application/Configuration/Commands/UpdateUiSettingsHandler.cs

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
namespace ErsatzTV.Application.Configuration;
public class UpdateUiSettingsHandler(IConfigElementRepository configElementRepository)
: IRequestHandler<UpdateUiSettings, Either<BaseError, Unit>>
{
public async Task<Either<BaseError, Unit>> Handle(
UpdateUiSettings request,
CancellationToken cancellationToken)
{
return await ApplyUpdate(request.UiSettings, cancellationToken);
}
private async Task<Unit> ApplyUpdate(UiSettingsViewModel uiSettings, CancellationToken cancellationToken)
{
await configElementRepository.Upsert(
ConfigElementKey.PagesIsDarkMode,
uiSettings.IsDarkMode,
cancellationToken);
await configElementRepository.Upsert(ConfigElementKey.PagesLanguage, uiSettings.Language, cancellationToken);
return Unit.Default;
}
}

3
ErsatzTV.Application/Configuration/Queries/GetUiSettings.cs

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
namespace ErsatzTV.Application.Configuration;
public record GetUiSettings : IRequest<UiSettingsViewModel>;

25
ErsatzTV.Application/Configuration/Queries/GetUiSettingsHandler.cs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
namespace ErsatzTV.Application.Configuration;
public class GetUiSettingsHandler(IConfigElementRepository configElementRepository)
: IRequestHandler<GetUiSettings, UiSettingsViewModel>
{
public async Task<UiSettingsViewModel> Handle(GetUiSettings request, CancellationToken cancellationToken)
{
Option<bool> pagesIsDarkMode = await configElementRepository.GetValue<bool>(
ConfigElementKey.PagesIsDarkMode,
cancellationToken);
Option<string> pagesLanguage = await configElementRepository.GetValue<string>(
ConfigElementKey.PagesLanguage,
cancellationToken);
return new UiSettingsViewModel
{
IsDarkMode = await pagesIsDarkMode.IfNoneAsync(true),
Language = await pagesLanguage.IfNoneAsync("en")
};
}
}

8
ErsatzTV.Application/Configuration/UiSettingsViewModel.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.Application.Configuration;
public class UiSettingsViewModel
{
public bool IsDarkMode { get; set; }
public string Language { get; set; }
}

2
ErsatzTV.Application/Playouts/Queries/GetFuturePlayoutItemsByIdHandler.cs

@ -112,7 +112,7 @@ public class GetFuturePlayoutItemsByIdHandler(IDbContextFactory<TvContext> dbCon @@ -112,7 +112,7 @@ public class GetFuturePlayoutItemsByIdHandler(IDbContextFactory<TvContext> dbCon
gap.FinishOffset,
TimeSpan.FromSeconds(Math.Round(gapDuration.TotalSeconds)).ToString(
gapDuration.TotalHours >= 1 ? @"h\:mm\:ss" : @"mm\:ss",
CultureInfo.CurrentUICulture.DateTimeFormat),
CultureInfo.CurrentCulture.DateTimeFormat),
None
);
}).ToList();

1
ErsatzTV.Core/Domain/ConfigElementKey.cs

@ -32,6 +32,7 @@ public class ConfigElementKey @@ -32,6 +32,7 @@ public class ConfigElementKey
public static ConfigElementKey HDHRTunerCount => new("hdhr.tuner_count");
public static ConfigElementKey HDHRUUID => new("hdhr.uuid");
public static ConfigElementKey PagesIsDarkMode => new("pages.is_dark_mode");
public static ConfigElementKey PagesLanguage => new("pages.language");
public static ConfigElementKey ChannelsPageSize => new("pages.channels.page_size");
public static ConfigElementKey ChannelsShowDisabled => new("pages.channels.show_disabled");
public static ConfigElementKey CollectionsPageSize => new("pages.collections.page_size");

27
ErsatzTV/DatabaseRequestCultureProvider.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using Microsoft.AspNetCore.Localization;
namespace ErsatzTV;
public class DatabaseRequestCultureProvider(AcceptLanguageHeaderRequestCultureProvider defaultProvider)
: RequestCultureProvider
{
public override async Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
{
var configElementRepository = httpContext.RequestServices.GetService<IConfigElementRepository>();
var defaultResult = await defaultProvider.DetermineProviderCultureResult(httpContext);
string configuredLanguage = await configElementRepository.GetValue<string>(
ConfigElementKey.PagesLanguage,
httpContext.RequestAborted)
.IfNoneAsync("en");
if (defaultResult != null)
{
return new ProviderCultureResult(defaultResult.Cultures, [configuredLanguage]);
}
return new ProviderCultureResult(configuredLanguage);
}
}

5
ErsatzTV/Locals/Resources.cs

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
namespace ErsatzTV;
public class Resources
{
}

35
ErsatzTV/Locals/Resources.pt-br.resx

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
<root>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ButtonMainMenuChannels" xml:space="preserve">
<value>Canais</value>
</data>
<data name="ButtonMainMenuFFmpegProfiles" xml:space="preserve">
<value>Perfis FFmpeg</value>
</data>
<data name="ButtonMainMenuWatermarks" xml:space="preserve">
<value>Marcas d'água</value>
</data>
<data name="ButtonMainMenuMediaSources" xml:space="preserve">
<value>Fontes de mídia</value>
</data>
<data name="ButtonMainMenuMedia" xml:space="preserve">
<value>Mídias</value>
</data>
<data name="ButtonMainMenuLists" xml:space="preserve">
<value>Listas</value>
</data>
<data name="ButtonMainMenuSettings" xml:space="preserve">
<value>Definições</value>
</data>
</root>

24
ErsatzTV/Locals/Resources.resx

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="ButtonMainMenuChannels" xml:space="preserve">
<value>Channels</value>
</data>
<data name="ButtonMainMenuFFmpegProfiles" xml:space="preserve">
<value>FFmpeg Profiles</value>
</data>
<data name="ButtonMainMenuWatermarks" xml:space="preserve">
<value>Watermarks</value>
</data>
<data name="ButtonMainMenuMediaSources" xml:space="preserve">
<value>Media Sources</value>
</data>
<data name="ButtonMainMenuMedia" xml:space="preserve">
<value>Media</value>
</data>
<data name="ButtonMainMenuLists" xml:space="preserve">
<value>Lists</value>
</data>
<data name="ButtonMainMenuSettings" xml:space="preserve">
<value>Settings</value>
</data>
</root>

4
ErsatzTV/Pages/DecoTemplateEditor.razor

@ -61,7 +61,7 @@ @@ -61,7 +61,7 @@
@foreach (DateTime startTime in _startTimes)
{
<MudSelectItem Value="@startTime">
@startTime.ToString(CultureInfo.CurrentUICulture.DateTimeFormat.ShortTimePattern)
@startTime.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern)
</MudSelectItem>
}
</MudSelect>
@ -129,7 +129,7 @@ @@ -129,7 +129,7 @@
ShowDatePicker="false"
ShowTodayButton="false"
DayTimeInterval="CalendarTimeInterval.Minutes10"
Use24HourClock="@(CultureInfo.CurrentUICulture.DateTimeFormat.ShortTimePattern.Contains("H"))"
Use24HourClock="@(CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains("H"))"
EnableDragItems="true"
EnableResizeItems="false"
ItemChanged="@(ci => CalendarItemChanged(ci))"/>

2
ErsatzTV/Pages/Logs.razor

@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
@code {
private CancellationTokenSource _cts;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat;
private MudTable<LogEntryViewModel> _table;
private int _rowsPerPage = 10;

2
ErsatzTV/Pages/PlayoutAlternateSchedulesEditor.razor

@ -224,7 +224,7 @@ @@ -224,7 +224,7 @@
@code {
private CancellationTokenSource _cts;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat;
[Parameter]
public int Id { get; set; }

2
ErsatzTV/Pages/PlayoutTemplatesEditor.razor

@ -273,7 +273,7 @@ @@ -273,7 +273,7 @@
@code {
private CancellationTokenSource _cts;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat;
[Parameter]
public int Id { get; set; }

2
ErsatzTV/Pages/Playouts.razor

@ -263,7 +263,7 @@ @@ -263,7 +263,7 @@
@code {
private CancellationTokenSource _cts;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat;
private readonly List<PlayoutNameViewModel> _currentPage = [];
private MudTable<PlayoutNameViewModel> _table;

84
ErsatzTV/Pages/Settings/UISettings.razor

@ -0,0 +1,84 @@ @@ -0,0 +1,84 @@
@page "/settings/ui"
@using ErsatzTV.Application.Configuration
@using ErsatzTV.Core.Notifications
@implements IDisposable
@inject IMediator Mediator
@inject ISnackbar Snackbar
@inject NavigationManager NavigationManager
@inject ILogger<UISettings> Logger
<MudForm Style="max-height: 100%">
<MudPaper Square="true" Style="display: flex; height: 64px; min-height: 64px; width: 100%; z-index: 100; align-items: center">
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => SaveUiSettings())" Class="ml-6" StartIcon="@Icons.Material.Filled.Save">Save Settings</MudButton>
</MudPaper>
<div class="d-flex flex-column" style="height: 100vh; overflow-x: auto">
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
<MudText Typo="Typo.h5" Class="mb-2">UI</MudText>
<MudDivider Class="mb-6"/>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Theme</MudText>
</div>
<MudSelect @bind-Value="_uiSettings.IsDarkMode">
<MudSelectItem Value="@true">Dark</MudSelectItem>
<MudSelectItem Value="@false">Light</MudSelectItem>
</MudSelect>
</MudStack>
<MudStack Row="true" Breakpoint="Breakpoint.SmAndDown" Class="form-field-stack gap-md-8 mb-5">
<div class="d-flex">
<MudText>Language</MudText>
</div>
<MudSelect @bind-Value="_uiSettings.Language">
<MudSelectItem Value="@("en-us")">English</MudSelectItem>
<MudSelectItem Value="@("pt-br")">Português (Brasil)</MudSelectItem>
</MudSelect>
</MudStack>
</MudContainer>
</div>
</MudForm>
@code {
private CancellationTokenSource _cts;
private UiSettingsViewModel _uiSettings = new();
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_cts?.Cancel();
_cts?.Dispose();
_cts = new CancellationTokenSource();
var token = _cts.Token;
try
{
_uiSettings = await Mediator.Send(new GetUiSettings(), token);
}
catch (OperationCanceledException)
{
// do nothing
}
}
private async Task SaveUiSettings()
{
Either<BaseError, Unit> result = await Mediator.Send(new UpdateUiSettings(_uiSettings), _cts.Token);
foreach (var error in result.LeftToSeq())
{
Snackbar.Add(error.Value, Severity.Error);
Logger.LogError("Unexpected error saving ui settings: {Error}", error.Value);
}
if (result.IsRight)
{
NavigationManager.Refresh(true);
}
}
}

4
ErsatzTV/Pages/TemplateEditor.razor

@ -61,7 +61,7 @@ @@ -61,7 +61,7 @@
@foreach (DateTime startTime in _startTimes)
{
<MudSelectItem Value="@startTime">
@startTime.ToString(CultureInfo.CurrentUICulture.DateTimeFormat.ShortTimePattern)
@startTime.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern)
</MudSelectItem>
}
</MudSelect>
@ -104,7 +104,7 @@ @@ -104,7 +104,7 @@
ShowDatePicker="false"
ShowTodayButton="false"
DayTimeInterval="CalendarTimeInterval.Minutes10"
Use24HourClock="@(CultureInfo.CurrentUICulture.DateTimeFormat.ShortTimePattern.Contains("H"))"
Use24HourClock="@(CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern.Contains("H"))"
EnableDragItems="true"
EnableResizeItems="false"
ItemChanged="@(ci => CalendarItemChanged(ci))"/>

2
ErsatzTV/Pages/Troubleshooting/BlockPlayoutHistory.razor

@ -91,7 +91,7 @@ @@ -91,7 +91,7 @@
public int? BlockId { get; set; }
private CancellationTokenSource _cts;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat;
private int _rowsPerPage = 10;
private int? _selectedPlayoutHistoryId;
private PlayoutHistoryDetailsViewModel _decodedHistory;

2
ErsatzTV/Pages/Troubleshooting/PlaybackTroubleshooting.razor

@ -193,7 +193,7 @@ @@ -193,7 +193,7 @@
@code {
private CancellationTokenSource _cts;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentUICulture.DateTimeFormat;
private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat;
private readonly List<FFmpegProfileViewModel> _ffmpegProfiles = [];
private readonly List<string> _streamSelectors = [];
private readonly List<WatermarkViewModel> _watermarks = [];

31
ErsatzTV/Shared/MainLayout.razor

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
@using ErsatzTV.Core.Notifications
@using ErsatzTV.Extensions
@using MediatR.Courier
@using Microsoft.Extensions.Localization
@implements IDisposable
@inject NavigationManager NavigationManager
@inject IMediator Mediator
@ -15,6 +16,7 @@ @@ -15,6 +16,7 @@
@inject ISearchTargets SearchTargets;
@inject ICourier Courier
@inject IHealthCheckService HealthCheckService;
@inject IStringLocalizer<Resources> StringLocalizer;
<MudThemeProvider Theme="ErsatzTvTheme" IsDarkMode="_isDarkMode"/>
<MudDialogProvider BackdropClick="false"/>
@ -103,7 +105,6 @@ @@ -103,7 +105,6 @@
<MudTooltip Text="GitHub">
<MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Primary" Href="https://github.com/ErsatzTV/ErsatzTV" Target="_blank"/>
</MudTooltip>
<MudIconButton Icon="@(DarkLightModeButtonIcon)" Color="Color.Primary" OnClick="@DarkModeToggle"/>
<AuthorizeView>
<form action="/account/logout" method="post">
<MudTooltip Text="Logout">
@ -117,16 +118,16 @@ @@ -117,16 +118,16 @@
{
<MudDrawer @bind-Open="@_drawerIsOpen" Elevation="2" ClipMode="DrawerClipMode.Always">
<MudNavMenu>
<MudNavLink Href="channels">Channels</MudNavLink>
<MudNavLink Href="ffmpeg">FFmpeg Profiles</MudNavLink>
<MudNavLink Href="watermarks">Watermarks</MudNavLink>
<MudNavGroup Title="Media Sources">
<MudNavLink Href="channels">@StringLocalizer["ButtonMainMenuChannels"]</MudNavLink>
<MudNavLink Href="ffmpeg">@StringLocalizer["ButtonMainMenuFFmpegProfiles"]</MudNavLink>
<MudNavLink Href="watermarks">@StringLocalizer["ButtonMainMenuWatermarks"]</MudNavLink>
<MudNavGroup Title="@StringLocalizer["ButtonMainMenuMediaSources"]">
<MudNavLink Href="media/sources/local">Local</MudNavLink>
<MudNavLink Href="media/sources/emby">Emby</MudNavLink>
<MudNavLink Href="media/sources/jellyfin">Jellyfin</MudNavLink>
<MudNavLink Href="media/sources/plex">Plex</MudNavLink>
</MudNavGroup>
<MudNavGroup Title="Media">
<MudNavGroup Title="@StringLocalizer["ButtonMainMenuMedia"]">
<MudNavLink Href="media/libraries">Libraries</MudNavLink>
<MudNavLink Href="media/trash">Trash</MudNavLink>
<MudNavLink Href="media/tv/shows">TV Shows</MudNavLink>
@ -137,7 +138,7 @@ @@ -137,7 +138,7 @@
<MudNavLink Href="media/browser/images">Images</MudNavLink>
<MudNavLink Href="media/remote/streams">Remote Streams</MudNavLink>
</MudNavGroup>
<MudNavGroup Title="Lists">
<MudNavGroup Title="@StringLocalizer["ButtonMainMenuLists"]">
<MudNavLink Href="media/collections">Manual Collections</MudNavLink>
<MudNavLink Href="media/smart-collections">Smart Collections</MudNavLink>
<MudNavLink Href="media/multi-collections">Multi Collections</MudNavLink>
@ -183,12 +184,13 @@ @@ -183,12 +184,13 @@
</MudNavLink>
</ChildContent>
</MudNavGroup>
<MudNavGroup Title="Settings">
<MudNavGroup Title="@StringLocalizer["ButtonMainMenuSettings"]">
<MudNavLink Href="settings/ffmpeg">FFmpeg</MudNavLink>
<MudNavLink Href="settings/logging">Logging</MudNavLink>
<MudNavLink Href="settings/hdhr">HDHomeRun</MudNavLink>
<MudNavLink Href="settings/scanner">Scanner</MudNavLink>
<MudNavLink Href="settings/playout">Playout</MudNavLink>
<MudNavLink Href="settings/ui">UI</MudNavLink>
<MudNavLink Href="settings/xmltv">XMLTV</MudNavLink>
</MudNavGroup>
<MudNavGroup Expanded="true">
@ -294,18 +296,6 @@ @@ -294,18 +296,6 @@
await HandleHealthCheckSummary(HealthCheckService.GetHealthCheckSummary(), CancellationToken.None);
}
private async Task DarkModeToggle()
{
_isDarkMode = !_isDarkMode;
await Mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PagesIsDarkMode, _isDarkMode.ToString()));
}
public string DarkLightModeButtonIcon => _isDarkMode switch
{
true => Icons.Material.Rounded.DarkMode,
false => Icons.Material.Outlined.LightMode
};
public void Dispose()
{
SystemStartup.OnDatabaseReady -= OnStartupProgress;
@ -314,6 +304,7 @@ @@ -314,6 +304,7 @@
SearchTargets.OnSearchTargetsChanged -= OnSearchTargetsChanged;
Courier.UnSubscribe<HealthCheckSummary>(HandleHealthCheckSummary);
Courier.UnSubscribe<PlayoutUpdatedNotification>(HandlePlayoutUpdated);
_cts?.Cancel();
_cts?.Dispose();

9
ErsatzTV/Startup.cs

@ -87,6 +87,7 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect; @@ -87,6 +87,7 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
@ -604,9 +605,13 @@ public class Startup @@ -604,9 +605,13 @@ public class Startup
{
CultureInfo[] cinfo = CultureInfo.GetCultures(CultureTypes.AllCultures & ~CultureTypes.NeutralCultures);
string[] supportedCultures = cinfo.Select(t => t.Name).Distinct().ToArray();
string[] supportedUiCultures = ["en-us", "pt-br"];
options.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures)
.SetDefaultCulture("en-US");
.AddSupportedUICultures(supportedUiCultures)
.SetDefaultCulture("en-us");
options.AddInitialRequestCultureProvider(
new DatabaseRequestCultureProvider(
new AcceptLanguageHeaderRequestCultureProvider()));
});
app.UseStaticFiles();

Loading…
Cancel
Save