From 79b7bf07e0ce59c1c3d25c97d2cf7ec9037f5358 Mon Sep 17 00:00:00 2001 From: Lilian Date: Thu, 23 May 2024 18:40:07 +0200 Subject: [PATCH] [frontend] Add localization support, perform initial localization pass --- Iceshrimp.Frontend/Components/Compose.razor | 6 +- .../Components/Note/NoteMetadata.razor | 24 ++-- .../Core/Miscellaneous/LocaleHelper.cs | 26 ++++ Iceshrimp.Frontend/Iceshrimp.Frontend.csproj | 20 +++ Iceshrimp.Frontend/Layout/Sidebar.razor | 15 ++- .../Localization/Localization.Designer.cs | 114 ++++++++++++++++++ .../Localization/Localization.en-150.resx | 47 ++++++++ .../Localization/Localization.resx | 54 +++++++++ Iceshrimp.Frontend/Startup.cs | 11 +- 9 files changed, 298 insertions(+), 19 deletions(-) create mode 100644 Iceshrimp.Frontend/Core/Miscellaneous/LocaleHelper.cs create mode 100644 Iceshrimp.Frontend/Localization/Localization.Designer.cs create mode 100644 Iceshrimp.Frontend/Localization/Localization.en-150.resx create mode 100644 Iceshrimp.Frontend/Localization/Localization.resx diff --git a/Iceshrimp.Frontend/Components/Compose.razor b/Iceshrimp.Frontend/Components/Compose.razor index 3ba127b3..9688fcdf 100644 --- a/Iceshrimp.Frontend/Components/Compose.razor +++ b/Iceshrimp.Frontend/Components/Compose.razor @@ -1,18 +1,20 @@ -@using FParsec @using Iceshrimp.Assets.PhosphorIcons @using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Shared.Schemas @using Iceshrimp.Frontend.Components.Note +@using Iceshrimp.Frontend.Localization +@using Microsoft.Extensions.Localization @inject IJSRuntime Js @inject ApiService ApiService @inject ComposeService ComposeService +@inject IStringLocalizer Loc;
- +
@if (ReplyOrQuote != null) { diff --git a/Iceshrimp.Frontend/Components/Note/NoteMetadata.razor b/Iceshrimp.Frontend/Components/Note/NoteMetadata.razor index 3d4e7ada..2239cbf1 100644 --- a/Iceshrimp.Frontend/Components/Note/NoteMetadata.razor +++ b/Iceshrimp.Frontend/Components/Note/NoteMetadata.razor @@ -1,5 +1,9 @@ @using Iceshrimp.Assets.PhosphorIcons +@using Iceshrimp.Frontend.Localization @using Iceshrimp.Shared.Schemas +@using Microsoft.Extensions.Localization +@inject IStringLocalizer Loc; + @code { - [Parameter] [EditorRequired] public required DateTime CreatedAt { get; set; } - [Parameter] [EditorRequired] public required NoteVisibility Visibility { get; set; } - [Parameter] [EditorRequired] public required string? InstanceName { get; set; } + [Parameter] [EditorRequired] public required DateTime CreatedAt { get; set; } + [Parameter] [EditorRequired] public required NoteVisibility Visibility { get; set; } + [Parameter] [EditorRequired] public required string? InstanceName { get; set; } private string RenderDate(DateTime date) { var diff = DateTime.Now - date; return diff switch { - { Days: >= 365 } => $"{diff.Days / 365}yr", - { Days: >= 30 } => $"{diff.Days / 30}mo", - { Days: >= 7 } => $"{diff.Days / 7}w", - { Days: >= 1 } => $"{diff.Days}d", - { Hours: >= 1 } => $"{diff.Hours}h", - { Minutes: >= 1 } => $"{diff.Minutes}m", - _ => "Just now." + { Days: >= 365 } => Loc["{0}y", diff.Days / 365], + { Days: >= 30 } => Loc["{0}mo" ,diff.Days / 30], + { Days: >= 7 } => Loc["{0}d", diff.Days / 7], + { Days: >= 1 } => Loc["{0}d", diff.Days], + { Hours: >= 1 } => Loc["{0}h", diff.Hours], + { Minutes: >= 1 } => Loc["{0}m", diff.Minutes], + _ => Loc["Just now"] }; } diff --git a/Iceshrimp.Frontend/Core/Miscellaneous/LocaleHelper.cs b/Iceshrimp.Frontend/Core/Miscellaneous/LocaleHelper.cs new file mode 100644 index 00000000..51d6d88d --- /dev/null +++ b/Iceshrimp.Frontend/Core/Miscellaneous/LocaleHelper.cs @@ -0,0 +1,26 @@ +using System.Globalization; +using Blazored.LocalStorage; +using Microsoft.AspNetCore.Components; + +namespace Iceshrimp.Frontend.Core.Miscellaneous; + +public class LocaleHelper(ISyncLocalStorageService localStorage) +{ + [Inject] public ISyncLocalStorageService LocalStorage { get; } = localStorage; + + public CultureInfo LoadCulture() + { + var defaultCulture = "en-150"; + var culture = LocalStorage.GetItem("blazorCulture") ?? defaultCulture; + var res = new CultureInfo(culture); + return res; + } + + public void StoreCulture(CultureInfo cultureInfo) + { + var cultureString = cultureInfo.Name; + LocalStorage.SetItem("blazorCulture", cultureString); + } + + +} \ No newline at end of file diff --git a/Iceshrimp.Frontend/Iceshrimp.Frontend.csproj b/Iceshrimp.Frontend/Iceshrimp.Frontend.csproj index 1ec4125a..1a568768 100644 --- a/Iceshrimp.Frontend/Iceshrimp.Frontend.csproj +++ b/Iceshrimp.Frontend/Iceshrimp.Frontend.csproj @@ -21,6 +21,10 @@ true + + true + + @@ -35,8 +39,24 @@ + + + + ResXFileCodeGenerator + Localization.Designer.cs + + + + + + True + True + Localization.resx + + + diff --git a/Iceshrimp.Frontend/Layout/Sidebar.razor b/Iceshrimp.Frontend/Layout/Sidebar.razor index f0c3b12a..935ce0c4 100644 --- a/Iceshrimp.Frontend/Layout/Sidebar.razor +++ b/Iceshrimp.Frontend/Layout/Sidebar.razor @@ -1,5 +1,8 @@ @using Iceshrimp.Frontend.Components @using Iceshrimp.Assets.PhosphorIcons +@using Microsoft.Extensions.Localization +@inject IStringLocalizer Loc; +
@@ -30,19 +33,19 @@
diff --git a/Iceshrimp.Frontend/Localization/Localization.Designer.cs b/Iceshrimp.Frontend/Localization/Localization.Designer.cs new file mode 100644 index 00000000..f7fc9eeb --- /dev/null +++ b/Iceshrimp.Frontend/Localization/Localization.Designer.cs @@ -0,0 +1,114 @@ +//------------------------------------------------------------------------------ +// +// 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 Iceshrimp.Frontend.Localization { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Localization { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Localization() { + } + + [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("Iceshrimp.Frontend.Localization.Localization", typeof(Localization).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 Timeline { + get { + return ResourceManager.GetString("Timeline", resourceCulture); + } + } + + internal static string Notifications { + get { + return ResourceManager.GetString("Notifications", resourceCulture); + } + } + + internal static string Post { + get { + return ResourceManager.GetString("Post", resourceCulture); + } + } + + internal static string _0_yr { + get { + return ResourceManager.GetString("{0}yr", resourceCulture); + } + } + + internal static string _0_mo { + get { + return ResourceManager.GetString("{0}mo", resourceCulture); + } + } + + internal static string _0_w { + get { + return ResourceManager.GetString("{0}w", resourceCulture); + } + } + + internal static string _0_d { + get { + return ResourceManager.GetString("{0}d", resourceCulture); + } + } + + internal static string _0_h { + get { + return ResourceManager.GetString("{0}h", resourceCulture); + } + } + + internal static string _0_m { + get { + return ResourceManager.GetString("{0}m", resourceCulture); + } + } + + internal static string Just_now { + get { + return ResourceManager.GetString("Just now", resourceCulture); + } + } + + internal static string ComposePost { + get { + return ResourceManager.GetString("ComposePost", resourceCulture); + } + } + } +} diff --git a/Iceshrimp.Frontend/Localization/Localization.en-150.resx b/Iceshrimp.Frontend/Localization/Localization.en-150.resx new file mode 100644 index 00000000..ec49037c --- /dev/null +++ b/Iceshrimp.Frontend/Localization/Localization.en-150.resx @@ -0,0 +1,47 @@ + + + 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 + + + Timeline + + + Notifications + + + Post + + + {0}yr + + + {0}d + + + {0}h + + + {0}m + + + {0}mo + + + {0}w + + + Just now + + + Post + + \ No newline at end of file diff --git a/Iceshrimp.Frontend/Localization/Localization.resx b/Iceshrimp.Frontend/Localization/Localization.resx new file mode 100644 index 00000000..f8a313ee --- /dev/null +++ b/Iceshrimp.Frontend/Localization/Localization.resx @@ -0,0 +1,54 @@ + + + + + + + + + + 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 + + + Timeline + + + Notifications + + + Post + + + {0}yr + + + {0}mo + + + {0}w + + + {0}d + + + {0}h + + + {0}m + + + Just now + + + Post + + \ No newline at end of file diff --git a/Iceshrimp.Frontend/Startup.cs b/Iceshrimp.Frontend/Startup.cs index 024171c5..7ec6ece6 100644 --- a/Iceshrimp.Frontend/Startup.cs +++ b/Iceshrimp.Frontend/Startup.cs @@ -5,12 +5,15 @@ using Iceshrimp.Frontend; using Iceshrimp.Frontend.Core.Services; using Ljbc1994.Blazor.IntersectionObserver; using Microsoft.AspNetCore.Components.Authorization; +using System.Globalization; +using Iceshrimp.Frontend.Core.Miscellaneous; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); builder.RootComponents.Add("head::after"); builder.Services.AddSingleton(_ => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); +builder.Services.AddLocalization(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddIntersectionObserver(); @@ -22,4 +25,10 @@ builder.Services.AddAuthorizationCore(); builder.Services.AddCascadingAuthenticationState(); builder.Services.AddBlazoredLocalStorageAsSingleton(); -await builder.Build().RunAsync(); \ No newline at end of file +// Culture information (locale) has to be set before run. +var host = builder.Build(); +var helper = new LocaleHelper(host.Services.GetRequiredService()); +var culture = helper.LoadCulture(); +CultureInfo.DefaultThreadCurrentCulture = culture; +CultureInfo.DefaultThreadCurrentUICulture = culture; +await host.RunAsync(); \ No newline at end of file