From e0c9efca0530dad97d6ebdb657f1316b4aebe312 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Wed, 19 Aug 2026 08:24:31 +0200 Subject: [PATCH] i18n: the five spots the smoke test found, and a BOM in the export Reported from a real pass through the window. The theme categories were a static readonly array, so the five names froze at whatever language the plugin started in and a runtime switch relabelled the entire window except them. Same shape as the layout labels earlier in this cycle; this one got missed because replacing the literals with resource lookups looks finished until you actually switch. The status bar built its counts from English literals -- tab, tabs, msg, tell, tells -- and the privacy pill said "Privacy-First" in all 25 files. The thousands separator follows the user's culture now too, so German reads 1,2k rather than 1.2k. The live preview claims to show what the window will look like. It was showing English channel names next to a translated placeholder, which is worse than either. Channel labels come from ChatType.Name() now and the status slots share the strings with the real status bar. The four mock chat lines stay English on the earlier decision. And the export wrote a byte order mark. Encoding.UTF8 emits one, and a leading U+FEFF makes the JSON invalid for every strict parser -- confirmed against a real export from the game, where python's json.load refused the file. CSV keeps its BOM, because without one Excel guesses the codepage and mangles every non-ASCII name. The self-test that was supposed to catch that read the file with File.ReadAllText, which strips a BOM while detecting the encoding. It reads bytes now. The status bar tests asserted English literals and started failing on a German machine -- they pin a fixed culture now instead of inheriting the locale of whoever runs them. --- HellionChat/Export/MessageExporter.cs | 11 +++++- .../Resources/HellionStrings.Designer.cs | 8 +++++ HellionChat/Resources/HellionStrings.ca.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.cs.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.da.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.de.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.el.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.es.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.fi.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.fr.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.hu.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.it.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.ja.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.ko.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.nb.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.nl.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.pl.resx | 26 +++++++++++++- .../Resources/HellionStrings.pt-BR.resx | 26 +++++++++++++- .../Resources/HellionStrings.pt-PT.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.resx | 24 +++++++++++++ HellionChat/Resources/HellionStrings.ro.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.ru.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.sv.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.tr.resx | 26 +++++++++++++- HellionChat/Resources/HellionStrings.uk.resx | 26 +++++++++++++- .../Resources/HellionStrings.zh-Hans.resx | 26 +++++++++++++- .../Resources/HellionStrings.zh-Hant.resx | 26 +++++++++++++- HellionChat/SelfTests/ExportRoundTripStep.cs | 7 ++++ .../Components/Settings/LivePreviewPanel.cs | 21 ++++++++--- .../Ui/Components/Settings/ThemePicker.cs | 35 ++++++++++--------- HellionChat/Ui/Components/StatusBar.cs | 18 +++++++--- 31 files changed, 699 insertions(+), 49 deletions(-) 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