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.
This commit is contained in:
2026-08-19 05:11:05 +02:00
parent 5991f49c59
commit 3a1b863def
31 changed files with 2419 additions and 135 deletions
@@ -1,6 +1,7 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Util;
@@ -31,26 +32,24 @@ internal sealed class ColorPicker
private void DrawIdleState()
{
var active = _themes.Active;
ImGui.TextDisabled($"Active theme: {active.Name}");
ImGui.TextDisabled(string.Format(HellionStrings.Settings_Theme_ActiveTheme, active.Name));
// Fork built-ins before editing: Switch() prefers built-in slugs over custom
// files with the same slug, so an in-place edit would silently no-op.
if (active.IsBuiltIn)
{
if (ImGui.Button("Fork & Edit"))
if (ImGui.Button(HellionStrings.Settings_Theme_ForkAndEdit))
{
ForkAndBeginEditing(active);
}
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip(
"Built-in themes cannot be edited in place. Fork creates a custom copy you can edit and save."
);
ImGui.SetTooltip(HellionStrings.Settings_Theme_ForkTooltip);
}
}
else
{
if (ImGui.Button("Edit theme"))
if (ImGui.Button(HellionStrings.Settings_Theme_EditTheme))
{
_themes.BeginEditing(active);
}
@@ -87,12 +86,12 @@ internal sealed class ColorPicker
private void DrawEditState(Theme buffer)
{
ImGui.TextUnformatted($"Editing: {buffer.Name}");
ImGui.TextUnformatted(string.Format(HellionStrings.Settings_Theme_Editing, buffer.Name));
ImGui.Separator();
DrawSection(
ImGui.GetID("colors.surfaces"u8),
"Surfaces",
HellionStrings.Settings_Theme_Group_Surfaces,
buffer,
c =>
new[]
@@ -116,7 +115,7 @@ internal sealed class ColorPicker
DrawSection(
ImGui.GetID("colors.borders"u8),
"Borders",
HellionStrings.Settings_Theme_Group_Borders,
buffer,
c => new[] { ("Border", c.Border) },
(c, edits) => c with { Border = edits[0].color }
@@ -124,7 +123,7 @@ internal sealed class ColorPicker
DrawSection(
ImGui.GetID("colors.text"u8),
"Text",
HellionStrings.Settings_Theme_Group_Text,
buffer,
c =>
new[]
@@ -186,7 +185,7 @@ internal sealed class ColorPicker
DrawSection(
ImGui.GetID("colors.identity"u8),
"Identity",
HellionStrings.Settings_Theme_Group_Identity,
buffer,
c => new[] { ("Identity", c.Identity) },
(c, edits) => c with { Identity = edits[0].color }
@@ -194,7 +193,7 @@ internal sealed class ColorPicker
DrawSection(
ImGui.GetID("colors.status"u8),
"Status",
HellionStrings.Settings_Theme_Group_Status,
buffer,
c =>
new[]
@@ -264,12 +263,12 @@ internal sealed class ColorPicker
private void DrawActionButtons(Theme buffer)
{
if (ImGui.Button("Save"))
if (ImGui.Button(HellionStrings.Settings_Theme_Save))
{
_themes.SaveEditingBuffer(out _);
}
ImGui.SameLine();
if (ImGui.Button("Cancel"))
if (ImGui.Button(HellionStrings.Settings_Theme_Cancel))
{
_themes.DiscardEditingBuffer();
}
@@ -282,14 +281,14 @@ internal sealed class ColorPicker
var isForkBuffer = !buffer.IsBuiltIn && _themes.Active.Slug != buffer.Slug;
using (ImRaii.Disabled(isForkBuffer))
{
if (ImGui.Button("Reset to source"))
if (ImGui.Button(HellionStrings.Settings_Theme_ResetToSource))
{
_themes.BeginEditing(_themes.Active);
}
}
if (isForkBuffer && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
ImGui.SetTooltip("Reset is unavailable while editing a fork. Save or Cancel first.");
ImGui.SetTooltip(HellionStrings.Settings_Theme_ResetUnavailable);
}
}
}
@@ -43,11 +43,14 @@ internal sealed class FontsSection
// Readout so the user can see which font is actually active.
var active =
Plugin.Config.UseHellionFont ? "Hellion Inter (bundled)"
Plugin.Config.UseHellionFont ? HellionStrings.Settings_Fonts_Bundled
: Plugin.Config.FontsEnabled
? $"Global: {Plugin.Config.GlobalFontV2.FontId.Family.EnglishName}"
: "FFXIV game font";
ImGui.TextDisabled($"Active: {active}");
? 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 (
@@ -3,6 +3,7 @@ using System.Threading;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
using HellionChat.Util;
@@ -307,7 +308,7 @@ internal sealed class LivePreviewPanel : IDisposable
);
draw.AddText(pillTextPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), pillLabel);
var placeholder = "Type a message...";
var placeholder = HellionStrings.Settings_Preview_TypeAMessage;
var phSize = ImGui.CalcTextSize(placeholder);
var phPos = new Vector2(pillMax.X + 6f, origin.Y + (height - phSize.Y) * 0.5f);
draw.AddText(phPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), placeholder);
@@ -21,13 +21,13 @@ internal sealed class ThemeImportExportRow
public void Draw()
{
if (ImGui.Button("Fork active theme"))
if (ImGui.Button(HellionStrings.Settings_Theme_ForkActive))
{
ForkActive();
}
ImGui.SameLine();
if (ImGui.Button("Import theme file…"))
if (ImGui.Button(HellionStrings.Settings_Theme_ImportFile))
{
ImportFromPath(_importPath);
}
@@ -39,7 +39,7 @@ internal sealed class ThemeImportExportRow
}
ImGui.SameLine();
if (ImGui.Button("Export active theme…"))
if (ImGui.Button(HellionStrings.Settings_Theme_ExportActive))
{
ExportActive();
}
@@ -47,7 +47,7 @@ internal sealed class ThemeImportExportRow
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint(
"##theme-import-path",
"Path to JSON file (or drag-and-drop into the folder)",
HellionStrings.Settings_Theme_ImportPathHint,
ref _importPath,
512
);
@@ -309,7 +309,7 @@ internal sealed class ThemeImportExportRow
var defaultName = $"{theme.Slug}.json";
Plugin.FileDialogManager.SaveFileDialog(
"Export theme",
HellionStrings.Settings_Theme_ExportDialogTitle,
".json",
defaultName,
".json",
@@ -1,6 +1,7 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Util;
@@ -16,13 +17,13 @@ internal sealed class ThemePicker
true
),
(
"Cool",
HellionStrings.Settings_Theme_Category_Cool,
new[] { "night-blue", "event-horizon", "indigo-violet", "crystal-nocturne" },
false
),
("Natural", new[] { "mint-grove" }, false),
("Classic", new[] { "chat2-classic" }, false),
("Retro", new[] { "synthwave-sunset" }, 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
@@ -78,7 +79,7 @@ internal sealed class ThemePicker
if (
_sections.Draw(
ImGui.GetID("theme.category.custom"u8),
$"Custom ({customs.Count})",
string.Format(HellionStrings.Settings_Theme_Custom, customs.Count),
disabled: locked
)
)
@@ -90,7 +91,7 @@ internal sealed class ThemePicker
if (locked && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
ImGui.SetTooltip("Save or discard your edits first");
ImGui.SetTooltip(HellionStrings.Settings_Theme_LockedTooltip);
}
}