diff --git a/HellionChat/Export/MessageExporter.cs b/HellionChat/Export/MessageExporter.cs index c6ddb47..5509459 100644 --- a/HellionChat/Export/MessageExporter.cs +++ b/HellionChat/Export/MessageExporter.cs @@ -49,6 +49,8 @@ internal static class ExportFormatExt // substring is applied here. internal static class MessageExporter { + private static readonly UTF8Encoding Utf8NoBom = new(encoderShouldEmitUTF8Identifier: false); + internal record FilterDescription( IReadOnlyCollection? ChatTypes, DateTimeOffset? From, @@ -82,7 +84,14 @@ internal static class MessageExporter int written; try { - using (var writer = new StreamWriter(temp, append: false, encoding: Encoding.UTF8)) + // Encoding.UTF8 writes a byte order mark, and that is not a + // cosmetic detail here: a leading U+FEFF makes the JSON invalid for + // every strict parser, Python's json.load included. CSV is the one + // format that wants it -- without a BOM Excel guesses the codepage + // and mangles every non-ASCII name in the file. + var encoding = format == ExportFormat.Csv ? Encoding.UTF8 : Utf8NoBom; + + using (var writer = new StreamWriter(temp, append: false, encoding)) { written = format switch { diff --git a/HellionChat/Resources/HellionStrings.Designer.cs b/HellionChat/Resources/HellionStrings.Designer.cs index 8f70b61..d1682c0 100644 --- a/HellionChat/Resources/HellionStrings.Designer.cs +++ b/HellionChat/Resources/HellionStrings.Designer.cs @@ -380,6 +380,14 @@ internal class HellionStrings internal static string Settings_Fonts_Global => Get(nameof(Settings_Fonts_Global)); internal static string Settings_Fonts_Active => Get(nameof(Settings_Fonts_Active)); internal static string Settings_Preview_TypeAMessage => Get(nameof(Settings_Preview_TypeAMessage)); + internal static string StatusBar_Tabs_One => Get(nameof(StatusBar_Tabs_One)); + internal static string StatusBar_Tabs_Other => Get(nameof(StatusBar_Tabs_Other)); + internal static string StatusBar_Tells_One => Get(nameof(StatusBar_Tells_One)); + internal static string StatusBar_Tells_Other => Get(nameof(StatusBar_Tells_Other)); + internal static string StatusBar_Messages => Get(nameof(StatusBar_Messages)); + internal static string StatusBar_MessagesThousands => Get(nameof(StatusBar_MessagesThousands)); + internal static string Settings_Preview_TitleMock => Get(nameof(Settings_Preview_TitleMock)); + internal static string Settings_Preview_StatusOpen => Get(nameof(Settings_Preview_StatusOpen)); internal static string Settings_Section_Links => Get(nameof(Settings_Section_Links)); internal static string Settings_Section_Behaviour => Get(nameof(Settings_Section_Behaviour)); internal static string Settings_Section_Keybinds => Get(nameof(Settings_Section_Keybinds)); diff --git a/HellionChat/Resources/HellionStrings.ca.resx b/HellionChat/Resources/HellionStrings.ca.resx index 52114ac..d2d0148 100644 --- a/HellionChat/Resources/HellionStrings.ca.resx +++ b/HellionChat/Resources/HellionStrings.ca.resx @@ -704,7 +704,7 @@ Conserva - Privacy-First + Privadesa primer Obert @@ -1334,4 +1334,28 @@ manteniment + + {0} pestanya + + + {0} pestanyes + + + {0} xiuxiueig + + + {0} xiuxiueigs + + + {0} msg + + + {0:0.0}k msg + + + «Campió» Vista prèvia + + + obert + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.cs.resx b/HellionChat/Resources/HellionStrings.cs.resx index 73e8050..ea554e1 100644 --- a/HellionChat/Resources/HellionStrings.cs.resx +++ b/HellionChat/Resources/HellionStrings.cs.resx @@ -704,7 +704,7 @@ Ponechat - Privacy-First + Soukromí především Otevřeno @@ -1333,4 +1333,28 @@ údržba + + {0} karta + + + {0} karty + + + {0} šeptání + + + {0} šeptání + + + {0} zpr. + + + {0:0.0}k zpr. + + + «Šampion» Náhled + + + otevřeno + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.da.resx b/HellionChat/Resources/HellionStrings.da.resx index 521f41c..29802a1 100644 --- a/HellionChat/Resources/HellionStrings.da.resx +++ b/HellionChat/Resources/HellionStrings.da.resx @@ -704,7 +704,7 @@ Behold - Privacy-First + Privatliv først Åben @@ -1333,4 +1333,28 @@ vedligeholdelse + + {0} tab + + + {0} tabs + + + {0} tell + + + {0} tells + + + {0} besk. + + + {0:0.0}k besk. + + + «Champion» Forhåndsvisning + + + åben + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.de.resx b/HellionChat/Resources/HellionStrings.de.resx index b030897..de48bc0 100644 --- a/HellionChat/Resources/HellionStrings.de.resx +++ b/HellionChat/Resources/HellionStrings.de.resx @@ -704,7 +704,7 @@ Behalten - Privacy-First + Datenschutz zuerst Offen @@ -1328,4 +1328,28 @@ Wartung + + {0} Tab + + + {0} Tabs + + + {0} Flüstern + + + {0} Flüstern + + + {0} Nachr. + + + {0:0.0}k Nachr. + + + «Champion» Vorschau + + + offen + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.el.resx b/HellionChat/Resources/HellionStrings.el.resx index 5ac707e..abfe8ef 100644 --- a/HellionChat/Resources/HellionStrings.el.resx +++ b/HellionChat/Resources/HellionStrings.el.resx @@ -704,7 +704,7 @@ Διατήρηση - Privacy-First + Απόρρητο πρώτα Ανοιχτό @@ -1333,4 +1333,28 @@ συντήρηση + + {0} καρτέλα + + + {0} καρτέλες + + + {0} tell + + + {0} tells + + + {0} μην. + + + {0:0.0}k μην. + + + «Πρωταθλητής» Προεπισκόπηση + + + ανοιχτό + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.es.resx b/HellionChat/Resources/HellionStrings.es.resx index 46acd7d..dfb57c8 100644 --- a/HellionChat/Resources/HellionStrings.es.resx +++ b/HellionChat/Resources/HellionStrings.es.resx @@ -704,7 +704,7 @@ Mantener - Privacy-First + Privacidad primero Abierto @@ -1334,4 +1334,28 @@ mantenimiento + + {0} pestaña + + + {0} pestañas + + + {0} susurro + + + {0} susurros + + + {0} msj + + + {0:0.0}k msj + + + «Campeón» Vista previa + + + abierto + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.fi.resx b/HellionChat/Resources/HellionStrings.fi.resx index d80898d..2bdefc6 100644 --- a/HellionChat/Resources/HellionStrings.fi.resx +++ b/HellionChat/Resources/HellionStrings.fi.resx @@ -704,7 +704,7 @@ Säilytä - Privacy-First + Yksityisyys ensin Avoin @@ -1333,4 +1333,28 @@ ylläpito + + {0} välilehti + + + {0} välilehteä + + + {0} kuiskaus + + + {0} kuiskausta + + + {0} vie. + + + {0:0.0}k vie. + + + «Mestari» Esikatselu + + + auki + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.fr.resx b/HellionChat/Resources/HellionStrings.fr.resx index 2f17062..956f1e5 100644 --- a/HellionChat/Resources/HellionStrings.fr.resx +++ b/HellionChat/Resources/HellionStrings.fr.resx @@ -704,7 +704,7 @@ Conserver - Confidentialité + Confidentialité d'abord Ouvert @@ -1334,4 +1334,28 @@ maintenance + + {0} onglet + + + {0} onglets + + + {0} MP + + + {0} MP + + + {0} msg + + + {0:0.0}k msg + + + «Champion» Aperçu + + + ouvert + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.hu.resx b/HellionChat/Resources/HellionStrings.hu.resx index 43e6153..f47efee 100644 --- a/HellionChat/Resources/HellionStrings.hu.resx +++ b/HellionChat/Resources/HellionStrings.hu.resx @@ -704,7 +704,7 @@ Megtartás - Privacy-First + Adatvédelem elöl Nyitott @@ -1333,4 +1333,28 @@ karbantartás + + {0} lap + + + {0} lap + + + {0} suttogás + + + {0} suttogás + + + {0} üz. + + + {0:0.0}k üz. + + + «Bajnok» Előnézet + + + nyitva + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.it.resx b/HellionChat/Resources/HellionStrings.it.resx index 8880a1f..7a372ef 100644 --- a/HellionChat/Resources/HellionStrings.it.resx +++ b/HellionChat/Resources/HellionStrings.it.resx @@ -704,7 +704,7 @@ Mantieni - Privacy-First + Privacy prima Aperto @@ -1334,4 +1334,28 @@ manutenzione + + {0} scheda + + + {0} schede + + + {0} sussurro + + + {0} sussurri + + + {0} msg + + + {0:0.0}k msg + + + «Campione» Anteprima + + + aperto + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ja.resx b/HellionChat/Resources/HellionStrings.ja.resx index f1beb26..a84ed43 100644 --- a/HellionChat/Resources/HellionStrings.ja.resx +++ b/HellionChat/Resources/HellionStrings.ja.resx @@ -704,7 +704,7 @@ 保持 - Privacy-First + プライバシー優先 オープン @@ -1334,4 +1334,28 @@ メンテナンス + + {0} タブ + + + {0} タブ + + + {0} テル + + + {0} テル + + + {0} 件 + + + {0:0.0}k 件 + + + «Champion» プレビュー + + + 開いています + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ko.resx b/HellionChat/Resources/HellionStrings.ko.resx index 3fb0e96..d6e4303 100644 --- a/HellionChat/Resources/HellionStrings.ko.resx +++ b/HellionChat/Resources/HellionStrings.ko.resx @@ -704,7 +704,7 @@ 유지 - Privacy-First + 개인정보 우선 전체 공개 @@ -1334,4 +1334,28 @@ 유지 관리 + + {0} 탭 + + + {0} 탭 + + + {0} 귓속말 + + + {0} 귓속말 + + + {0} 개 + + + {0:0.0}k 개 + + + «Champion» 미리보기 + + + 열림 + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.nb.resx b/HellionChat/Resources/HellionStrings.nb.resx index e1654d5..d5f6a01 100644 --- a/HellionChat/Resources/HellionStrings.nb.resx +++ b/HellionChat/Resources/HellionStrings.nb.resx @@ -704,7 +704,7 @@ Behold - Privacy-First + Personvern først Åpen @@ -1333,4 +1333,28 @@ vedlikehold + + {0} fane + + + {0} faner + + + {0} tell + + + {0} tells + + + {0} meld. + + + {0:0.0}k meld. + + + «Champion» Forhåndsvisning + + + åpen + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.nl.resx b/HellionChat/Resources/HellionStrings.nl.resx index 59bc189..08ac71a 100644 --- a/HellionChat/Resources/HellionStrings.nl.resx +++ b/HellionChat/Resources/HellionStrings.nl.resx @@ -704,7 +704,7 @@ Bewaren - Privacy-First + Privacy voorop Open @@ -1334,4 +1334,28 @@ onderhoud + + {0} tabblad + + + {0} tabbladen + + + {0} tell + + + {0} tells + + + {0} ber. + + + {0:0.0}k ber. + + + «Kampioen» Voorbeeld + + + open + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.pl.resx b/HellionChat/Resources/HellionStrings.pl.resx index 7a74f13..d88313d 100644 --- a/HellionChat/Resources/HellionStrings.pl.resx +++ b/HellionChat/Resources/HellionStrings.pl.resx @@ -704,7 +704,7 @@ Zachowaj - Privacy-First + Prywatność przede wszystkim Otwarty @@ -1333,4 +1333,28 @@ konserwacja + + {0} zakładka + + + {0} zakładki + + + {0} szept + + + {0} szepty + + + {0} wiad. + + + {0:0.0}k wiad. + + + «Mistrz» Podgląd + + + otwarte + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.pt-BR.resx b/HellionChat/Resources/HellionStrings.pt-BR.resx index 89fd3dd..b37e345 100644 --- a/HellionChat/Resources/HellionStrings.pt-BR.resx +++ b/HellionChat/Resources/HellionStrings.pt-BR.resx @@ -704,7 +704,7 @@ Manter - Privacy-First + Privacidade primeiro Aberto @@ -1334,4 +1334,28 @@ manutenção + + {0} aba + + + {0} abas + + + {0} sussurro + + + {0} sussurros + + + {0} msg + + + {0:0.0}k msg + + + «Campeão» Pré-visualização + + + aberto + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.pt-PT.resx b/HellionChat/Resources/HellionStrings.pt-PT.resx index 8087376..1eb606a 100644 --- a/HellionChat/Resources/HellionStrings.pt-PT.resx +++ b/HellionChat/Resources/HellionStrings.pt-PT.resx @@ -704,7 +704,7 @@ Manter - Privacy-First + Privacidade primeiro Aberto @@ -1333,4 +1333,28 @@ manutenção + + {0} separador + + + {0} separadores + + + {0} sussurro + + + {0} sussurros + + + {0} msg + + + {0:0.0}k msg + + + «Campeão» Pré-visualização + + + aberto + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.resx b/HellionChat/Resources/HellionStrings.resx index 2f352d2..d6ebc8a 100644 --- a/HellionChat/Resources/HellionStrings.resx +++ b/HellionChat/Resources/HellionStrings.resx @@ -1345,4 +1345,28 @@ maintenance + + {0} tab + + + {0} tabs + + + {0} tell + + + {0} tells + + + {0} msg + + + {0:0.0}k msg + + + «Champion» Vorschau + + + open + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ro.resx b/HellionChat/Resources/HellionStrings.ro.resx index bc6d3eb..d7b6900 100644 --- a/HellionChat/Resources/HellionStrings.ro.resx +++ b/HellionChat/Resources/HellionStrings.ro.resx @@ -704,7 +704,7 @@ Păstrează - Privacy-First + Confidențialitate întâi Deschis @@ -1334,4 +1334,28 @@ întreținere + + {0} tab + + + {0} tab-uri + + + {0} tell + + + {0} tell-uri + + + {0} mes. + + + {0:0.0}k mes. + + + «Campion» Previzualizare + + + deschis + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.ru.resx b/HellionChat/Resources/HellionStrings.ru.resx index 891dae5..365e56d 100644 --- a/HellionChat/Resources/HellionStrings.ru.resx +++ b/HellionChat/Resources/HellionStrings.ru.resx @@ -704,7 +704,7 @@ Оставить - Privacy-First + Приватность прежде всего Открыто @@ -1334,4 +1334,28 @@ обслуживание + + {0} вкладка + + + {0} вкладок + + + {0} шёпот + + + {0} шёпотов + + + {0} сообщ. + + + {0:0.0}k сообщ. + + + «Чемпион» Предпросмотр + + + открыт + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.sv.resx b/HellionChat/Resources/HellionStrings.sv.resx index 2db63e2..276879b 100644 --- a/HellionChat/Resources/HellionStrings.sv.resx +++ b/HellionChat/Resources/HellionStrings.sv.resx @@ -704,7 +704,7 @@ Behåll - Privacy-First + Integritet först Öppen @@ -1334,4 +1334,28 @@ underhåll + + {0} flik + + + {0} flikar + + + {0} viskning + + + {0} viskningar + + + {0} medd. + + + {0:0.0}k medd. + + + «Mästare» Förhandsgranskning + + + öppen + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.tr.resx b/HellionChat/Resources/HellionStrings.tr.resx index eeeb1c6..3b310af 100644 --- a/HellionChat/Resources/HellionStrings.tr.resx +++ b/HellionChat/Resources/HellionStrings.tr.resx @@ -704,7 +704,7 @@ Koru - Privacy-First + Önce gizlilik Açık @@ -1333,4 +1333,28 @@ bakım + + {0} sekme + + + {0} sekme + + + {0} tell + + + {0} tell + + + {0} ileti + + + {0:0.0}k ileti + + + «Şampiyon» Önizleme + + + açık + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.uk.resx b/HellionChat/Resources/HellionStrings.uk.resx index 74744c5..23e35f6 100644 --- a/HellionChat/Resources/HellionStrings.uk.resx +++ b/HellionChat/Resources/HellionStrings.uk.resx @@ -704,7 +704,7 @@ Залишити - Privacy-First + Приватність передусім Відкрито @@ -1333,4 +1333,28 @@ обслуговування + + {0} вкладка + + + {0} вкладок + + + {0} шепіт + + + {0} шепотів + + + {0} повід. + + + {0:0.0}k повід. + + + «Чемпіон» Перегляд + + + відкрито + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.zh-Hans.resx b/HellionChat/Resources/HellionStrings.zh-Hans.resx index f5ab8e6..66299f2 100644 --- a/HellionChat/Resources/HellionStrings.zh-Hans.resx +++ b/HellionChat/Resources/HellionStrings.zh-Hans.resx @@ -704,7 +704,7 @@ 保留 - Privacy-First + 隐私优先 开放 @@ -1334,4 +1334,28 @@ 维护 + + {0} 标签 + + + {0} 标签 + + + {0} 密语 + + + {0} 密语 + + + {0} 条 + + + {0:0.0}k 条 + + + «冠军» 预览 + + + 打开 + \ No newline at end of file diff --git a/HellionChat/Resources/HellionStrings.zh-Hant.resx b/HellionChat/Resources/HellionStrings.zh-Hant.resx index 357b246..e000264 100644 --- a/HellionChat/Resources/HellionStrings.zh-Hant.resx +++ b/HellionChat/Resources/HellionStrings.zh-Hant.resx @@ -704,7 +704,7 @@ 保留 - Privacy-First + 隱私優先 開放 @@ -1334,4 +1334,28 @@ 維護 + + {0} 分頁 + + + {0} 分頁 + + + {0} 悄悄話 + + + {0} 悄悄話 + + + {0} 則 + + + {0:0.0}k 則 + + + «冠軍» 預覽 + + + 開啟 + \ No newline at end of file diff --git a/HellionChat/SelfTests/ExportRoundTripStep.cs b/HellionChat/SelfTests/ExportRoundTripStep.cs index be1ac17..7c9c0be 100644 --- a/HellionChat/SelfTests/ExportRoundTripStep.cs +++ b/HellionChat/SelfTests/ExportRoundTripStep.cs @@ -126,6 +126,13 @@ internal sealed class ExportRoundTripStep : ISelfTestStep if (File.Exists(path + ".part")) failures.Add("temporary file left behind after a successful export"); + // Bytes, not text. File.ReadAllText strips a byte order mark while + // detecting the encoding, so a BOM that breaks every strict JSON parser + // is invisible to the check below -- and it shipped exactly that way. + var head = File.ReadAllBytes(path); + if (head.Length >= 3 && head[0] == 0xEF && head[1] == 0xBB && head[2] == 0xBF) + failures.Add("export JSON starts with a byte order mark"); + // Parsed, not merely counted. The writer builds JSON by hand, and it // shipped a build where the chat relation kinds were interpolated as // enum names -- "source_kind":LocalPlayer -- which every parser diff --git a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs index acf9984..815db7f 100644 --- a/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs +++ b/HellionChat/Ui/Components/Settings/LivePreviewPanel.cs @@ -3,6 +3,7 @@ using System.Threading; using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Utility.Raii; +using HellionChat.Code; using HellionChat.Resources; using HellionChat.Themes; using HellionChat.Ui.StyleEngine; @@ -153,7 +154,7 @@ internal sealed class LivePreviewPanel : IDisposable // unchanged — but both paths now share one resolver. No truncation here: // the preview draws a fixed, centred "«Champion» Preview" string. var textAbgr = HonorificTitleColor.ResolveTitleAbgr(null, theme); - var title = "«Champion» Preview"; + var title = HellionStrings.Settings_Preview_TitleMock; var crownGlyph = FontAwesomeIcon.Crown.ToIconString(); // Crown is a FontAwesome glyph (matches the real header); measure + draw @@ -196,7 +197,14 @@ internal sealed class LivePreviewPanel : IDisposable // sat on different rows here, so the preview promised a layout the // sidebar never delivered -- and then the sidebar caught up. var borderAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Border); - ReadOnlySpan labels = ["Linkshell", "Tell", "FC"]; + // Real channel names, not literals: the preview claims to show what the + // sidebar will look like, and the sidebar is localised. + ReadOnlySpan labels = + [ + ChatType.Linkshell1.Name(), + ChatType.TellIncoming.Name(), + ChatType.FreeCompany.Name(), + ]; for (var i = 0; i < 3; i++) { var rowMin = new Vector2(origin.X, origin.Y + i * rowHeight); @@ -300,7 +308,7 @@ internal sealed class LivePreviewPanel : IDisposable var pillMax = new Vector2(origin.X + pillWidth, max.Y - 4f); draw.AddRectFilled(pillMin, pillMax, ColourUtil.RgbaToAbgr(theme.Colors.Primary), 6f); - var pillLabel = "Say"; + var pillLabel = ChatType.Say.Name(); var pillLabelSize = ImGui.CalcTextSize(pillLabel); var pillTextPos = new Vector2( pillMin.X + ((pillMax.X - pillMin.X) - pillLabelSize.X) * 0.5f, @@ -353,7 +361,12 @@ internal sealed class LivePreviewPanel : IDisposable var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary); var pillY = origin.Y + (height - pillH) * 0.5f; - ReadOnlySpan slots = ["Say", "open", "3 tabs"]; + ReadOnlySpan slots = + [ + ChatType.Say.Name(), + HellionStrings.Settings_Preview_StatusOpen, + string.Format(HellionStrings.StatusBar_Tabs_Other, 3), + ]; ReadOnlySpan dots = [ theme.Colors.StatusSuccess, diff --git a/HellionChat/Ui/Components/Settings/ThemePicker.cs b/HellionChat/Ui/Components/Settings/ThemePicker.cs index c18bee8..6069633 100644 --- a/HellionChat/Ui/Components/Settings/ThemePicker.cs +++ b/HellionChat/Ui/Components/Settings/ThemePicker.cs @@ -9,22 +9,25 @@ namespace HellionChat.Ui.Components.Settings; internal sealed class ThemePicker { - private static readonly (string Category, string[] Slugs, bool DefaultExpanded)[] CategoryMap = - { - ( - "Hellion Brand", - new[] { "hellion-arctic", "hellion-spectrum", "forge-merchantman" }, - true - ), - ( - HellionStrings.Settings_Theme_Category_Cool, - new[] { "night-blue", "event-horizon", "indigo-violet", "crystal-nocturne" }, - false - ), - (HellionStrings.Settings_Theme_Category_Natural, new[] { "mint-grove" }, false), - (HellionStrings.Settings_Theme_Category_Classic, new[] { "chat2-classic" }, false), - (HellionStrings.Settings_Theme_Category_Retro, new[] { "synthwave-sunset" }, false), - }; + // Built per call, not once. As a static readonly array the category names + // froze at whatever language the plugin started in -- a runtime switch + // relabelled the whole window except these five. + private static (string Category, string[] Slugs, bool DefaultExpanded)[] CategoryMap => + [ + ( + "Hellion Brand", + new[] { "hellion-arctic", "hellion-spectrum", "forge-merchantman" }, + true + ), + ( + HellionStrings.Settings_Theme_Category_Cool, + new[] { "night-blue", "event-horizon", "indigo-violet", "crystal-nocturne" }, + false + ), + (HellionStrings.Settings_Theme_Category_Natural, new[] { "mint-grove" }, false), + (HellionStrings.Settings_Theme_Category_Classic, new[] { "chat2-classic" }, false), + (HellionStrings.Settings_Theme_Category_Retro, new[] { "synthwave-sunset" }, false), + ]; // T2 ThemePickerCategoryStep diffs this against ThemeRegistry.BuiltinSlugs // to enforce coverage. Kept on the static map so the test does not pierce instance state. diff --git a/HellionChat/Ui/Components/StatusBar.cs b/HellionChat/Ui/Components/StatusBar.cs index b0eaeb2..a83d6d6 100644 --- a/HellionChat/Ui/Components/StatusBar.cs +++ b/HellionChat/Ui/Components/StatusBar.cs @@ -56,9 +56,16 @@ internal sealed class StatusBar { var msgPart = messages >= 1000 - ? string.Format(CultureInfo.InvariantCulture, "{0:0.0}k msg", messages / 1000.0) - : $"{messages} msg"; - var tabsPart = $"{tabs} {(tabs == 1 ? "tab" : "tabs")}"; + ? string.Format( + CultureInfo.CurrentCulture, + HellionStrings.StatusBar_MessagesThousands, + messages / 1000.0 + ) + : string.Format(HellionStrings.StatusBar_Messages, messages); + var tabsPart = string.Format( + tabs == 1 ? HellionStrings.StatusBar_Tabs_One : HellionStrings.StatusBar_Tabs_Other, + tabs + ); return $"{tabsPart} · {msgPart}"; } @@ -66,7 +73,10 @@ internal sealed class StatusBar { if (count <= 0) return string.Empty; - return $"{count} {(count == 1 ? "tell" : "tells")}"; + return string.Format( + count == 1 ? HellionStrings.StatusBar_Tells_One : HellionStrings.StatusBar_Tells_Other, + count + ); } // Single-pass aggregator — same shape as the previous helper so the