Files
HellionChat/HellionChat/Ui/Components/Settings/FontsSection.cs
T
JonKazama-Hellion 3a1b863def i18n: adopt the client's word for tell, and translate the appearance tab
Two decisions, both yours.

Where Square Enix ships a client in a language, that client's word wins.
German says Flüstern, French message privé, Japanese テル, Korean
귓속말, Simplified Chinese 密语. Everywhere else there is no official
client and the loanword is what players actually say, so it stays.

That meant rewriting the whole corpus in those languages rather than
just the new keys, which is the reason the split existed in the first
place: forty German values and thirty-seven French ones carried the old
word. /tell is untouched, because that is a command and not a noun.

The mechanical pass left French with three agreement errors -- "des
message privé", "ce onglet", "messages de message privé" -- which is
what happens when you substring-replace a language with gender and
number. Fixed by hand.

Traditional Chinese keeps 悄悄話, which the file already used nineteen
times; it has no official client of its own.

The appearance tab is translated as well. Thirty keys: the colour
editor's groups and buttons, the theme categories, fork and import and
export, the font labels and the preview's input placeholder. Theme token
names stay English -- WindowBg and TextPrimary are JSON keys, not
prose -- and so do the brand strings.

465 keys, 25 files, no gaps, no orphans, no placeholder drift.

Still English and not in this commit: the database viewer behind
/hellionView, which is a documented user command rather than a
developer tool, and the symbol picker. Both are their own block.
2026-08-19 05:11:05 +02:00

224 lines
7.3 KiB
C#

using Dalamud;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.FontIdentifier;
using HellionChat.Resources;
using HellionChat.Util;
namespace HellionChat.Ui.Components.Settings;
// Restores the 1.5.6 font-selection UI (1d3b429:Appearance.cs:249-405): pick the
// bundled Hellion font vs a custom global/Japanese/italic font, sizes, and extra
// glyph ranges. v1.6.0 saves live, so any change persists and rebuilds the atlas
// at once (RebuildDelegateFonts, unconditional — a face change keeps the size, so
// the size-gated IfChanged path would miss it).
internal sealed class FontsSection
{
private readonly Plugin _plugin;
private readonly FontManager _fontManager;
private readonly SectionRenderer _sections;
public FontsSection(Plugin plugin, FontManager fontManager, SectionRenderer sections)
{
_sections = sections;
_plugin = plugin;
_fontManager = fontManager;
}
private void Apply()
{
_plugin.SaveConfig();
_fontManager.RebuildDelegateFonts();
}
public void Draw()
{
if (
!_sections.Draw(
ImGui.GetID("appearance.fonts"u8),
HellionStrings.Settings_Section_Fonts,
open: false
)
)
return;
// Readout so the user can see which font is actually active.
var active =
Plugin.Config.UseHellionFont ? HellionStrings.Settings_Fonts_Bundled
: Plugin.Config.FontsEnabled
? string.Format(
HellionStrings.Settings_Fonts_Global,
Plugin.Config.GlobalFontV2.FontId.Family.EnglishName
)
: HellionStrings.Settings_Fonts_GameFont;
ImGui.TextDisabled(string.Format(HellionStrings.Settings_Fonts_Active, active));
ImGui.Spacing();
if (
ImGui.Checkbox(
HellionStrings.Theme_UseHellionFont_Name,
ref Plugin.Config.UseHellionFont
)
)
{
if (Plugin.Config.UseHellionFont)
Plugin.Config.FontsEnabled = false;
Apply();
}
ImGuiUtil.HelpMarker(HellionStrings.Theme_UseHellionFont_Description);
ImGui.Spacing();
if (Plugin.Config.UseHellionFont)
{
DrawSizeCombo(Language.Options_FontSize_Name, ref Plugin.Config.FontSizeV2);
ImGui.Spacing();
}
else if (ImGui.Checkbox(Language.Options_FontsEnabled, ref Plugin.Config.FontsEnabled))
{
Apply();
}
var unused = false;
if (!Plugin.Config.UseHellionFont && !Plugin.Config.FontsEnabled)
{
DrawSizeCombo(Language.Options_FontSize_Name, ref Plugin.Config.FontSizeV2);
}
else if (!Plugin.Config.UseHellionFont)
{
DrawFontChooser(
Language.Options_Font_Name,
Plugin.Config.GlobalFontV2,
false,
ref unused,
spec => Plugin.Config.GlobalFontV2 = spec,
() => Plugin.Config.GlobalFontV2 = DefaultFont(DalamudAsset.NotoSansCjkRegular),
"global"
);
ImGuiUtil.HelpMarker(
string.Format(Language.Options_Font_Description, Plugin.PluginName)
);
ImGuiUtil.WarningText(Language.Options_Font_Warning);
ImGui.Spacing();
DrawFontChooser(
Language.Options_JapaneseFont_Name,
Plugin.Config.JapaneseFontV2,
false,
ref unused,
spec => Plugin.Config.JapaneseFontV2 = spec,
() => Plugin.Config.JapaneseFontV2 = DefaultFont(DalamudAsset.NotoSansCjkMedium),
"japanese",
id => !id.LocaleNames?.ContainsKey("ja-jp") ?? false,
"いろはにほへと ちりぬるを"
);
ImGuiUtil.HelpMarker(
string.Format(Language.Options_JapaneseFont_Description, Plugin.PluginName)
);
ImGui.Spacing();
DrawFontChooser(
Language.Options_ItalicFont_Name,
Plugin.Config.ItalicFontV2,
true,
ref Plugin.Config.ItalicEnabled,
spec => Plugin.Config.ItalicFontV2 = spec,
() =>
{
Plugin.Config.ItalicEnabled = false;
Plugin.Config.ItalicFontV2 = DefaultFont(DalamudAsset.NotoSansCjkRegular);
},
"italic"
);
ImGuiUtil.HelpMarker(
string.Format(Language.Options_Italic_Description, Plugin.PluginName)
);
ImGui.Spacing();
}
// ExtraGlyphRanges stays reachable regardless of the font source so the
// user can verify/override the per-language auto-activation (v1.5.3 note).
ImGui.Spacing();
if (
_sections.Draw(
ImGui.GetID("appearance.fonts.glyphs"u8),
Language.Options_ExtraGlyphs_Name,
open: false
)
)
{
ImGuiUtil.HelpMarker(
string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName)
);
var range = (int)Plugin.Config.ExtraGlyphRanges;
var changed = false;
foreach (var extra in Enum.GetValues<ExtraGlyphRanges>())
changed |= ImGui.CheckboxFlags(extra.Name(), ref range, (int)extra);
if (changed)
{
Plugin.Config.ExtraGlyphRanges = (ExtraGlyphRanges)range;
Apply();
}
}
DrawSizeCombo(Language.Options_SymbolsFontSize_Name, ref Plugin.Config.SymbolsFontSizeV2);
ImGuiUtil.HelpMarker(Language.Options_SymbolsFontSize_Description);
ImGui.Spacing();
}
private void DrawSizeCombo(string label, ref float size)
{
var before = size;
ImGuiUtil.FontSizeCombo(label, ref size);
if (!size.Equals(before))
Apply();
}
private void DrawFontChooser(
string label,
SingleFontSpec font,
bool checkbox,
ref bool checkboxValue,
Action<SingleFontSpec> set,
Action reset,
string resetId,
Predicate<IFontFamilyId>? exclusion = null,
string? preview = null
)
{
var prevCheckbox = checkboxValue;
var chooser = ImGuiUtil.FontChooser(
label,
font,
checkbox,
ref checkboxValue,
exclusion,
preview
);
if (checkbox && checkboxValue != prevCheckbox)
Apply();
// The chooser dialog resolves on a worker thread; marshal the result back
// onto the framework thread before touching config + the font atlas.
chooser?.ResultTask.ContinueWith(r =>
{
if (r.IsCompletedSuccessfully)
Plugin.Framework.Run(() =>
{
set(r.Result);
Apply();
});
});
ImGui.SameLine();
if (ImGui.Button($"Reset##{resetId}"))
{
reset();
Apply();
}
}
private static SingleFontSpec DefaultFont(DalamudAsset asset) =>
new() { FontId = new DalamudAssetFontAndFamilyId(asset), SizePt = 12.75f };
}