Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement a strategy to select the language/culture for each request in a localized ASP.NET Core app #254

Merged
merged 9 commits into from
Jul 1, 2024
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ FRONTEND_APP_BASE_URL=http://localhost:80/
MAX_DAYS_IN_CALENDAR=60
BUSINESS_NAME=World Dental CO
DENTAL_SERVICES_IMAGES_PATH=/app/dental_services
LANGUAGE=es
PLUGINS="
Plugin.ChatBot.dll
Plugin.AppointmentReminders.dll
Expand Down
1 change: 1 addition & 0 deletions DentallApp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Elements", "Soluti
.env.example = .env.example
tests\IntegrationTests\.env.test = tests\IntegrationTests\.env.test
tests\IntegrationTests\.env.test.example = tests\IntegrationTests\.env.test.example
src\HostApplication\appsettings.json = src\HostApplication\appsettings.json
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
EndProjectSection
Expand Down
4 changes: 3 additions & 1 deletion src/HostApplication/Extensions/AuthenticationJwtBearer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

public static class AuthenticationJwtBearer
{
public static IServiceCollection AddAuthenticationJwtBearer(this IServiceCollection services, AppSettings settings)
public static IServiceCollection AddAuthenticationJwtBearer(
this IServiceCollection services,
AppSettings settings)
{
services.AddAuthentication(options =>
{
Expand Down
31 changes: 31 additions & 0 deletions src/HostApplication/Extensions/LanguageExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace DentallApp.HostApplication.Extensions;

public static class LanguageExtensions
{
public static IServiceCollection ConfigureLanguages(
this IServiceCollection services,
IConfiguration configuration)
{
services.Configure<RequestLocalizationOptions>(options =>
{
List<CultureInfo> supportedCultures = [];
var languages = configuration.GetLanguages();
foreach (var language in languages)
supportedCultures.Add(new CultureInfo(language));

var defaultLanguage = configuration.GetDefaultLanguage();
options.DefaultRequestCulture = new RequestCulture(defaultLanguage);
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
return services;
}

public static string[] GetLanguages(this IConfiguration configuration)
=> configuration
.GetRequiredSection("Languages")
.Get<string[]>() ?? [];

public static string GetDefaultLanguage(this IConfiguration configuration)
=> configuration.GetValue<string>("DefaultLanguage") ?? "es";
}
32 changes: 30 additions & 2 deletions src/HostApplication/Extensions/SwaggerGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ public static class SwaggerGen
public static IServiceCollection AddSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(options =>
{
{
options.OperationFilter<AcceptLanguageHeaderParameter>();
options.EnableAnnotations();
options.SwaggerDoc("v1", new OpenApiInfo { Title = "DentallApi", Version = "v1" });
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
Expand All @@ -27,7 +28,7 @@ public static IServiceCollection AddSwagger(this IServiceCollection services)
Id = "Bearer"
}
},
new string[] { }
Array.Empty<string>()
}
});
var coreAssemblyName = typeof(GetDependentsByCurrentUserIdUseCase).Assembly.GetName().Name;
Expand All @@ -44,4 +45,31 @@ public static IServiceCollection AddSwagger(this IServiceCollection services)
});
return services;
}

private class AcceptLanguageHeaderParameter(IConfiguration configuration) : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters ??= [];
var languageOptions = new List<IOpenApiAny>();
var languages = configuration.GetLanguages();
var defaultLanguage = configuration.GetDefaultLanguage();
foreach (var language in languages)
languageOptions.Add(new OpenApiString(language));

operation.Parameters.Add(new OpenApiParameter
{
Name = "Accept-Language",
Description = "Language preference for the response.",
In = ParameterLocation.Header,
Required = false,
Schema = new OpenApiSchema
{
Type = "string",
Default = new OpenApiString(defaultLanguage),
Enum = languageOptions
}
});
}
}
}
4 changes: 4 additions & 0 deletions src/HostApplication/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
global using DentallApp.Shared.Configuration;

global using System.Reflection;
global using System.Globalization;
global using System.Net;
global using System.Text;
global using System.Text.Encodings.Web;
global using System.Text.Json.Serialization;

global using Microsoft.AspNetCore.Localization;
global using Microsoft.AspNetCore.Mvc.ApplicationParts;
global using Microsoft.AspNetCore.Authentication;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
Expand All @@ -31,6 +33,8 @@
global using Microsoft.Extensions.Options;
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.OpenApi.Models;
global using Microsoft.OpenApi.Any;
global using Swashbuckle.AspNetCore.SwaggerGen;
global using EntityFramework.Exceptions.Common;

global using DotEnv.Core;
Expand Down
4 changes: 2 additions & 2 deletions src/HostApplication/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
builder.Services.AddSwagger();
builder.Services.AddAuthenticationJwtBearer(appSettings);
builder.Services.AddValidators();
builder.Services.ConfigureLanguages(builder.Configuration);

builder.Services
.AddExceptionHandler<ReferenceConstraintExceptionHandler>()
Expand All @@ -44,8 +45,7 @@
app.UseExceptionHandler("/");
}

app.UseRequestLocalization(appSettings.Language);

app.UseRequestLocalization();
app.UseWebSockets()
.UsePathBase(new PathString("/api"))
.UseRouting()
Expand Down
9 changes: 0 additions & 9 deletions src/HostApplication/appsettings.Development.json

This file was deleted.

13 changes: 9 additions & 4 deletions src/HostApplication/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": ""
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"DefaultLanguage": "es",
"Languages": [
"es",
"en"
]
}
1 change: 0 additions & 1 deletion src/Shared/Configuration/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ public class AppSettings
{
public string BusinessName { get; set; } = string.Empty;
public string DentalServicesImagesPath { get; set; } = string.Empty;
public string Language { get; set; } = string.Empty;

public string AccessTokenKey { get; set; } = string.Empty;
public double AccessTokenExpires { get; set; }
Expand Down
Loading