Merge pull request #967 from hishamco/language-switcher

Add Language switcher
This commit is contained in:
Shaun Walker 2020-12-04 14:53:48 -05:00 committed by GitHub
commit 1968b0283d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 158 additions and 19 deletions

View File

@ -17,6 +17,7 @@
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -30,6 +31,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" /> <PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -1,19 +1,23 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using System.Runtime.Loader; using System.Runtime.Loader;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Oqtane.Modules; using Oqtane.Modules;
using Oqtane.Providers; using Oqtane.Providers;
using Oqtane.Shared;
using Oqtane.Services; using Oqtane.Services;
using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane.Client namespace Oqtane.Client
{ {
@ -62,6 +66,7 @@ namespace Oqtane.Client
builder.Services.AddScoped<ISiteTemplateService, SiteTemplateService>(); builder.Services.AddScoped<ISiteTemplateService, SiteTemplateService>();
builder.Services.AddScoped<ISqlService, SqlService>(); builder.Services.AddScoped<ISqlService, SqlService>();
builder.Services.AddScoped<ISystemService, SystemService>(); builder.Services.AddScoped<ISystemService, SystemService>();
builder.Services.AddScoped<ILocalizationService, LocalizationService>();
await LoadClientAssemblies(httpClient); await LoadClientAssemblies(httpClient);
@ -88,6 +93,19 @@ namespace Oqtane.Client
} }
var host = builder.Build(); var host = builder.Build();
var jsRuntime = host.Services.GetRequiredService<IJSRuntime>();
var interop = new Interop(jsRuntime);
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie).UICultures[0].Value;
var localizationService = host.Services.GetRequiredService<ILocalizationService>();
var cultures = await localizationService.GetCulturesAsync();
if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase)))
{
culture = cultures.Single(c => c.IsDefault).Name;
}
SetCulture(culture);
ServiceActivator.Configure(host.Services); ServiceActivator.Configure(host.Services);
@ -142,5 +160,12 @@ namespace Oqtane.Client
} }
} }
} }
private static void SetCulture(string culture)
{
var cultureInfo = CultureInfo.GetCultureInfo(culture);
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
}
} }
} }

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Oqtane.Models;
namespace Oqtane.Services
{
public interface ILocalizationService
{
Task<IEnumerable<Culture>> GetCulturesAsync();
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Services
{
public class LocalizationService : ServiceBase, ILocalizationService
{
private readonly SiteState _siteState;
public LocalizationService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "Localization");
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
}
}

View File

@ -198,6 +198,8 @@
</div> </div>
} }
<LanguageSwitcher />
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null)) @if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null))
{ {
if (PageState.EditMode) if (PageState.EditMode)

View File

@ -0,0 +1,46 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@using System.Globalization
@using Microsoft.AspNetCore.Localization;
@using Oqtane.Models
@inject ILocalizationService LocalizationService
@inject NavigationManager NavigationManager
@if (_supportedCultures != null && Visible)
{
<div class="btn-group" role="group">
<button id="btnCultures" type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="oi oi-globe"></span>
</button>
<div class="dropdown-menu" aria-labelledby="btnCultures">
@foreach (var culture in _supportedCultures)
{
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="#" @onclick="@(async e => await SetCultureAsync(culture.Name))">@culture.DisplayName</a>
}
</div>
</div>
}
@code{
private IEnumerable<Culture> _supportedCultures;
[Parameter]
public bool Visible { get; set; } = true;
protected override async Task OnParametersSetAsync()
{
_supportedCultures = await LocalizationService.GetCulturesAsync();
}
private async Task SetCultureAsync(string culture)
{
if (culture != CultureInfo.CurrentUICulture.Name)
{
var interop = new Interop(JSRuntime);
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
}
}
}

View File

@ -1,5 +1,4 @@
using Microsoft.JSInterop; using Microsoft.JSInterop;
using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.UI namespace Oqtane.UI
@ -233,6 +232,5 @@ namespace Oqtane.UI
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
} }
} }

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
public class LocalizationController : Controller
{
private readonly ILocalizationManager _localizationManager;
public LocalizationController(ILocalizationManager localizationManager)
{
_localizationManager = localizationManager;
}
// GET: api/localization
[HttpGet()]
public IEnumerable<Culture> Get()
=> _localizationManager.GetSupportedCultures().Select(c => new Culture {
Name = CultureInfo.GetCultureInfo(c).Name,
DisplayName = CultureInfo.GetCultureInfo(c).DisplayName,
IsDefault = _localizationManager.GetDefaultCulture()
.Equals(CultureInfo.GetCultureInfo(c).Name, StringComparison.OrdinalIgnoreCase)
});
}
}

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
@ -31,11 +30,9 @@ namespace Oqtane.Extensions
var defaultCulture = localizationManager.GetDefaultCulture(); var defaultCulture = localizationManager.GetDefaultCulture();
var supportedCultures = localizationManager.GetSupportedCultures(); var supportedCultures = localizationManager.GetSupportedCultures();
CultureInfo.CurrentUICulture = new CultureInfo(defaultCulture);
app.UseRequestLocalization(options => { app.UseRequestLocalization(options => {
options.SetDefaultCulture(defaultCulture) options.SetDefaultCulture(defaultCulture)
.AddSupportedUICultures(supportedCultures) .AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures); .AddSupportedUICultures(supportedCultures);
}); });

View File

@ -1,17 +1,9 @@
@page "/" @page "/"
@namespace Oqtane.Pages @namespace Oqtane.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@using Microsoft.Extensions.Configuration @using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration @inject IConfiguration Configuration
@model Oqtane.Pages.HostModel @model Oqtane.Pages.HostModel
@{
// Set localization cookie
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture));
HttpContext.Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue);
}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>

View File

@ -127,6 +127,7 @@ namespace Oqtane
services.AddScoped<ISiteTemplateService, SiteTemplateService>(); services.AddScoped<ISiteTemplateService, SiteTemplateService>();
services.AddScoped<ISqlService, SqlService>(); services.AddScoped<ISqlService, SqlService>();
services.AddScoped<ISystemService, SystemService>(); services.AddScoped<ISystemService, SystemService>();
services.AddScoped<ILocalizationService, LocalizationService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

View File

@ -0,0 +1,11 @@
namespace Oqtane.Models
{
public class Culture
{
public string Name { get; set; }
public string DisplayName { get; set; }
public bool IsDefault { get; set; }
}
}