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

Data Localization #4466

Open
wants to merge 68 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
a126cae
Add data localization infrastructure APIs
hishamco Oct 3, 2019
129296e
Inherit from IStringLocalizer
hishamco Oct 3, 2019
1f5e087
Add unit tests
hishamco Oct 3, 2019
c95b6ea
Add ILocalizationDataProvider and Content implementations
hishamco Oct 3, 2019
0b9b455
First attempt to support import/export deployment step
hishamco Oct 4, 2019
f62c403
Add TranslationsManager unit tests
hishamco Oct 5, 2019
c84f2b4
Introduce DataLocalizedString
hishamco Oct 5, 2019
4589796
Merge branch 'dev' into hishamco/data-localization
Skrypt Dec 20, 2020
6a0431b
Fixing build
Skrypt Dec 20, 2020
94b6599
Cleanup
Skrypt Dec 20, 2020
4a1d923
Fix bug in CreateContentTypeDefinition()
hishamco Dec 21, 2020
9ab13e5
Address feedback
hishamco Dec 22, 2020
b7f74cf
Fix unit tests
hishamco Dec 22, 2020
609cf58
Merge branch 'dev' into skrypt/data-localization
Skrypt Dec 24, 2020
4194e42
Merge branch 'dev' into skrypt/data-localization
Skrypt Jan 1, 2021
25c74b8
Merge branch 'dev' into skrypt/data-localization
Skrypt Jan 4, 2021
8c6a71d
Merge branch 'dev' into skrypt/data-localization
Skrypt Jan 7, 2021
b756c6a
Merge branch 'dev' into skrypt/data-localization
Skrypt Jan 11, 2021
c20de77
Merge branch 'dev' into skrypt/data-localization
Skrypt Jan 23, 2021
0950120
Move to OrchardCore.Localization.Dynamic as a separate feature with a…
Skrypt Jan 23, 2021
2f769ae
Remove unnecessary using
Skrypt Jan 23, 2021
dae05ab
Merge branch 'dev' into skrypt/data-localization
Skrypt Feb 3, 2021
66642dc
Merge branch 'dev' into skrypt/data-localization
Skrypt Feb 5, 2021
5a7e40e
Fix solution after last merge
Skrypt Feb 5, 2021
7ee8f59
Merge branch 'dev' into skrypt/data-localization
Skrypt Feb 17, 2021
b418f5b
Merge branch 'dev' into skrypt/data-localization
Skrypt Feb 26, 2021
1ca4a96
Fix merge and rename to OrchardCore.Localization.Data
Skrypt Feb 26, 2021
3d3d7e3
Merge branch 'dev' into skrypt/data-localization
Skrypt Mar 2, 2021
3a96304
Merge branch 'dev' into skrypt/data-localization
Skrypt Mar 4, 2021
bb8787a
Merge branch 'dev' into skrypt/data-localization
Skrypt May 7, 2021
833eafc
Merge remote-tracking branch 'origin/dev' into hishamco/data-localiza…
hishamco May 7, 2021
01f7d86
DynamicData -> Data
hishamco May 7, 2021
a416e05
Fix extnsion method name
hishamco May 7, 2021
713e089
DataLocalizer should support pluralization
hishamco May 7, 2021
4c7fa54
Call AddDataLocalization() in OC.Localization.Data module
hishamco May 7, 2021
52f1d6d
Fix localizer type in tests
hishamco May 7, 2021
51d4922
Add proper IDataLocalizer registration
hishamco May 7, 2021
a28a2b5
Tweaks
hishamco May 8, 2021
a33c2d8
Uncomment LocalizerReturnsTranslationFromInnerClass() test
hishamco May 8, 2021
1434c94
Add DataLocalizerFactory unit tests
hishamco May 8, 2021
3f1d0a4
OrchardCore.Localization.Data -> OrchardCore.DataLocalization
hishamco May 8, 2021
2ef68b8
Allow mutiple ITranslationProvider registration
hishamco May 8, 2021
3ecac03
Add missing changes in solution file
hishamco May 8, 2021
d4e33f2
Fix OC.DataLocalization folder
hishamco May 8, 2021
c62cd8a
Fix the build
hishamco May 8, 2021
75f9d48
Removing unnecessary views
Skrypt May 10, 2021
2332b87
Merge branch 'dev' into hishamco/data-localization
Skrypt May 10, 2021
b7fa41b
Rename to GetAllStrings to GetDescriptors in ILocalizationDataProvider
Skrypt May 14, 2021
9ca5fdc
Renaming
Skrypt May 14, 2021
734ffa2
Merge branch 'dev' into hishamco/data-localization
Skrypt May 31, 2021
1c88a22
Merge branch 'main' into hishamco/data-localization
hishamco Mar 30, 2023
f981fff
Fix merge conflict
hishamco Mar 30, 2023
5ce581a
New updates from OCC
hishamco Mar 30, 2023
e8dedb2
Fix broken test
hishamco Mar 30, 2023
9fb846a
Fix unit tests namespace
hishamco Mar 30, 2023
6d78ae3
usings cleanup
hishamco Mar 30, 2023
4b029f4
Use file-scoped namespace
hishamco Mar 30, 2023
d6a9bce
Fix namespace
hishamco Mar 30, 2023
dd10ed1
Remove WithCulture method
hishamco Mar 30, 2023
8026d0f
Use IPluralRuleProvider in DataResourceManager
hishamco Mar 30, 2023
4191ad4
Merge branch 'main' into hishamco/data-localization
hishamco Feb 23, 2024
94905b6
Fix the build
hishamco Feb 24, 2024
3c9cc91
Fix unit tests & refactoring
hishamco Feb 24, 2024
8ae7ce3
GetDescriptors() -> GetDescriptorsAsync()
hishamco Feb 24, 2024
594f6ed
Merge branch 'main' into hishamco/data-localization
hishamco Apr 8, 2024
ca0288b
Merge branch 'main' into hishamco/data-localization
hishamco Jun 22, 2024
773756f
Merge branch 'main' into hishamco/data-localization
sebastienros Aug 29, 2024
a172c7e
Merge branch 'main' into hishamco/data-localization
hishamco Dec 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions OrchardCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Search.Elastics
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Search.Elasticsearch", "src\OrchardCore.Modules\OrchardCore.Search.Elasticsearch\OrchardCore.Search.Elasticsearch.csproj", "{B6A54EA8-F285-436D-8257-6F6EDE4C3339}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.DataLocalization", "src\OrchardCore.Modules\OrchardCore.DataLocalization\OrchardCore.DataLocalization.csproj", "{64598086-231E-40F9-8163-1042A0533697}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Data.YesSql", "src\OrchardCore\OrchardCore.Data.YesSql\OrchardCore.Data.YesSql.csproj", "{F06E4E20-3675-4BA5-AD2D-4538FAB154D5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Data.YesSql.Abstractions", "src\OrchardCore\OrchardCore.Data.YesSql.Abstractions\OrchardCore.Data.YesSql.Abstractions.csproj", "{AB47A65C-7BA9-4CE7-BA73-285EB7A2CEFD}"
Expand Down Expand Up @@ -1340,6 +1342,10 @@ Global
{19594A96-A033-4820-820B-C6186D00D507}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19594A96-A033-4820-820B-C6186D00D507}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19594A96-A033-4820-820B-C6186D00D507}.Release|Any CPU.Build.0 = Release|Any CPU
{64598086-231E-40F9-8163-1042A0533697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64598086-231E-40F9-8163-1042A0533697}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64598086-231E-40F9-8163-1042A0533697}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64598086-231E-40F9-8163-1042A0533697}.Release|Any CPU.Build.0 = Release|Any CPU
{91CED599-45B1-474D-A7F2-231BF857A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91CED599-45B1-474D-A7F2-231BF857A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91CED599-45B1-474D-A7F2-231BF857A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -1634,6 +1640,7 @@ Global
{307AA9CB-D62E-4E30-B715-53E3BF535D94} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{2A6E7DF9-E417-42F8-94F7-0060E252E4D6} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{19594A96-A033-4820-820B-C6186D00D507} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
{64598086-231E-40F9-8163-1042A0533697} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
{91CED599-45B1-474D-A7F2-231BF857A5F2} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{C61CC748-39BD-4900-9FEE-A2483259573D} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{2D93F509-1FB3-4E22-92F0-588D0EFBA921} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using OrchardCore.DataLocalization.Services;
using OrchardCore.Deployment;

namespace OrchardCore.DataLocalization.Deployment;

public class AllDataTranslationsDeploymentSource : IDeploymentSource
{
private readonly TranslationsManager _translationsManager;

public AllDataTranslationsDeploymentSource(TranslationsManager translationsManager)
{
_translationsManager = translationsManager ?? throw new ArgumentNullException(nameof(translationsManager));
}

public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result)
{
var allDataTranslationsState = step as AllDataTranslationsDeploymentStep;

if (allDataTranslationsState == null)
{
return;
}

var translationObjects = new JsonArray();
var translationsDocument = await _translationsManager.GetTranslationsDocumentAsync();

foreach (var translation in translationsDocument.Translations)
{
translationObjects[translation.Key] = JObject.FromObject(translation.Value);
}

result.Steps.Add(new JsonObject
{
["name"] = "DynamicDataTranslations",
["DynamicDataTranslations"] = translationObjects,
});

await Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using OrchardCore.Deployment;

namespace OrchardCore.DataLocalization.Deployment;

public class AllDataTranslationsDeploymentStep : DeploymentStep
{
public AllDataTranslationsDeploymentStep()
{
Name = "AllDataTranslations";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using OrchardCore.Deployment;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;

namespace OrchardCore.DataLocalization.Deployment
{
public class AllDataTranslationsDeploymentStepDriver : DisplayDriver<DeploymentStep, AllDataTranslationsDeploymentStep>
{
public override IDisplayResult Display(AllDataTranslationsDeploymentStep step)
{
return
Combine(
View("AllDataTranslationsDeploymentStep_Summary", step).Location("Summary", "Content"),
View("AllDataTranslationsDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content")
);
}

public override IDisplayResult Edit(AllDataTranslationsDeploymentStep step)
{
return View("AllDataTranslationsDeploymentStep_Edit", step).Location("Content");
}
}
}
11 changes: 11 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.DataLocalization/Manifest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using OrchardCore.Modules.Manifest;

[assembly: Module(
Name = "Data Localization",
Author = ManifestConstants.OrchardCoreTeam,
Website = ManifestConstants.OrchardCoreWebsite,
Version = ManifestConstants.OrchardCoreVersion,
Description = "Provides support for data localization.",
Category = "Internationalization",
Dependencies = new[] { "OrchardCore.Localization" }
)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace OrchardCore.DataLocalization.Models;

public class Translation
{
public string Context { get; set; }

public string Key { get; set; }

public string Value { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using OrchardCore.Data.Documents;

namespace OrchardCore.DataLocalization.Models;

public class TranslationsDocument : Document
{
public TranslationsDocument()
{
Translations = new Dictionary<string, IEnumerable<Translation>>(StringComparer.OrdinalIgnoreCase);
}

public Dictionary<string, IEnumerable<Translation>> Translations { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<!-- NuGet properties-->
<Title>OrchardCore Data Localization</Title>
<Description>$(OCFrameworkDescription)

Provides the infrastructure necessary to support localized data string.
</Description>
<PackageTags>$(PackageTags) OrchardCoreFramework Localization</PackageTags>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Apis.GraphQL.Abstractions\OrchardCore.Apis.GraphQL.Abstractions.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Deployment.Abstractions\OrchardCore.Deployment.Abstractions.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.DisplayManagement\OrchardCore.DisplayManagement.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Localization.Abstractions\OrchardCore.Localization.Abstractions.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Localization.Core\OrchardCore.Localization.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Module.Targets\OrchardCore.Module.Targets.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Navigation.Core\OrchardCore.Navigation.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.ResourceManagement\OrchardCore.ResourceManagement.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Settings.Core\OrchardCore.Settings.Core.csproj" />
<ProjectReference Include="..\OrchardCore.ContentTypes\OrchardCore.ContentTypes.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using OrchardCore.Security.Permissions;

namespace OrchardCore.DataLocalization;

/// <summary>
/// Represents the localization module permissions.
/// </summary>
public class Permissions : IPermissionProvider
{
/// <summary>
/// Gets a permission for managing the cultures.
/// </summary>
public static readonly Permission ManageLocalization = new("ManageLocalization", "Manage dynamic localizations");

private readonly IEnumerable<Permission> _allPermissions =
[
ManageLocalization
];

public Task<IEnumerable<Permission>> GetPermissionsAsync()
=> Task.FromResult(_allPermissions);

public IEnumerable<PermissionStereotype> GetDefaultStereotypes() =>
[
new PermissionStereotype
{
Name = "Administrator",
Permissions = _allPermissions,
},
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using OrchardCore.DataLocalization.Models;
using OrchardCore.DataLocalization.Services;
using OrchardCore.Recipes.Models;
using OrchardCore.Recipes.Services;

namespace OrchardCore.DataLocalization.Recipes;

public class TranslationsStep : IRecipeStepHandler
{
private readonly TranslationsManager _translationsManager;

public TranslationsStep(TranslationsManager translationsManager)
{
_translationsManager = translationsManager;
}

public async Task ExecuteAsync(RecipeExecutionContext context)
{
if (!string.Equals(context.Name, "DynamicDataTranslations", StringComparison.OrdinalIgnoreCase))
{
return;
}

var model = context.Step.ToObject<TranslationsStepModel>();

foreach (var importedTranslation in model.Translations)
{
if (string.IsNullOrWhiteSpace(importedTranslation.Name))
{
continue;
}

await _translationsManager.UpdateTranslationAsync(importedTranslation.Name, new[] { importedTranslation.Translation });
}
}

public class TranslationsStepModel
{
public TranslationsStepRoleModel[] Translations { get; set; }
}
}

public class TranslationsStepRoleModel
{
public string Name { get; set; }

public Translation Translation { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OrchardCore.ContentTypes.Services;
using OrchardCore.Localization.Data;

namespace OrchardCore.DataLocalization.Services;

public class ContentFieldDataLocalizationProvider : ILocalizationDataProvider
{
private readonly IContentDefinitionService _contentDefinitionService;

private static readonly string _contentFieldsContext = "Content Fields";

public ContentFieldDataLocalizationProvider(IContentDefinitionService contentDefinitionService)
{
_contentDefinitionService = contentDefinitionService;
}

// TODO: Check if there's a better way to get the fields
public async Task<IEnumerable<DataLocalizedString>> GetDescriptorsAsync()
=> (await _contentDefinitionService.GetTypesAsync())
.SelectMany(t => t.TypeDefinition.Parts)
.SelectMany(p => p.PartDefinition.Fields.Select(f => new DataLocalizedString(_contentFieldsContext, f.Name)));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OrchardCore.ContentTypes.Services;
using OrchardCore.Localization.Data;

namespace OrchardCore.DataLocalization.Services;

public class ContentTypeDataLocalizationProvider : ILocalizationDataProvider
{
private readonly IContentDefinitionService _contentDefinitionService;

private static readonly string _contentTypesContext = "Content Types";

public ContentTypeDataLocalizationProvider(IContentDefinitionService contentDefinitionService)
{
_contentDefinitionService = contentDefinitionService;
}

public async Task<IEnumerable<DataLocalizedString>> GetDescriptorsAsync()
=> (await _contentDefinitionService.GetTypesAsync())
.Select(t => new DataLocalizedString(_contentTypesContext, t.DisplayName));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Localization.Data;
using OrchardCore.Localization;
using System.Linq;

namespace OrchardCore.DataLocalization.Services;

public class DataTranslationProvider : IDataTranslationProvider
{
private readonly IServiceScopeFactory _scopeFactory;

public DataTranslationProvider(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}

/// <inheritdoc/>
public void LoadTranslations(string cultureName, CultureDictionary dictionary)
{
using (var scope = _scopeFactory.CreateScope())
{
var translationsManager = scope.ServiceProvider.GetService<TranslationsManager>();

var translationsDocument = translationsManager.GetTranslationsDocumentAsync().Result;

if (translationsDocument.Translations.ContainsKey(cultureName))
{
var records = translationsDocument.Translations[cultureName]
.Select(t => new CultureDictionaryRecord(t.Key, t.Context, new[] { t.Value }));

dictionary.MergeTranslations(records);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using OrchardCore.DataLocalization.Models;

namespace OrchardCore.DataLocalization.Services;

public interface ITranslationsManager
{
Task<TranslationsDocument> LoadTranslationsDocumentAsync();

Task<TranslationsDocument> GetTranslationsDocumentAsync();

Task RemoveTranslationAsync(string name);

Task UpdateTranslationAsync(string name, IEnumerable<Translation> translations);
}
Loading
Loading