diff --git a/Build/Installer.targets b/Build/Installer.targets
index 10d5af8527..f3bccf9eb5 100644
--- a/Build/Installer.targets
+++ b/Build/Installer.targets
@@ -210,6 +210,7 @@
+
diff --git a/Build/Localize.targets b/Build/Localize.targets
index 2906a83fef..048427c24e 100644
--- a/Build/Localize.targets
+++ b/Build/Localize.targets
@@ -1,6 +1,7 @@
+
@@ -13,7 +14,7 @@
-
+
@@ -34,6 +35,9 @@
+
+
+
diff --git a/Build/Src/FwBuildTasks/FwBuildTasks.csproj b/Build/Src/FwBuildTasks/FwBuildTasks.csproj
index 37cc8bf9bf..4bca0f850c 100644
--- a/Build/Src/FwBuildTasks/FwBuildTasks.csproj
+++ b/Build/Src/FwBuildTasks/FwBuildTasks.csproj
@@ -3,13 +3,13 @@
SIL.FieldWorks.Build.Tasks
Additional msbuild tasks for FieldWorks
FwBuildTasks
- net461
+ net462
../..
false
-
+
diff --git a/Build/Src/FwBuildTasks/FwBuildTasksTests/NormalizeLocalesTests.cs b/Build/Src/FwBuildTasks/FwBuildTasksTests/NormalizeLocalesTests.cs
index faaa84937d..db2e0ed77b 100644
--- a/Build/Src/FwBuildTasks/FwBuildTasksTests/NormalizeLocalesTests.cs
+++ b/Build/Src/FwBuildTasks/FwBuildTasksTests/NormalizeLocalesTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2020 SIL International
+// Copyright (c) 2020 SIL International
// This software is licensed under the LGPL, version 2.1 or later
// (http://www.gnu.org/licenses/lgpl-2.1.html)
@@ -60,6 +60,19 @@ public void Works()
VerifyLocale("zh-CN", "zh");
}
+ [Test]
+ public void CopyMalay()
+ {
+ FileSystemSetup(new[] { "ms" });
+
+ VerifyLocale("ms", "zlm");
+
+ _task.Execute();
+
+ VerifyLocale("ms", "zzz");
+ VerifyLocale("zlm", "zzz");
+ }
+
private void FileSystemSetup(string[] locales)
{
foreach (var locale in locales)
diff --git a/Build/Src/FwBuildTasks/Localization/CopyLocale.cs b/Build/Src/FwBuildTasks/Localization/CopyLocale.cs
new file mode 100644
index 0000000000..5660a1d70b
--- /dev/null
+++ b/Build/Src/FwBuildTasks/Localization/CopyLocale.cs
@@ -0,0 +1,105 @@
+// Copyright (c) 2024 SIL International
+// This software is licensed under the LGPL, version 2.1 or later
+// (http://www.gnu.org/licenses/lgpl-2.1.html)
+
+using System;
+using System.IO;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+
+namespace SIL.FieldWorks.Build.Tasks.Localization
+{
+ public class CopyLocale : Task
+ {
+ [Required]
+ public string SourceL10n { get; set; }
+
+ [Required]
+ public string DestL10n { get; set; }
+
+ [Required]
+ public string LcmDir { get; set; }
+
+ public override bool Execute()
+ {
+ var srcLangCode = Path.GetFileName(SourceL10n);
+ var destLangCode = Path.GetFileName(DestL10n);
+ if (!Directory.Exists(SourceL10n))
+ {
+ Log.LogError($"Source directory '{SourceL10n}' does not exist.");
+ return false;
+ }
+ if (Directory.Exists(DestL10n))
+ {
+ Log.LogError($"Destination directory '{DestL10n}' already exists.");
+ return false;
+ }
+ // Create the destination directory
+ Directory.CreateDirectory(DestL10n);
+
+ // Get the files in the source directory and copy to the destination directory
+ CopyDirectory(SourceL10n, DestL10n, true);
+
+ NormalizeLocales.RenameLocaleFiles(DestL10n, srcLangCode, destLangCode);
+ // Get the files in the source directory and copy to the destination directory
+ foreach (var file in Directory.GetFiles(LcmDir, "*.resx", SearchOption.AllDirectories))
+ {
+ var relativePath = GetRelativePath(LcmDir, file);
+ Log.LogMessage(MessageImportance.Normal, "CopyLocale: relpath - " + relativePath);
+ var newFileName = Path.GetFileNameWithoutExtension(file) + $".{destLangCode}.resx";
+ var newFilePath = Path.Combine(DestL10n, Path.Combine("Src", Path.GetDirectoryName(relativePath)));
+
+ // Create the directory for the new file if it doesn't exist
+ Directory.CreateDirectory(newFilePath);
+
+ Log.LogMessage(MessageImportance.Normal, $"CopyLocale: {newFilePath}, {newFileName}");
+ // Copy the file to the new location
+ File.Move(file, Path.Combine(newFilePath, newFileName));
+ }
+
+ return true;
+ }
+
+ static void CopyDirectory(string sourceDir, string destinationDir, bool recursive)
+ {
+ // From: https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories
+ // Get information about the source directory
+ var dir = new DirectoryInfo(sourceDir);
+
+ // Check if the source directory exists
+ if (!dir.Exists)
+ throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
+
+ // Cache directories before we start copying
+ DirectoryInfo[] dirs = dir.GetDirectories();
+
+ // Create the destination directory
+ Directory.CreateDirectory(destinationDir);
+
+ // Get the files in the source directory and copy to the destination directory
+ foreach (FileInfo file in dir.GetFiles())
+ {
+ string targetFilePath = Path.Combine(destinationDir, file.Name);
+ file.CopyTo(targetFilePath);
+ }
+
+ // If recursive and copying subdirectories, recursively call this method
+ if (recursive)
+ {
+ foreach (DirectoryInfo subDir in dirs)
+ {
+ string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
+ CopyDirectory(subDir.FullName, newDestinationDir, true);
+ }
+ }
+ }
+
+ static string GetRelativePath(string baseDir, string filePath)
+ {
+ Uri baseUri = new Uri(baseDir);
+ Uri fileUri = new Uri(filePath);
+ return Uri.UnescapeDataString(baseUri.MakeRelativeUri(fileUri).ToString().Replace('/', Path.DirectorySeparatorChar));
+ }
+ }
+}
diff --git a/Build/Src/FwBuildTasks/Localization/NormalizeLocales.cs b/Build/Src/FwBuildTasks/Localization/NormalizeLocales.cs
index d8cbdb2364..5ab43f29da 100644
--- a/Build/Src/FwBuildTasks/Localization/NormalizeLocales.cs
+++ b/Build/Src/FwBuildTasks/Localization/NormalizeLocales.cs
@@ -24,7 +24,8 @@ public override bool Execute()
var locales = Directory.GetDirectories(L10nsDirectory).Select(Path.GetFileName);
foreach (var locale in locales)
{
- RenameLocale(locale, Normalize(locale));
+ var normalizedLocale = Normalize(locale);
+ RenameLocale(locale, normalizedLocale);
}
return true;
}
@@ -49,8 +50,12 @@ private void RenameLocale(string source, string dest)
var sourceDir = Path.Combine(L10nsDirectory, source);
var destDir = Path.Combine(L10nsDirectory, dest);
Directory.Move(sourceDir, destDir);
+ RenameLocaleFiles(destDir, source, dest);
+ }
- foreach (var file in Directory.EnumerateFiles(destDir, "*", SearchOption.AllDirectories))
+ internal static void RenameLocaleFiles(string destDirName, string source, string dest, string extension = "*")
+ {
+ foreach (var file in Directory.EnumerateFiles(destDirName, extension, SearchOption.AllDirectories))
{
var nameNoExt = Path.GetFileNameWithoutExtension(file);
// ReSharper disable once PossibleNullReferenceException - no files are null
diff --git a/FLExInstaller/CustomComponents.wxi b/FLExInstaller/CustomComponents.wxi
index 6d378309d6..f9e630d2cc 100644
--- a/FLExInstaller/CustomComponents.wxi
+++ b/FLExInstaller/CustomComponents.wxi
@@ -169,6 +169,7 @@
+
@@ -225,6 +226,7 @@
+
@@ -254,6 +256,7 @@
+
@@ -284,5 +287,6 @@
+
\ No newline at end of file