From a77a2d56ae2f83328d27070533dadc332888c78a Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:20:43 -0600 Subject: [PATCH] prepare channels list for localization (#2814) * prepare channels list for localization * define supported ui cultures/languages in a single location * fixes --- CHANGELOG.md | 2 +- ErsatzTV/ErsatzTV.csproj | 18 +++ ErsatzTV/Localization.cs | 17 ++ ErsatzTV/Locals/Pages/Channels.Designer.cs | 180 +++++++++++++++++++++ ErsatzTV/Locals/Pages/Channels.pl.resx | 14 ++ ErsatzTV/Locals/Pages/Channels.pt-br.resx | 14 ++ ErsatzTV/Locals/Pages/Channels.resx | 87 ++++++++++ ErsatzTV/Locals/Shared/Common.Designer.cs | 54 +++++++ ErsatzTV/Locals/Shared/Common.pl.resx | 14 ++ ErsatzTV/Locals/Shared/Common.pt-br.resx | 14 ++ ErsatzTV/Locals/Shared/Common.resx | 24 +++ ErsatzTV/Pages/Channels.razor | 61 +++---- ErsatzTV/Pages/Settings/UISettings.razor | 7 +- ErsatzTV/Shared/MainLayout.razor | 1 - ErsatzTV/Startup.cs | 5 +- ErsatzTV/_Imports.razor | 1 + 16 files changed, 476 insertions(+), 37 deletions(-) create mode 100644 ErsatzTV/Localization.cs create mode 100644 ErsatzTV/Locals/Pages/Channels.Designer.cs create mode 100644 ErsatzTV/Locals/Pages/Channels.pl.resx create mode 100644 ErsatzTV/Locals/Pages/Channels.pt-br.resx create mode 100644 ErsatzTV/Locals/Pages/Channels.resx create mode 100644 ErsatzTV/Locals/Shared/Common.Designer.cs create mode 100644 ErsatzTV/Locals/Shared/Common.pl.resx create mode 100644 ErsatzTV/Locals/Shared/Common.pt-br.resx create mode 100644 ErsatzTV/Locals/Shared/Common.resx diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ae427e70..b3fddbc80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - 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)` + - A small number of translations have been added for `Português (Brasil)` and `Polski` - Translation contributions are always welcome! ### Changed diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index ff2f914b4..d13c75219 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -111,6 +111,14 @@ ResXFileCodeGenerator MainLayout.Designer.cs + + ResXFileCodeGenerator + Channels.Designer.cs + + + ResXFileCodeGenerator + Common.Designer.cs + @@ -138,6 +146,16 @@ True MainLayout.resx + + True + True + Channels.resx + + + True + True + Common.resx + diff --git a/ErsatzTV/Localization.cs b/ErsatzTV/Localization.cs new file mode 100644 index 000000000..dad7e4ab9 --- /dev/null +++ b/ErsatzTV/Localization.cs @@ -0,0 +1,17 @@ +namespace ErsatzTV; + +public static class Localization +{ + public static readonly List SupportedLanguages = + [ + new("en-us", "English"), + new("pl", "Polski"), + new("pt-br", "Português (Brasil)") + ]; + + public static string DefaultCulture => "en-us"; + + public static string[] UiCultures => SupportedLanguages.Map(cl => cl.Culture).ToArray(); + + public sealed record CultureLanguage(string Culture, string Language); +} diff --git a/ErsatzTV/Locals/Pages/Channels.Designer.cs b/ErsatzTV/Locals/Pages/Channels.Designer.cs new file mode 100644 index 000000000..59ef6f872 --- /dev/null +++ b/ErsatzTV/Locals/Pages/Channels.Designer.cs @@ -0,0 +1,180 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ErsatzTV.Locals.Pages { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Channels { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Channels() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ErsatzTV.Locals.Pages.Channels", typeof(Channels).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string ButtonAddChannel { + get { + return ResourceManager.GetString("ButtonAddChannel", resourceCulture); + } + } + + internal static string ButtonEditChannelNumbers { + get { + return ResourceManager.GetString("ButtonEditChannelNumbers", resourceCulture); + } + } + + internal static string LabelChannels { + get { + return ResourceManager.GetString("LabelChannels", resourceCulture); + } + } + + internal static string ButtonShowDisabled { + get { + return ResourceManager.GetString("ButtonShowDisabled", resourceCulture); + } + } + + internal static string LabelNumber { + get { + return ResourceManager.GetString("LabelNumber", resourceCulture); + } + } + + internal static string LabelLogo { + get { + return ResourceManager.GetString("LabelLogo", resourceCulture); + } + } + + internal static string LabelName { + get { + return ResourceManager.GetString("LabelName", resourceCulture); + } + } + + internal static string LabelLanguage { + get { + return ResourceManager.GetString("LabelLanguage", resourceCulture); + } + } + + internal static string LabelStreamingMode { + get { + return ResourceManager.GetString("LabelStreamingMode", resourceCulture); + } + } + + internal static string LabelFFmpegProfile { + get { + return ResourceManager.GetString("LabelFFmpegProfile", resourceCulture); + } + } + + internal static string LabelChannelDisabledHelp { + get { + return ResourceManager.GetString("LabelChannelDisabledHelp", resourceCulture); + } + } + + internal static string LabelChannelHiddenHelp { + get { + return ResourceManager.GetString("LabelChannelHiddenHelp", resourceCulture); + } + } + + internal static string LabelChannelHiddenAbbreviation { + get { + return ResourceManager.GetString("LabelChannelHiddenAbbreviation", resourceCulture); + } + } + + internal static string LabelChannelDisabledAbbreviation { + get { + return ResourceManager.GetString("LabelChannelDisabledAbbreviation", resourceCulture); + } + } + + internal static string ButtonPreviewChannelHelp { + get { + return ResourceManager.GetString("ButtonPreviewChannelHelp", resourceCulture); + } + } + + internal static string ButtonPreviewChannelHelpNormalize { + get { + return ResourceManager.GetString("ButtonPreviewChannelHelpNormalize", resourceCulture); + } + } + + internal static string ButtonPreviewChannelHelpBrowser { + get { + return ResourceManager.GetString("ButtonPreviewChannelHelpBrowser", resourceCulture); + } + } + + internal static string ButtonPreviewChannelHelpSettings { + get { + return ResourceManager.GetString("ButtonPreviewChannelHelpSettings", resourceCulture); + } + } + + internal static string ButtonStopTranscodeHelp { + get { + return ResourceManager.GetString("ButtonStopTranscodeHelp", resourceCulture); + } + } + + internal static string ButtonEditChannelHelp { + get { + return ResourceManager.GetString("ButtonEditChannelHelp", resourceCulture); + } + } + + internal static string ButtonTroubleshootChannelHelp { + get { + return ResourceManager.GetString("ButtonTroubleshootChannelHelp", resourceCulture); + } + } + + internal static string ButtonDeleteChannelHelp { + get { + return ResourceManager.GetString("ButtonDeleteChannelHelp", resourceCulture); + } + } + } +} diff --git a/ErsatzTV/Locals/Pages/Channels.pl.resx b/ErsatzTV/Locals/Pages/Channels.pl.resx new file mode 100644 index 000000000..0db197394 --- /dev/null +++ b/ErsatzTV/Locals/Pages/Channels.pl.resx @@ -0,0 +1,14 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ErsatzTV/Locals/Pages/Channels.pt-br.resx b/ErsatzTV/Locals/Pages/Channels.pt-br.resx new file mode 100644 index 000000000..0db197394 --- /dev/null +++ b/ErsatzTV/Locals/Pages/Channels.pt-br.resx @@ -0,0 +1,14 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ErsatzTV/Locals/Pages/Channels.resx b/ErsatzTV/Locals/Pages/Channels.resx new file mode 100644 index 000000000..29ec25c6b --- /dev/null +++ b/ErsatzTV/Locals/Pages/Channels.resx @@ -0,0 +1,87 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Add Channel + + + Edit Channel Numbers + + + Channels + + + Show Disabled + + + Number + + + Logo + + + Name + + + Language + + + Mode + + + FFmpeg Profile + + + Channel is disabled + + + Channel is hidden from EPG + + + H + + + D + + + Preview Channel + + + Preview Channel; audio and/or video may not work without normalization + + + Channel preview requires FFmpeg Profile compatible with this browser + + + Channel preview requires playout, MPEG-TS/HLS Segmenter, and compatible FFmpeg Profile + + + Stop Transcode Session + + + Edit Channel + + + Troubleshoot Channel + + + Delete Channel + + diff --git a/ErsatzTV/Locals/Shared/Common.Designer.cs b/ErsatzTV/Locals/Shared/Common.Designer.cs new file mode 100644 index 000000000..56b96c658 --- /dev/null +++ b/ErsatzTV/Locals/Shared/Common.Designer.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ErsatzTV.Locals.Shared { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Common { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Common() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("ErsatzTV.Locals.Shared.Common", typeof(Common).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string LabelRowsPerPage { + get { + return ResourceManager.GetString("LabelRowsPerPage", resourceCulture); + } + } + } +} diff --git a/ErsatzTV/Locals/Shared/Common.pl.resx b/ErsatzTV/Locals/Shared/Common.pl.resx new file mode 100644 index 000000000..0db197394 --- /dev/null +++ b/ErsatzTV/Locals/Shared/Common.pl.resx @@ -0,0 +1,14 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ErsatzTV/Locals/Shared/Common.pt-br.resx b/ErsatzTV/Locals/Shared/Common.pt-br.resx new file mode 100644 index 000000000..0db197394 --- /dev/null +++ b/ErsatzTV/Locals/Shared/Common.pt-br.resx @@ -0,0 +1,14 @@ + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ErsatzTV/Locals/Shared/Common.resx b/ErsatzTV/Locals/Shared/Common.resx new file mode 100644 index 000000000..26b3d4ad5 --- /dev/null +++ b/ErsatzTV/Locals/Shared/Common.resx @@ -0,0 +1,24 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Rows per page: + + diff --git a/ErsatzTV/Pages/Channels.razor b/ErsatzTV/Pages/Channels.razor index c19956229..562a3a7df 100644 --- a/ErsatzTV/Pages/Channels.razor +++ b/ErsatzTV/Pages/Channels.razor @@ -10,19 +10,22 @@ @inject IMediator Mediator @inject NavigationManager NavigationManager @inject IFFmpegSegmenterService SegmenterService +@inject IStringLocalizer Loc; +@inject IStringLocalizer CommonLoc; +@inject IStringLocalizer MainLayoutLoc; - Add Channel + @Loc["ButtonAddChannel"] - Edit Channel Numbers + @Loc["ButtonEditChannelNumbers"]
- Channels + @MainLayoutLoc["ButtonChannels"] - + @@ -45,29 +48,29 @@ - Number + @Loc["LabelNumber"] - Logo + @Loc["LabelLogo"] - Name + @Loc["LabelName"] - Language - Mode - FFmpeg Profile + @Loc["LabelLanguage"] + @Loc["LabelStreamingMode"] + @Loc["LabelFFmpegProfile"] - + @if (!context.IsEnabled) { - - @($"(D) {context.Number}") + + @($"({Loc["LabelChannelDisabledAbbreviation"]}) {context.Number}") } else if (!context.ShowInEpg) { - - @($"(H) {context.Number}") + + @($"({Loc["LabelChannelHiddenAbbreviation"]}) {context.Number}") } else @@ -75,7 +78,7 @@ @context.Number } - + @if (!string.IsNullOrWhiteSpace(context.Logo?.Path)) { @@ -85,10 +88,10 @@ } - @context.Name - @context.PreferredAudioLanguageCode - @GetStreamingMode(context.StreamingMode) - + @context.Name + @context.PreferredAudioLanguageCode + @GetStreamingMode(context.StreamingMode) + @if (context.StreamingMode != StreamingMode.HttpLiveStreamingDirect) { @_ffmpegProfiles.Find(p => p.Id == context.FFmpegProfileId)?.Name @@ -102,7 +105,7 @@ bool notNormalizing = maybeProfile.Any(p => !p.NormalizeVideo || !p.NormalizeAudio); if (notNormalizing) { - + @@ -111,7 +114,7 @@ } else { - + @@ -120,7 +123,7 @@ } else if (CanPreviewChannel(context) && _ffmpegProfilesThatCanPreview.ContainsKey(context.FFmpegProfileId) && !_ffmpegProfilesThatCanPreview[context.FFmpegProfileId]) { - +
@@ -130,14 +133,14 @@ } else { - + } @if (SegmenterService.IsActive(context.Number)) { - + @@ -147,14 +150,14 @@ {
} - + @if (context.PlayoutCount > 0 && context.IsEnabled) { - + @@ -164,7 +167,7 @@ {
} - + @@ -173,7 +176,7 @@ - + diff --git a/ErsatzTV/Pages/Settings/UISettings.razor b/ErsatzTV/Pages/Settings/UISettings.razor index e6fa9d409..9815d0c1c 100644 --- a/ErsatzTV/Pages/Settings/UISettings.razor +++ b/ErsatzTV/Pages/Settings/UISettings.razor @@ -29,9 +29,10 @@ Language
- English - Polski - Português (Brasil) + @foreach (Localization.CultureLanguage cultureLanguage in Localization.SupportedLanguages) + { + @cultureLanguage.Language + }
diff --git a/ErsatzTV/Shared/MainLayout.razor b/ErsatzTV/Shared/MainLayout.razor index 81a3e175b..49c9b360d 100644 --- a/ErsatzTV/Shared/MainLayout.razor +++ b/ErsatzTV/Shared/MainLayout.razor @@ -8,7 +8,6 @@ @using ErsatzTV.Core.Notifications @using ErsatzTV.Extensions @using MediatR.Courier -@using Microsoft.Extensions.Localization @implements IDisposable @inject NavigationManager NavigationManager @inject IMediator Mediator diff --git a/ErsatzTV/Startup.cs b/ErsatzTV/Startup.cs index b18631538..09dedf951 100644 --- a/ErsatzTV/Startup.cs +++ b/ErsatzTV/Startup.cs @@ -605,10 +605,9 @@ public class Startup { CultureInfo[] cinfo = CultureInfo.GetCultures(CultureTypes.AllCultures & ~CultureTypes.NeutralCultures); string[] supportedCultures = cinfo.Select(t => t.Name).Distinct().ToArray(); - string[] supportedUiCultures = ["en-us", "pl", "pt-br"]; options.AddSupportedCultures(supportedCultures) - .AddSupportedUICultures(supportedUiCultures) - .SetDefaultCulture("en-us"); + .AddSupportedUICultures(Localization.UiCultures) + .SetDefaultCulture(Localization.DefaultCulture); options.AddInitialRequestCultureProvider( new DatabaseRequestCultureProvider( new AcceptLanguageHeaderRequestCultureProvider())); diff --git a/ErsatzTV/_Imports.razor b/ErsatzTV/_Imports.razor index 8b536efdf..bbe1c8cab 100644 --- a/ErsatzTV/_Imports.razor +++ b/ErsatzTV/_Imports.razor @@ -11,6 +11,7 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.Data.Sqlite @using Microsoft.EntityFrameworkCore +@using Microsoft.Extensions.Localization @using Microsoft.Extensions.Logging @using Microsoft.JSInterop @using Blazored.FluentValidation