-
Notifications
You must be signed in to change notification settings - Fork 2
Localisation Layer #603
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
base: master
Are you sure you want to change the base?
Localisation Layer #603
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,13 @@ | ||
| // Copyright (c) Alamo Engine Tools and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for details. | ||
|
|
||
| using AnakinRaW.CommonUtilities.Hashing; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.DependencyInjection.Extensions; | ||
| using PG.Commons; | ||
| using PG.StarWarsGame.Files.DAT.Binary; | ||
| using PG.StarWarsGame.Files.DAT.Services; | ||
|
|
||
| namespace PG.StarWarsGame.Files.DAT; | ||
|
|
||
| /// <summary> | ||
|
|
@@ -18,10 +22,13 @@ public static class DatServiceContribution | |
| /// <param name="serviceCollection">The <see cref="IServiceCollection"/> to add services to.</param> | ||
| public static void SupportDAT(this IServiceCollection serviceCollection) | ||
| { | ||
| PetroglyphCommons.ContributeServices(serviceCollection); | ||
| serviceCollection.TryAddSingleton<IHashingService>(sp => new HashingService(sp)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. providing the hashing service, for me, is also responsibility of the consumer |
||
|
|
||
| serviceCollection | ||
| .AddSingleton<IDatFileService>(sp => new DatFileService(sp)) | ||
| .AddSingleton<IDatModelService>(sp => new DatModelService(sp)) | ||
| .AddTransient<IDatFileReader>(sp => new DatFileReader(sp)) | ||
| .AddTransient<IDatBinaryConverter>(sp => new DatBinaryConverter(sp)); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Copyright (c) Alamo Engine Tools and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for details. | ||
|
|
||
| using System; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using PG.StarWarsGame.Localisation.Languages; | ||
| using PG.Testing; | ||
| using Xunit; | ||
|
|
||
| namespace PG.StarWarsGame.Localisation.Baseline.Test; | ||
|
|
||
| public class BaselineTranslationProviderTest : PGTestBase | ||
| { | ||
| protected override void SetupServices(IServiceCollection serviceCollection) | ||
| { | ||
| base.SetupServices(serviceCollection); | ||
| serviceCollection.SupportLocalisationBaseline(); | ||
| } | ||
|
|
||
| private IBaselineTranslationProvider Provider => | ||
| ServiceProvider.GetRequiredService<IBaselineTranslationProvider>(); | ||
|
|
||
| [Theory] | ||
|
Check failure on line 23 in PG.StarWarsGame.Localisation.Baseline/PG.StarWarsGame.Localisation.Baseline.Test/BaselineTranslationProviderTest.cs
|
||
| [InlineData(GameContext.EaW)] | ||
| [InlineData(GameContext.FoC)] | ||
| public void GetMasterText_SingleLanguage_IsNonEmpty(GameContext game) | ||
| { | ||
| var lang = new EnglishAlamoLanguageDefinition(); | ||
| var db = Provider.GetMasterText(game, lang); | ||
| Assert.NotEmpty(db); | ||
| } | ||
|
|
||
| [Theory] | ||
|
Check failure on line 33 in PG.StarWarsGame.Localisation.Baseline/PG.StarWarsGame.Localisation.Baseline.Test/BaselineTranslationProviderTest.cs
|
||
| [InlineData(GameContext.EaW)] | ||
| [InlineData(GameContext.FoC)] | ||
| public void GetCreditsText_SingleLanguage_IsNonEmpty(GameContext game) | ||
| { | ||
| var lang = new EnglishAlamoLanguageDefinition(); | ||
| var db = Provider.GetCreditsText(game, lang); | ||
| Assert.NotEmpty(db); | ||
| } | ||
|
|
||
| [Fact] | ||
|
Check failure on line 43 in PG.StarWarsGame.Localisation.Baseline/PG.StarWarsGame.Localisation.Baseline.Test/BaselineTranslationProviderTest.cs
|
||
| public void GetMasterText_MultiLanguage_ContainsAllRequestedLanguages() | ||
| { | ||
| var langs = new IAlamoLanguageDefinition[] | ||
| { | ||
| new EnglishAlamoLanguageDefinition(), | ||
| new GermanAlamoLanguageDefinition(), | ||
| }; | ||
| var db = Provider.GetMasterText(GameContext.EaW, langs); | ||
| Assert.Equal(2, db.Languages.Count); | ||
| } | ||
|
|
||
| [Fact] | ||
|
Check failure on line 55 in PG.StarWarsGame.Localisation.Baseline/PG.StarWarsGame.Localisation.Baseline.Test/BaselineTranslationProviderTest.cs
|
||
| public void GetMasterText_EaW_English_ContainsKnownKey() | ||
| { | ||
| var lang = new EnglishAlamoLanguageDefinition(); | ||
| var db = Provider.GetMasterText(GameContext.EaW, lang); | ||
| Assert.True(db.Count > 100); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void GetMasterText_NullLanguage_Throws() | ||
| { | ||
| Assert.Throws<ArgumentNullException>(() => Provider.GetMasterText(GameContext.EaW, (IAlamoLanguageDefinition)null!)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <TargetFrameworks>net8.0;net10.0</TargetFrameworks> | ||
| <TargetFrameworks Condition="!$([MSBuild]::IsOsUnixLike())">$(TargetFrameworks);net481</TargetFrameworks> | ||
| </PropertyGroup> | ||
| <PropertyGroup> | ||
| <IsPackable>false</IsPackable> | ||
| <IsTestable>true</IsTestable> | ||
| <OutputType>Exe</OutputType> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PackageReference Include="GitHubActionsTestLogger" Version="3.0.4" /> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" /> | ||
| <PackageReference Include="xunit.v3.mtp-v2" Version="3.2.2" /> | ||
| <PackageReference Include="Microsoft.Testing.Platform" Version="2.2.3" /> | ||
| <PackageReference Include="coverlet.msbuild" Version="10.0.1"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="xunit.runner.visualstudio" Version="3.1.5"> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="coverlet.collector" Version="10.0.1"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| </ItemGroup> | ||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\PG.Testing\PG.Testing.csproj" /> | ||
| <ProjectReference Include="..\..\PG.StarWarsGame.Files.DAT\PG.StarWarsGame.Files.DAT\PG.StarWarsGame.Files.DAT.csproj" /> | ||
| <ProjectReference Include="..\PG.StarWarsGame.Localisation.Baseline\PG.StarWarsGame.Localisation.Baseline.csproj" /> | ||
| </ItemGroup> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // Copyright (c) Alamo Engine Tools and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for details. | ||
|
|
||
| using Microsoft.Extensions.DependencyInjection; | ||
| using PG.StarWarsGame.Files.DAT; | ||
|
|
||
| namespace PG.StarWarsGame.Localisation.Baseline | ||
| { | ||
| /// <summary> | ||
| /// Provides initialization routines for the PG.StarWarsGame.Localisation.Baseline library. | ||
| /// </summary> | ||
| public static class BaselineServiceContribution | ||
| { | ||
| /// <summary> | ||
| /// Adds all services required by this library, including DAT and localisation core services. | ||
| /// </summary> | ||
| public static IServiceCollection SupportLocalisationBaseline(this IServiceCollection serviceCollection) | ||
| { | ||
| serviceCollection.SupportDAT(); | ||
| serviceCollection.SupportLocalisation(); | ||
|
|
||
| serviceCollection.AddSingleton<IBaselineTranslationProvider>(sp => | ||
| new BaselineTranslationProvider(sp)); | ||
|
|
||
| return serviceCollection; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| // Copyright (c) Alamo Engine Tools and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for details. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Reflection; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using PG.Commons.Services; | ||
| using PG.StarWarsGame.Files.DAT.Services; | ||
| using PG.StarWarsGame.Localisation.Data; | ||
| using PG.StarWarsGame.Localisation.IO.Dat; | ||
| using PG.StarWarsGame.Localisation.Languages; | ||
|
|
||
| namespace PG.StarWarsGame.Localisation.Baseline | ||
| { | ||
| internal sealed class BaselineTranslationProvider : ServiceBase, IBaselineTranslationProvider | ||
| { | ||
| // The 5 western languages shipped with the original games have embedded baseline data. | ||
| // The remaining officially supported languages are mapped for future resource additions. | ||
| private static readonly IReadOnlyDictionary<string, (string Folder, string NameSuffix)> LanguageMap = | ||
|
AnakinRaW marked this conversation as resolved.
|
||
| new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase) | ||
| { | ||
| ["ENGLISH"] = ("en", "english"), | ||
| ["GERMAN"] = ("de", "german"), | ||
| ["SPANISH"] = ("es", "spanish"), | ||
| ["FRENCH"] = ("fr", "french"), | ||
| ["ITALIAN"] = ("it", "italian"), | ||
| ["CHINESE"] = ("zh", "chinese"), | ||
| ["POLISH"] = ("pl", "polish"), | ||
| ["RUSSIAN"] = ("ru", "russian"), | ||
| ["JAPANESE"] = ("ja", "japanese"), | ||
| ["KOREAN"] = ("ko", "korean"), | ||
| ["THAI"] = ("th", "thai"), | ||
| }; | ||
|
|
||
| private readonly IDatFileService _datFileService; | ||
| private readonly IDatTranslationImporter _importer; | ||
| private readonly ITranslationDatabaseFactory _factory; | ||
|
|
||
| public BaselineTranslationProvider(IServiceProvider services) : base(services) | ||
| { | ||
| _datFileService = services.GetRequiredService<IDatFileService>(); | ||
| _importer = services.GetRequiredService<IDatTranslationImporter>(); | ||
| _factory = services.GetRequiredService<ITranslationDatabaseFactory>(); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public IKeyedTranslationDatabase GetMasterText(GameContext game, IAlamoLanguageDefinition language) | ||
| { | ||
| if (language is null) throw new ArgumentNullException(nameof(language)); | ||
| var db = _factory.CreateKeyed(new[] { language }); | ||
| LoadInto(db, game, "mastertextfile", language); | ||
| return db; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public IKeyedTranslationDatabase GetMasterText(GameContext game, IReadOnlyList<IAlamoLanguageDefinition> languages) | ||
| { | ||
| if (languages is null) throw new ArgumentNullException(nameof(languages)); | ||
| var db = _factory.CreateKeyed(languages); | ||
| foreach (var lang in languages) | ||
| LoadInto(db, game, "mastertextfile", lang); | ||
| return db; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public IOrderedTranslationDatabase GetCreditsText(GameContext game, IAlamoLanguageDefinition language) | ||
| { | ||
| if (language is null) throw new ArgumentNullException(nameof(language)); | ||
| var db = _factory.CreateOrdered(new[] { language }); | ||
| LoadInto(db, game, "creditstext", language); | ||
| return db; | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public IOrderedTranslationDatabase GetCreditsText(GameContext game, IReadOnlyList<IAlamoLanguageDefinition> languages) | ||
| { | ||
| if (languages is null) throw new ArgumentNullException(nameof(languages)); | ||
| var db = _factory.CreateOrdered(languages); | ||
| foreach (var lang in languages) | ||
| LoadInto(db, game, "creditstext", lang); | ||
| return db; | ||
| } | ||
|
|
||
| private void LoadInto(ITranslationDatabase db, GameContext game, string filePrefix, IAlamoLanguageDefinition language) | ||
| { | ||
| if (!LanguageMap.TryGetValue(language.LanguageIdentifier, out var map)) | ||
| return; | ||
|
|
||
| var resourceName = BuildResourceName(game, map.Folder, $"{filePrefix}_{map.NameSuffix}.dat"); | ||
| using var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); | ||
| if (resourceStream is null) return; | ||
|
|
||
| var tempPath = FileSystem.Path.Combine( | ||
| FileSystem.Path.GetTempPath(), | ||
| $"pg_baseline_{Guid.NewGuid():N}.dat"); | ||
|
|
||
| try | ||
| { | ||
| using (var fs = FileSystem.FileStream.New(tempPath, FileMode.Create, FileAccess.Write)) | ||
| resourceStream.CopyTo(fs); | ||
|
|
||
| var datFile = _datFileService.Load(tempPath); | ||
| _importer.Import(datFile.Content, language, db); | ||
| } | ||
| finally | ||
| { | ||
| try | ||
| { | ||
| FileSystem.File.Delete(tempPath); | ||
| } | ||
| catch | ||
| { | ||
| // NOP | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static string BuildResourceName(GameContext game, string langFolder, string fileName) | ||
| { | ||
| var gameFolder = game == GameContext.EaW ? "EaW" : "FoC"; | ||
| return $"PG.StarWarsGame.Localisation.Baseline.Resources.{gameFolder}.{langFolder}.{fileName}"; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| // Copyright (c) Alamo Engine Tools and contributors. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for details. | ||
|
|
||
| using System.Collections.Generic; | ||
| using PG.StarWarsGame.Localisation.Data; | ||
| using PG.StarWarsGame.Localisation.Languages; | ||
|
|
||
| namespace PG.StarWarsGame.Localisation.Baseline | ||
| { | ||
| /// <summary> | ||
| /// Provides pre-loaded translation databases built from the official EaW and FoC game localisations. | ||
| /// </summary> | ||
| public interface IBaselineTranslationProvider | ||
| { | ||
| /// <summary> | ||
| /// Returns a keyed translation database (MasterText) for the given game and single language. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Languages with no baseline data contribute no entries to the returned database. | ||
| /// </remarks> | ||
| /// <exception cref="System.ArgumentNullException"><paramref name="language"/> is <see langword="null"/>.</exception> | ||
| IKeyedTranslationDatabase GetMasterText(GameContext game, IAlamoLanguageDefinition language); | ||
|
|
||
| /// <summary> | ||
| /// Returns a keyed translation database (MasterText) merged from the given game and languages. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Languages with no baseline data contribute no entries to the returned database. | ||
| /// </remarks> | ||
| /// <exception cref="System.ArgumentNullException"><paramref name="languages"/> is <see langword="null"/>.</exception> | ||
| IKeyedTranslationDatabase GetMasterText(GameContext game, IReadOnlyList<IAlamoLanguageDefinition> languages); | ||
|
|
||
| /// <summary> | ||
| /// Returns an ordered translation database (CreditsText) for the given game and single language. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Languages with no baseline data contribute no entries to the returned database. | ||
| /// </remarks> | ||
| /// <exception cref="System.ArgumentNullException"><paramref name="language"/> is <see langword="null"/>.</exception> | ||
| IOrderedTranslationDatabase GetCreditsText(GameContext game, IAlamoLanguageDefinition language); | ||
|
|
||
| /// <summary> | ||
| /// Returns an ordered translation database (CreditsText) merged from the given game and languages. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Languages with no baseline data contribute no entries to the returned database. | ||
| /// </remarks> | ||
| /// <exception cref="System.ArgumentNullException"><paramref name="languages"/> is <see langword="null"/>.</exception> | ||
| IOrderedTranslationDatabase GetCreditsText(GameContext game, IReadOnlyList<IAlamoLanguageDefinition> languages); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
providing the hashing service, for me, is also responsibility of the consumer