Feature: Implement translation service #1
23
.gitignore
vendored
23
.gitignore
vendored
@@ -414,3 +414,26 @@ FodyWeavers.xsd
|
|||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
.ionide
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode
|
||||||
@@ -4,11 +4,17 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>5637e3c4-2341-4bdb-85ec-c75faeee9847</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.2" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\GameIdeas.Resources\GameIdeas.Resources.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<NavMenu />
|
<NavMenu />
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using GameIdeas.Resources;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Layout;
|
||||||
|
|
||||||
|
public partial class MainLayout(
|
||||||
|
IHttpClientFactory HttpClientFactory,
|
||||||
|
TranslationService TranslationService,
|
||||||
|
Translations Translations) : LayoutComponentBase
|
||||||
|
{
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var client = HttpClientFactory.CreateClient("GameIdeas.WebAPI");
|
||||||
|
var response = await client.GetAsync("api/Translations");
|
||||||
|
var dictionary = await response.Content.ReadFromJsonAsync<Dictionary<string, string>>();
|
||||||
|
if (dictionary != null)
|
||||||
|
{
|
||||||
|
TranslationService.Initialize(dictionary);
|
||||||
|
ResourcesKey.Initialize(Translations);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using GameIdeas.WebApp;
|
using GameIdeas.BlazorApp;
|
||||||
|
using GameIdeas.Resources;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
|
|
||||||
@@ -6,6 +7,20 @@ var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
|||||||
builder.RootComponents.Add<App>("#app");
|
builder.RootComponents.Add<App>("#app");
|
||||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||||
|
|
||||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
UriBuilder uriBuilder = new(builder.HostEnvironment.BaseAddress)
|
||||||
|
{
|
||||||
|
Port = 8000
|
||||||
|
};
|
||||||
|
|
||||||
await builder.Build().RunAsync();
|
builder.Services.AddHttpClient(
|
||||||
|
"GameIdeas.WebAPI",
|
||||||
|
client =>
|
||||||
|
{
|
||||||
|
client.BaseAddress = uriBuilder.Uri;
|
||||||
|
client.Timeout = TimeSpan.FromMinutes(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<TranslationService>();
|
||||||
|
builder.Services.AddSingleton<Translations>();
|
||||||
|
|
||||||
|
await builder.Build().RunAsync();
|
||||||
@@ -6,5 +6,5 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using GameIdeas.WebApp
|
@using GameIdeas.BlazorApp
|
||||||
@using GameIdeas.WebApp.Layout
|
@using GameIdeas.BlazorApp.Layout
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"date": "2022-01-06",
|
|
||||||
"temperatureC": 1,
|
|
||||||
"summary": "Freezing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2022-01-07",
|
|
||||||
"temperatureC": 14,
|
|
||||||
"summary": "Bracing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2022-01-08",
|
|
||||||
"temperatureC": -13,
|
|
||||||
"summary": "Freezing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2022-01-09",
|
|
||||||
"temperatureC": -16,
|
|
||||||
"summary": "Balmy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2022-01-10",
|
|
||||||
"temperatureC": -2,
|
|
||||||
"summary": "Chilly"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1 +1,19 @@
|
|||||||
|
|
||||||
|
namespace GameIdeas.Resources;
|
||||||
|
|
||||||
|
public class Translations (TranslationService translationService)
|
||||||
|
{
|
||||||
|
public string GamesIdeas => translationService.Translate(nameof(GamesIdeas));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ResourcesKey
|
||||||
|
{
|
||||||
|
private static Translations? _instance;
|
||||||
|
|
||||||
|
public static void Initialize(Translations translations)
|
||||||
|
{
|
||||||
|
_instance = translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GamesIdeas => _instance?.GamesIdeas ?? throw new InvalidOperationException("ResourcesKey.GamesIdeas is not initialized.");
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<#
|
<#
|
||||||
var path = Path.GetDirectoryName(Host.TemplateFile);
|
var path = Path.GetDirectoryName(Host.TemplateFile);
|
||||||
var localPath = Host.ResolvePath(Path.Combine(path,"../../Server/GameIdeas.API/Files/GameIdeas.fr.json"));
|
var localPath = Host.ResolvePath(Path.Combine(path,"../../GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json"));
|
||||||
|
|
||||||
var json = File.ReadAllText(localPath);
|
var json = File.ReadAllText(localPath);
|
||||||
|
|
||||||
@@ -121,8 +121,8 @@
|
|||||||
|
|
||||||
// Generate the class
|
// Generate the class
|
||||||
#>
|
#>
|
||||||
using System;
|
namespace GameIdeas.Resources;
|
||||||
namespace ArgosV2.Resources;
|
|
||||||
public class Translations (TranslationService translationService)
|
public class Translations (TranslationService translationService)
|
||||||
{
|
{
|
||||||
<#
|
<#
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace ArgosV2.Resources;
|
namespace GameIdeas.Resources;
|
||||||
|
|
||||||
public class TranslationService
|
public class TranslationService
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
using ArgosV2.AppInsight.Logging;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.IdentityModel.Abstractions;
|
|
||||||
|
|
||||||
namespace ArgosV2.Server.WebAPI.Controllers;
|
namespace GameIdeas.WebAPI.Controllers;
|
||||||
|
|
||||||
[Route("api/[controller]")]
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class TranslationsController (ITelemetryService telemetryClient) : ControllerBase
|
[Route("api/[controller]")]
|
||||||
|
|
||||||
|
public class TranslationsController (ILogger<TranslationsController> Logger) : ControllerBase
|
||||||
{
|
{
|
||||||
[Authorize]
|
[HttpGet]
|
||||||
[HttpGet]
|
|
||||||
public async Task<IActionResult> GetTranslations()
|
public async Task<IActionResult> GetTranslations()
|
||||||
{
|
{
|
||||||
var dictionary = new Dictionary<string, string>();
|
var dictionary = new Dictionary<string, string>();
|
||||||
@@ -31,7 +28,7 @@ public class TranslationsController (ITelemetryService telemetryClient) : Contro
|
|||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
{
|
{
|
||||||
telemetryClient.TrackException<TranslationsController>(ex);
|
Logger.LogError(ex, "Internal translations error");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(dictionary);
|
return Ok(dictionary);
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace GameIdeas.API.Controllers
|
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("[controller]")]
|
|
||||||
public class WeatherForecastController : ControllerBase
|
|
||||||
{
|
|
||||||
private static readonly string[] Summaries = new[]
|
|
||||||
{
|
|
||||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly ILogger<WeatherForecastController> _logger;
|
|
||||||
|
|
||||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet(Name = "GetWeatherForecast")]
|
|
||||||
public IEnumerable<WeatherForecast> Get()
|
|
||||||
{
|
|
||||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
|
||||||
{
|
|
||||||
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
|
||||||
TemperatureC = Random.Shared.Next(-20, 55),
|
|
||||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
|
||||||
})
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
|
"GamesIdeas": "Game Ideas"
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
@GameIdeas.API_HostAddress = http://localhost:5190
|
@GameIdeas.API_HostAddress = http://localhost:8000
|
||||||
|
|
||||||
GET {{GameIdeas.API_HostAddress}}/weatherforecast/
|
GET {{GameIdeas.API_HostAddress}}/translations
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|||||||
@@ -7,15 +7,15 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="Files\GameIdeas.fr.json" />
|
<EmbeddedResource Include="Files\GameIdeas.fr.json" />
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="Files\GameIdeas.fr.json" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\GameIdeas.Resources\GameIdeas.Resources.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using GameIdeas.Resources;
|
||||||
|
using System.Resources;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
@@ -6,6 +9,14 @@ builder.Services.AddControllers();
|
|||||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
|
||||||
|
builder.Services.AddCors(option => option.AddDefaultPolicy(policy =>
|
||||||
|
policy.WithOrigins("http://localhost:5172")
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.WithMethods("GET", "POST")));
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<TranslationService>();
|
||||||
|
builder.Services.AddSingleton<Translations>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
@@ -14,6 +25,21 @@ if (app.Environment.IsDevelopment())
|
|||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var filesDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Files");
|
||||||
|
var translationFiles = Directory.GetFiles(filesDirectory, "*.json");
|
||||||
|
var dictionary = new Dictionary<string, string>();
|
||||||
|
foreach (var file in translationFiles)
|
||||||
|
{
|
||||||
|
var name = file.Split('.');
|
||||||
|
var culture = name[^2];
|
||||||
|
var content = await File.ReadAllTextAsync(file);
|
||||||
|
dictionary.Add(culture, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Services.GetRequiredService<TranslationService>().Initialize(dictionary);
|
||||||
|
ResourcesKey.Initialize(app.Services.GetRequiredService<Translations>());
|
||||||
|
|
||||||
|
app.UseCors();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": false,
|
||||||
"applicationUrl": "http://localhost:5190",
|
"applicationUrl": "http://localhost:8000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace GameIdeas.API
|
|
||||||
{
|
|
||||||
public class WeatherForecast
|
|
||||||
{
|
|
||||||
public DateOnly Date { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureC { get; set; }
|
|
||||||
|
|
||||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
|
||||||
|
|
||||||
public string? Summary { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0
workflows/build.yaml
Normal file
0
workflows/build.yaml
Normal file
Reference in New Issue
Block a user