diff --git a/Directory.Packages.props b/Directory.Packages.props
index dbc519b8ba..ca5d2f1f68 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,6 +3,7 @@
+
diff --git a/src/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj b/src/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj
index a3331e1bde..961aa30ba1 100755
--- a/src/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj
+++ b/src/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj
@@ -10,9 +10,11 @@
true
+
+
diff --git a/src/BizHawk.Client.DiscoHawk/Program.cs b/src/BizHawk.Client.DiscoHawk/Program.cs
index f85d66f0a7..a1892e4c0b 100644
--- a/src/BizHawk.Client.DiscoHawk/Program.cs
+++ b/src/BizHawk.Client.DiscoHawk/Program.cs
@@ -114,6 +114,8 @@ namespace BizHawk.Client.DiscoHawk
}
}
+ Sample.RunAll(); // testing Fluent.Net
+
// Do something for visuals, I guess
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
diff --git a/src/BizHawk.Client.DiscoHawk/Sample.cs b/src/BizHawk.Client.DiscoHawk/Sample.cs
new file mode 100644
index 0000000000..219d07be4e
--- /dev/null
+++ b/src/BizHawk.Client.DiscoHawk/Sample.cs
@@ -0,0 +1,101 @@
+#nullable enable
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+
+using Fluent.Net;
+
+namespace BizHawk.Client.DiscoHawk
+{
+ public sealed class ArgsDict : Dictionary
+ {
+ public ArgsDict(int? tabCount = null)
+ {
+ Add(nameof(tabCount), tabCount);
+ }
+ }
+
+ public readonly struct MultiMessageContext
+ {
+ private static IReadOnlyList ReadEmbeddedAndConcat(string lang, MessageContext[] overlays)
+ {
+ MessageContext mc = new(lang, new() { UseIsolating = false });
+ Stream embeddedStream;
+ try
+ {
+ embeddedStream = ReflectionCache.EmbeddedResourceStream($"locale.{lang}.ftl");
+ }
+ catch (ArgumentException e)
+ {
+ Console.WriteLine(e);
+ return overlays;
+ }
+ using StreamReader sr = new(embeddedStream);
+ var errors = mc.AddMessages(sr);
+ foreach (var error in errors) Console.WriteLine(error);
+ return [ ..overlays, mc ];
+ }
+
+ private readonly IReadOnlyList _contexts;
+
+ public readonly CultureInfo Culture;
+
+ public MultiMessageContext(IReadOnlyList contexts)
+ {
+ _contexts = contexts;
+ Culture = new(_contexts.FirstOrDefault()?.Locales?.First());
+ }
+
+ public MultiMessageContext(string lang, params MessageContext[] overlays)
+ : this(ReadEmbeddedAndConcat(lang, overlays)) {}
+
+ public string? this[string id]
+ => GetString(id);
+
+ public string? GetString(
+ string id,
+ IDictionary? args = null,
+ ICollection? errors = null)
+ {
+ foreach (var context in _contexts)
+ {
+ var msg = context.GetMessage(id);
+ if (msg is not null) return context.Format(msg, args, errors);
+ }
+ return null;
+ }
+ }
+
+ public static class Sample
+ {
+ public static void RunAll()
+ {
+ static void RunTest(string lang)
+ {
+ MultiMessageContext translator = new(lang);
+ Console.WriteLine($"\n{lang}:");
+ Console.WriteLine($"tabs-close-button = {translator.GetString("tabs-close-button")}");
+ Console.WriteLine(
+ "tabs-close-tooltip ($tabCount = 1) = "
+ + translator.GetString("tabs-close-tooltip", new ArgsDict(tabCount: 1)));
+ Console.WriteLine(
+ "tabs-close-tooltip ($tabCount = 2) = "
+ + translator.GetString("tabs-close-tooltip", new ArgsDict(tabCount: 2)));
+ Console.WriteLine(
+ "tabs-close-warning ($tabCount = 1) = "
+ + translator.GetString("tabs-close-warning", new ArgsDict(tabCount: 1)));
+ Console.WriteLine(
+ "tabs-close-warning ($tabCount = 2) = "
+ + translator.GetString("tabs-close-warning", new ArgsDict(tabCount: 2)));
+ Console.WriteLine($"sync-dialog-title = {translator.GetString("sync-dialog-title")}");
+ Console.WriteLine($"sync-headline-title = {translator.GetString("sync-headline-title")}");
+ Console.WriteLine($"sync-signedout-title = {translator.GetString("sync-signedout-title")}");
+ }
+ RunTest("en");
+ RunTest("it");
+ RunTest("pl");
+ }
+ }
+}
diff --git a/src/BizHawk.Client.DiscoHawk/locale/en.ftl b/src/BizHawk.Client.DiscoHawk/locale/en.ftl
new file mode 100644
index 0000000000..f1600fe936
--- /dev/null
+++ b/src/BizHawk.Client.DiscoHawk/locale/en.ftl
@@ -0,0 +1,24 @@
+## Closing tabs
+
+tabs-close-button = Close
+tabs-close-tooltip = {$tabCount ->
+ [one] Close {$tabCount} tab
+ *[other] Close {$tabCount} tabs
+}
+tabs-close-warning =
+ You are about to close {$tabCount} tabs.
+ Are you sure you want to continue?
+
+## Syncing
+
+-sync-brand-name = Firefox Account
+
+sync-dialog-title = {-sync-brand-name}
+sync-headline-title =
+ {-sync-brand-name}: The best way to bring
+ your data always with you
+sync-signedout-title =
+ Connect with your {-sync-brand-name}
+
+## Datetime
+date-is = The date is 'DATETIME($dt, weekday: "short", month: "short", year: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric")'.
diff --git a/src/BizHawk.Client.DiscoHawk/locale/it.ftl b/src/BizHawk.Client.DiscoHawk/locale/it.ftl
new file mode 100644
index 0000000000..9281d708fd
--- /dev/null
+++ b/src/BizHawk.Client.DiscoHawk/locale/it.ftl
@@ -0,0 +1,23 @@
+## Closing tabs
+
+tabs-close-button = Chiudi
+tabs-close-tooltip = {$tabCount ->
+ [one] Chiudi {$tabCount} scheda
+ *[other] Chiudi {$tabCount} schede
+}
+tabs-close-warning =
+ Verranno chiuse {$tabCount} schede. Proseguire?
+
+## Syncing
+
+-sync-brand-name = {$first ->
+ *[uppercase] Account Firefox
+ [lowercase] account Firefox
+}
+
+sync-dialog-title = {-sync-brand-name}
+sync-headline-title =
+ {-sync-brand-name}: il modo migliore
+ per avere i tuoi dati sempre con te
+sync-signedout-title =
+ Connetti il tuo {-sync-brand-name[lowercase]}
diff --git a/src/BizHawk.Client.DiscoHawk/locale/pl.ftl b/src/BizHawk.Client.DiscoHawk/locale/pl.ftl
new file mode 100644
index 0000000000..9048f6b08a
--- /dev/null
+++ b/src/BizHawk.Client.DiscoHawk/locale/pl.ftl
@@ -0,0 +1,29 @@
+## Closing tabs
+
+tabs-close-button = Zamknij
+tabs-close-tooltip = {$tabCount ->
+ [one] Zamknij kartę
+ [few] Zamknij {$tabCount} karty
+ *[many] Zamknij { $tabCount } kart
+}
+tabs-close-warning = {$tabCount ->
+ [few] Zostaną zamknięte {$tabCount} karty.
+ Czy chcesz kontynuować?
+ *[many] Zostanie zamkniętych {$tabCount} kart.
+ Czy chcesz kontynuować?
+}
+
+## Syncing
+
+-sync-brand-name = {$case ->
+ *[nominative] Konto Firefox
+ [genitive] Konta Firefox
+ [accusative] Kontem Firefox
+}
+
+sync-dialog-title = {-sync-brand-name}
+sync-headline-title =
+ {-sync-brand-name}: Najlepszy sposób na to,
+ aby mieć swoje dane zawsze przy sobie
+sync-signedout-title =
+ Zaloguj do {-sync-brand-name[genitive]}