diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs
new file mode 100644
index 0000000000..18a3fc8214
--- /dev/null
+++ b/src/Components/WebAssembly/Build/src/Tasks/BlazorReadSatelliteAssemblyFile.cs
@@ -0,0 +1,38 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq;
+using System.Xml.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Components.WebAssembly.Build
+{
+ public class BlazorReadSatelliteAssemblyFile : Task
+ {
+ [Output]
+ public ITaskItem[] SatelliteAssembly { get; set; }
+
+ [Required]
+ public ITaskItem ReadFile { get; set; }
+
+ public override bool Execute()
+ {
+ var document = XDocument.Load(ReadFile.ItemSpec);
+ SatelliteAssembly = document.Root
+ .Elements()
+ .Select(e =>
+ {
+ //
+
+ var taskItem = new TaskItem(e.Attribute("Name").Value);
+ taskItem.SetMetadata("Culture", e.Attribute("Culture").Value);
+ taskItem.SetMetadata("DestinationSubDirectory", e.Attribute("DestinationSubDirectory").Value);
+
+ return taskItem;
+ }).ToArray();
+
+ return true;
+ }
+ }
+}
diff --git a/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs b/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs
new file mode 100644
index 0000000000..e1e6a8150e
--- /dev/null
+++ b/src/Components/WebAssembly/Build/src/Tasks/BlazorWriteSatelliteAssemblyFile.cs
@@ -0,0 +1,53 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO;
+using System.Xml;
+using System.Xml.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Components.WebAssembly.Build
+{
+ public class BlazorWriteSatelliteAssemblyFile : Task
+ {
+ [Required]
+ public ITaskItem[] SatelliteAssembly { get; set; }
+
+ [Required]
+ public ITaskItem WriteFile { get; set; }
+
+ public override bool Execute()
+ {
+ using var fileStream = File.Create(WriteFile.ItemSpec);
+ WriteSatelliteAssemblyFile(fileStream);
+ return true;
+ }
+
+ internal void WriteSatelliteAssemblyFile(Stream stream)
+ {
+ var root = new XElement("SatelliteAssembly");
+
+ foreach (var item in SatelliteAssembly)
+ {
+ //
+
+ root.Add(new XElement("Assembly",
+ new XAttribute("Name", item.ItemSpec),
+ new XAttribute("Culture", item.GetMetadata("Culture")),
+ new XAttribute("DestinationSubDirectory", item.GetMetadata("DestinationSubDirectory"))));
+ }
+
+ var xmlWriterSettings = new XmlWriterSettings
+ {
+ Indent = true,
+ OmitXmlDeclaration = true
+ };
+
+ using var writer = XmlWriter.Create(stream, xmlWriterSettings);
+ var xDocument = new XDocument(root);
+
+ xDocument.Save(writer);
+ }
+ }
+}
diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets
index 8cb135f996..7d6eb810b4 100644
--- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets
+++ b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets
@@ -75,6 +75,9 @@
+
+
+
+
+
+ <_BlazorSatelliteAssemblyStashFile>$(_BlazorIntermediateOutputPath)blazor.satelliteasm.props
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorOutputWithTargetPath Include="@(_BlazorReadSatelliteAssembly)">
+ satellite
+ %(_BlazorReadSatelliteAssembly.DestinationSubDirectory)%(FileName)%(Extension)
+ $(_BlazorRuntimeBinOutputPath)%(_BlazorReadSatelliteAssembly.DestinationSubDirectory)%(FileName)%(Extension)
+
", updatedContent);
+ File.WriteAllText(standaloneProjFile, updated);
+
+ var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build", $"/restore");
+
+ Assert.BuildPassed(result);
+
+ result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:BuildProjectReferences=false");
+
+ var publishDirectory = project.PublishOutputDirectory;
+ // Make sure the main project exists
+ Assert.FileExists(result, publishDirectory, "blazorhosted.dll");
+
+ var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "ja", "standalone.resources.dll");
+ Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "fr", "Microsoft.CodeAnalysis.resources.dll");
+
+ var bootJson = Path.Combine(project.DirectoryPath, blazorPublishDirectory, "_framework", "blazor.boot.json");
+ var bootJsonFile = JsonSerializer.Deserialize(File.ReadAllText(bootJson), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ var satelliteResources = bootJsonFile.resources.satelliteResources;
+
+ Assert.Contains("es-ES", satelliteResources.Keys);
+ Assert.Contains("es-ES/classlibrarywithsatelliteassemblies.resources.dll", satelliteResources["es-ES"].Keys);
+ Assert.Contains("fr", satelliteResources.Keys);
+ Assert.Contains("fr/Microsoft.CodeAnalysis.CSharp.resources.dll", satelliteResources["fr"].Keys);
+ Assert.Contains("ja", satelliteResources.Keys);
+ Assert.Contains("ja/standalone.resources.dll", satelliteResources["ja"].Keys);
+
+ VerifyServiceWorkerFiles(result, blazorPublishDirectory,
+ serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"),
+ serviceWorkerContent: "// This is the production service worker",
+ assetsManifestPath: "custom-service-worker-assets.js");
+ }
+
private static void AddSiblingProjectFileContent(ProjectDirectory project, string content)
{
var path = Path.Combine(project.SolutionPath, "standalone", "standalone.csproj");
@@ -543,5 +645,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
var json = jsContents.Substring(jsonStart, jsonLength);
return JsonSerializer.Deserialize(json);
}
+
+ private static GenerateBlazorBootJson.BootJsonData ReadBootJsonData(MSBuildResult result, string path)
+ {
+ return JsonSerializer.Deserialize(
+ File.ReadAllText(Path.Combine(result.Project.DirectoryPath, path)),
+ new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ }
}
}