Merge branch 'feature/v1.12.0' into main

Reconnection cycle. Export, the tab editor, database maintenance and pinning were
unreachable after the v1.6.0 window rebuild; all four are back, the settings
window is translated into 25 languages, and the channel grid decides what gets
stored.

Local state only. repo.json stays on 1.5.6.0 and the download links are
untouched.
This commit is contained in:
2026-08-19 17:40:20 +02:00
136 changed files with 19634 additions and 4985 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup .NET 10 - name: Setup .NET 10
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
with: with:
dotnet-version: 10.0.x dotnet-version: 10.0.x
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup .NET 10 - name: Setup .NET 10
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
with: with:
dotnet-version: 10.0.x dotnet-version: 10.0.x
+6 -6
View File
@@ -1,4 +1,8 @@
name: Security name: Security
# Ruft den zentralen Scan-Workflow in security-workflows auf
# (Semgrep SAST + Trivy filesystem scan).
on: on:
push: push:
branches: [main, master] branches: [main, master]
@@ -11,10 +15,6 @@ jobs:
scan: scan:
uses: JonKazama-Hellion/security-workflows/.gitea/workflows/security-scan.yml@main uses: JonKazama-Hellion/security-workflows/.gitea/workflows/security-scan.yml@main
with: with:
# MessageStore.cs uses string-interpolation in CommandText for table # MessageStore.cs interpoliert SQL-Strings, die plugin-lokal sicher sind;
# names and clause-joins that come from internal code constants, not # Semgrep matcht das Pattern, CodeQL mit Datenflussanalyse nicht.
# user input. Values are bound via SqlParameter, the SQL surface is
# local-only inside a Dalamud plugin. Semgrep matches the pattern
# without dataflow, so it flags those eight call sites; CodeQL
# would not. Suppressed for this repo only.
semgrep-exclude-rules: 'csharp.lang.security.sqli.csharp-sqli.csharp-sqli' semgrep-exclude-rules: 'csharp.lang.security.sqli.csharp-sqli.csharp-sqli'
+161 -55
View File
@@ -21,7 +21,15 @@ internal sealed class AutoTellTabsService : IDisposable
private readonly MessageManager _messageManager; private readonly MessageManager _messageManager;
private readonly MessageStore _store; private readonly MessageStore _store;
private readonly ILogger<AutoTellTabsService> _logger; private readonly ILogger<AutoTellTabsService> _logger;
private readonly object _tempTabsLock = new();
// Tabs-list structure lock now lives on Plugin (neutral owner) so the
// MessageManager refilter can share it. See Plugin.TabsListLock / B3.
private object TabsListLock => _plugin.TabsListLock;
// Bumped whenever something wipes unpinned temp tabs wholesale (logout).
// HandleTell reads it before releasing the lock and re-checks after, so a
// tab built in between is discarded instead of outliving the wipe.
private int _tabGeneration;
// Hard cap on pinned TempTabs so the sidebar doesn't inflate over years // Hard cap on pinned TempTabs so the sidebar doesn't inflate over years
// of usage. Separate pool from AutoTellTabsLimit (15) — pinned tabs live // of usage. Separate pool from AutoTellTabsLimit (15) — pinned tabs live
@@ -31,6 +39,10 @@ internal sealed class AutoTellTabsService : IDisposable
private bool _initialized; private bool _initialized;
// Set when Initialize ran before a character was available; cleared once the
// history has actually been loaded.
private bool _rehydratePending;
internal AutoTellTabsService( internal AutoTellTabsService(
Plugin plugin, Plugin plugin,
MessageManager messageManager, MessageManager messageManager,
@@ -68,12 +80,30 @@ internal sealed class AutoTellTabsService : IDisposable
RehydratePinnedTabs(); RehydratePinnedTabs();
_messageManager.MessageProcessed += HandleTell; _messageManager.MessageProcessed += HandleTell;
Plugin.ClientState.Login += OnLogin;
Plugin.ClientState.Logout += OnLogout; Plugin.ClientState.Logout += OnLogout;
_initialized = true; _initialized = true;
} }
// Deferred when the plugin starts before a character is logged in, which is
// the normal case: the game loads plugins at boot. CurrentContentId is 0
// until then, so the history query would look up tells for character zero,
// find none, and leave every pinned tab blank for the whole session.
//
// Only visible to someone who actually pins a tell tab AND starts the game
// with the plugin already installed. Reloading the plugin in a running
// session -- what a developer does all day -- hides it completely.
private void RehydratePinnedTabs() private void RehydratePinnedTabs()
{ {
if (_messageManager.CurrentContentId == 0)
{
_logger.LogDebug("[Pin] Rehydrate deferred: no character yet, waiting for login");
_rehydratePending = true;
return;
}
_rehydratePending = false;
var pinned = Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInPinnedPool); var pinned = Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInPinnedPool);
_logger.LogDebug($"[Pin] Rehydrate scan: {pinned} pinned tab(s) found"); _logger.LogDebug($"[Pin] Rehydrate scan: {pinned} pinned tab(s) found");
@@ -114,6 +144,7 @@ internal sealed class AutoTellTabsService : IDisposable
return; return;
} }
Plugin.ClientState.Login -= OnLogin;
Plugin.ClientState.Logout -= OnLogout; Plugin.ClientState.Logout -= OnLogout;
_messageManager.MessageProcessed -= HandleTell; _messageManager.MessageProcessed -= HandleTell;
_initialized = false; _initialized = false;
@@ -147,15 +178,19 @@ internal sealed class AutoTellTabsService : IDisposable
return; return;
} }
lock (_tempTabsLock) // Three steps, because building the tab pulls history out of the store and
// that must not happen under TabsListLock (B3 rule; the query sorts the whole
// receiver history). Step 1 and 3 are locked, step 2 is not.
int generation;
lock (TabsListLock)
{ {
var existing = FindTempTab(partner.Value.Name, partner.Value.World); var existing = FindTempTab(partner.Value.Name, partner.Value.World);
if (existing != null) if (existing != null)
{ {
// Already routed via MessageManager pipeline. Repair the // Already routed via MessageManager pipeline — no AddMessage here,
// tell-target if the fallback hit a pinned tab whose // HandleTell runs after the delivery loop. Repair the tell-target if
// TellTarget didn't survive a previous round-trip — keeps // the fallback hit a pinned tab whose TellTarget didn't survive a
// FindTempTab fast on the next message. // previous round-trip — keeps FindTempTab fast on the next message.
if ( if (
existing.IsPinned existing.IsPinned
&& (existing.TellTarget is null || !existing.TellTarget.IsSet()) && (existing.TellTarget is null || !existing.TellTarget.IsSet())
@@ -172,12 +207,29 @@ internal sealed class AutoTellTabsService : IDisposable
return; return;
} }
if (ActiveTempTabCount >= Plugin.Config.AutoTellTabsLimit) generation = _tabGeneration;
}
var tab = BuildTempTabWithHistory(partner.Value, message);
lock (TabsListLock)
{
// A logout in between wiped the unpinned pool; committing now would
// resurrect a tab for a character we already left.
if (generation != _tabGeneration)
return;
// Someone else (self-test, UI) may have created the tab while we built
// ours. Hand the message to theirs and drop what we built — unlike the
// early return above, this tab appeared after the delivery loop ran.
var raced = FindTempTab(partner.Value.Name, partner.Value.World);
if (raced != null)
{ {
DropOldestTempTab(); raced.AddMessage(message, unread: true);
return;
} }
SpawnTempTab(partner.Value, message); CommitTempTab(tab);
} }
} }
@@ -240,49 +292,59 @@ internal sealed class AutoTellTabsService : IDisposable
} }
// Lock-protected lookup for the framework-thread caller (TellRouterService). // Lock-protected lookup for the framework-thread caller (TellRouterService).
// Config.Tabs is mutated under _tempTabsLock on the PendingMessage worker thread, // Config.Tabs is mutated under the shared Plugin.TabsListLock on the worker thread,
// so a framework-tick reader must take the same lock to avoid enumerating the list // so a framework-tick reader must take the same lock to avoid enumerating the list
// mid-mutation. // mid-mutation.
internal Tab? FindTempTabSafe(string name, uint world) internal Tab? FindTempTabSafe(string name, uint world)
{ {
lock (_tempTabsLock) lock (TabsListLock)
return FindTempTab(name, world); return FindTempTab(name, world);
} }
internal void DropOldestTempTab() internal void DropOldestTempTab()
{ {
// Pinned tabs live in their own bucket (MaxPinnedTempTabs) and are // B3: lock the list-structure ops so the (currently caller-less) Unpin path
// never drop candidates. They leave the bucket only via Unpin or // can't race the worker; re-entrant when HandleTell already holds the lock.
// PromoteToPermanent. lock (TabsListLock)
var victim = Plugin
.Config.Tabs.Select((tab, idx) => (Tab: tab, Index: idx))
.Where(t => TabLifecycleHelpers.IsInUnpinnedPool(t.Tab))
.OrderByDescending(t => t.Tab.IsGreeted)
.ThenBy(t => t.Tab.LastActivity)
.FirstOrDefault();
if (victim.Tab == null)
{ {
return; // Pinned tabs live in their own bucket (MaxPinnedTempTabs) and are
// never drop candidates. They leave the bucket only via Unpin or
// PromoteToPermanent.
var victim = Plugin
.Config.Tabs.Select((tab, idx) => (Tab: tab, Index: idx))
.Where(t => TabLifecycleHelpers.IsInUnpinnedPool(t.Tab))
.OrderByDescending(t => t.Tab.IsGreeted)
.ThenBy(t => t.Tab.LastActivity)
.FirstOrDefault();
if (victim.Tab == null)
{
return;
}
var dropped = victim.Tab;
// By reference, not by index: the index came from a Select() earlier in
// this block and would point at the wrong tab if anything shifted the list.
Plugin.Config.Tabs.Remove(dropped);
// Re-anchor the UI selection if it pointed at the dropped tab, and close any
// pop-out window the dropped tab owned. Both run on the PendingMessage worker
// thread and touch window state the Draw path reads (OnTabActivated re-seed +
// the pool's Unbind), so marshal onto the framework thread to serialize with
// Draw (reference_dalamud_framework_thread). TryClose is idempotent: a tab that
// was never popped is a silent no-op.
Plugin.Framework.RunOnFrameworkThread(() =>
{
_plugin.ChannelPopoutPool.TryClose(dropped.Identifier);
_plugin.MainWindow?.ResetActiveTabIfRemoved(dropped);
});
} }
var dropped = victim.Tab;
Plugin.Config.Tabs.RemoveAt(victim.Index);
// Re-anchor the UI selection if it pointed at the dropped tab, and close any
// pop-out window the dropped tab owned. Both run on the PendingMessage worker
// thread and touch window state the Draw path reads (OnTabActivated re-seed +
// the pool's Unbind), so marshal onto the framework thread to serialize with
// Draw (reference_dalamud_framework_thread). TryClose is idempotent: a tab that
// was never popped is a silent no-op.
Plugin.Framework.RunOnFrameworkThread(() =>
{
_plugin.ChannelPopoutPool.TryClose(dropped.Identifier);
_plugin.MainWindow?.ResetActiveTabIfRemoved(dropped);
});
} }
private void SpawnTempTab((string Name, uint World) partner, Message currentMessage) // Runs WITHOUT TabsListLock: PreloadHistory hits the store, which used to hold
// the lock across a query that sorted the whole receiver history. The tab is not
// public until CommitTempTab adds it, so building it unlocked is safe.
private Tab BuildTempTabWithHistory((string Name, uint World) partner, Message currentMessage)
{ {
var tab = BuildTempTab(partner.Name, partner.World); var tab = BuildTempTab(partner.Name, partner.World);
@@ -298,11 +360,22 @@ internal sealed class AutoTellTabsService : IDisposable
tab.PopOut = true; tab.PopOut = true;
} }
return tab;
}
// Caller MUST hold TabsListLock.
private void CommitTempTab(Tab tab)
{
if (ActiveTempTabCount >= Plugin.Config.AutoTellTabsLimit)
{
DropOldestTempTab();
}
Plugin.Config.Tabs.Add(tab); Plugin.Config.Tabs.Add(tab);
// Actually open the pop-out window for the flagged tab — without this the // Actually open the pop-out window for the flagged tab — without this the
// flag was dead (a PopOut tab with no window). SpawnTempTab runs on the // flag was dead (a PopOut tab with no window). CommitTempTab runs on the
// PendingMessage worker thread under _tempTabsLock; TryOpen does // PendingMessage worker thread under Plugin.TabsListLock; TryOpen does
// OnTabActivated + Bind (window state Draw reads), so marshal onto the // OnTabActivated + Bind (window state Draw reads), so marshal onto the
// framework thread. If the pool is full, drop the flag so it never claims a // framework thread. If the pool is full, drop the flag so it never claims a
// window it didn't get (flag/window parity). // window it didn't get (flag/window parity).
@@ -428,7 +501,7 @@ internal sealed class AutoTellTabsService : IDisposable
return; return;
} }
lock (_tempTabsLock) lock (TabsListLock)
{ {
// Guard against frame-race: sidebar might render a tab already removed by LRU or logout // Guard against frame-race: sidebar might render a tab already removed by LRU or logout
if (!Plugin.Config.Tabs.Contains(tab)) if (!Plugin.Config.Tabs.Contains(tab))
@@ -440,9 +513,20 @@ internal sealed class AutoTellTabsService : IDisposable
} }
} }
// Fires on the login that follows a boot-time start, and on every character
// switch after one. Guarded by the pending flag so a switch does not append
// a second copy of the history to tabs that already have it.
private void OnLogin()
{
if (!_rehydratePending)
return;
RehydratePinnedTabs();
}
private void OnLogout(int type, int code) private void OnLogout(int type, int code)
{ {
lock (_tempTabsLock) lock (TabsListLock)
{ {
// Pinned TempTabs must survive char-switch — that's the whole point // Pinned TempTabs must survive char-switch — that's the whole point
// of pinning. Only unpinned ones get stripped. // of pinning. Only unpinned ones get stripped.
@@ -464,6 +548,11 @@ internal sealed class AutoTellTabsService : IDisposable
Plugin.Config.Tabs.RemoveAll(TabLifecycleHelpers.IsInUnpinnedPool); Plugin.Config.Tabs.RemoveAll(TabLifecycleHelpers.IsInUnpinnedPool);
// HandleTell builds a tab outside the lock; bumping here lets it detect
// that the world moved on and drop what it built. Read and compared under
// the same lock, so no volatile needed.
_tabGeneration++;
// Re-anchor the UI selection if the active tab was one of the stripped // Re-anchor the UI selection if the active tab was one of the stripped
// unpinned temp tabs (reference predicate, not an index). Logout is a // unpinned temp tabs (reference predicate, not an index). Logout is a
// framework-thread event, so this is already serialized with Draw — no // framework-thread event, so this is already serialized with Draw — no
@@ -485,16 +574,23 @@ internal sealed class AutoTellTabsService : IDisposable
return false; return false;
} }
if (PinnedTempTabCount >= MaxPinnedTempTabs) // Count and flag under one lock so the cap can't be raced. SaveConfig stays
// OUTSIDE -- holding TabsListLock across a save would put an fsync on the
// click path, which is what B6 just removed elsewhere.
lock (TabsListLock)
{ {
WrapperUtil.AddNotification( if (PinnedTempTabCount >= MaxPinnedTempTabs)
string.Format(HellionStrings.PinTab_LimitReached, MaxPinnedTempTabs), {
NotificationType.Warning WrapperUtil.AddNotification(
); string.Format(HellionStrings.PinTab_LimitReached, MaxPinnedTempTabs),
return false; NotificationType.Warning
);
return false;
}
tab.IsPinned = true;
} }
tab.IsPinned = true;
_logger.LogDebug( _logger.LogDebug(
$"[Pin] Pinned tab '{tab.Name}' target={tab.TellTarget?.Name}@{tab.TellTarget?.World}" $"[Pin] Pinned tab '{tab.Name}' target={tab.TellTarget?.Name}@{tab.TellTarget?.World}"
); );
@@ -511,13 +607,18 @@ internal sealed class AutoTellTabsService : IDisposable
// If the unpinned pool is already full, dropping the oldest before // If the unpinned pool is already full, dropping the oldest before
// flipping the flag avoids counting the just-unpinned tab as a drop // flipping the flag avoids counting the just-unpinned tab as a drop
// candidate. // candidate. Under lock, since DropOldestTempTab mutates the list.
if (ActiveTempTabCount >= Plugin.Config.AutoTellTabsLimit) // SaveConfig stays outside, see TryPin.
lock (TabsListLock)
{ {
DropOldestTempTab(); if (ActiveTempTabCount >= Plugin.Config.AutoTellTabsLimit)
{
DropOldestTempTab();
}
tab.IsPinned = false;
} }
tab.IsPinned = false;
_logger.LogDebug("[Pin] Unpinned tab '{TabName}'", tab.Name); _logger.LogDebug("[Pin] Unpinned tab '{TabName}'", tab.Name);
_plugin.SaveConfig(); _plugin.SaveConfig();
} }
@@ -534,7 +635,12 @@ internal sealed class AutoTellTabsService : IDisposable
// see StripTellBindingOnPromote; clearing Tab.TellTarget alone would leave // see StripTellBindingOnPromote; clearing Tab.TellTarget alone would leave
// CurrentChannel.Channel == Tell + a stale target and route a typed line // CurrentChannel.Channel == Tell + a stale target and route a typed line
// silently as /tell to the old partner. // silently as /tell to the old partner.
TabLifecycleHelpers.StripTellBindingOnPromote(tab); // Flips IsTempTab/IsPinned, which decide pool membership and whether a save
// strips the tab. Under lock so a concurrent save sees one or the other, never
// half. SaveConfig stays outside, see TryPin.
lock (TabsListLock)
TabLifecycleHelpers.StripTellBindingOnPromote(tab);
_logger.LogDebug($"[Pin] Promoted tab '{tab.Name}' to permanent (tell-binding dropped)"); _logger.LogDebug($"[Pin] Promoted tab '{tab.Name}' to permanent (tell-binding dropped)");
_plugin.SaveConfig(); _plugin.SaveConfig();
} }
+26
View File
@@ -0,0 +1,26 @@
namespace HellionChat;
// Reduced CJK fallback coverage for the v1.5.3 NotoSansCjk fallback merge (B1).
// Before B1 the fallback merged over the full `Ranges` array (Default + endonyms),
// duplicating the Latin/Default work already done by the global/Japanese fonts.
// This is the trimmed remainder the fallback is actually the sole source for:
// - Hangul Syllables (AC00-D7A3): no other merged font ships Korean glyphs.
// - The full CJK Unified Ideographs (Han) block: at UseHellionFont=true the global
// font is Inter-Light (no CJK), so the fallback is the SOLE Han source. The JpRange
// overlap is harmless (MergeMode: the Japanese font wins for shared kanji).
// Deliberately excluded: ONLY the ASCII/Latin Default block (0x20-0xFF), which the
// global font already owns -- that doubled Latin merge is the B1 waste being removed.
// Kept as plain start/end pairs so it is unit-testable without the unsafe ImGui
// glyph-range builder (mirrors FontSizeResolver's split-for-test rationale).
internal static class CjkFallbackRange
{
// Hangul Syllables + the full CJK Unified Ideographs (Han) block. Rationale: see
// class comment. Plain start/end pairs so it stays unit-testable.
internal static readonly ushort[] Pairs =
[
0xAC00,
0xD7A3, // Hangul Syllables
0x4E00,
0x9FFF, // CJK Unified Ideographs (full Han) -- sole source at UseHellionFont
];
}
+109 -201
View File
@@ -35,7 +35,7 @@ public class ConfigKeyBind
[Serializable] [Serializable]
public class Configuration : IPluginConfiguration public class Configuration : IPluginConfiguration
{ {
internal const int LatestVersion = 23; internal const int LatestVersion = 25;
public int Version { get; set; } = LatestVersion; public int Version { get; set; } = LatestVersion;
@@ -59,7 +59,15 @@ public class Configuration : IPluginConfiguration
// Privacy by Default master switch. Set false to restore upstream behaviour. // Privacy by Default master switch. Set false to restore upstream behaviour.
public bool PrivacyFilterEnabled = true; public bool PrivacyFilterEnabled = true;
// Empty set means the migration has not run yet — see Plugin.cs v6→v7. // Stays empty here. Dalamud deserialises with Json.NET's default settings,
// which means ObjectCreationHandling.Auto: a collection field that already
// holds items is *populated*, not replaced. A non-empty initializer would
// therefore union itself into every config on load and switch channels the
// user had unticked back on. Verified against Newtonsoft 13.0.3:
// saved [] loads as the initializer, saved [Say] loads as initializer + Say.
//
// Privacy by Default (DSGVO Art. 25) is seeded in CreateFresh instead, which
// only runs when there is no config file at all.
public HashSet<ChatType> PrivacyPersistChannels = []; public HashSet<ChatType> PrivacyPersistChannels = [];
// Failsafe for ChatTypes added by future FFXIV patches. New configs default // Failsafe for ChatTypes added by future FFXIV patches. New configs default
@@ -75,18 +83,33 @@ public class Configuration : IPluginConfiguration
[NonSerialized] [NonSerialized]
private readonly HashSet<ChatType> _warnedUnknownChannels = new(); private readonly HashSet<ChatType> _warnedUnknownChannels = new();
// A first-ever start records the player's own conversations and nothing
// else. Deliberately not a field initializer -- see PrivacyPersistChannels.
internal static Configuration CreateFresh()
{
var config = new Configuration();
config.PrivacyPersistChannels = [.. Privacy.PrivacyDefaults.PrivacyFirstWhitelist];
return config;
}
public bool IsAllowedForStorage(ChatType type) public bool IsAllowedForStorage(ChatType type)
{ {
if (!PrivacyFilterEnabled) if (!PrivacyFilterEnabled)
return true; return true;
if (PrivacyPersistChannels.Contains(type))
return true; // Runs per message on the worker thread while the settings UI can Add to the
// same set from the draw thread. A HashSet.Contains racing an Add that
// resizes buckets can return the wrong answer -- and this answer decides
// whether a message is persisted. Lock kept tight, this is a hot path.
bool listed;
lock (Plugin.Instance.ConfigMapsLock)
listed = PrivacyPersistChannels.Contains(type);
var known = Enum.IsDefined(typeof(ChatType), type);
// F3.2: log first occurrence of a ChatType the running build doesn't // F3.2: log first occurrence of a ChatType the running build doesn't
// recognise — i.e. one a future FFXIV patch may have added. Known // recognise — i.e. one a future FFXIV patch may have added.
// types the user opted out of are routed through the failsafe if (!known && !listed && _warnedUnknownChannels.Add(type))
// silently, like before.
if (!Enum.IsDefined(typeof(ChatType), type) && _warnedUnknownChannels.Add(type))
{ {
Plugin.LogProxy.Warning( Plugin.LogProxy.Warning(
"PrivacyFilter: unrecognised ChatType {Type} — falling back to PrivacyPersistUnknownChannels={Persist}.", "PrivacyFilter: unrecognised ChatType {Type} — falling back to PrivacyPersistUnknownChannels={Persist}.",
@@ -95,7 +118,7 @@ public class Configuration : IPluginConfiguration
); );
} }
return PrivacyPersistUnknownChannels; return Privacy.StorageRule.Allows(listed, known, PrivacyPersistUnknownChannels);
} }
// Retention master switch defaults to false — plugin will not delete // Retention master switch defaults to false — plugin will not delete
@@ -161,21 +184,20 @@ public class Configuration : IPluginConfiguration
// v1.2.1: default flipped false → true for consistency with other hide defaults. // v1.2.1: default flipped false → true for consistency with other hide defaults.
public bool HideInNewGamePlusMenu = true; public bool HideInNewGamePlusMenu = true;
public bool HideWhenInactive; public bool HideWhenInactive;
public int InactivityHideTimeout = 10;
public bool InactivityHideActiveDuringBattle = true;
[Obsolete("Use InactivityHideChannelsV2 instead")]
public Dictionary<ChatType, ChatSource> InactivityHideChannels = [];
public Dictionary<ChatType, (ChatSource, ChatSource)> InactivityHideChannelsV2 = [];
public bool InactivityHideExtraChatAll = true;
public HashSet<Guid> InactivityHideExtraChatChannels = [];
public bool ShowHideButton = true; public bool ShowHideButton = true;
public bool NativeItemTooltips = true; public bool NativeItemTooltips = true;
public bool ScreenshotMode; public bool ScreenshotMode;
// No control and no reader. Kept so a stored value survives until the
// rendering they describe exists; see the reconnect backlog. Note the two
// resource sets disagree on what PrettierTimestamps even means -- the wizard
// called it "relative time", the settings tab "modern layout".
public bool PrettierTimestamps = true; public bool PrettierTimestamps = true;
public bool MoreCompactPretty; public bool MoreCompactPretty;
public bool HideSameTimestamps = true; public bool HideSameTimestamps = true;
// No reader; see the reconnect backlog.
public bool ShowNoviceNetwork; public bool ShowNoviceNetwork;
// Migration-only since v23: the 1.5.6 sidebar↔top-tabs switch, superseded by // Migration-only since v23: the 1.5.6 sidebar↔top-tabs switch, superseded by
@@ -183,6 +205,8 @@ public class Configuration : IPluginConfiguration
// the v23 migration in Plugin.cs and kept deserializable so a 1.5.6 user's // the v23 migration in Plugin.cs and kept deserializable so a 1.5.6 user's
// false value survives one load. Remove in a later schema bump. // false value survives one load. Remove in a later schema bump.
public bool SidebarTabView = true; public bool SidebarTabView = true;
// No reader; see the reconnect backlog.
public bool PrintChangelog = true; public bool PrintChangelog = true;
public bool OnlyPreviewIf; public bool OnlyPreviewIf;
public int PreviewMinimum = 1; public int PreviewMinimum = 1;
@@ -195,7 +219,6 @@ public class Configuration : IPluginConfiguration
public bool ShowTitleBar = true; public bool ShowTitleBar = true;
public bool ShowPopOutTitleBar = true; public bool ShowPopOutTitleBar = true;
public bool DatabaseBattleMessages; public bool DatabaseBattleMessages;
public bool LoadPreviousSession;
public bool FilterIncludePreviousSessions; public bool FilterIncludePreviousSessions;
public bool SortAutoTranslate; public bool SortAutoTranslate;
public bool CollapseDuplicateMessages; public bool CollapseDuplicateMessages;
@@ -212,7 +235,6 @@ public class Configuration : IPluginConfiguration
// UI-11: warn before sending a message that carries plugin-only glyphs. // UI-11: warn before sending a message that carries plugin-only glyphs.
public bool NotifyPluginDisclosure = true; public bool NotifyPluginDisclosure = true;
public bool KeepInputFocus = true; public bool KeepInputFocus = true;
public int MaxLinesToRender = 2_500; // 1-10000
public bool Use24HourClock = true; public bool Use24HourClock = true;
public bool ShowEmotes = true; public bool ShowEmotes = true;
public HashSet<string> BlockedEmotes = []; public HashSet<string> BlockedEmotes = [];
@@ -252,6 +274,7 @@ public class Configuration : IPluginConfiguration
return defaults; return defaults;
} }
// No reader; see the reconnect backlog.
public bool ColorSelectedInputChannelButton = true; public bool ColorSelectedInputChannelButton = true;
public List<Tab> Tabs = []; public List<Tab> Tabs = [];
@@ -278,165 +301,6 @@ public class Configuration : IPluginConfiguration
// v22 field: MainWindow layout mode (sidebar vs. horizontal top tabs). // v22 field: MainWindow layout mode (sidebar vs. horizontal top tabs).
// Initializer doubles as the migration default for configs loaded at v21. // Initializer doubles as the migration default for configs loaded at v21.
public MainWindowLayoutMode MainWindowLayoutMode = MainWindowLayoutMode.Sidebar; public MainWindowLayoutMode MainWindowLayoutMode = MainWindowLayoutMode.Sidebar;
public void UpdateFrom(Configuration other, bool backToOriginal)
{
if (backToOriginal)
{
// NOTE (v1.8.0): this only flips the PopOut flag back. If a future
// caller ever wires UpdateFrom(backToOriginal: true) to a live
// settings-cancel path, that CALL-SITE must also iterate
// ChannelPopoutPool.TryClose over the affected Tab.Identifiers,
// otherwise pool windows stay IsOpen=true while the flag is false
// (orphan window). The pool is not reachable from this POCO by design.
foreach (var tab in Tabs.Where(t => t.PopOut))
tab.PopOut = false;
}
HideChat = other.HideChat;
HideDuringCutscenes = other.HideDuringCutscenes;
HideWhenNotLoggedIn = other.HideWhenNotLoggedIn;
HideWhenUiHidden = other.HideWhenUiHidden;
HideInLoadingScreens = other.HideInLoadingScreens;
HideInBattle = other.HideInBattle;
HideInNewGamePlusMenu = other.HideInNewGamePlusMenu;
HideWhenInactive = other.HideWhenInactive;
InactivityHideTimeout = other.InactivityHideTimeout;
InactivityHideActiveDuringBattle = other.InactivityHideActiveDuringBattle;
InactivityHideChannelsV2 = other.InactivityHideChannelsV2.ToDictionary(
pair => pair.Key,
pair => pair.Value
);
InactivityHideExtraChatAll = other.InactivityHideExtraChatAll;
InactivityHideExtraChatChannels = other.InactivityHideExtraChatChannels.ToHashSet();
ShowHideButton = other.ShowHideButton;
NativeItemTooltips = other.NativeItemTooltips;
ScreenshotMode = other.ScreenshotMode;
PrettierTimestamps = other.PrettierTimestamps;
MoreCompactPretty = other.MoreCompactPretty;
HideSameTimestamps = other.HideSameTimestamps;
ShowNoviceNetwork = other.ShowNoviceNetwork;
SidebarTabView = other.SidebarTabView;
PrintChangelog = other.PrintChangelog;
OnlyPreviewIf = other.OnlyPreviewIf;
PreviewMinimum = other.PreviewMinimum;
PreviewPosition = other.PreviewPosition;
CommandHelpSide = other.CommandHelpSide;
KeybindMode = other.KeybindMode;
LanguageOverride = other.LanguageOverride;
CanMove = other.CanMove;
CanResize = other.CanResize;
ShowTitleBar = other.ShowTitleBar;
ShowPopOutTitleBar = other.ShowPopOutTitleBar;
DatabaseBattleMessages = other.DatabaseBattleMessages;
LoadPreviousSession = other.LoadPreviousSession;
FilterIncludePreviousSessions = other.FilterIncludePreviousSessions;
SortAutoTranslate = other.SortAutoTranslate;
CollapseDuplicateMessages = other.CollapseDuplicateMessages;
CollapseKeepUniqueLinks = other.CollapseKeepUniqueLinks;
SymbolPickerEnabled = other.SymbolPickerEnabled;
PlaySounds = other.PlaySounds;
CustomSoundVolume = other.CustomSoundVolume;
NotifyFailedTell = other.NotifyFailedTell;
NotifyPluginDisclosure = other.NotifyPluginDisclosure;
KeepInputFocus = other.KeepInputFocus;
MaxLinesToRender = other.MaxLinesToRender;
Use24HourClock = other.Use24HourClock;
ShowEmotes = other.ShowEmotes;
// Deep-copy so settings window edits don't leak into live config before Save.
BlockedEmotes = new HashSet<string>(other.BlockedEmotes);
FontsEnabled = other.FontsEnabled;
ItalicEnabled = other.ItalicEnabled;
ExtraGlyphRanges = other.ExtraGlyphRanges;
FontSizeV2 = other.FontSizeV2;
GlobalFontV2 = other.GlobalFontV2;
JapaneseFontV2 = other.JapaneseFontV2;
ItalicFontV2 = other.ItalicFontV2;
SymbolsFontSizeV2 = other.SymbolsFontSizeV2;
TooltipOffset = other.TooltipOffset;
ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value);
ColorSelectedInputChannelButton = other.ColorSelectedInputChannelButton;
// Keep live temp tabs alive across UpdateFrom — a settings save must
// not destroy open tell conversations. Pinned TempTabs are persistent
// and come through `other` like regular tabs; unpinned TempTabs are
// session-only and held from the local state. For persistent tabs
// (incl. pinned), capture live runtime state by Identifier and restore
// it onto the freshly cloned tabs — CurrentChannel is critical because
// the user may have switched channel in-game between settings-open
// and settings-save, and we'd otherwise overwrite that with the
// settings-time snapshot.
var liveUnpinnedTempTabs = Tabs.Where(TabLifecycleHelpers.IsInUnpinnedPool).ToList();
var livePersistentSession = Tabs.Where(t => !TabLifecycleHelpers.IsInUnpinnedPool(t))
.ToDictionary(t => t.Identifier, t => (t.Messages, t.LastSendUnread, t.CurrentChannel));
Tabs = other
.Tabs.Where(t => !t.IsTempTab || t.IsPinned)
.Select(t =>
{
var clone = t.Clone();
if (livePersistentSession.TryGetValue(clone.Identifier, out var live))
{
clone.Messages = live.Messages;
clone.LastSendUnread = live.LastSendUnread;
clone.CurrentChannel = live.CurrentChannel;
}
return clone;
})
.ToList();
Tabs.AddRange(liveUnpinnedTempTabs);
ChatTabForward = other.ChatTabForward;
ChatTabBackward = other.ChatTabBackward;
PrivacyFilterEnabled = other.PrivacyFilterEnabled;
PrivacyPersistChannels = [.. other.PrivacyPersistChannels];
PrivacyPersistUnknownChannels = other.PrivacyPersistUnknownChannels;
RetentionEnabled = other.RetentionEnabled;
RetentionDefaultDays = other.RetentionDefaultDays;
RetentionPerChannelDays = other.RetentionPerChannelDays.ToDictionary(
p => p.Key,
p => p.Value
);
RetentionLastRunAt = other.RetentionLastRunAt;
FirstRunCompleted = other.FirstRunCompleted;
WizardLastShownVersion = other.WizardLastShownVersion;
UseHellionFont = other.UseHellionFont;
ShowHonorificTitleInHeader = other.ShowHonorificTitleInHeader;
ShowHonorificGlow = other.ShowHonorificGlow;
// v1.1.0 theme engine fields
Theme = other.Theme;
WindowOpacity = other.WindowOpacity;
WindowOpacityInactive = other.WindowOpacityInactive;
ReduceMotion = other.ReduceMotion;
UseCompactDensity = other.UseCompactDensity;
EnableAutoTellTabs = other.EnableAutoTellTabs;
AutoTellTabsLimit = other.AutoTellTabsLimit;
AutoTellTabsCompactDisplay = other.AutoTellTabsCompactDisplay;
AutoTellTabsHistoryPreload = other.AutoTellTabsHistoryPreload;
SidebarWidth = other.SidebarWidth;
AutoTellTabsShowGreetedToggle = other.AutoTellTabsShowGreetedToggle;
SeenPopOutInputHint = other.SeenPopOutInputHint;
PopOutInputEnabled = other.PopOutInputEnabled;
SeenPopOutHeaderHint = other.SeenPopOutHeaderHint;
AutoTellTabsOpenAsPopout = other.AutoTellTabsOpenAsPopout;
WorldSuffixMode = other.WorldSuffixMode;
NameFormMode = other.NameFormMode;
MainWindowOpen = other.MainWindowOpen;
SettingsWindowOpen = other.SettingsWindowOpen;
MaxParallelPopouts = other.MaxParallelPopouts;
TellAutoOpenMode = other.TellAutoOpenMode;
TellAutoOpenSwitchAlways = other.TellAutoOpenSwitchAlways;
SidebarAutoSwitchThresholdPx = other.SidebarAutoSwitchThresholdPx;
MainWindowLayoutMode = other.MainWindowLayoutMode;
}
} }
[Serializable] [Serializable]
@@ -448,6 +312,23 @@ public enum TellAutoOpenMode
Popout, Popout,
} }
public static class TellAutoOpenModeExt
{
// The only display name set still in English. It sat inline in ChannelsTab
// as a literal array, which is why it was missed when the rest moved into
// resources; here it is at least in the same place as its peers for the
// localisation pass to pick up.
public static string Name(this TellAutoOpenMode mode) =>
mode switch
{
TellAutoOpenMode.Off => "Off",
TellAutoOpenMode.Sidebar => "Sidebar",
TellAutoOpenMode.TopTab => "Top tab",
TellAutoOpenMode.Popout => "Popout",
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null),
};
}
[Serializable] [Serializable]
public enum MainWindowLayoutMode public enum MainWindowLayoutMode
{ {
@@ -492,9 +373,6 @@ public class Tab
// Optional FontAwesome glyph name; null falls back to TabIconMapping default. // Optional FontAwesome glyph name; null falls back to TabIconMapping default.
public string? Icon = null; public string? Icon = null;
[Obsolete("Removed in favor of SelectedChannels")]
public Dictionary<ChatType, ChatSource> ChatCodes = new();
public Dictionary<ChatType, (ChatSource, ChatSource)> SelectedChannels = new(); public Dictionary<ChatType, (ChatSource, ChatSource)> SelectedChannels = new();
public bool ExtraChatAll; public bool ExtraChatAll;
public HashSet<Guid> ExtraChatChannels = []; public HashSet<Guid> ExtraChatChannels = [];
@@ -511,12 +389,15 @@ public class Tab
public bool CanMove = true; public bool CanMove = true;
public bool CanResize = true; public bool CanResize = true;
public bool IndependentHide; // Six per-tab hide conditions used to live here. Their reader was the
public bool HideDuringCutscenes = true; // pop-out window, which stopped consulting them in cf4705e; the equivalents
public bool HideWhenNotLoggedIn = true; // that survive are the window-level fields of the same name further up this
public bool HideWhenUiHidden = true; // file, and v1.12.0 gave every one of those a control.
public bool HideInLoadingScreens; //
public bool HideInBattle; // Per-tab was the wrong unit anyway: "hide during cutscenes" is a statement
// about the screen, not about one conversation.
//
// HideWhenInactive stays -- the auto-tell service writes it.
public bool HideWhenInactive; public bool HideWhenInactive;
public bool IsTempTab; public bool IsTempTab;
@@ -585,6 +466,34 @@ public class Tab
[NonSerialized] [NonSerialized]
internal float _cardHoverAlpha; internal float _cardHoverAlpha;
// Copy-on-write for the three channel-filter fields. They are read without
// any lock from the pending-message thread, the filter worker and the draw
// thread, and until v1.12.0 nothing ever wrote them after load -- so the
// tab editor is their first writer, and mutating a live Dictionary while
// Matches enumerates it is the classic way to get a wrong answer or an
// exception on somebody else's thread.
//
// Building the replacements and swapping the references means a reader sees
// either the old set or the new one, never half of either.
//
// What this deliberately does not do is make the three writes one atomic
// step. A reader can catch the new dictionary with the old ExtraChat flag
// for a single message. That is harmless: the editor finishes by clearing
// and refiltering every tab, so any message placed by a mixed view is
// reconsidered a moment later. Making it truly atomic would mean one
// reference for all three, and these three are serialized fields with a
// shape the config file already has.
internal void ReplaceChannelFilter(
Dictionary<ChatType, (ChatSource, ChatSource)> selected,
bool extraChatAll,
HashSet<Guid> extraChatChannels
)
{
Volatile.Write(ref SelectedChannels, selected);
Volatile.Write(ref ExtraChatChannels, extraChatChannels);
Volatile.Write(ref ExtraChatAll, extraChatAll);
}
public bool Matches(Message message) public bool Matches(Message message)
{ {
if (!message.Matches(SelectedChannels, ExtraChatAll, ExtraChatChannels)) if (!message.Matches(SelectedChannels, ExtraChatAll, ExtraChatChannels))
@@ -605,14 +514,14 @@ public class Tab
return; return;
Unread += 1; Unread += 1;
if (
message.Matches( // Stamped for every message now. The condition that used to sit here
Plugin.Config.InactivityHideChannelsV2, // filtered on InactivityHideChannels, a setting for the hide-when-
Plugin.Config.InactivityHideExtraChatAll, // inactive feature -- and that feature lost its reader in cf4705e. So
Plugin.Config.InactivityHideExtraChatChannels // which tell tab the auto-tell pool drops first, which is the only
) // thing that reads this stamp, hung on a setting for something that
) // does not happen.
LastActivity = Environment.TickCount64; LastActivity = Environment.TickCount64;
} }
public void Clear() => Messages.Clear(); public void Clear() => Messages.Clear();
@@ -622,6 +531,11 @@ public class Tab
return new Tab return new Tab
{ {
Name = Name, Name = Name,
// Icon feeds the sidebar glyph and a clone round-trip used to drop
// it silently. ChatCodes sat beside it until v1.12.0, carrying data
// for a migration that the v16 schema gate had already made
// unreachable.
Icon = Icon,
SelectedChannels = SelectedChannels.ToDictionary(pair => pair.Key, pair => pair.Value), SelectedChannels = SelectedChannels.ToDictionary(pair => pair.Key, pair => pair.Value),
ExtraChatAll = ExtraChatAll, ExtraChatAll = ExtraChatAll,
ExtraChatChannels = ExtraChatChannels.ToHashSet(), ExtraChatChannels = ExtraChatChannels.ToHashSet(),
@@ -639,12 +553,6 @@ public class Tab
CurrentChannel = CurrentChannel.Clone(), CurrentChannel = CurrentChannel.Clone(),
CanMove = CanMove, CanMove = CanMove,
CanResize = CanResize, CanResize = CanResize,
IndependentHide = IndependentHide,
HideDuringCutscenes = HideDuringCutscenes,
HideWhenNotLoggedIn = HideWhenNotLoggedIn,
HideWhenUiHidden = HideWhenUiHidden,
HideInLoadingScreens = HideInLoadingScreens,
HideInBattle = HideInBattle,
HideWhenInactive = HideWhenInactive, HideWhenInactive = HideWhenInactive,
IsTempTab = IsTempTab, IsTempTab = IsTempTab,
IsPinned = IsPinned, IsPinned = IsPinned,
+29 -2
View File
@@ -125,6 +125,15 @@ public static class EmoteCache
try try
{ {
var global = await Client.GetAsync(GlobalEmotes, ct); var global = await Client.GetAsync(GlobalEmotes, ct);
if (!global.IsSuccessStatusCode)
{
// Nothing usable at all -- let the catch below reset to Unloaded
// so a later trigger can retry.
throw new HttpRequestException(
$"BetterTTV global emotes returned {(int)global.StatusCode}."
);
}
var globalList = await global.Content.ReadAsStringAsync(ct); var globalList = await global.Content.ReadAsStringAsync(ct);
foreach (var emote in JsonSerializer.Deserialize<Emote[]>(globalList)!) foreach (var emote in JsonSerializer.Deserialize<Emote[]>(globalList)!)
@@ -135,9 +144,27 @@ public static class EmoteCache
for (var i = 0; i < 15; i++) for (var i = 0; i < 15; i++)
{ {
var top = await Client.GetAsync(Top100Emotes.Format(BetterTTV, lastId), ct); var top = await Client.GetAsync(Top100Emotes.Format(BetterTTV, lastId), ct);
// The shared-emote endpoint went behind authentication and now
// answers 403 with a JSON object. Deserializing that as a list
// threw on every single start, and took the global emotes -- which
// still work -- down with it. The global set is the useful half
// anyway, so a failure here stops paging instead of the load.
if (!top.IsSuccessStatusCode)
{
Plugin.LogProxy.Warning(
$"BetterTTV shared emotes unavailable ({(int)top.StatusCode}); "
+ "continuing with global emotes only."
);
break;
}
var topList = await top.Content.ReadAsStringAsync(ct); var topList = await top.Content.ReadAsStringAsync(ct);
var jsonList = JsonSerializer.Deserialize<List<Top100>>(topList)!; var jsonList = JsonSerializer.Deserialize<List<Top100>>(topList);
if (jsonList is not { Count: > 0 })
break;
// BetterTTV occasionally returns entries with a null Code; // BetterTTV occasionally returns entries with a null Code;
// skip them so a single bad row doesn't break the whole cache. // skip them so a single bad row doesn't break the whole cache.
foreach (var emote in jsonList) foreach (var emote in jsonList)
@@ -147,7 +174,7 @@ public static class EmoteCache
) )
Cache.TryAdd(emote.Emote.Code, emote.Emote); Cache.TryAdd(emote.Emote.Code, emote.Emote);
lastId = jsonList.Last().Id; lastId = jsonList[^1].Id;
} }
SortedCodeArray = Cache.Keys.Order().ToArray(); SortedCodeArray = Cache.Keys.Order().ToArray();
+107 -17
View File
@@ -1,6 +1,7 @@
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using HellionChat.Code; using HellionChat.Code;
using HellionChat.Util;
namespace HellionChat.Export; namespace HellionChat.Export;
@@ -33,9 +34,23 @@ internal static class ExportFormatExt
} }
// Serializes message snapshots to Markdown, JSON, or CSV. // Serializes message snapshots to Markdown, JSON, or CSV.
// Caller handles pre-filtering except sender substring, which requires deserialized SeString.TextValue. //
// Text comes from the chunk lists, never from SenderSource/ContentSource. Those
// are raw SeStrings, and reading TextValue on one containing an auto-translate
// phrase reaches SeStringEvaluator, which asserts it is on the main thread and
// throws unconditionally when a macro resolves a global number. An export runs on
// a worker, so that would abort it partway and leave half a file behind.
//
// The chunks are already resolved: ChunkUtil turns auto-translate into text at
// ingest, and the full-text index reads them exactly this way. Same strings, no
// evaluator, no thread affinity.
//
// The caller pre-filters by channel and date via StreamForExport; only the sender
// substring is applied here.
internal static class MessageExporter internal static class MessageExporter
{ {
private static readonly UTF8Encoding Utf8NoBom = new(encoderShouldEmitUTF8Identifier: false);
internal record FilterDescription( internal record FilterDescription(
IReadOnlyCollection<int>? ChatTypes, IReadOnlyCollection<int>? ChatTypes,
DateTimeOffset? From, DateTimeOffset? From,
@@ -50,22 +65,83 @@ internal static class MessageExporter
FilterDescription filter FilterDescription filter
) )
{ {
// Rejected before the file is touched. The old order opened the stream
// first, so an unknown format left a zero-byte file where the user's
// previous export had been.
if (!Enum.IsDefined(format))
throw new ArgumentOutOfRangeException(nameof(format), format, null);
var matching = filter.SenderSubstring is { Length: > 0 } needle var matching = filter.SenderSubstring is { Length: > 0 } needle
? messages.Where(m => MatchesSender(m, needle)) ? messages.Where(m => MatchesSender(m, needle))
: messages; : messages;
using var writer = new StreamWriter(path, append: false, encoding: Encoding.UTF8); // Written beside the target and moved into place at the end. A crash or
return format switch // an unplugged drive halfway through would otherwise leave a file that
// opens fine and is quietly incomplete -- and this is the path a GDPR
// access request goes out on, where "looks complete" is the dangerous
// failure.
var temp = path + ".part";
int written;
try
{ {
ExportFormat.Markdown => WriteMarkdown(writer, matching, filter), // Encoding.UTF8 writes a byte order mark, and that is not a
ExportFormat.Json => WriteJson(writer, matching, filter), // cosmetic detail here: a leading U+FEFF makes the JSON invalid for
ExportFormat.Csv => WriteCsv(writer, matching, filter), // every strict parser, Python's json.load included. CSV is the one
_ => throw new ArgumentOutOfRangeException(nameof(format), format, null), // 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
{
ExportFormat.Markdown => WriteMarkdown(writer, matching, filter),
ExportFormat.Json => WriteJson(writer, matching, filter),
_ => WriteCsv(writer, matching, filter),
};
}
// An export that matched nothing does not replace anything. The
// file still has a header and a footer, so moving it would put a
// near-empty file where the user's previous export was -- and then
// report "no message matched the filter", which reads as "nothing
// happened". Dalamud's save dialog has no overwrite confirmation to
// fall back on.
if (written == 0)
{
TryDeleteTemp(temp);
return 0;
}
File.Move(temp, path, overwrite: true);
return written;
}
catch
{
TryDeleteTemp(temp);
throw;
}
}
// Best effort: the export already failed, and a leftover .part file is a
// smaller problem than masking the original exception with an IO one.
private static void TryDeleteTemp(string temp)
{
try
{
if (File.Exists(temp))
File.Delete(temp);
}
catch (IOException) { }
catch (UnauthorizedAccessException) { }
} }
private static bool MatchesSender(Message m, string needle) => private static bool MatchesSender(Message m, string needle) =>
m.SenderSource.TextValue.Contains(needle, StringComparison.OrdinalIgnoreCase); SenderText(m).Contains(needle, StringComparison.OrdinalIgnoreCase);
private static string SenderText(Message m) => ChunkUtil.ToRawString(m.Sender);
private static string ContentText(Message m) => ChunkUtil.ToRawString(m.Content);
private static int WriteMarkdown( private static int WriteMarkdown(
StreamWriter w, StreamWriter w,
@@ -94,8 +170,8 @@ internal static class MessageExporter
} }
var chatType = (ChatType)(ushort)m.Code.Type; var chatType = (ChatType)(ushort)m.Code.Type;
var sender = m.SenderSource.TextValue.Trim().Trim('<', '>', '[', ']', ':').Trim(); var sender = SenderText(m).Trim().Trim('<', '>', '[', ']', ':').Trim();
var content = m.ContentSource.TextValue; var content = ContentText(m);
if (string.IsNullOrEmpty(sender)) if (string.IsNullOrEmpty(sender))
w.WriteLine($"**[{localDate:HH:mm}] {chatType}:** {content}"); w.WriteLine($"**[{localDate:HH:mm}] {chatType}:** {content}");
@@ -170,12 +246,17 @@ internal static class MessageExporter
w.Write($",\"date\":\"{m.Date.ToString("O", CultureInfo.InvariantCulture)}\""); w.Write($",\"date\":\"{m.Date.ToString("O", CultureInfo.InvariantCulture)}\"");
w.Write($",\"chat_type\":{(int)m.Code.Type}"); w.Write($",\"chat_type\":{(int)m.Code.Type}");
w.Write($",\"chat_type_name\":\"{chatType}\""); w.Write($",\"chat_type_name\":\"{chatType}\"");
w.Write($",\"source_kind\":{m.Code.Source}"); // Cast, not interpolate. These are XivChatRelationKind, and string
w.Write($",\"target_kind\":{m.Code.Target}"); // interpolation of an enum writes the member name -- so every
// message with a recognised relation produced
// "source_kind":LocalPlayer, which no parser accepts. This is the
// file an access request goes out on.
w.Write($",\"source_kind\":{(int)m.Code.Source}");
w.Write($",\"target_kind\":{(int)m.Code.Target}");
w.Write($",\"receiver\":{m.Receiver}"); w.Write($",\"receiver\":{m.Receiver}");
w.Write($",\"content_id\":{m.ContentId}"); w.Write($",\"content_id\":{m.ContentId}");
w.Write($",\"sender\":{JsonString(m.SenderSource.TextValue)}"); w.Write($",\"sender\":{JsonString(SenderText(m))}");
w.Write($",\"content\":{JsonString(m.ContentSource.TextValue)}"); w.Write($",\"content\":{JsonString(ContentText(m))}");
w.Write("}"); w.Write("}");
} }
@@ -203,9 +284,9 @@ internal static class MessageExporter
w.Write(','); w.Write(',');
w.Write(CsvString(chatType.ToString())); w.Write(CsvString(chatType.ToString()));
w.Write(','); w.Write(',');
w.Write(CsvString(m.SenderSource.TextValue)); w.Write(CsvString(SenderText(m)));
w.Write(','); w.Write(',');
w.Write(CsvString(m.ContentSource.TextValue)); w.Write(CsvString(ContentText(m)));
w.Write(','); w.Write(',');
w.Write(m.Receiver); w.Write(m.Receiver);
w.Write(','); w.Write(',');
@@ -258,8 +339,17 @@ internal static class MessageExporter
private static string CsvString(string s) private static string CsvString(string s)
{ {
// Leading =, +, - and @ make a spreadsheet treat the cell as a formula.
// Every value here is text somebody else typed into a chat channel, and
// this file exists to be opened in Excel, so a prefixed apostrophe goes
// in front. It is the standard defence and it costs one character that
// spreadsheets hide.
if (s.Length > 0 && s[0] is '=' or '+' or '-' or '@' or '\t' or '\r')
s = "'" + s;
if (s.IndexOfAny(['"', ',', '\n', '\r']) < 0) if (s.IndexOfAny(['"', ',', '\n', '\r']) < 0)
return s; return s;
return "\"" + s.Replace("\"", "\"\"") + "\""; return "\"" + s.Replace("\"", "\"\"") + "\"";
} }
} }
+94 -61
View File
@@ -61,6 +61,17 @@ public sealed class FontManager : IDisposable
private ushort[] Ranges = []; private ushort[] Ranges = [];
private ushort[] JpRange = []; private ushort[] JpRange = [];
// B1: trimmed remainder the NotoSansCjk fallback is the sole source for
// (Hangul + full Han); excludes the Default/Latin block already merged
// by the global font, so the fallback no longer re-merges the full Ranges array.
private ushort[] CjkFallbackGlyphRange = [];
// Report accessor for the ctor self-test: built glyph-range array lengths so
// the step can show the B1 dedup effect (a small trimmed fallback vs the large
// primary range) in its on-disk report instead of a bare Pass.
internal (int Ranges, int JpRange, int CjkFallback) GlyphRangeLengths =>
(Ranges.Length, JpRange.Length, CjkFallbackGlyphRange.Length);
public static readonly HashSet<float> AxisFontSizeList = public static readonly HashSet<float> AxisFontSizeList =
[ [
9.6f, 9.6f,
@@ -170,6 +181,38 @@ public sealed class FontManager : IDisposable
// Instance method so Ranges / JpRange are reachable without parameter // Instance method so Ranges / JpRange are reachable without parameter
// plumbing; PascalCase field names follow the existing class style. // plumbing; PascalCase field names follow the existing class style.
// B1: shared CJK + symbols tail for both the regular and italic delegate
// fonts. Earlier-merged fonts win for shared codepoints (imgui MergeMode),
// so this runs AFTER the primary font is set as config.MergeFont. The CJK
// fallback is the sole Hangul/Simplified-Han source when UseHellionFont=true
// (global=Inter-Light), so it stays in the chain — only its glyph range is
// trimmed (CjkFallbackGlyphRange) to drop the Default-block/endonym overlap.
// The Japanese merge keeps its own configured size and the full JpRange (which
// owns Traditional Han such as 體 U+9AD4), so japanese↔fallback no longer overlap.
private void AddCjkAndSymbols(
IFontAtlasBuildToolkitPreBuild tk,
SafeFontConfig config,
float basePt
)
{
config.SizePt = Plugin.Config.JapaneseFontV2.SizePt;
config.GlyphRanges = JpRange;
AddFontWithFallback(tk, Plugin.Config.JapaneseFontV2.FontId, config, "japanese");
// NotoSansCjk fallback, trimmed to CjkFallbackGlyphRange (B1). Merged last so earlier fonts win.
config.SizePt = basePt;
config.GlyphRanges = CjkFallbackGlyphRange;
AddFontWithFallback(
tk,
new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
config,
"noto-cjk-fallback"
);
config.SizePt = ResolveSymbolsFontPt();
tk.AddGameSymbol(config);
}
private IFontHandle BuildRegularFontHandle(IFontAtlas atlas) => private IFontHandle BuildRegularFontHandle(IFontAtlas atlas) =>
atlas.NewDelegateFontHandle(e => atlas.NewDelegateFontHandle(e =>
e.OnPreBuild(tk => e.OnPreBuild(tk =>
@@ -183,25 +226,7 @@ public sealed class FontManager : IDisposable
? tk.AddFontFromMemory(bundledBytes, config, "Inter-Light") ? tk.AddFontFromMemory(bundledBytes, config, "Inter-Light")
: AddFontWithFallback(tk, Plugin.Config.GlobalFontV2.FontId, config, "global"); : AddFontWithFallback(tk, Plugin.Config.GlobalFontV2.FontId, config, "global");
config.SizePt = Plugin.Config.JapaneseFontV2.SizePt; AddCjkAndSymbols(tk, config, basePt);
config.GlyphRanges = JpRange;
AddFontWithFallback(tk, Plugin.Config.JapaneseFontV2.FontId, config, "japanese");
// v1.5.3: NotoSansCjk fallback covers Hangul, Simplified-Chinese
// -specific Han (e.g. 简) and other CJK glyphs that the primary
// (Inter Light / global font) and the FFXIV Japanese font do not
// ship. Merged last so earlier fonts win for shared codepoints.
config.SizePt = basePt;
config.GlyphRanges = Ranges;
AddFontWithFallback(
tk,
new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
config,
"noto-cjk-fallback"
);
config.SizePt = ResolveSymbolsFontPt();
tk.AddGameSymbol(config);
tk.Font = config.MergeFont; tk.Font = config.MergeFont;
}) })
@@ -223,22 +248,7 @@ public sealed class FontManager : IDisposable
"italic" "italic"
); );
config.SizePt = Plugin.Config.JapaneseFontV2.SizePt; AddCjkAndSymbols(tk, config, Plugin.Config.ItalicFontV2.SizePt);
config.GlyphRanges = JpRange;
AddFontWithFallback(tk, Plugin.Config.JapaneseFontV2.FontId, config, "japanese");
// v1.5.3: NotoSansCjk fallback (see BuildRegularFontHandle).
config.SizePt = Plugin.Config.ItalicFontV2.SizePt;
config.GlyphRanges = Ranges;
AddFontWithFallback(
tk,
new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
config,
"noto-cjk-fallback"
);
config.SizePt = ResolveSymbolsFontPt();
tk.AddGameSymbol(config);
tk.Font = config.MergeFont; tk.Font = config.MergeFont;
}) })
@@ -282,7 +292,11 @@ public sealed class FontManager : IDisposable
private unsafe void SetUpRanges() private unsafe void SetUpRanges()
{ {
ushort[] BuildRange(IReadOnlyList<ushort>? chars, params nint[] ranges) ushort[] BuildRange(
IReadOnlyList<ushort>? chars,
bool includeCommonExtras,
params nint[] ranges
)
{ {
var builder = new ImFontGlyphRangesBuilderPtr(ImGuiNative.ImFontGlyphRangesBuilder()); var builder = new ImFontGlyphRangesBuilderPtr(ImGuiNative.ImFontGlyphRangesBuilder());
foreach (var range in ranges) foreach (var range in ranges)
@@ -300,33 +314,43 @@ public sealed class FontManager : IDisposable
} }
} }
// Ingame supported ranges // Common extras (Axis ingame glyphs, endonyms, enclosed alphanumerics)
var reader = new FdtReader(Plugin.DataManager.GetFile("common/font/axis_12.fdt")!.Data); // belong to the primary/Japanese ranges only. The trimmed CJK fallback
foreach (var c in reader.Glyphs) // (B1) skips them so it stays a pure Hangul/Simplified-Han remainder and
builder.AddChar(c.Char); // does not re-merge the Default-block work the global font already did.
if (includeCommonExtras)
{
// Ingame supported ranges
var reader = new FdtReader(
Plugin.DataManager.GetFile("common/font/axis_12.fdt")!.Data
);
foreach (var c in reader.Glyphs)
builder.AddChar(c.Char);
// French // French
// Romanian // Romanian
builder.AddText("Œœ"); builder.AddText("Œœ");
builder.AddText("ĂăÂâÎîȘșȚț"); builder.AddText("ĂăÂâÎîȘșȚț");
// v1.5.3: language-dropdown endonyms. The dropdown renders // v1.5.3: language-dropdown endonyms. The dropdown renders
// with the currently active font range; without these glyphs // with the currently active font range; without these glyphs
// a user on an English UI cannot read non-Latin language names // a user on an English UI cannot read non-Latin language names
// before switching. Auto-activation in Settings.Apply then // before switching. Auto-activation in Settings.Apply then
// pulls in the full ExtraGlyphRange for the chosen locale. // pulls in the full ExtraGlyphRange for the chosen locale.
builder.AddText( builder.AddText(
"Català Čeština Dansk Deutsch Ελληνικά English Español Suomi" "Català Čeština Dansk Deutsch Ελληνικά English Español Suomi"
+ " Français Magyar Italiano 日本語 한국어 Norsk bokmål Nederlands" + " Français Magyar Italiano 日本語 한국어 Norsk bokmål Nederlands"
+ " Polski Português Brasil (Portugal) Română Русский Svenska" + " Polski Português Brasil (Portugal) Română Русский Svenska"
+ " Türkçe Українська 简体中文 繁體中文" + " Türkçe Українська 简体中文 繁體中文"
); );
// "Enclosed Alphanumerics" (partial) https://www.compart.com/en/unicode/block/U+2460 // "Enclosed Alphanumerics" (partial) https://www.compart.com/en/unicode/block/U+2460
for (var i = 0x2460; i <= 0x24B5; i++) for (var i = 0x2460; i <= 0x24B5; i++)
builder.AddChar((char)i); builder.AddChar((char)i);
builder.AddChar('⓪');
}
builder.AddChar('⓪');
return builder.BuildRangesToArray(); return builder.BuildRangesToArray();
} }
@@ -356,8 +380,17 @@ public sealed class FontManager : IDisposable
} }
} }
Ranges = BuildRange(customChars.Count > 0 ? customChars : null, ranges.ToArray()); Ranges = BuildRange(
JpRange = BuildRange(GlyphRangesJapanese.GlyphRanges); customChars.Count > 0 ? customChars : null,
includeCommonExtras: true,
ranges.ToArray()
);
JpRange = BuildRange(GlyphRangesJapanese.GlyphRanges, includeCommonExtras: true);
// B1: the fallback gets only the trimmed Hangul/Simplified-Han remainder.
// No Default block, no endonyms — those are already merged by the global and
// Japanese fonts, so re-merging them on the fallback was wasted atlas work.
CjkFallbackGlyphRange = BuildRange(CjkFallbackRange.Pairs, includeCommonExtras: false);
} }
// Add font with fallback to NotoSansCjkRegular if unavailable // Add font with fallback to NotoSansCjkRegular if unavailable
+43 -16
View File
@@ -317,6 +317,30 @@ internal sealed unsafe class Chat : IDisposable
ReplyInSelectedChatModeHook!.Original(agent); ReplyInSelectedChatModeHook!.Original(agent);
} }
// Pure /tell-prefill command builder, shared by the two native SetTellTarget
// detours and the PayloadHandler Send-Tell payload. Empty/null world drops the
// @World suffix (matches the old IsNullOrEmpty guard); trailing space lets the
// user type straight after. internal static so the Build-Suite can pin it
// frame-free. TEST-MIRROR: ../../../Hellion Build test/GameFunctions/PrefillTellCommandTests.cs
internal static string BuildTellCommand(string name, string? world)
{
var command = $"/tell {name}";
if (!string.IsNullOrEmpty(world))
command += $"@{world}";
command += " ";
return command;
}
// Prefills + focuses our own input bar with a /tell command. The DI/Dalamud
// plumbing (Plugin.InputBar) lives here; the string assembly is BuildTellCommand.
// The in-foray TellSpecial routing (SetEurekaTellChannel) is NOT this helper's
// job — it stays at the call-site (v1.8.1 deferral).
private void PrefillTellInput(string name, string? world)
{
Plugin.InputBar.SetPendingMessage(BuildTellCommand(name, world));
Plugin.InputBar.Activate = true;
}
private bool SetContextTellTarget( private bool SetContextTellTarget(
RaptureShellModule* a1, RaptureShellModule* a1,
Utf8String* playerName, Utf8String* playerName,
@@ -334,14 +358,10 @@ internal sealed unsafe class Chat : IDisposable
// "Send Tell" payload menu does (PayloadHandler), then focus. Prefill- // "Send Tell" payload menu does (PayloadHandler), then focus. Prefill-
// only — no tab switch, no ChatActivatedArgs revival (Flo decision // only — no tab switch, no ChatActivatedArgs revival (Flo decision
// 2026-06-15). The game supplies worldName here, so no sheet lookup. // 2026-06-15). The game supplies worldName here, so no sheet lookup.
var tellName = playerName->ToString(); PrefillTellInput(
var tellWorld = worldName != null ? worldName->ToString() : string.Empty; playerName->ToString(),
var tellCommand = $"/tell {tellName}"; worldName != null ? worldName->ToString() : null
if (!string.IsNullOrEmpty(tellWorld)) );
tellCommand += $"@{tellWorld}";
tellCommand += " ";
Plugin.InputBar.SetPendingMessage(tellCommand);
Plugin.InputBar.Activate = true;
} }
return SetChatLogTellTargetHook!.Original( return SetChatLogTellTargetHook!.Original(
@@ -374,14 +394,10 @@ internal sealed unsafe class Chat : IDisposable
// In-foray right-click -> Send Tell: same prefill path as the non-foray // In-foray right-click -> Send Tell: same prefill path as the non-foray
// tell. The foray-specific TellSpecial channel routing stays deferred // tell. The foray-specific TellSpecial channel routing stays deferred
// (v1.8.1, SetEurekaTellChannel) — prefill-only here (Flo decision 2026-06-15). // (v1.8.1, SetEurekaTellChannel) — prefill-only here (Flo decision 2026-06-15).
var forayName = playerName->ToString(); PrefillTellInput(
var forayWorld = worldName != null ? worldName->ToString() : string.Empty; playerName->ToString(),
var forayCommand = $"/tell {forayName}"; worldName != null ? worldName->ToString() : null
if (!string.IsNullOrEmpty(forayWorld)) );
forayCommand += $"@{forayWorld}";
forayCommand += " ";
Plugin.InputBar.SetPendingMessage(forayCommand);
Plugin.InputBar.Activate = true;
} }
ContextMenuTellInForayHook!.Original( ContextMenuTellInForayHook!.Original(
@@ -435,6 +451,17 @@ internal sealed unsafe class Chat : IDisposable
uint currentIndex, uint currentIndex,
RotateMode rotate, RotateMode rotate,
Func<uint, bool> validFn Func<uint, bool> validFn
) => RotateLinkshellIndex(currentIndex, rotate, validFn);
// Pure index-stepper (Dalamud-free): wrap (8 + currentIndex + delta) % 8 and return the
// first index validFn accepts within 8 iterations, else null. Extracted so the
// modulo/termination logic is unit-testable with a synthetic predicate; the
// production caller binds validFn to InfoProxyLinkshell (in-game only).
// TEST-MIRROR: ../../../Hellion Build test/_Helpers/RotateLinkshellIndexTests.cs
internal static uint? RotateLinkshellIndex(
uint currentIndex,
RotateMode rotate,
Func<uint, bool> validFn
) )
{ {
if (rotate == RotateMode.None) if (rotate == RotateMode.None)
+173 -28
View File
@@ -7,6 +7,7 @@ using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using HellionChat.Code; using HellionChat.Code;
using HellionChat.GameFunctions.Types; using HellionChat.GameFunctions.Types;
using HellionChat.Ui.Windows;
using HellionChat.Util; using HellionChat.Util;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using ModifierFlag = HellionChat.GameFunctions.Types.ModifierFlag; using ModifierFlag = HellionChat.GameFunctions.Types.ModifierFlag;
@@ -504,41 +505,185 @@ internal unsafe class KeybindManager : IDisposable
if (!KeybindsToIntercept.TryGetValue(currentBest.Item2, out var info)) if (!KeybindsToIntercept.TryGetValue(currentBest.Item2, out var info))
return; return;
// Re-surface the chat-activation entry point retired in v1.6.0: a chat-open // Resolve the surface this keybind acts on FIRST: a focused pop-out otherwise
// keybind shows + focuses the window, restoring it from a user-hide or a // the main window. Channel-set/REPLY/prefill all write here so the action
// closed state. // follows the input the user is typing in (C3 full tail rebuild, OD-1).
Plugin.Instance.MainWindow?.ActivateChat(); var (targetWindow, targetTab) = ResolveKeybindTarget();
// Direct channel-switch binds (CMD_SAY/PARTY/numbered linkshells/…): switch the // Surface + focus the resolved target ONCE, before routing. Main: ActivateChat
// game channel AND mirror it onto the active tab so the input pill shows the // re-surfaces it from a hide/closed state (the chat-activation entry point
// real send target (pill-sync, Flo decision 2026-06-15). Rotation binds (REPLY / // retired in v1.6.0). Pop-out: arm only its focus — NOT ActivateChat, which
// linkshell-cycle, Rotate != None) are skipped; the temp-vs-permanent distinction // would yank the main window to front and un-hide it on every pop-out-targeted
// (v1.5.6's UseTempChannel / info.Permanent) collapses to one permanent-style // keybind (OD-1: stay where the user types). Exactly one window arms focus per
// switch here — restoring it is the keybind-routing follow-cycle. // keybind, so the next frame has no SetKeyboardFocusHere race.
if (info.Channel is { } channel && info.Rotate == RotateMode.None) if (targetWindow is ChannelPopoutWindow)
targetWindow.RequestInputFocus();
else
Plugin.Instance.MainWindow?.ActivateChat();
// The routing tail makes native game calls (GetTellHistoryInfo, UIModule,
// RotateLinkshellHistory) on the framework tick — wrap it so one bad frame logs
// instead of throwing into Dalamud's update loop (v1.5.6 parity).
try
{ {
Plugin.Instance.Functions.Chat.SetChannel(channel); if (info.Channel is { } channel && info.Rotate == RotateMode.None)
// Only mirror onto the tab when the game actually accepted the switch — an
// empty linkshell slot leaves the game channel untouched, so the pill must
// stay put rather than show a target the game will not send to.
if (
Chat.IsChannelOrExistingLinkshell(channel)
&& Plugin.Instance.MainWindow?.ActiveTab is { } activeTab
)
{ {
activeTab.CurrentChannel.SetChannel(channel); // Direct channel-switch binds (CMD_SAY/PARTY/numbered linkshells/…): switch
activeTab.CurrentChannel.TellTarget = null; // the game channel AND mirror it onto the resolved tab so the input pill
activeTab.CurrentChannel.ResetTempChannel(); // shows the real send target (pill-sync, Flo decision 2026-06-15).
Plugin.Instance.Functions.Chat.SetChannel(channel);
// Only mirror onto the tab when the game actually accepted the switch — an
// empty linkshell slot leaves the game channel untouched, so the pill must
// stay put rather than show a target the game will not send to.
if (Chat.IsChannelOrExistingLinkshell(channel) && targetTab is { } directTab)
{
directTab.CurrentChannel.SetChannel(channel);
directTab.CurrentChannel.TellTarget = null;
directTab.CurrentChannel.ResetTempChannel();
}
} }
} else if (info.Channel is { } rotateChannel && info.Rotate != RotateMode.None)
{
// Rotation binds (REPLY / linkshell-cycle). Ported from v1.5.6's
// ChatLogWindow.Activated (1d3b429:240-334) without the ChatActivatedArgs
// indirection (gone in the rewrite). Writes onto the resolved surface's
// tab (C2/C3 shared target), not Plugin.CurrentTab.
if (targetTab is { } rotTab)
{
var targetChannel = (InputChannel?)rotateChannel;
// Prefill text binds (CMD_COMMAND seeds "/"): drop the token into our input. // REPLY rotation: the reply target is ALWAYS temp (never permanent —
if (info.Text is { } text) // a permanent reply would leak the partner onto the tab) and ALWAYS
Plugin.Instance.InputBar.SetPendingMessage(text); // TellReason.Reply. info.Permanent does not gate this step; only the
// channel-set tail below honours the _ALWAYS binds' permanence.
if (rotateChannel == InputChannel.Tell)
{
var idx =
rotTab.CurrentChannel.TempChannel != InputChannel.Tell ? 0
: info.Rotate == RotateMode.Reverse ? -1
: 1;
var tellInfo = Plugin.Instance.Functions.Chat.GetTellHistoryInfo(idx);
if (tellInfo != null)
rotTab.CurrentChannel.TempTellTarget = new TellTarget(
tellInfo.Name,
tellInfo.World,
tellInfo.ContentId,
TellReason.Reply
);
}
else
{
// Cycling AWAY from Tell to a linkshell: drop any stale permanent
// tell target so a typed line cannot silently route to the old
// partner (v1.5.6 ChatLogWindow.cs:280, privacy guard).
rotTab.CurrentChannel.TellTarget = null;
}
// LS/CWLS cycle: permanent rotates the game's own history and reads the
// landed cycle index back; temp resolves the next valid linkshell index
// without touching game state. Both leave targetChannel null on failure
// (no valid linkshell in 8 iterations) so the tail below logs + skips.
if (rotateChannel is InputChannel.Linkshell1 or InputChannel.CrossLinkshell1)
{
var module = UIModule.Instance();
if (info.Permanent)
{
if (rotateChannel == InputChannel.Linkshell1)
{
Chat.RotateLinkshellHistory(info.Rotate);
targetChannel = rotateChannel + (uint)module->LinkshellCycle;
}
else
{
Chat.RotateCrossLinkshellHistory(info.Rotate);
targetChannel =
rotateChannel + (uint)module->CrossWorldLinkshellCycle;
}
}
else
{
targetChannel = Chat.ResolveTempInputChannel(
rotTab.CurrentChannel.TempChannel,
rotateChannel,
info.Rotate
);
}
}
// Shared channel-set tail (runs for Tell too: IsChannelOrExistingLinkshell
// is true for Tell and targetChannel stays Tell). Permanent => commit the
// game channel; temp => arm UseTempChannel/TempChannel only. This is the
// ONLY place info.Permanent decides temp vs permanent for the channel.
if (
targetChannel is null
|| !Chat.IsChannelOrExistingLinkshell(targetChannel.Value)
)
{
_logger.LogWarning(
"Rotation channel resolved to an invalid value '{Channel}', ignoring",
targetChannel
);
return;
}
if (info.Permanent)
{
// KB-01 (1.5.6 parity, ChatLogWindow.SetChannel 1d3b429:1476-1479):
// committing the game channel also pre-targets the game's native input.
// Forward the tab's reply target for Tell so the partner is armed
// game-side (ChangeChatChannel code 17); null for a linkshell —
// targetChannel is the FINAL resolved value (9..16/19..26 for LS, never
// 0=Tell), so a stale TempTellTarget can never flip an LS cycle to Tell.
var gameTarget =
targetChannel.Value == InputChannel.Tell
? rotTab.CurrentChannel.TempTellTarget
?? rotTab.CurrentChannel.TellTarget
: null;
Plugin.Instance.Functions.Chat.SetChannel(targetChannel.Value, gameTarget);
rotTab.CurrentChannel.SetChannel(targetChannel.Value);
}
else
{
rotTab.CurrentChannel.UseTempChannel = true;
rotTab.CurrentChannel.TempChannel = targetChannel.Value;
}
}
}
// Prefill text binds (CMD_COMMAND seeds "/"): the token always goes to the
// main InputBar (the focus contract does not expose pop-out buffers); a
// focused pop-out already received focus above, so only token routing matters
// here (documented scope limit, OD-1).
if (info.Text is { } text)
Plugin.Instance.InputBar.SetPendingMessage(text);
}
catch (Exception ex)
{
_logger.LogError(ex, "Keybind routing failed for channel {Channel}", info.Channel);
}
} }
// Pop-out input-bar focus-forward stays deferred (no focus contract yet) — // Resolve which chat surface a keybind action targets: the open pop-out whose
// main-window tabs only. // input currently has focus, otherwise the main window. C2/C3 share this so a
// channel-switch/REPLY/prefill follows the surface the user is typing in. The
// returned tab is that surface's bound tab (pop-out: Bound; main: ActiveTab).
// Null tab => skip the tab-write (early-load window where no tab exists yet).
private (IFocusableChatWindow Window, Tab? Tab) ResolveKeybindTarget()
{
foreach (var popout in Plugin.Instance.ChannelPopoutPool.Instances)
{
if (popout.Bound is { } bound && popout.IsOpen && popout.HasFocusedInput)
return (popout, bound);
}
var main = Plugin.Instance.MainWindow;
return (main!, main?.ActiveTab);
}
// Tab-delta keybinds (ChatTabForward/Backward) stay main-window-only by design:
// a channel-bound pop-out has no tab list to cycle (OD-1). The focus contract is
// consumed by the channel-set/REPLY/prefill tail, not here.
private void DispatchTabDelta(int delta) private void DispatchTabDelta(int delta)
{ {
Plugin.Instance.MainWindow?.ChangeTabDelta(delta); Plugin.Instance.MainWindow?.ChangeTabDelta(delta);
+3 -3
View File
@@ -1,7 +1,7 @@
<Project Sdk="Dalamud.NET.Sdk/15.0.0"> <Project Sdk="Dalamud.NET.Sdk/15.0.0">
<PropertyGroup> <PropertyGroup>
<!-- Independent versioning; see yaml changelog for upstream Chat 2 base --> <!-- Independent versioning; see yaml changelog for upstream Chat 2 base -->
<Version>1.8.8</Version> <Version>1.12.0</Version>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<!-- Use lock file to pin exact versions --> <!-- Use lock file to pin exact versions -->
@@ -14,7 +14,7 @@
<ItemGroup> <ItemGroup>
<!-- Closed ranges prevent surprise major bumps during lock file regeneration --> <!-- Closed ranges prevent surprise major bumps during lock file regeneration -->
<PackageReference Include="MessagePack" Version="[3.1.4, 4.0.0)" /> <PackageReference Include="MessagePack" Version="[3.1.4, 4.0.0)" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.7" /> <PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.8" />
<!-- v1.5.0 DI-container foundation; matches Lightless pin (Hosting 10.0.7) --> <!-- v1.5.0 DI-container foundation; matches Lightless pin (Hosting 10.0.7) -->
<PackageReference <PackageReference
Include="Microsoft.Extensions.DependencyInjection" Include="Microsoft.Extensions.DependencyInjection"
@@ -30,7 +30,7 @@
Media-Foundation-based codecs do not). Using the sub-package avoids pulling in Media-Foundation-based codecs do not). Using the sub-package avoids pulling in
NAudio.WinForms (which requires WindowsDesktop and does not build on Linux hosts). NAudio.WinForms (which requires WindowsDesktop and does not build on Linux hosts).
WaveOutEvent and WaveFileReader both live in NAudio.WinMM + NAudio.Core. --> WaveOutEvent and WaveFileReader both live in NAudio.WinMM + NAudio.Core. -->
<PackageReference Include="NAudio.WinMM" Version="2.2.1" /> <PackageReference Include="NAudio.WinMM" Version="2.3.0" />
<PackageReference Include="Pidgin" Version="[3.5.1, 4.0.0)" /> <PackageReference Include="Pidgin" Version="[3.5.1, 4.0.0)" />
<PackageReference Include="SixLabors.ImageSharp" Version="[3.1.12, 4.0.0)" /> <PackageReference Include="SixLabors.ImageSharp" Version="[3.1.12, 4.0.0)" />
</ItemGroup> </ItemGroup>
+71 -17
View File
@@ -163,8 +163,15 @@ internal class MessageManager : IAsyncDisposable
internal void ClearAllTabs() internal void ClearAllTabs()
{ {
// B3: snapshot the tab LIST under the shared lock so the worker-thread
// add/remove can't tear the enumeration; tab.Clear() then runs lock-free
// (each tab's Messages has its own SemaphoreSlim — lock order: list outer).
List<Tab> tabsSnapshot;
lock (Plugin.TabsListLock)
tabsSnapshot = Plugin.Config.Tabs.ToList();
// TempTabs are session-only (not persisted); exclude them to preserve Tell history // TempTabs are session-only (not persisted); exclude them to preserve Tell history
foreach (var tab in Plugin.Config.Tabs.Where(t => !t.IsTempTab)) foreach (var tab in tabsSnapshot.Where(t => !t.IsTempTab))
tab.Clear(); tab.Clear();
} }
@@ -176,18 +183,19 @@ internal class MessageManager : IAsyncDisposable
using var messages = Store.GetMostRecentMessages(CurrentContentId, since); using var messages = Store.GetMostRecentMessages(CurrentContentId, since);
// TempTabs are excluded; they maintain live state from AutoTellTabsService // TempTabs excluded (live state from AutoTellTabsService). Bucket via the
var pendingTabs = Plugin // pure MapMessagesToTabs so the assignment stays testable outside Dalamud (B3-1).
.Config.Tabs.Where(t => !t.IsTempTab) // B3: snapshot under the shared lock (list copy only — short critical
.Select(tab => (tab, new List<Message>())) // section). The Store query above and the AddSortPrune writes below stay
.ToList(); // OUTSIDE the lock (lock order: list outer, MessageList inner).
foreach (var message in messages) List<Tab> nonTempTabs;
foreach (var (_, pendingMessages) in pendingTabs.Where(ptab => ptab.Item1.Matches(message))) lock (Plugin.TabsListLock)
pendingMessages.Add(message); nonTempTabs = Plugin.Config.Tabs.Where(t => !t.IsTempTab).ToList();
var buckets = MapMessagesToTabs(nonTempTabs, messages);
// Apply messages to chat log all at once. // Apply messages to chat log all at once.
foreach (var (tab, pendingMessages) in pendingTabs) foreach (var tab in nonTempTabs)
tab.Messages.AddSortPrune(pendingMessages, MessageDisplayLimit); tab.Messages.AddSortPrune(buckets[tab], MessageDisplayLimit);
if (!messages.DidError) if (!messages.DidError)
return; return;
@@ -206,6 +214,26 @@ internal class MessageManager : IAsyncDisposable
} }
} }
// Pure message->tab bucketing for the refilter. Dalamud-free + static so the
// assignment can be unit-pinned in the build suite; the live caller owns the
// Store query, the snapshot and the SemaphoreSlim writes.
internal static Dictionary<Tab, List<Message>> MapMessagesToTabs(
IReadOnlyList<Tab> tabs,
IEnumerable<Message> messages
)
{
var buckets = new Dictionary<Tab, List<Message>>(tabs.Count);
foreach (var tab in tabs)
buckets[tab] = new List<Message>();
foreach (var message in messages)
foreach (var tab in tabs)
if (tab.Matches(message))
buckets[tab].Add(message);
return buckets;
}
internal void FilterAllTabsAsync() internal void FilterAllTabsAsync()
{ {
Task.Run(() => Task.Run(() =>
@@ -331,12 +359,21 @@ internal class MessageManager : IAsyncDisposable
if (Plugin.Config.DatabaseBattleMessages || !message.Code.IsBattle()) if (Plugin.Config.DatabaseBattleMessages || !message.Code.IsBattle())
Store.UpsertMessage(message); Store.UpsertMessage(message);
// Snapshot the list, not just the active tab. This loop runs on the worker
// thread while SaveConfig's strip and the auto-tell spawn mutate Config.Tabs
// under TabsListLock — enumerating it live throws "collection was modified",
// and the catch in ProcessPendingMessages swallows that, silently dropping
// the whole message: no tab entry, no sound, no MessageProcessed.
List<Tab> tabsSnapshot;
lock (Plugin.TabsListLock)
tabsSnapshot = Plugin.Config.Tabs.ToList();
// Snapshot the active tab and whether it shows this message ONCE, so the // Snapshot the active tab and whether it shows this message ONCE, so the
// whole loop sees a consistent value (the getter is a cross-thread read of // whole loop sees a consistent value (the getter is a cross-thread read of
// MainWindow.ActiveTab). // MainWindow.ActiveTab).
var currentTab = Plugin.CurrentTab; var currentTab = Plugin.CurrentTab;
var currentTabMatches = currentTab.Matches(message); var currentTabMatches = currentTab.Matches(message);
foreach (var tab in Plugin.Config.Tabs) foreach (var tab in tabsSnapshot)
{ {
if (tab.Matches(message)) if (tab.Matches(message))
tab.AddMessage(message, ShouldCountUnread(tab, currentTab, currentTabMatches)); tab.AddMessage(message, ShouldCountUnread(tab, currentTab, currentTabMatches));
@@ -346,12 +383,24 @@ internal class MessageManager : IAsyncDisposable
// stays pure and SelfTest-able; AddMessage above and playback below keep // stays pure and SelfTest-able; AddMessage above and playback below keep
// the side effects. // the side effects.
var notificationSound = SelectNotificationSound( var notificationSound = SelectNotificationSound(
Plugin.Config.Tabs, tabsSnapshot,
Plugin.CurrentTab, currentTab,
message, message,
Plugin.Config.PlaySounds Plugin.Config.PlaySounds,
out var soundSource
); );
// The snapshot can outlive a tab (eviction, logout). Playing its sound would
// be an audible artefact for a tab that is already gone, so re-check first.
if (notificationSound is not null && soundSource is not null)
{
bool sourceStillPresent;
lock (Plugin.TabsListLock)
sourceStillPresent = Plugin.Config.Tabs.Contains(soundSource);
if (!sourceStillPresent)
notificationSound = null;
}
if (notificationSound is { } soundId) if (notificationSound is { } soundId)
{ {
if (soundId is >= 1 and <= 16) if (soundId is >= 1 and <= 16)
@@ -399,14 +448,18 @@ internal class MessageManager : IAsyncDisposable
&& currentTabMatches && currentTabMatches
); );
// Reports the tab the sound came from, so the caller can drop it if that tab
// disappeared between snapshot and playback.
internal static uint? SelectNotificationSound( internal static uint? SelectNotificationSound(
IEnumerable<Tab> tabs, IEnumerable<Tab> tabs,
Tab currentTab, Tab currentTab,
Message probe, Message probe,
bool playSounds bool playSounds,
out Tab? source
) )
{ {
uint? picked = null; uint? picked = null;
source = null;
foreach (var tab in tabs) foreach (var tab in tabs)
{ {
if (!tab.Matches(probe)) if (!tab.Matches(probe))
@@ -421,6 +474,7 @@ internal class MessageManager : IAsyncDisposable
) )
{ {
picked = tab.NotificationSoundId; picked = tab.NotificationSoundId;
source = tab;
} }
} }
return picked; return picked;
@@ -432,7 +486,7 @@ internal class MessageManager : IAsyncDisposable
Tab currentTab, Tab currentTab,
Message probe, Message probe,
bool playSounds bool playSounds
) => SelectNotificationSound(tabs, currentTab, probe, playSounds); ) => SelectNotificationSound(tabs, currentTab, probe, playSounds, out _);
internal class NameFormatting internal class NameFormatting
{ {
+200 -53
View File
@@ -279,18 +279,25 @@ internal class MessageStore : IDisposable
migrationsToDo.Add(Migrate2); migrationsToDo.Add(Migrate2);
migrationsToDo.Add(Migrate3); migrationsToDo.Add(Migrate3);
migrationsToDo.Add(Migrate4); migrationsToDo.Add(Migrate4);
migrationsToDo.Add(Migrate5);
break; break;
case 1: case 1:
migrationsToDo.Add(Migrate2); migrationsToDo.Add(Migrate2);
migrationsToDo.Add(Migrate3); migrationsToDo.Add(Migrate3);
migrationsToDo.Add(Migrate4); migrationsToDo.Add(Migrate4);
migrationsToDo.Add(Migrate5);
break; break;
case 2: case 2:
migrationsToDo.Add(Migrate3); migrationsToDo.Add(Migrate3);
migrationsToDo.Add(Migrate4); migrationsToDo.Add(Migrate4);
migrationsToDo.Add(Migrate5);
break; break;
case 3: case 3:
migrationsToDo.Add(Migrate4); migrationsToDo.Add(Migrate4);
migrationsToDo.Add(Migrate5);
break;
case 4:
migrationsToDo.Add(Migrate5);
break; break;
} }
@@ -430,6 +437,23 @@ internal class MessageStore : IDisposable
SetMigrationVersion(4); SetMigrationVersion(4);
} }
private void Migrate5()
{
_logger.LogInformation("Running migration 5: Add (Receiver, Date) index for tell history");
// GetTellHistoryWithSender filters on Receiver and orders by Date DESC.
// Without a matching index SQLite sorts the whole receiver history into a
// temp b-tree before returning row one, which defeats the early break in
// the caller. (Receiver, ChatType, Date) does NOT help: the ChatType IN
// filter sits between the equality prefix and the sort column.
using var cmd = Connection.CreateCommand();
cmd.CommandText =
"CREATE INDEX IF NOT EXISTS idx_messages_receiver_date ON messages (Receiver, Date);";
cmd.ExecuteNonQuery();
SetMigrationVersion(5);
}
private void SetMigrationVersion(int version) private void SetMigrationVersion(int version)
{ {
_logger.LogInformation($"Setting version {version}"); _logger.LogInformation($"Setting version {version}");
@@ -440,35 +464,60 @@ internal class MessageStore : IDisposable
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
// Drops the full-text index and marks it for a rebuild.
//
// messages_fts stores sender_text and content_text in the clear, and no
// delete path touched it: ClearMessages, CleanupRetainOnly and the retention
// sweep all removed rows from `messages` only. The plain text of every
// "deleted" message stayed on disk.
//
// Worse, it was self-sealing. InitFtsReadyCache treats a non-empty index as
// ready, so after a wipe the index stayed full, the readiness flag stayed
// true, and the rebuild that would have cleared it never ran again.
//
// Wiping rather than deleting matched rows: message_guid is stored as a GUID
// string while messages.Id is a BLOB, so the two cannot be joined in SQL.
// The index is derived data and rebuilds from the surviving rows on the next
// start, which is the cheap and provably complete option.
//
// Caller must already hold _readLock.
private void InvalidateFtsIndex()
{
Connection.Execute("DELETE FROM messages_fts;");
_ftsReady = false;
}
internal void ClearMessages() internal void ClearMessages()
{ {
lock (_readLock) lock (_readLock)
{ {
Connection.Execute("DELETE FROM messages;"); Connection.Execute("DELETE FROM messages;");
PerformMaintenance(); InvalidateFtsIndex();
TryPerformMaintenance();
} }
} }
// Returns a (ChatType, count) snapshot over non-deleted messages. // Returns a (ChatType, count) snapshot over non-deleted messages.
// Used by the Privacy tab to preview retroactive cleanup impact. // Used by the Privacy tab to preview retroactive cleanup impact.
internal Dictionary<int, long> GetMessageCountsByChatType() //
// Caller-owned connection, same reasoning as StreamForExport: this is a
// GROUP BY over every row, and holding _readLock for it would stall
// UpsertMessage on the framework thread for as long as the scan takes.
internal Dictionary<int, long> GetMessageCountsByChatType(SqliteConnection conn)
{ {
lock (_readLock) var result = new Dictionary<int, long>();
using var cmd = conn.CreateCommand();
cmd.CommandText =
"SELECT ChatType, COUNT(*) FROM messages WHERE deleted = false GROUP BY ChatType;";
cmd.CommandTimeout = 120;
using var reader = cmd.ExecuteReader();
while (reader.Read())
{ {
var result = new Dictionary<int, long>(); var chatType = reader.GetInt32(0);
using var cmd = Connection.CreateCommand(); var count = reader.GetInt64(1);
cmd.CommandText = result[chatType] = count;
"SELECT ChatType, COUNT(*) FROM messages WHERE deleted = false GROUP BY ChatType;";
cmd.CommandTimeout = 120;
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
var chatType = reader.GetInt32(0);
var count = reader.GetInt64(1);
result[chatType] = count;
}
return result;
} }
return result;
} }
// Deletes messages older than the per-channel retention window, with a global // Deletes messages older than the per-channel retention window, with a global
@@ -505,6 +554,12 @@ internal class MessageStore : IDisposable
var index = 0; var index = 0;
foreach (var (type, days) in chatTypeDaysMap) foreach (var (type, days) in chatTypeDaysMap)
{ {
// Careful: 0 here is NOT the "keep forever" it means for
// defaultDays below. A per-channel 0 puts the cutoff at now
// and deletes the channel's entire history. No profile ships
// a 0 and no UI can set one, which is why this is a comment
// and not a guard -- but any editor added later has to
// reconcile the two meanings before it exposes the value.
var cutoff = nowMs - days * 86400000L; var cutoff = nowMs - days * 86400000L;
var typeParam = $"$type{index}"; var typeParam = $"$type{index}";
var cutoffParam = $"$cutoff{index}"; var cutoffParam = $"$cutoff{index}";
@@ -538,7 +593,11 @@ internal class MessageStore : IDisposable
} }
if (deleted > 0) if (deleted > 0)
PerformMaintenance(); {
InvalidateFtsIndex();
TryPerformMaintenance();
}
return deleted; return deleted;
} }
} }
@@ -562,7 +621,51 @@ internal class MessageStore : IDisposable
cmd.CommandTimeout = 600; cmd.CommandTimeout = 600;
deleted = cmd.ExecuteNonQuery(); deleted = cmd.ExecuteNonQuery();
} }
PerformMaintenance();
// Skipped when nothing matched: VACUUM rewrites the whole file, and
// running it for zero deleted rows costs seconds on a large database
// for no benefit. DeleteByRetentionPolicy already guards this way.
if (deleted > 0)
{
InvalidateFtsIndex();
TryPerformMaintenance();
}
return deleted;
}
}
// Hard-deletes every message whose ChatType IS in the list, then VACUUMs.
// Returns the number of rows deleted.
//
// The mirror image of CleanupRetainOnly, and the privacy filter needs both.
// With the unknown-channel failsafe on, the rule keeps every channel this
// build does not recognise -- and a retain-list can only name the ones that
// were already in the database when the list was built, so a channel whose
// first message arrives after that would be deleted. Naming what goes
// instead of what stays removes the window entirely.
internal long CleanupDeleteTypes(IReadOnlyCollection<int> deleteTypes)
{
if (deleteTypes.Count == 0)
return 0;
lock (_readLock)
{
long deleted;
using (var cmd = Connection.CreateCommand())
{
var placeholders = BindIntList(cmd, "dt", deleteTypes);
cmd.CommandText = $"DELETE FROM messages WHERE ChatType IN ({placeholders});";
cmd.CommandTimeout = 600;
deleted = cmd.ExecuteNonQuery();
}
if (deleted > 0)
{
InvalidateFtsIndex();
TryPerformMaintenance();
}
return deleted; return deleted;
} }
} }
@@ -581,6 +684,33 @@ internal class MessageStore : IDisposable
} }
} }
// Runs maintenance and swallows a failure, for the delete paths only.
//
// VACUUM needs the database to itself, and a lazily consumed reader on the
// primary connection -- which GetMostRecentMessages hands out and the
// refilter walks outside the lock -- makes it fail immediately with "cannot
// VACUUM - SQL statements in progress". That happens after the DELETE has
// committed, so letting it escape means the caller reports "nothing was
// removed" about a wipe that emptied the database.
//
// The rows are gone either way. An uncompacted file is a housekeeping
// problem; telling somebody their history is still there when it is not is
// a different kind of problem.
private void TryPerformMaintenance()
{
try
{
PerformMaintenance();
}
catch (Exception e)
{
_logger.LogWarning(
e,
"Maintenance after a delete failed; the rows are gone but the file was not compacted."
);
}
}
private string LogPath => DbPath + "-wal"; private string LogPath => DbPath + "-wal";
internal long DatabaseSize() => !File.Exists(DbPath) ? 0 : new FileInfo(DbPath).Length; internal long DatabaseSize() => !File.Exists(DbPath) ? 0 : new FileInfo(DbPath).Length;
@@ -643,9 +773,21 @@ internal class MessageStore : IDisposable
internal SqliteConnection OpenSecondaryConnection() internal SqliteConnection OpenSecondaryConnection()
{ {
var conn = new SqliteConnection(BuildConnectionString(DbPath)); var conn = new SqliteConnection(BuildConnectionString(DbPath));
conn.Open(); try
ApplyPragmas(conn); {
return conn; conn.Open();
ApplyPragmas(conn);
return conn;
}
catch
{
// Open can succeed and ApplyPragmas still throw: journal_mode=WAL
// needs a lock and gives up after DefaultTimeout. Without this the
// connection is neither returned nor closed, and with Pooling=false
// it survives until a finalizer gets to it.
conn.Dispose();
throw;
}
} }
// Worker-only mutator. The bulk-insert worker is the single legitimate // Worker-only mutator. The bulk-insert worker is the single legitimate
@@ -909,53 +1051,57 @@ internal class MessageStore : IDisposable
// Streams messages for export, sorted ascending by Date, excluding soft-deleted rows. // Streams messages for export, sorted ascending by Date, excluding soft-deleted rows.
// Optional filters: chatTypes, from/to inclusive date range. // Optional filters: chatTypes, from/to inclusive date range.
// Caller is responsible for disposing the enumerator. // Caller is responsible for disposing the enumerator and the connection.
// Lock caveat: lock guards command setup and ExecuteReader; the returned //
// MessageEnumerator is iterated lazily by the caller outside the lock. // Takes a caller-owned connection from OpenSecondaryConnection rather than
// Acceptable for v1.4.8 -- DbViewer iterates on its filter-worker Task and // using the primary one, and therefore takes no lock. The reader stays open
// any clash with UpsertMessage on the primary Connection is rare and // for as long as the export writes, which is seconds to minutes on a large
// serialised by SQLite's own connection-level lock. v1.5.x DI cycle should // history, and chat keeps arriving throughout -- so the primary connection
// address this with a snapshot-to-list or connection pool. // would be read here and written by UpsertMessage at the same time, and
// SqliteConnection is not thread-safe. Holding _readLock for the whole
// export would trade that for freezing the game instead.
//
// WAL gives readers their own snapshot, so a live write cannot tear the
// export mid-file either.
internal MessageEnumerator StreamForExport( internal MessageEnumerator StreamForExport(
SqliteConnection conn,
IReadOnlyCollection<int>? chatTypes, IReadOnlyCollection<int>? chatTypes,
DateTimeOffset? from, DateTimeOffset? from,
DateTimeOffset? to DateTimeOffset? to
) )
{ {
lock (_readLock) var cmd = conn.CreateCommand();
{
var cmd = Connection.CreateCommand();
var clauses = new List<string> { "deleted = false" }; var clauses = new List<string> { "deleted = false" };
if (chatTypes is { Count: > 0 }) if (chatTypes is { Count: > 0 })
clauses.Add($"ChatType IN ({BindIntList(cmd, "exct", chatTypes)})"); clauses.Add($"ChatType IN ({BindIntList(cmd, "exct", chatTypes)})");
if (from is not null) if (from is not null)
clauses.Add("Date >= $From"); clauses.Add("Date >= $From");
if (to is not null) if (to is not null)
clauses.Add("Date <= $To"); clauses.Add("Date <= $To");
cmd.CommandText = cmd.CommandText =
@" @"
SELECT SELECT
Id, Receiver, ContentId, Date, ChatType, SourceKind, TargetKind, Id, Receiver, ContentId, Date, ChatType, SourceKind, TargetKind,
Sender, Content, SenderSource, ContentSource, ExtraChatChannel Sender, Content, SenderSource, ContentSource, ExtraChatChannel
FROM messages FROM messages
WHERE " WHERE "
+ string.Join(" AND ", clauses) + string.Join(" AND ", clauses)
+ @" + @"
ORDER BY Date ASC;"; ORDER BY Date ASC;";
cmd.CommandTimeout = 600; cmd.CommandTimeout = 600;
if (from is not null) if (from is not null)
cmd.Parameters.AddWithValue("$From", from.Value.ToUnixTimeMilliseconds()); cmd.Parameters.AddWithValue("$From", from.Value.ToUnixTimeMilliseconds());
if (to is not null) if (to is not null)
cmd.Parameters.AddWithValue("$To", to.Value.ToUnixTimeMilliseconds()); cmd.Parameters.AddWithValue("$To", to.Value.ToUnixTimeMilliseconds());
return new MessageEnumerator( // Logger first: an argument list evaluates left to right, so a throwing
cmd.ExecuteReader(), // CreateLogger -- which is what a disposed host gives you -- would leave
_loggerFactory.CreateLogger<MessageEnumerator>() // an open reader that no MessageEnumerator owns.
); var logger = _loggerFactory.CreateLogger<MessageEnumerator>();
} return new MessageEnumerator(cmd.ExecuteReader(), logger);
} }
// Returns the most recent messages, oldest-first. // Returns the most recent messages, oldest-first.
@@ -1014,7 +1160,8 @@ internal class MessageStore : IDisposable
} }
// Returns up to `limit` tells exchanged with the named player, oldest-first. // Returns up to `limit` tells exchanged with the named player, oldest-first.
// SQL narrows by Receiver + ChatType via the (Receiver, Date) index, then // SQL narrows by Receiver + ChatType via the (Receiver, Date) index (migration
// 5; before that the ordering fell back to a temp b-tree over all rows), then
// the client-side loop runs PlayerPayload comparison and breaks once // the client-side loop runs PlayerPayload comparison and breaks once
// `limit` partner matches accumulate. Earlier versions had a hardcoded // `limit` partner matches accumulate. Earlier versions had a hardcoded
// 500-row scan cap that cut less-frequent pinned partners off the back of // 500-row scan cap that cut less-frequent pinned partners off the back of
+9 -7
View File
@@ -263,13 +263,15 @@ internal sealed class PayloadHandler
// Eureka, Bozja and Occult need special handling as tells work different // Eureka, Bozja and Occult need special handling as tells work different
if (!Sheets.IsInForay()) if (!Sheets.IsInForay())
{ {
// §6.9: single SetPendingMessage call; v1.5.6 used incremental Chat += writes // §6.9: single SetPendingMessage call; v1.5.6 used incremental Chat += writes.
var builder = $"/tell {player.PlayerName}"; // XC-8: shares the /tell builder with the native detours. IsPublic (not
if (world.Value.IsPublic) // IsNullOrEmpty) is resolved HERE — a private/null world must NOT leak @World.
builder += $"@{world.Value.Name}"; _inputBar.SetPendingMessage(
GameFunctions.Chat.BuildTellCommand(
builder += " "; player.PlayerName,
_inputBar.SetPendingMessage(builder); world.Value.IsPublic ? world.Value.Name.ToString() : null
)
);
} }
else if (validContentId) else if (validContentId)
{ {
+387 -121
View File
@@ -109,12 +109,20 @@ public sealed class Plugin : IAsyncDalamudPlugin
internal static InputPreview InputPreview { get; private set; } = null!; internal static InputPreview InputPreview { get; private set; } = null!;
internal CommandHelpWindow CommandHelpWindow { get; private set; } = null!; internal CommandHelpWindow CommandHelpWindow { get; private set; } = null!;
public SeStringDebugger SeStringDebugger { get; private set; } = null!; public SeStringDebugger SeStringDebugger { get; private set; } = null!;
#if DEBUG
internal Ui.Windows.WidgetGalleryWindow WidgetGallery { get; private set; } = null!;
#endif
public FirstRunWizard FirstRunWizard { get; private set; } = null!; public FirstRunWizard FirstRunWizard { get; private set; } = null!;
internal DebuggerWindow DebuggerWindow { get; private set; } = null!; internal DebuggerWindow DebuggerWindow { get; private set; } = null!;
internal Commands Commands { get; private set; } = null!; internal Commands Commands { get; private set; } = null!;
internal GameFunctions.GameFunctions Functions { get; private set; } = null!; internal GameFunctions.GameFunctions Functions { get; private set; } = null!;
internal MessageManager MessageManager { get; private set; } = null!; internal MessageManager MessageManager { get; private set; } = null!;
// Reached by the gate-wiring self-test, which has to ask the live tab
// whether it sees a held gate.
internal Ui.Components.Settings.Tabs.DataPrivacyTab DataPrivacyTab { get; private set; } =
null!;
internal AutoTellTabsService AutoTellTabsService { get; private set; } = null!; internal AutoTellTabsService AutoTellTabsService { get; private set; } = null!;
internal IpcManager Ipc { get; private set; } = null!; internal IpcManager Ipc { get; private set; } = null!;
internal ExtraChat ExtraChat { get; private set; } = null!; internal ExtraChat ExtraChat { get; private set; } = null!;
@@ -160,13 +168,33 @@ public sealed class Plugin : IAsyncDalamudPlugin
// Idempotency guard — Dalamud may fire DisposeAsync twice in a reload race. // Idempotency guard — Dalamud may fire DisposeAsync twice in a reload race.
private int _disposeStarted; private int _disposeStarted;
// The three hide conditions v1.5.6 evaluated and cf4705e left without a
// reader. Advanced once per draw, before any window is drawn.
private Util.ChatHideReason _hideReason = Util.ChatHideReason.None;
// Set by the chat-activation keybind, consumed by the next hide evaluation.
// A cutscene the user dismissed stays dismissed until it ends.
internal bool ChatActivationRequested;
// Set in the first DisposeAsync statement so async callbacks scheduled // Set in the first DisposeAsync statement so async callbacks scheduled
// via Framework.RunOnTick (v1.4.8 B3 retention sweep) can early-bail // via Framework.RunOnTick (v1.4.8 B3 retention sweep) can early-bail
// before they touch state that has already been torn down. Volatile // before they touch state that has already been torn down. Volatile
// because the tick reads it from a different thread than the writer. // because the tick reads it from a different thread than the writer.
private volatile bool _isDisposing; private volatile bool _isDisposing;
internal int DeferredSaveFrames = -1; // Read by background workers that outlive a teardown -- the export thread
// finishes its file either way, but a notification for a plugin the user
// just unloaded belongs to nobody.
internal bool IsDisposing => _isDisposing;
// v1.9.0 B5: last full Draw() wall-time in ms, written once per frame at
// the end of the UiBuilder.Draw handler. Covers the GlobalStyleScope push
// and the font push (§7.5 First-Frame-HITCH must include atlas/style
// prologue cost), not just WindowSystem.Draw — measuring the inner call
// alone would drop the prologue and make the figure non-comparable to the
// v1.5.6 baseline. Only accumulated here; the disk write happens in
// PerformanceBaselineStep so the hot path stays allocation-free.
internal double LastDrawMs;
// Cancels the v1.4.8 FTS5 bulk-insert worker on plugin teardown. The // Cancels the v1.4.8 FTS5 bulk-insert worker on plugin teardown. The
// worker runs off the framework thread on its own SqliteConnection, so a // worker runs off the framework thread on its own SqliteConnection, so a
@@ -174,11 +202,30 @@ public sealed class Plugin : IAsyncDalamudPlugin
// tears down (the worker logs "rebuild failed" via Log on error paths). // tears down (the worker logs "rebuild failed" via Log on error paths).
private CancellationTokenSource? _ftsRebuildCts; private CancellationTokenSource? _ftsRebuildCts;
// Serialises retention sweeps so a manual trigger and the 24h auto-sweep // Serialises every long-running database operation against every other one,
// can't run in parallel. Volatile because the ImGui thread reads it outside // not just retention sweeps against each other. An export leaves a reader
// the lock to gate the manual button. // open on the primary connection outside _readLock by design -- the
internal readonly object RetentionSweepLock = new(); // enumerator is consumed lazily -- and a VACUUM meeting that reader hits a
internal volatile bool RetentionSweepRunning; // connection Microsoft documents as not thread-safe.
//
// Replaces the retention-only pair, which solved the same problem for one
// case. The draw thread reads Current every frame to disable buttons and
// must never block doing so.
internal readonly Util.DbOperationGate DbOperations = new();
// B3: neutral owner of the Config.Tabs LIST-structure lock so both the
// worker-thread mutator (AutoTellTabsService) and the framework-thread
// refilter (MessageManager) share ONE monitor. Lock order: this outer,
// MessageList's SemaphoreSlim inner — never the reverse.
internal readonly object TabsListLock = new();
// Guards the serialized config maps that the draw thread mutates while a
// background save may be serializing them: ChatColours, PrivacyPersistChannels
// and RetentionPerChannelDays. TabsListLock does not cover these.
// Ordering: ConfigMapsLock sits INSIDE TabsListLock (that edge is real, via
// AutoTellTabsService calling SaveConfig under the tabs lock). Never the other
// way round -- so SaveConfig must never be called while holding ConfigMapsLock.
internal readonly object ConfigMapsLock = new();
internal DateTime GameStarted { get; } internal DateTime GameStarted { get; }
@@ -186,8 +233,22 @@ public sealed class Plugin : IAsyncDalamudPlugin
// installed before MainWindow is Phase-1 resolved, so the null-conditional // installed before MainWindow is Phase-1 resolved, so the null-conditional
// fallback to Tabs[0] is load-bearing — it keeps the pre-coupling behavior // fallback to Tabs[0] is load-bearing — it keeps the pre-coupling behavior
// in that early window rather than being merely defensive. // in that early window rather than being merely defensive.
internal Tab CurrentTab => // Read once into a local: Count and [0] as two separate accesses can be split
MainWindow?.ActiveTab ?? (Config.Tabs.Count > 0 ? Config.Tabs[0] : new Tab()); // by a removal on another thread. Only reachable before MainWindow exists.
internal Tab CurrentTab
{
get
{
if (MainWindow?.ActiveTab is { } active)
return active;
lock (TabsListLock)
{
var tabs = Config.Tabs;
return tabs.Count > 0 ? tabs[0] : new Tab();
}
}
}
public Plugin() public Plugin()
{ {
@@ -203,7 +264,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
// Migrate config + database from upstream ChatTwo on first start. // Migrate config + database from upstream ChatTwo on first start.
MigrateFromChatTwoLayout(); MigrateFromChatTwoLayout();
Config = Interface.GetPluginConfig() as Configuration ?? new Configuration(); Config = Interface.GetPluginConfig() as Configuration ?? Configuration.CreateFresh();
// PlatformUtil and LogProxy are filled from the DI container in // PlatformUtil and LogProxy are filled from the DI container in
// Phase-1 below (`_host.Services.GetRequiredService<IPlatformUtil>()` // Phase-1 below (`_host.Services.GetRequiredService<IPlatformUtil>()`
@@ -235,12 +296,51 @@ public sealed class Plugin : IAsyncDalamudPlugin
{ {
Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs; Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs;
} }
Config.Version = 23;
// v24 migration: the privacy filter used to route a known but unticked
// channel through the unknown-type failsafe, so the channel grid was
// inert whenever that failsafe was on. Corrected in v1.12.0. A config
// that never picked a channel was storing everything through that hole,
// and the corrected rule would store nothing at all -- so the intent is
// carried forward as a filter that is honestly switched off.
if (
Config.Version < 24
&& Privacy.StorageRule.ShouldDisableFilterOnV24(
Config.PrivacyFilterEnabled,
Config.PrivacyPersistUnknownChannels,
Config.PrivacyPersistChannels.Count
)
)
{
Config.PrivacyFilterEnabled = false;
// Log, not LogProxy: this runs in Phase-0 and the proxy is only
// resolved from the container further down.
Log.Information(
"Privacy filter switched off during the v24 migration: it was on with no channels "
+ "picked, which stored everything through the unknown-channel failsafe. Pick "
+ "channels in Settings to switch it back on."
);
}
// v25 carries no migration step. The schema gate above only refuses
// anything under 16, and Json.NET drops keys it does not recognise on
// load, so the fields v1.12.0 deleted simply stop being written on the
// next save. The bump is documentation, and it has to be consistent:
// the constant and this stamp are two separate places, and changing
// only one gives a config that re-stamps itself on every start.
Config.Version = 25;
// Unpinned TempTabs are session-only and dropped on every load. Pinned // Unpinned TempTabs are session-only and dropped on every load. Pinned
// TempTabs survive reload — Jin's tester feedback (v1.4.7). // TempTabs survive reload — Jin's tester feedback (v1.4.7).
Config.Tabs.RemoveAll(TabLifecycleHelpers.ShouldStripOnLoad); Config.Tabs.RemoveAll(TabLifecycleHelpers.ShouldStripOnLoad);
// GP-04: clear stale Tab.PopOut flags now — the pool binds further down
// (ChannelPopoutPool resolve below), so at this point no tab can own a
// slot. A persisted PopOut=true (notably on surviving pinned TempTabs)
// would otherwise be a flag with no window. Runs after the strip, before
// any pool TryOpen.
TabLifecycleHelpers.ResetPopOutOnLoad(Config.Tabs);
LanguageChanged(Interface.UiLanguage); LanguageChanged(Interface.UiLanguage);
// v1.5.3 migration: Settings.Apply auto-activates the matching // v1.5.3 migration: Settings.Apply auto-activates the matching
@@ -255,8 +355,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
ImGuiUtil.Initialize(this); ImGuiUtil.Initialize(this);
DeferredSaveFrames = -1;
// Custom themes dir + seed run before the container builds so the // Custom themes dir + seed run before the container builds so the
// ThemeRegistry factory lambda finds the directory ready. // ThemeRegistry factory lambda finds the directory ready.
var customThemesDir = Path.Combine(Interface.ConfigDirectory.FullName, "themes"); var customThemesDir = Path.Combine(Interface.ConfigDirectory.FullName, "themes");
@@ -321,10 +419,15 @@ public sealed class Plugin : IAsyncDalamudPlugin
InputBar = _host.Services.GetRequiredService<Ui.Components.InputBar>(); InputBar = _host.Services.GetRequiredService<Ui.Components.InputBar>();
MainWindow = _host.Services.GetRequiredService<Ui.Windows.MainWindow>(); MainWindow = _host.Services.GetRequiredService<Ui.Windows.MainWindow>();
SettingsWindow = _host.Services.GetRequiredService<Ui.Windows.SettingsWindow>(); SettingsWindow = _host.Services.GetRequiredService<Ui.Windows.SettingsWindow>();
DataPrivacyTab =
_host.Services.GetRequiredService<Ui.Components.Settings.Tabs.DataPrivacyTab>();
DbViewer = _host.Services.GetRequiredService<DbViewer>(); DbViewer = _host.Services.GetRequiredService<DbViewer>();
InputPreview = _host.Services.GetRequiredService<InputPreview>(); InputPreview = _host.Services.GetRequiredService<InputPreview>();
CommandHelpWindow = _host.Services.GetRequiredService<CommandHelpWindow>(); CommandHelpWindow = _host.Services.GetRequiredService<CommandHelpWindow>();
SeStringDebugger = _host.Services.GetRequiredService<SeStringDebugger>(); SeStringDebugger = _host.Services.GetRequiredService<SeStringDebugger>();
#if DEBUG
WidgetGallery = _host.Services.GetRequiredService<Ui.Windows.WidgetGalleryWindow>();
#endif
DebuggerWindow = _host.Services.GetRequiredService<DebuggerWindow>(); DebuggerWindow = _host.Services.GetRequiredService<DebuggerWindow>();
FirstRunWizard = _host.Services.GetRequiredService<FirstRunWizard>(); FirstRunWizard = _host.Services.GetRequiredService<FirstRunWizard>();
ChannelPopoutPool = _host.Services.GetRequiredService<Ui.Windows.ChannelPopoutPool>(); ChannelPopoutPool = _host.Services.GetRequiredService<Ui.Windows.ChannelPopoutPool>();
@@ -371,6 +474,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
await _lifecycle.LoadAsync(cancellationToken).ConfigureAwait(false); await _lifecycle.LoadAsync(cancellationToken).ConfigureAwait(false);
SelfTestRegistry.RegisterTestSteps([ SelfTestRegistry.RegisterTestSteps([
new SelfTests.ExportRoundTripStep(),
new SelfTests.ThemeSwitchSelfTestStep(this), new SelfTests.ThemeSwitchSelfTestStep(this),
new SelfTests.ThemeCrossfadeSelfTestStep(this), new SelfTests.ThemeCrossfadeSelfTestStep(this),
new SelfTests.FontManagerCtorSmokeStep(this), new SelfTests.FontManagerCtorSmokeStep(this),
@@ -387,12 +491,14 @@ public sealed class Plugin : IAsyncDalamudPlugin
new SelfTests.SettingsWindowOpenStep(this), new SelfTests.SettingsWindowOpenStep(this),
new SelfTests.OnOpenMainUiRoutesMainWindowStep(this), new SelfTests.OnOpenMainUiRoutesMainWindowStep(this),
new SelfTests.TypingIpcStateStep(this), new SelfTests.TypingIpcStateStep(this),
new SelfTests.ConfigMigrationV23Step(this), new SelfTests.ConfigMigrationV25Step(this),
new SelfTests.DbGateWiringStep(this),
new SelfTests.ChannelPopoutBindStep(this), new SelfTests.ChannelPopoutBindStep(this),
new SelfTests.HoverSheenAllocStep(this), new SelfTests.HoverStateFootprintStep(),
new SelfTests.HonorificHeaderRenderStep(this), new SelfTests.HonorificHeaderRenderStep(this),
new SelfTests.AboutIntegrationsStatusStep(this), new SelfTests.AboutIntegrationsStatusStep(this),
new SelfTests.PerformanceBaselineStep(this), new SelfTests.PerformanceBaselineStep(this),
new SelfTests.GlobalStyleScopeAllocStep(this),
new SelfTests.MainWindowFocusOpacityStep(this), new SelfTests.MainWindowFocusOpacityStep(this),
new SelfTests.MainWindowFlagsStep(this), new SelfTests.MainWindowFlagsStep(this),
new SelfTests.SenderNameReformatStep(this), new SelfTests.SenderNameReformatStep(this),
@@ -407,8 +513,11 @@ public sealed class Plugin : IAsyncDalamudPlugin
new SelfTests.TellResetOnActivateStep(), new SelfTests.TellResetOnActivateStep(),
new SelfTests.CurrentTabCouplingStep(this), new SelfTests.CurrentTabCouplingStep(this),
new SelfTests.SidebarUnreadDotStep(this), new SelfTests.SidebarUnreadDotStep(this),
new SelfTests.SidebarActiveSurfaceStep(this),
new SelfTests.TopTabUnderlineStep(this),
new SelfTests.UnreadDecisionStep(), new SelfTests.UnreadDecisionStep(),
new SelfTests.CurrentTabGuidedStep(this), new SelfTests.CurrentTabGuidedStep(this),
new SelfTests.CardClipPlanStep(this),
]); ]);
// Re-surface the wizard for existing users when a major UX // Re-surface the wizard for existing users when a major UX
@@ -617,19 +726,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
} }
); );
// Flush a pending DeferredSave — FrameworkUpdate won't fire it anymore.
failure = CaptureFailure(
failure,
() =>
{
if (DeferredSaveFrames >= 0)
{
SaveConfig();
DeferredSaveFrames = -1;
}
}
);
// Framework-thread cleanup the container does not reach. // Framework-thread cleanup the container does not reach.
try try
{ {
@@ -650,6 +746,21 @@ public sealed class Plugin : IAsyncDalamudPlugin
failure ??= ex; failure ??= ex;
} }
// The four long-running workers are background threads with no
// cancellation path, and one of them may be holding an open reader or
// sitting inside a VACUUM. Disposing the store under that tears the
// connection out mid-statement. Five seconds is not a guarantee, but it
// covers everything short of a VACUUM over a very large file, and it
// costs nothing when nothing is running.
var grace = Stopwatch.StartNew();
while (DbOperations.IsBusy && grace.ElapsedMilliseconds < 5_000)
await Task.Delay(50).ConfigureAwait(false);
if (DbOperations.IsBusy)
Log.Warning(
$"Disposing while {DbOperations.Current} still owns the store; it outlasted the 5s grace period."
);
// Container disposes services + windows on the framework thread. // Container disposes services + windows on the framework thread.
// MessageManager.DisposeAsync is not idempotent, so we let the // MessageManager.DisposeAsync is not idempotent, so we let the
// container do it once instead of double-disposing. // container do it once instead of double-disposing.
@@ -896,6 +1007,13 @@ public sealed class Plugin : IAsyncDalamudPlugin
SettingsWindow.Toggle(); SettingsWindow.Toggle();
return; return;
} }
#if DEBUG
if (arg.Equals("widgets", StringComparison.OrdinalIgnoreCase))
{
WidgetGallery.Toggle();
return;
}
#endif
if (arg.Equals("reset", StringComparison.OrdinalIgnoreCase)) if (arg.Equals("reset", StringComparison.OrdinalIgnoreCase))
{ {
// Recovery path documented in the v2.x master spec — drops a // Recovery path documented in the v2.x master spec — drops a
@@ -929,124 +1047,267 @@ public sealed class Plugin : IAsyncDalamudPlugin
if (DateTimeOffset.UtcNow - Config.RetentionLastRunAt < TimeSpan.FromHours(24)) if (DateTimeOffset.UtcNow - Config.RetentionLastRunAt < TimeSpan.FromHours(24))
return; return;
StartRetentionSweep(notify: false);
}
// Shared by the daily check above and the manual button in settings.
//
// notify: the unattended sweep stays quiet, because a notification for
// something the user did not ask for at a moment they did not choose is
// noise. A run they pressed a button for reports back.
//
// Returns false when the store is already busy, so the caller can say so
// instead of leaving the user waiting for a run that never started.
internal bool StartRetentionSweep(bool notify)
{
if (DbOperations.IsBusy)
return false;
// Snapshot the policy so the user can edit settings while the sweep runs. // Snapshot the policy so the user can edit settings while the sweep runs.
//
// Seeded from the spec defaults only when the global limit is not "keep
// forever". The slider is labelled "0 = never", and pre-filling 31
// channels with 365- and 90-day windows made that label a lie: setting
// it to zero still lost free company, linkshell and party history after
// ninety days, and the short-circuit in DeleteByRetentionPolicy could
// never be reached because the map was never empty.
//
// Explicit per-channel overrides still apply. Somebody who typed a
// number for one channel meant that number.
var policy = new Dictionary<int, int>(); var policy = new Dictionary<int, int>();
foreach (var (type, days) in Privacy.PrivacyDefaults.DefaultRetentionDays) if (Config.RetentionDefaultDays > 0)
policy[(int)(ushort)type] = days; {
foreach (var (type, days) in Config.RetentionPerChannelDays) foreach (var (type, days) in Privacy.PrivacyDefaults.DefaultRetentionDays)
policy[(int)(ushort)type] = days; policy[(int)(ushort)type] = days;
}
// This is the enumerator the wizard's Clear() cuts short. Reading under the
// same lock the writers take keeps the policy snapshot whole.
lock (ConfigMapsLock)
{
foreach (var (type, days) in Config.RetentionPerChannelDays)
policy[(int)(ushort)type] = days;
}
var defaultDays = Config.RetentionDefaultDays; var defaultDays = Config.RetentionDefaultDays;
// IsBackground = true so a stuck sweep never blocks plugin unload. _retentionSweepRunning = true;
new Thread(() =>
{
// Bail early if a manual sweep is already in flight.
lock (RetentionSweepLock)
{
if (RetentionSweepRunning)
return;
RetentionSweepRunning = true;
}
// IsBackground = true so a stuck sweep never blocks plugin unload.
var worker = new Thread(() =>
{
// Bails when anything else already owns the store, not only another
// sweep: a user-triggered export or cleanup counts too.
try try
{ {
var deleted = MessageManager.Store.DeleteByRetentionPolicy(policy, defaultDays); if (!DbOperations.TryBegin(Util.DbOperation.RetentionSweep))
Config.RetentionLastRunAt = DateTimeOffset.UtcNow;
SaveConfig();
if (deleted > 0)
{ {
Log.Information($"Retention sweep deleted {deleted} expired messages."); // A run the user pressed a button for has to say something.
// Schedule on the next framework tick to avoid the ~194ms // The pre-check in StartRetentionSweep only covers a gate
// hitch from blocking with .Wait() while the framework // that was already busy; losing the race here is the same
// finishes the current frame. Tabs-list mutation must // outcome and used to be silent.
// stay on the framework thread because Plugin.Config.Tabs if (notify)
// (Configuration.cs:222) is not lock-protected and NotifySweep(
// AutoTellTabsService can mutate it from background paths. Resources.HellionStrings.Retention_Error,
// Pattern reference: SimpleTweaks Dalamud.Interface.ImGuiNotification.NotificationType.Warning
// Tweaks/Chat/CaseInsensitiveCommands.cs:45. );
Framework.RunOnTick(() => return;
{
// The retention thread is IsBackground=true so plugin
// unload can fire while a scheduled tick is still
// pending; bail before touching anything torn down.
if (_isDisposing)
return;
try
{
MessageManager.ClearAllTabs();
MessageManager.FilterAllTabs();
}
catch (Exception ex)
{
Log.Error(ex, "Retention sweep clear+refilter failed");
}
});
} }
else
try
{ {
Log.Information("Retention sweep ran, nothing expired."); var deleted = MessageManager.Store.DeleteByRetentionPolicy(policy, defaultDays);
Config.RetentionLastRunAt = DateTimeOffset.UtcNow;
SaveConfig();
if (notify)
Util.WrapperUtil.AddNotification(
string.Format(Resources.HellionStrings.Retention_Success, deleted),
Dalamud.Interface.ImGuiNotification.NotificationType.Success
);
if (deleted > 0)
{
Log.Information($"Retention sweep deleted {deleted} expired messages.");
// Schedule on the next framework tick to avoid the ~194ms
// hitch from blocking with .Wait() while the frame finishes.
// The Config.Tabs enumeration in ClearAllTabs/FilterAllTabs is
// now guarded by the shared Plugin.TabsListLock (B3), so this
// tick scheduling is purely hitch-avoidance, not safety.
// Pattern reference: SimpleTweaks
// Tweaks/Chat/CaseInsensitiveCommands.cs:45.
Framework.RunOnTick(() =>
{
// The retention thread is IsBackground=true so plugin
// unload can fire while a scheduled tick is still
// pending; bail before touching anything torn down.
if (_isDisposing)
return;
try
{
MessageManager.ClearAllTabs();
MessageManager.FilterAllTabs();
}
catch (Exception ex)
{
Log.Error(ex, "Retention sweep clear+refilter failed");
}
});
}
else
{
Log.Information("Retention sweep ran, nothing expired.");
}
}
finally
{
DbOperations.End(Util.DbOperation.RetentionSweep);
} }
} }
catch (Exception e) catch (Exception e)
{ {
Log.Error(e, "Retention sweep failed"); Log.Error(e, "Retention sweep failed");
if (notify)
NotifySweep(
Resources.HellionStrings.Retention_Error,
Dalamud.Interface.ImGuiNotification.NotificationType.Error
);
} }
finally finally
{ {
lock (RetentionSweepLock) _retentionSweepRunning = false;
RetentionSweepRunning = false;
} }
}) })
{ {
IsBackground = true, IsBackground = true,
}.Start(); };
try
{
worker.Start();
return true;
}
catch (Exception e)
{
// The thread never ran, so nothing will clear the flag for us.
_retentionSweepRunning = false;
Log.Error(e, "Could not start the retention sweep thread");
return false;
}
} }
// The sweep is a background thread that can outlive an unload, same as the
// settings-tab workers. A notification filed against a plugin that is gone
// belongs to nobody.
private void NotifySweep(
string message,
Dalamud.Interface.ImGuiNotification.NotificationType type
)
{
if (_isDisposing)
return;
Util.WrapperUtil.AddNotification(message, type);
}
// Read by the settings tab every frame so the manual button can say a run is
// in progress. The gate itself cannot answer that: it goes busy only once
// the worker reaches TryBegin, which is after Start returns.
private volatile bool _retentionSweepRunning;
internal bool RetentionSweepRunning => _retentionSweepRunning;
private void Draw() private void Draw()
{ {
// v1.4.8 B2: pick up external edits of the active custom theme JSON // v1.9.0 B5: time the whole handler (style + font prologue included).
// without forcing the user to re-click the picker. The disk-stat is // Bail before measuring once teardown has begun — a late Draw tick
// 1Hz-throttled inside RefreshActiveIfStale, so this is essentially // must not touch ThemeRegistry / FontManager after DisposeAsync.
// free on built-in themes and ~1 stat/second on custom themes. if (_isDisposing)
ThemeRegistry.RefreshActiveIfStale();
using IDisposable _style = Ui.StyleEngine.GlobalStyleScope.Push(
ThemeRegistry.Active,
ThemeRegistry,
Config.WindowOpacity
);
if (Config.HideInLoadingScreens && Condition[ConditionFlag.BetweenAreas])
{
TypingIpc.Update();
return; return;
}
// Hide all plugin windows while the New Game+ menu is open. var drawWatch = Stopwatch.StartNew();
if ( try
Config.HideInNewGamePlusMenu {
&& GameFunctions.GameFunctions.IsAddonInteractable( // v1.4.8 B2: pick up external edits of the active custom theme JSON
GameFunctions.GameFunctions.NewGamePlusAddonName // without forcing the user to re-click the picker. The disk-stat is
// 1Hz-throttled inside RefreshActiveIfStale, so this is essentially
// free on built-in themes and ~1 stat/second on custom themes.
ThemeRegistry.RefreshActiveIfStale();
using IDisposable _style = Ui.StyleEngine.GlobalStyleScope.Push(
ThemeRegistry.Active,
ThemeRegistry,
Config.WindowOpacity
);
// Advance every held hover value once, before any window draws. Sits
// above the early returns below so a hidden main window still lets
// pop-out hovers fade instead of freezing mid-blend.
Ui.StyleEngine.HoverState.BeginFrame();
if (Config.HideInLoadingScreens && Condition[ConditionFlag.BetweenAreas])
{
TypingIpc.Update();
return;
}
// Hide all plugin windows while the New Game+ menu is open.
if (
Config.HideInNewGamePlusMenu
&& GameFunctions.GameFunctions.IsAddonInteractable(
GameFunctions.GameFunctions.NewGamePlusAddonName
)
) )
) {
{ TypingIpc.Update();
return;
}
Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden;
// Stateless, so it needs no machine: there is no gesture that shows
// the chat while nobody is logged in.
if (Config.HideWhenNotLoggedIn && !ClientState.IsLoggedIn)
{
TypingIpc.Update();
return;
}
_hideReason = Util.ChatHideState.Next(
_hideReason,
new Util.ChatHideState.Inputs(
Config.HideInBattle,
InBattle,
Config.HideDuringCutscenes,
CutsceneActive || GposeActive,
ChatActivationRequested
)
);
ChatActivationRequested = false;
if (Util.ChatHideState.Hides(_hideReason))
{
TypingIpc.Update();
return;
}
// RegularFont is nullable only because the live rebuild path
// disposes it before reassigning; both ends of that swap happen on
// this same draw thread, so it cannot be null here.
var useRegularFont = Config.FontsEnabled || Config.UseHellionFont;
using ((useRegularFont ? FontManager.RegularFont! : FontManager.Axis).Push())
WindowSystem.Draw();
TypingIpc.Update(); TypingIpc.Update();
return;
FileDialogManager.Draw();
}
finally
{
// finally so the early-return frames (loading screen / NG+) record
// their (cheap) time too instead of freezing on the last full frame.
drawWatch.Stop();
LastDrawMs = drawWatch.Elapsed.TotalMilliseconds;
} }
Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden;
// RegularFont is nullable only because the live rebuild path
// disposes it before reassigning; both ends of that swap happen on
// this same draw thread, so it cannot be null here.
var useRegularFont = Config.FontsEnabled || Config.UseHellionFont;
using ((useRegularFont ? FontManager.RegularFont! : FontManager.Axis).Push())
WindowSystem.Draw();
TypingIpc.Update();
FileDialogManager.Draw();
} }
internal void SaveConfig() internal void SaveConfig()
@@ -1056,12 +1317,20 @@ public sealed class Plugin : IAsyncDalamudPlugin
// Config.Tabs across the save so JSON includes them. Cloning only the // Config.Tabs across the save so JSON includes them. Cloning only the
// unpinned subset keeps the allocation proportional to // unpinned subset keeps the allocation proportional to
// AutoTellTabsLimit (<=15) instead of the full tab list. // AutoTellTabsLimit (<=15) instead of the full tab list.
var unpinnedTempTabs = Config.Tabs.Where(TabLifecycleHelpers.IsInUnpinnedPool).ToList(); // B3: the strip/restore mutates the tab LIST, so it shares TabsListLock
Config.Tabs.RemoveAll(TabLifecycleHelpers.ShouldStripOnSave); // with the worker add/remove and the refilter snapshot. Re-entrant: the
// one worker caller (HandleTell) already holds it; framework callers take
// it here. SavePluginConfig runs inside (short, in-memory) — the §8 fallback
// (serialize a copy outside the lock) is a tracked pre-beta to-do.
lock (TabsListLock)
{
var unpinnedTempTabs = Config.Tabs.Where(TabLifecycleHelpers.IsInUnpinnedPool).ToList();
Config.Tabs.RemoveAll(TabLifecycleHelpers.ShouldStripOnSave);
Interface.SavePluginConfig(Config); Interface.SavePluginConfig(Config);
Config.Tabs.AddRange(unpinnedTempTabs); Config.Tabs.AddRange(unpinnedTempTabs);
}
} }
internal void LanguageChanged(string langCode) internal void LanguageChanged(string langCode)
@@ -1086,9 +1355,6 @@ public sealed class Plugin : IAsyncDalamudPlugin
private void FrameworkUpdate(IFramework framework) private void FrameworkUpdate(IFramework framework)
{ {
if (DeferredSaveFrames >= 0 && DeferredSaveFrames-- == 0)
SaveConfig();
if (!Config.HideChat) if (!Config.HideChat)
return; return;
+54 -14
View File
@@ -101,6 +101,18 @@ internal static class PluginHostFactory
)); ));
services.AddSingleton(_ => new Ui.StyleEngine.TokenResolver()); services.AddSingleton(_ => new Ui.StyleEngine.TokenResolver());
// Transient: each surface owns its motes, so two backdrops on screen do
// not drift in lockstep.
services.AddSingleton(sp => new Ui.Components.Settings.SectionRenderer(
sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
));
services.AddTransient(sp => new Ui.StyleEngine.SurfaceBackdrop(
sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
));
services.AddSingleton(sp => new Ui.StyleEngine.PushStack( services.AddSingleton(sp => new Ui.StyleEngine.PushStack(
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>() sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
)); ));
@@ -160,15 +172,21 @@ internal static class PluginHostFactory
() => sp.GetRequiredService<Plugin>().MainWindow.UserHide() () => sp.GetRequiredService<Plugin>().MainWindow.UserHide()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.TabSidebar( services.AddSingleton(sp => new Ui.Components.Settings.TabSidebar(
sp.GetRequiredService<FontManager>() sp.GetRequiredService<FontManager>(),
sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
));
services.AddSingleton(sp => new Ui.Components.Settings.ContentArea(
sp.GetRequiredService<Ui.StyleEngine.SurfaceBackdrop>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.ContentArea());
services.AddSingleton(sp => new Ui.Components.Settings.ThemePicker( services.AddSingleton(sp => new Ui.Components.Settings.ThemePicker(
sp.GetRequiredService<ThemeRegistry>(), sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Plugin>() sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<Ui.Components.Settings.SectionRenderer>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.ColorPicker( services.AddSingleton(sp => new Ui.Components.Settings.ColorPicker(
sp.GetRequiredService<ThemeRegistry>() sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Ui.Components.Settings.SectionRenderer>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.LivePreviewPanel( services.AddSingleton(sp => new Ui.Components.Settings.LivePreviewPanel(
sp.GetRequiredService<ThemeRegistry>(), sp.GetRequiredService<ThemeRegistry>(),
@@ -181,11 +199,13 @@ internal static class PluginHostFactory
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.FontsSection( services.AddSingleton(sp => new Ui.Components.Settings.FontsSection(
sp.GetRequiredService<Plugin>(), sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<FontManager>() sp.GetRequiredService<FontManager>(),
sp.GetRequiredService<Ui.Components.Settings.SectionRenderer>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.ChatColourPicker( services.AddSingleton(sp => new Ui.Components.Settings.ChatColourPicker(
sp.GetRequiredService<Plugin>(), sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<ThemeRegistry>() sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Ui.Components.Settings.SectionRenderer>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AppearanceTab( services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AppearanceTab(
sp.GetRequiredService<Ui.Components.Settings.ThemePicker>(), sp.GetRequiredService<Ui.Components.Settings.ThemePicker>(),
@@ -196,19 +216,26 @@ internal static class PluginHostFactory
sp.GetRequiredService<Ui.Components.Settings.ChatColourPicker>() sp.GetRequiredService<Ui.Components.Settings.ChatColourPicker>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab( services.AddSingleton(sp => new Ui.Components.Settings.Tabs.GeneralTab(
sp.GetRequiredService<Plugin>() sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<FontManager>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.ChatTab( services.AddSingleton(sp => new Ui.Components.Settings.Tabs.ChatTab(
sp.GetRequiredService<Plugin>() sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.WindowTab( services.AddSingleton(sp => new Ui.Components.Settings.Tabs.WindowTab(
sp.GetRequiredService<Plugin>() sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.ChannelsTab( services.AddSingleton(sp => new Ui.Components.Settings.Tabs.ChannelsTab(
sp.GetRequiredService<Plugin>() sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.DataPrivacyTab( services.AddSingleton(sp => new Ui.Components.Settings.Tabs.DataPrivacyTab(
sp.GetRequiredService<Plugin>() sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>(),
sp.GetRequiredService<ILogger<Ui.Components.Settings.Tabs.DataPrivacyTab>>()
)); ));
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AboutTab( services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AboutTab(
sp.GetRequiredService<FontManager>(), sp.GetRequiredService<FontManager>(),
@@ -222,7 +249,9 @@ internal static class PluginHostFactory
sp.GetRequiredService<FontManager>() sp.GetRequiredService<FontManager>()
)); ));
services.AddSingleton(sp => new Ui.Components.TopTabBar( services.AddSingleton(sp => new Ui.Components.TopTabBar(
sp.GetRequiredService<Ui.Windows.ChannelPopoutPool>() sp.GetRequiredService<Ui.Windows.ChannelPopoutPool>(),
sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
)); ));
services.AddSingleton(sp => new Ui.Windows.MainWindow( services.AddSingleton(sp => new Ui.Windows.MainWindow(
sp.GetRequiredService<Ui.Components.HonorificHeader>(), sp.GetRequiredService<Ui.Components.HonorificHeader>(),
@@ -231,7 +260,9 @@ internal static class PluginHostFactory
sp.GetRequiredService<Ui.Components.MessageList>(), sp.GetRequiredService<Ui.Components.MessageList>(),
sp.GetRequiredService<Ui.Components.InputBar>(), sp.GetRequiredService<Ui.Components.InputBar>(),
sp.GetRequiredService<Ui.Components.StatusBar>(), sp.GetRequiredService<Ui.Components.StatusBar>(),
sp.GetRequiredService<Lender<PayloadHandler>>() sp.GetRequiredService<Lender<PayloadHandler>>(),
sp.GetRequiredService<Ui.Windows.ChannelPopoutPool>(),
sp.GetRequiredService<Ui.StyleEngine.SurfaceBackdrop>()
)); ));
services.AddSingleton(sp => new Integrations.FailedTellNotifier( services.AddSingleton(sp => new Integrations.FailedTellNotifier(
sp.GetRequiredService<ILogger<Integrations.FailedTellNotifier>>() sp.GetRequiredService<ILogger<Integrations.FailedTellNotifier>>()
@@ -294,7 +325,10 @@ internal static class PluginHostFactory
sp.GetRequiredService<Ui.CommandHelpWindow>() sp.GetRequiredService<Ui.CommandHelpWindow>()
), ),
sp.GetRequiredService<ILogger<Ui.Windows.ChannelPopoutWindow>>(), sp.GetRequiredService<ILogger<Ui.Windows.ChannelPopoutWindow>>(),
sp.GetRequiredService<FontManager>() sp.GetRequiredService<FontManager>(),
sp.GetRequiredService<Ui.StyleEngine.SurfaceBackdrop>(),
sp.GetRequiredService<ThemeRegistry>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
) )
); );
services.AddSingleton(sp => new Ui.Windows.ChannelPopoutPool( services.AddSingleton(sp => new Ui.Windows.ChannelPopoutPool(
@@ -339,6 +373,12 @@ internal static class PluginHostFactory
sp.GetRequiredService<ILogger<CommandHelpWindow>>() sp.GetRequiredService<ILogger<CommandHelpWindow>>()
)); ));
services.AddSingleton(sp => new SeStringDebugger(sp.GetRequiredService<Plugin>())); services.AddSingleton(sp => new SeStringDebugger(sp.GetRequiredService<Plugin>()));
#if DEBUG
services.AddSingleton(sp => new Ui.Windows.WidgetGalleryWindow(
sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
));
#endif
services.AddSingleton(sp => new DebuggerWindow( services.AddSingleton(sp => new DebuggerWindow(
sp.GetRequiredService<Plugin>(), sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<PayloadHandler>() sp.GetRequiredService<PayloadHandler>()
+3
View File
@@ -64,6 +64,9 @@ internal sealed class PluginLifecycle : IAsyncDisposable
plugin.WindowSystem.AddWindow(Plugin.InputPreview); plugin.WindowSystem.AddWindow(Plugin.InputPreview);
plugin.WindowSystem.AddWindow(plugin.CommandHelpWindow); plugin.WindowSystem.AddWindow(plugin.CommandHelpWindow);
plugin.WindowSystem.AddWindow(plugin.SeStringDebugger); plugin.WindowSystem.AddWindow(plugin.SeStringDebugger);
#if DEBUG
plugin.WindowSystem.AddWindow(plugin.WidgetGallery);
#endif
plugin.WindowSystem.AddWindow(plugin.DebuggerWindow); plugin.WindowSystem.AddWindow(plugin.DebuggerWindow);
plugin.WindowSystem.AddWindow(plugin.FirstRunWizard); plugin.WindowSystem.AddWindow(plugin.FirstRunWizard);
+154
View File
@@ -0,0 +1,154 @@
using HellionChat.Code;
using HellionChat.Resources;
namespace HellionChat.Privacy;
// The eight buckets the privacy surface sorts channels into. Eighty-nine
// checkboxes in one flat list is not a choice anybody makes; eight named groups
// is. Shared by the export form and the persist grid so the two screens
// describe channels the same way.
//
// Headings are functions, not strings, so a language switch at runtime relabels
// them on the next frame. A captured string would keep the language the window
// happened to be opened in.
//
// Game Master channels follow ChatTypeExt.Parent(), which already pairs each of
// them with its player counterpart. Filing them all under system traffic reads
// tidier but puts GmTell -- a private two-person conversation -- outside the
// direct-messages group, and an access request that quietly drops part of what
// it promises is the dangerous kind of gap.
//
// Every ChatType belongs to exactly one group, and a build-suite test pins that.
// A channel in no group cannot be picked in any of these screens, which reads as
// a missing checkbox rather than as an omission.
internal static class ChannelGroups
{
internal static readonly (Func<string> Heading, ChatType[] Types)[] All =
[
(
() => HellionStrings.Privacy_Group_DirectMessages,
[ChatType.TellIncoming, ChatType.TellOutgoing, ChatType.GmTell]
),
(
() => HellionStrings.Privacy_Group_PartyAlliance,
[
ChatType.Party,
ChatType.CrossParty,
ChatType.Alliance,
ChatType.PvpTeam,
ChatType.PvpTeamAnnouncement,
ChatType.PvpTeamLoginLogout,
ChatType.GmParty,
]
),
(
() => HellionStrings.Privacy_Group_FreeCompany,
[
ChatType.FreeCompany,
ChatType.FreeCompanyAnnouncement,
ChatType.FreeCompanyLoginLogout,
ChatType.GmFreeCompany,
]
),
(
() => HellionStrings.Privacy_Group_Linkshells,
[
ChatType.Linkshell1,
ChatType.Linkshell2,
ChatType.Linkshell3,
ChatType.Linkshell4,
ChatType.Linkshell5,
ChatType.Linkshell6,
ChatType.Linkshell7,
ChatType.Linkshell8,
ChatType.GmLinkshell1,
ChatType.GmLinkshell2,
ChatType.GmLinkshell3,
ChatType.GmLinkshell4,
ChatType.GmLinkshell5,
ChatType.GmLinkshell6,
ChatType.GmLinkshell7,
ChatType.GmLinkshell8,
]
),
(
() => HellionStrings.Privacy_Group_CrossLinkshells,
[
ChatType.CrossLinkshell1,
ChatType.CrossLinkshell2,
ChatType.CrossLinkshell3,
ChatType.CrossLinkshell4,
ChatType.CrossLinkshell5,
ChatType.CrossLinkshell6,
ChatType.CrossLinkshell7,
ChatType.CrossLinkshell8,
]
),
(
() => HellionStrings.Privacy_Group_ExtraChat,
[
ChatType.ExtraChatLinkshell1,
ChatType.ExtraChatLinkshell2,
ChatType.ExtraChatLinkshell3,
ChatType.ExtraChatLinkshell4,
ChatType.ExtraChatLinkshell5,
ChatType.ExtraChatLinkshell6,
ChatType.ExtraChatLinkshell7,
ChatType.ExtraChatLinkshell8,
]
),
(
() => HellionStrings.Privacy_Group_PublicChat,
[
ChatType.Say,
ChatType.Shout,
ChatType.Yell,
ChatType.NoviceNetwork,
ChatType.NoviceNetworkSystem,
ChatType.CustomEmote,
ChatType.StandardEmote,
ChatType.GmSay,
ChatType.GmShout,
ChatType.GmYell,
ChatType.GmNoviceNetwork,
]
),
(
() => HellionStrings.Privacy_Group_SystemLogs,
[
ChatType.System,
ChatType.Notice,
ChatType.Urgent,
ChatType.Echo,
ChatType.NpcDialogue,
ChatType.NpcAnnouncement,
ChatType.LootNotice,
ChatType.LootRoll,
ChatType.RetainerSale,
ChatType.Crafting,
ChatType.Gathering,
ChatType.Sign,
ChatType.RandomNumber,
ChatType.MessageBook,
ChatType.Alarm,
ChatType.Orchestrion,
ChatType.GlamourNotifications,
ChatType.PeriodicRecruitmentNotification,
ChatType.GatheringSystem,
ChatType.Progress,
ChatType.Debug,
ChatType.Error,
ChatType.Item,
ChatType.Action,
ChatType.BattleSystem,
ChatType.Damage,
ChatType.Healing,
ChatType.Miss,
ChatType.GainBuff,
ChatType.GainDebuff,
ChatType.LoseBuff,
ChatType.LoseDebuff,
]
),
];
}
+51
View File
@@ -0,0 +1,51 @@
namespace HellionChat.Privacy;
// The rule that decides whether a message is written to disk, as plain logic.
//
// It lives apart from Configuration because Configuration implements a Dalamud
// interface, and the build suite cannot load Dalamud.dll -- the runtime resolves
// the declaring type before it ever reaches the method body, so even a static
// call on it fails. This is the single most consequential branch in the plugin,
// and it belongs where it can be pinned.
internal static class StorageRule
{
// v1.12.0 corrected the last term. A known channel the user had unticked
// used to fall through to the unknown-type failsafe, so the channel grid did
// nothing at all whenever that failsafe was on. It is on by default, so the
// filter stored everything while its own description promised "only messages
// from allowed channels are written to the database".
internal static bool Allows(bool listed, bool knownType, bool persistUnknownTypes) =>
listed || (!knownType && persistUnknownTypes);
// Carry-over for configs written before that correction. Where the failsafe
// made the list irrelevant and no channel was ever picked, the old rule
// stored everything; the corrected rule would store nothing. Switching the
// filter off keeps the behaviour and states it where the user can see it.
//
// A config that does have picks keeps them and starts honouring them, which
// is the point of the change.
internal static bool ShouldDisableFilterOnV24(
bool filterEnabled,
bool persistUnknownTypes,
int listedCount
) => filterEnabled && persistUnknownTypes && listedCount == 0;
// Why a retroactive cleanup cannot be offered, or that it can.
internal enum CleanupAvailability
{
Available,
// Nothing is filtered, so nothing in the database contradicts the rule.
FilterDisabled,
// The rule keeps no channel at all. CleanupRetainOnly refuses an empty
// allowlist on purpose -- that request is a full wipe, and a full wipe
// has its own button with its own confirmation.
NothingListed,
}
internal static CleanupAvailability CleanupState(bool filterEnabled, int listedCount) =>
!filterEnabled ? CleanupAvailability.FilterDisabled
: listedCount == 0 ? CleanupAvailability.NothingListed
: CleanupAvailability.Available;
}
+107 -35
View File
@@ -59,11 +59,10 @@ internal class HellionStrings
internal static string Privacy_PersistUnknown_Name => Get(nameof(Privacy_PersistUnknown_Name)); internal static string Privacy_PersistUnknown_Name => Get(nameof(Privacy_PersistUnknown_Name));
internal static string Privacy_PersistUnknown_Description => Get(nameof(Privacy_PersistUnknown_Description)); internal static string Privacy_PersistUnknown_Description => Get(nameof(Privacy_PersistUnknown_Description));
internal static string Cleanup_Heading => Get(nameof(Cleanup_Heading)); internal static string Cleanup_Unavailable_FilterOff => Get(nameof(Cleanup_Unavailable_FilterOff));
internal static string Cleanup_Unavailable_NothingListed => Get(nameof(Cleanup_Unavailable_NothingListed));
internal static string Cleanup_Help_Intro => Get(nameof(Cleanup_Help_Intro)); internal static string Cleanup_Help_Intro => Get(nameof(Cleanup_Help_Intro));
internal static string Cleanup_Help_SavedNote => Get(nameof(Cleanup_Help_SavedNote));
internal static string Cleanup_Preview_Stale => Get(nameof(Cleanup_Preview_Stale)); internal static string Cleanup_Preview_Stale => Get(nameof(Cleanup_Preview_Stale));
internal static string Retention_Help_SavedNote => Get(nameof(Retention_Help_SavedNote));
internal static string Cleanup_RefreshPreview => Get(nameof(Cleanup_RefreshPreview)); internal static string Cleanup_RefreshPreview => Get(nameof(Cleanup_RefreshPreview));
internal static string Cleanup_NoPreview => Get(nameof(Cleanup_NoPreview)); internal static string Cleanup_NoPreview => Get(nameof(Cleanup_NoPreview));
internal static string Cleanup_TotalStored => Get(nameof(Cleanup_TotalStored)); internal static string Cleanup_TotalStored => Get(nameof(Cleanup_TotalStored));
@@ -92,8 +91,8 @@ internal class HellionStrings
internal static string Retention_Tag_Global => Get(nameof(Retention_Tag_Global)); internal static string Retention_Tag_Global => Get(nameof(Retention_Tag_Global));
internal static string Retention_Reset_Button => Get(nameof(Retention_Reset_Button)); internal static string Retention_Reset_Button => Get(nameof(Retention_Reset_Button));
internal static string Retention_Apply_Label => Get(nameof(Retention_Apply_Label)); internal static string Retention_Apply_Label => Get(nameof(Retention_Apply_Label));
internal static string Retention_Apply_Tooltip => Get(nameof(Retention_Apply_Tooltip));
internal static string Retention_Running => Get(nameof(Retention_Running)); internal static string Retention_Running => Get(nameof(Retention_Running));
internal static string Retention_RunNow_Tooltip => Get(nameof(Retention_RunNow_Tooltip));
internal static string Retention_LastRun_Never => Get(nameof(Retention_LastRun_Never)); internal static string Retention_LastRun_Never => Get(nameof(Retention_LastRun_Never));
internal static string Retention_LastRun_At => Get(nameof(Retention_LastRun_At)); internal static string Retention_LastRun_At => Get(nameof(Retention_LastRun_At));
internal static string Retention_Success => Get(nameof(Retention_Success)); internal static string Retention_Success => Get(nameof(Retention_Success));
@@ -131,7 +130,6 @@ internal class HellionStrings
internal static string Wizard_Step3_Section_History => Get(nameof(Wizard_Step3_Section_History)); internal static string Wizard_Step3_Section_History => Get(nameof(Wizard_Step3_Section_History));
internal static string Wizard_Step3_Section_TellTabs => Get(nameof(Wizard_Step3_Section_TellTabs)); internal static string Wizard_Step3_Section_TellTabs => Get(nameof(Wizard_Step3_Section_TellTabs));
internal static string Wizard_Step3_Section_Visual => Get(nameof(Wizard_Step3_Section_Visual)); internal static string Wizard_Step3_Section_Visual => Get(nameof(Wizard_Step3_Section_Visual));
internal static string Wizard_Step3_LoadPreviousSession_Label => Get(nameof(Wizard_Step3_LoadPreviousSession_Label));
internal static string Wizard_Step3_FilterIncludePreviousSessions_Label => Get(nameof(Wizard_Step3_FilterIncludePreviousSessions_Label)); internal static string Wizard_Step3_FilterIncludePreviousSessions_Label => Get(nameof(Wizard_Step3_FilterIncludePreviousSessions_Label));
internal static string Wizard_Step3_AutoTellTabsHistoryPreload_Label => Get(nameof(Wizard_Step3_AutoTellTabsHistoryPreload_Label)); internal static string Wizard_Step3_AutoTellTabsHistoryPreload_Label => Get(nameof(Wizard_Step3_AutoTellTabsHistoryPreload_Label));
internal static string Wizard_Step3_UseCompactDensity_Label => Get(nameof(Wizard_Step3_UseCompactDensity_Label)); internal static string Wizard_Step3_UseCompactDensity_Label => Get(nameof(Wizard_Step3_UseCompactDensity_Label));
@@ -144,10 +142,10 @@ internal class HellionStrings
internal static string Wizard_Step4_Summary_TellTabs => Get(nameof(Wizard_Step4_Summary_TellTabs)); internal static string Wizard_Step4_Summary_TellTabs => Get(nameof(Wizard_Step4_Summary_TellTabs));
internal static string Wizard_Step4_Summary_Visual => Get(nameof(Wizard_Step4_Summary_Visual)); internal static string Wizard_Step4_Summary_Visual => Get(nameof(Wizard_Step4_Summary_Visual));
internal static string Wizard_Step4_Summary_Unchanged => Get(nameof(Wizard_Step4_Summary_Unchanged)); internal static string Wizard_Step4_Summary_Unchanged => Get(nameof(Wizard_Step4_Summary_Unchanged));
internal static string Wizard_Step4_Summary_Off => Get(nameof(Wizard_Step4_Summary_Off));
internal static string Wizard_Step4_TestHint => Get(nameof(Wizard_Step4_TestHint)); internal static string Wizard_Step4_TestHint => Get(nameof(Wizard_Step4_TestHint));
internal static string Wizard_Step4_SettingsHint => Get(nameof(Wizard_Step4_SettingsHint)); internal static string Wizard_Step4_SettingsHint => Get(nameof(Wizard_Step4_SettingsHint));
internal static string Export_Heading => Get(nameof(Export_Heading));
internal static string Export_Help => Get(nameof(Export_Help)); internal static string Export_Help => Get(nameof(Export_Help));
internal static string Export_Range_Label => Get(nameof(Export_Range_Label)); internal static string Export_Range_Label => Get(nameof(Export_Range_Label));
internal static string Export_Sender_Label => Get(nameof(Export_Sender_Label)); internal static string Export_Sender_Label => Get(nameof(Export_Sender_Label));
@@ -233,8 +231,6 @@ internal class HellionStrings
internal static string Privacy_AutoTellTabs_Preload_Hint => Get(nameof(Privacy_AutoTellTabs_Preload_Hint)); internal static string Privacy_AutoTellTabs_Preload_Hint => Get(nameof(Privacy_AutoTellTabs_Preload_Hint));
// Hellion Chat — Settings UX Polish v10 wipe migration // Hellion Chat — Settings UX Polish v10 wipe migration
internal static string SettingsRefactor_Migration_Title => Get(nameof(SettingsRefactor_Migration_Title));
internal static string SettingsRefactor_Migration_Content => Get(nameof(SettingsRefactor_Migration_Content));
// Hellion Chat — Settings UX Polish 8-tab structure // Hellion Chat — Settings UX Polish 8-tab structure
internal static string Settings_Tab_General => Get(nameof(Settings_Tab_General)); internal static string Settings_Tab_General => Get(nameof(Settings_Tab_General));
@@ -246,22 +242,6 @@ internal class HellionStrings
internal static string Settings_Tab_Information => Get(nameof(Settings_Tab_Information)); internal static string Settings_Tab_Information => Get(nameof(Settings_Tab_Information));
// v1.1.0 — Settings card-grid overview // v1.1.0 — Settings card-grid overview
internal static string Settings_Card_General_Title => Get(nameof(Settings_Card_General_Title));
internal static string Settings_Card_General_Subtext => Get(nameof(Settings_Card_General_Subtext));
internal static string Settings_Card_Appearance_Title => Get(nameof(Settings_Card_Appearance_Title));
internal static string Settings_Card_Appearance_Subtext => Get(nameof(Settings_Card_Appearance_Subtext));
internal static string Settings_Card_Themes_Title => Get(nameof(Settings_Card_Themes_Title));
internal static string Settings_Card_Themes_Subtext => Get(nameof(Settings_Card_Themes_Subtext));
internal static string Settings_Card_Window_Title => Get(nameof(Settings_Card_Window_Title));
internal static string Settings_Card_Window_Subtext => Get(nameof(Settings_Card_Window_Subtext));
internal static string Settings_Card_Chat_Title => Get(nameof(Settings_Card_Chat_Title));
internal static string Settings_Card_Chat_Subtext => Get(nameof(Settings_Card_Chat_Subtext));
internal static string Settings_Card_Tabs_Title => Get(nameof(Settings_Card_Tabs_Title));
internal static string Settings_Card_Tabs_Subtext => Get(nameof(Settings_Card_Tabs_Subtext));
internal static string Settings_Card_Database_Title => Get(nameof(Settings_Card_Database_Title));
internal static string Settings_Card_Database_Subtext => Get(nameof(Settings_Card_Database_Subtext));
internal static string Settings_Card_Information_Title => Get(nameof(Settings_Card_Information_Title));
internal static string Settings_Card_Information_Subtext => Get(nameof(Settings_Card_Information_Subtext));
// v1.1.0 — Themes-Settings-Tab // v1.1.0 — Themes-Settings-Tab
internal static string Settings_Tab_Themes => Get(nameof(Settings_Tab_Themes)); internal static string Settings_Tab_Themes => Get(nameof(Settings_Tab_Themes));
@@ -290,9 +270,15 @@ internal class HellionStrings
internal static string Settings_Chat_SymbolPicker_Enable_Description => Get(nameof(Settings_Chat_SymbolPicker_Enable_Description)); internal static string Settings_Chat_SymbolPicker_Enable_Description => Get(nameof(Settings_Chat_SymbolPicker_Enable_Description));
// Hellion Chat — Database-Tab section headings // Hellion Chat — Database-Tab section headings
internal static string Settings_Database_Storage_Heading => Get(nameof(Settings_Database_Storage_Heading)); internal static string Settings_Database_Busy => Get(nameof(Settings_Database_Busy));
internal static string Settings_Database_Viewer_Heading => Get(nameof(Settings_Database_Viewer_Heading)); internal static string Settings_Database_ClearHint => Get(nameof(Settings_Database_ClearHint));
internal static string Settings_Database_Stats_Heading => Get(nameof(Settings_Database_Stats_Heading)); internal static string Settings_Database_ClearError => Get(nameof(Settings_Database_ClearError));
internal static string Settings_Database_Op_RetentionSweep => Get(nameof(Settings_Database_Op_RetentionSweep));
internal static string Settings_Database_Op_Export => Get(nameof(Settings_Database_Op_Export));
internal static string Settings_Database_Op_Cleanup => Get(nameof(Settings_Database_Op_Cleanup));
internal static string Settings_Database_Op_Clear => Get(nameof(Settings_Database_Op_Clear));
internal static string Settings_Database_Op_Preview => Get(nameof(Settings_Database_Op_Preview));
internal static string Settings_Database_Op_Maintenance => Get(nameof(Settings_Database_Op_Maintenance));
// Hellion Chat — Default tab presets (channel-themed) // Hellion Chat — Default tab presets (channel-themed)
internal static string Tabs_Presets_System => Get(nameof(Tabs_Presets_System)); internal static string Tabs_Presets_System => Get(nameof(Tabs_Presets_System));
@@ -328,14 +314,8 @@ internal class HellionStrings
internal static string Settings_Window_ResetPosition_Description => Get(nameof(Settings_Window_ResetPosition_Description)); internal static string Settings_Window_ResetPosition_Description => Get(nameof(Settings_Window_ResetPosition_Description));
// Hellion Chat — v0.6.0 one-time hint banner shown inside pop-outs // Hellion Chat — v0.6.0 one-time hint banner shown inside pop-outs
internal static string Popout_v060_HintText => Get(nameof(Popout_v060_HintText));
internal static string Popout_v060_HintAck => Get(nameof(Popout_v060_HintAck));
internal static string Popout_v060_HintOpenSettings => Get(nameof(Popout_v060_HintOpenSettings));
// Hellion Chat — v0.6.1 pop-out header hint banner (discoverability) // Hellion Chat — v0.6.1 pop-out header hint banner (discoverability)
internal static string Hint_v061_PopOutHeader_Body => Get(nameof(Hint_v061_PopOutHeader_Body));
internal static string Hint_v061_PopOutHeader_Ack => Get(nameof(Hint_v061_PopOutHeader_Ack));
internal static string Hint_v061_PopOutHeader_OpenSettings => Get(nameof(Hint_v061_PopOutHeader_OpenSettings));
// Hellion Chat — v1.0.0 Chat 2 parallel-load conflict detection // Hellion Chat — v1.0.0 Chat 2 parallel-load conflict detection
internal static string ChatTwoConflictTitle => Get(nameof(ChatTwoConflictTitle)); internal static string ChatTwoConflictTitle => Get(nameof(ChatTwoConflictTitle));
@@ -352,7 +332,6 @@ internal class HellionStrings
// Hellion Chat — v1.2.1 Settings Cleanup: new card titles + subtexts // Hellion Chat — v1.2.1 Settings Cleanup: new card titles + subtexts
internal static string Settings_Card_DataManagement_Title => Get(nameof(Settings_Card_DataManagement_Title)); internal static string Settings_Card_DataManagement_Title => Get(nameof(Settings_Card_DataManagement_Title));
internal static string Settings_Card_DataManagement_Subtext => Get(nameof(Settings_Card_DataManagement_Subtext));
// Hellion Chat — v1.2.1 Theme & Layout tab section headings + WindowOpacity slider // Hellion Chat — v1.2.1 Theme & Layout tab section headings + WindowOpacity slider
internal static string Settings_ThemeAndLayout_Theme_Heading => Get(nameof(Settings_ThemeAndLayout_Theme_Heading)); internal static string Settings_ThemeAndLayout_Theme_Heading => Get(nameof(Settings_ThemeAndLayout_Theme_Heading));
@@ -370,10 +349,95 @@ internal class HellionStrings
internal static string Settings_Section_Retention => Get(nameof(Settings_Section_Retention)); internal static string Settings_Section_Retention => Get(nameof(Settings_Section_Retention));
internal static string Settings_Section_Cleanup => Get(nameof(Settings_Section_Cleanup)); internal static string Settings_Section_Cleanup => Get(nameof(Settings_Section_Cleanup));
internal static string Settings_Section_Export => Get(nameof(Settings_Section_Export)); internal static string Settings_Section_Export => Get(nameof(Settings_Section_Export));
internal static string Settings_Section_Telemetry => Get(nameof(Settings_Section_Telemetry));
internal static string Settings_Theme_ActiveTheme => Get(nameof(Settings_Theme_ActiveTheme));
internal static string Settings_Theme_ForkAndEdit => Get(nameof(Settings_Theme_ForkAndEdit));
internal static string Settings_Theme_ForkTooltip => Get(nameof(Settings_Theme_ForkTooltip));
internal static string Settings_Theme_EditTheme => Get(nameof(Settings_Theme_EditTheme));
internal static string Settings_Theme_Editing => Get(nameof(Settings_Theme_Editing));
internal static string Settings_Theme_Group_Surfaces => Get(nameof(Settings_Theme_Group_Surfaces));
internal static string Settings_Theme_Group_Borders => Get(nameof(Settings_Theme_Group_Borders));
internal static string Settings_Theme_Group_Text => Get(nameof(Settings_Theme_Group_Text));
internal static string Settings_Theme_Group_Identity => Get(nameof(Settings_Theme_Group_Identity));
internal static string Settings_Theme_Group_Status => Get(nameof(Settings_Theme_Group_Status));
internal static string Settings_Theme_Save => Get(nameof(Settings_Theme_Save));
internal static string Settings_Theme_Cancel => Get(nameof(Settings_Theme_Cancel));
internal static string Settings_Theme_ResetToSource => Get(nameof(Settings_Theme_ResetToSource));
internal static string Settings_Theme_ResetUnavailable => Get(nameof(Settings_Theme_ResetUnavailable));
internal static string Settings_Theme_LockedTooltip => Get(nameof(Settings_Theme_LockedTooltip));
internal static string Settings_Theme_Custom => Get(nameof(Settings_Theme_Custom));
internal static string Settings_Tabs_Duplicate => Get(nameof(Settings_Tabs_Duplicate));
internal static string Settings_Theme_ForkActive => Get(nameof(Settings_Theme_ForkActive));
internal static string Settings_Theme_ImportFile => Get(nameof(Settings_Theme_ImportFile));
internal static string Settings_Theme_ImportPathHint => Get(nameof(Settings_Theme_ImportPathHint));
internal static string Settings_Theme_ExportDialogTitle => Get(nameof(Settings_Theme_ExportDialogTitle));
internal static string Settings_Theme_Category_Cool => Get(nameof(Settings_Theme_Category_Cool));
internal static string Settings_Theme_Category_Natural => Get(nameof(Settings_Theme_Category_Natural));
internal static string Settings_Theme_Category_Classic => Get(nameof(Settings_Theme_Category_Classic));
internal static string Settings_Theme_Category_Retro => Get(nameof(Settings_Theme_Category_Retro));
internal static string Settings_Fonts_Bundled => Get(nameof(Settings_Fonts_Bundled));
internal static string Settings_Fonts_GameFont => Get(nameof(Settings_Fonts_GameFont));
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));
internal static string Settings_Section_Notifications => Get(nameof(Settings_Section_Notifications));
internal static string Settings_Section_DisplayModes => Get(nameof(Settings_Section_DisplayModes));
internal static string Settings_Section_History => Get(nameof(Settings_Section_History));
internal static string Settings_Section_CommandHelp => Get(nameof(Settings_Section_CommandHelp));
internal static string Settings_Section_PluginDisclosure => Get(nameof(Settings_Section_PluginDisclosure));
internal static string Settings_Section_LayoutMode => Get(nameof(Settings_Section_LayoutMode));
internal static string Settings_Section_Opacity => Get(nameof(Settings_Section_Opacity));
internal static string Settings_Section_ResizeBehaviour => Get(nameof(Settings_Section_ResizeBehaviour));
internal static string Settings_Section_TellAutoOpen => Get(nameof(Settings_Section_TellAutoOpen));
internal static string Settings_Section_Sidebar => Get(nameof(Settings_Section_Sidebar));
internal static string Settings_Section_Brand => Get(nameof(Settings_Section_Brand));
internal static string Settings_Section_Integrations => Get(nameof(Settings_Section_Integrations));
internal static string Settings_Section_Credits => Get(nameof(Settings_Section_Credits));
internal static string Settings_Section_License => Get(nameof(Settings_Section_License));
internal static string Settings_Keybinds_Hint => Get(nameof(Settings_Keybinds_Hint));
internal static string Settings_Keybinds_CycleNext => Get(nameof(Settings_Keybinds_CycleNext));
internal static string Settings_Keybinds_CyclePrevious => Get(nameof(Settings_Keybinds_CyclePrevious));
internal static string Settings_General_Language_Description => Get(nameof(Settings_General_Language_Description));
internal static string Settings_Chat_Clock24_Name => Get(nameof(Settings_Chat_Clock24_Name));
internal static string Settings_Chat_PreviousSessions_Name => Get(nameof(Settings_Chat_PreviousSessions_Name));
internal static string Settings_Chat_PreviousSessions_Description => Get(nameof(Settings_Chat_PreviousSessions_Description));
internal static string Settings_Chat_CommandHelpSide_Name => Get(nameof(Settings_Chat_CommandHelpSide_Name));
internal static string Settings_Chat_CommandHelpSide_Description => Get(nameof(Settings_Chat_CommandHelpSide_Description));
internal static string Settings_Channels_TellAutoOpenMode_Name => Get(nameof(Settings_Channels_TellAutoOpenMode_Name));
internal static string Settings_Channels_TellAutoOpenMode_Description => Get(nameof(Settings_Channels_TellAutoOpenMode_Description));
internal static string Settings_Channels_TellSwitchAlways_Name => Get(nameof(Settings_Channels_TellSwitchAlways_Name));
internal static string Settings_Channels_TellSwitchAlways_Description => Get(nameof(Settings_Channels_TellSwitchAlways_Description));
internal static string Settings_Window_LayoutSidebar => Get(nameof(Settings_Window_LayoutSidebar));
internal static string Settings_Window_LayoutTopTabs => Get(nameof(Settings_Window_LayoutTopTabs));
internal static string Settings_Window_TabPlacement_Name => Get(nameof(Settings_Window_TabPlacement_Name));
internal static string Settings_Window_TabPlacement_Description => Get(nameof(Settings_Window_TabPlacement_Description));
internal static string Settings_Window_TitleBar_Name => Get(nameof(Settings_Window_TitleBar_Name));
internal static string Settings_Window_PopoutTitleBar_Name => Get(nameof(Settings_Window_PopoutTitleBar_Name));
internal static string Settings_Window_AllowMove_Name => Get(nameof(Settings_Window_AllowMove_Name));
internal static string Settings_Window_AllowResize_Name => Get(nameof(Settings_Window_AllowResize_Name));
internal static string Settings_Window_SidebarThreshold_Name => Get(nameof(Settings_Window_SidebarThreshold_Name));
internal static string Settings_Window_SidebarThreshold_Description => Get(nameof(Settings_Window_SidebarThreshold_Description));
internal static string Settings_Window_PreviewPosition_Name => Get(nameof(Settings_Window_PreviewPosition_Name));
internal static string Settings_Window_PreviewOnlyTyping_Name => Get(nameof(Settings_Window_PreviewOnlyTyping_Name));
internal static string Settings_About_GiteaRepo => Get(nameof(Settings_About_GiteaRepo));
internal static string Settings_About_CustomRepo => Get(nameof(Settings_About_CustomRepo));
internal static string Settings_Section_AutoTranslate => Get(nameof(Settings_Section_AutoTranslate));
internal static string Settings_Emotes_Block => Get(nameof(Settings_Emotes_Block));
internal static string Settings_Telemetry_None => Get(nameof(Settings_Telemetry_None));
internal static string Settings_Section_Database => Get(nameof(Settings_Section_Database)); internal static string Settings_Section_Database => Get(nameof(Settings_Section_Database));
// Hellion Chat — v1.2.1 Migration v15 → v16 toast // Hellion Chat — v1.2.1 Migration v15 → v16 toast
internal static string Migration_v16_OverrideStyle_Toast => Get(nameof(Migration_v16_OverrideStyle_Toast));
// Hellion Chat — v1.3.0 Integrations (Honorific + Coming-Soon roadmap) — now in About tab // Hellion Chat — v1.3.0 Integrations (Honorific + Coming-Soon roadmap) — now in About tab
internal static string Settings_Integrations_Intro => Get(nameof(Settings_Integrations_Intro)); internal static string Settings_Integrations_Intro => Get(nameof(Settings_Integrations_Intro));
@@ -413,6 +477,14 @@ internal class HellionStrings
// Hellion Chat — v1.5.4 header quick-picker + reduce-motion toggle // Hellion Chat — v1.5.4 header quick-picker + reduce-motion toggle
internal static string Settings_QuickPicker_Tooltip => Get(nameof(Settings_QuickPicker_Tooltip)); internal static string Settings_QuickPicker_Tooltip => Get(nameof(Settings_QuickPicker_Tooltip));
internal static string InputBar_InsertSymbol_Tooltip =>
Get(nameof(InputBar_InsertSymbol_Tooltip));
internal static string InputBar_Settings_Tooltip => Get(nameof(InputBar_Settings_Tooltip));
internal static string InputBar_HideChat_Tooltip => Get(nameof(InputBar_HideChat_Tooltip));
internal static string InputBar_PopIn_Tooltip => Get(nameof(InputBar_PopIn_Tooltip));
internal static string Settings_QuickPicker_Themes_Header => Get(nameof(Settings_QuickPicker_Themes_Header)); internal static string Settings_QuickPicker_Themes_Header => Get(nameof(Settings_QuickPicker_Themes_Header));
internal static string Settings_QuickPicker_Tabs_Header => Get(nameof(Settings_QuickPicker_Tabs_Header)); internal static string Settings_QuickPicker_Tabs_Header => Get(nameof(Settings_QuickPicker_Tabs_Header));
internal static string Settings_ThemeAndLayout_ReduceMotion_Name => Get(nameof(Settings_ThemeAndLayout_ReduceMotion_Name)); internal static string Settings_ThemeAndLayout_ReduceMotion_Name => Get(nameof(Settings_ThemeAndLayout_ReduceMotion_Name));
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Xarxa de seguretat per als ChatTypes que afegiran futures actualitzacions de FFXIV que el plugin encara no coneix. Per defecte és DESACTIVAT (minimització de dades). Activa-ho si vols que els canals futurs també es registrin completament.</value> <value>Xarxa de seguretat per als ChatTypes que afegiran futures actualitzacions de FFXIV que el plugin encara no coneix. Per defecte és DESACTIVAT (minimització de dades). Activa-ho si vols que els canals futurs també es registrin completament.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Aplica el filtre a la base de dades existent</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>El filtre de privadesa només afecta els missatges nous. La neteja de sota et permet eliminar retroactivament els missatges ja emmagatzemats que no coincideixen amb la teva llista blanca desada.</value> <value>El filtre de privadesa només afecta els missatges nous. La neteja de sota et permet eliminar retroactivament els missatges ja emmagatzemats que no coincideixen amb la teva llista blanca desada.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>La neteja utilitza la teva llista blanca DESADA (Plugin.Config), no els canvis sense desar de dalt. Fes clic a Desa primer si vols que s'apliquin els canvis actuals.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>L'execució manual utilitza la teva política de retenció DESADA, no els valors dels controls de dalt. Fes clic a Desa primer si vols que l'execució apliqui els canvis actuals.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>La previsualització ha quedat obsoleta: la teva llista blanca ha canviat des de l'última actualització. Fes clic a Actualitza per recalcular.</value> <value>La previsualització ha quedat obsoleta: la teva llista blanca ha canviat des de l'última actualització. Fes clic a Actualitza per recalcular.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Aplica la retenció ara</value> <value>Aplica la retenció ara</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Maj: Executa la neteja de retenció immediatament amb la política DESADA. Desa els canvis primer.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>La neteja de retenció s'està executant en segon pla…</value> <value>La neteja de retenció s'està executant en segon pla…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Visual</value> <value>Visual</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Carrega la sessió anterior en iniciar</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Aplica els filtres als missatges de sessions anteriors</value> <value>Aplica els filtres als missatges de sessions anteriors</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Configuració → Hellion Chat per ajustar-ho més tard</value> <value>Configuració → Hellion Chat per ajustar-ho més tard</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Exportació (RGPD Art. 15 — Dret d'accés)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exporta els missatges emmagatzemats en format Markdown, JSON o CSV. Això et permet respondre una sol·licitud d'accés d'una persona els missatges de la qual has guardat, o bé endur-te el teu propi historial.</value> <value>Exporta els missatges emmagatzemats en format Markdown, JSON o CSV. Això et permet respondre una sol·licitud d'accés d'una persona els missatges de la qual has guardat, o bé endur-te el teu propi historial.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Traductors de la comunitat de Chat 2 (upstream)</value> <value>Traductors de la comunitat de Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Tells actius</value> <value>Tells actius</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fixada: sobreviu al reconnectar.</value> <value>Fixada: sobreviu al reconnectar.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Nota: Si XIV Messenger o un plugin similar suprimeix els tells, desactiva l'opció "Suppress DMs" allà perquè Hellion Chat pugui rebre tells i obrir les pestanyes automàtiques.</value> <value>Nota: Si XIV Messenger o un plugin similar suprimeix els tells, desactiva l'opció "Suppress DMs" allà perquè Hellion Chat pugui rebre tells i obrir les pestanyes automàtiques.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Historial de tells a les pestanyes automàtiques</value> <value>Historial de tells a les pestanyes automàtiques</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Només té efecte quan les pestanyes auto-tell estan activades a la pestanya Chat.</value> <value>Només té efecte quan les pestanyes auto-tell estan activades a la pestanya Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Configuració reestructurada</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 ha reestructurat la configuració en pestanyes temàtiques. La teva base de dades de xat i l'historial de missatges no han canviat. La configuració s'ha restablert als valors per defecte. Si vols tornar a seleccionar el teu perfil de privadesa, el botó Reobre es troba a la pestanya Privadesa. Una còpia de seguretat de la configuració anterior es troba a HellionChat.json.pre-v10-backup al costat del fitxer de configuració actiu.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>General</value> <value>General</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Quant a</value> <value>Quant a</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Marques de temps</value> <value>Marques de temps</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Marc de la finestra</value> <value>Marc de la finestra</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Mostra el botó del selector de símbols al costat de l'entrada del xat</value> <value>Mostra el botó del selector de símbols al costat de l'entrada del xat</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Afegeix un petit botó a l'esquerra de l'indicador de canal que obre una finestra emergent amb les icones de FFXIV i una llista de símbols seleccionada. Desactiva-ho si prefereixes una barra d'entrada més senzilla.</value> <value>Afegeix un petit botó a l'esquerra de l'indicador de canal que obre una finestra emergent amb les icones de FFXIV i una llista de símbols seleccionada. Desactiva-ho si prefereixes una barra d'entrada més senzilla.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Emmagatzematge</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Visió general</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Manteniment</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value> <value>Sistema</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Si fas servir diverses linkshells, el mantenidor recomana una pestanya per shell per tenir una visió general més neta. Duplica la pestanya i restringeix la selecció de canal en cada còpia.</value> <value>Si fas servir diverses linkshells, el mantenidor recomana una pestanya per shell per tenir una visió general més neta. Duplica la pestanya i restringeix la selecció de canal en cada còpia.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Icona de la pestanya</value> <value>Icona de la pestanya</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Mou la finestra del xat i totes les finestres emergents actives de tornada a la cantonada superior esquerra del monitor principal. Útil quan una finestra ha acabat fora de l'àrea visible després d'un canvi de disposició de pantalla (monitor desconnectat, resolució canviada). El plugin també fa una comprovació automàtica dels límits un cop per sessió; aquest botó és la sortida manual d'emergència si alguna cosa queda inaccessible de totes maneres.</value> <value>Mou la finestra del xat i totes les finestres emergents actives de tornada a la cantonada superior esquerra del monitor principal. Útil quan una finestra ha acabat fora de l'àrea visible després d'un canvi de disposició de pantalla (monitor desconnectat, resolució canviada). El plugin també fa una comprovació automàtica dels límits un cop per sessió; aquest botó és la sortida manual d'emergència si alguna cosa queda inaccessible de totes maneres.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Novetat a v0.6.0: ara pots escriure directament a les finestres emergents. Activa el commutador principal a la configuració de Finestra.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Entesos</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Obre la configuració de finestra</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Pots obrir qualsevol pestanya del xat com a finestra pròpia. Fes clic a la icona de finestra a la part superior dreta o fes clic dret sobre la pestanya. Novetat a v0.6.1: l'entrada a les finestres emergents està activa per defecte (es pot desactivar a Configuració → Finestra).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Entesos</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Obre la configuració</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat no pot iniciar-se mentre Chat 2 estigui carregat.</value> <value>Hellion Chat no pot iniciar-se mentre Chat 2 estigui carregat.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Desactiva Chat 2 a /xlplugins i torna a activar Hellion Chat.</value> <value>Desactiva Chat 2 a /xlplugins i torna a activar Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>General</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Idioma, entrada, àudio i rendiment.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Aparença</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Opacitat de la finestra, fonts, moviment</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Tria un theme o importa el teu propi</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Finestra</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Quan la finestra és visible i si es pot moure.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Xat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, previsualització, comportament dels missatges i emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Pestanyes</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Crea i configura pestanyes de xat personalitzades.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Base de dades</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Emmagatzematge, migració, neteja de dades antigues</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Quant a</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensions, versió, informació del projecte, traductors i changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Conserva</value> <value>Conserva</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Privadesa primer</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Obert</value> <value>Obert</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Dades i privadesa</value> <value>Dades i privadesa</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtre de privadesa, retenció, neteja, exportació i estadístiques de la base de dades.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avançat (Maj+clic per obrir)</value> <value>Avançat (Maj+clic per obrir)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 ha reorganitzat el menú de configuració i ha eliminat l'antiga opció "Substitueix l'estil" (substituïda pel sistema de themes des de la versió 1.1.0). La resta de la configuració no ha canviat. La transparència de la finestra s'ha migrat a "Theme &amp; Layout". Una còpia de seguretat de la configuració anterior es troba a pluginConfigs/HellionChat.json.pre-v16-backup al costat del HellionChat.json actiu.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Les integracions de plugins permeten que HellionChat funcioni conjuntament amb altres plugins de Dalamud instal·lats. Cada integració detecta automàticament el seu objectiu i es desactiva silenciosament quan el plugin objectiu no és present.</value> <value>Les integracions de plugins permeten que HellionChat funcioni conjuntament amb altres plugins de Dalamud instal·lats. Cada integració detecta automàticament el seu objectiu i es desactiva silenciosament quan el plugin objectiu no és present.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Entrada</value> <value>Entrada</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Aquest missatge conté símbols exclusius del connector que altres jugadors poden veure com a quadres buits. Prem Retorn de nou per enviar-lo igualment.</value> <value>Aquest missatge conté símbols exclusius del connector que altres jugadors poden veure com a quadres buits. Prem Retorn de nou per enviar-lo igualment.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Insereix un símbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Configuració</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Amaga el xat (Retorn per recuperar-lo)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Retorna aquesta pestanya a la finestra principal</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Hi ha una altra operació de base de dades en curs: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>neteja de retenció</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>exportació</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>neteja</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>esborrat de l'historial</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>El filtre de privadesa està desactivat, així que es desa cada canal i res de la base de dades no contradiu la teva configuració. Activa el filtre i tria els canals primer.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>No hi ha cap canal seleccionat, així que una neteja esborraria tot l'historial. Tria els canals que vols conservar, o fes servir el botó d'esborrar si de debò ho vols eliminar tot.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Hi ha {0:N0} missatges desats. Si vols conservar-ne una còpia, exporta'ls abans d'esborrar.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Maj: executa la neteja de retenció ara mateix en comptes d'esperar el pas diari. Esborra els missatges més antics que els límits de dalt.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>No s'ha pogut esborrar l'historial. No s'ha eliminat res, consulta /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetria</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>No es recull cap telemetria. El complement no envia res sobre tu ni sobre el teu ús enlloc.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Traducció automàtica</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Bloqueja</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Vés al missatge més recent</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Insereix la marca del mapa &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Insereix l'objecte enllaçat &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>desactivat</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Comportament</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Dreceres</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notificacions</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Modes de visualització</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historial</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Ajuda d'ordres</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Avís de complement</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Mode de disposició</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacitat</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Comportament de mida</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Obertura automàtica de tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marca</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Enllaços</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integracions</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Crèdits</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Llicència</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Fes clic a un botó i després prem la combinació de tecles. Esc esborra.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Passa a la pestanya següent</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Passa a la pestanya anterior</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Canviar-ho reconstrueix l'atles de fonts, així que el xat queda en blanc un moment.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Rellotge de 24 hores</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Mostra l'historial de sessions anteriors</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Desactivat, el registre comença buit cada vegada que s'inicia el joc i només s'omple amb els missatges rebuts a partir d'aleshores.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Costat de l'ajuda d'ordres</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>A quin costat apareix la llista de suggeriments mentre escrius.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Mode d'obertura automàtica de tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>On s'obre un tell quan arriba.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Canvia a la pestanya a cada tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Altrament la pestanya s'obre en segon pla després de la primera.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Pestanyes superiors</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Ubicació de les pestanyes</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>On es col·loca la llista de pestanyes a la finestra principal.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Mostra la barra de títol</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Barra de títol als pop-outs</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Permet moure</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Permet redimensionar</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Llindar de canvi automàtic de la barra lateral</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Per sota d'aquesta amplada la barra lateral es plega en pestanyes superiors, en píxels.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Posició de la vista prèvia</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Mostra la vista prèvia només en escriure</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Repositori Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest del repositori personalitzat</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Tema actiu: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Bifurca i edita</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Els temes integrats no es poden editar directament. La bifurcació en crea una còpia personalitzada que pots editar i desar.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Edita el tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>S'està editant: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Superfícies</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Vores</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identitat</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Estat</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Desa</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Cancel·la</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Restableix a l'original</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>No es pot restablir mentre s'edita una bifurcació. Desa o cancel·la primer.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Desa o descarta primer els teus canvis</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Personalitzats ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Bifurca el tema actiu</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importa un fitxer de tema…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Ruta al fitxer JSON (o arrossega'l a la carpeta)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exporta el tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Freds</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturals</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Clàssics</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (inclosa)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Tipus de lletra del joc</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Actiu: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Escriu un missatge...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplica</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>vista prèvia</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>manteniment</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} pestanya</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} pestanyes</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} xiuxiueig</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} xiuxiueigs</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} msg</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k msg</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Campió» Vista prèvia</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>obert</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Záchranná síť pro ChatTypes přidané budoucími patchemi FFXIV, které plugin ještě nezná. Výchozí stav je VYPNUTO (minimalizace dat). Zapni, pokud chceš, aby se budoucí kanály také plně logovaly.</value> <value>Záchranná síť pro ChatTypes přidané budoucími patchemi FFXIV, které plugin ještě nezná. Výchozí stav je VYPNUTO (minimalizace dat). Zapni, pokud chceš, aby se budoucí kanály také plně logovaly.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Použít filtr na existující databázi</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Filtr soukromí ovlivňuje jen nové zprávy. Čištění níže ti umožní zpětně odstranit již uložené zprávy, které neodpovídají tvé uložené whitelistě.</value> <value>Filtr soukromí ovlivňuje jen nové zprávy. Čištění níže ti umožní zpětně odstranit již uložené zprávy, které neodpovídají tvé uložené whitelistě.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Čištění používá tvoji ULOŽENOU whitelistu (Plugin.Config), ne neuložené změny výše. Klikni nejdřív na Uložit, chceš-li použít aktuální změny.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Ruční spuštění používá tvoji ULOŽENOU zásadu uchovávání, ne hodnoty posuvníků výše. Klikni nejdřív na Uložit, chceš-li použít aktuální změny.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Náhled je zastaralý: tvoje whitelist se od posledního obnovení změnila. Klikni na Obnovit a přepočítej.</value> <value>Náhled je zastaralý: tvoje whitelist se od posledního obnovení změnila. Klikni na Obnovit a přepočítej.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Použít uchovávání nyní</value> <value>Použít uchovávání nyní</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Spustí čištění uchovávání okamžitě podle ULOŽENÉ zásady. Nejdřív ulož změny.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Čištění uchovávání běží na pozadí…</value> <value>Čištění uchovávání běží na pozadí…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Vzhled</value> <value>Vzhled</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Načíst předchozí relaci při spuštění</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Použít filtry na zprávy z předchozích relací</value> <value>Použít filtry na zprávy z předchozích relací</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Nastavení → Hellion Chat pro pozdější doladění</value> <value>Nastavení → Hellion Chat pro pozdější doladění</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Export (GDPR Art. 15 — Právo na přístup)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exportuj uložené zprávy jako Markdown, JSON nebo CSV. Umožňuje ti splnit žádost o přístup od osoby, jejíž zprávy uchováváš, nebo vzít s sebou vlastní historii.</value> <value>Exportuj uložené zprávy jako Markdown, JSON nebo CSV. Umožňuje ti splnit žádost o přístup od osoby, jejíž zprávy uchováváš, nebo vzít s sebou vlastní historii.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Komunitní překladatelé Chat 2 (upstream)</value> <value>Komunitní překladatelé Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktivní telly</value> <value>Aktivní telly</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Připnuto: přežívá relog.</value> <value>Připnuto: přežívá relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Poznámka: Pokud XIV Messenger nebo podobný plugin potlačuje telly, deaktivuj tam možnost „Suppress DMs", aby Hellion Chat mohl přijímat telly a otevírat auto-záložky.</value> <value>Poznámka: Pokud XIV Messenger nebo podobný plugin potlačuje telly, deaktivuj tam možnost „Suppress DMs", aby Hellion Chat mohl přijímat telly a otevírat auto-záložky.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Historie tellů v auto-záložkách</value> <value>Historie tellů v auto-záložkách</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Funguje pouze tehdy, jsou-li auto-tell záložky zapnuty v záložce Chat.</value> <value>Funguje pouze tehdy, jsou-li auto-tell záložky zapnuty v záložce Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Nastavení bylo přestrukturováno</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 přestrukturoval nastavení do tematických záložek. Tvoje databáze chatu a historie zpráv zůstávají beze změny. Nastavení bylo resetováno na výchozí hodnoty. Chceš-li znovu vybrat profil soukromí, tlačítko Znovu otevřít je v záložce Soukromí. Záloha předchozí konfigurace se nachází v HellionChat.json.pre-v10-backup vedle aktivního konfiguračního souboru.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Obecné</value> <value>Obecné</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>O pluginu</value> <value>O pluginu</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Časová razítka</value> <value>Časová razítka</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Rám okna</value> <value>Rám okna</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Zobrazit tlačítko pro výběr symbolů vedle chatového vstupu</value> <value>Zobrazit tlačítko pro výběr symbolů vedle chatového vstupu</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Přidá malé tlačítko vlevo od indikátoru kanálu, které otevře popup s ikonami FFXIV a kurátovaným seznamem symbolů. Vypni, chceš-li mít čistší vstupní lištu.</value> <value>Přidá malé tlačítko vlevo od indikátoru kanálu, které otevře popup s ikonami FFXIV a kurátovaným seznamem symbolů. Vypni, chceš-li mít čistší vstupní lištu.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Úložiště</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Přehled</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Údržba</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Systém</value> <value>Systém</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Pokud používáš více linkshellů, maintainer doporučuje mít jednu záložku na shell pro přehlednější zobrazení. Zduplikuj záložku a v každé kopii omezte výběr kanálů.</value> <value>Pokud používáš více linkshellů, maintainer doporučuje mít jednu záložku na shell pro přehlednější zobrazení. Zduplikuj záložku a v každé kopii omezte výběr kanálů.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Ikona záložky</value> <value>Ikona záložky</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Přesune okno chatu a všechna aktivní pop-out okna zpět do levého horního rohu primárního monitoru. Užitečné, když se okno po změně rozvržení displeje ocitlo mimo viditelnou oblast (monitor odpojen, změněné rozlišení). Plugin také jednou za relaci provádí automatickou kontrolu hranic. Toto tlačítko je ruční záchranný východ, pokud přesto něco zůstane nedostupné.</value> <value>Přesune okno chatu a všechna aktivní pop-out okna zpět do levého horního rohu primárního monitoru. Užitečné, když se okno po změně rozvržení displeje ocitlo mimo viditelnou oblast (monitor odpojen, změněné rozlišení). Plugin také jednou za relaci provádí automatickou kontrolu hranic. Toto tlačítko je ruční záchranný východ, pokud přesto něco zůstane nedostupné.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Novinka ve v0.6.0: Teď můžeš psát přímo v pop-outech. Zapni hlavní přepínač v nastavení okna.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Rozumím</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Otevřít nastavení okna</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Libovolnou chat záložku můžeš otevřít jako vlastní okno. Klikni na ikonu okna vpravo nahoře nebo pravým tlačítkem klikni na záložku. Novinka ve v0.6.1: vstup v pop-outu je ve výchozím nastavení aktivní (lze vypnout v Nastavení → Okno).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Rozumím</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Otevřít nastavení</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat se nemůže spustit, dokud je načten Chat 2.</value> <value>Hellion Chat se nemůže spustit, dokud je načten Chat 2.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Deaktivuj Chat 2 v /xlplugins a poté znovu aktivuj Hellion Chat.</value> <value>Deaktivuj Chat 2 v /xlplugins a poté znovu aktivuj Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Obecné</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Jazyk, vstup, zvuk a výkon.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Vzhled</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Průhlednost okna, písma, animace</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Vyber theme nebo importuj vlastní</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Okno</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Kdy je okno viditelné a zda se dá přesouvat.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Telly, náhled, chování zpráv a emoty.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Záložky</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Vytvárej a konfiguruj vlastní chat záložky.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Databáze</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Úložiště, migrace, staré čištění</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>O pluginu</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Rozšíření, verze, informace o projektu, překladatelé a changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Ponechat</value> <value>Ponechat</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Soukromí především</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Otevřeno</value> <value>Otevřeno</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Data a soukromí</value> <value>Data a soukromí</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtr soukromí, uchovávání, čištění, export a statistiky databáze.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Pokročilé (Shift+klik pro otevření)</value> <value>Pokročilé (Shift+klik pro otevření)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 přeorganizoval nabídku nastavení a odstranil starou možnost „Override style" (nahrazenou systémem themes od verze 1.1.0). Zbývající nastavení zůstávají nezměněna. Průhlednost okna byla přesunuta do „Theme &amp; Layout". Záloha předchozí konfigurace se nachází v pluginConfigs/HellionChat.json.pre-v16-backup vedle aktivního HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Integrace pluginů umožňují HellionChat spolupracovat s ostatními nainstalovanými Dalamud pluginy. Každá integrace automaticky detekuje svůj cíl a tiše se deaktivuje, když cílový plugin chybí.</value> <value>Integrace pluginů umožňují HellionChat spolupracovat s ostatními nainstalovanými Dalamud pluginy. Každá integrace automaticky detekuje svůj cíl a tiše se deaktivuje, když cílový plugin chybí.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Vstup</value> <value>Vstup</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Tato zpráva obsahuje symboly pouze pro plugin, které ostatní hráči mohou vidět jako prázdné čtverce. Stiskni Enter znovu pro odeslání.</value> <value>Tato zpráva obsahuje symboly pouze pro plugin, které ostatní hráči mohou vidět jako prázdné čtverce. Stiskni Enter znovu pro odeslání.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Vložit symbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Nastavení</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Skrýt chat (Enter jej vrátí)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Vrátit tuto kartu do hlavního okna</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Právě probíhá jiná operace s databází: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>úklid podle doby uchování</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>export</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>úklid</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>mazání historie</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Filtr soukromí je vypnutý, takže se ukládá každý kanál a nic v databázi neodporuje tvému nastavení. Nejdřív filtr zapni a vyber kanály.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Není vybraný žádný kanál, takže úklid by smazal celou historii. Vyber kanály, které chceš zachovat, nebo použij tlačítko pro vymazání, pokud opravdu chceš smazat vše.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Uloženo je {0:N0} zpráv. Pokud si chceš ponechat kopii, před vymazáním je exportuj.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: spustí úklid podle doby uchování hned, místo čekání na denní běh. Smaže zprávy starší než limity výše.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Vymazání historie selhalo. Nic nebylo odstraněno, viz /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetrie</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Nesbírá se žádná telemetrie. Plugin nikam neposílá nic o tobě ani o tvém používání.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Automatický překlad</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Zablokovat</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Přejít na nejnovější zprávu</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Vložit značku mapy &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Vložit odkázaný předmět &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>vypnuto</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Chování</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Klávesové zkratky</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Oznámení</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Režimy zobrazení</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historie</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Nápověda k příkazům</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Upozornění na plugin</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Režim rozvržení</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Neprůhlednost</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Chování při změně velikosti</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Automatické otevření tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Postranní panel</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Značka</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Odkazy</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrace</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Poděkování</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licence</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Klikni na tlačítko a poté stiskni kombinaci kláves. Esc smaže.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Přepnout na další záložku</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Přepnout na předchozí záložku</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Přepnutí přestaví atlas písem, takže chat na okamžik zmizí.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24hodinový formát</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Zobrazit historii z předchozích relací</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Vypnuto znamená, že se protokol při každém spuštění hry začíná prázdný a plní se jen zprávami přijatými od té chvíle.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Strana nápovědy k příkazům</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Na které straně se během psaní zobrazuje seznam nápověd.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Režim automatického otevření tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Kde se tell otevře, když dorazí.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Přepnout na kartu při každém tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Jinak se záložka po první zprávě otevře na pozadí.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Postranní panel</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Horní záložky</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Umístění záložek</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Kde v hlavním okně sedí seznam záložek.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Zobrazit záhlaví</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Záhlaví u pop-out oken</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Povolit přesouvání</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Povolit změnu velikosti</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Práh automatického přepnutí postranního panelu</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Pod touto šířkou se postranní panel složí do horních záložek, v pixelech.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Umístění náhledu</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Zobrazit náhled jen při psaní</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Repozitář Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest vlastního repozitáře</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktivní motiv: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Rozvětvit a upravit</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Vestavěné motivy nelze upravovat přímo. Rozvětvení vytvoří vlastní kopii, kterou můžeš upravit a uložit.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Upravit motiv</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Upravuje se: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Plochy</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Okraje</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identita</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Stav</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Uložit</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Zrušit</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Obnovit podle zdroje</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Při úpravě rozvětvení nelze obnovit. Nejdřív ulož nebo zruš.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Nejdřív ulož nebo zahoď své změny</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Vlastní ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Rozvětvit aktivní motiv</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importovat soubor motivu…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Cesta k souboru JSON (nebo přetáhni do složky)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exportovat motiv</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Chladné</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Přírodní</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klasické</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (v balíčku)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Herní písmo</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Globální: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktivní: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Napiš zprávu...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplikovat</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>náhled</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>údržba</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} karta</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} karty</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} šeptání</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} šeptání</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} zpr.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k zpr.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Šampion» Náhled</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>otevřeno</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Sikkerhedsnet for ChatTypes tilføjet af fremtidige FFXIV-opdateringer, som plugin'et endnu ikke kender. Standard er FRA (dataminimering). Aktivér hvis du også vil have fremtidige kanaler logget fuldt ud.</value> <value>Sikkerhedsnet for ChatTypes tilføjet af fremtidige FFXIV-opdateringer, som plugin'et endnu ikke kender. Standard er FRA (dataminimering). Aktivér hvis du også vil have fremtidige kanaler logget fuldt ud.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Anvend filter på eksisterende database</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Privatlivsfilteret påvirker kun nye beskeder. Oprydningen nedenfor lader dig bagudrettet fjerne allerede gemte beskeder, der ikke matcher din gemte hvidliste.</value> <value>Privatlivsfilteret påvirker kun nye beskeder. Oprydningen nedenfor lader dig bagudrettet fjerne allerede gemte beskeder, der ikke matcher din gemte hvidliste.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Oprydningen bruger din GEMTE hvidliste (Plugin.Config), ikke ugemte ændringer ovenfor. Klik Gem først, hvis du vil anvende dine aktuelle ændringer.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Den manuelle kørsel bruger din GEMTE opbevaringspolitik, ikke skyderknapværdierne ovenfor. Klik Gem først, hvis kørslen skal anvende dine aktuelle ændringer.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Forhåndsvisning er forældet: din hvidliste er ændret siden sidste opdatering. Klik Opdater for at genberegne.</value> <value>Forhåndsvisning er forældet: din hvidliste er ændret siden sidste opdatering. Klik Opdater for at genberegne.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Anvend opbevaring nu</value> <value>Anvend opbevaring nu</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Kører oprydningen med den GEMTE politik med det samme. Gem dine ændringer først.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Opbevaringsoprydning kører i baggrunden…</value> <value>Opbevaringsoprydning kører i baggrunden…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Udseende</value> <value>Udseende</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Indlæs forrige session ved opstart</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Anvend filtre på beskeder fra tidligere sessioner</value> <value>Anvend filtre på beskeder fra tidligere sessioner</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Indstillinger → Hellion Chat for at finjustere senere</value> <value>Indstillinger → Hellion Chat for at finjustere senere</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Eksport (GDPR Art. 15 — Ret til indsigt)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Eksportér gemte beskeder som Markdown, JSON eller CSV. Det lader dig opfylde en indsigtsanmodning fra en person, hvis beskeder du har gemt, eller tage din egen historik med.</value> <value>Eksportér gemte beskeder som Markdown, JSON eller CSV. Det lader dig opfylde en indsigtsanmodning fra en person, hvis beskeder du har gemt, eller tage din egen historik med.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 community-oversættere (upstream)</value> <value>Chat 2 community-oversættere (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktive tells</value> <value>Aktive tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fastgjort: overlever genlog.</value> <value>Fastgjort: overlever genlog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Bemærk: Hvis XIV Messenger eller et lignende plugin undertrykker tells, skal du deaktivere indstillingen "Suppress DMs" der, så Hellion Chat kan modtage tells og åbne auto-tabs.</value> <value>Bemærk: Hvis XIV Messenger eller et lignende plugin undertrykker tells, skal du deaktivere indstillingen "Suppress DMs" der, så Hellion Chat kan modtage tells og åbne auto-tabs.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell-historik i auto-tabs</value> <value>Tell-historik i auto-tabs</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Træder kun i kraft når auto-tell-tabs er aktiveret under fanen Chat.</value> <value>Træder kun i kraft når auto-tell-tabs er aktiveret under fanen Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Indstillinger omstruktureret</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 har omstruktureret indstillingerne i tematiske faner. Din chat-database og beskedhistorik er uændret. Indstillinger er nulstillet til standarder. Hvis du vil vælge din privatlivsprofil på ny, finder du Genåbn-knappen under fanen Privatliv. En sikkerhedskopi af den forrige konfiguration ligger ved siden af den aktive konfigurationsfil som HellionChat.json.pre-v10-backup.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Generelt</value> <value>Generelt</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Om</value> <value>Om</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Tidsstempler</value> <value>Tidsstempler</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Vinduesramme</value> <value>Vinduesramme</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Vis symbolvælger-knap ved siden af chat-input</value> <value>Vis symbolvælger-knap ved siden af chat-input</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Tilføjer en lille knap til venstre for kanalindikatoren, der åbner et popup med FFXIV-ikoner og en udvalgt symbolliste. Deaktivér for en slankere inputlinje.</value> <value>Tilføjer en lille knap til venstre for kanalindikatoren, der åbner et popup med FFXIV-ikoner og en udvalgt symbolliste. Deaktivér for en slankere inputlinje.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Lagring</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Oversigt</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Vedligeholdelse</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Bruger du flere linkshells, anbefaler vedligeholderen én tab pr. shell for et bedre overblik. Dupliker tab'en og begræns kanalvalget i hver kopi.</value> <value>Bruger du flere linkshells, anbefaler vedligeholderen én tab pr. shell for et bedre overblik. Dupliker tab'en og begræns kanalvalget i hver kopi.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Tab-ikon</value> <value>Tab-ikon</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Flytter chat-vinduet og alle aktive pop-outs tilbage til øverste venstre hjørne af den primære skærm. Nyttigt når et vindue er havnet uden for det synlige område efter en skærmlayoutændring (skærm frakoblet, opløsning ændret). Plugin'et udfører også et automatisk bounds-check én gang pr. session. Denne knap er den manuelle nødudgang, hvis noget alligevel ender utilgængeligt.</value> <value>Flytter chat-vinduet og alle aktive pop-outs tilbage til øverste venstre hjørne af den primære skærm. Nyttigt når et vindue er havnet uden for det synlige område efter en skærmlayoutændring (skærm frakoblet, opløsning ændret). Plugin'et udfører også et automatisk bounds-check én gang pr. session. Denne knap er den manuelle nødudgang, hvis noget alligevel ender utilgængeligt.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Nyt i v0.6.0: Du kan nu skrive direkte i pop-outs. Aktivér masterknappen under Vinduesindstillinger.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Forstået</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Åbn vinduesindstillinger</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Du kan åbne enhver chat-tab som sit eget vindue. Klik på vindues-ikonet øverst til højre, eller højreklik på tab'en. Nyt i v0.6.1: pop-out-input er aktiv som standard (kan deaktiveres under Indstillinger → Vindue).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Forstået</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Åbn indstillinger</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat kan ikke starte mens Chat 2 er indlæst.</value> <value>Hellion Chat kan ikke starte mens Chat 2 er indlæst.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Deaktivér Chat 2 i /xlplugins, og aktivér derefter Hellion Chat igen.</value> <value>Deaktivér Chat 2 i /xlplugins, og aktivér derefter Hellion Chat igen.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Generelt</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Sprog, input, lyd og ydeevne.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Udseende</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Vindues-opacitet, skrifttyper, bevægelse</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Vælg et theme eller importér dit eget</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Vindue</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Hvornår vinduet er synligt, og om det kan flyttes.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, forhåndsvisning, beskedadfærd og emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Tabs</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Opret og konfigurér brugerdefinerede chat-tabs.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Database</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Lagring, migration, gammel oprydning</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Om</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Udvidelser, version, projektoplysninger, oversættere og changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Behold</value> <value>Behold</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Privatliv først</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Åben</value> <value>Åben</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Data og privatliv</value> <value>Data og privatliv</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Privatlivsfilter, opbevaring, oprydning, eksport og databasestatistik.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avanceret (Shift+klik for at åbne)</value> <value>Avanceret (Shift+klik for at åbne)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 har omorganiseret indstillingsmenuen og fjernet den gamle "Tilsidesæt stil"-indstilling (erstattet af theme-systemet fra 1.1.0). Dine øvrige indstillinger er uændret. Vinduesgennemsigtighed er migreret til "Theme &amp; Layout". En sikkerhedskopi af den forrige konfiguration ligger ved siden af den aktive HellionChat.json som pluginConfigs/HellionChat.json.pre-v16-backup.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Plugin-integrationer lader HellionChat arbejde sammen med andre installerede Dalamud-plugins. Hver integration registrerer automatisk sit mål og deaktiverer sig stille, når målplugin'et mangler.</value> <value>Plugin-integrationer lader HellionChat arbejde sammen med andre installerede Dalamud-plugins. Hver integration registrerer automatisk sit mål og deaktiverer sig stille, når målplugin'et mangler.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Input</value> <value>Input</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Denne besked indeholder plugin-kun symboler, som andre spillere muligvis ser som tomme bokse. Tryk Enter igen for at sende alligevel.</value> <value>Denne besked indeholder plugin-kun symboler, som andre spillere muligvis ser som tomme bokse. Tryk Enter igen for at sende alligevel.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Indsæt symbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Indstillinger</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Skjul chat (Enter henter den tilbage)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Send denne fane tilbage til hovedvinduet</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>En anden databasehandling kører: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>oprydning efter opbevaringsregler</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>eksport</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>oprydning</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>sletning af historikken</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Privatlivsfilteret er slået fra, så alle kanaler gemmes, og intet i databasen strider mod dine indstillinger. Slå filteret til, og vælg kanaler først.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Ingen kanal er valgt, så en oprydning ville slette hele historikken. Vælg de kanaler, du vil beholde, eller brug sletteknappen, hvis du virkelig vil af med alt.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Der er gemt {0:N0} beskeder. Vil du beholde en kopi, så eksportér dem før du sletter.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: kører oprydningen med det samme i stedet for at vente på det daglige gennemløb. Sletter beskeder ældre end grænserne ovenfor.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Sletning af historikken mislykkedes. Intet blev fjernet, se /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetri</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Der indsamles ingen telemetri. Pluginet sender intet om dig eller din brug nogen steder.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Auto-oversættelse</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Bloker</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Hop til den seneste besked</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Indsæt kortmarkering &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Indsæt linket genstand &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>fra</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Adfærd</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Tastaturgenveje</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notifikationer</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Visningstilstande</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historik</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Kommandohjælp</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Plugin-oplysning</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Layouttilstand</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Uigennemsigtighed</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Størrelsesændring</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Automatisk åbning af tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Sidepanel</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Varemærke</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Links</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrationer</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Krediteringer</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licens</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Klik på en knap, og tryk derefter tastekombinationen. Esc rydder.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Skift til næste tab</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Skift til forrige tab</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Skift genopbygger fontatlasset, så chatten er tom et øjeblik.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-timers ur</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Vis historik fra tidligere sessioner</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Slået fra starter loggen tom, hver gang spillet startes, og fyldes kun med beskeder modtaget derefter.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Side for kommandohjælp</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Hvilken side listen med kommandotips vises på, mens du skriver.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Tilstand for automatisk åbning af tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Hvor en tell åbnes, når den ankommer.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Skift til tab ved hver tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Ellers åbnes tab'en i baggrunden efter den første.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Sidepanel</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Tabs øverst</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Placering af tabs</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Hvor tab-listen sidder i hovedvinduet.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Vis titellinje</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Vis titellinje for pop-out-vinduer</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Tillad flytning</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Tillad størrelsesændring</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Grænse for automatisk skift af sidepanel</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Under denne bredde folder sidepanelet sig til tabs øverst, i pixels.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Placering af forhåndsvisning</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Vis kun forhåndsvisning under skrivning</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea-repositorium</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest for brugerdefineret repo</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktivt tema: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Forgren og rediger</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Indbyggede temaer kan ikke redigeres direkte. Forgrening laver en brugerdefineret kopi, du kan redigere og gemme.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Rediger tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Redigerer: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Flader</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Kanter</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Tekst</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identitet</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Gem</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Annuller</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Nulstil til kilden</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Nulstilling er ikke muligt, mens du redigerer en forgrening. Gem eller annuller først.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Gem eller kassér dine ændringer først</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Brugerdefinerede ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Forgren aktivt tema</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importér temafil…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Sti til JSON-fil (eller træk-og-slip i mappen)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Eksportér tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Kølige</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturlige</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klassiske</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (medfølger)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Spillets skrifttype</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktiv: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Skriv en besked...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplikér</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>forhåndsvisning</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>vedligeholdelse</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} tab</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} tabs</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} tells</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} besk.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k besk.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Champion» Forhåndsvisning</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>åben</value>
</data>
</root>
+362 -150
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Sicherheitsnetz für ChatTypes, die durch zukünftige FFXIV-Patches dazukommen und dem Plugin noch nicht bekannt sind. Standard ist AUS (Datensparsamkeit). Aktivieren, wenn du auch zukünftige Kanäle vollständig mitloggen willst.</value> <value>Sicherheitsnetz für ChatTypes, die durch zukünftige FFXIV-Patches dazukommen und dem Plugin noch nicht bekannt sind. Standard ist AUS (Datensparsamkeit). Aktivieren, wenn du auch zukünftige Kanäle vollständig mitloggen willst.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Filter auf bestehende Datenbank anwenden</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Der Datenschutz-Filter wirkt nur auf neue Nachrichten. Über das Aufräumen unten kannst du bereits gespeicherte Nachrichten nachträglich entfernen, die nicht zu deiner gespeicherten Whitelist passen.</value> <value>Der Datenschutz-Filter wirkt nur auf neue Nachrichten. Über das Aufräumen unten kannst du bereits gespeicherte Nachrichten nachträglich entfernen, die nicht zu deiner gespeicherten Whitelist passen.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Das Aufräumen nutzt deine GESPEICHERTE Whitelist (Plugin.Config), nicht ungespeicherte Änderungen oben. Klicke zuerst Speichern, wenn du deine aktuellen Änderungen anwenden willst.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Der manuelle Lauf nutzt deine GESPEICHERTE Retention-Policy, nicht die Slider-Werte oben. Klicke zuerst Speichern, wenn der Lauf deine aktuellen Änderungen anwenden soll.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Vorschau veraltet, deine Whitelist hat sich seit dem letzten Aktualisieren geändert. Klicke Aktualisieren, um neu zu berechnen.</value> <value>Vorschau veraltet, deine Whitelist hat sich seit dem letzten Aktualisieren geändert. Klicke Aktualisieren, um neu zu berechnen.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Aufbewahrung jetzt anwenden</value> <value>Aufbewahrung jetzt anwenden</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Strg+Umschalt: Führt die Aufbewahrungs-Bereinigung sofort mit der GESPEICHERTEN Vorgabe aus. Speichere deine Änderungen vorher.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Aufbewahrungs-Bereinigung läuft im Hintergrund…</value> <value>Aufbewahrungs-Bereinigung läuft im Hintergrund…</value>
</data> </data>
@@ -187,7 +175,7 @@
<value>Datensparsamkeit (empfohlen)</value> <value>Datensparsamkeit (empfohlen)</value>
</data> </data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve"> <data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Es werden nur deine eigenen Konversationen gespeichert: Tells, Gruppe, FC, Linkshells, Cross-World-Linkshells, Allianz und ExtraChat. Öffentlicher Chat, NPC-Dialoge und System-Spam werden auf der Storage-Ebene verworfen. Aufbewahrung nach Spec-Defaults (Tells 365 Tage, eigene Konversations-Kanäle 90 Tage).</value> <value>Es werden nur deine eigenen Konversationen gespeichert: Flüsternachrichten, Gruppe, FC, Linkshells, Cross-World-Linkshells, Allianz und ExtraChat. Öffentlicher Chat, NPC-Dialoge und System-Spam werden auf der Storage-Ebene verworfen. Aufbewahrung nach Spec-Defaults (Flüsternachrichten 365 Tage, eigene Konversations-Kanäle 90 Tage).</value>
</data> </data>
<data name="Wizard_Profile_PrivacyFirst_Apply" xml:space="preserve"> <data name="Wizard_Profile_PrivacyFirst_Apply" xml:space="preserve">
<value>Datensparsamkeit übernehmen</value> <value>Datensparsamkeit übernehmen</value>
@@ -268,22 +256,19 @@
<value>Verlauf</value> <value>Verlauf</value>
</data> </data>
<data name="Wizard_Step3_Section_TellTabs" xml:space="preserve"> <data name="Wizard_Step3_Section_TellTabs" xml:space="preserve">
<value>Tell-Tabs</value> <value>Flüster-Tabs</value>
</data> </data>
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Optik</value> <value>Optik</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Vorherige Session beim Start laden</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Filter auch auf alte Messages anwenden</value> <value>Filter auch auf alte Messages anwenden</value>
</data> </data>
<data name="Wizard_Step3_AutoTellTabsHistoryPreload_Label" xml:space="preserve"> <data name="Wizard_Step3_AutoTellTabsHistoryPreload_Label" xml:space="preserve">
<value>N Tell-Messages beim Öffnen eines Auto-Tabs vorladen</value> <value>N Flüsternachrichten beim Öffnen eines Auto-Tabs vorladen</value>
</data> </data>
<data name="Wizard_Step3_UseCompactDensity_Label" xml:space="preserve"> <data name="Wizard_Step3_UseCompactDensity_Label" xml:space="preserve">
<value>Kompakter Density-Modus</value> <value>Kompakte Dichte</value>
</data> </data>
<data name="Wizard_Step3_PrettierTimestamps_Label" xml:space="preserve"> <data name="Wizard_Step3_PrettierTimestamps_Label" xml:space="preserve">
<value>Schönere Timestamps (relative Zeit)</value> <value>Schönere Timestamps (relative Zeit)</value>
@@ -304,7 +289,7 @@
<value>Verlauf: {0}</value> <value>Verlauf: {0}</value>
</data> </data>
<data name="Wizard_Step4_Summary_TellTabs" xml:space="preserve"> <data name="Wizard_Step4_Summary_TellTabs" xml:space="preserve">
<value>Tell-Tabs: {0} Messages vorladen</value> <value>Flüster-Tabs: {0} Messages vorladen</value>
</data> </data>
<data name="Wizard_Step4_Summary_Visual" xml:space="preserve"> <data name="Wizard_Step4_Summary_Visual" xml:space="preserve">
<value>Optik: {0}</value> <value>Optik: {0}</value>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Einstellungen → Hellion Chat zum späteren Anpassen</value> <value>Einstellungen → Hellion Chat zum späteren Anpassen</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Export (DSGVO Art. 15 — Auskunftsrecht)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Gespeicherte Nachrichten als Markdown, JSON oder CSV exportieren. Damit kannst du einer Auskunftsanfrage einer Person nachkommen, deren Nachrichten du gespeichert hast, oder deine eigene Historie mitnehmen.</value> <value>Gespeicherte Nachrichten als Markdown, JSON oder CSV exportieren. Damit kannst du einer Auskunftsanfrage einer Person nachkommen, deren Nachrichten du gespeichert hast, oder deine eigene Historie mitnehmen.</value>
</data> </data>
@@ -457,9 +439,9 @@
<value>Chat-2-Community-Übersetzer (Upstream)</value> <value>Chat-2-Community-Übersetzer (Upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime-Strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktive Tells</value> <value>Aktive Flüsternachrichten</value>
</data> </data>
<data name="AutoTellTabs_HistorySeparator" xml:space="preserve"> <data name="AutoTellTabs_HistorySeparator" xml:space="preserve">
<value>— Frühere Unterhaltungen —</value> <value>— Frühere Unterhaltungen —</value>
@@ -483,16 +465,16 @@
<value>In Standard-Tab umwandeln</value> <value>In Standard-Tab umwandeln</value>
</data> </data>
<data name="PinTab_PromoteTooltip" xml:space="preserve"> <data name="PinTab_PromoteTooltip" xml:space="preserve">
<value>Wandelt den TempTell in einen regulären Tab um. Die Tell-Bindung an die Person geht verloren, der Tab fängt dann Nachrichten anhand der Channel-Filter ein. Für „Tab überlebt Relog" stattdessen „Tab anpinnen" wählen.</value> <value>Wandelt den temporären Flüster-Tab in einen regulären Tab um. Die Flüster-Bindung an die Person geht verloren, der Tab fängt dann Nachrichten anhand der Channel-Filter ein. Für „Tab überlebt Relog" stattdessen „Tab anpinnen" wählen.</value>
</data> </data>
<data name="PinTab_LimitReached" xml:space="preserve"> <data name="PinTab_LimitReached" xml:space="preserve">
<value>Maximal {0} angepinnte Tell-Tabs erreicht. Erst einen lösen oder dauerhaft behalten.</value> <value>Maximal {0} angepinnte Flüster-Tabs erreicht. Erst einen lösen oder dauerhaft behalten.</value>
</data> </data>
<data name="PinTab_PinnedTooltip" xml:space="preserve"> <data name="PinTab_PinnedTooltip" xml:space="preserve">
<value>Angepinnt: überlebt Relog.</value> <value>Angepinnt: überlebt Relog.</value>
</data> </data>
<data name="PinTab_PinTooltip" xml:space="preserve"> <data name="PinTab_PinTooltip" xml:space="preserve">
<value>Angepinnte Tabs überleben Relog und behalten die Bindung an die Tell-Person.</value> <value>Angepinnte Tabs überleben Relog und behalten die Bindung an die Flüster-Partner.</value>
</data> </data>
<data name="PinTab_SectionHeader" xml:space="preserve"> <data name="PinTab_SectionHeader" xml:space="preserve">
<value>Angepinnt</value> <value>Angepinnt</value>
@@ -501,12 +483,12 @@
<value>Sidebar-Breite</value> <value>Sidebar-Breite</value>
</data> </data>
<data name="Settings_ThemeAndLayout_SidebarWidth_Description" xml:space="preserve"> <data name="Settings_ThemeAndLayout_SidebarWidth_Description" xml:space="preserve">
<value>Breite der Tab-Sidebar in Pixeln. Default (44 px) ist Icon-only; breiter machen damit Sektion-Header wie „Aktive Tells (3)" nicht abgeschnitten werden.</value> <value>Breite der Tab-Sidebar in Pixeln. Default (44 px) ist Icon-only; breiter machen damit Sektion-Header wie „Aktive Flüsternachrichten (3)" nicht abgeschnitten werden.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat-Einstellungstab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Flüster-Tabs</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve">
<value>Bei jedem /tell automatisch einen Tab pro Gesprächspartner öffnen</value> <value>Bei jedem /tell automatisch einen Tab pro Gesprächspartner öffnen</value>
@@ -515,22 +497,22 @@
<value>Sobald du einen /tell empfängst oder sendest, wird automatisch ein temporärer Tab für diesen Spieler geöffnet. Die Tabs verschwinden beim Logout.</value> <value>Sobald du einen /tell empfängst oder sendest, wird automatisch ein temporärer Tab für diesen Spieler geöffnet. Die Tabs verschwinden beim Logout.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Limit_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Limit_Name" xml:space="preserve">
<value>Maximale Anzahl der Auto-Tell-Tabs</value> <value>Maximale Anzahl der Auto-Flüster-Tabs</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Limit_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Limit_Description" xml:space="preserve">
<value>Beim Erreichen werden begrüßte Tabs mit der ältesten Aktivität zuerst geschlossen. Änderungen greifen beim nächsten /tell. Diese Grenze gilt nur für den automatisch verwalteten Pool. Angepinnte Tell-Tabs (Rechtsklick → Tab anpinnen) leben in einem separaten Pool von bis zu 5 Tabs und überleben Relog.</value> <value>Beim Erreichen werden begrüßte Tabs mit der ältesten Aktivität zuerst geschlossen. Änderungen greifen beim nächsten /tell. Diese Grenze gilt nur für den automatisch verwalteten Pool. Angepinnte Flüster-Tabs (Rechtsklick → Tab anpinnen) leben in einem separaten Pool von bis zu 5 Tabs und überleben Relog.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Compact_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Compact_Name" xml:space="preserve">
<value>Kompakte Anzeige</value> <value>Kompakte Anzeige</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Compact_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Compact_Description" xml:space="preserve">
<value>Zeigt nur einen dünnen Separator zwischen normalen Tabs und Auto-Tell-Tabs, ohne Sektions-Header.</value> <value>Zeigt nur einen dünnen Separator zwischen normalen Tabs und Auto-Flüster-Tabs, ohne Sektions-Header.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_GreetedToggle_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_GreetedToggle_Name" xml:space="preserve">
<value>„Als begrüßt markieren"-Button anzeigen</value> <value>„Als begrüßt markieren"-Button anzeigen</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_GreetedToggle_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_GreetedToggle_Description" xml:space="preserve">
<value>Fügt neben jedem Auto-Tell-Tab einen Klick-Button hinzu, um einen Gesprächspartner als bereits begrüßt zu markieren: der Tab-Name wird dann gedimmt. Nützlich für Club-Greeter, die parallel viele Konversationen führen. Standardmäßig aus.</value> <value>Fügt neben jedem Auto-Flüster-Tab einen Klick-Button hinzu, um einen Gesprächspartner als bereits begrüßt zu markieren: der Tab-Name wird dann gedimmt. Nützlich für Club-Greeter, die parallel viele Konversationen führen. Standardmäßig aus.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_OpenAsPopout_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_OpenAsPopout_Name" xml:space="preserve">
<value>Neue /tell-Tabs direkt als Pop-Out öffnen</value> <value>Neue /tell-Tabs direkt als Pop-Out öffnen</value>
@@ -539,35 +521,29 @@
<value>Wenn aktiv, wird jeder neu angelegte /tell-Tab sofort als eigenes Fenster geöffnet. Beim Schließen des Fensters kehrt der Tab in die Seitenleiste zurück.</value> <value>Wenn aktiv, wird jeder neu angelegte /tell-Tab sofort als eigenes Fenster geöffnet. Beim Schließen des Fensters kehrt der Tab in die Seitenleiste zurück.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_PreloadHint" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_PreloadHint" xml:space="preserve">
<value>Die Anzahl der vorgeladenen Tells lässt sich im Datenschutz-Tab einstellen.</value> <value>Die Anzahl der vorgeladenen Flüsternachrichten lässt sich im Datenschutz-Tab einstellen.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_ConflictHint" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_ConflictHint" xml:space="preserve">
<value>Hinweis: Falls XIV Messanger oder ein ähnliches Plugin Tells unterdrückt, dort die Option „Suppress DMs" deaktivieren, damit Hellion Chat Tells empfangen und die Auto-Tabs öffnen kann.</value> <value>Hinweis: Falls XIV Messanger oder ein ähnliches Plugin Flüsternachrichten unterdrückt, dort die Option „Suppress DMs" deaktivieren, damit Hellion Chat Flüsternachrichten empfangen und die Auto-Tabs öffnen kann.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Datenschutz-Einstellungstab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell-Verlauf in Auto-Tabs</value> <value>Flüster-Verlauf in Auto-Tabs</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Name" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Name" xml:space="preserve">
<value>Anzahl der vorgeladenen Tells</value> <value>Anzahl der vorgeladenen Flüsternachrichten</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Description" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Description" xml:space="preserve">
<value>Wie viele frühere Tell-Nachrichten beim Öffnen eines Auto-Tell-Tabs aus der Datenbank geladen werden. 0 deaktiviert die Vorladung.</value> <value>Wie viele frühere Flüsternachrichten beim Öffnen eines Auto-Flüster-Tabs aus der Datenbank geladen werden. 0 deaktiviert die Vorladung.</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Hint" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Hint" xml:space="preserve">
<value>Greift nur, wenn Auto-Tell-Tabs im Chat-Tab aktiviert sind.</value> <value>Greift nur, wenn Auto-Flüster-Tabs im Chat-Tab aktiviert sind.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe-Migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Settings umstrukturiert</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 hat die Settings in thematische Tabs umstrukturiert. Deine Chat-Datenbank und dein Nachrichtenverlauf bleiben unverändert. Settings wurden auf Defaults zurückgesetzt. Falls du das Privacy-Profil neu wählen willst, findest du den Reopen-Button im Datenschutz-Tab. Ein Backup der vorherigen Config liegt unter HellionChat.json.pre-v10-backup neben der aktiven Config-Datei.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-Tab-Struktur -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Allgemein</value> <value>Allgemein</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Über</value> <value>Über</value>
</data> </data>
<!-- Hellion Chat — Sektions-Überschriften des Allgemein-Tabs -->
<!-- Hellion Chat — Sektions-Überschriften des Aussehen-Tabs -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Zeitstempel</value> <value>Zeitstempel</value>
</data> </data>
<!-- Hellion Chat — Sektions-Überschriften des Fenster-Tabs -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Fenster-Rahmen</value> <value>Fenster-Rahmen</value>
</data> </data>
<!-- Hellion Chat — Sektions-Überschriften des Chat-Tabs -->
<!-- Hellion Chat — Chat-Tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Symbol-Picker-Button neben dem Chat-Eingang anzeigen</value> <value>Symbol-Picker-Button neben dem Chat-Eingang anzeigen</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Fügt einen kleinen Button links neben dem Kanal-Indikator ein. Klick öffnet ein Popup mit FFXIV-Glyphen und einer kuratierten Symbol-Liste. Ausschalten für eine schlankere Eingabezeile.</value> <value>Fügt einen kleinen Button links neben dem Kanal-Indikator ein. Klick öffnet ein Popup mit FFXIV-Glyphen und einer kuratierten Symbol-Liste. Ausschalten für eine schlankere Eingabezeile.</value>
</data> </data>
<!-- Hellion Chat — Sektions-Überschriften des Database-Tabs -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Speicherung</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Übersicht</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Wartung</value>
</data>
<!-- Hellion Chat — Sektions-Überschriften des Information-Tabs -->
<!-- Hellion Chat — Default-Tab-Presets (kanalspezifisch) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Wenn du mehrere Linkshells benutzt, empfiehlt der Maintainer einen Tab pro Shell für eine sauberere Übersicht. Tab duplizieren und je Kopie die Kanalauswahl einschränken.</value> <value>Wenn du mehrere Linkshells benutzt, empfiehlt der Maintainer einen Tab pro Shell für eine sauberere Übersicht. Tab duplizieren und je Kopie die Kanalauswahl einschränken.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Tab-Icon</value> <value>Tab-Icon</value>
</data> </data>
@@ -692,7 +659,7 @@
<value>Eingabe in Pop-Outs aktivieren</value> <value>Eingabe in Pop-Outs aktivieren</value>
</data> </data>
<data name="Settings_Window_PopOutInputEnabled_Description" xml:space="preserve"> <data name="Settings_Window_PopOutInputEnabled_Description" xml:space="preserve">
<value>Master-Switch: erlaubt direktes Tippen und Absenden in jedem Pop-Out-Fenster (inkl. Auto-Tell-Tabs). Channel-Wechsel im Pop-Out wirkt global wie im Hauptfenster; Text-Buffer und History-Cursor sind pro Pop-Out unabhängig.</value> <value>Master-Switch: erlaubt direktes Tippen und Absenden in jedem Pop-Out-Fenster (inkl. Auto-Flüster-Tabs). Channel-Wechsel im Pop-Out wirkt global wie im Hauptfenster; Text-Buffer und History-Cursor sind pro Pop-Out unabhängig.</value>
</data> </data>
<data name="Settings_Window_ResetPosition_Name" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Name" xml:space="preserve">
<value>Fenster-Position zurücksetzen</value> <value>Fenster-Position zurücksetzen</value>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Holt das Chat-Fenster und alle aktiven Pop-Outs zurück in die linke obere Ecke des Hauptmonitors. Hilfreich wenn ein Fenster nach einem Display-Layout-Wechsel außerhalb des sichtbaren Bereichs gelandet ist (Monitor abgezogen, Auflösung geändert). Das Plugin macht außerdem einmal pro Session einen automatischen Bounds-Check, dieser Button ist der manuelle Notausgang falls trotzdem etwas unerreichbar bleibt.</value> <value>Holt das Chat-Fenster und alle aktiven Pop-Outs zurück in die linke obere Ecke des Hauptmonitors. Hilfreich wenn ein Fenster nach einem Display-Layout-Wechsel außerhalb des sichtbaren Bereichs gelandet ist (Monitor abgezogen, Auflösung geändert). Das Plugin macht außerdem einmal pro Session einen automatischen Bounds-Check, dieser Button ist der manuelle Notausgang falls trotzdem etwas unerreichbar bleibt.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Neu in v0.6.0: Du kannst jetzt direkt im Pop-Out tippen. Master-Switch in den Fenster-Settings aktivieren.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Verstanden</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Fenster-Settings öffnen</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Du kannst jeden Chat-Tab als eigenes Fenster öffnen. Klicke auf das Fenster-Symbol oben rechts oder rechtsklicke den Tab. Neu in v0.6.1: die Pop-Out-Eingabe ist standardmäßig aktiv (abschaltbar unter Einstellungen → Fenster).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Verstanden</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Einstellungen öffnen</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat kann nicht starten, solange Chat 2 geladen ist.</value> <value>Hellion Chat kann nicht starten, solange Chat 2 geladen ist.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Chat 2 in /xlplugins deaktivieren, danach Hellion Chat erneut aktivieren.</value> <value>Chat 2 in /xlplugins deaktivieren, danach Hellion Chat erneut aktivieren.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Allgemein</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Sprache, Eingabe, Audio und Performance.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Erscheinungsbild</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Fensterdeckkraft, Schriften, Bewegung</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Theme wählen oder eigenes importieren</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Fenster</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Wann das Fenster sichtbar ist und ob es sich bewegen lässt.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, Vorschau, Nachrichten-Verhalten und Emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Tabs</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Eigene Chat-Tabs anlegen und konfigurieren.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Datenbank</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Speicher, Migration, alte Bereinigung</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Über</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Erweiterungen, Version, Projektinformationen, Übersetzer und Changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Behalten</value> <value>Behalten</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Datenschutz zuerst</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Offen</value> <value>Offen</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Daten &amp; Privatsphäre</value> <value>Daten &amp; Privatsphäre</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Privatsphäre-Filter, Aufbewahrung, Aufräumen, Export und Datenbank-Statistiken.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Erweitert (Shift+Klick zum Öffnen)</value> <value>Erweitert (Shift+Klick zum Öffnen)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 hat das Settings-Menü neu sortiert und die alte „Stilüberschreiben"-Option entfernt (überholt durch das Theme-System aus 1.1.0). Deine restlichen Einstellungen bleiben unverändert. Die Fenster-Transparenz ist nach „Theme &amp; Layout" migriert. Ein Backup der vorherigen Config liegt unter pluginConfigs/HellionChat.json.pre-v16-backup neben der aktiven HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Plugin-Integrationen lassen HellionChat mit anderen installierten Dalamud-Plugins zusammenarbeiten. Jede Integration erkennt ihr Ziel automatisch und deaktiviert sich still, wenn das Ziel-Plugin fehlt.</value> <value>Plugin-Integrationen lassen HellionChat mit anderen installierten Dalamud-Plugins zusammenarbeiten. Jede Integration erkennt ihr Ziel automatisch und deaktiviert sich still, wenn das Ziel-Plugin fehlt.</value>
</data> </data>
@@ -950,7 +845,7 @@
<value>Deaktiviert die Theme-Überblendung, die Hover-Animationen von Seitenleiste und Karten sowie das Pulsieren ungelesener Tabs. Theme-Wechsel und Hover-Zustände greifen dann sofort.</value> <value>Deaktiviert die Theme-Überblendung, die Hover-Animationen von Seitenleiste und Karten sowie das Pulsieren ungelesener Tabs. Theme-Wechsel und Hover-Zustände greifen dann sofort.</value>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Eingabe</value> <value>Eingabe</value>
</data> </data>
@@ -991,7 +886,7 @@
<value>Eingabe &amp; Vorschau</value> <value>Eingabe &amp; Vorschau</value>
</data> </data>
<data name="Settings_Section_AutoTellTabs" xml:space="preserve"> <data name="Settings_Section_AutoTellTabs" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Flüster-Tabs</value>
</data> </data>
<data name="Settings_Section_Emotes" xml:space="preserve"> <data name="Settings_Section_Emotes" xml:space="preserve">
<value>Emotes</value> <value>Emotes</value>
@@ -1063,10 +958,10 @@
<value>Änderungsprotokoll</value> <value>Änderungsprotokoll</value>
</data> </data>
<data name="Settings_Chat_NotifyFailedTell_Name" xml:space="preserve"> <data name="Settings_Chat_NotifyFailedTell_Name" xml:space="preserve">
<value>Benachrichtigung bei fehlgeschlagenem Tell</value> <value>Benachrichtigung bei fehlgeschlagenem Flüstern</value>
</data> </data>
<data name="Settings_Chat_NotifyFailedTell_Description" xml:space="preserve"> <data name="Settings_Chat_NotifyFailedTell_Description" xml:space="preserve">
<value>Zeigt eine Toast-Meldung an, wenn ein von dir gesendeter Tell nicht zugestellt werden konnte (Empfänger offline, in einer Instanz oder hat dich blockiert).</value> <value>Zeigt eine Toast-Meldung an, wenn ein von dir gesendeter Flüstern nicht zugestellt werden konnte (Empfänger offline, in einer Instanz oder hat dich blockiert).</value>
</data> </data>
<data name="Settings_Chat_NotifyPluginDisclosure_Name" xml:space="preserve"> <data name="Settings_Chat_NotifyPluginDisclosure_Name" xml:space="preserve">
<value>Warnung vor dem Senden plugin-exklusiver Symbole</value> <value>Warnung vor dem Senden plugin-exklusiver Symbole</value>
@@ -1132,12 +1027,329 @@
<value>Hellion-Sound</value> <value>Hellion-Sound</value>
</data> </data>
<data name="FailedTell_Notification_Generic" xml:space="preserve"> <data name="FailedTell_Notification_Generic" xml:space="preserve">
<value>Ein Tell konnte nicht zugestellt werden.</value> <value>Ein Flüstern konnte nicht zugestellt werden.</value>
</data> </data>
<data name="FailedTell_Notification_Named" xml:space="preserve"> <data name="FailedTell_Notification_Named" xml:space="preserve">
<value>Tell an {0} konnte nicht zugestellt werden.</value> <value>Flüstern an {0} konnte nicht zugestellt werden.</value>
</data> </data>
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Diese Nachricht enthält plugin-exklusive Symbole, die andere Spieler als leere Kästchen sehen könnten. Drücke Enter erneut, um sie trotzdem zu senden.</value> <value>Diese Nachricht enthält plugin-exklusive Symbole, die andere Spieler als leere Kästchen sehen könnten. Drücke Enter erneut, um sie trotzdem zu senden.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Symbol einfügen</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Einstellungen</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Chat ausblenden (Enter holt ihn zurück)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Diesen Tab ins Hauptfenster zurückholen</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Es läuft gerade eine andere Datenbankoperation: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>Aufbewahrungslauf</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>Export</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>Bereinigung</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>Löschen des Verlaufs</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Der Datenschutzfilter ist aus, deshalb wird jeder Kanal gespeichert und nichts in der Datenbank widerspricht deinen Einstellungen. Schalte den Filter zuerst ein und wähle Kanäle aus.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Es ist kein Kanal ausgewählt, eine Bereinigung würde also den gesamten Verlauf löschen. Wähle die Kanäle aus, die du behalten willst, oder nimm den Löschen-Knopf, wenn wirklich alles weg soll.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Es sind {0:N0} Nachrichten gespeichert. Wenn du eine Kopie behalten willst, exportiere sie vor dem Löschen.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Strg+Umschalt: führt den Aufbewahrungslauf sofort aus, statt auf den täglichen Durchlauf zu warten. Löscht Nachrichten, die älter sind als die Grenzen oben.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Das Löschen des Verlaufs ist fehlgeschlagen. Es wurde nichts entfernt, siehe /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetrie</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Es wird keine Telemetrie erhoben. Das Plugin sendet nichts über dich oder deine Nutzung irgendwohin.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Auto-Übersetzung</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Sperren</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Zur neuesten Nachricht springen</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Kartenmarkierung einfügen &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Verlinkten Gegenstand einfügen &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>aus</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Verhalten</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Tastenkürzel</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Benachrichtigungen</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Anzeigemodi</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Verlauf</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Befehlshilfe</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Plugin-Hinweis</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Anordnung</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Deckkraft</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Größenverhalten</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Flüstern automatisch öffnen</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Seitenleiste</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marke</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Links</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrationen</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Mitwirkende</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Lizenz</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Auf einen Knopf klicken, dann die Tastenkombination drücken. Esc löscht.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Zum nächsten Chat-Tab wechseln</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Zum vorherigen Chat-Tab wechseln</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Ein Wechsel baut den Schrift-Atlas neu auf, deshalb ist der Chat kurz leer.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-Stunden-Uhr</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Verlauf früherer Sitzungen anzeigen</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Aus bedeutet, der Verlauf startet bei jedem Spielstart leer und füllt sich nur mit Nachrichten, die danach eintreffen.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Seite der Befehlshilfe</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Auf welcher Seite die Befehlsliste beim Tippen erscheint.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Modus für automatisches Flüster-Öffnen</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Wo ein Flüstern geöffnet wird, wenn er ankommt.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Bei jedem Flüstern zum Tab wechseln</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Sonst öffnet der Tab nach dem ersten im Hintergrund.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Seitenleiste</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Tabs oben</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Tab-Anordnung</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Wo die Tab-Liste im Hauptfenster sitzt.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Titelleiste anzeigen</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Titelleiste für Pop-Outs</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Verschieben erlauben</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Größe ändern erlauben</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Umschaltschwelle der Seitenleiste</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Unterhalb dieser Breite klappt die Seitenleiste zu Tabs oben, in Pixeln.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Position der Vorschau</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Vorschau nur beim Tippen zeigen</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea-Repository</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest des eigenen Repos</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktives Theme: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Abzweigen und bearbeiten</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Eingebaute Themes lassen sich nicht direkt bearbeiten. Abzweigen legt eine eigene Kopie an, die du ändern und speichern kannst.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Theme bearbeiten</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>In Bearbeitung: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Flächen</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Rahmen</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identität</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Speichern</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Abbrechen</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Auf das Original zurücksetzen</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Zurücksetzen geht nicht, solange eine Abzweigung bearbeitet wird. Erst speichern oder abbrechen.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Erst deine Änderungen speichern oder verwerfen</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Eigene ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Aktives Theme abzweigen</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Theme-Datei importieren…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Pfad zur JSON-Datei (oder in den Ordner ziehen)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Theme exportieren</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Kühl</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Natürlich</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klassisch</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (mitgeliefert)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Spielschrift</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktiv: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Nachricht schreiben...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplizieren</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>Vorschau</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>Wartung</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} Tab</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} Tabs</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} Flüstern</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} Flüstern</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} Nachr.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k Nachr.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Champion» Vorschau</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>offen</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Δίχτυ ασφαλείας για ChatTypes που θα προστεθούν από μελλοντικά patches του FFXIV και δεν τα γνωρίζει ακόμη το plugin. Η προεπιλογή είναι ΑΠΕΝΕΡΓΟ (ελαχιστοποίηση δεδομένων). Ενεργοποίησε αν θέλεις να καταγράφονται πλήρως και τα μελλοντικά κανάλια.</value> <value>Δίχτυ ασφαλείας για ChatTypes που θα προστεθούν από μελλοντικά patches του FFXIV και δεν τα γνωρίζει ακόμη το plugin. Η προεπιλογή είναι ΑΠΕΝΕΡΓΟ (ελαχιστοποίηση δεδομένων). Ενεργοποίησε αν θέλεις να καταγράφονται πλήρως και τα μελλοντικά κανάλια.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Εφαρμογή φίλτρου στην υπάρχουσα βάση δεδομένων</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Το φίλτρο απορρήτου επηρεάζει μόνο νέα μηνύματα. Το παρακάτω cleanup σου επιτρέπει να αφαιρέσεις αναδρομικά ήδη αποθηκευμένα μηνύματα που δεν ταιριάζουν με την αποθηκευμένη whitelist σου.</value> <value>Το φίλτρο απορρήτου επηρεάζει μόνο νέα μηνύματα. Το παρακάτω cleanup σου επιτρέπει να αφαιρέσεις αναδρομικά ήδη αποθηκευμένα μηνύματα που δεν ταιριάζουν με την αποθηκευμένη whitelist σου.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Το cleanup χρησιμοποιεί την ΑΠΟΘΗΚΕΥΜΕΝΗ whitelist σου (Plugin.Config), όχι ανεπίσημες αλλαγές παραπάνω. Κάνε πρώτα Αποθήκευση αν θέλεις να εφαρμοστούν οι τρέχουσες αλλαγές σου.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Η χειροκίνητη εκτέλεση χρησιμοποιεί την ΑΠΟΘΗΚΕΥΜΕΝΗ πολιτική διατήρησης, όχι τις τιμές του slider παραπάνω. Κάνε πρώτα Αποθήκευση αν θέλεις να εφαρμοστούν οι τρέχουσες αλλαγές σου.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Η προεπισκόπηση είναι παλιά: η whitelist σου άλλαξε από την τελευταία ανανέωση. Κάνε κλικ στο Ανανέωση για επανυπολογισμό.</value> <value>Η προεπισκόπηση είναι παλιά: η whitelist σου άλλαξε από την τελευταία ανανέωση. Κάνε κλικ στο Ανανέωση για επανυπολογισμό.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Εφαρμογή διατήρησης τώρα</value> <value>Εφαρμογή διατήρησης τώρα</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Εκτελεί αμέσως το cleanup διατήρησης με την ΑΠΟΘΗΚΕΥΜΕΝΗ πολιτική. Αποθήκευσε πρώτα τις αλλαγές σου.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Το cleanup διατήρησης εκτελείται στο παρασκήνιο…</value> <value>Το cleanup διατήρησης εκτελείται στο παρασκήνιο…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Εμφάνιση</value> <value>Εμφάνιση</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Φόρτωση προηγούμενης συνεδρίας κατά την εκκίνηση</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Εφαρμογή φίλτρων σε μηνύματα από προηγούμενες συνεδρίες</value> <value>Εφαρμογή φίλτρων σε μηνύματα από προηγούμενες συνεδρίες</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Ρυθμίσεις → Hellion Chat για προσαρμογή αργότερα</value> <value>Ρυθμίσεις → Hellion Chat για προσαρμογή αργότερα</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Εξαγωγή (GDPR Art. 15 — Δικαίωμα πρόσβασης)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Εξαγωγή αποθηκευμένων μηνυμάτων ως Markdown, JSON ή CSV. Έτσι μπορείς να ανταποκριθείς σε αίτημα πρόσβασης από κάποιον του οποίου τα μηνύματα έχεις αποθηκεύσει, ή να πάρεις μαζί σου το δικό σου ιστορικό.</value> <value>Εξαγωγή αποθηκευμένων μηνυμάτων ως Markdown, JSON ή CSV. Έτσι μπορείς να ανταποκριθείς σε αίτημα πρόσβασης από κάποιον του οποίου τα μηνύματα έχεις αποθηκεύσει, ή να πάρεις μαζί σου το δικό σου ιστορικό.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Μεταφραστές κοινότητας Chat 2 (upstream)</value> <value>Μεταφραστές κοινότητας Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Ενεργά tells</value> <value>Ενεργά tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Καρφιτσωμένο: επιβιώνει relog.</value> <value>Καρφιτσωμένο: επιβιώνει relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Σημείωση: Αν το XIV Messenger ή παρόμοιο plugin αποκλείει tells, απενεργοποίησε την επιλογή "Suppress DMs" εκεί ώστε το Hellion Chat να μπορεί να λαμβάνει tells και να ανοίγει τις αυτόματες καρτέλες.</value> <value>Σημείωση: Αν το XIV Messenger ή παρόμοιο plugin αποκλείει tells, απενεργοποίησε την επιλογή "Suppress DMs" εκεί ώστε το Hellion Chat να μπορεί να λαμβάνει tells και να ανοίγει τις αυτόματες καρτέλες.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Ιστορικό tell σε αυτόματες καρτέλες</value> <value>Ιστορικό tell σε αυτόματες καρτέλες</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Ισχύει μόνο όταν οι auto-tell tabs είναι ενεργοποιημένες στην καρτέλα Chat.</value> <value>Ισχύει μόνο όταν οι auto-tell tabs είναι ενεργοποιημένες στην καρτέλα Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Οι ρυθμίσεις αναδιαρθρώθηκαν</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Το Hellion Chat 0.5.0 αναδιάρθρωσε τις ρυθμίσεις σε θεματικές καρτέλες. Η βάση δεδομένων chat και το ιστορικό μηνυμάτων σου παραμένουν αναλλοίωτα. Οι ρυθμίσεις έχουν επαναφερθεί στις προεπιλογές. Αν θέλεις να επιλέξεις ξανά το προφίλ απορρήτου σου, το κουμπί Επαναφορά βρίσκεται στην καρτέλα Απόρρητο. Αντίγραφο ασφαλείας της προηγούμενης διαμόρφωσης βρίσκεται στο HellionChat.json.pre-v10-backup δίπλα στο ενεργό αρχείο διαμόρφωσης.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Γενικά</value> <value>Γενικά</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Πληροφορίες</value> <value>Πληροφορίες</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Χρονοσφραγίδες</value> <value>Χρονοσφραγίδες</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Πλαίσιο παραθύρου</value> <value>Πλαίσιο παραθύρου</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Εμφάνιση κουμπιού symbol-picker δίπλα στο πεδίο chat</value> <value>Εμφάνιση κουμπιού symbol-picker δίπλα στο πεδίο chat</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Προσθέτει ένα μικρό κουμπί αριστερά της ένδειξης καναλιού που ανοίγει ένα popup με εικονίδια FFXIV και μια επιμελημένη λίστα συμβόλων. Απενεργοποίησε αν προτιμάς πιο λιτή γραμμή εισαγωγής.</value> <value>Προσθέτει ένα μικρό κουμπί αριστερά της ένδειξης καναλιού που ανοίγει ένα popup με εικονίδια FFXIV και μια επιμελημένη λίστα συμβόλων. Απενεργοποίησε αν προτιμάς πιο λιτή γραμμή εισαγωγής.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Αποθήκευση</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Επισκόπηση</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Συντήρηση</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Αν χρησιμοποιείς πολλαπλά linkshells, ο maintainer συνιστά μία καρτέλα ανά shell για πιο καθαρή επισκόπηση. Αντίγραψε την καρτέλα και περιόρισε την επιλογή καναλιών σε κάθε αντίγραφο.</value> <value>Αν χρησιμοποιείς πολλαπλά linkshells, ο maintainer συνιστά μία καρτέλα ανά shell για πιο καθαρή επισκόπηση. Αντίγραψε την καρτέλα και περιόρισε την επιλογή καναλιών σε κάθε αντίγραφο.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Εικονίδιο καρτέλας</value> <value>Εικονίδιο καρτέλας</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Μετακινεί το παράθυρο chat και όλα τα ενεργά pop-outs πίσω στην επάνω αριστερή γωνία της κύριας οθόνης. Χρήσιμο όταν ένα παράθυρο βρεθεί εκτός ορατής περιοχής μετά από αλλαγή διάταξης οθόνης (αποσύνδεση οθόνης, αλλαγή ανάλυσης). Το plugin εκτελεί επίσης αυτόματο έλεγχο ορίων μία φορά ανά συνεδρία. Αυτό το κουμπί είναι η χειροκίνητη διαφυγή αν κάτι παραμένει απρόσιτο.</value> <value>Μετακινεί το παράθυρο chat και όλα τα ενεργά pop-outs πίσω στην επάνω αριστερή γωνία της κύριας οθόνης. Χρήσιμο όταν ένα παράθυρο βρεθεί εκτός ορατής περιοχής μετά από αλλαγή διάταξης οθόνης (αποσύνδεση οθόνης, αλλαγή ανάλυσης). Το plugin εκτελεί επίσης αυτόματο έλεγχο ορίων μία φορά ανά συνεδρία. Αυτό το κουμπί είναι η χειροκίνητη διαφυγή αν κάτι παραμένει απρόσιτο.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Νέο στην v0.6.0: Μπορείς πλέον να πληκτρολογείς απευθείας σε pop-outs. Ενεργοποίησε τον κεντρικό διακόπτη στις ρυθμίσεις Παραθύρου.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Κατάλαβα</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Άνοιγμα ρυθμίσεων παραθύρου</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Μπορείς να ανοίξεις οποιαδήποτε καρτέλα chat ως δικό της παράθυρο. Κάνε κλικ στο εικονίδιο παραθύρου επάνω δεξιά ή δεξί κλικ στην καρτέλα. Νέο στην v0.6.1: η εισαγωγή σε pop-out είναι ενεργή εξ ορισμού (μπορεί να απενεργοποιηθεί στις Ρυθμίσεις → Παράθυρο).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Κατάλαβα</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Άνοιγμα ρυθμίσεων</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Το Hellion Chat δεν μπορεί να ξεκινήσει ενώ το Chat 2 είναι φορτωμένο.</value> <value>Το Hellion Chat δεν μπορεί να ξεκινήσει ενώ το Chat 2 είναι φορτωμένο.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Απενεργοποίησε το Chat 2 στο /xlplugins και μετά ενεργοποίησε ξανά το Hellion Chat.</value> <value>Απενεργοποίησε το Chat 2 στο /xlplugins και μετά ενεργοποίησε ξανά το Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Γενικά</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Γλώσσα, εισαγωγή, ήχος και απόδοση.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Εμφάνιση</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Αδιαφάνεια παραθύρου, γραμματοσειρές, κίνηση</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Επίλεξε theme ή εισαγωγή δικού σου</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Παράθυρο</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Πότε το παράθυρο είναι ορατό και αν μπορεί να μετακινηθεί.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, προεπισκόπηση, συμπεριφορά μηνυμάτων και emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Καρτέλες</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Δημιουργία και ρύθμιση προσαρμοσμένων καρτελών chat.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Βάση δεδομένων</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Αποθήκευση, μετεγκατάσταση, παλαιό cleanup</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Σχετικά</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Επεκτάσεις, έκδοση, πληροφορίες έργου, μεταφραστές και changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Διατήρηση</value> <value>Διατήρηση</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Απόρρητο πρώτα</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Ανοιχτό</value> <value>Ανοιχτό</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Δεδομένα και απόρρητο</value> <value>Δεδομένα και απόρρητο</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Φίλτρο απορρήτου, διατήρηση, cleanup, εξαγωγή και στατιστικά βάσης δεδομένων.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Για προχωρημένους (Shift+κλικ για άνοιγμα)</value> <value>Για προχωρημένους (Shift+κλικ για άνοιγμα)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Το Hellion Chat 1.2.1 αναδιοργάνωσε το μενού ρυθμίσεων και αφαίρεσε την παλιά επιλογή "Override style" (αντικαταστάθηκε από το σύστημα themes από την έκδοση 1.1.0). Οι υπόλοιπες ρυθμίσεις σου παραμένουν αναλλοίωτες. Η διαφάνεια παραθύρου έχει μεταφερθεί στο "Theme &amp; Layout". Αντίγραφο ασφαλείας της προηγούμενης διαμόρφωσης βρίσκεται στο pluginConfigs/HellionChat.json.pre-v16-backup δίπλα στο ενεργό HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Οι ενσωματώσεις plugin επιτρέπουν στο HellionChat να συνεργάζεται με άλλα εγκατεστημένα Dalamud plugins. Κάθε ενσωμάτωση ανιχνεύει αυτόματα τον στόχο της και απενεργοποιείται σιωπηλά όταν το plugin-στόχος λείπει.</value> <value>Οι ενσωματώσεις plugin επιτρέπουν στο HellionChat να συνεργάζεται με άλλα εγκατεστημένα Dalamud plugins. Κάθε ενσωμάτωση ανιχνεύει αυτόματα τον στόχο της και απενεργοποιείται σιωπηλά όταν το plugin-στόχος λείπει.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Εισαγωγή</value> <value>Εισαγωγή</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Αυτό το μήνυμα περιέχει σύμβολα plugin που άλλοι παίκτες μπορεί να βλέπουν ως κενά κουτιά. Πιέστε Enter ξανά για αποστολή.</value> <value>Αυτό το μήνυμα περιέχει σύμβολα plugin που άλλοι παίκτες μπορεί να βλέπουν ως κενά κουτιά. Πιέστε Enter ξανά για αποστολή.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Εισαγωγή συμβόλου</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Ρυθμίσεις</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Απόκρυψη συνομιλίας (Enter για επαναφορά)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Επιστροφή αυτής της καρτέλας στο κύριο παράθυρο</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Εκτελείται ήδη μια άλλη λειτουργία βάσης δεδομένων: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>εκκαθάριση διατήρησης</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>εξαγωγή</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>εκκαθάριση</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>διαγραφή ιστορικού</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Το φίλτρο απορρήτου είναι απενεργοποιημένο, άρα αποθηκεύεται κάθε κανάλι και τίποτα στη βάση δεδομένων δεν έρχεται σε αντίθεση με τις ρυθμίσεις σας. Ενεργοποιήστε πρώτα το φίλτρο και επιλέξτε κανάλια.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Δεν έχει επιλεγεί κανένα κανάλι, οπότε μια εκκαθάριση θα διέγραφε όλο το ιστορικό. Επιλέξτε τα κανάλια που θέλετε να κρατήσετε ή χρησιμοποιήστε το κουμπί διαγραφής αν θέλετε πραγματικά να φύγουν όλα.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Έχουν αποθηκευτεί {0:N0} μηνύματα. Αν θέλετε να κρατήσετε αντίγραφο, εξαγάγετέ τα πριν τη διαγραφή.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: εκτελεί την εκκαθάριση διατήρησης αμέσως, χωρίς να περιμένει το ημερήσιο πέρασμα. Διαγράφει μηνύματα παλαιότερα από τα παραπάνω όρια.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Η διαγραφή του ιστορικού απέτυχε. Δεν αφαιρέθηκε τίποτα, δείτε /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Τηλεμετρία</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Δεν συλλέγεται τηλεμετρία. Το πρόσθετο δεν στέλνει πουθενά τίποτα για εσάς ή τη χρήση σας.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Αυτόματη μετάφραση</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Αποκλεισμός</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Μετάβαση στο πιο πρόσφατο μήνυμα</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Εισαγωγή σημαίας χάρτη &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Εισαγωγή συνδεδεμένου αντικειμένου &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>ανενεργό</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Συμπεριφορά</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Συντομεύσεις</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Ειδοποιήσεις</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Λειτουργίες εμφάνισης</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Ιστορικό</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Βοήθεια εντολών</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Γνωστοποίηση πρόσθετου</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Λειτουργία διάταξης</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Αδιαφάνεια</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Συμπεριφορά μεγέθους</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Αυτόματο άνοιγμα tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Πλαϊνή μπάρα</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Επωνυμία</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Σύνδεσμοι</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Ενσωματώσεις</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Συντελεστές</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Άδεια</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Κάντε κλικ σε ένα κουμπί και μετά πατήστε τον συνδυασμό πλήκτρων. Το Esc καθαρίζει.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Μετάβαση στην επόμενη καρτέλα</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Μετάβαση στην προηγούμενη καρτέλα</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Η αλλαγή ξαναχτίζει τον άτλαντα γραμματοσειρών, οπότε η συνομιλία αδειάζει στιγμιαία.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24ωρο ρολόι</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Εμφάνιση ιστορικού από προηγούμενες συνεδρίες</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Απενεργοποιημένο σημαίνει ότι το αρχείο ξεκινά άδειο σε κάθε εκκίνηση και γεμίζει μόνο με μηνύματα που έρχονται μετά.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Πλευρά βοήθειας εντολών</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Σε ποια πλευρά εμφανίζεται η λίστα υποδείξεων ενώ πληκτρολογείτε.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Λειτουργία αυτόματου ανοίγματος tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Πού ανοίγει ένα tell όταν φτάνει.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Μετάβαση στην καρτέλα σε κάθε tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Διαφορετικά η καρτέλα ανοίγει στο παρασκήνιο μετά το πρώτο.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Πλαϊνή μπάρα</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Καρτέλες πάνω</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Τοποθέτηση καρτελών</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Πού βρίσκεται η λίστα καρτελών στο κύριο παράθυρο.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Εμφάνιση γραμμής τίτλου</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Εμφάνιση γραμμής τίτλου στα αποσπώμενα παράθυρα</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Να επιτρέπεται η μετακίνηση</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Αλλαγή μεγέθους επιτρέπεται</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Όριο αυτόματης εναλλαγής πλαϊνής μπάρας</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Κάτω από αυτό το πλάτος η πλαϊνή μπάρα διπλώνει σε καρτέλες πάνω, σε pixel.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Θέση προεπισκόπησης</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Προεπισκόπηση μόνο κατά την πληκτρολόγηση</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Αποθετήριο Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Δήλωση προσαρμοσμένου αποθετηρίου</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Ενεργό θέμα: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Διακλάδωση και επεξεργασία</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Τα ενσωματωμένα θέματα δεν επεξεργάζονται απευθείας. Η διακλάδωση δημιουργεί ένα προσαρμοσμένο αντίγραφο που μπορείτε να επεξεργαστείτε και να αποθηκεύσετε.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Επεξεργασία θέματος</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Επεξεργασία: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Επιφάνειες</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Περιγράμματα</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Κείμενο</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Ταυτότητα</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Κατάσταση</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Αποθήκευση</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Άκυρο</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Επαναφορά στην πηγή</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Η επαναφορά δεν είναι διαθέσιμη κατά την επεξεργασία διακλάδωσης. Αποθηκεύστε ή ακυρώστε πρώτα.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Αποθηκεύστε ή απορρίψτε πρώτα τις αλλαγές σας</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Προσαρμοσμένα ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Διακλάδωση ενεργού θέματος</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Εισαγωγή αρχείου θέματος…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Διαδρομή προς αρχείο JSON (ή σύρετε στον φάκελο)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Εξαγωγή θέματος</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Ψυχρά</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Φυσικά</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Κλασικά</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Ρετρό</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (ενσωματωμένη)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Γραμματοσειρά παιχνιδιού</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Καθολική: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Ενεργή: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Γράψτε ένα μήνυμα...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Αντιγραφή</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>προεπισκόπηση</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>συντήρηση</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} καρτέλα</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} καρτέλες</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} tells</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} μην.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k μην.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Πρωταθλητής» Προεπισκόπηση</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>ανοιχτό</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Red de seguridad para ChatTypes añadidos por futuros parches de FFXIV que el plugin aún no conoce. El valor predeterminado es DESACTIVADO (minimización de datos). Actívalo si quieres que los canales futuros también se registren completamente.</value> <value>Red de seguridad para ChatTypes añadidos por futuros parches de FFXIV que el plugin aún no conoce. El valor predeterminado es DESACTIVADO (minimización de datos). Actívalo si quieres que los canales futuros también se registren completamente.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Aplicar filtro a la base de datos existente</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>El filtro de privacidad solo afecta a los mensajes nuevos. La limpieza de abajo te permite eliminar retroactivamente los mensajes ya almacenados que no coincidan con tu lista blanca guardada.</value> <value>El filtro de privacidad solo afecta a los mensajes nuevos. La limpieza de abajo te permite eliminar retroactivamente los mensajes ya almacenados que no coincidan con tu lista blanca guardada.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>La limpieza usa tu lista blanca GUARDADA (Plugin.Config), no los cambios sin guardar de arriba. Haz clic en Guardar primero si quieres que se apliquen tus cambios actuales.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>La ejecución manual usa tu política de retención GUARDADA, no los valores del deslizador de arriba. Haz clic en Guardar primero si quieres que la ejecución aplique tus cambios actuales.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>La vista previa está desactualizada: tu lista blanca ha cambiado desde la última actualización. Haz clic en Actualizar para recalcular.</value> <value>La vista previa está desactualizada: tu lista blanca ha cambiado desde la última actualización. Haz clic en Actualizar para recalcular.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Aplicar retención ahora</value> <value>Aplicar retención ahora</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Mayús: Ejecuta la limpieza de retención de inmediato usando la política GUARDADA. Guarda los cambios primero.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Limpieza de retención ejecutándose en segundo plano…</value> <value>Limpieza de retención ejecutándose en segundo plano…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Visual</value> <value>Visual</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Cargar sesión anterior al iniciar</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Aplicar filtros a mensajes de sesiones anteriores</value> <value>Aplicar filtros a mensajes de sesiones anteriores</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Ajustes → Hellion Chat para personalizar más tarde</value> <value>Ajustes → Hellion Chat para personalizar más tarde</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Exportar (GDPR Art. 15 — Derecho de acceso)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exporta los mensajes almacenados como Markdown, JSON o CSV. Esto te permite atender una solicitud de acceso de una persona cuyos mensajes hayas almacenado, o llevarte tu propio historial.</value> <value>Exporta los mensajes almacenados como Markdown, JSON o CSV. Esto te permite atender una solicitud de acceso de una persona cuyos mensajes hayas almacenado, o llevarte tu propio historial.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Traductores de la comunidad de Chat 2 (upstream)</value> <value>Traductores de la comunidad de Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Tells activos</value> <value>Tells activos</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fijada: sobrevive al relog.</value> <value>Fijada: sobrevive al relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Nota: Si XIV Messenger u otro plugin similar suprime los tells, desactiva la opción "Suppress DMs" allí para que Hellion Chat pueda recibir tells y abrir las pestañas automáticas.</value> <value>Nota: Si XIV Messenger u otro plugin similar suprime los tells, desactiva la opción "Suppress DMs" allí para que Hellion Chat pueda recibir tells y abrir las pestañas automáticas.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Historial de tells en pestañas automáticas</value> <value>Historial de tells en pestañas automáticas</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Solo tiene efecto cuando las pestañas de tell automáticas están activadas en la pestaña Chat.</value> <value>Solo tiene efecto cuando las pestañas de tell automáticas están activadas en la pestaña Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Ajustes reestructurados</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 ha reestructurado los ajustes en pestañas temáticas. Tu base de datos de chat y tu historial de mensajes no han cambiado. Los ajustes se han restablecido a los valores predeterminados. Si quieres volver a seleccionar tu perfil de privacidad, el botón Reabrir está en la pestaña Privacidad. Una copia de seguridad de la configuración anterior se encuentra en HellionChat.json.pre-v10-backup junto al archivo de configuración activo.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>General</value> <value>General</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Acerca de</value> <value>Acerca de</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Marcas de tiempo</value> <value>Marcas de tiempo</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Marco de ventana</value> <value>Marco de ventana</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Mostrar botón de selector de símbolos junto a la entrada del chat</value> <value>Mostrar botón de selector de símbolos junto a la entrada del chat</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Añade un pequeño botón a la izquierda del indicador de canal que abre un desplegable con iconos de FFXIV y una lista de símbolos curada. Desactívalo si prefieres una barra de entrada más sencilla.</value> <value>Añade un pequeño botón a la izquierda del indicador de canal que abre un desplegable con iconos de FFXIV y una lista de símbolos curada. Desactívalo si prefieres una barra de entrada más sencilla.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Almacenamiento</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Resumen</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Mantenimiento</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value> <value>Sistema</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Si usas varios linkshells, el mantenedor recomienda una pestaña por linkshell para una vista más clara. Duplica la pestaña y restringe la selección de canales en cada copia.</value> <value>Si usas varios linkshells, el mantenedor recomienda una pestaña por linkshell para una vista más clara. Duplica la pestaña y restringe la selección de canales en cada copia.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Icono de pestaña</value> <value>Icono de pestaña</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Mueve la ventana de chat y todas las ventanas emergentes activas de vuelta a la esquina superior izquierda del monitor principal. Útil cuando una ventana ha quedado fuera del área visible tras un cambio de configuración de pantalla (monitor desconectado, resolución cambiada). El plugin también realiza una comprobación automática de límites una vez por sesión; este botón es la salida de emergencia manual si algo sigue siendo inaccesible.</value> <value>Mueve la ventana de chat y todas las ventanas emergentes activas de vuelta a la esquina superior izquierda del monitor principal. Útil cuando una ventana ha quedado fuera del área visible tras un cambio de configuración de pantalla (monitor desconectado, resolución cambiada). El plugin también realiza una comprobación automática de límites una vez por sesión; este botón es la salida de emergencia manual si algo sigue siendo inaccesible.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Novedad en v0.6.0: ahora puedes escribir directamente en las ventanas emergentes. Activa el interruptor maestro en los ajustes de Ventana.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Entendido</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Abrir ajustes de ventana</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Puedes abrir cualquier pestaña de chat como su propia ventana. Haz clic en el icono de ventana en la parte superior derecha o haz clic derecho en la pestaña. Novedad en v0.6.1: la entrada en ventanas emergentes está activa por defecto (se puede desactivar en Ajustes → Ventana).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Entendido</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Abrir ajustes</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat no puede iniciarse mientras Chat 2 está cargado.</value> <value>Hellion Chat no puede iniciarse mientras Chat 2 está cargado.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Desactiva Chat 2 en /xlplugins y luego vuelve a activar Hellion Chat.</value> <value>Desactiva Chat 2 en /xlplugins y luego vuelve a activar Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>General</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Idioma, entrada, audio y rendimiento.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Apariencia</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Opacidad de ventana, fuentes, movimiento</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Elige un theme o importa el tuyo</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Ventana</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Cuándo es visible la ventana y si se puede mover.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, vista previa, comportamiento de mensajes y emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Pestañas</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Crea y configura pestañas de chat personalizadas.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Base de datos</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Almacenamiento, migración, limpieza de datos antiguos</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Acerca de</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensiones, versión, información del proyecto, traductores y changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Mantener</value> <value>Mantener</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Privacidad primero</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Abierto</value> <value>Abierto</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Datos y privacidad</value> <value>Datos y privacidad</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtro de privacidad, retención, limpieza, exportación y estadísticas de base de datos.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avanzado (Mayús+clic para abrir)</value> <value>Avanzado (Mayús+clic para abrir)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 ha reorganizado el menú de ajustes y eliminado la antigua opción "Anular estilo" (reemplazada por el sistema de themes de la versión 1.1.0). El resto de tus ajustes no ha cambiado. La transparencia de ventana se ha migrado a "Theme &amp; Layout". Una copia de seguridad de la configuración anterior se encuentra en pluginConfigs/HellionChat.json.pre-v16-backup junto al HellionChat.json activo.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Las integraciones de plugins permiten que HellionChat trabaje junto con otros plugins de Dalamud instalados. Cada integración detecta automáticamente su objetivo y se desactiva silenciosamente cuando el plugin objetivo no está presente.</value> <value>Las integraciones de plugins permiten que HellionChat trabaje junto con otros plugins de Dalamud instalados. Cada integración detecta automáticamente su objetivo y se desactiva silenciosamente cuando el plugin objetivo no está presente.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Entrada</value> <value>Entrada</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Este mensaje contiene símbolos exclusivos del plugin que otros jugadores pueden ver como cuadros vacíos. Presiona Intro de nuevo para enviarlo de todas formas.</value> <value>Este mensaje contiene símbolos exclusivos del plugin que otros jugadores pueden ver como cuadros vacíos. Presiona Intro de nuevo para enviarlo de todas formas.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Insertar símbolo</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Ajustes</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Ocultar el chat (Intro para recuperarlo)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Devolver esta pestaña a la ventana principal</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Ya se está ejecutando otra operación de base de datos: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>limpieza de retención</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>exportación</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>limpieza</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>borrado del historial</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>El filtro de privacidad está desactivado, así que se guarda cada canal y nada en la base de datos contradice tu configuración. Activa el filtro y elige canales primero.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>No hay ningún canal seleccionado, así que una limpieza borraría todo el historial. Elige los canales que quieras conservar, o usa el botón de borrar si de verdad quieres eliminarlo todo.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Hay {0:N0} mensajes guardados. Si quieres conservar una copia, expórtalos antes de borrar.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Mayús: ejecuta la limpieza de retención ahora mismo en lugar de esperar al barrido diario. Borra los mensajes más antiguos que los límites de arriba.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>No se pudo borrar el historial. No se eliminó nada, consulta /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetría</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>No se recopila telemetría. El plugin no envía nada sobre ti ni sobre tu uso a ninguna parte.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Traducción automática</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Bloquear</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Ir al mensaje más reciente</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Insertar marca del mapa &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Insertar objeto enlazado &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>desactivado</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Comportamiento</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Atajos de teclado</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notificaciones</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Modos de visualización</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historial</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Ayuda de comandos</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Aviso del plugin</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Modo de disposición</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacidad</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Comportamiento de tamaño</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Apertura automática de tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marca</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Enlaces</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integraciones</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Créditos</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licencia</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Haz clic en un botón y luego pulsa la combinación de teclas. Esc borra.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Cambiar a la pestaña siguiente</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Cambiar a la pestaña anterior</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Cambiarlo reconstruye el atlas de fuentes, así que el chat queda en blanco un momento.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Reloj de 24 horas</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Mostrar el historial de sesiones anteriores</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Desactivado, el registro empieza vacío cada vez que se inicia el juego y solo se llena con los mensajes recibidos desde entonces.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Lado de la ayuda de comandos</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>En qué lado aparece la lista de sugerencias mientras escribes.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Modo de apertura automática de tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Dónde se abre un tell cuando llega.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Cambiar a la pestaña con cada tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Si no, la pestaña se abre en segundo plano tras el primero.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Pestañas superiores</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Ubicación de las pestañas</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Dónde se sitúa la lista de pestañas en la ventana principal.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Mostrar la barra de título</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Barra de título en pop-outs</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Permitir mover</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Permitir redimensionar</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Umbral de cambio automático de la barra lateral</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Por debajo de este ancho la barra lateral se pliega en pestañas superiores, en píxeles.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Posición de la vista previa</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Mostrar la vista previa solo al escribir</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Repositorio de Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifiesto del repositorio personalizado</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Tema activo: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Bifurcar y editar</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Los temas integrados no se pueden editar directamente. Bifurcar crea una copia personalizada que puedes editar y guardar.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Editar tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Editando: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Superficies</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Bordes</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Texto</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identidad</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Estado</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Guardar</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Cancelar</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Restablecer al original</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>No se puede restablecer mientras editas una bifurcación. Guarda o cancela primero.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Guarda o descarta tus cambios primero</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Personalizados ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Bifurcar el tema activo</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importar archivo de tema…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Ruta al archivo JSON (o arrástralo a la carpeta)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exportar tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Fríos</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturales</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Clásicos</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (incluida)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Fuente del juego</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Activa: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Escribe un mensaje...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplicar</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>vista previa</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>mantenimiento</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} pestaña</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} pestañas</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} susurro</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} susurros</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} msj</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k msj</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Campeón» Vista previa</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>abierto</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Turvaverkko ChatType-tyypeille, jotka tulevat tulevissa FFXIV-päivityksissä eikä lisäosa vielä tunne niitä. Oletuksena POIS (tietojen minimointi). Ota käyttöön, jos haluat myös tulevat kanavat lokitettavan kokonaan.</value> <value>Turvaverkko ChatType-tyypeille, jotka tulevat tulevissa FFXIV-päivityksissä eikä lisäosa vielä tunne niitä. Oletuksena POIS (tietojen minimointi). Ota käyttöön, jos haluat myös tulevat kanavat lokitettavan kokonaan.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Käytä suodatinta olemassa olevaan tietokantaan</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Tietosuojasuodatin vaikuttaa vain uusiin viesteihin. Alla oleva siivous antaa sinulle mahdollisuuden poistaa jälkikäteen jo tallennetut viestit, jotka eivät vastaa tallennettua sallittujen listaasi.</value> <value>Tietosuojasuodatin vaikuttaa vain uusiin viesteihin. Alla oleva siivous antaa sinulle mahdollisuuden poistaa jälkikäteen jo tallennetut viestit, jotka eivät vastaa tallennettua sallittujen listaasi.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Siivous käyttää TALLENNETTUA sallittujen listaasi (Plugin.Config), ei yllä olevia tallentamattomia muutoksia. Napsauta Tallenna ensin, jos haluat muutostesi tulevan voimaan.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Manuaalinen ajo käyttää TALLENNETTUA säilytyskäytäntöäsi, ei yllä olevia liukusäätimen arvoja. Napsauta Tallenna ensin, jos haluat ajon soveltavan nykyiset muutoksesi.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Esikatselu on vanhentunut: sallittujen listasi on muuttunut viimeisen päivityksen jälkeen. Napsauta Päivitä laskeaksesi uudelleen.</value> <value>Esikatselu on vanhentunut: sallittujen listasi on muuttunut viimeisen päivityksen jälkeen. Napsauta Päivitä laskeaksesi uudelleen.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Käytä säilytystä nyt</value> <value>Käytä säilytystä nyt</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Ajaa säilytykseen liittyvän siivouksen heti TALLENNETULLA käytännöllä. Tallenna muutoksesi ensin.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Säilytykseen liittyvä siivous käynnissä taustalla…</value> <value>Säilytykseen liittyvä siivous käynnissä taustalla…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Ulkoasu</value> <value>Ulkoasu</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Lataa edellinen istunto käynnistyksen yhteydessä</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Käytä suodattimia aiempien istuntojen viesteihin</value> <value>Käytä suodattimia aiempien istuntojen viesteihin</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Asetukset → Hellion Chat hienosäätöä varten myöhemmin</value> <value>Asetukset → Hellion Chat hienosäätöä varten myöhemmin</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Vie (GDPR Art. 15 — Tarkastusoikeus)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Vie tallennetut viestit Markdown-, JSON- tai CSV-muodossa. Näin voit täyttää tietopyynnön henkilöltä, jonka viestejä olet tallentanut, tai ottaa oman historiasi mukaasi.</value> <value>Vie tallennetut viestit Markdown-, JSON- tai CSV-muodossa. Näin voit täyttää tietopyynnön henkilöltä, jonka viestejä olet tallentanut, tai ottaa oman historiasi mukaasi.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2:n yhteisökääntäjät (upstream)</value> <value>Chat 2:n yhteisökääntäjät (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktiiviset tellit</value> <value>Aktiiviset tellit</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Kiinnitetty: selviää relogista.</value> <value>Kiinnitetty: selviää relogista.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Huomio: Jos XIV Messenger tai vastaava lisäosa estää tellit, poista siellä käytöstä "Suppress DMs" -asetus, jotta Hellion Chat voi vastaanottaa tellejä ja avata auto-välilehdet.</value> <value>Huomio: Jos XIV Messenger tai vastaava lisäosa estää tellit, poista siellä käytöstä "Suppress DMs" -asetus, jotta Hellion Chat voi vastaanottaa tellejä ja avata auto-välilehdet.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell-historia auto-välilehdissä</value> <value>Tell-historia auto-välilehdissä</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Tulee voimaan vain, kun auto-tell-välilehdet ovat käytössä Chat-välilehdellä.</value> <value>Tulee voimaan vain, kun auto-tell-välilehdet ovat käytössä Chat-välilehdellä.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Asetukset uudelleenjärjestetty</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 on uudelleenjärjestänyt asetukset temaattisiin välilehtiin. Chat-tietokantasi ja viestihistoriasi pysyvät muuttumattomina. Asetukset on nollattu oletuksiin. Jos haluat valita tietosuojaprofiilin uudelleen, Avaa uudelleen -painike on Tietosuoja-välilehdellä. Varmuuskopio aiemmasta asetustiedostosta löytyy nimellä HellionChat.json.pre-v10-backup aktiivisen asetustiedoston vierestä.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Yleiset</value> <value>Yleiset</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Tietoja</value> <value>Tietoja</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Teema</value> <value>Teema</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Aikaleimat</value> <value>Aikaleimat</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Ikkunakehys</value> <value>Ikkunakehys</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Näytä symbolinvalitsinpainike chat-syötteen vieressä</value> <value>Näytä symbolinvalitsinpainike chat-syötteen vieressä</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Lisää pienen painikkeen kanavan ilmaisimen vasemmalle puolelle, joka avaa ponnahdusikkunan FFXIV-kuvakkeilla ja kuratoitulla symbolilistalla. Poista käytöstä, jos haluat siistimmän syöterivin.</value> <value>Lisää pienen painikkeen kanavan ilmaisimen vasemmalle puolelle, joka avaa ponnahdusikkunan FFXIV-kuvakkeilla ja kuratoitulla symbolilistalla. Poista käytöstä, jos haluat siistimmän syöterivin.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Tallennus</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Yleiskatsaus</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Ylläpito</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Järjestelmä</value> <value>Järjestelmä</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Jos käytät useita linkshell-ryhmiä, ylläpitäjä suosittelee yhtä välilehteä kullekin, jotta yleiskatsaus pysyy selkeänä. Kopioi välilehti ja rajaa kanavan valinta kussakin kopiossa.</value> <value>Jos käytät useita linkshell-ryhmiä, ylläpitäjä suosittelee yhtä välilehteä kullekin, jotta yleiskatsaus pysyy selkeänä. Kopioi välilehti ja rajaa kanavan valinta kussakin kopiossa.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Välilehden kuvake</value> <value>Välilehden kuvake</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Siirtää chat-ikkunan ja kaikki aktiiviset irrotetut ikkunat takaisin ensisijaisen näytön vasempaan yläkulmaan. Hyödyllinen, kun ikkuna on päätynyt näkyvän alueen ulkopuolelle näyttöasettelumuutoksen jälkeen (näyttö irrotettu, tarkkuus muutettu). Lisäosa suorittaa myös automaattisen rajatarkistuksen kerran istunnon aikana; tämä painike on manuaalinen pelastusreitti, jos jokin silti päätyy ulottumattomiin.</value> <value>Siirtää chat-ikkunan ja kaikki aktiiviset irrotetut ikkunat takaisin ensisijaisen näytön vasempaan yläkulmaan. Hyödyllinen, kun ikkuna on päätynyt näkyvän alueen ulkopuolelle näyttöasettelumuutoksen jälkeen (näyttö irrotettu, tarkkuus muutettu). Lisäosa suorittaa myös automaattisen rajatarkistuksen kerran istunnon aikana; tämä painike on manuaalinen pelastusreitti, jos jokin silti päätyy ulottumattomiin.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Uutta versiossa v0.6.0: voit nyt kirjoittaa suoraan irrotettuissa ikkunoissa. Ota pääkytkin käyttöön Ikkuna-asetuksissa.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Selvä</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Avaa ikkunan asetukset</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Voit avata minkä tahansa chat-välilehden omana ikkunanaan. Napsauta ikkunakuvaketta oikeassa yläkulmassa tai hiiren oikealla painikkeella välilehteä. Uutta versiossa v0.6.1: irrotetun ikkunan syöte on oletuksena aktiivinen (voidaan poistaa käytöstä kohdassa Asetukset → Ikkuna).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Selvä</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Avaa asetukset</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat ei voi käynnistyä, kun Chat 2 on ladattuna.</value> <value>Hellion Chat ei voi käynnistyä, kun Chat 2 on ladattuna.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Poista Chat 2 käytöstä /xlplugins-kohdassa, ota sitten Hellion Chat uudelleen käyttöön.</value> <value>Poista Chat 2 käytöstä /xlplugins-kohdassa, ota sitten Hellion Chat uudelleen käyttöön.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Yleiset</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Kieli, syöte, ääni ja suorituskyky.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Ulkoasu</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Ikkunan läpinäkyvyys, fontit, liike</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Teemat</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Valitse teema tai tuo oma</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Ikkuna</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Milloin ikkuna on näkyvissä ja voiko sitä siirtää.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tellit, esikatselu, viestien toiminta ja emotet.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Välilehdet</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Luo ja määritä mukautettuja chat-välilehtiä.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Tietokanta</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Tallennus, siirto, vanhan datan siivous</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Tietoja</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Laajennukset, versio, projektin tiedot, kääntäjät ja changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Teemat</value> <value>Teemat</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Säilytä</value> <value>Säilytä</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Yksityisyys ensin</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Avoin</value> <value>Avoin</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Tiedot ja yksityisyys</value> <value>Tiedot ja yksityisyys</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Yksityisyyssuodatin, säilytys, siivous, vienti ja tietokantatilastot.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Teema</value> <value>Teema</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Lisäasetukset (Shift+napsauta avataksesi)</value> <value>Lisäasetukset (Shift+napsauta avataksesi)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 on uudelleenjärjestänyt asetusvalikon ja poistanut vanhan "Ohita tyyli" -vaihtoehdon (korvattu versiossa 1.1.0 esitellyllä teemajärjestelmällä). Muut asetuksesi pysyvät muuttumattomina. Ikkunan läpinäkyvyys on siirretty kohtaan "Teema &amp; asettelu". Varmuuskopio aiemmasta asetustiedostosta löytyy nimellä pluginConfigs/HellionChat.json.pre-v16-backup aktiivisen HellionChat.json-tiedoston vierestä.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Lisäosaintegraatiot mahdollistavat HellionChatin yhteistyön muiden asennettujen Dalamud-lisäosien kanssa. Jokainen integraatio tunnistaa kohteensa automaattisesti ja poistaa itsensä hiljaisesti käytöstä, kun kohdalisäosa puuttuu.</value> <value>Lisäosaintegraatiot mahdollistavat HellionChatin yhteistyön muiden asennettujen Dalamud-lisäosien kanssa. Jokainen integraatio tunnistaa kohteensa automaattisesti ja poistaa itsensä hiljaisesti käytöstä, kun kohdalisäosa puuttuu.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Syöte</value> <value>Syöte</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Tämä viesti sisältää plugin-symboleja, jotka muut pelaajat saattavat nähdä tyhjinä ruutuina. Paina Enter uudelleen lähettääksesi joka tapauksessa.</value> <value>Tämä viesti sisältää plugin-symboleja, jotka muut pelaajat saattavat nähdä tyhjinä ruutuina. Paina Enter uudelleen lähettääksesi joka tapauksessa.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Lisää symboli</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Asetukset</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Piilota chat (Enter palauttaa sen)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Palauta tämä välilehti pääikkunaan</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Toinen tietokantatoiminto on käynnissä: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>säilytysajo</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>vienti</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>siivous</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>historian tyhjennys</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Yksityisyyssuodatin on pois päältä, joten jokainen kanava tallennetaan eikä mikään tietokannassa ole ristiriidassa asetustesi kanssa. Kytke suodatin päälle ja valitse kanavat ensin.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Yhtään kanavaa ei ole valittu, joten siivous poistaisi koko historian. Valitse säilytettävät kanavat tai käytä tyhjennyspainiketta, jos haluat todella poistaa kaiken.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Tallennettuna on {0:N0} viestiä. Jos haluat säilyttää kopion, vie ne ennen tyhjennystä.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: suorittaa säilytysajon heti sen sijaan, että odottaisi päivittäistä ajoa. Poistaa yllä olevia rajoja vanhemmat viestit.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Historian tyhjennys epäonnistui. Mitään ei poistettu, katso /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetria</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Telemetriaa ei kerätä. Lisäosa ei lähetä mitään sinusta tai käytöstäsi minnekään.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Automaattikäännös</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Estä</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Siirry uusimpaan viestiin</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Lisää karttamerkki &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Lisää linkitetty esine &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>pois</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Toiminta</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Pikanäppäimet</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Ilmoitukset</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Näyttötilat</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historia</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Komento-ohje</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Lisäosailmoitus</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Asettelutila</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Läpinäkymättömyys</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Koon muuttaminen</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Tellin automaattinen avaus</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Sivupalkki</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Tuotemerkki</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Linkit</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integraatiot</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Tekijät</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Lisenssi</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Napsauta painiketta ja paina sitten näppäinyhdistelmää. Esc tyhjentää.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Siirry seuraavaan välilehteen</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Siirry edelliseen välilehteen</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Vaihto rakentaa fonttiatlaksen uudelleen, joten chat on hetken tyhjä.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24 tunnin kello</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Näytä aiempien istuntojen historia</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Pois päältä loki alkaa tyhjänä joka käynnistyksellä ja täyttyy vain sen jälkeen saapuneilla viesteillä.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Komento-ohjeen puoli</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Kummalla puolella komentovihjeluettelo näkyy kirjoitettaessa.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Tellin automaattisen avauksen tila</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Missä tell avautuu saapuessaan.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Vaihda välilehteen jokaisella tellillä</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Muuten välilehti avautuu taustalle ensimmäisen jälkeen.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Sivupalkki</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Ylävälilehdet</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Välilehtien sijainti</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Missä välilehtiluettelo sijaitsee pääikkunassa.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Näytä otsikkopalkki</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Näytä otsikkopalkki irrotetuissa ikkunoissa</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Salli siirtäminen</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Salli koon muuttaminen</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Sivupalkin automaattisen vaihdon raja</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Tämän leveyden alapuolella sivupalkki taittuu ylävälilehdiksi, pikseleinä.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Esikatselun sijainti</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Näytä esikatselu vain kirjoitettaessa</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea-tietovarasto</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Mukautetun tietovaraston manifesti</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktiivinen teema: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Haaroita ja muokkaa</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Sisäänrakennettuja teemoja ei voi muokata suoraan. Haaroitus luo mukautetun kopion, jota voit muokata ja tallentaa.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Muokkaa teemaa</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Muokataan: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Pinnat</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Reunat</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Teksti</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identiteetti</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Tila</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Tallenna</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Peruuta</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Palauta lähteeseen</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Palautus ei ole käytettävissä haaraa muokattaessa. Tallenna tai peruuta ensin.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Tallenna tai hylkää muutoksesi ensin</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Mukautetut ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Haaroita aktiivinen teema</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Tuo teematiedosto…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>JSON-tiedoston polku (tai vedä kansioon)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Vie teema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Viileät</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Luonnolliset</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klassiset</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (mukana)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Pelin fontti</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Yleinen: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktiivinen: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Kirjoita viesti...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Kahdenna</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>esikatselu</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>ylläpito</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} välilehti</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} välilehteä</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} kuiskaus</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} kuiskausta</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} vie.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k vie.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Mestari» Esikatselu</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>auki</value>
</data>
</root>
+360 -148
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Filet de sécurité pour les ChatTypes ajoutés par de futurs correctifs de FFXIV que le plugin ne connaît pas encore. Désactivé par défaut (minimisation des données). Activez cette option si vous souhaitez que les futurs canaux soient également enregistrés intégralement.</value> <value>Filet de sécurité pour les ChatTypes ajoutés par de futurs correctifs de FFXIV que le plugin ne connaît pas encore. Désactivé par défaut (minimisation des données). Activez cette option si vous souhaitez que les futurs canaux soient également enregistrés intégralement.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Appliquer le filtre à la base de données existante</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Le filtre de confidentialité n'affecte que les nouveaux messages. Le nettoyage ci-dessous vous permet de supprimer rétroactivement les messages déjà enregistrés qui ne correspondent pas à votre liste blanche enregistrée.</value> <value>Le filtre de confidentialité n'affecte que les nouveaux messages. Le nettoyage ci-dessous vous permet de supprimer rétroactivement les messages déjà enregistrés qui ne correspondent pas à votre liste blanche enregistrée.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Le nettoyage utilise votre liste blanche ENREGISTRÉE (Plugin.Config), pas les modifications non enregistrées ci-dessus. Cliquez d'abord sur Enregistrer si vous souhaitez que vos modifications actuelles soient appliquées.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>L'exécution manuelle utilise votre politique de conservation ENREGISTRÉE, pas les valeurs du curseur ci-dessus. Cliquez d'abord sur Enregistrer si vous souhaitez que l'exécution applique vos modifications actuelles.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>L'aperçu est obsolète : votre liste blanche a été modifiée depuis le dernier rafraîchissement. Cliquez sur Rafraîchir pour recalculer.</value> <value>L'aperçu est obsolète : votre liste blanche a été modifiée depuis le dernier rafraîchissement. Cliquez sur Rafraîchir pour recalculer.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Appliquer la conservation maintenant</value> <value>Appliquer la conservation maintenant</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Maj : exécute immédiatement le nettoyage de conservation en utilisant la politique ENREGISTRÉE. Enregistrez d'abord vos modifications.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Nettoyage de conservation en cours en arrière-plan…</value> <value>Nettoyage de conservation en cours en arrière-plan…</value>
</data> </data>
@@ -187,7 +175,7 @@
<value>Minimisation des données (recommandé)</value> <value>Minimisation des données (recommandé)</value>
</data> </data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve"> <data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Seules vos propres conversations sont enregistrées : tells, équipe, CL, linkshells, linkshells inter-mondes, alliance et ExtraChat. Le chat public, les dialogues PNJ et le spam système sont écartés au niveau du stockage. La conservation suit les valeurs par défaut de la spécification (tells 365 jours, vos canaux de conversation 90 jours).</value> <value>Seules vos propres conversations sont enregistrées : messages privés, équipe, CL, linkshells, linkshells inter-mondes, alliance et ExtraChat. Le chat public, les dialogues PNJ et le spam système sont écartés au niveau du stockage. La conservation suit les valeurs par défaut de la spécification (messages privés 365 jours, vos canaux de conversation 90 jours).</value>
</data> </data>
<data name="Wizard_Profile_PrivacyFirst_Apply" xml:space="preserve"> <data name="Wizard_Profile_PrivacyFirst_Apply" xml:space="preserve">
<value>Appliquer la minimisation des données</value> <value>Appliquer la minimisation des données</value>
@@ -268,19 +256,16 @@
<value>Historique</value> <value>Historique</value>
</data> </data>
<data name="Wizard_Step3_Section_TellTabs" xml:space="preserve"> <data name="Wizard_Step3_Section_TellTabs" xml:space="preserve">
<value>Onglets de tell</value> <value>Onglets de message privé</value>
</data> </data>
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Visuel</value> <value>Visuel</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Charger la session précédente au démarrage</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Appliquer les filtres aux messages des sessions précédentes</value> <value>Appliquer les filtres aux messages des sessions précédentes</value>
</data> </data>
<data name="Wizard_Step3_AutoTellTabsHistoryPreload_Label" xml:space="preserve"> <data name="Wizard_Step3_AutoTellTabsHistoryPreload_Label" xml:space="preserve">
<value>Précharger N messages de tell à l'ouverture d'un onglet automatique</value> <value>Précharger N messages privés à l'ouverture d'un onglet automatique</value>
</data> </data>
<data name="Wizard_Step3_UseCompactDensity_Label" xml:space="preserve"> <data name="Wizard_Step3_UseCompactDensity_Label" xml:space="preserve">
<value>Densité compacte</value> <value>Densité compacte</value>
@@ -304,7 +289,7 @@
<value>Historique : {0}</value> <value>Historique : {0}</value>
</data> </data>
<data name="Wizard_Step4_Summary_TellTabs" xml:space="preserve"> <data name="Wizard_Step4_Summary_TellTabs" xml:space="preserve">
<value>Onglets de tell : préchargement de {0} messages</value> <value>Onglets de message privé : préchargement de {0} messages</value>
</data> </data>
<data name="Wizard_Step4_Summary_Visual" xml:space="preserve"> <data name="Wizard_Step4_Summary_Visual" xml:space="preserve">
<value>Visuel : {0}</value> <value>Visuel : {0}</value>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Paramètres → Hellion Chat pour affiner plus tard</value> <value>Paramètres → Hellion Chat pour affiner plus tard</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Exportation (RGPD art. 15 — Droit d'accès)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exportez les messages enregistrés en Markdown, JSON ou CSV. Cela vous permet de répondre à une demande d'accès émanant d'une personne dont vous avez enregistré les messages, ou d'emporter votre propre historique.</value> <value>Exportez les messages enregistrés en Markdown, JSON ou CSV. Cela vous permet de répondre à une demande d'accès émanant d'une personne dont vous avez enregistré les messages, ou d'emporter votre propre historique.</value>
</data> </data>
@@ -457,9 +439,9 @@
<value>Traducteurs communautaires de Chat 2 (en amont)</value> <value>Traducteurs communautaires de Chat 2 (en amont)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Tells actifs</value> <value>Messages privés actifs</value>
</data> </data>
<data name="AutoTellTabs_HistorySeparator" xml:space="preserve"> <data name="AutoTellTabs_HistorySeparator" xml:space="preserve">
<value>— Conversations précédentes —</value> <value>— Conversations précédentes —</value>
@@ -483,7 +465,7 @@
<value>Promouvoir en permanent</value> <value>Promouvoir en permanent</value>
</data> </data>
<data name="PinTab_PromoteTooltip" xml:space="preserve"> <data name="PinTab_PromoteTooltip" xml:space="preserve">
<value>Transforme ce TempTell en onglet régulier. Le lien du tell avec le partenaire est rompu. L'onglet capturera désormais les messages selon ses filtres de canaux. Pour « onglet qui survit à la reconnexion en restant lié à ce partenaire », utilisez Épingler l'onglet à la place.</value> <value>Transforme cet onglet MP temporaire en onglet régulier. Le lien du message privé avec le partenaire est rompu. L'onglet capturera désormais les messages selon ses filtres de canaux. Pour « onglet qui survit à la reconnexion en restant lié à ce partenaire », utilisez Épingler l'onglet à la place.</value>
</data> </data>
<data name="PinTab_PinTooltip" xml:space="preserve"> <data name="PinTab_PinTooltip" xml:space="preserve">
<value>Les onglets épinglés survivent à la reconnexion et restent liés à ce partenaire de conversation.</value> <value>Les onglets épinglés survivent à la reconnexion et restent liés à ce partenaire de conversation.</value>
@@ -495,18 +477,18 @@
<value>Largeur de la barre latérale</value> <value>Largeur de la barre latérale</value>
</data> </data>
<data name="Settings_ThemeAndLayout_SidebarWidth_Description" xml:space="preserve"> <data name="Settings_ThemeAndLayout_SidebarWidth_Description" xml:space="preserve">
<value>Largeur de la barre latérale d'onglets en pixels. La valeur par défaut (44 px) n'affiche que les icônes ; élargissez-la pour permettre l'affichage complet d'en-têtes de section comme « Tells actifs (3) » sans troncature.</value> <value>Largeur de la barre latérale d'onglets en pixels. La valeur par défaut (44 px) n'affiche que les icônes ; élargissez-la pour permettre l'affichage complet d'en-têtes de section comme « Messages privés actifs (3) » sans troncature.</value>
</data> </data>
<data name="PinTab_LimitReached" xml:space="preserve"> <data name="PinTab_LimitReached" xml:space="preserve">
<value>Maximum de {0} onglets de tell épinglés atteint. Désépinglez-en un d'abord, ou utilisez Promouvoir en permanent.</value> <value>Maximum de {0} onglets de message privé épinglés atteint. Désépinglez-en un d'abord, ou utilisez Promouvoir en permanent.</value>
</data> </data>
<data name="PinTab_PinnedTooltip" xml:space="preserve"> <data name="PinTab_PinnedTooltip" xml:space="preserve">
<value>Épinglé : survit à la reconnexion.</value> <value>Épinglé : survit à la reconnexion.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Onglets de tell automatiques</value> <value>Onglets de message privé automatiques</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve">
<value>Ouvrir automatiquement un onglet par partenaire de conversation pour chaque /tell</value> <value>Ouvrir automatiquement un onglet par partenaire de conversation pour chaque /tell</value>
@@ -515,22 +497,22 @@
<value>Dès que vous recevez ou envoyez un /tell, un onglet temporaire est automatiquement ouvert pour ce joueur. Les onglets sont supprimés à la déconnexion.</value> <value>Dès que vous recevez ou envoyez un /tell, un onglet temporaire est automatiquement ouvert pour ce joueur. Les onglets sont supprimés à la déconnexion.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Limit_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Limit_Name" xml:space="preserve">
<value>Nombre maximum d'onglets de tell automatiques</value> <value>Nombre maximum d'onglets de message privé automatiques</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Limit_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Limit_Description" xml:space="preserve">
<value>Lorsque la limite est atteinte, les onglets salués avec l'activité la plus ancienne sont fermés en premier. Les changements prennent effet au prochain /tell. Cette limite s'applique au pool géré automatiquement. Les onglets de tell épinglés (clic droit → Épingler l'onglet) vivent dans un pool distinct pouvant contenir jusqu'à 5 éléments et survivent à la reconnexion.</value> <value>Lorsque la limite est atteinte, les onglets salués avec l'activité la plus ancienne sont fermés en premier. Les changements prennent effet au prochain /tell. Cette limite s'applique au pool géré automatiquement. Les onglets de message privé épinglés (clic droit → Épingler l'onglet) vivent dans un pool distinct pouvant contenir jusqu'à 5 éléments et survivent à la reconnexion.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Compact_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Compact_Name" xml:space="preserve">
<value>Affichage compact</value> <value>Affichage compact</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Compact_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Compact_Description" xml:space="preserve">
<value>Affiche uniquement un fin séparateur entre les onglets réguliers et les onglets de tell automatiques, sans en-tête de section.</value> <value>Affiche uniquement un fin séparateur entre les onglets réguliers et les onglets de message privé automatiques, sans en-tête de section.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_GreetedToggle_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_GreetedToggle_Name" xml:space="preserve">
<value>Afficher le bouton « Marquer comme salué »</value> <value>Afficher le bouton « Marquer comme salué »</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_GreetedToggle_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_GreetedToggle_Description" xml:space="preserve">
<value>Ajoute un bouton à côté de chaque onglet de tell automatique pour marquer un partenaire de conversation comme déjà salué. Le nom de l'onglet est alors atténué. Utile pour les hôtes de club qui gèrent de nombreuses conversations en parallèle. Désactivé par défaut.</value> <value>Ajoute un bouton à côté de chaque onglet de message privé automatique pour marquer un partenaire de conversation comme déjà salué. Le nom de l'onglet est alors atténué. Utile pour les hôtes de club qui gèrent de nombreuses conversations en parallèle. Désactivé par défaut.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_OpenAsPopout_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_OpenAsPopout_Name" xml:space="preserve">
<value>Ouvrir les nouveaux onglets /tell directement comme fenêtres détachées</value> <value>Ouvrir les nouveaux onglets /tell directement comme fenêtres détachées</value>
@@ -539,35 +521,29 @@
<value>Quand cette option est active, chaque onglet /tell nouvellement créé est immédiatement ouvert dans sa propre fenêtre. Fermer la fenêtre renvoie l'onglet dans la barre latérale.</value> <value>Quand cette option est active, chaque onglet /tell nouvellement créé est immédiatement ouvert dans sa propre fenêtre. Fermer la fenêtre renvoie l'onglet dans la barre latérale.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_PreloadHint" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_PreloadHint" xml:space="preserve">
<value>Le nombre de tells préchargés peut être configuré dans l'onglet Confidentialité.</value> <value>Le nombre de messages privés préchargés peut être configuré dans l'onglet Confidentialité.</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_ConflictHint" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_ConflictHint" xml:space="preserve">
<value>Remarque : si XIV Messenger ou un plugin similaire supprime les tells, désactivez l'option « Suppress DMs » à cet endroit afin que Hellion Chat puisse recevoir les tells et ouvrir les onglets automatiques.</value> <value>Remarque : si XIV Messenger ou un plugin similaire supprime les messages privés, désactivez l'option « Suppress DMs » à cet endroit afin que Hellion Chat puisse recevoir les messages privés et ouvrir les onglets automatiques.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Historique des tells dans les onglets automatiques</value> <value>Historique des messages privés dans les onglets automatiques</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Name" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Name" xml:space="preserve">
<value>Nombre de tells préchargés</value> <value>Nombre de messages privés préchargés</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Description" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Description" xml:space="preserve">
<value>Combien de messages de tell antérieurs sont chargés depuis la base de données à l'ouverture d'un onglet de tell automatique. 0 désactive le préchargement.</value> <value>Combien de messages privés antérieurs sont chargés depuis la base de données à l'ouverture d'un onglet de message privé automatique. 0 désactive le préchargement.</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Hint" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Hint" xml:space="preserve">
<value>N'a d'effet que lorsque les onglets de tell automatiques sont activés dans l'onglet Chat.</value> <value>N'a d'effet que lorsque les onglets de message privé automatiques sont activés dans l'onglet Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Paramètres restructurés</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 a restructuré les paramètres en onglets thématiques. Votre base de données de chat et votre historique de messages restent inchangés. Les paramètres ont été réinitialisés à leurs valeurs par défaut. Si vous souhaitez resélectionner votre profil de confidentialité, le bouton Rouvrir se trouve dans l'onglet Confidentialité. Une sauvegarde de la configuration précédente est disponible dans HellionChat.json.pre-v10-backup, à côté du fichier de configuration actif.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Général</value> <value>Général</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>À propos</value> <value>À propos</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Thème</value> <value>Thème</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Horodatages</value> <value>Horodatages</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Cadre de la fenêtre</value> <value>Cadre de la fenêtre</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Afficher le bouton de sélection de symboles à côté de la saisie</value> <value>Afficher le bouton de sélection de symboles à côté de la saisie</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Ajoute un petit bouton à gauche de l'indicateur de canal qui ouvre une fenêtre contextuelle contenant des icônes FFXIV et une liste de symboles soigneusement sélectionnés. Désactivez si vous préférez une barre de saisie plus épurée.</value> <value>Ajoute un petit bouton à gauche de l'indicateur de canal qui ouvre une fenêtre contextuelle contenant des icônes FFXIV et une liste de symboles soigneusement sélectionnés. Désactivez si vous préférez une barre de saisie plus épurée.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Stockage</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Visualiseur</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Maintenance</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Système</value> <value>Système</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Si vous utilisez plusieurs linkshells, le mainteneur recommande un onglet par linkshell pour une meilleure vue d'ensemble. Dupliquez l'onglet et restreignez la sélection de canaux dans chaque copie.</value> <value>Si vous utilisez plusieurs linkshells, le mainteneur recommande un onglet par linkshell pour une meilleure vue d'ensemble. Dupliquez l'onglet et restreignez la sélection de canaux dans chaque copie.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Icône d'onglet</value> <value>Icône d'onglet</value>
</data> </data>
@@ -692,7 +659,7 @@
<value>Activer la saisie dans les fenêtres détachées</value> <value>Activer la saisie dans les fenêtres détachées</value>
</data> </data>
<data name="Settings_Window_PopOutInputEnabled_Description" xml:space="preserve"> <data name="Settings_Window_PopOutInputEnabled_Description" xml:space="preserve">
<value>Interrupteur principal : permet de taper et d'envoyer directement dans n'importe quelle fenêtre détachée (y compris les onglets de tell automatiques). Le changement de canal dans une fenêtre détachée agit globalement comme dans la fenêtre principale ; le tampon de texte et le curseur d'historique sont indépendants pour chaque fenêtre détachée.</value> <value>Interrupteur principal : permet de taper et d'envoyer directement dans n'importe quelle fenêtre détachée (y compris les onglets de message privé automatiques). Le changement de canal dans une fenêtre détachée agit globalement comme dans la fenêtre principale ; le tampon de texte et le curseur d'historique sont indépendants pour chaque fenêtre détachée.</value>
</data> </data>
<data name="Settings_Window_ResetPosition_Name" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Name" xml:space="preserve">
<value>Réinitialiser la position de la fenêtre</value> <value>Réinitialiser la position de la fenêtre</value>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Ramène la fenêtre de chat et toutes les fenêtres détachées actives dans le coin supérieur gauche de l'écran principal. Utile lorsqu'une fenêtre s'est retrouvée hors de la zone visible après un changement de configuration d'affichage (déconnexion d'un moniteur, changement de résolution). Le plugin effectue également une vérification automatique des limites une fois par session ; ce bouton est l'issue de secours manuelle si quelque chose reste malgré tout inaccessible.</value> <value>Ramène la fenêtre de chat et toutes les fenêtres détachées actives dans le coin supérieur gauche de l'écran principal. Utile lorsqu'une fenêtre s'est retrouvée hors de la zone visible après un changement de configuration d'affichage (déconnexion d'un moniteur, changement de résolution). Le plugin effectue également une vérification automatique des limites une fois par session ; ce bouton est l'issue de secours manuelle si quelque chose reste malgré tout inaccessible.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Nouveauté de la v0.6.0 : vous pouvez désormais saisir directement dans les fenêtres détachées. Activez l'interrupteur principal dans les paramètres de la fenêtre.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Compris</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Ouvrir les paramètres de la fenêtre</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Vous pouvez ouvrir n'importe quel onglet de chat comme sa propre fenêtre. Cliquez sur l'icône de fenêtre en haut à droite ou faites un clic droit sur l'onglet. Nouveauté de la v0.6.1 : la saisie dans les fenêtres détachées est active par défaut (peut être désactivée dans Paramètres → Fenêtre).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Compris</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Ouvrir les paramètres</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat ne peut pas démarrer tant que Chat 2 est chargé.</value> <value>Hellion Chat ne peut pas démarrer tant que Chat 2 est chargé.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Désactivez Chat 2 dans /xlplugins, puis réactivez Hellion Chat.</value> <value>Désactivez Chat 2 dans /xlplugins, puis réactivez Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Général</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Langue, saisie, audio et performance.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Apparence</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Opacité de la fenêtre, polices, animations</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Thèmes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Choisissez un thème ou importez le vôtre</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Fenêtre</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Quand la fenêtre est visible et si elle peut être déplacée.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, aperçu, comportement des messages et emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Onglets</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Créez et configurez des onglets de chat personnalisés.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Base de données</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Stockage, migration, nettoyage des données historiques</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>À propos</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensions, version, informations du projet, traducteurs et journal des modifications.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Thèmes</value> <value>Thèmes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Conserver</value> <value>Conserver</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Confidentialité</value> <value>Confidentialité d'abord</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Ouvert</value> <value>Ouvert</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Données et confidentialité</value> <value>Données et confidentialité</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtre de confidentialité, conservation, nettoyage, exportation et statistiques de la base de données.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Thème</value> <value>Thème</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avancé (Maj+clic pour ouvrir)</value> <value>Avancé (Maj+clic pour ouvrir)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 a réorganisé le menu des paramètres et supprimé l'ancienne option « Remplacer le style » (remplacée par le système de thèmes de la 1.1.0). Vos autres paramètres restent inchangés. La transparence de la fenêtre a été migrée vers « Thème et mise en page ». Une sauvegarde de la configuration précédente est disponible dans pluginConfigs/HellionChat.json.pre-v16-backup, à côté du HellionChat.json actif.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Les intégrations de plugins permettent à HellionChat de fonctionner avec d'autres plugins Dalamud installés. Chaque intégration détecte automatiquement sa cible et se désactive silencieusement lorsque le plugin cible est absent.</value> <value>Les intégrations de plugins permettent à HellionChat de fonctionner avec d'autres plugins Dalamud installés. Chaque intégration détecte automatiquement sa cible et se désactive silencieusement lorsque le plugin cible est absent.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Saisie</value> <value>Saisie</value>
</data> </data>
@@ -997,7 +892,7 @@
<value>Saisie et aperçu</value> <value>Saisie et aperçu</value>
</data> </data>
<data name="Settings_Section_AutoTellTabs" xml:space="preserve"> <data name="Settings_Section_AutoTellTabs" xml:space="preserve">
<value>Onglets tell automatiques</value> <value>Onglets de message privé automatiques</value>
</data> </data>
<data name="Settings_Section_Emotes" xml:space="preserve"> <data name="Settings_Section_Emotes" xml:space="preserve">
<value>Emotes</value> <value>Emotes</value>
@@ -1069,10 +964,10 @@
<value>Journal des modifications</value> <value>Journal des modifications</value>
</data> </data>
<data name="Settings_Chat_NotifyFailedTell_Name" xml:space="preserve"> <data name="Settings_Chat_NotifyFailedTell_Name" xml:space="preserve">
<value>Notifier en cas de Tell échoué</value> <value>Notifier en cas de Message privé échoué</value>
</data> </data>
<data name="Settings_Chat_NotifyFailedTell_Description" xml:space="preserve"> <data name="Settings_Chat_NotifyFailedTell_Description" xml:space="preserve">
<value>Affiche une notification lorsqu'un Tell que vous avez envoyé n'a pas pu être remis (destinataire hors ligne, dans une instance ou vous bloquant).</value> <value>Affiche une notification lorsqu'un Message privé que vous avez envoyé n'a pas pu être remis (destinataire hors ligne, dans une instance ou vous bloquant).</value>
</data> </data>
<data name="Settings_Chat_NotifyPluginDisclosure_Name" xml:space="preserve"> <data name="Settings_Chat_NotifyPluginDisclosure_Name" xml:space="preserve">
<value>Avertir avant d'envoyer des symboles réservés au plugin</value> <value>Avertir avant d'envoyer des symboles réservés au plugin</value>
@@ -1138,12 +1033,329 @@
<value>Son Hellion</value> <value>Son Hellion</value>
</data> </data>
<data name="FailedTell_Notification_Generic" xml:space="preserve"> <data name="FailedTell_Notification_Generic" xml:space="preserve">
<value>Un Tell n'a pas pu être remis.</value> <value>Un Message privé n'a pas pu être remis.</value>
</data> </data>
<data name="FailedTell_Notification_Named" xml:space="preserve"> <data name="FailedTell_Notification_Named" xml:space="preserve">
<value>Le Tell à {0} n'a pas pu être remis.</value> <value>Le Message privé à {0} n'a pas pu être remis.</value>
</data> </data>
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Ce message contient des symboles réservés au plugin que d'autres joueurs pourraient voir comme des cases vides. Appuyez à nouveau sur Entrée pour envoyer quand même.</value> <value>Ce message contient des symboles réservés au plugin que d'autres joueurs pourraient voir comme des cases vides. Appuyez à nouveau sur Entrée pour envoyer quand même.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Insérer un symbole</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Paramètres</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Masquer le chat (Entrée pour le rouvrir)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Renvoyer cet onglet vers la fenêtre principale</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Une autre opération de base de données est en cours : {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>nettoyage de rétention</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>export</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>nettoyage</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>effacement de l'historique</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Le filtre de confidentialité est désactivé, donc chaque canal est enregistré et rien dans la base de données ne contredit vos réglages. Activez d'abord le filtre et choisissez des canaux.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Aucun canal n'est sélectionné, un nettoyage supprimerait donc tout l'historique. Choisissez les canaux à conserver, ou utilisez le bouton d'effacement si vous voulez vraiment tout supprimer.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0} messages sont enregistrés. Si vous voulez en garder une copie, exportez-les avant d'effacer.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Maj : lance le nettoyage de rétention immédiatement au lieu d'attendre le passage quotidien. Supprime les messages plus anciens que les limites ci-dessus.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>L'effacement de l'historique a échoué. Rien n'a été supprimé, voir /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Télémétrie</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Aucune télémétrie n'est collectée. Le plugin n'envoie rien vous concernant ni sur votre utilisation.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Traduction automatique</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Bloquer</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Aller au message le plus récent</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Insérer le marqueur de carte &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Insérer l'objet lié &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>désactivé</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Comportement</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Raccourcis clavier</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notifications</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Modes d'affichage</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historique</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Aide aux commandes</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Divulgation du plugin</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Mode de disposition</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacité</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Comportement de redimensionnement</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Ouverture automatique des messages privés</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Barre latérale</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marque</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Liens</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Intégrations</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Crédits</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licence</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Cliquez sur un bouton, puis appuyez sur la combinaison de touches. Échap efface.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Passer à l'onglet suivant</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Passer à l'onglet précédent</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Changer reconstruit l'atlas de polices, le chat est donc vide un instant.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Horloge 24 heures</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Afficher l'historique des sessions précédentes</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Désactivé, le journal démarre vide à chaque lancement et ne se remplit qu'avec les messages reçus ensuite.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Côté de l'aide aux commandes</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>De quel côté la liste de suggestions apparaît pendant la saisie.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Mode d'ouverture automatique des messages privés</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Où s'ouvre un message privé à son arrivée.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Basculer vers l'onglet à chaque message privé</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Sinon l'onglet s'ouvre en arrière-plan après le premier.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Barre latérale</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Onglets en haut</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Emplacement des onglets</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Où se place la liste d'onglets dans la fenêtre principale.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Afficher la barre de titre</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Afficher la barre de titre des fenêtres détachées</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Autoriser le déplacement</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Autoriser le redimensionnement</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Seuil de bascule automatique de la barre latérale</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>En dessous de cette largeur, la barre latérale se replie en onglets, en pixels.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Position de l'aperçu</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Afficher l'aperçu uniquement pendant la saisie</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Dépôt Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifeste du dépôt personnalisé</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Thème actif : {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Dupliquer et modifier</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Les thèmes intégrés ne peuvent pas être modifiés directement. Dupliquer crée une copie personnalisée que vous pouvez modifier et enregistrer.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Modifier le thème</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Modification : {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Surfaces</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Bordures</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Texte</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identité</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>État</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Enregistrer</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Annuler</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Réinitialiser à la source</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>La réinitialisation est indisponible pendant la modification d'une copie. Enregistrez ou annulez d'abord.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Enregistrez ou annulez vos modifications d'abord</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Personnalisés ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Dupliquer le thème actif</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importer un fichier de thème…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Chemin du fichier JSON (ou glissez-déposez dans le dossier)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exporter le thème</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Froids</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturels</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Classiques</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Rétro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (incluse)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Police du jeu</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Globale : {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Actif : {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Écrivez un message...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Dupliquer</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>aperçu</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>maintenance</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} onglet</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} onglets</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} MP</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} MP</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} msg</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k msg</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Champion» Aperçu</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>ouvert</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Biztonsági háló azokhoz a ChatType-okhoz, amelyeket jövőbeli FFXIV-frissítések adnak hozzá, és a plugin még nem ismer. Az alapértelmezés KI (adatminimalizálás). Kapcsold be, ha a jövőbeli csatornákat is teljes egészében naplózni szeretnéd.</value> <value>Biztonsági háló azokhoz a ChatType-okhoz, amelyeket jövőbeli FFXIV-frissítések adnak hozzá, és a plugin még nem ismer. Az alapértelmezés KI (adatminimalizálás). Kapcsold be, ha a jövőbeli csatornákat is teljes egészében naplózni szeretnéd.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Szűrő alkalmazása a meglévő adatbázisra</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Az adatvédelmi szűrő csak az új üzenetekre hat. Az alábbi takarítással utólag eltávolíthatod a már tárolt üzeneteket, amelyek nem illeszkednek a mentett fehérlistádhoz.</value> <value>Az adatvédelmi szűrő csak az új üzenetekre hat. Az alábbi takarítással utólag eltávolíthatod a már tárolt üzeneteket, amelyek nem illeszkednek a mentett fehérlistádhoz.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>A takarítás a MENTETT fehérlistádat (Plugin.Config) használja, nem a fenti nem mentett módosításokat. Először kattints a Mentés gombra, ha az aktuális változtatásokat szeretnéd alkalmazni.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>A kézi futtatás a MENTETT megőrzési szabályzatot használja, nem a fenti csúszkák értékeit. Először kattints a Mentés gombra, ha a futtatásban az aktuális változtatásokat szeretnéd alkalmazni.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Az előnézet elavult: a fehérlistád megváltozott az utolsó frissítés óta. Kattints a Frissítés gombra az újraszámításhoz.</value> <value>Az előnézet elavult: a fehérlistád megváltozott az utolsó frissítés óta. Kattints a Frissítés gombra az újraszámításhoz.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Megőrzés alkalmazása most</value> <value>Megőrzés alkalmazása most</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: A megőrzési takarítást azonnal futtatja a MENTETT szabályzat alapján. Előbb mentsd el a módosításaidat.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Megőrzési takarítás fut a háttérben…</value> <value>Megőrzési takarítás fut a háttérben…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Megjelenés</value> <value>Megjelenés</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Előző munkamenet betöltése indításkor</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Szűrők alkalmazása korábbi munkamenetek üzeneteire</value> <value>Szűrők alkalmazása korábbi munkamenetek üzeneteire</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Beállítások → Hellion Chat a finomhangoláshoz</value> <value>Beállítások → Hellion Chat a finomhangoláshoz</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Export (GDPR Art. 15 — Hozzáférési jog)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Tárolt üzenetek exportálása Markdown, JSON vagy CSV formátumban. Ezzel teljesíthetsz egy hozzáférési kérelmet attól a személytől, akinek az üzeneteit tároltad, vagy magaddal viheted a saját előzményeidet.</value> <value>Tárolt üzenetek exportálása Markdown, JSON vagy CSV formátumban. Ezzel teljesíthetsz egy hozzáférési kérelmet attól a személytől, akinek az üzeneteit tároltad, vagy magaddal viheted a saját előzményeidet.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 közösségi fordítók (upstream)</value> <value>Chat 2 közösségi fordítók (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktív tellek</value> <value>Aktív tellek</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Rögzített: túléli az újrabejelentkezést.</value> <value>Rögzített: túléli az újrabejelentkezést.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Megjegyzés: Ha az XIV Messenger vagy egy hasonló plugin elnyomja a telleket, kapcsold ki ott a „Suppress DMs" opciót, hogy a Hellion Chat fogadhassa a telleket és megnyithassa az auto-füleket.</value> <value>Megjegyzés: Ha az XIV Messenger vagy egy hasonló plugin elnyomja a telleket, kapcsold ki ott a „Suppress DMs" opciót, hogy a Hellion Chat fogadhassa a telleket és megnyithassa az auto-füleket.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell-előzmények az auto-fülekben</value> <value>Tell-előzmények az auto-fülekben</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Csak akkor lép életbe, ha az auto-tell-fülek be vannak kapcsolva a Chat fülön.</value> <value>Csak akkor lép életbe, ha az auto-tell-fülek be vannak kapcsolva a Chat fülön.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Beállítások átstrukturálva</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>A Hellion Chat 0.5.0 tematikus fülekbe rendezte a beállításokat. A chat-adatbázisod és az üzenettörténeted változatlan maradt. A beállítások visszaálltak az alapértelmezettekre. Ha újra ki szeretnéd választani az adatvédelmi profilodat, a Varázsló újra megnyitása gomb az Adatvédelem fülön található. Az előző konfiguráció biztonsági másolata HellionChat.json.pre-v10-backup néven található az aktív konfigurációs fájl mellett.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Általános</value> <value>Általános</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Névjegy</value> <value>Névjegy</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Időbélyegek</value> <value>Időbélyegek</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Ablakkeret</value> <value>Ablakkeret</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Szimbólumválasztó gomb megjelenítése a chat-beviteli mező mellett</value> <value>Szimbólumválasztó gomb megjelenítése a chat-beviteli mező mellett</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Egy kis gombot helyez a csatornajelző bal oldalára, amely egy FFXIV-ikonokat és válogatott szimbólumokat tartalmazó felugró ablakot nyit meg. Kapcsold ki, ha karcsúbb beviteli sort szeretnél.</value> <value>Egy kis gombot helyez a csatornajelző bal oldalára, amely egy FFXIV-ikonokat és válogatott szimbólumokat tartalmazó felugró ablakot nyit meg. Kapcsold ki, ha karcsúbb beviteli sort szeretnél.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Tárolás</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Áttekintés</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Karbantartás</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Rendszer</value> <value>Rendszer</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Ha több linkshellt használsz, a karbantartó azt javasolja, hogy minden shellnek legyen külön füle az áttekinthetőség érdekében. Másold le a fület, és minden másolatban szűkítsd a csatornaválasztást.</value> <value>Ha több linkshellt használsz, a karbantartó azt javasolja, hogy minden shellnek legyen külön füle az áttekinthetőség érdekében. Másold le a fület, és minden másolatban szűkítsd a csatornaválasztást.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Fül ikonja</value> <value>Fül ikonja</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>A chat-ablakot és az összes aktív pop-outot az elsődleges monitor bal felső sarkába helyezi vissza. Akkor hasznos, ha egy ablak a megjelenítési elrendezés megváltozása után (monitor lecsatlakoztatva, felbontás megváltozott) a látható területen kívülre kerül. A plugin munkamenetenként egyszer automatikus határellenőrzést is végez; ez a gomb a kézi menekülési útvonal, ha valami még így is elérhetetlenné válik.</value> <value>A chat-ablakot és az összes aktív pop-outot az elsődleges monitor bal felső sarkába helyezi vissza. Akkor hasznos, ha egy ablak a megjelenítési elrendezés megváltozása után (monitor lecsatlakoztatva, felbontás megváltozott) a látható területen kívülre kerül. A plugin munkamenetenként egyszer automatikus határellenőrzést is végez; ez a gomb a kézi menekülési útvonal, ha valami még így is elérhetetlenné válik.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Újdonság a v0.6.0-ban: most már közvetlenül a pop-outban is gépelhetsz. Kapcsold be a főkapcsolót az Ablak beállításokban.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Értettem</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Ablakbeállítások megnyitása</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Bármely chat-fület megnyithatod saját ablakként. Kattints az ablak ikonra a jobb felső sarokban, vagy jobb klikk a fülön. Újdonság a v0.6.1-ben: a pop-out bevitel alapértelmezés szerint aktív (kikapcsolható a Beállítások → Ablak alatt).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Értettem</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Beállítások megnyitása</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>A Hellion Chat nem indítható el, amíg a Chat 2 be van töltve.</value> <value>A Hellion Chat nem indítható el, amíg a Chat 2 be van töltve.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Tiltsd le a Chat 2-t a /xlplugins-ban, majd engedélyezd újra a Hellion Chat-et.</value> <value>Tiltsd le a Chat 2-t a /xlplugins-ban, majd engedélyezd újra a Hellion Chat-et.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Általános</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Nyelv, bevitel, hang és teljesítmény.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Megjelenés</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Ablak átlátszatlansága, betűtípusok, animáció</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Témák</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Témát választhatsz vagy importálhatod a sajátodat</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Ablak</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Mikor látható az ablak, és lehet-e mozgatni.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tellek, előnézet, üzenetviselkedés és emote-ok.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Fülek</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Egyéni chat-fülek létrehozása és konfigurálása.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Adatbázis</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Tárolás, migráció, régi adatok takarítása</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Névjegy</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Bővítmények, verzió, projektinformációk, fordítók és changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Témák</value> <value>Témák</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Megtartás</value> <value>Megtartás</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Adatvédelem elöl</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Nyitott</value> <value>Nyitott</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Adatok és adatvédelem</value> <value>Adatok és adatvédelem</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Adatvédelmi szűrő, megőrzés, takarítás, export és adatbázis-statisztikák.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Speciális (Shift+kattintás a megnyitáshoz)</value> <value>Speciális (Shift+kattintás a megnyitáshoz)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>A Hellion Chat 1.2.1 átrendezte a beállítások menüt és eltávolította a régi „Stílus felülbírálása" opciót (amelyet az 1.1.0-ban bevezetett témarendszer váltott fel). A többi beállításod változatlan maradt. Az ablak átlátszósága átkerült a „Theme &amp; Layout" menübe. Az előző konfiguráció biztonsági másolata a pluginConfigs/HellionChat.json.pre-v16-backup fájlban található az aktív HellionChat.json mellett.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>A plugin-integrációk lehetővé teszik, hogy a HellionChat más telepített Dalamud-pluginekkel együttműködjön. Minden integráció automatikusan felismeri a célpluginét, és csendesen letiltja magát, ha a célplugin hiányzik.</value> <value>A plugin-integrációk lehetővé teszik, hogy a HellionChat más telepített Dalamud-pluginekkel együttműködjön. Minden integráció automatikusan felismeri a célpluginét, és csendesen letiltja magát, ha a célplugin hiányzik.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Bevitel</value> <value>Bevitel</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Ez az üzenet plugin-kizárólagos szimbólumokat tartalmaz, amelyeket más játékosok üres négyzetként láthatnak. Nyomd meg ismét az Entert a küldéshez.</value> <value>Ez az üzenet plugin-kizárólagos szimbólumokat tartalmaz, amelyeket más játékosok üres négyzetként láthatnak. Nyomd meg ismét az Entert a küldéshez.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Szimbólum beszúrása</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Beállítások</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Csevegés elrejtése (Enter visszahozza)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Lap visszahelyezése a főablakba</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Már fut egy másik adatbázisművelet: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>megőrzési takarítás</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>exportálás</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>takarítás</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>az előzmények törlése</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Az adatvédelmi szűrő ki van kapcsolva, így minden csatorna mentésre kerül, és semmi sem mond ellent a beállításaidnak az adatbázisban. Előbb kapcsold be a szűrőt, és válassz csatornákat.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Nincs kiválasztva csatorna, így a takarítás a teljes előzményt törölné. Válaszd ki a megtartandó csatornákat, vagy használd a törlés gombot, ha tényleg mindent el akarsz távolítani.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0} üzenet van elmentve. Ha meg akarsz tartani egy másolatot, törlés előtt exportáld őket.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: azonnal lefuttatja a megőrzési takarítást, nem várja meg a napi futást. Törli a fenti korlátoknál régebbi üzeneteket.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Az előzmények törlése nem sikerült. Semmi sem lett eltávolítva, lásd /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetria</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Nem gyűjtünk telemetriát. A bővítmény semmit sem küld rólad vagy a használatodról sehová.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Automatikus fordítás</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Letiltás</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Ugrás a legutóbbi üzenetre</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Térképjelölő beszúrása &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Hivatkozott tárgy beszúrása &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>kikapcsolva</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Viselkedés</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Gyorsbillentyűk</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Értesítések</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Megjelenítési módok</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Előzmények</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Parancssúgó</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Bővítmény jelzése</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Elrendezési mód</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Átlátszatlanság</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Átméretezés</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Tell automatikus megnyitása</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Oldalsáv</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Márka</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Hivatkozások</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrációk</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Közreműködők</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licenc</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Kattints egy gombra, majd nyomd le a billentyűkombinációt. Az Esc törli.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Váltás a következő fülre</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Váltás az előző fülre</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>A váltás újraépíti a betűatlaszt, ezért a csevegés egy pillanatra kiürül.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24 órás óra</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Korábbi munkamenetek előzményeinek megjelenítése</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Kikapcsolva a napló minden indításkor üresen kezd, és csak az azóta érkezett üzenetekkel telik meg.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Parancssúgó oldala</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Melyik oldalon jelenik meg a parancslista gépelés közben.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Tell automatikus megnyitásának módja</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Hol nyílik meg egy tell, amikor megérkezik.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Váltás a fülre minden tellnél</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Egyébként a fül az első után a háttérben nyílik meg.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Oldalsáv</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Felső fülek</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Fülek elhelyezése</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Hol helyezkedik el a füllista a főablakban.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Címsor megjelenítése</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Címsor a pop-out ablakoknál</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Mozgatás engedélyezése</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Átméretezés engedélyezése</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Oldalsáv automatikus váltásának küszöbe</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>E szélesség alatt az oldalsáv felső fülekké alakul, képpontban.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Előnézet helye</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Előnézet csak gépelés közben</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea-tároló</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Egyéni tároló manifesztje</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktív téma: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Elágaztatás és szerkesztés</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>A beépített témák nem szerkeszthetők közvetlenül. Az elágaztatás egyéni másolatot hoz létre, amit szerkeszthetsz és menthetsz.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Téma szerkesztése</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Szerkesztés alatt: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Felületek</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Szegélyek</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Szöveg</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identitás</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Állapot</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Mentés</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Mégse</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Visszaállítás az eredetire</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Visszaállítás nem érhető el elágazás szerkesztése közben. Előbb ments vagy szakítsd meg.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Előbb mentsd vagy vesd el a módosításaidat</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Egyéni ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Aktív téma elágaztatása</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Témafájl importálása…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>A JSON-fájl útvonala (vagy húzd a mappába)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Téma exportálása</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Hűvös</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Természetes</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klasszikus</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retró</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (mellékelt)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>A játék betűtípusa</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Globális: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktív: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Írj egy üzenetet...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Másolat</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>előnézet</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>karbantartás</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} lap</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} lap</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} suttogás</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} suttogás</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} üz.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k üz.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Bajnok» Előnézet</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>nyitva</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Rete di sicurezza per i ChatType aggiunti da futuri aggiornamenti di FFXIV che il plugin non conosce ancora. Il predefinito è DISATTIVATO (minimizzazione dei dati). Attiva se vuoi che anche i canali futuri vengano registrati completamente.</value> <value>Rete di sicurezza per i ChatType aggiunti da futuri aggiornamenti di FFXIV che il plugin non conosce ancora. Il predefinito è DISATTIVATO (minimizzazione dei dati). Attiva se vuoi che anche i canali futuri vengano registrati completamente.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Applica filtro al database esistente</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Il filtro privacy agisce solo sui nuovi messaggi. La pulizia qui sotto consente di rimuovere retroattivamente i messaggi già salvati che non corrispondono alla whitelist salvata.</value> <value>Il filtro privacy agisce solo sui nuovi messaggi. La pulizia qui sotto consente di rimuovere retroattivamente i messaggi già salvati che non corrispondono alla whitelist salvata.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>La pulizia usa la tua whitelist SALVATA (Plugin.Config), non le modifiche non salvate sopra. Clicca Salva prima se vuoi che le modifiche attuali vengano applicate.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>L'esecuzione manuale usa la tua policy di conservazione SALVATA, non i valori degli slider sopra. Clicca Salva prima se vuoi che l'esecuzione applichi le modifiche attuali.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Anteprima non aggiornata: la whitelist è cambiata dall'ultimo aggiornamento. Clicca Aggiorna per ricalcolare.</value> <value>Anteprima non aggiornata: la whitelist è cambiata dall'ultimo aggiornamento. Clicca Aggiorna per ricalcolare.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Applica conservazione ora</value> <value>Applica conservazione ora</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Esegue immediatamente la pulizia della conservazione usando la policy SALVATA. Salva prima le modifiche.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Pulizia della conservazione in esecuzione in background…</value> <value>Pulizia della conservazione in esecuzione in background…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Aspetto</value> <value>Aspetto</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Carica la sessione precedente all'avvio</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Applica filtri ai messaggi delle sessioni precedenti</value> <value>Applica filtri ai messaggi delle sessioni precedenti</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Impostazioni → Hellion Chat per regolazioni successive</value> <value>Impostazioni → Hellion Chat per regolazioni successive</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Esporta (GDPR Art. 15 — Diritto di accesso)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Esporta i messaggi salvati in Markdown, JSON o CSV. Puoi così soddisfare una richiesta di accesso da parte di una persona i cui messaggi hai conservato, o portare con te la tua cronologia.</value> <value>Esporta i messaggi salvati in Markdown, JSON o CSV. Puoi così soddisfare una richiesta di accesso da parte di una persona i cui messaggi hai conservato, o portare con te la tua cronologia.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Traduttori della community di Chat 2 (upstream)</value> <value>Traduttori della community di Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Tell attivi</value> <value>Tell attivi</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fisso: sopravvive al relog.</value> <value>Fisso: sopravvive al relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Nota: se XIV Messenger o un plugin simile sopprime i tell, disattiva lì l'opzione "Suppress DMs" in modo che Hellion Chat possa ricevere i tell e aprire i tab automatici.</value> <value>Nota: se XIV Messenger o un plugin simile sopprime i tell, disattiva lì l'opzione "Suppress DMs" in modo che Hellion Chat possa ricevere i tell e aprire i tab automatici.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Cronologia tell nei tab automatici</value> <value>Cronologia tell nei tab automatici</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Ha effetto solo quando i tab tell automatici sono attivati nel tab Chat.</value> <value>Ha effetto solo quando i tab tell automatici sono attivati nel tab Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Impostazioni ristrutturate</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 ha ristrutturato le impostazioni in tab tematici. Il database della chat e la cronologia dei messaggi rimangono invariati. Le impostazioni sono state ripristinate ai valori predefiniti. Se vuoi riselezionare il profilo privacy, il pulsante Riapri si trova nel tab Privacy. Un backup della configurazione precedente si trova in HellionChat.json.pre-v10-backup accanto al file di configurazione attivo.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Generale</value> <value>Generale</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Info</value> <value>Info</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Timestamp</value> <value>Timestamp</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Cornice finestra</value> <value>Cornice finestra</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Mostra pulsante selettore simboli accanto all'input della chat</value> <value>Mostra pulsante selettore simboli accanto all'input della chat</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Aggiunge un piccolo pulsante a sinistra dell'indicatore del canale che apre un popup con le icone FFXIV e un elenco di simboli selezionati. Disattiva se preferisci una barra di input più essenziale.</value> <value>Aggiunge un piccolo pulsante a sinistra dell'indicatore del canale che apre un popup con le icone FFXIV e un elenco di simboli selezionati. Disattiva se preferisci una barra di input più essenziale.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Archiviazione</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Panoramica</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Manutenzione</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value> <value>Sistema</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Se usi più linkshell, il maintainer consiglia un tab per shell per una panoramica più ordinata. Duplica il tab e limita la selezione dei canali in ogni copia.</value> <value>Se usi più linkshell, il maintainer consiglia un tab per shell per una panoramica più ordinata. Duplica il tab e limita la selezione dei canali in ogni copia.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Icona tab</value> <value>Icona tab</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Sposta la finestra della chat e tutti i pop-out attivi nell'angolo in alto a sinistra del monitor principale. Utile quando una finestra è finita fuori dall'area visibile dopo un cambio di layout del display (monitor scollegato, risoluzione cambiata). Il plugin esegue anche un controllo automatico dei limiti una volta per sessione; questo pulsante è la via di fuga manuale se qualcosa finisce comunque irraggiungibile.</value> <value>Sposta la finestra della chat e tutti i pop-out attivi nell'angolo in alto a sinistra del monitor principale. Utile quando una finestra è finita fuori dall'area visibile dopo un cambio di layout del display (monitor scollegato, risoluzione cambiata). Il plugin esegue anche un controllo automatico dei limiti una volta per sessione; questo pulsante è la via di fuga manuale se qualcosa finisce comunque irraggiungibile.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Novità in v0.6.0: ora puoi digitare direttamente nei pop-out. Attiva l'interruttore principale nelle impostazioni Finestra.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Capito</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Apri impostazioni finestra</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Puoi aprire qualsiasi tab della chat come finestra separata. Clicca l'icona finestra in alto a destra o fai clic destro sul tab. Novità in v0.6.1: l'input nei pop-out è attivo per impostazione predefinita (disattivabile in Impostazioni → Finestra).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Capito</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Apri impostazioni</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat non può avviarsi mentre Chat 2 è caricato.</value> <value>Hellion Chat non può avviarsi mentre Chat 2 è caricato.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Disattiva Chat 2 in /xlplugins, poi riattiva Hellion Chat.</value> <value>Disattiva Chat 2 in /xlplugins, poi riattiva Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Generale</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Lingua, input, audio e prestazioni.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Aspetto</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Opacità finestra, font, animazioni</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Scegli un theme o importa il tuo</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Finestra</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Quando la finestra è visibile e se può essere spostata.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tell, anteprima, comportamento messaggi ed emote.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Tab</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Crea e configura tab della chat personalizzati.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Database</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Archiviazione, migrazione, pulizia legacy</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Informazioni</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Estensioni, versione, informazioni sul progetto, traduttori e changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Mantieni</value> <value>Mantieni</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Privacy prima</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Aperto</value> <value>Aperto</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Dati e privacy</value> <value>Dati e privacy</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtro privacy, conservazione, pulizia, esportazione e statistiche database.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avanzate (Shift+clic per aprire)</value> <value>Avanzate (Shift+clic per aprire)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 ha riorganizzato il menu delle impostazioni e rimosso la vecchia opzione "Override style" (sostituita dal sistema theme dalla 1.1.0). Le impostazioni rimanenti sono invariate. La trasparenza della finestra è stata migrata in "Theme &amp; Layout". Un backup della configurazione precedente si trova in pluginConfigs/HellionChat.json.pre-v16-backup accanto al file HellionChat.json attivo.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Le integrazioni con plugin consentono a HellionChat di lavorare insieme ad altri plugin Dalamud installati. Ogni integrazione rileva automaticamente il suo target e si disattiva silenziosamente quando il plugin target è assente.</value> <value>Le integrazioni con plugin consentono a HellionChat di lavorare insieme ad altri plugin Dalamud installati. Ogni integrazione rileva automaticamente il suo target e si disattiva silenziosamente quando il plugin target è assente.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Inserimento</value> <value>Inserimento</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Questo messaggio contiene simboli esclusivi del plugin che altri giocatori potrebbero vedere come caselle vuote. Premi Invio di nuovo per inviare comunque.</value> <value>Questo messaggio contiene simboli esclusivi del plugin che altri giocatori potrebbero vedere come caselle vuote. Premi Invio di nuovo per inviare comunque.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Inserisci simbolo</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Impostazioni</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Nascondi la chat (Invio per riaprirla)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Riporta questa scheda nella finestra principale</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>È già in corso un'altra operazione sul database: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>pulizia di conservazione</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>esportazione</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>pulizia</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>cancellazione della cronologia</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Il filtro privacy è disattivato, quindi ogni canale viene salvato e nulla nel database contraddice le tue impostazioni. Attiva prima il filtro e scegli i canali.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Nessun canale è selezionato, quindi una pulizia cancellerebbe l'intera cronologia. Scegli i canali da conservare, oppure usa il pulsante di cancellazione se vuoi davvero eliminare tutto.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Ci sono {0:N0} messaggi salvati. Se vuoi conservarne una copia, esportali prima di cancellare.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: esegue subito la pulizia di conservazione invece di aspettare il passaggio giornaliero. Cancella i messaggi più vecchi dei limiti sopra.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>La cancellazione della cronologia non è riuscita. Non è stato rimosso nulla, vedi /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetria</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Non viene raccolta alcuna telemetria. Il plugin non invia nulla su di te o sul tuo utilizzo.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Traduzione automatica</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Blocca</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Vai al messaggio più recente</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Inserisci il segnalino mappa &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Inserisci l'oggetto collegato &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>disattivato</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Comportamento</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Scorciatoie</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notifiche</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Modalità di visualizzazione</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Cronologia</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Aiuto comandi</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Avviso plugin</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Modalità di layout</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacità</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Ridimensionamento</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Apertura automatica dei tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Barra laterale</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marchio</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Collegamenti</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrazioni</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Riconoscimenti</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licenza</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Fai clic su un pulsante, poi premi la combinazione di tasti. Esc cancella.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Passa alla scheda successiva</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Passa alla scheda precedente</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Il cambio ricostruisce l'atlante dei caratteri, quindi la chat resta vuota per un attimo.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Orologio 24 ore</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Mostra la cronologia delle sessioni precedenti</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Disattivato, il registro parte vuoto a ogni avvio del gioco e si riempie solo con i messaggi ricevuti da quel momento.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Lato dell'aiuto comandi</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Su quale lato compare l'elenco dei suggerimenti mentre scrivi.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Modalità di apertura automatica dei tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Dove si apre un tell quando arriva.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Passa alla scheda a ogni tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Altrimenti la scheda si apre in secondo piano dopo il primo.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Barra laterale</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Schede in alto</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Posizione delle schede</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Dove si trova l'elenco delle schede nella finestra principale.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Mostra la barra del titolo</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Barra del titolo nei pop-out</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Consenti lo spostamento</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Consenti il ridimensionamento</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Soglia di passaggio barra laterale</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Sotto questa larghezza la barra laterale si ripiega in schede in alto, in pixel.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Posizione dell'anteprima</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Mostra l'anteprima solo durante la digitazione</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Repository Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest del repository personalizzato</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Tema attivo: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Duplica e modifica</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>I temi integrati non si possono modificare direttamente. La duplicazione crea una copia personalizzata che puoi modificare e salvare.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Modifica tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Modifica di: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Superfici</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Bordi</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Testo</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identità</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Stato</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Salva</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Annulla</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Ripristina all'originale</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Il ripristino non è disponibile durante la modifica di una copia. Salva o annulla prima.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Salva o annulla prima le tue modifiche</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Personalizzati ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Duplica il tema attivo</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importa file tema…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Percorso del file JSON (o trascinalo nella cartella)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Esporta tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Freddi</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturali</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Classici</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (inclusa)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Carattere del gioco</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Globale: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Attivo: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Scrivi un messaggio...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplica</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>anteprima</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>manutenzione</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} scheda</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} schede</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} sussurro</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} sussurri</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} msg</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k msg</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Campione» Anteprima</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>aperto</value>
</data>
</root>
+337 -125
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>将来の FFXIVパッチで追加されるプラグイン未対応の ChatType に対するセーフティネットです。デフォルトはオフ(データ最小化)。将来のチャンネルも完全に記録したい場合は有効にしてください。</value> <value>将来の FFXIVパッチで追加されるプラグイン未対応の ChatType に対するセーフティネットです。デフォルトはオフ(データ最小化)。将来のチャンネルも完全に記録したい場合は有効にしてください。</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>既存データベースにフィルターを適用する</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>プライバシーフィルターは新しいメッセージにのみ適用されます。以下のクリーンアップを使用すると、保存済みのホワイトリストに一致しない既存のメッセージをさかのぼって削除できます。</value> <value>プライバシーフィルターは新しいメッセージにのみ適用されます。以下のクリーンアップを使用すると、保存済みのホワイトリストに一致しない既存のメッセージをさかのぼって削除できます。</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>クリーンアップには保存済みのホワイトリスト(Plugin.Config)が使用されます。上部の未保存の変更は反映されません。現在の変更を適用したい場合は、先に保存してください。</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>手動実行には保存済みの保持ポリシーが使用されます。上部のスライダーの値は反映されません。現在の変更を適用したい場合は、先に保存してください。</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>プレビューが古くなっています。前回の更新以降にホワイトリストが変更されました。「更新」をクリックして再計算してください。</value> <value>プレビューが古くなっています。前回の更新以降にホワイトリストが変更されました。「更新」をクリックして再計算してください。</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>保持期間をすぐに適用する</value> <value>保持期間をすぐに適用する</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: 保存済みのポリシーを使って保持期間のクリーンアップを即時実行します。先に変更を保存してください。</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>バックグラウンドで保持期間のクリーンアップを実行中…</value> <value>バックグラウンドで保持期間のクリーンアップを実行中…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>表示</value> <value>表示</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>起動時に前回のセッションを読み込む</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>以前のセッションのメッセージにもフィルターを適用する</value> <value>以前のセッションのメッセージにもフィルターを適用する</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>設定 → Hellion Chat で後から細かく調整できます</value> <value>設定 → Hellion Chat で後から細かく調整できます</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>エクスポート(GDPR Art. 15 — アクセス権)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>保存済みメッセージを Markdown、JSON、CSV 形式でエクスポートします。メッセージを保存した相手からのアクセス請求への対応や、自分の履歴を持ち出す際に活用できます。</value> <value>保存済みメッセージを Markdown、JSON、CSV 形式でエクスポートします。メッセージを保存した相手からのアクセス請求への対応や、自分の履歴を持ち出す際に活用できます。</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 コミュニティ翻訳者(アップストリーム)</value> <value>Chat 2 コミュニティ翻訳者(アップストリーム)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>アクティブなテル</value> <value>アクティブなテル</value>
</data> </data>
@@ -504,9 +486,9 @@
<value>ピン留め済み: 再ログイン後も残ります。</value> <value>ピン留め済み: 再ログイン後も残ります。</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>オートテルタブ</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve">
<value>/tell ごとに会話相手ごとのタブを自動で開く</value> <value>/tell ごとに会話相手ごとのタブを自動で開く</value>
@@ -545,7 +527,7 @@
<value>注意: XIV Messenger や類似のプラグインがテルを抑制している場合、そちらの「Suppress DMs」オプションを無効にしてください。無効にすることで Hellion Chat がテルを受信し、自動タブを開けるようになります。</value> <value>注意: XIV Messenger や類似のプラグインがテルを抑制している場合、そちらの「Suppress DMs」オプションを無効にしてください。無効にすることで Hellion Chat がテルを受信し、自動タブを開けるようになります。</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>自動タブのテル履歴</value> <value>自動タブのテル履歴</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>チャットタブで自動テルタブが有効になっている場合にのみ適用されます。</value> <value>チャットタブで自動テルタブが有効になっている場合にのみ適用されます。</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>設定が再構成されました</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 で設定がテーマ別タブに再構成されました。チャットデータベースとメッセージ履歴はそのまま保持されます。設定はデフォルト値にリセットされました。プライバシープロファイルを再選択したい場合は、プライバシータブの「再表示」ボタンをご利用ください。以前の設定のバックアップは、アクティブな設定ファイルの隣に HellionChat.json.pre-v10-backup として保存されています。</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>一般</value> <value>一般</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>情報</value> <value>情報</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>テーマ</value> <value>テーマ</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>タイムスタンプ</value> <value>タイムスタンプ</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>ウィンドウフレーム</value> <value>ウィンドウフレーム</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>チャット入力欄の隣にシンボルピッカーボタンを表示する</value> <value>チャット入力欄の隣にシンボルピッカーボタンを表示する</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>チャンネルインジケーターの左に小さなボタンを追加します。クリックすると FFXIV アイコンとシンボル一覧のポップアップが開きます。入力バーをシンプルにしたい場合は無効にしてください。</value> <value>チャンネルインジケーターの左に小さなボタンを追加します。クリックすると FFXIV アイコンとシンボル一覧のポップアップが開きます。入力バーをシンプルにしたい場合は無効にしてください。</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>ストレージ</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>概要</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>メンテナンス</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>システム</value> <value>システム</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>複数のリンクシェルを使用している場合、メンテナーはより整理された見通しのために各シェルに専用タブを作成することを推奨しています。タブを複製して各コピーのチャンネル選択を絞り込んでください。</value> <value>複数のリンクシェルを使用している場合、メンテナーはより整理された見通しのために各シェルに専用タブを作成することを推奨しています。タブを複製して各コピーのチャンネル選択を絞り込んでください。</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>タブアイコン</value> <value>タブアイコン</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>チャットウィンドウとすべてのアクティブなポップアウトをプライマリモニターの左上隅に戻します。ディスプレイレイアウトの変更(モニターの切断、解像度変更)後にウィンドウが表示領域外に移動した場合に便利です。プラグインはセッションごとに自動で境界チェックを行いますが、それでも届かない場合の手動での脱出手段としてこのボタンをご利用ください。</value> <value>チャットウィンドウとすべてのアクティブなポップアウトをプライマリモニターの左上隅に戻します。ディスプレイレイアウトの変更(モニターの切断、解像度変更)後にウィンドウが表示領域外に移動した場合に便利です。プラグインはセッションごとに自動で境界チェックを行いますが、それでも届かない場合の手動での脱出手段としてこのボタンをご利用ください。</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>v0.6.0 の新機能: ポップアウト内で直接入力できるようになりました。ウィンドウ設定のマスタースイッチを有効にしてください。</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>了解</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>ウィンドウ設定を開く</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>どのチャットタブも独立したウィンドウとして開けます。右上のウィンドウアイコンをクリックするか、タブを右クリックしてください。v0.6.1 の新機能: ポップアウト入力がデフォルトで有効になりました(設定 → ウィンドウで無効にできます)。</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>了解</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>設定を開く</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Chat 2 が読み込まれている間は Hellion Chat を起動できません。</value> <value>Chat 2 が読み込まれている間は Hellion Chat を起動できません。</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>/xlplugins で Chat 2 を無効にしてから、Hellion Chat を再度有効にしてください。</value> <value>/xlplugins で Chat 2 を無効にしてから、Hellion Chat を再度有効にしてください。</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>一般</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>言語、入力、オーディオ、パフォーマンス。</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>外観</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>ウィンドウの不透明度、フォント、モーション</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>テーマ</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>テーマを選択するか、独自のテーマをインポートする</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>ウィンドウ</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>ウィンドウの表示タイミングと移動可否。</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>チャット</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>テル、プレビュー、メッセージの動作、エモート。</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>タブ</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>カスタムチャットタブの作成と設定。</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>データベース</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>ストレージ、移行、レガシーのクリーンアップ</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>概要</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>拡張機能、バージョン、プロジェクト情報、翻訳者、Changelog。</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>テーマ</value> <value>テーマ</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>保持</value> <value>保持</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>プライバシー優先</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>オープン</value> <value>オープン</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>データとプライバシー</value> <value>データとプライバシー</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>プライバシーフィルター、保持期間、クリーンアップ、エクスポート、データベース統計。</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>テーマ</value> <value>テーマ</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>詳細設定(Shift+クリックで開く)</value> <value>詳細設定(Shift+クリックで開く)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 で設定メニューが整理され、古い「スタイルを上書き」オプションが削除されました(1.1.0 のテーマシステムに置き換えられました)。その他の設定は変更されていません。ウィンドウの透明度は「テーマ &amp; レイアウト」に移行されました。以前の設定のバックアップは、アクティブな HellionChat.json の隣に pluginConfigs/HellionChat.json.pre-v16-backup として保存されています。</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>プラグイン連携により、HellionChat は他のインストール済み Dalamud プラグインと連携できます。各連携は対象プラグインを自動で検出し、対象プラグインがない場合は静かに無効化されます。</value> <value>プラグイン連携により、HellionChat は他のインストール済み Dalamud プラグインと連携できます。各連携は対象プラグインを自動で検出し、対象プラグインがない場合は静かに無効化されます。</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>入力</value> <value>入力</value>
</data> </data>
@@ -997,7 +892,7 @@
<value>入力とプレビュー</value> <value>入力とプレビュー</value>
</data> </data>
<data name="Settings_Section_AutoTellTabs" xml:space="preserve"> <data name="Settings_Section_AutoTellTabs" xml:space="preserve">
<value>オートTellタブ</value> <value>オートテルタブ</value>
</data> </data>
<data name="Settings_Section_Emotes" xml:space="preserve"> <data name="Settings_Section_Emotes" xml:space="preserve">
<value>エモート</value> <value>エモート</value>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>このメッセージにはプラグイン専用のシンボルが含まれており、他のプレイヤーには空白の箱として表示される可能性があります。Enterを再度押して送信します。</value> <value>このメッセージにはプラグイン専用のシンボルが含まれており、他のプレイヤーには空白の箱として表示される可能性があります。Enterを再度押して送信します。</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>記号を挿入</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>設定</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>チャットを非表示(Enterで再表示)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>このタブをメインウィンドウに戻す</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>別のデータベース処理を実行中です: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>保存期間の整理</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>エクスポート</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>クリーンアップ</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>履歴の削除</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>プライバシーフィルターがオフのため、すべてのチャンネルが保存されており、設定と矛盾するデータはありません。まずフィルターをオンにしてチャンネルを選んでください。</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>チャンネルが一つも選ばれていないため、クリーンアップは履歴をすべて削除します。残したいチャンネルを選ぶか、本当にすべて消したい場合は削除ボタンを使ってください。</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0} 件のメッセージが保存されています。控えを残したい場合は、削除する前にエクスポートしてください。</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: 毎日の処理を待たずに保存期間の整理をすぐ実行します。上の期限より古いメッセージを削除します。</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>履歴の削除に失敗しました。何も削除されていません。/xllog を確認してください。</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>テレメトリ</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>テレメトリは収集していません。あなたや利用状況に関する情報をどこにも送信しません。</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>定型文</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>ブロック</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>最新のメッセージへ移動</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>マップフラグを挿入 &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>リンクしたアイテムを挿入 &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>オフ</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>動作</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>キー設定</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>通知</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>表示モード</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>履歴</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>コマンドヘルプ</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>プラグインの明示</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>レイアウトモード</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>不透明度</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>サイズ変更の動作</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>テルの自動オープン</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>サイドバー</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>ブランド</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>リンク</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>連携</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>クレジット</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>ライセンス</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>ボタンをクリックしてからキーの組み合わせを押してください。Esc で解除します。</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>次のチャットタブへ</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>前のチャットタブへ</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>切り替えるとフォントアトラスが再構築されるため、チャットが一瞬空になります。</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24時間表示</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>以前のセッションの履歴を表示</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>オフの場合、ログはゲーム起動のたびに空から始まり、それ以降に受信したメッセージだけが入ります。</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>コマンドヘルプの表示位置</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>入力中にコマンド候補をどちら側に表示するか。</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>テル自動オープンの方式</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>テルが届いたときにどこで開くか。</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>テルのたびにタブへ切り替える</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>そうでない場合、最初の 1 通のあとタブは背面で開きます。</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>サイドバー</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>上部タブ</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>タブの配置</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>メインウィンドウでタブ一覧をどこに置くか。</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>タイトルバーを表示</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>ポップアウトのタイトルバーを表示</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>移動を許可</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>サイズ変更を許可</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>サイドバー自動切り替えのしきい値</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>この幅を下回るとサイドバーが上部タブに折りたたまれます(ピクセル)。</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>プレビューの位置</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>入力中のみプレビューを表示</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea リポジトリ</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>カスタムリポジトリのマニフェスト</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>現在のテーマ: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>複製して編集</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>組み込みテーマは直接編集できません。複製すると、編集して保存できる独自のコピーが作られます。</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>テーマを編集</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>編集中: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>面</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>枠線</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>テキスト</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>アイデンティティ</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>ステータス</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>保存</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>キャンセル</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>元に戻す</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>複製を編集中はリセットできません。先に保存またはキャンセルしてください。</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>先に変更を保存または破棄してください</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>カスタム ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>現在のテーマを複製</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>テーマファイルをインポート…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>JSONファイルのパス(またはフォルダーにドラッグ&ドロップ)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>テーマをエクスポート</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>クール</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>ナチュラル</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>クラシック</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>レトロ</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter(同梱)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>ゲーム内フォント</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>全体: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>使用中: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>メッセージを入力...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>複製</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>プレビュー</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>メンテナンス</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} タブ</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} タブ</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} テル</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} テル</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} 件</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k 件</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Champion» プレビュー</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>開いています</value>
</data>
</root>
+337 -125
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>향후 FFXIV 패치로 추가될 수 있는 미지의 ChatType에 대한 안전장치입니다. 기본값은 꺼짐 (데이터 최소화)입니다. 향후 채널도 완전히 기록하고 싶다면 활성화하세요.</value> <value>향후 FFXIV 패치로 추가될 수 있는 미지의 ChatType에 대한 안전장치입니다. 기본값은 꺼짐 (데이터 최소화)입니다. 향후 채널도 완전히 기록하고 싶다면 활성화하세요.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>기존 데이터베이스에 필터 적용</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>개인정보 필터는 새 메시지에만 적용됩니다. 아래 정리 기능을 사용하면 저장된 화이트리스트에 맞지 않는 기존 메시지를 소급하여 삭제할 수 있습니다.</value> <value>개인정보 필터는 새 메시지에만 적용됩니다. 아래 정리 기능을 사용하면 저장된 화이트리스트에 맞지 않는 기존 메시지를 소급하여 삭제할 수 있습니다.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>정리 작업은 위의 미저장 변경사항이 아닌 저장된 화이트리스트 (Plugin.Config)를 사용합니다. 현재 변경사항을 적용하려면 먼저 저장하세요.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>수동 실행은 위의 슬라이더 값이 아닌 저장된 보존 정책을 사용합니다. 현재 변경사항을 적용하려면 먼저 저장하세요.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>미리보기가 오래되었습니다. 마지막 새로 고침 이후 화이트리스트가 변경되었습니다. 새로 고침을 클릭하여 다시 계산하세요.</value> <value>미리보기가 오래되었습니다. 마지막 새로 고침 이후 화이트리스트가 변경되었습니다. 새로 고침을 클릭하여 다시 계산하세요.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>지금 보존 정책 적용</value> <value>지금 보존 정책 적용</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: 저장된 정책으로 보존 정리를 즉시 실행합니다. 먼저 변경사항을 저장하세요.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>백그라운드에서 보존 정리 작업 중…</value> <value>백그라운드에서 보존 정리 작업 중…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>시각적 설정</value> <value>시각적 설정</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>시작 시 이전 세션 불러오기</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>이전 세션 메시지에 필터 적용</value> <value>이전 세션 메시지에 필터 적용</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>나중에 세부 조정은 설정 → Hellion Chat에서</value> <value>나중에 세부 조정은 설정 → Hellion Chat에서</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>내보내기 (GDPR Art. 15 — 열람권)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>저장된 메시지를 Markdown, JSON, 또는 CSV 형식으로 내보냅니다. 저장된 메시지의 당사자가 열람을 요청하거나 자신의 기록을 가져갈 때 사용할 수 있습니다.</value> <value>저장된 메시지를 Markdown, JSON, 또는 CSV 형식으로 내보냅니다. 저장된 메시지의 당사자가 열람을 요청하거나 자신의 기록을 가져갈 때 사용할 수 있습니다.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 커뮤니티 번역자 (업스트림)</value> <value>Chat 2 커뮤니티 번역자 (업스트림)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>활성 귓속말</value> <value>활성 귓속말</value>
</data> </data>
@@ -504,9 +486,9 @@
<value>고정됨: 재접속 후에도 유지됩니다.</value> <value>고정됨: 재접속 후에도 유지됩니다.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>자동 귓속말 탭</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve">
<value>모든 /tell에 대해 대화 상대별 탭 자동 열기</value> <value>모든 /tell에 대해 대화 상대별 탭 자동 열기</value>
@@ -545,7 +527,7 @@
<value>참고: XIV Messenger 또는 유사한 플러그인이 귓속말을 차단하는 경우 해당 플러그인의 "Suppress DMs" 옵션을 비활성화해야 Hellion Chat이 귓속말을 받고 자동 탭을 열 수 있습니다.</value> <value>참고: XIV Messenger 또는 유사한 플러그인이 귓속말을 차단하는 경우 해당 플러그인의 "Suppress DMs" 옵션을 비활성화해야 Hellion Chat이 귓속말을 받고 자동 탭을 열 수 있습니다.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>자동 탭의 귓속말 기록</value> <value>자동 탭의 귓속말 기록</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>채팅 탭에서 자동 귓속말 탭이 활성화된 경우에만 적용됩니다.</value> <value>채팅 탭에서 자동 귓속말 탭이 활성화된 경우에만 적용됩니다.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>설정이 재구성되었습니다</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0에서 설정이 주제별 탭으로 재구성되었습니다. 채팅 데이터베이스와 메시지 기록은 변경되지 않았습니다. 설정이 기본값으로 초기화되었습니다. 개인정보 프로필을 다시 선택하려면 개인정보 탭의 다시 열기 버튼을 사용하세요. 이전 설정의 백업이 활성 설정 파일 옆에 HellionChat.json.pre-v10-backup으로 저장되어 있습니다.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>일반</value> <value>일반</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>정보</value> <value>정보</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>테마</value> <value>테마</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>타임스탬프</value> <value>타임스탬프</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>창 프레임</value> <value>창 프레임</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>채팅 입력창 옆에 기호 선택기 버튼 표시</value> <value>채팅 입력창 옆에 기호 선택기 버튼 표시</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>채널 표시기 왼쪽에 작은 버튼을 추가합니다. 클릭하면 FFXIV 아이콘과 엄선된 기호 목록이 있는 팝업이 열립니다. 입력창을 간결하게 유지하려면 비활성화하세요.</value> <value>채널 표시기 왼쪽에 작은 버튼을 추가합니다. 클릭하면 FFXIV 아이콘과 엄선된 기호 목록이 있는 팝업이 열립니다. 입력창을 간결하게 유지하려면 비활성화하세요.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>저장소</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>개요</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>유지 관리</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>시스템</value> <value>시스템</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>링크셸을 여러 개 사용한다면 관리자는 각 셸마다 탭 하나를 사용하여 더 깔끔한 개요를 유지할 것을 권장합니다. 탭을 복제하고 각 복사본에서 채널 선택을 제한하세요.</value> <value>링크셸을 여러 개 사용한다면 관리자는 각 셸마다 탭 하나를 사용하여 더 깔끔한 개요를 유지할 것을 권장합니다. 탭을 복제하고 각 복사본에서 채널 선택을 제한하세요.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>탭 아이콘</value> <value>탭 아이콘</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>채팅 창과 모든 활성 팝아웃을 기본 모니터의 왼쪽 상단 모서리로 이동합니다. 디스플레이 레이아웃 변경 (모니터 분리, 해상도 변경) 후 창이 화면 밖으로 벗어났을 때 유용합니다. 플러그인은 세션당 한 번 자동 경계 검사를 수행하며, 이 버튼은 그래도 접근할 수 없는 경우를 위한 수동 탈출구입니다.</value> <value>채팅 창과 모든 활성 팝아웃을 기본 모니터의 왼쪽 상단 모서리로 이동합니다. 디스플레이 레이아웃 변경 (모니터 분리, 해상도 변경) 후 창이 화면 밖으로 벗어났을 때 유용합니다. 플러그인은 세션당 한 번 자동 경계 검사를 수행하며, 이 버튼은 그래도 접근할 수 없는 경우를 위한 수동 탈출구입니다.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>v0.6.0의 새 기능: 이제 팝아웃에서 직접 입력할 수 있습니다. 창 설정에서 마스터 스위치를 활성화하세요.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>확인</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>창 설정 열기</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>모든 채팅 탭을 별도 창으로 열 수 있습니다. 오른쪽 상단의 창 아이콘을 클릭하거나 탭을 우클릭하세요. v0.6.1의 새 기능: 팝아웃 입력이 기본으로 활성화되었습니다 (설정 → 창에서 비활성화 가능).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>확인</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>설정 열기</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Chat 2가 로드된 상태에서는 Hellion Chat을 시작할 수 없습니다.</value> <value>Chat 2가 로드된 상태에서는 Hellion Chat을 시작할 수 없습니다.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>/xlplugins에서 Chat 2를 비활성화한 후 Hellion Chat을 다시 활성화하세요.</value> <value>/xlplugins에서 Chat 2를 비활성화한 후 Hellion Chat을 다시 활성화하세요.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>일반</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>언어, 입력, 오디오, 성능.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>외형</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>창 불투명도, 글꼴, 모션.</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>테마</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>테마 선택 또는 가져오기</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>창</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>창의 표시 조건 및 이동 가능 여부.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>채팅</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>귓속말, 미리보기, 메시지 동작, 감정 표현.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>탭</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>사용자 정의 채팅 탭 생성 및 설정.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>데이터베이스</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>저장소, 마이그레이션, 레거시 정리.</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>소개</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>확장 기능, 버전, 프로젝트 정보, 번역자, Changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>테마</value> <value>테마</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>유지</value> <value>유지</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>개인정보 우선</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>전체 공개</value> <value>전체 공개</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>데이터 및 개인 정보</value> <value>데이터 및 개인 정보</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>개인 정보 필터, 보존, 정리, 내보내기, 데이터베이스 통계.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>테마</value> <value>테마</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>고급 설정 (Shift+클릭으로 열기)</value> <value>고급 설정 (Shift+클릭으로 열기)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1에서 설정 메뉴가 재구성되었으며 기존 "스타일 재정의" 옵션이 제거되었습니다 (1.1.0의 테마 시스템으로 대체됨). 나머지 설정은 변경되지 않았습니다. 창 투명도는 "테마 &amp; 레이아웃"으로 이전되었습니다. 이전 설정의 백업이 활성 HellionChat.json 옆에 pluginConfigs/HellionChat.json.pre-v16-backup으로 저장되어 있습니다.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>플러그인 연동을 통해 HellionChat은 설치된 다른 Dalamud 플러그인과 함께 작동합니다. 각 연동은 대상 플러그인을 자동으로 감지하며, 대상 플러그인이 없으면 자동으로 비활성화됩니다.</value> <value>플러그인 연동을 통해 HellionChat은 설치된 다른 Dalamud 플러그인과 함께 작동합니다. 각 연동은 대상 플러그인을 자동으로 감지하며, 대상 플러그인이 없으면 자동으로 비활성화됩니다.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>입력</value> <value>입력</value>
</data> </data>
@@ -997,7 +892,7 @@
<value>입력 및 미리 보기</value> <value>입력 및 미리 보기</value>
</data> </data>
<data name="Settings_Section_AutoTellTabs" xml:space="preserve"> <data name="Settings_Section_AutoTellTabs" xml:space="preserve">
<value>자동 텔 탭</value> <value>자동 귓속말 탭</value>
</data> </data>
<data name="Settings_Section_Emotes" xml:space="preserve"> <data name="Settings_Section_Emotes" xml:space="preserve">
<value>감정 표현</value> <value>감정 표현</value>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>이 메시지에는 다른 플레이어에게 빈 박스로 보일 수 있는 플러그인 전용 기호가 포함되어 있습니다. Enter를 다시 눌러 전송합니다.</value> <value>이 메시지에는 다른 플레이어에게 빈 박스로 보일 수 있는 플러그인 전용 기호가 포함되어 있습니다. Enter를 다시 눌러 전송합니다.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>기호 삽입</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>설정</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>채팅 숨기기 (Enter로 다시 표시)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>이 탭을 기본 창으로 되돌리기</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>다른 데이터베이스 작업이 실행 중입니다: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>보존 기간 정리</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>내보내기</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>정리</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>기록 삭제</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>개인정보 필터가 꺼져 있어 모든 채널이 저장되며, 데이터베이스에 설정과 어긋나는 내용이 없습니다. 먼저 필터를 켜고 채널을 선택하세요.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>선택된 채널이 없어 정리를 실행하면 기록 전체가 삭제됩니다. 남길 채널을 선택하거나, 정말 모두 지우려면 삭제 버튼을 사용하세요.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0}개의 메시지가 저장되어 있습니다. 사본을 남기려면 삭제하기 전에 내보내세요.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: 매일 실행을 기다리지 않고 보존 기간 정리를 지금 실행합니다. 위 기한보다 오래된 메시지를 삭제합니다.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>기록 삭제에 실패했습니다. 아무것도 삭제되지 않았습니다. /xllog를 확인하세요.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>원격 측정</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>원격 측정 정보를 수집하지 않습니다. 사용자나 사용 내역에 관한 어떤 정보도 전송하지 않습니다.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>정형문</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>차단</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>최신 메시지로 이동</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>지도 표시 삽입 &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>연결된 아이템 삽입 &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>끔</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>동작</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>단축키</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>알림</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>표시 모드</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>기록</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>명령어 도움말</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>플러그인 고지</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>레이아웃 모드</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>불투명도</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>크기 조절 동작</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>귓속말 자동 열기</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>사이드바</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>브랜드</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>링크</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>연동</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>제작 정보</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>라이선스</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>버튼을 클릭한 뒤 키 조합을 누르세요. Esc로 지웁니다.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>다음 채팅 탭으로</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>이전 채팅 탭으로</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>전환하면 폰트 아틀라스를 다시 만들기 때문에 채팅이 잠시 비워집니다.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24시간제</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>이전 세션의 기록 표시</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>끄면 게임을 실행할 때마다 기록이 비어 있는 상태로 시작하고, 이후 받은 메시지만 채워집니다.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>명령어 도움말 위치</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>입력 중에 명령어 힌트 목록을 어느 쪽에 표시할지.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>귓속말 자동 열기 방식</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>귓속말이 도착했을 때 어디에서 열릴지.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>귓속말마다 탭으로 전환</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>그렇지 않으면 첫 통 이후 탭이 배경에서 열립니다.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>사이드바</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>상단 탭</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>탭 배치</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>메인 창에서 탭 목록의 위치.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>제목 표시줄 보이기</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>팝아웃 창의 제목 표시줄 보이기</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>이동 허용</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>크기 조절 허용</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>사이드바 자동 전환 임계값</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>이 너비 미만이면 사이드바가 상단 탭으로 접힙니다(픽셀).</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>미리보기 위치</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>입력 중에만 미리보기 표시</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea 저장소</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>커스텀 저장소 매니페스트</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>현재 테마: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>복제 후 편집</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>기본 제공 테마는 직접 편집할 수 없습니다. 복제하면 편집하고 저장할 수 있는 사용자 테마가 만들어집니다.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>테마 편집</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>편집 중: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>표면</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>테두리</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>텍스트</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>아이덴티티</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>상태</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>저장</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>취소</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>원본으로 되돌리기</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>복제본을 편집하는 동안에는 되돌릴 수 없습니다. 먼저 저장하거나 취소하세요.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>먼저 변경 사항을 저장하거나 취소하세요</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>사용자 지정 ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>현재 테마 복제</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>테마 파일 가져오기…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>JSON 파일 경로 (또는 폴더로 끌어다 놓기)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>테마 내보내기</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>쿨</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>내추럴</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>클래식</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>레트로</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (기본 포함)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>게임 글꼴</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>전역: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>사용 중: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>메시지를 입력하세요...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>복제</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>미리보기</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>유지 관리</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} 탭</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} 탭</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} 귓속말</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} 귓속말</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} 개</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k 개</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Champion» 미리보기</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>열림</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Sikkerhetsnett for ChatTypes som legges til av fremtidige FFXIV-oppdateringer som pluginen ennå ikke kjenner til. Standard er AV (dataminimering). Aktiver om du vil at fremtidige kanaler også skal logges fullstendig.</value> <value>Sikkerhetsnett for ChatTypes som legges til av fremtidige FFXIV-oppdateringer som pluginen ennå ikke kjenner til. Standard er AV (dataminimering). Aktiver om du vil at fremtidige kanaler også skal logges fullstendig.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Bruk filter på eksisterende database</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Personvernfilteret påvirker bare nye meldinger. Oppryddingen nedenfor lar deg fjerne allerede lagrede meldinger som ikke samsvarer med den lagrede hvitelisten din.</value> <value>Personvernfilteret påvirker bare nye meldinger. Oppryddingen nedenfor lar deg fjerne allerede lagrede meldinger som ikke samsvarer med den lagrede hvitelisten din.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Oppryddingen bruker den LAGREDE hvitelisten din (Plugin.Config), ikke ulagrede endringer ovenfor. Klikk Lagre først om du vil at de nåværende endringene skal brukes.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Den manuelle kjøringen bruker den LAGREDE oppbevaringspolicyen din, ikke skyveknappverdiene ovenfor. Klikk Lagre først om du vil at kjøringen skal bruke de nåværende endringene.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Forhåndsvisningen er utdatert: hvitelisten din har endret seg siden siste oppdatering. Klikk Oppdater for å beregne på nytt.</value> <value>Forhåndsvisningen er utdatert: hvitelisten din har endret seg siden siste oppdatering. Klikk Oppdater for å beregne på nytt.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Bruk oppbevaring nå</value> <value>Bruk oppbevaring nå</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Kjører oppbevaringsoppryddingen umiddelbart med den LAGREDE policyen. Lagre endringene dine først.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Oppbevaringsopprydding kjører i bakgrunnen…</value> <value>Oppbevaringsopprydding kjører i bakgrunnen…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Utseende</value> <value>Utseende</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Last inn forrige økt ved oppstart</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Bruk filtre på meldinger fra tidligere økter</value> <value>Bruk filtre på meldinger fra tidligere økter</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Innstillinger → Hellion Chat for å finjustere senere</value> <value>Innstillinger → Hellion Chat for å finjustere senere</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Eksport (GDPR Art. 15 — Rett til innsyn)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Eksporter lagrede meldinger som Markdown, JSON eller CSV. Dette lar deg oppfylle en innsynsforespørsel fra en person hvis meldinger du har lagret, eller ta med deg din egen historikk.</value> <value>Eksporter lagrede meldinger som Markdown, JSON eller CSV. Dette lar deg oppfylle en innsynsforespørsel fra en person hvis meldinger du har lagret, eller ta med deg din egen historikk.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 fellesskapsoversetterne (oppstrøms)</value> <value>Chat 2 fellesskapsoversetterne (oppstrøms)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktive tells</value> <value>Aktive tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Festet: overlever relog.</value> <value>Festet: overlever relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Merk: Hvis XIV Messenger eller et lignende plugin undertrykker tells, deaktiver "Suppress DMs"-alternativet der slik at Hellion Chat kan motta tells og åpne auto-fanene.</value> <value>Merk: Hvis XIV Messenger eller et lignende plugin undertrykker tells, deaktiver "Suppress DMs"-alternativet der slik at Hellion Chat kan motta tells og åpne auto-fanene.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell-historikk i auto-faner</value> <value>Tell-historikk i auto-faner</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Gjelder bare når auto-tell-faner er aktivert i Chat-fanen.</value> <value>Gjelder bare når auto-tell-faner er aktivert i Chat-fanen.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Innstillinger omstrukturert</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 har omstrukturert innstillingene i tematiske faner. Chat-databasen og meldingshistorikken din er uendret. Innstillinger har blitt tilbakestilt til standardverdier. Hvis du vil velge personvernprofil på nytt, finner du Åpne igjen-knappen i Personvern-fanen. En sikkerhetskopi av forrige konfig ligger på HellionChat.json.pre-v10-backup ved siden av den aktive konfig-filen.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Generelt</value> <value>Generelt</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Om</value> <value>Om</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Tidsstempler</value> <value>Tidsstempler</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Vindusramme</value> <value>Vindusramme</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Vis symbolvelger-knapp ved siden av chat-inndata</value> <value>Vis symbolvelger-knapp ved siden av chat-inndata</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Legger til en liten knapp til venstre for kanalindikatoren som åpner et popup-vindu med FFXIV-ikoner og en kuratert symbolliste. Deaktiver for en slankere inndatalinje.</value> <value>Legger til en liten knapp til venstre for kanalindikatoren som åpner et popup-vindu med FFXIV-ikoner og en kuratert symbolliste. Deaktiver for en slankere inndatalinje.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Lagring</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Oversikt</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Vedlikehold</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Hvis du bruker flere linkshells, anbefaler vedlikeholderen én fane per shell for en ryddigere oversikt. Dupliser fanen og begrens kanalvalget i hver kopi.</value> <value>Hvis du bruker flere linkshells, anbefaler vedlikeholderen én fane per shell for en ryddigere oversikt. Dupliser fanen og begrens kanalvalget i hver kopi.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Fane-ikon</value> <value>Fane-ikon</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Flytter chat-vinduet og alle aktive pop-out-vinduer tilbake til øverste venstre hjørne på primærskjermen. Nyttig når et vindu har havnet utenfor det synlige området etter en endring i skjermoppsettet (skjerm koblet fra, oppløsning endret). Pluginen utfører også en automatisk grensekontroll én gang per økt. Denne knappen er den manuelle nødutgangen hvis noe likevel havner utenfor rekkevidde.</value> <value>Flytter chat-vinduet og alle aktive pop-out-vinduer tilbake til øverste venstre hjørne på primærskjermen. Nyttig når et vindu har havnet utenfor det synlige området etter en endring i skjermoppsettet (skjerm koblet fra, oppløsning endret). Pluginen utfører også en automatisk grensekontroll én gang per økt. Denne knappen er den manuelle nødutgangen hvis noe likevel havner utenfor rekkevidde.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Nytt i v0.6.0: Du kan nå skrive direkte i pop-out-vinduer. Aktiver hovedbryteren i Vindu-innstillingene.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Forstått</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Åpne vindusinnstillinger</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Du kan åpne hvilken som helst chat-fane som sitt eget vindu. Klikk vindusikonet øverst til høyre eller høyreklikk fanen. Nytt i v0.6.1: pop-out-inndata er aktiv som standard (kan deaktiveres under Innstillinger → Vindu).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Forstått</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Åpne innstillinger</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat kan ikke starte mens Chat 2 er lastet.</value> <value>Hellion Chat kan ikke starte mens Chat 2 er lastet.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Deaktiver Chat 2 i /xlplugins, aktiver deretter Hellion Chat på nytt.</value> <value>Deaktiver Chat 2 i /xlplugins, aktiver deretter Hellion Chat på nytt.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Generelt</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Språk, inndata, lyd og ytelse.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Utseende</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Vindusopasitet, skrifttyper, bevegelse</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Velg et theme eller importer ditt eget</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Vindu</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Når vinduet er synlig og om det kan flyttes.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, forhåndsvisning, meldingsoppførsel og emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Faner</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Opprett og konfigurer egendefinerte chat-faner.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Database</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Lagring, migrering, eldre opprydding</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Om</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Utvidelser, versjon, prosjektinformasjon, oversettere og changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Behold</value> <value>Behold</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Personvern først</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Åpen</value> <value>Åpen</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Data og personvern</value> <value>Data og personvern</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Personvernfilter, oppbevaring, opprydding, eksport og databasestatistikk.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avansert (Shift+klikk for å åpne)</value> <value>Avansert (Shift+klikk for å åpne)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 har reorganisert innstillingsmenyen og fjernet det gamle "Overstyr stil"-alternativet (erstattet av theme-systemet fra 1.1.0). Øvrige innstillinger er uendret. Vindustransparens er migrert til "Theme &amp; Layout". En sikkerhetskopi av forrige konfig ligger under pluginConfigs/HellionChat.json.pre-v16-backup ved siden av den aktive HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Plugin-integrasjoner lar HellionChat samarbeide med andre installerte Dalamud-plugins. Hver integrasjon oppdager automatisk målet sitt og deaktiverer seg stille når mål-pluginen mangler.</value> <value>Plugin-integrasjoner lar HellionChat samarbeide med andre installerte Dalamud-plugins. Hver integrasjon oppdager automatisk målet sitt og deaktiverer seg stille når mål-pluginen mangler.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Input</value> <value>Input</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Denne meldingen inneholder plugin-eksklusive symboler som andre spillere kanskje ser som tomme bokser. Trykk Enter igjen for å sende likevel.</value> <value>Denne meldingen inneholder plugin-eksklusive symboler som andre spillere kanskje ser som tomme bokser. Trykk Enter igjen for å sende likevel.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Sett inn symbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Innstillinger</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Skjul chatten (Enter henter den tilbake)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Send denne fanen tilbake til hovedvinduet</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>En annen databaseoperasjon kjører: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>oppbevaringsopprydding</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>eksport</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>opprydding</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>sletting av historikken</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Personvernfilteret er av, så alle kanaler lagres, og ingenting i databasen strider mot innstillingene dine. Slå på filteret og velg kanaler først.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Ingen kanal er valgt, så en opprydding ville slette hele historikken. Velg kanalene du vil beholde, eller bruk sletteknappen hvis du virkelig vil fjerne alt.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0} meldinger er lagret. Vil du beholde en kopi, eksporter dem før du sletter.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: kjører oppryddingen med en gang i stedet for å vente på det daglige gjennomløpet. Sletter meldinger eldre enn grensene over.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Sletting av historikken mislyktes. Ingenting ble fjernet, se /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetri</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Ingen telemetri samles inn. Dette pluginet sender ingenting om deg eller bruken din noe sted.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Auto-oversettelse</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Blokker</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Hopp til den nyeste meldingen</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Sett inn kartmarkør &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Sett inn lenket gjenstand &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>av</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Oppførsel</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Hurtigtaster</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Varsler</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Visningsmoduser</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historikk</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Kommandohjelp</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Plugin-varsel</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Oppsettsmodus</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Dekkevne</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Størrelsesendring</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Automatisk åpning av tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Sidepanel</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Merke</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Lenker</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrasjoner</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Bidragsytere</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Lisens</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Klikk på en knapp, og trykk deretter tastekombinasjonen. Esc tømmer.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Bytt til neste fane</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Bytt til forrige fane</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Bytte bygger fontatlaset på nytt, så chatten er tom et øyeblikk.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-timers klokke</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Vis historikk fra tidligere økter</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Av betyr at loggen starter tom hver gang spillet startes, og bare fylles med meldinger mottatt etterpå.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Side for kommandohjelp</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Hvilken side listen med kommandotips vises på mens du skriver.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Modus for automatisk åpning av tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Hvor en tell åpnes når den kommer.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Bytt til fanen ved hver tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Ellers åpnes fanen i bakgrunnen etter den første.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Sidepanel</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Faner øverst</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Plassering av faner</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Hvor fanelisten sitter i hovedvinduet.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Vis tittellinje</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Tittellinje for pop-out-vinduer</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Tillat flytting</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Tillat størrelsesendring</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Terskel for automatisk bytte av sidepanel</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Under denne bredden brettes sidepanelet til faner øverst, i piksler.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Plassering av forhåndsvisning</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Vis forhåndsvisning bare under skriving</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea-repositorium</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest for egendefinert repo</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktivt tema: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Forgrein og rediger</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Innebygde temaer kan ikke redigeres direkte. Forgrening lager en egen kopi du kan redigere og lagre.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Rediger tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Redigerer: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Flater</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Kanter</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Tekst</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identitet</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Lagre</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Avbryt</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Tilbakestill til kilden</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Tilbakestilling er ikke tilgjengelig mens du redigerer en forgrening. Lagre eller avbryt først.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Lagre eller forkast endringene dine først</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Egendefinerte ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Forgrein aktivt tema</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importer temafil…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Sti til JSON-fil (eller dra og slipp i mappen)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Eksporter tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Kjølige</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturlige</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klassiske</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (medfølger)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Spillets skrift</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktiv: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Skriv en melding...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Dupliser</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>forhåndsvisning</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>vedlikehold</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} fane</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} faner</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} tells</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} meld.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k meld.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Champion» Forhåndsvisning</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>åpen</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Vangnet voor ChatTypes die via toekomstige FFXIV-patches worden toegevoegd en nog niet bekend zijn bij de plugin. Standaard is UIT (dataminimalisatie). Schakel in als je toekomstige kanalen ook volledig wilt loggen.</value> <value>Vangnet voor ChatTypes die via toekomstige FFXIV-patches worden toegevoegd en nog niet bekend zijn bij de plugin. Standaard is UIT (dataminimalisatie). Schakel in als je toekomstige kanalen ook volledig wilt loggen.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Filter toepassen op bestaande database</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Het privacyfilter heeft alleen invloed op nieuwe berichten. Met de opschoning hieronder kun je reeds opgeslagen berichten alsnog verwijderen die niet overeenkomen met je opgeslagen whitelist.</value> <value>Het privacyfilter heeft alleen invloed op nieuwe berichten. Met de opschoning hieronder kun je reeds opgeslagen berichten alsnog verwijderen die niet overeenkomen met je opgeslagen whitelist.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>De opschoning gebruikt je OPGESLAGEN whitelist (Plugin.Config), niet de niet-opgeslagen wijzigingen hierboven. Klik eerst op Opslaan als je wilt dat je huidige wijzigingen worden toegepast.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>De handmatige uitvoering gebruikt je OPGESLAGEN retentiebeleid, niet de sliderwaarden hierboven. Klik eerst op Opslaan als je wilt dat de uitvoering je huidige wijzigingen toepast.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Voorbeeld is verouderd: je whitelist is gewijzigd sinds de laatste vernieuwing. Klik op Vernieuwen om opnieuw te berekenen.</value> <value>Voorbeeld is verouderd: je whitelist is gewijzigd sinds de laatste vernieuwing. Klik op Vernieuwen om opnieuw te berekenen.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Retentie nu toepassen</value> <value>Retentie nu toepassen</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Voert de retentieopschoning onmiddellijk uit met het OPGESLAGEN beleid. Sla je wijzigingen eerst op.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Retentieopschoning wordt op de achtergrond uitgevoerd…</value> <value>Retentieopschoning wordt op de achtergrond uitgevoerd…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Uiterlijk</value> <value>Uiterlijk</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Vorige sessie laden bij opstarten</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Filters toepassen op berichten uit vorige sessies</value> <value>Filters toepassen op berichten uit vorige sessies</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Instellingen → Hellion Chat om later te verfijnen</value> <value>Instellingen → Hellion Chat om later te verfijnen</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Exporteren (AVG Art. 15 — Recht op inzage)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exporteer opgeslagen berichten als Markdown, JSON of CSV. Hiermee kun je een inzageverzoek van een persoon wiens berichten je hebt opgeslagen afhandelen, of je eigen geschiedenis meenemen.</value> <value>Exporteer opgeslagen berichten als Markdown, JSON of CSV. Hiermee kun je een inzageverzoek van een persoon wiens berichten je hebt opgeslagen afhandelen, of je eigen geschiedenis meenemen.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 community-vertalers (upstream)</value> <value>Chat 2 community-vertalers (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Actieve tells</value> <value>Actieve tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Vastgepind: overleeft relog.</value> <value>Vastgepind: overleeft relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Let op: Als XIV Messenger of een vergelijkbare plugin tells onderdrukt, schakel dan de optie "Suppress DMs" daar uit zodat Hellion Chat tells kan ontvangen en de auto-tabbladen kan openen.</value> <value>Let op: Als XIV Messenger of een vergelijkbare plugin tells onderdrukt, schakel dan de optie "Suppress DMs" daar uit zodat Hellion Chat tells kan ontvangen en de auto-tabbladen kan openen.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell-geschiedenis in auto-tabbladen</value> <value>Tell-geschiedenis in auto-tabbladen</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Werkt alleen als auto-tell-tabbladen zijn ingeschakeld in het Chat-tabblad.</value> <value>Werkt alleen als auto-tell-tabbladen zijn ingeschakeld in het Chat-tabblad.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Instellingen geherstructureerd</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 heeft de instellingen geherstructureerd in thematische tabbladen. Je chatdatabase en berichtengeschiedenis blijven ongewijzigd. Instellingen zijn teruggezet naar de standaardwaarden. Als je je privacyprofiel opnieuw wilt kiezen, staat de knop Wizard opnieuw tonen in het tabblad Privacy. Een back-up van de vorige configuratie staat bij HellionChat.json.pre-v10-backup naast het actieve configuratiebestand.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Algemeen</value> <value>Algemeen</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Over</value> <value>Over</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Tijdstempels</value> <value>Tijdstempels</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Vensterkader</value> <value>Vensterkader</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Symboolkiezer naast de chatinvoer tonen</value> <value>Symboolkiezer naast de chatinvoer tonen</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Voegt een kleine knop links van de kanaalkiezer toe die een popup opent met FFXIV-pictogrammen en een samengestelde symbolenlijst. Schakel uit als je een slankere invoerbalk verkiest.</value> <value>Voegt een kleine knop links van de kanaalkiezer toe die een popup opent met FFXIV-pictogrammen en een samengestelde symbolenlijst. Schakel uit als je een slankere invoerbalk verkiest.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Opslag</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Overzicht</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Onderhoud</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Systeem</value> <value>Systeem</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Als je meerdere linkshells gebruikt, raadt de maintainer aan om één tabblad per shell te gebruiken voor een overzichtelijker geheel. Dupliceer het tabblad en beperk de kanaalselectie in elke kopie.</value> <value>Als je meerdere linkshells gebruikt, raadt de maintainer aan om één tabblad per shell te gebruiken voor een overzichtelijker geheel. Dupliceer het tabblad en beperk de kanaalselectie in elke kopie.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Tabblad-pictogram</value> <value>Tabblad-pictogram</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Verplaatst het chatvenster en alle actieve pop-outs terug naar de linkerbovenhoek van de primaire monitor. Handig wanneer een venster buiten het zichtbare gebied is beland na een wijziging in de schermindeling (monitor losgekoppeld, resolutie gewijzigd). De plugin voert ook eenmalig per sessie een automatische grenscontrole uit; deze knop is de handmatige nooduitgang als er toch iets onbereikbaar blijft.</value> <value>Verplaatst het chatvenster en alle actieve pop-outs terug naar de linkerbovenhoek van de primaire monitor. Handig wanneer een venster buiten het zichtbare gebied is beland na een wijziging in de schermindeling (monitor losgekoppeld, resolutie gewijzigd). De plugin voert ook eenmalig per sessie een automatische grenscontrole uit; deze knop is de handmatige nooduitgang als er toch iets onbereikbaar blijft.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Nieuw in v0.6.0: je kunt nu direct typen in pop-outs. Schakel de hoofdschakelaar in via de vensterinstellingen.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Begrepen</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Vensterinstellingen openen</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Je kunt elk chattabblad als een eigen venster openen. Klik op het venster-pictogram rechtsboven of klik met rechts op het tabblad. Nieuw in v0.6.1: pop-outinvoer is standaard actief (uit te schakelen via Instellingen → Venster).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Begrepen</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Instellingen openen</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat kan niet starten terwijl Chat 2 geladen is.</value> <value>Hellion Chat kan niet starten terwijl Chat 2 geladen is.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Schakel Chat 2 uit in /xlplugins en schakel daarna Hellion Chat opnieuw in.</value> <value>Schakel Chat 2 uit in /xlplugins en schakel daarna Hellion Chat opnieuw in.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Algemeen</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Taal, invoer, audio en prestaties.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Uiterlijk</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Vensterdekking, lettertypen, beweging</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Een theme kiezen of je eigen theme importeren</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Venster</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Wanneer het venster zichtbaar is en of het verplaatst kan worden.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, voorbeeld, berichtgedrag en emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Tabbladen</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Aangepaste chattabbladen aanmaken en configureren.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Database</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Opslag, migratie, verouderde opschoning</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Over</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensies, versie, projectinformatie, vertalers en changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Bewaren</value> <value>Bewaren</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Privacy voorop</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Open</value> <value>Open</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Gegevens en privacy</value> <value>Gegevens en privacy</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Privacyfilter, retentie, opschoning, export en databasestatistieken.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Geavanceerd (Shift+klik om te openen)</value> <value>Geavanceerd (Shift+klik om te openen)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 heeft het instellingenmenu opnieuw ingedeeld en de oude optie "Stijl overschrijven" verwijderd (vervangen door het themasysteem uit 1.1.0). Je overige instellingen zijn ongewijzigd. Venstertransparantie is verplaatst naar "Theme &amp; Layout". Een back-up van de vorige configuratie staat bij pluginConfigs/HellionChat.json.pre-v16-backup naast de actieve HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Plugin-integraties laten HellionChat samenwerken met andere geïnstalleerde Dalamud-plugins. Elke integratie detecteert automatisch zijn doelplugin en schakelt zichzelf stil uit als die ontbreekt.</value> <value>Plugin-integraties laten HellionChat samenwerken met andere geïnstalleerde Dalamud-plugins. Elke integratie detecteert automatisch zijn doelplugin en schakelt zichzelf stil uit als die ontbreekt.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Invoer</value> <value>Invoer</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Dit bericht bevat plugin-exclusieve symbolen die andere spelers mogelijk als lege vakjes zien. Druk opnieuw op Enter om toch te verzenden.</value> <value>Dit bericht bevat plugin-exclusieve symbolen die andere spelers mogelijk als lege vakjes zien. Druk opnieuw op Enter om toch te verzenden.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Symbool invoegen</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Instellingen</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Chat verbergen (Enter haalt hem terug)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Dit tabblad terugzetten in het hoofdvenster</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Er wordt al een andere databasebewerking uitgevoerd: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>bewaartermijnopschoning</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>export</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>opschoning</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>wissen van de geschiedenis</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Het privacyfilter staat uit, dus elk kanaal wordt opgeslagen en niets in de database spreekt je instellingen tegen. Zet het filter eerst aan en kies kanalen.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Er is geen kanaal geselecteerd, dus een opschoning zou de hele geschiedenis wissen. Kies de kanalen die je wilt bewaren, of gebruik de wisknop als je echt alles kwijt wilt.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Er zijn {0:N0} berichten opgeslagen. Wil je een kopie houden, exporteer ze dan voordat je wist.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: voert de opschoning nu meteen uit in plaats van te wachten op de dagelijkse ronde. Wist berichten ouder dan de limieten hierboven.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Het wissen van de geschiedenis is mislukt. Er is niets verwijderd, zie /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetrie</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Er wordt geen telemetrie verzameld. De plug-in stuurt niets over jou of je gebruik ergens heen.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Automatische vertaling</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Blokkeren</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Ga naar het nieuwste bericht</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Kaartmarkering invoegen &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Gekoppeld voorwerp invoegen &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>uit</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Gedrag</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Sneltoetsen</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Meldingen</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Weergavemodi</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Geschiedenis</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Commandohulp</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Plug-invermelding</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Lay-outmodus</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Dekking</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Formaatgedrag</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Tells automatisch openen</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Zijbalk</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Merk</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Links</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integraties</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Met dank aan</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licentie</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Klik op een knop en druk daarna de toetsencombinatie. Esc wist.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Naar het volgende tabblad</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Naar het vorige tabblad</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Wisselen bouwt de lettertype-atlas opnieuw op, dus de chat is even leeg.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-uursklok</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Geschiedenis van eerdere sessies tonen</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Uit betekent dat het logboek bij elke start leeg begint en zich alleen vult met daarna ontvangen berichten.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Zijde van de commandohulp</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Aan welke kant de lijst met commandotips verschijnt tijdens het typen.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Modus voor automatisch openen van tells</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Waar een tell opent wanneer die binnenkomt.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Bij elke tell naar het tabblad schakelen</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Anders opent het tabblad na de eerste op de achtergrond.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Zijbalk</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Tabbladen bovenaan</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Tabbladpositie</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Waar de tabbladlijst in het hoofdvenster staat.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Titelbalk tonen</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Titelbalk voor pop-outvensters</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Verplaatsen toestaan</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Formaat wijzigen toestaan</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Omschakeldrempel zijbalk</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Onder deze breedte vouwt de zijbalk samen tot tabbladen bovenaan, in pixels.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Positie van het voorbeeld</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Voorbeeld alleen tijdens typen tonen</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea-repository</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest van eigen repository</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Actief thema: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Aftakken en bewerken</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Ingebouwde thema's kun je niet rechtstreeks bewerken. Aftakken maakt een eigen kopie die je kunt bewerken en opslaan.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Thema bewerken</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Bezig met bewerken: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Vlakken</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Randen</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Tekst</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identiteit</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Opslaan</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Annuleren</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Terugzetten naar bron</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Terugzetten kan niet tijdens het bewerken van een aftakking. Sla eerst op of annuleer.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Sla je wijzigingen eerst op of verwerp ze</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Eigen ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Actief thema aftakken</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Themabestand importeren…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Pad naar JSON-bestand (of sleep het naar de map)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Thema exporteren</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Koel</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Natuurlijk</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klassiek</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (meegeleverd)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Speltypografie</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Globaal: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Actief: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Typ een bericht...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Dupliceren</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>voorbeeld</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>onderhoud</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} tabblad</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} tabbladen</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} tells</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} ber.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k ber.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Kampioen» Voorbeeld</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>open</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Zabezpieczenie na wypadek ChatTypes dodanych przez przyszłe patche FFXIV, których plugin jeszcze nie zna. Domyślnie WYŁĄCZONE (minimalizacja danych). Włącz, jeśli chcesz, żeby przyszłe kanały też były w pełni logowane.</value> <value>Zabezpieczenie na wypadek ChatTypes dodanych przez przyszłe patche FFXIV, których plugin jeszcze nie zna. Domyślnie WYŁĄCZONE (minimalizacja danych). Włącz, jeśli chcesz, żeby przyszłe kanały też były w pełni logowane.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Zastosuj filtr do istniejącej bazy danych</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Filtr prywatności działa tylko na nowe wiadomości. Czyszczenie poniżej pozwala retroaktywnie usunąć już zapisane wiadomości, które nie pasują do zapisanej listy dozwolonych.</value> <value>Filtr prywatności działa tylko na nowe wiadomości. Czyszczenie poniżej pozwala retroaktywnie usunąć już zapisane wiadomości, które nie pasują do zapisanej listy dozwolonych.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Czyszczenie używa ZAPISANEJ listy dozwolonych (Plugin.Config), a nie niezapisanych zmian powyżej. Najpierw kliknij Zapisz, jeśli chcesz zastosować bieżące zmiany.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Ręczne uruchomienie używa ZAPISANEJ polityki przechowywania, nie wartości suwaków powyżej. Najpierw kliknij Zapisz, jeśli chcesz zastosować bieżące zmiany.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Podgląd jest nieaktualny: lista dozwolonych zmieniła się od ostatniego odświeżenia. Kliknij Odśwież, aby przeliczyć.</value> <value>Podgląd jest nieaktualny: lista dozwolonych zmieniła się od ostatniego odświeżenia. Kliknij Odśwież, aby przeliczyć.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Zastosuj przechowywanie teraz</value> <value>Zastosuj przechowywanie teraz</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Natychmiast uruchamia czyszczenie przechowywania przy użyciu ZAPISANEJ polityki. Najpierw zapisz zmiany.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Czyszczenie przechowywania trwa w tle…</value> <value>Czyszczenie przechowywania trwa w tle…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Wygląd</value> <value>Wygląd</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Wczytaj poprzednią sesję przy uruchomieniu</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Stosuj filtry do wiadomości z poprzednich sesji</value> <value>Stosuj filtry do wiadomości z poprzednich sesji</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Ustawienia → Hellion Chat, aby dostosować później</value> <value>Ustawienia → Hellion Chat, aby dostosować później</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Eksport (GDPR Art. 15 — Prawo dostępu)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Eksportuj zapisane wiadomości jako Markdown, JSON lub CSV. Pozwala to zrealizować wniosek o dostęp od osoby, której wiadomości przechowujesz, lub zabrać własną historię ze sobą.</value> <value>Eksportuj zapisane wiadomości jako Markdown, JSON lub CSV. Pozwala to zrealizować wniosek o dostęp od osoby, której wiadomości przechowujesz, lub zabrać własną historię ze sobą.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Tłumacze społeczności Chat 2 (upstream)</value> <value>Tłumacze społeczności Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktywne tells</value> <value>Aktywne tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Przypięta: przeżywa relog.</value> <value>Przypięta: przeżywa relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Uwaga: Jeśli XIV Messenger lub podobny plugin blokuje tells, wyłącz tam opcję „Suppress DMs", aby Hellion Chat mógł odbierać tells i otwierać auto-zakładki.</value> <value>Uwaga: Jeśli XIV Messenger lub podobny plugin blokuje tells, wyłącz tam opcję „Suppress DMs", aby Hellion Chat mógł odbierać tells i otwierać auto-zakładki.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Historia tell w auto-zakładkach</value> <value>Historia tell w auto-zakładkach</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Działa tylko wtedy, gdy auto-zakładki tell są włączone w zakładce Chat.</value> <value>Działa tylko wtedy, gdy auto-zakładki tell są włączone w zakładce Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Ustawienia przeorganizowane</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 przeorganizował ustawienia w tematyczne zakładki. Twoja baza danych czatu i historia wiadomości pozostają bez zmian. Ustawienia zostały zresetowane do domyślnych. Jeśli chcesz ponownie wybrać profil prywatności, przycisk Otwórz ponownie znajduje się w zakładce Prywatność. Kopia zapasowa poprzedniej konfiguracji znajduje się w pliku HellionChat.json.pre-v10-backup obok aktywnego pliku konfiguracji.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Ogólne</value> <value>Ogólne</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>O pluginie</value> <value>O pluginie</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Znaczniki czasu</value> <value>Znaczniki czasu</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Ramka okna</value> <value>Ramka okna</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Pokaż przycisk wyboru symboli obok pola wprowadzania czatu</value> <value>Pokaż przycisk wyboru symboli obok pola wprowadzania czatu</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Dodaje mały przycisk po lewej stronie wskaźnika kanału, który otwiera popup z ikonami FFXIV i listą wybranych symboli. Wyłącz, jeśli wolisz bardziej minimalistyczny pasek wprowadzania.</value> <value>Dodaje mały przycisk po lewej stronie wskaźnika kanału, który otwiera popup z ikonami FFXIV i listą wybranych symboli. Wyłącz, jeśli wolisz bardziej minimalistyczny pasek wprowadzania.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Przechowywanie</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Przegląd</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Konserwacja</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Jeśli używasz wielu linkshells, maintainer zaleca po jednej zakładce na każdą dla lepszego przeglądu. Zduplikuj zakładkę i ogranicz wybór kanałów w każdej kopii.</value> <value>Jeśli używasz wielu linkshells, maintainer zaleca po jednej zakładce na każdą dla lepszego przeglądu. Zduplikuj zakładkę i ogranicz wybór kanałów w każdej kopii.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Ikona zakładki</value> <value>Ikona zakładki</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Przenosi okno czatu i wszystkie aktywne pop-outy z powrotem do lewego górnego rogu głównego monitora. Przydatne, gdy okno znalazło się poza widocznym obszarem po zmianie układu wyświetlania (odłączony monitor, zmieniona rozdzielczość). Plugin wykonuje też automatyczne sprawdzenie granic raz na sesję; ten przycisk to ręczna opcja awaryjna, gdy coś nadal pozostaje niedostępne.</value> <value>Przenosi okno czatu i wszystkie aktywne pop-outy z powrotem do lewego górnego rogu głównego monitora. Przydatne, gdy okno znalazło się poza widocznym obszarem po zmianie układu wyświetlania (odłączony monitor, zmieniona rozdzielczość). Plugin wykonuje też automatyczne sprawdzenie granic raz na sesję; ten przycisk to ręczna opcja awaryjna, gdy coś nadal pozostaje niedostępne.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Nowość w v0.6.0: Możesz teraz pisać bezpośrednio w pop-outach. Włącz główny przełącznik w ustawieniach okna.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Rozumiem</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Otwórz ustawienia okna</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Możesz otworzyć dowolną zakładkę czatu jako własne okno. Kliknij ikonę okna w prawym górnym rogu lub kliknij prawym przyciskiem zakładkę. Nowość w v0.6.1: wprowadzanie w pop-oucie jest domyślnie aktywne (można wyłączyć w Ustawieniach → Okno).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Rozumiem</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Otwórz ustawienia</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat nie może uruchomić się, gdy Chat 2 jest załadowany.</value> <value>Hellion Chat nie może uruchomić się, gdy Chat 2 jest załadowany.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Wyłącz Chat 2 w /xlplugins, a następnie ponownie włącz Hellion Chat.</value> <value>Wyłącz Chat 2 w /xlplugins, a następnie ponownie włącz Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Ogólne</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Język, wprowadzanie, audio i wydajność.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Wygląd</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Przezroczystość okna, czcionki, animacje</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Motywy</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Wybierz motyw lub zaimportuj własny</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Okno</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Kiedy okno jest widoczne i czy można je przesuwać.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, podgląd, zachowanie wiadomości i emoty.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Zakładki</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Twórz i konfiguruj niestandardowe zakładki czatu.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Baza danych</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Przechowywanie, migracja, stare czyszczenie</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>O wtyczce</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Rozszerzenia, wersja, informacje o projekcie, tłumacze i changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Motywy</value> <value>Motywy</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Zachowaj</value> <value>Zachowaj</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Prywatność przede wszystkim</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Otwarty</value> <value>Otwarty</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Dane i prywatność</value> <value>Dane i prywatność</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtr prywatności, przechowywanie, czyszczenie, eksport i statystyki bazy danych.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Zaawansowane (Shift+kliknięcie, aby otworzyć)</value> <value>Zaawansowane (Shift+kliknięcie, aby otworzyć)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 przeorganizował menu ustawień i usunął starą opcję „Override style" (zastąpioną przez system motywów z wersji 1.1.0). Pozostałe ustawienia pozostają bez zmian. Przezroczystość okna została przeniesiona do „Theme &amp; Layout". Kopia zapasowa poprzedniej konfiguracji znajduje się w pluginConfigs/HellionChat.json.pre-v16-backup obok aktywnego HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Integracje z pluginami pozwalają HellionChat współpracować z innymi zainstalowanymi pluginami Dalamud. Każda integracja automatycznie wykrywa swój cel i cicho się wyłącza, gdy docelowy plugin jest niedostępny.</value> <value>Integracje z pluginami pozwalają HellionChat współpracować z innymi zainstalowanymi pluginami Dalamud. Każda integracja automatycznie wykrywa swój cel i cicho się wyłącza, gdy docelowy plugin jest niedostępny.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Wejście</value> <value>Wejście</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Ta wiadomość zawiera symbole tylko dla wtyczki, które inni gracze mogą widzieć jako puste kwadraty. Naciśnij Enter ponownie, aby wysłać mimo to.</value> <value>Ta wiadomość zawiera symbole tylko dla wtyczki, które inni gracze mogą widzieć jako puste kwadraty. Naciśnij Enter ponownie, aby wysłać mimo to.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Wstaw symbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Ustawienia</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Ukryj czat (Enter go przywraca)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Przywróć tę kartę do okna głównego</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Trwa już inna operacja na bazie danych: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>porządkowanie według czasu przechowywania</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>eksport</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>porządkowanie</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>usuwanie historii</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Filtr prywatności jest wyłączony, więc zapisywany jest każdy kanał i nic w bazie danych nie kłóci się z twoimi ustawieniami. Najpierw włącz filtr i wybierz kanały.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Nie wybrano żadnego kanału, więc porządkowanie usunęłoby całą historię. Wybierz kanały, które chcesz zachować, albo użyj przycisku czyszczenia, jeśli naprawdę chcesz usunąć wszystko.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Zapisanych jest {0:N0} wiadomości. Jeśli chcesz zachować kopię, wyeksportuj je przed wyczyszczeniem.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: uruchamia porządkowanie od razu, zamiast czekać na codzienny przebieg. Usuwa wiadomości starsze niż limity powyżej.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Usuwanie historii nie powiodło się. Nic nie zostało usunięte, zobacz /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetria</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Nie zbieramy żadnej telemetrii. Plugin nigdzie nie wysyła informacji o tobie ani o twoim użytkowaniu.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Autotłumaczenie</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Zablokuj</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Przejdź do najnowszej wiadomości</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Wstaw znacznik mapy &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Wstaw powiązany przedmiot &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>wyłączone</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Zachowanie</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Skróty klawiszowe</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Powiadomienia</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Tryby wyświetlania</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historia</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Pomoc do poleceń</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Informacja o pluginie</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Tryb układu</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Nieprzezroczystość</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Zmiana rozmiaru</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Automatyczne otwieranie tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Panel boczny</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marka</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Odnośniki</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integracje</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Twórcy</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licencja</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Kliknij przycisk, a następnie naciśnij kombinację klawiszy. Esc czyści.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Przejdź do następnej zakładki</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Przejdź do poprzedniej zakładki</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Zmiana przebudowuje atlas czcionek, więc czat na chwilę pustoszeje.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Zegar 24-godzinny</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Pokaż historię z poprzednich sesji</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Wyłączone oznacza, że dziennik startuje pusty przy każdym uruchomieniu gry i wypełnia się tylko wiadomościami odebranymi od tego momentu.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Strona pomocy do poleceń</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Po której stronie pojawia się lista podpowiedzi podczas pisania.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Tryb automatycznego otwierania tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Gdzie otwiera się tell, gdy nadejdzie.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Przełączaj na zakładkę przy każdym tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>W przeciwnym razie zakładka po pierwszym otwiera się w tle.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Panel boczny</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Zakładki u góry</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Rozmieszczenie zakładek</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Gdzie w oknie głównym znajduje się lista zakładek.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Pokaż pasek tytułu</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Pokaż pasek tytułu w oknach odłączonych</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Zezwól na przesuwanie</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Zezwól na zmianę rozmiaru</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Próg automatycznego przełączania panelu bocznego</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Poniżej tej szerokości panel boczny zwija się w zakładki u góry, w pikselach.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Pozycja podglądu</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Pokaż podgląd tylko podczas pisania</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Repozytorium Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest własnego repozytorium</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktywny motyw: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Rozgałęź i edytuj</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Wbudowanych motywów nie można edytować bezpośrednio. Rozgałęzienie tworzy własną kopię, którą możesz edytować i zapisać.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Edytuj motyw</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Edytowanie: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Powierzchnie</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Obramowania</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Tekst</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Tożsamość</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Zapisz</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Anuluj</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Przywróć do źródła</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Przywracanie jest niedostępne podczas edycji rozgałęzienia. Najpierw zapisz lub anuluj.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Najpierw zapisz lub odrzuć swoje zmiany</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Własne ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Rozgałęź aktywny motyw</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importuj plik motywu…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Ścieżka do pliku JSON (lub przeciągnij do folderu)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Eksportuj motyw</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Chłodne</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturalne</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klasyczne</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (w zestawie)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Czcionka z gry</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Globalna: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktywna: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Napisz wiadomość...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplikuj</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>podgląd</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>konserwacja</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} zakładka</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} zakładki</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} szept</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} szepty</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} wiad.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k wiad.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Mistrz» Podgląd</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>otwarte</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Rede de segurança para ChatTypes adicionados por patches futuros do FFXIV que o plugin ainda não conhece. O padrão é DESATIVADO (minimização de dados). Ative se quiser que canais futuros também sejam registrados por completo.</value> <value>Rede de segurança para ChatTypes adicionados por patches futuros do FFXIV que o plugin ainda não conhece. O padrão é DESATIVADO (minimização de dados). Ative se quiser que canais futuros também sejam registrados por completo.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Aplicar filtro ao banco de dados existente</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>O filtro de privacidade afeta apenas novas mensagens. A limpeza abaixo permite remover retroativamente mensagens já armazenadas que não correspondem à sua lista de permissões salva.</value> <value>O filtro de privacidade afeta apenas novas mensagens. A limpeza abaixo permite remover retroativamente mensagens já armazenadas que não correspondem à sua lista de permissões salva.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>A limpeza usa sua lista de permissões SALVA (Plugin.Config), não as alterações não salvas acima. Clique em Salvar primeiro se quiser que suas alterações atuais sejam aplicadas.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>A execução manual usa sua política de retenção SALVA, não os valores dos controles acima. Clique em Salvar primeiro se quiser que a execução aplique suas alterações atuais.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>A prévia está desatualizada: sua lista de permissões mudou desde a última atualização. Clique em Atualizar para recalcular.</value> <value>A prévia está desatualizada: sua lista de permissões mudou desde a última atualização. Clique em Atualizar para recalcular.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Aplicar retenção agora</value> <value>Aplicar retenção agora</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Executa a limpeza de retenção imediatamente usando a política SALVA. Salve suas alterações primeiro.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Limpeza de retenção em execução em segundo plano…</value> <value>Limpeza de retenção em execução em segundo plano…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Visual</value> <value>Visual</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Carregar sessão anterior ao iniciar</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Aplicar filtros às mensagens de sessões anteriores</value> <value>Aplicar filtros às mensagens de sessões anteriores</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Configurações → Hellion Chat para ajustar depois</value> <value>Configurações → Hellion Chat para ajustar depois</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Exportar (GDPR Art. 15 — Direito de acesso)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exporte mensagens armazenadas como Markdown, JSON ou CSV. Isso permite atender a uma solicitação de acesso de uma pessoa cujas mensagens você armazenou, ou levar seu próprio histórico com você.</value> <value>Exporte mensagens armazenadas como Markdown, JSON ou CSV. Isso permite atender a uma solicitação de acesso de uma pessoa cujas mensagens você armazenou, ou levar seu próprio histórico com você.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Tradutores da comunidade do Chat 2 (upstream)</value> <value>Tradutores da comunidade do Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Tells ativos</value> <value>Tells ativos</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fixado: sobrevive ao relog.</value> <value>Fixado: sobrevive ao relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Atenção: se o XIV Messenger ou um plugin similar suprimir tells, desative a opção "Suppress DMs" nele para que o Hellion Chat possa receber tells e abrir as abas automáticas.</value> <value>Atenção: se o XIV Messenger ou um plugin similar suprimir tells, desative a opção "Suppress DMs" nele para que o Hellion Chat possa receber tells e abrir as abas automáticas.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Histórico de tell nas abas automáticas</value> <value>Histórico de tell nas abas automáticas</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Só tem efeito quando as abas de tell automáticas estão ativadas na aba Chat.</value> <value>Só tem efeito quando as abas de tell automáticas estão ativadas na aba Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Configurações reestruturadas</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>O Hellion Chat 0.5.0 reestruturou as configurações em abas temáticas. Seu banco de dados de chat e histórico de mensagens permanecem inalterados. As configurações foram redefinidas para os padrões. Se quiser selecionar seu perfil de privacidade novamente, o botão Reabrir está na aba Privacidade. Um backup da configuração anterior está em HellionChat.json.pre-v10-backup ao lado do arquivo de configuração ativo.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Geral</value> <value>Geral</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Sobre</value> <value>Sobre</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Timestamps</value> <value>Timestamps</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Moldura da janela</value> <value>Moldura da janela</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Mostrar botão de seleção de símbolos ao lado da entrada de chat</value> <value>Mostrar botão de seleção de símbolos ao lado da entrada de chat</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Adiciona um pequeno botão à esquerda do indicador de canal que abre um popup com ícones do FFXIV e uma lista de símbolos selecionados. Desative se preferir uma barra de entrada mais simples.</value> <value>Adiciona um pequeno botão à esquerda do indicador de canal que abre um popup com ícones do FFXIV e uma lista de símbolos selecionados. Desative se preferir uma barra de entrada mais simples.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Armazenamento</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Visão geral</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Manutenção</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value> <value>Sistema</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Se você usa várias linkshells, o mantenedor recomenda uma aba por shell para uma visão mais organizada. Duplique a aba e restrinja a seleção de canais em cada cópia.</value> <value>Se você usa várias linkshells, o mantenedor recomenda uma aba por shell para uma visão mais organizada. Duplique a aba e restrinja a seleção de canais em cada cópia.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Ícone da aba</value> <value>Ícone da aba</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Move a janela de chat e todos os pop-outs ativos de volta para o canto superior esquerdo do monitor principal. Útil quando uma janela foi parar fora da área visível após uma mudança de layout de tela (monitor desconectado, resolução alterada). O plugin também realiza uma verificação automática de limites uma vez por sessão; este botão é a saída manual caso algo ainda fique inacessível.</value> <value>Move a janela de chat e todos os pop-outs ativos de volta para o canto superior esquerdo do monitor principal. Útil quando uma janela foi parar fora da área visível após uma mudança de layout de tela (monitor desconectado, resolução alterada). O plugin também realiza uma verificação automática de limites uma vez por sessão; este botão é a saída manual caso algo ainda fique inacessível.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Novidade na v0.6.0: agora você pode digitar diretamente nos pop-outs. Ative a chave mestra nas configurações de Janela.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Entendi</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Abrir configurações de janela</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Você pode abrir qualquer aba de chat como sua própria janela. Clique no ícone de janela no canto superior direito ou clique com o botão direito na aba. Novidade na v0.6.1: a entrada em pop-out está ativa por padrão (pode ser desativada em Configurações → Janela).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Entendi</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Abrir configurações</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>O Hellion Chat não pode iniciar enquanto o Chat 2 estiver carregado.</value> <value>O Hellion Chat não pode iniciar enquanto o Chat 2 estiver carregado.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Desative o Chat 2 em /xlplugins e depois reative o Hellion Chat.</value> <value>Desative o Chat 2 em /xlplugins e depois reative o Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Geral</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Idioma, entrada, áudio e performance.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Aparência</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Opacidade da janela, fontes, animação</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Escolha um tema ou importe o seu</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Janela</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Quando a janela é visível e se ela pode ser movida.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, prévia, comportamento de mensagens e emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Abas</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Crie e configure abas de chat personalizadas.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Banco de Dados</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Armazenamento, migração, limpeza legada</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Sobre</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensões, versão, informações do projeto, tradutores e changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Manter</value> <value>Manter</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Privacidade primeiro</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Aberto</value> <value>Aberto</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Dados e privacidade</value> <value>Dados e privacidade</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtro de privacidade, retenção, limpeza, exportação e estatísticas do banco de dados.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avançado (Shift+clique para abrir)</value> <value>Avançado (Shift+clique para abrir)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>O Hellion Chat 1.2.1 reorganizou o menu de configurações e removeu a antiga opção "Substituir estilo" (substituída pelo sistema de temas da versão 1.1.0). Suas demais configurações permanecem inalteradas. A transparência da janela foi migrada para "Theme &amp; Layout". Um backup da configuração anterior está em pluginConfigs/HellionChat.json.pre-v16-backup ao lado do HellionChat.json ativo.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>As integrações de plugin permitem que o HellionChat trabalhe junto com outros plugins Dalamud instalados. Cada integração detecta automaticamente seu alvo e se desativa silenciosamente quando o plugin alvo está ausente.</value> <value>As integrações de plugin permitem que o HellionChat trabalhe junto com outros plugins Dalamud instalados. Cada integração detecta automaticamente seu alvo e se desativa silenciosamente quando o plugin alvo está ausente.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Entrada</value> <value>Entrada</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Esta mensagem contém símbolos exclusivos do plugin que outros jogadores podem ver como caixas vazias. Pressione Enter novamente para enviar mesmo assim.</value> <value>Esta mensagem contém símbolos exclusivos do plugin que outros jogadores podem ver como caixas vazias. Pressione Enter novamente para enviar mesmo assim.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Inserir símbolo</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Configurações</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Ocultar o chat (Enter para trazer de volta)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Devolver esta aba à janela principal</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Outra operação de banco de dados está em andamento: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>limpeza de retenção</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>exportação</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>limpeza</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>exclusão do histórico</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>O filtro de privacidade está desligado, então todos os canais são salvos e nada no banco de dados contradiz suas configurações. Ligue o filtro e escolha os canais primeiro.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Nenhum canal está selecionado, então uma limpeza apagaria todo o histórico. Escolha os canais que quer manter, ou use o botão de apagar se realmente quiser remover tudo.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Há {0:N0} mensagens salvas. Se quiser manter uma cópia, exporte-as antes de apagar.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: executa a limpeza de retenção agora em vez de esperar a varredura diária. Apaga mensagens mais antigas que os limites acima.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Falha ao apagar o histórico. Nada foi removido, veja /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetria</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Nenhuma telemetria é coletada. O plugin não envia nada sobre você ou seu uso para lugar nenhum.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Tradução automática</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Bloquear</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Ir para a mensagem mais recente</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Inserir marcação do mapa &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Inserir item vinculado &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>desativado</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Comportamento</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Atalhos de teclado</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notificações</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Modos de exibição</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Histórico</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Ajuda de comandos</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Aviso de plugin</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Modo de layout</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacidade</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Comportamento de redimensionamento</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Abertura automática de tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marca</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Links</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrações</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Créditos</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licença</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Clique em um botão e depois pressione a combinação de teclas. Esc limpa.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Ir para a próxima aba</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Ir para a aba anterior</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Trocar reconstrói o atlas de fontes, então o chat fica vazio por um instante.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Relógio de 24 horas</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Mostrar histórico de sessões anteriores</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Desligado, o registro começa vazio a cada início do jogo e só se enche com as mensagens recebidas a partir daí.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Lado da ajuda de comandos</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>De que lado a lista de sugestões aparece enquanto você digita.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Modo de abertura automática de tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Onde um tell abre quando chega.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Mudar para a aba a cada tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Caso contrário, a aba abre em segundo plano após o primeiro.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Abas no topo</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Posição das abas</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Onde a lista de abas fica na janela principal.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Mostrar barra de título</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Mostrar barra de título nas janelas destacadas</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Permitir mover</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Permitir redimensionar</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Limite de troca automática da barra lateral</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Abaixo desta largura a barra lateral vira abas no topo, em pixels.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Posição da pré-visualização</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Mostrar a pré-visualização apenas ao digitar</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Repositório Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifesto do repositório personalizado</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Tema ativo: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Bifurcar e editar</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Temas integrados não podem ser editados diretamente. Bifurcar cria uma cópia personalizada que você pode editar e salvar.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Editar tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Editando: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Superfícies</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Bordas</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Texto</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identidade</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Salvar</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Cancelar</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Restaurar para o original</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Não é possível restaurar enquanto edita uma bifurcação. Salve ou cancele primeiro.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Salve ou descarte suas alterações primeiro</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Personalizados ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Bifurcar o tema ativo</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importar arquivo de tema…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Caminho do arquivo JSON (ou arraste para a pasta)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exportar tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Frios</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturais</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Clássicos</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retrô</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (incluída)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Fonte do jogo</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Ativa: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Digite uma mensagem...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplicar</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>pré-visualização</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>manutenção</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} aba</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} abas</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} sussurro</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} sussurros</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} msg</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k msg</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Campeão» Pré-visualização</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>aberto</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Rede de segurança para ChatTypes adicionados por futuras atualizações de FFXIV que o plugin ainda não reconhece. A predefinição é DESATIVADO (minimização de dados). Ativa se quiseres que futuros canais sejam também registados na íntegra.</value> <value>Rede de segurança para ChatTypes adicionados por futuras atualizações de FFXIV que o plugin ainda não reconhece. A predefinição é DESATIVADO (minimização de dados). Ativa se quiseres que futuros canais sejam também registados na íntegra.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Aplicar filtro à base de dados existente</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>O filtro de privacidade só afeta novas mensagens. A limpeza abaixo permite remover retroativamente mensagens já armazenadas que não correspondem à tua lista de permissões guardada.</value> <value>O filtro de privacidade só afeta novas mensagens. A limpeza abaixo permite remover retroativamente mensagens já armazenadas que não correspondem à tua lista de permissões guardada.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>A limpeza usa a tua lista de permissões GUARDADA (Plugin.Config), não as alterações por guardar acima. Clica em Guardar primeiro se quiseres que as tuas alterações atuais sejam aplicadas.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>A execução manual usa a tua política de retenção GUARDADA, não os valores do cursor acima. Clica em Guardar primeiro se quiseres que a execução aplique as tuas alterações atuais.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>A pré-visualização está desatualizada: a tua lista de permissões mudou desde a última atualização. Clica em Atualizar para recalcular.</value> <value>A pré-visualização está desatualizada: a tua lista de permissões mudou desde a última atualização. Clica em Atualizar para recalcular.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Aplicar retenção agora</value> <value>Aplicar retenção agora</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Executa a limpeza de retenção imediatamente com a política GUARDADA. Guarda as tuas alterações primeiro.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Limpeza de retenção a decorrer em segundo plano…</value> <value>Limpeza de retenção a decorrer em segundo plano…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Visual</value> <value>Visual</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Carregar sessão anterior no arranque</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Aplicar filtros a mensagens de sessões anteriores</value> <value>Aplicar filtros a mensagens de sessões anteriores</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Definições → Hellion Chat para ajustar mais tarde</value> <value>Definições → Hellion Chat para ajustar mais tarde</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Exportar (GDPR Art. 15 — Direito de acesso)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exporta mensagens armazenadas em Markdown, JSON ou CSV. Permite responder a um pedido de acesso de uma pessoa cujas mensagens guardaste, ou levar o teu próprio histórico contigo.</value> <value>Exporta mensagens armazenadas em Markdown, JSON ou CSV. Permite responder a um pedido de acesso de uma pessoa cujas mensagens guardaste, ou levar o teu próprio histórico contigo.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Tradutores da comunidade do Chat 2 (upstream)</value> <value>Tradutores da comunidade do Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Tells ativos</value> <value>Tells ativos</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fixado: sobrevive ao relog.</value> <value>Fixado: sobrevive ao relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Nota: Se o XIV Messenger ou um plugin semelhante suprimir tells, desativa a opção "Suppress DMs" lá para que o Hellion Chat possa receber tells e abrir os separadores automáticos.</value> <value>Nota: Se o XIV Messenger ou um plugin semelhante suprimir tells, desativa a opção "Suppress DMs" lá para que o Hellion Chat possa receber tells e abrir os separadores automáticos.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Histórico de tells em separadores automáticos</value> <value>Histórico de tells em separadores automáticos</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Só tem efeito quando os separadores de tell automáticos estão ativados no separador Chat.</value> <value>Só tem efeito quando os separadores de tell automáticos estão ativados no separador Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Definições reestruturadas</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>O Hellion Chat 0.5.0 reestruturou as definições em separadores temáticos. A tua base de dados de chat e o histórico de mensagens permanecem inalterados. As definições foram repostas para os valores predefinidos. Se quiseres voltar a selecionar o teu perfil de privacidade, o botão Reabrir está no separador Privacidade. Uma cópia de segurança da configuração anterior encontra-se em HellionChat.json.pre-v10-backup junto ao ficheiro de configuração ativo.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Geral</value> <value>Geral</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Sobre</value> <value>Sobre</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Marcas de tempo</value> <value>Marcas de tempo</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Moldura da janela</value> <value>Moldura da janela</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Mostrar botão do seletor de símbolos junto à entrada de chat</value> <value>Mostrar botão do seletor de símbolos junto à entrada de chat</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Adiciona um pequeno botão à esquerda do indicador de canal que abre uma janela pop-up com ícones de FFXIV e uma lista curada de símbolos. Desativa se preferires uma barra de entrada mais minimalista.</value> <value>Adiciona um pequeno botão à esquerda do indicador de canal que abre uma janela pop-up com ícones de FFXIV e uma lista curada de símbolos. Desativa se preferires uma barra de entrada mais minimalista.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Armazenamento</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Visão geral</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Manutenção</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value> <value>Sistema</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Se usares várias linkshells, o maintainer recomenda um separador por shell para uma visão geral mais limpa. Duplica o separador e restringe a seleção de canais em cada cópia.</value> <value>Se usares várias linkshells, o maintainer recomenda um separador por shell para uma visão geral mais limpa. Duplica o separador e restringe a seleção de canais em cada cópia.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Ícone do separador</value> <value>Ícone do separador</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Move a janela de chat e todas as janelas flutuantes ativas de volta para o canto superior esquerdo do monitor principal. Útil quando uma janela ficou fora da área visível após uma alteração de layout de ecrã (monitor desligado, resolução alterada). O plugin também faz uma verificação automática de limites uma vez por sessão; este botão é a saída manual caso algo continue inacessível.</value> <value>Move a janela de chat e todas as janelas flutuantes ativas de volta para o canto superior esquerdo do monitor principal. Útil quando uma janela ficou fora da área visível após uma alteração de layout de ecrã (monitor desligado, resolução alterada). O plugin também faz uma verificação automática de limites uma vez por sessão; este botão é a saída manual caso algo continue inacessível.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Novidade na v0.6.0: já podes escrever diretamente em janelas flutuantes. Ativa o interruptor principal nas definições de Janela.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Percebido</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Abrir definições de janela</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Podes abrir qualquer separador de chat como janela própria. Clica no ícone de janela no canto superior direito ou faz clique direito no separador. Novidade na v0.6.1: a entrada em janelas flutuantes está ativa por predefinição (pode ser desativada em Definições → Janela).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Percebido</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Abrir definições</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>O Hellion Chat não pode iniciar enquanto o Chat 2 estiver carregado.</value> <value>O Hellion Chat não pode iniciar enquanto o Chat 2 estiver carregado.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Desativa o Chat 2 em /xlplugins e volta a ativar o Hellion Chat.</value> <value>Desativa o Chat 2 em /xlplugins e volta a ativar o Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Geral</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Idioma, entrada, áudio e desempenho.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Aparência</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Opacidade da janela, tipos de letra, movimento</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Escolhe um theme ou importa o teu próprio</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Janela</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Quando a janela está visível e se pode ser movida.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, pré-visualização, comportamento de mensagens e emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Separadores</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Cria e configura separadores de chat personalizados.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Base de dados</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Armazenamento, migração, limpeza de dados antigos</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Sobre</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensões, versão, informações do projeto, tradutores e changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Manter</value> <value>Manter</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Privacidade primeiro</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Aberto</value> <value>Aberto</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Dados e privacidade</value> <value>Dados e privacidade</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtro de privacidade, retenção, limpeza, exportação e estatísticas da base de dados.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avançado (Shift+clique para abrir)</value> <value>Avançado (Shift+clique para abrir)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>O Hellion Chat 1.2.1 reorganizou o menu de definições e removeu a antiga opção "Substituir estilo" (substituída pelo sistema de themes a partir da versão 1.1.0). As tuas restantes definições ficam inalteradas. A transparência da janela foi migrada para "Theme &amp; Layout". Uma cópia de segurança da configuração anterior encontra-se em pluginConfigs/HellionChat.json.pre-v16-backup junto ao ficheiro HellionChat.json ativo.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>As integrações de plugins permitem que o HellionChat funcione em conjunto com outros plugins Dalamud instalados. Cada integração deteta automaticamente o seu alvo e desativa-se silenciosamente quando o plugin alvo está em falta.</value> <value>As integrações de plugins permitem que o HellionChat funcione em conjunto com outros plugins Dalamud instalados. Cada integração deteta automaticamente o seu alvo e desativa-se silenciosamente quando o plugin alvo está em falta.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Entrada</value> <value>Entrada</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Esta mensagem contém símbolos exclusivos do plugin que outros jogadores podem ver como caixas vazias. Pressiona Enter novamente para enviar mesmo assim.</value> <value>Esta mensagem contém símbolos exclusivos do plugin que outros jogadores podem ver como caixas vazias. Pressiona Enter novamente para enviar mesmo assim.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Inserir símbolo</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Definições</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Ocultar o chat (Enter para o repor)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Devolver este separador à janela principal</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Está em curso outra operação de base de dados: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>limpeza de retenção</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>exportação</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>limpeza</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>eliminação do histórico</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>O filtro de privacidade está desligado, por isso todos os canais são guardados e nada na base de dados contradiz as suas definições. Ligue primeiro o filtro e escolha os canais.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Não está selecionado nenhum canal, por isso uma limpeza apagaria todo o histórico. Escolha os canais que quer manter, ou use o botão de apagar se quiser mesmo remover tudo.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Estão guardadas {0:N0} mensagens. Se quiser manter uma cópia, exporte-as antes de apagar.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: executa a limpeza de retenção agora em vez de esperar a varredura diária. Apaga mensagens mais antigas do que os limites acima.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Falha ao apagar o histórico. Nada foi removido, consulte /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetria</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Não é recolhida qualquer telemetria. O plugin não envia nada sobre si ou sobre a sua utilização.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Tradução automática</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Bloquear</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Ir para a mensagem mais recente</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Inserir marcação do mapa &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Inserir item ligado &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>desativado</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Comportamento</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Atalhos de teclado</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notificações</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Modos de visualização</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Histórico</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Ajuda de comandos</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Aviso de plugin</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Modo de disposição</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacidade</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Comportamento de redimensionamento</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Abertura automática de tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marca</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Ligações</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrações</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Créditos</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licença</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Clique num botão e depois prima a combinação de teclas. Esc limpa.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Ir para o separador seguinte</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Ir para o separador anterior</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Mudar reconstrói o atlas de tipos de letra, por isso o chat fica vazio por um instante.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Relógio de 24 horas</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Mostrar histórico de sessões anteriores</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Desligado, o registo começa vazio a cada início do jogo e só se enche com as mensagens recebidas a partir daí.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Lado da ajuda de comandos</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>De que lado a lista de sugestões aparece enquanto escreve.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Modo de abertura automática de tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Onde um tell abre quando chega.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Mudar para o separador a cada tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Caso contrário, o separador abre em segundo plano após o primeiro.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Barra lateral</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Separadores no topo</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Posição dos separadores</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Onde a lista de separadores fica na janela principal.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Mostrar barra de título</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Mostrar barra de título nas janelas destacadas</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Permitir mover</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Permitir redimensionar</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Limite de troca automática da barra lateral</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Abaixo desta largura a barra lateral passa a separadores no topo, em píxeis.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Posição da pré-visualização</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Mostrar a pré-visualização apenas ao escrever</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Repositório Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifesto do repositório personalizado</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Tema ativo: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Bifurcar e editar</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Os temas integrados não podem ser editados diretamente. Bifurcar cria uma cópia personalizada que pode editar e guardar.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Editar tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>A editar: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Superfícies</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Contornos</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Texto</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identidade</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Estado</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Guardar</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Cancelar</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Repor para o original</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Não é possível repor enquanto edita uma bifurcação. Guarde ou cancele primeiro.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Guarde ou descarte primeiro as suas alterações</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Personalizados ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Bifurcar o tema ativo</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importar ficheiro de tema…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Caminho do ficheiro JSON (ou arraste para a pasta)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exportar tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Frios</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturais</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Clássicos</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (incluída)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Tipo de letra do jogo</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Ativo: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Escreva uma mensagem...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplicar</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>pré-visualização</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>manutenção</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} separador</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} separadores</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} sussurro</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} sussurros</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} msg</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k msg</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Campeão» Pré-visualização</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>aberto</value>
</data>
</root>
+334 -131
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Safety net for ChatTypes added by future FFXIV patches that the plugin does not yet know about. Default is OFF (data minimisation). Enable if you want future channels to be fully logged as well.</value> <value>Safety net for ChatTypes added by future FFXIV patches that the plugin does not yet know about. Default is OFF (data minimisation). Enable if you want future channels to be fully logged as well.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Apply filter to existing database</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>The privacy filter only affects new messages. The cleanup below lets you retroactively remove already-stored messages that do not match your saved whitelist.</value> <value>The privacy filter only affects new messages. The cleanup below lets you retroactively remove already-stored messages that do not match your saved whitelist.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Cleanup uses your SAVED whitelist (Plugin.Config), not unsaved changes above. Click Save first if you want your current changes to be applied.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>The manual run uses your SAVED retention policy, not the slider values above. Click Save first if you want the run to apply your current changes.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Preview is stale: your whitelist has changed since the last refresh. Click Refresh to recalculate.</value> <value>Preview is stale: your whitelist has changed since the last refresh. Click Refresh to recalculate.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Apply retention now</value> <value>Apply retention now</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Runs the retention cleanup immediately using the SAVED policy. Save your changes first.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Retention cleanup running in the background…</value> <value>Retention cleanup running in the background…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Visual</value> <value>Visual</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Load previous session on startup</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Apply filters to messages from previous sessions</value> <value>Apply filters to messages from previous sessions</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Settings → Hellion Chat to fine-tune later</value> <value>Settings → Hellion Chat to fine-tune later</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Export (GDPR Art. 15 — Right of access)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Export stored messages as Markdown, JSON, or CSV. This lets you fulfil an access request from a person whose messages you have stored, or take your own history with you.</value> <value>Export stored messages as Markdown, JSON, or CSV. This lets you fulfil an access request from a person whose messages you have stored, or take your own history with you.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 community translators (upstream)</value> <value>Chat 2 community translators (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Active tells</value> <value>Active tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Pinned: survives relog.</value> <value>Pinned: survives relog.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Note: If XIV Messenger or a similar plugin suppresses tells, disable the "Suppress DMs" option there so that Hellion Chat can receive tells and open the auto-tabs.</value> <value>Note: If XIV Messenger or a similar plugin suppresses tells, disable the "Suppress DMs" option there so that Hellion Chat can receive tells and open the auto-tabs.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell history in auto-tabs</value> <value>Tell history in auto-tabs</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Only takes effect when auto-tell tabs are enabled in the Chat tab.</value> <value>Only takes effect when auto-tell tabs are enabled in the Chat tab.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Settings restructured</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 has restructured the settings into thematic tabs. Your chat database and message history remain unchanged. Settings have been reset to defaults. If you want to re-select your privacy profile, the Reopen button is in the Privacy tab. A backup of the previous config is located at HellionChat.json.pre-v10-backup next to the active config file.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>General</value> <value>General</value>
</data> </data>
@@ -590,7 +566,7 @@
<value>About</value> <value>About</value>
</data> </data>
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -604,11 +580,11 @@
<value>Timestamps</value> <value>Timestamps</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings (pre-cycle legacy, kept for reference) -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Window frame</value> <value>Window frame</value>
</data> </data>
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Show symbol-picker button next to chat input</value> <value>Show symbol-picker button next to chat input</value>
</data> </data>
@@ -616,18 +592,9 @@
<value>Adds a small button left of the channel indicator that opens a popup with FFXIV icons and a curated symbol list. Disable if you prefer a leaner input bar.</value> <value>Adds a small button left of the channel indicator that opens a popup with FFXIV icons and a curated symbol list. Disable if you prefer a leaner input bar.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Storage</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Overview</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Maintenance</value>
</data>
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -647,7 +614,7 @@
<value>If you use multiple linkshells, the maintainer recommends one tab per shell for a cleaner overview. Duplicate the tab and restrict the channel selection in each copy.</value> <value>If you use multiple linkshells, the maintainer recommends one tab per shell for a cleaner overview. Duplicate the tab and restrict the channel selection in each copy.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Tab icon</value> <value>Tab icon</value>
</data> </data>
@@ -693,24 +660,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Moves the chat window and all active pop-outs back to the top-left corner of the primary monitor. Useful when a window has ended up outside the visible area after a display layout change (monitor disconnected, resolution changed). The plugin also performs an automatic bounds check once per session; this button is the manual escape hatch if something still ends up unreachable.</value> <value>Moves the chat window and all active pop-outs back to the top-left corner of the primary monitor. Useful when a window has ended up outside the visible area after a display layout change (monitor disconnected, resolution changed). The plugin also performs an automatic bounds check once per session; this button is the manual escape hatch if something still ends up unreachable.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>New in v0.6.0: You can now type directly in pop-outs. Enable the master switch in the Window settings.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Got it</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Open window settings</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>You can open any chat tab as its own window. Click the window icon in the top right or right-click the tab. New in v0.6.1: pop-out input is active by default (can be disabled under Settings → Window).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Got it</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Open settings</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat cannot start while Chat 2 is loaded.</value> <value>Hellion Chat cannot start while Chat 2 is loaded.</value>
</data> </data>
@@ -720,54 +669,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Disable Chat 2 in /xlplugins, then re-enable Hellion Chat.</value> <value>Disable Chat 2 in /xlplugins, then re-enable Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>General</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Language, input, audio, and performance.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Appearance</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Window opacity, fonts, motion</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Choose a theme or import your own</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Window</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>When the window is visible and whether it can be moved.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, preview, message behaviour, and emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Tabs</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Create and configure custom chat tabs.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Database</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Storage, migration, legacy cleanup</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>About</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensions, version, project info, translators, and changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -810,9 +711,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Data &amp; Privacy</value> <value>Data &amp; Privacy</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Privacy filter, retention, cleanup, export, and database statistics.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -849,9 +747,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Advanced (Shift+click to open)</value> <value>Advanced (Shift+click to open)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 has reorganised the settings menu and removed the old "Override style" option (superseded by the theme system from 1.1.0). Your remaining settings are unchanged. Window transparency has been migrated to "Theme &amp; Layout". A backup of the previous config is located at pluginConfigs/HellionChat.json.pre-v16-backup next to the active HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Plugin integrations let HellionChat work together with other installed Dalamud plugins. Each integration automatically detects its target and silently disables itself when the target plugin is missing.</value> <value>Plugin integrations let HellionChat work together with other installed Dalamud plugins. Each integration automatically detects its target and silently disables itself when the target plugin is missing.</value>
</data> </data>
@@ -961,7 +856,7 @@
<value>Disables the theme crossfade, the sidebar and card-row hover animations, and the unread-tab pulse. Theme switches and hover states apply instantly instead.</value> <value>Disables the theme crossfade, the sidebar and card-row hover animations, and the unread-tab pulse. Theme switches and hover states apply instantly instead.</value>
</data> </data>
<!-- Failed-tell notification -->
<data name="FailedTell_Notification_Generic" xml:space="preserve"> <data name="FailedTell_Notification_Generic" xml:space="preserve">
<value>A tell could not be delivered.</value> <value>A tell could not be delivered.</value>
</data> </data>
@@ -975,7 +870,7 @@
<value>Show a toast when a tell you sent could not be delivered (recipient offline, in an instance, or blocking you).</value> <value>Show a toast when a tell you sent could not be delivered (recipient offline, in an instance, or blocking you).</value>
</data> </data>
<!-- Per-tab notification sound -->
<data name="Tabs_NotificationSound_Enable_Name" xml:space="preserve"> <data name="Tabs_NotificationSound_Enable_Name" xml:space="preserve">
<value>Notification sound</value> <value>Notification sound</value>
</data> </data>
@@ -992,7 +887,7 @@
<value>Hellion sound</value> <value>Hellion sound</value>
</data> </data>
<!-- Scroll-to-bottom and item/flag linking -->
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve"> <data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Jump to the latest message</value> <value>Jump to the latest message</value>
</data> </data>
@@ -1003,7 +898,7 @@
<value>Insert linked item &lt;item&gt;</value> <value>Insert linked item &lt;item&gt;</value>
</data> </data>
<!-- v1.5.6: plugin-disclosure warning -->
<data name="Settings_Chat_NotifyPluginDisclosure_Name" xml:space="preserve"> <data name="Settings_Chat_NotifyPluginDisclosure_Name" xml:space="preserve">
<value>Warn before sending plugin-only symbols</value> <value>Warn before sending plugin-only symbols</value>
</data> </data>
@@ -1014,7 +909,7 @@
<value>This message contains plugin-only symbols that other players may see as empty boxes. Press Enter again to send anyway.</value> <value>This message contains plugin-only symbols that other players may see as empty boxes. Press Enter again to send anyway.</value>
</data> </data>
<!-- v1.5.6: world suffix + name format display options -->
<data name="Settings_Chat_WorldSuffix_Name" xml:space="preserve"> <data name="Settings_Chat_WorldSuffix_Name" xml:space="preserve">
<value>World suffix</value> <value>World suffix</value>
</data> </data>
@@ -1046,7 +941,7 @@
<value>Initials</value> <value>Initials</value>
</data> </data>
<!-- v1.5.6: inactive window opacity -->
<data name="Settings_ThemeAndLayout_WindowOpacityInactive_Name" xml:space="preserve"> <data name="Settings_ThemeAndLayout_WindowOpacityInactive_Name" xml:space="preserve">
<value>Inactive window opacity</value> <value>Inactive window opacity</value>
</data> </data>
@@ -1054,7 +949,7 @@
<value>Background opacity of the main chat window while it is not focused. The slider above sets the focused value. A per-window override in Dalamud's window pinning menu still takes precedence over both.</value> <value>Background opacity of the main chat window while it is not focused. The slider above sets the focused value. A per-window override in Dalamud's window pinning menu still takes precedence over both.</value>
</data> </data>
<!-- v1.5.6: custom sound volume -->
<data name="Settings_General_CustomSoundVolume_Name" xml:space="preserve"> <data name="Settings_General_CustomSoundVolume_Name" xml:space="preserve">
<value>Custom sound volume</value> <value>Custom sound volume</value>
</data> </data>
@@ -1062,7 +957,7 @@
<value>Playback volume for the three bundled custom notification sounds. Does not affect the 16 game sounds.</value> <value>Playback volume for the three bundled custom notification sounds. Does not affect the 16 game sounds.</value>
</data> </data>
<!-- v1.5.6: General tab section titles (collapsible, R6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Input</value> <value>Input</value>
</data> </data>
@@ -1079,7 +974,7 @@
<value>Which sound plays per tab is set in the Channels tab.</value> <value>Which sound plays per tab is set in the Channels tab.</value>
</data> </data>
<!-- v1.5.6: Chat tab section titles (collapsible, R6) -->
<data name="Settings_Section_Messages" xml:space="preserve"> <data name="Settings_Section_Messages" xml:space="preserve">
<value>Messages</value> <value>Messages</value>
</data> </data>
@@ -1099,7 +994,7 @@
<value>Novice network</value> <value>Novice network</value>
</data> </data>
<!-- v1.5.6: Appearance tab section titles (collapsible, R6) -->
<data name="Settings_Section_Theme" xml:space="preserve"> <data name="Settings_Section_Theme" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -1119,7 +1014,7 @@
<value>Animations</value> <value>Animations</value>
</data> </data>
<!-- v1.5.6: Window tab collapsible section titles (R6) -->
<data name="Settings_Section_Hide" xml:space="preserve"> <data name="Settings_Section_Hide" xml:space="preserve">
<value>Hide</value> <value>Hide</value>
</data> </data>
@@ -1130,7 +1025,7 @@
<value>Frame</value> <value>Frame</value>
</data> </data>
<!-- v1.5.6: Tabs tab per-tab-item sub-section titles (R6) -->
<data name="Settings_Section_Tab_Channels" xml:space="preserve"> <data name="Settings_Section_Tab_Channels" xml:space="preserve">
<value>Channels</value> <value>Channels</value>
</data> </data>
@@ -1150,7 +1045,7 @@
<value>This volume applies to all tabs.</value> <value>This volume applies to all tabs.</value>
</data> </data>
<!-- v1.5.6: About tab collapsible section titles (R6) -->
<data name="Settings_Section_Extensions" xml:space="preserve"> <data name="Settings_Section_Extensions" xml:space="preserve">
<value>Extensions</value> <value>Extensions</value>
</data> </data>
@@ -1166,4 +1061,312 @@
<data name="Settings_Section_Changelog" xml:space="preserve"> <data name="Settings_Section_Changelog" xml:space="preserve">
<value>Changelog</value> <value>Changelog</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Insert symbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Settings</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Hide chat (Enter to bring back)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Return this tab to the main window</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Another database operation is running: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>retention sweep</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>export</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>cleanup</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>clearing the history</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>The privacy filter is off, so every channel is stored and nothing in the database contradicts your settings. Switch the filter on and pick channels first.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>No channel is selected, so a cleanup would delete the entire history. Pick the channels you want to keep, or use the clear button if you really want everything gone.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0} messages are stored. If you want to keep a copy, export them before clearing.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: runs the retention cleanup right now instead of waiting for the daily sweep. Deletes messages older than the limits above.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Clearing the history failed. Nothing was removed, see /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetry</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>No telemetry is collected. The plugin sends nothing about you or your usage anywhere.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Auto-translate</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Block</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>off</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Behaviour</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Keybinds</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notifications</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Display modes</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>History</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Command help</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Plugin disclosure</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Layout mode</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacity</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Resize behaviour</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Tell auto-open</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Sidebar</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Brand</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Links</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrations</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Credits</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>License</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Click a button, then press the key combination. Esc clears.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Cycle to next chat tab</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Cycle to previous chat tab</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Switching rebuilds the font atlas, so the chat goes blank for a moment.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-hour clock</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Show history from previous sessions</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Off means the log starts empty each time the game launches and only fills with messages received since.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Command help side</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Which side the command hint list appears on while typing.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Tell auto-open mode</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Where a tell opens when it arrives.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Switch to the tab on every tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Otherwise the tab opens in the background after the first one.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Sidebar</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Top tabs</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Tab placement</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Where the tab list sits in the main window.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Show title bar</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Show title bar for pop-outs</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Allow movement</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Allow resize</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Sidebar auto-switch threshold</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Below this width the sidebar folds into top tabs, in pixels.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Preview position</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Only show preview when typing</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea repository</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Custom repo manifest</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Active theme: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Fork &amp; Edit</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Built-in themes cannot be edited in place. Fork creates a custom copy you can edit and save.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Edit theme</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Editing: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Surfaces</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Borders</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identity</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Reset to source</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Reset is unavailable while editing a fork. Save or Cancel first.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Save or discard your edits first</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Custom ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Fork active theme</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Import theme file…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Path to JSON file (or drag-and-drop into the folder)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Export theme</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Cool</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Natural</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Classic</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (bundled)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>FFXIV game font</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Active: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Type a message...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplicate</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>preview</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>maintenance</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} tab</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} tabs</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} tells</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} msg</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k msg</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Champion» Vorschau</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>open</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Plasă de siguranță pentru tipurile ChatType adăugate de patch-uri viitoare FFXIV pe care plugin-ul nu le cunoaște încă. Implicit este DEZACTIVAT (minimizarea datelor). Activează dacă vrei ca și canalele viitoare să fie înregistrate complet.</value> <value>Plasă de siguranță pentru tipurile ChatType adăugate de patch-uri viitoare FFXIV pe care plugin-ul nu le cunoaște încă. Implicit este DEZACTIVAT (minimizarea datelor). Activează dacă vrei ca și canalele viitoare să fie înregistrate complet.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Aplică filtrul pe baza de date existentă</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Filtrul de confidențialitate afectează doar mesajele noi. Curățarea de mai jos îți permite să elimini retroactiv mesajele deja stocate care nu corespund listei albe salvate.</value> <value>Filtrul de confidențialitate afectează doar mesajele noi. Curățarea de mai jos îți permite să elimini retroactiv mesajele deja stocate care nu corespund listei albe salvate.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Curățarea folosește lista albă SALVATĂ (Plugin.Config), nu modificările nesalvate de mai sus. Apasă Salvează întâi dacă vrei ca modificările curente să fie aplicate.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Rularea manuală folosește politica de retenție SALVATĂ, nu valorile sliderelor de mai sus. Apasă Salvează întâi dacă vrei ca rularea să aplice modificările curente.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Previzualizarea este depășită: lista albă s-a schimbat de la ultima reîmprospătare. Apasă Reîmprospătează pentru a recalcula.</value> <value>Previzualizarea este depășită: lista albă s-a schimbat de la ultima reîmprospătare. Apasă Reîmprospătează pentru a recalcula.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Aplică retenția acum</value> <value>Aplică retenția acum</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Rulează imediat curățarea de retenție folosind politica SALVATĂ. Salvează mai întâi modificările.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Curățare de retenție în desfășurare în fundal…</value> <value>Curățare de retenție în desfășurare în fundal…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Vizual</value> <value>Vizual</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Încarcă sesiunea anterioară la pornire</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Aplică filtrele și pe mesajele din sesiunile anterioare</value> <value>Aplică filtrele și pe mesajele din sesiunile anterioare</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Setări → Hellion Chat pentru ajustări ulterioare</value> <value>Setări → Hellion Chat pentru ajustări ulterioare</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Export (GDPR Art. 15 — Dreptul de acces)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exportă mesajele stocate ca Markdown, JSON sau CSV. Acest lucru îți permite să onorezi o cerere de acces din partea unei persoane ale cărei mesaje le-ai stocat, sau să îți iei propriul istoric cu tine.</value> <value>Exportă mesajele stocate ca Markdown, JSON sau CSV. Acest lucru îți permite să onorezi o cerere de acces din partea unei persoane ale cărei mesaje le-ai stocat, sau să îți iei propriul istoric cu tine.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Traducători comunitate Chat 2 (upstream)</value> <value>Traducători comunitate Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Tell-uri active</value> <value>Tell-uri active</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fixat: supraviețuiește relog-ului.</value> <value>Fixat: supraviețuiește relog-ului.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Notă: Dacă XIV Messenger sau un plugin similar suprimă tell-urile, dezactivează opțiunea „Suppress DMs" de acolo pentru ca Hellion Chat să poată primi tell-uri și să deschidă tab-urile automate.</value> <value>Notă: Dacă XIV Messenger sau un plugin similar suprimă tell-urile, dezactivează opțiunea „Suppress DMs" de acolo pentru ca Hellion Chat să poată primi tell-uri și să deschidă tab-urile automate.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Istoric tell în tab-uri automate</value> <value>Istoric tell în tab-uri automate</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Intră în vigoare doar când tab-urile auto-tell sunt activate în tab-ul Chat.</value> <value>Intră în vigoare doar când tab-urile auto-tell sunt activate în tab-ul Chat.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Setări restructurate</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 a restructurat setările în tab-uri tematice. Baza ta de date de chat și istoricul mesajelor rămân nemodificate. Setările au fost resetate la valorile implicite. Dacă vrei să reselecți profilul de confidențialitate, butonul Redeschide se află în tab-ul Confidențialitate. O copie de rezervă a configurației anterioare se află la HellionChat.json.pre-v10-backup lângă fișierul de configurare activ.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>General</value> <value>General</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Despre</value> <value>Despre</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Temă</value> <value>Temă</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Marcaje de timp</value> <value>Marcaje de timp</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Cadru fereastră</value> <value>Cadru fereastră</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Arată butonul selector de simboluri lângă câmpul de chat</value> <value>Arată butonul selector de simboluri lângă câmpul de chat</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Adaugă un buton mic în stânga indicatorului de canal care deschide un popup cu icoane FFXIV și o listă de simboluri curată. Dezactivează dacă preferi o bară de introducere mai simplă.</value> <value>Adaugă un buton mic în stânga indicatorului de canal care deschide un popup cu icoane FFXIV și o listă de simboluri curată. Dezactivează dacă preferi o bară de introducere mai simplă.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Stocare</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Prezentare generală</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Întreținere</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Dacă folosești mai multe linkshell-uri, maintainer-ul recomandă un tab per shell pentru o prezentare mai clară. Duplică tab-ul și restrânge selecția de canal în fiecare copie.</value> <value>Dacă folosești mai multe linkshell-uri, maintainer-ul recomandă un tab per shell pentru o prezentare mai clară. Duplică tab-ul și restrânge selecția de canal în fiecare copie.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Icoană tab</value> <value>Icoană tab</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Mută fereastra de chat și toate pop-out-urile active înapoi în colțul din stânga sus al monitorului principal. Util când o fereastră a ajuns în afara zonei vizibile după o schimbare a configurației de afișaj (monitor deconectat, rezoluție schimbată). Plugin-ul efectuează și o verificare automată a limitelor o dată per sesiune; acest buton este ieșirea manuală de urgență dacă ceva tot rămâne inaccesibil.</value> <value>Mută fereastra de chat și toate pop-out-urile active înapoi în colțul din stânga sus al monitorului principal. Util când o fereastră a ajuns în afara zonei vizibile după o schimbare a configurației de afișaj (monitor deconectat, rezoluție schimbată). Plugin-ul efectuează și o verificare automată a limitelor o dată per sesiune; acest buton este ieșirea manuală de urgență dacă ceva tot rămâne inaccesibil.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Nou în v0.6.0: Poți scrie direct în pop-out-uri. Activează comutatorul principal din setările Fereastră.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Am înțeles</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Deschide setările ferestrei</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Poți deschide orice tab de chat ca propria fereastră. Apasă pe icoana de fereastră din dreapta sus sau clic dreapta pe tab. Nou în v0.6.1: introducerea în pop-out este activă implicit (poate fi dezactivată din Setări → Fereastră).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Am înțeles</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Deschide setările</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat nu poate porni cât timp Chat 2 este încărcat.</value> <value>Hellion Chat nu poate porni cât timp Chat 2 este încărcat.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Dezactivează Chat 2 în /xlplugins, apoi reactivează Hellion Chat.</value> <value>Dezactivează Chat 2 în /xlplugins, apoi reactivează Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>General</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Limbă, introducere, audio și performanță.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Aspect</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Opacitate fereastră, fonturi, mișcare</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Teme</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Alege o temă sau importă propria ta temă</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Fereastră</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Când fereastra este vizibilă și dacă poate fi mutată.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tell-uri, previzualizare, comportament mesaje și emote-uri.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Tab-uri</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Creează și configurează tab-uri de chat personalizate.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Bază de date</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Stocare, migrare, curățare veche</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Despre</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Extensii, versiune, informații despre proiect, traducători și changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Teme</value> <value>Teme</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Păstrează</value> <value>Păstrează</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Confidențialitate întâi</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Deschis</value> <value>Deschis</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Date și confidențialitate</value> <value>Date și confidențialitate</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Filtru de confidențialitate, retenție, curățare, export și statistici bază de date.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Temă</value> <value>Temă</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avansat (Shift+clic pentru deschidere)</value> <value>Avansat (Shift+clic pentru deschidere)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 a reorganizat meniul de setări și a eliminat vechea opțiune „Override style" (înlocuită de sistemul de teme din 1.1.0). Setările tale rămase sunt nemodificate. Transparența ferestrei a fost migrată la „Temă &amp; aspect". O copie de rezervă a configurației anterioare se află la pluginConfigs/HellionChat.json.pre-v16-backup lângă fișierul activ HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Integrările de plugin-uri permit HellionChat să colaboreze cu alte plugin-uri Dalamud instalate. Fiecare integrare își detectează automat ținta și se dezactivează în liniște când plugin-ul țintă lipsește.</value> <value>Integrările de plugin-uri permit HellionChat să colaboreze cu alte plugin-uri Dalamud instalate. Fiecare integrare își detectează automat ținta și se dezactivează în liniște când plugin-ul țintă lipsește.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Introducere</value> <value>Introducere</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Acest mesaj conține simboluri exclusiv plugin pe care alți jucători le-ar putea vedea ca casete goale. Apasă Enter din nou pentru a trimite oricum.</value> <value>Acest mesaj conține simboluri exclusiv plugin pe care alți jucători le-ar putea vedea ca casete goale. Apasă Enter din nou pentru a trimite oricum.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Inserează un simbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Setări</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Ascunde chatul (Enter îl readuce)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Readu această filă în fereastra principală</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Rulează deja o altă operațiune pe baza de date: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>curățarea după perioada de păstrare</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>export</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>curățare</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>ștergerea istoricului</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Filtrul de confidențialitate este oprit, deci fiecare canal este salvat și nimic din baza de date nu contrazice setările tale. Pornește mai întâi filtrul și alege canalele.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Niciun canal nu este selectat, deci o curățare ar șterge tot istoricul. Alege canalele pe care vrei să le păstrezi sau folosește butonul de ștergere dacă chiar vrei să dispară tot.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Sunt salvate {0:N0} mesaje. Dacă vrei să păstrezi o copie, exportă-le înainte de ștergere.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: rulează curățarea acum, în loc să aștepte trecerea zilnică. Șterge mesajele mai vechi decât limitele de mai sus.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Ștergerea istoricului a eșuat. Nu a fost eliminat nimic, vezi /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetrie</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Nu se colectează telemetrie. Pluginul nu trimite nimic despre tine sau despre utilizarea ta nicăieri.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Traducere automată</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Blochează</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Salt la cel mai recent mesaj</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Inserează marcajul hărții &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Inserează obiectul legat &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>dezactivat</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Comportament</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Scurtături</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Notificări</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Moduri de afișare</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Istoric</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Ajutor pentru comenzi</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Notificare privind pluginul</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Mod de aranjare</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacitate</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Comportament la redimensionare</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Deschidere automată a tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Bară laterală</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marcă</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Linkuri</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrări</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Credite</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licență</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Fă clic pe un buton, apoi apasă combinația de taste. Esc șterge.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Treci la tab-ul următor</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Treci la tab-ul anterior</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Schimbarea reconstruiește atlasul de fonturi, deci chatul rămâne gol o clipă.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>Ceas de 24 de ore</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Afișează istoricul din sesiunile anterioare</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Dezactivat, jurnalul pornește gol la fiecare lansare a jocului și se umple doar cu mesajele primite ulterior.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Partea ajutorului pentru comenzi</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>În ce parte apare lista de sugestii în timp ce scrii.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Mod de deschidere automată a tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Unde se deschide un tell când sosește.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Comută pe tab la fiecare tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Altfel tab-ul se deschide în fundal după primul.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Bară laterală</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Tab-uri sus</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Poziția tab-urilor</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Unde stă lista de tab-uri în fereastra principală.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Afișează bara de titlu</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Afișează bara de titlu la ferestrele desprinse</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Permite mutarea</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Permite redimensionarea</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Pragul de comutare automată a barei laterale</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Sub această lățime bara laterală se pliază în tab-uri sus, în pixeli.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Poziția previzualizării</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Afișează previzualizarea doar în timp ce scrii</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Depozit Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest depozit personalizat</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Tema activă: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Ramifică și editează</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Temele integrate nu pot fi editate direct. Ramificarea creează o copie personalizată pe care o poți edita și salva.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Editează tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Se editează: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Suprafețe</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Margini</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identitate</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Stare</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Salvează</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Anulează</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Resetează la sursă</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Resetarea nu este disponibilă în timpul editării unei ramificații. Salvează sau anulează mai întâi.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Salvează sau renunță mai întâi la modificări</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Personalizate ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Ramifică tema activă</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importă fișier de temă…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Calea către fișierul JSON (sau trage-l în folder)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exportă tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Reci</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturale</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Clasice</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (inclusă)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Fontul jocului</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Activ: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Scrie un mesaj...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplică</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>previzualizare</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>întreținere</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} tab</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} tab-uri</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} tell-uri</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} mes.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k mes.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Campion» Previzualizare</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>deschis</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Страховочная сеть для типов ChatType, добавленных в будущих патчах FFXIV, которые плагин ещё не знает. По умолчанию ВЫКЛ (минимизация данных). Включите, если хотите, чтобы будущие каналы тоже полностью логировались.</value> <value>Страховочная сеть для типов ChatType, добавленных в будущих патчах FFXIV, которые плагин ещё не знает. По умолчанию ВЫКЛ (минимизация данных). Включите, если хотите, чтобы будущие каналы тоже полностью логировались.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Применить фильтр к существующей базе данных</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Фильтр конфиденциальности действует только на новые сообщения. Очистка ниже позволяет ретроактивно удалить уже сохранённые сообщения, не соответствующие вашему сохранённому белому списку.</value> <value>Фильтр конфиденциальности действует только на новые сообщения. Очистка ниже позволяет ретроактивно удалить уже сохранённые сообщения, не соответствующие вашему сохранённому белому списку.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Очистка использует СОХРАНЁННЫЙ белый список (Plugin.Config), а не несохранённые изменения выше. Нажмите «Сохранить» сначала, если хотите применить текущие изменения.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Ручной запуск использует СОХРАНЁННУЮ политику хранения, а не значения слайдеров выше. Нажмите «Сохранить» сначала, если хотите применить текущие изменения.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Предпросмотр устарел — ваш белый список изменился с момента последнего обновления. Нажмите «Обновить» для пересчёта.</value> <value>Предпросмотр устарел — ваш белый список изменился с момента последнего обновления. Нажмите «Обновить» для пересчёта.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Применить хранение сейчас</value> <value>Применить хранение сейчас</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Немедленно запускает очистку по хранению, используя СОХРАНЁННУЮ политику. Сначала сохраните изменения.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Очистка по хранению выполняется в фоне…</value> <value>Очистка по хранению выполняется в фоне…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Оформление</value> <value>Оформление</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Загружать предыдущую сессию при запуске</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Применять фильтры к сообщениям из предыдущих сессий</value> <value>Применять фильтры к сообщениям из предыдущих сессий</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Настройки → Hellion Chat для тонкой настройки позже</value> <value>Настройки → Hellion Chat для тонкой настройки позже</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Экспорт (GDPR Art. 15 — Право на доступ)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Экспортируйте сохранённые сообщения в формате Markdown, JSON или CSV. Это позволяет выполнить запрос на доступ от лица, чьи сообщения вы сохранили, или забрать собственную историю.</value> <value>Экспортируйте сохранённые сообщения в формате Markdown, JSON или CSV. Это позволяет выполнить запрос на доступ от лица, чьи сообщения вы сохранили, или забрать собственную историю.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Переводчики сообщества Chat 2 (upstream)</value> <value>Переводчики сообщества Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Активные ЛС</value> <value>Активные ЛС</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Закреплено — переживает релог.</value> <value>Закреплено — переживает релог.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Примечание: если XIV Messenger или похожий плагин блокирует ЛС, отключите там опцию «Suppress DMs», чтобы Hellion Chat мог получать ЛС и открывать авто-вкладки.</value> <value>Примечание: если XIV Messenger или похожий плагин блокирует ЛС, отключите там опцию «Suppress DMs», чтобы Hellion Chat мог получать ЛС и открывать авто-вкладки.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>История ЛС в авто-вкладках</value> <value>История ЛС в авто-вкладках</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Действует только если авто-вкладки ЛС включены на вкладке Чат.</value> <value>Действует только если авто-вкладки ЛС включены на вкладке Чат.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Настройки реструктурированы</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 реструктурировал настройки по тематическим вкладкам. Ваша база данных чата и история сообщений остались без изменений. Настройки сброшены к значениям по умолчанию. Если вы хотите повторно выбрать профиль конфиденциальности, кнопка «Открыть снова» находится на вкладке Конфиденциальность. Резервная копия предыдущей конфигурации расположена по адресу HellionChat.json.pre-v10-backup рядом с активным файлом конфигурации.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Общие</value> <value>Общие</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>О программе</value> <value>О программе</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Временны́е метки</value> <value>Временны́е метки</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Рамка окна</value> <value>Рамка окна</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Показывать кнопку выбора символов рядом с полем ввода чата</value> <value>Показывать кнопку выбора символов рядом с полем ввода чата</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Добавляет небольшую кнопку слева от индикатора канала, открывающую всплывающее окно с иконками FFXIV и подобранным списком символов. Отключите для более компактной строки ввода.</value> <value>Добавляет небольшую кнопку слева от индикатора канала, открывающую всплывающее окно с иконками FFXIV и подобранным списком символов. Отключите для более компактной строки ввода.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Хранение</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Обзор</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Обслуживание</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Система</value> <value>Система</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Если вы используете несколько Linkshell, разработчик рекомендует создать отдельную вкладку для каждой — для более удобного обзора. Продублируйте вкладку и ограничьте выбор каналов в каждой копии.</value> <value>Если вы используете несколько Linkshell, разработчик рекомендует создать отдельную вкладку для каждой — для более удобного обзора. Продублируйте вкладку и ограничьте выбор каналов в каждой копии.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Иконка вкладки</value> <value>Иконка вкладки</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Возвращает окно чата и все активные всплывающие окна в верхний левый угол основного монитора. Полезно, когда окно оказалось за пределами видимой области после изменения раскладки дисплея (отключён монитор, изменено разрешение). Плагин также выполняет автоматическую проверку границ один раз за сессию; эта кнопка — ручной аварийный выход, если что-то всё равно оказалось недоступным.</value> <value>Возвращает окно чата и все активные всплывающие окна в верхний левый угол основного монитора. Полезно, когда окно оказалось за пределами видимой области после изменения раскладки дисплея (отключён монитор, изменено разрешение). Плагин также выполняет автоматическую проверку границ один раз за сессию; эта кнопка — ручной аварийный выход, если что-то всё равно оказалось недоступным.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Новое в v0.6.0: теперь можно вводить текст прямо во всплывающих окнах. Включите главный переключатель в настройках окна.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Понятно</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Открыть настройки окна</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Любую вкладку чата можно открыть в собственном окне. Нажмите иконку окна в правом верхнем углу или кликните правой кнопкой мыши по вкладке. Новое в v0.6.1: ввод во всплывающих окнах активен по умолчанию (можно отключить в разделе Настройки → Окно).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Понятно</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Открыть настройки</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat не может запуститься, пока загружен Chat 2.</value> <value>Hellion Chat не может запуститься, пока загружен Chat 2.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Отключите Chat 2 в /xlplugins, затем снова включите Hellion Chat.</value> <value>Отключите Chat 2 в /xlplugins, затем снова включите Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Общие</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Язык, ввод, звук и производительность.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Внешний вид</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Прозрачность окна, шрифты, анимации</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Темы</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Выберите тему или импортируйте свою</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Окно</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Когда окно видимо и можно ли его перемещать.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Чат</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>ЛС, предпросмотр, поведение сообщений и эмоции.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Вкладки</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Создание и настройка пользовательских вкладок чата.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>База данных</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Хранение, миграция, очистка устаревшего</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>О плагине</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Расширения, версия, информация о проекте, переводчики и changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Темы</value> <value>Темы</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Оставить</value> <value>Оставить</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Приватность прежде всего</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Открыто</value> <value>Открыто</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Данные и конфиденциальность</value> <value>Данные и конфиденциальность</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Фильтр конфиденциальности, хранение, очистка, экспорт и статистика базы данных.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Дополнительно (Shift+клик для открытия)</value> <value>Дополнительно (Shift+клик для открытия)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 реорганизовал меню настроек и удалил устаревшую опцию «Override style» (заменена системой тем из версии 1.1.0). Остальные настройки не изменились. Прозрачность окна перенесена в раздел «Theme &amp; Layout». Резервная копия предыдущей конфигурации расположена по адресу pluginConfigs/HellionChat.json.pre-v16-backup рядом с активным файлом HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Интеграции плагинов позволяют HellionChat работать совместно с другими установленными плагинами Dalamud. Каждая интеграция автоматически определяет целевой плагин и тихо отключается, если тот не установлен.</value> <value>Интеграции плагинов позволяют HellionChat работать совместно с другими установленными плагинами Dalamud. Каждая интеграция автоматически определяет целевой плагин и тихо отключается, если тот не установлен.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Ввод</value> <value>Ввод</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Это сообщение содержит символы плагина, которые другие игроки могут видеть как пустые квадраты. Нажмите Enter ещё раз, чтобы всё равно отправить.</value> <value>Это сообщение содержит символы плагина, которые другие игроки могут видеть как пустые квадраты. Нажмите Enter ещё раз, чтобы всё равно отправить.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Вставить символ</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Настройки</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Скрыть чат (Enter вернёт его)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Вернуть эту вкладку в главное окно</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Уже выполняется другая операция с базой данных: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>очистка по сроку хранения</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>экспорт</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>очистка</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>удаление истории</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Фильтр конфиденциальности выключен, поэтому сохраняются все каналы и ничто в базе данных не противоречит вашим настройкам. Сначала включите фильтр и выберите каналы.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Не выбран ни один канал, поэтому очистка удалит всю историю. Выберите каналы, которые хотите сохранить, или воспользуйтесь кнопкой удаления, если действительно хотите стереть всё.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Сохранено {0:N0} сообщений. Если хотите оставить копию, экспортируйте их перед удалением.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: выполняет очистку по сроку хранения сразу, не дожидаясь ежедневного прохода. Удаляет сообщения старше указанных выше пределов.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Не удалось удалить историю. Ничего не было удалено, см. /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Телеметрия</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Телеметрия не собирается. Плагин никуда не отправляет данные о вас или о вашем использовании.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Автоперевод</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Заблокировать</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Перейти к последнему сообщению</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Вставить метку карты &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Вставить связанный предмет &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>выключено</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Поведение</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Горячие клавиши</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Уведомления</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Режимы отображения</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>История</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Справка по командам</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Уведомление о плагине</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Режим компоновки</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Непрозрачность</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Изменение размера</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Автооткрытие tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Боковая панель</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Бренд</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Ссылки</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Интеграции</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Благодарности</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Лицензия</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Нажмите кнопку, затем нажмите сочетание клавиш. Esc очищает.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Перейти к следующей вкладке</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Перейти к предыдущей вкладке</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Переключение перестраивает атлас шрифтов, поэтому чат ненадолго пустеет.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-часовой формат</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Показывать историю прошлых сеансов</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Выключено: журнал начинается пустым при каждом запуске игры и заполняется только сообщениями, полученными после.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Сторона справки по командам</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>С какой стороны появляется список подсказок при вводе.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Режим автооткрытия tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Где открывается tell при получении.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Переключаться на вкладку при каждом tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Иначе вкладка после первого открывается в фоне.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Боковая панель</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Вкладки сверху</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Расположение вкладок</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Где в главном окне находится список вкладок.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Показывать заголовок окна</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Показывать заголовок в отдельных окнах</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Разрешить перемещение</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Разрешить изменение размера</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Порог автопереключения боковой панели</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Ниже этой ширины боковая панель сворачивается во вкладки сверху, в пикселях.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Положение предпросмотра</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Показывать предпросмотр только при вводе</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Репозиторий Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Манифест пользовательского репозитория</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Активная тема: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Ответвить и изменить</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Встроенные темы нельзя изменить напрямую. Ответвление создаёт собственную копию, которую можно изменить и сохранить.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Изменить тему</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Изменяется: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Поверхности</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Границы</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Текст</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Идентичность</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Состояние</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Сохранить</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Отмена</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Вернуть к исходной</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Сброс недоступен во время правки ответвления. Сначала сохраните или отмените.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Сначала сохраните или отмените изменения</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Свои ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Ответвить активную тему</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Импортировать файл темы…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Путь к файлу JSON (или перетащите его в папку)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Экспорт темы</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Холодные</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Природные</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Классические</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Ретро</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (в комплекте)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Игровой шрифт</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Общий: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Активный: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Введите сообщение...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Дублировать</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>предпросмотр</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>обслуживание</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} вкладка</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} вкладок</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} шёпот</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} шёпотов</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} сообщ.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k сообщ.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Чемпион» Предпросмотр</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>открыт</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Säkerhetsnät för ChatTypes som läggs till av framtida FFXIV-patchar och som pluginen ännu inte känner till. Standard är AV (dataminimering). Aktivera om du vill att framtida kanaler också loggas fullt ut.</value> <value>Säkerhetsnät för ChatTypes som läggs till av framtida FFXIV-patchar och som pluginen ännu inte känner till. Standard är AV (dataminimering). Aktivera om du vill att framtida kanaler också loggas fullt ut.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Tillämpa filter på befintlig databas</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Sekretessfiltret påverkar bara nya meddelanden. Rensningen nedan låter dig retroaktivt ta bort redan sparade meddelanden som inte matchar din sparade vitlista.</value> <value>Sekretessfiltret påverkar bara nya meddelanden. Rensningen nedan låter dig retroaktivt ta bort redan sparade meddelanden som inte matchar din sparade vitlista.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Rensningen använder din SPARADE vitlista (Plugin.Config), inte osparade ändringar ovan. Klicka Spara först om du vill att dina aktuella ändringar ska tillämpas.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Den manuella körningen använder din SPARADE lagringspolicy, inte skjutreglagevärdena ovan. Klicka Spara först om du vill att körningen ska tillämpa dina aktuella ändringar.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Förhandsgranskningen är inaktuell: din vitlista har ändrats sedan senaste uppdateringen. Klicka Uppdatera för att räkna om.</value> <value>Förhandsgranskningen är inaktuell: din vitlista har ändrats sedan senaste uppdateringen. Klicka Uppdatera för att räkna om.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Tillämpa lagring nu</value> <value>Tillämpa lagring nu</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Kör lagringsrensningen omedelbart med den SPARADE policyn. Spara dina ändringar först.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Lagringsrensning pågår i bakgrunden…</value> <value>Lagringsrensning pågår i bakgrunden…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Utseende</value> <value>Utseende</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Läs in föregående session vid start</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Tillämpa filter på meddelanden från tidigare sessioner</value> <value>Tillämpa filter på meddelanden från tidigare sessioner</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Inställningar → Hellion Chat för att finjustera senare</value> <value>Inställningar → Hellion Chat för att finjustera senare</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Export (GDPR Art. 15 — Rätt till tillgång)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Exportera sparade meddelanden som Markdown, JSON eller CSV. Det här låter dig uppfylla en begäran om tillgång från en person vars meddelanden du har sparat, eller ta med din egen historik.</value> <value>Exportera sparade meddelanden som Markdown, JSON eller CSV. Det här låter dig uppfylla en begäran om tillgång från en person vars meddelanden du har sparat, eller ta med din egen historik.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 community-översättare (upstream)</value> <value>Chat 2 community-översättare (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktiva tells</value> <value>Aktiva tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Fäst: överlever omloggning.</value> <value>Fäst: överlever omloggning.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Notering: Om XIV Messenger eller ett liknande plugin undertrycker tells, inaktivera alternativet "Suppress DMs" där så att Hellion Chat kan ta emot tells och öppna auto-flikar.</value> <value>Notering: Om XIV Messenger eller ett liknande plugin undertrycker tells, inaktivera alternativet "Suppress DMs" där så att Hellion Chat kan ta emot tells och öppna auto-flikar.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Tell-historik i auto-flikar</value> <value>Tell-historik i auto-flikar</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Träder bara i kraft när auto-tell-flikar är aktiverade i fliken Chatt.</value> <value>Träder bara i kraft när auto-tell-flikar är aktiverade i fliken Chatt.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Inställningar omstrukturerade</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 har omstrukturerat inställningarna i tematiska flikar. Din chattdatabas och meddelandehistorik är oförändrade. Inställningarna har återställts till standardvärden. Om du vill välja om din sekretesprofil finns Återöppna-knappen i fliken Sekretess. En säkerhetskopia av den tidigare konfigurationen finns vid HellionChat.json.pre-v10-backup bredvid den aktiva konfigurationsfilen.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Allmänt</value> <value>Allmänt</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Om</value> <value>Om</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Tidsstämplar</value> <value>Tidsstämplar</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Fönsterram</value> <value>Fönsterram</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Visa symbolväljarknapp bredvid chattinmatningen</value> <value>Visa symbolväljarknapp bredvid chattinmatningen</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Lägger till en liten knapp till vänster om kanalindikatorerna som öppnar ett popup-fönster med FFXIV-ikoner och en utvald symbollista. Inaktivera om du föredrar ett smalare inmatningsfält.</value> <value>Lägger till en liten knapp till vänster om kanalindikatorerna som öppnar ett popup-fönster med FFXIV-ikoner och en utvald symbollista. Inaktivera om du föredrar ett smalare inmatningsfält.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Lagring</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Översikt</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Underhåll</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value> <value>System</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Om du använder flera linkshells rekommenderar underhållaren en flik per shell för en tydligare översikt. Duplicera fliken och begränsa kanalvalet i varje kopia.</value> <value>Om du använder flera linkshells rekommenderar underhållaren en flik per shell för en tydligare översikt. Duplicera fliken och begränsa kanalvalet i varje kopia.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Flikikon</value> <value>Flikikon</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Flyttar chattfönstret och alla aktiva pop-out-fönster tillbaka till det övre vänstra hörnet av primärskärmen. Användbart när ett fönster har hamnat utanför det synliga området efter en skärmlayoutändring (skärm frånkopplad, upplösning ändrad). Pluginen utför också en automatisk gränskontroll en gång per session. Den här knappen är den manuella nödutgången om något ändå hamnar oåtkomligt.</value> <value>Flyttar chattfönstret och alla aktiva pop-out-fönster tillbaka till det övre vänstra hörnet av primärskärmen. Användbart när ett fönster har hamnat utanför det synliga området efter en skärmlayoutändring (skärm frånkopplad, upplösning ändrad). Pluginen utför också en automatisk gränskontroll en gång per session. Den här knappen är den manuella nödutgången om något ändå hamnar oåtkomligt.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Nytt i v0.6.0: Du kan nu skriva direkt i pop-out-fönster. Aktivera huvudströmbrytaren i fönsterinställningarna.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Förstått</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Öppna fönsterinställningar</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Du kan öppna valfri chattflik som ett eget fönster. Klicka på fönsterikonen uppe till höger eller högerklicka på fliken. Nytt i v0.6.1: pop-out-inmatning är aktiv som standard (kan inaktiveras under Inställningar → Fönster).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Förstått</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Öppna inställningar</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat kan inte starta medan Chat 2 är inläst.</value> <value>Hellion Chat kan inte starta medan Chat 2 är inläst.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Inaktivera Chat 2 i /xlplugins och aktivera sedan Hellion Chat igen.</value> <value>Inaktivera Chat 2 i /xlplugins och aktivera sedan Hellion Chat igen.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Allmänt</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Språk, inmatning, ljud och prestanda.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Utseende</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Fönsteropacitet, teckensnitt, rörelse</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Välj ett theme eller importera ett eget</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Fönster</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>När fönstret är synligt och om det kan flyttas.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Chatt</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, förhandsgranskning, meddelandebeteende och emotes.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Flikar</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Skapa och konfigurera anpassade chattflikar.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Databas</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Lagring, migrering, äldre rensning</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Om</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Tillägg, version, projektinformation, översättare och changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Behåll</value> <value>Behåll</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Integritet först</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Öppen</value> <value>Öppen</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Data och sekretess</value> <value>Data och sekretess</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Sekretessfilter, lagring, rensning, export och databasstatistik.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Avancerat (Shift+klicka för att öppna)</value> <value>Avancerat (Shift+klicka för att öppna)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 har omorganiserat inställningsmenyn och tagit bort det gamla alternativet "Åsidosätt stil" (ersatt av theme-systemet från 1.1.0). Dina övriga inställningar är oförändrade. Fönstrets genomskinlighet har migrerats till "Theme &amp; Layout". En säkerhetskopia av den tidigare konfigurationen finns vid pluginConfigs/HellionChat.json.pre-v16-backup bredvid den aktiva HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Plugin-integrationer låter HellionChat samarbeta med andra installerade Dalamud-plugins. Varje integration identifierar automatiskt sitt mål och inaktiverar sig tyst när målpluginen saknas.</value> <value>Plugin-integrationer låter HellionChat samarbeta med andra installerade Dalamud-plugins. Varje integration identifierar automatiskt sitt mål och inaktiverar sig tyst när målpluginen saknas.</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Inmatning</value> <value>Inmatning</value>
</data> </data>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Det här meddelandet innehåller plugin-exklusiva symboler som andra spelare kanske ser som tomma rutor. Tryck Enter igen för att skicka ändå.</value> <value>Det här meddelandet innehåller plugin-exklusiva symboler som andra spelare kanske ser som tomma rutor. Tryck Enter igen för att skicka ändå.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Infoga symbol</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Inställningar</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Dölj chatten (Enter tar tillbaka den)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Återför den här fliken till huvudfönstret</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>En annan databasåtgärd pågår: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>gallring enligt lagringstid</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>export</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>rensning</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>radering av historiken</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Integritetsfiltret är avstängt, så alla kanaler sparas och inget i databasen strider mot dina inställningar. Slå på filtret och välj kanaler först.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Ingen kanal är vald, så en rensning skulle radera hela historiken. Välj de kanaler du vill behålla, eller använd raderingsknappen om du verkligen vill ta bort allt.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0} meddelanden är sparade. Vill du behålla en kopia, exportera dem innan du raderar.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: kör gallringen direkt i stället för att vänta på den dagliga körningen. Raderar meddelanden äldre än gränserna ovan.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Raderingen av historiken misslyckades. Ingenting togs bort, se /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetri</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Ingen telemetri samlas in. Detta plugin skickar ingenting om dig eller din användning någonstans.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Autoöversättning</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Blockera</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Hoppa till det senaste meddelandet</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Infoga kartmarkering &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Infoga länkat föremål &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>av</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Beteende</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Kortkommandon</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Aviseringar</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Visningslägen</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Historik</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Kommandohjälp</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Plugin-information</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Layoutläge</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opacitet</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Storleksändring</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Automatisk öppning av tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Sidofält</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Varumärke</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Länkar</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Integrationer</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Medverkande</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Licens</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Klicka på en knapp och tryck sedan tangentkombinationen. Esc rensar.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Byt till nästa flik</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Byt till föregående flik</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Byte bygger om typsnittsatlasen, så chatten är tom ett ögonblick.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-timmarsklocka</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Visa historik från tidigare sessioner</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Av innebär att loggen börjar tom vid varje spelstart och bara fylls med meddelanden som tas emot därefter.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Sida för kommandohjälp</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Vilken sida listan med kommandotips visas på medan du skriver.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Läge för automatisk öppning av tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Var en tell öppnas när den kommer.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Byt till fliken vid varje tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Annars öppnas fliken i bakgrunden efter den första.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Sidofält</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Flikar upptill</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Flikarnas placering</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Var fliklistan sitter i huvudfönstret.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Visa namnlist</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Namnlist för pop-out-fönster</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Tillåt flytt</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Tillåt storleksändring</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Tröskel för automatiskt byte av sidofält</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Under denna bredd viks sidofältet ihop till flikar upptill, i pixlar.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Förhandsgranskningens position</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Visa förhandsgranskning endast vid skrivning</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea-arkiv</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Manifest för eget arkiv</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Aktivt tema: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Förgrena och redigera</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Inbyggda teman kan inte redigeras direkt. Förgrening skapar en egen kopia som du kan redigera och spara.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Redigera tema</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Redigerar: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Ytor</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Kanter</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Identitet</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Status</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Spara</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Avbryt</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Återställ till källan</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Återställning är inte tillgänglig medan du redigerar en förgrening. Spara eller avbryt först.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Spara eller kasta dina ändringar först</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Egna ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Förgrena aktivt tema</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Importera temafil…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Sökväg till JSON-fil (eller dra och släpp i mappen)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Exportera tema</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Svala</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Naturliga</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klassiska</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (medföljer)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Spelets typsnitt</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Global: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Aktiv: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Skriv ett meddelande...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Duplicera</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>förhandsgranskning</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>underhåll</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} flik</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} flikar</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} viskning</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} viskningar</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} medd.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k medd.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Mästare» Förhandsgranskning</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>öppen</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Eklentinin henüz tanımadığı, gelecekteki FFXIV yamaları tarafından eklenen ChatType'lar için güvenlik ağı. Varsayılan KAPALI'dır (veri minimizasyonu). Gelecekteki kanalların da tam olarak loglanmasını istiyorsan etkinleştir.</value> <value>Eklentinin henüz tanımadığı, gelecekteki FFXIV yamaları tarafından eklenen ChatType'lar için güvenlik ağı. Varsayılan KAPALI'dır (veri minimizasyonu). Gelecekteki kanalların da tam olarak loglanmasını istiyorsan etkinleştir.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Filtreyi mevcut veritabanına uygula</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Gizlilik filtresi yalnızca yeni mesajları etkiler. Aşağıdaki temizlik, kayıtlı beyaz listene uymayan önceden depolanmış mesajları geriye dönük olarak kaldırmanı sağlar.</value> <value>Gizlilik filtresi yalnızca yeni mesajları etkiler. Aşağıdaki temizlik, kayıtlı beyaz listene uymayan önceden depolanmış mesajları geriye dönük olarak kaldırmanı sağlar.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Temizlik, yukarıdaki kaydedilmemiş değişiklikler değil KAYITLI beyaz listeni (Plugin.Config) kullanır. Mevcut değişikliklerinin uygulanmasını istiyorsan önce Kaydet'e tıkla.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Manuel çalıştırma, yukarıdaki kaydırıcı değerleri değil KAYITLI saklama politikasını kullanır. Mevcut değişikliklerinin uygulanmasını istiyorsan önce Kaydet'e tıkla.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Önizleme güncel değil: beyaz listen son yenilemeden bu yana değişti. Yeniden hesaplamak için Yenile'ye tıkla.</value> <value>Önizleme güncel değil: beyaz listen son yenilemeden bu yana değişti. Yeniden hesaplamak için Yenile'ye tıkla.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Saklama süresini şimdi uygula</value> <value>Saklama süresini şimdi uygula</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: KAYITLI politikayı kullanarak saklama temizliğini hemen çalıştırır. Önce değişikliklerini kaydet.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Saklama temizliği arka planda çalışıyor…</value> <value>Saklama temizliği arka planda çalışıyor…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Görsel</value> <value>Görsel</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Başlangıçta önceki oturumu yükle</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Önceki oturumlardan gelen mesajlara filtre uygula</value> <value>Önceki oturumlardan gelen mesajlara filtre uygula</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Daha sonra ince ayar için Ayarlar → Hellion Chat</value> <value>Daha sonra ince ayar için Ayarlar → Hellion Chat</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Dışa aktarma (GDPR Art. 15 — Erişim hakkı)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Saklanan mesajları Markdown, JSON veya CSV olarak dışa aktar. Bu, mesajlarını sakladığın bir kişinin erişim talebini karşılamana veya kendi geçmişini yanında götürmene olanak tanır.</value> <value>Saklanan mesajları Markdown, JSON veya CSV olarak dışa aktar. Bu, mesajlarını sakladığın bir kişinin erişim talebini karşılamana veya kendi geçmişini yanında götürmene olanak tanır.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 topluluk çevirmenleri (upstream)</value> <value>Chat 2 topluluk çevirmenleri (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Aktif tell'ler</value> <value>Aktif tell'ler</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Sabitlenmiş: yeniden girişten sonra hayatta kalır.</value> <value>Sabitlenmiş: yeniden girişten sonra hayatta kalır.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Not: XIV Messenger veya benzer bir eklenti tell'leri engelliyorsa, Hellion Chat'in tell'leri alıp otomatik sekmeleri açabilmesi için oradaki "Suppress DMs" seçeneğini devre dışı bırak.</value> <value>Not: XIV Messenger veya benzer bir eklenti tell'leri engelliyorsa, Hellion Chat'in tell'leri alıp otomatik sekmeleri açabilmesi için oradaki "Suppress DMs" seçeneğini devre dışı bırak.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Otomatik sekmelerdeki tell geçmişi</value> <value>Otomatik sekmelerdeki tell geçmişi</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Yalnızca Sohbet sekmesinde otomatik tell sekmeleri etkinleştirildiğinde geçerli olur.</value> <value>Yalnızca Sohbet sekmesinde otomatik tell sekmeleri etkinleştirildiğinde geçerli olur.</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Ayarlar yeniden yapılandırıldı</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0, ayarları tematik sekmelere göre yeniden yapılandırdı. Sohbet veritabanın ve mesaj geçmişin değişmedi. Ayarlar varsayılanlara sıfırlandı. Gizlilik profilini yeniden seçmek istiyorsan, Yeniden Aç düğmesi Gizlilik sekmesindedir. Önceki yapılandırmanın yedeği, aktif yapılandırma dosyasının yanında HellionChat.json.pre-v10-backup olarak bulunmaktadır.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Genel</value> <value>Genel</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Hakkında</value> <value>Hakkında</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Zaman damgaları</value> <value>Zaman damgaları</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Pencere çerçevesi</value> <value>Pencere çerçevesi</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Sohbet girişinin yanında sembol seçici düğmesini göster</value> <value>Sohbet girişinin yanında sembol seçici düğmesini göster</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Kanal göstergesinin soluna, FFXIV ikonları ve özenle seçilmiş bir sembol listesiyle bir açılır pencere açan küçük bir düğme ekler. Daha sade bir giriş çubuğu tercih ediyorsan devre dışı bırak.</value> <value>Kanal göstergesinin soluna, FFXIV ikonları ve özenle seçilmiş bir sembol listesiyle bir açılır pencere açan küçük bir düğme ekler. Daha sade bir giriş çubuğu tercih ediyorsan devre dışı bırak.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Depolama</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Genel bakış</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Bakım</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistem</value> <value>Sistem</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Birden fazla linkshell kullanıyorsan, geliştirici daha temiz bir genel bakış için her kabuk için bir sekme kullanmanı önerir. Sekmeyi çoğalt ve her kopyada kanal seçimini kısıtla.</value> <value>Birden fazla linkshell kullanıyorsan, geliştirici daha temiz bir genel bakış için her kabuk için bir sekme kullanmanı önerir. Sekmeyi çoğalt ve her kopyada kanal seçimini kısıtla.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Sekme ikonu</value> <value>Sekme ikonu</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Sohbet penceresini ve tüm aktif pop-out'ları birincil monitörün sol üst köşesine geri taşır. Ekran düzeni değişikliğinden sonra (monitör bağlantısı kesildi, çözünürlük değişti) bir pencere görünür alanın dışında kaldığında kullanışlıdır. Eklenti ayrıca oturum başına bir kez otomatik sınır denetimi yapar; bu düğme, bir şey hâlâ erişilemez kalırsa kullanılacak manuel kaçış çıkışıdır.</value> <value>Sohbet penceresini ve tüm aktif pop-out'ları birincil monitörün sol üst köşesine geri taşır. Ekran düzeni değişikliğinden sonra (monitör bağlantısı kesildi, çözünürlük değişti) bir pencere görünür alanın dışında kaldığında kullanışlıdır. Eklenti ayrıca oturum başına bir kez otomatik sınır denetimi yapar; bu düğme, bir şey hâlâ erişilemez kalırsa kullanılacak manuel kaçış çıkışıdır.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>v0.6.0'da yeni: Artık doğrudan pop-out'larda yazabilirsin. Pencere ayarlarındaki ana anahtarı etkinleştir.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Anladım</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Pencere ayarlarını aç</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Herhangi bir sohbet sekmesini kendi penceresi olarak açabilirsin. Sağ üstteki pencere ikonuna tıkla ya da sekmeye sağ tıkla. v0.6.1'de yeni: pop-out girişi varsayılan olarak aktiftir (Ayarlar → Pencere altından devre dışı bırakılabilir).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Anladım</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Ayarları aç</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Chat 2 yüklüyken Hellion Chat başlatılamaz.</value> <value>Chat 2 yüklüyken Hellion Chat başlatılamaz.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Chat 2'yi /xlplugins'te devre dışı bırak, ardından Hellion Chat'i yeniden etkinleştir.</value> <value>Chat 2'yi /xlplugins'te devre dışı bırak, ardından Hellion Chat'i yeniden etkinleştir.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Genel</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Dil, giriş, ses ve performans.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Görünüm</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Pencere opaklığı, yazı tipleri, hareket</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Themes</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Bir tema seç veya kendininkini içe aktar</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Pencere</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Pencerenin ne zaman görünür olduğu ve taşınıp taşınamayacağı.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Sohbet</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tell'ler, önizleme, mesaj davranışı ve emote'lar.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Sekmeler</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Özel sohbet sekmeleri oluştur ve yapılandır.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>Veritabanı</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Depolama, geçiş, eski temizlik</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Hakkında</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Eklentiler, sürüm, proje bilgileri, çevirmenler ve changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Themes</value> <value>Themes</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Koru</value> <value>Koru</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Önce gizlilik</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Açık</value> <value>Açık</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Veri ve gizlilik</value> <value>Veri ve gizlilik</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Gizlilik filtresi, saklama süresi, temizlik, dışa aktarma ve veritabanı istatistikleri.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Theme</value> <value>Theme</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Gelişmiş (açmak için Shift+tıkla)</value> <value>Gelişmiş (açmak için Shift+tıkla)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1, ayarlar menüsünü yeniden düzenledi ve eski "Stili geçersiz kıl" seçeneğini kaldırdı (1.1.0'daki tema sistemi tarafından yerini aldı). Kalan ayarların değişmedi. Pencere şeffaflığı "Theme &amp; Layout" bölümüne taşındı. Önceki yapılandırmanın yedeği, aktif HellionChat.json dosyasının yanında pluginConfigs/HellionChat.json.pre-v16-backup olarak bulunmaktadır.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Eklenti entegrasyonları, HellionChat'in yüklü diğer Dalamud eklentileriyle birlikte çalışmasını sağlar. Her entegrasyon hedefini otomatik olarak algılar ve hedef eklenti eksik olduğunda sessizce devre dışı kalır.</value> <value>Eklenti entegrasyonları, HellionChat'in yüklü diğer Dalamud eklentileriyle birlikte çalışmasını sağlar. Her entegrasyon hedefini otomatik olarak algılar ve hedef eklenti eksik olduğunda sessizce devre dışı kalır.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Giriş</value> <value>Giriş</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Bu mesaj, diğer oyuncuların boş kutu olarak görebileceği yalnızca plugin semboller içeriyor. Yine de göndermek için Enter'a tekrar basın.</value> <value>Bu mesaj, diğer oyuncuların boş kutu olarak görebileceği yalnızca plugin semboller içeriyor. Yine de göndermek için Enter'a tekrar basın.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Sembol ekle</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Ayarlar</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Sohbeti gizle (Geri getirmek için Enter)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Bu sekmeyi ana pencereye geri al</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Başka bir veritabanı işlemi çalışıyor: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>saklama temizliği</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>dışa aktarma</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>temizlik</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>geçmişin silinmesi</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Gizlilik filtresi kapalı, bu yüzden her kanal saklanıyor ve veritabanında ayarlarınızla çelişen bir şey yok. Önce filtreyi açın ve kanalları seçin.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Hiçbir kanal seçili değil, bu yüzden temizlik tüm geçmişi siler. Saklamak istediğiniz kanalları seçin veya gerçekten her şeyin gitmesini istiyorsanız silme düğmesini kullanın.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>{0:N0} ileti saklanıyor. Bir kopya tutmak istiyorsanız, silmeden önce dışa aktarın.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: günlük taramayı beklemeden saklama temizliğini hemen çalıştırır. Yukarıdaki sınırlardan daha eski iletileri siler.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Geçmiş silinemedi. Hiçbir şey kaldırılmadı, /xllog kaydına bakın.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Telemetri</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Hiçbir telemetri toplanmıyor. Eklenti sizinle veya kullanımınızla ilgili hiçbir şeyi hiçbir yere göndermiyor.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Otomatik çeviri</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Engelle</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>En son iletiye git</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Harita işaretini ekle &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Bağlantılı eşyayı ekle &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>kapalı</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Davranış</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Kısayol tuşları</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Bildirimler</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Görünüm modları</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Geçmiş</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Komut yardımı</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Eklenti bildirimi</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Yerleşim modu</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Opaklık</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Boyutlandırma davranışı</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Tell'leri otomatik açma</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Kenar çubuğu</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Marka</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Bağlantılar</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Entegrasyonlar</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Katkıda bulunanlar</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Lisans</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Bir düğmeye tıklayın, ardından tuş kombinasyonuna basın. Esc temizler.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Sonraki sekmeye geç</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Önceki sekmeye geç</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Değiştirmek yazı tipi atlasını yeniden oluşturur, bu yüzden sohbet bir an boşalır.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24 saat biçimi</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Önceki oturumların geçmişini göster</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Kapalıyken sohbet kaydı her oyun açılışında boş başlar ve yalnızca o andan sonra gelen iletilerle dolar.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Komut yardımı tarafı</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>Yazarken komut ipucu listesinin hangi tarafta görüneceği.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Tell otomatik açma modu</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Bir tell geldiğinde nerede açılacağı.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Her tell'de sekmeye geç</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Aksi hâlde sekme ilkinden sonra arka planda açılır.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Kenar çubuğu</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Üst sekmeler</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Sekme yerleşimi</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Sekme listesinin ana pencerede nerede duracağı.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Başlık çubuğunu göster</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Ayrık pencereler için başlık çubuğunu göster</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Taşımaya izin ver</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Boyutlandırmaya izin ver</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Kenar çubuğu otomatik geçiş eşiği</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Bu genişliğin altında kenar çubuğu üst sekmelere katlanır, piksel olarak.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Önizleme konumu</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Önizlemeyi yalnızca yazarken göster</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea deposu</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Özel depo manifesti</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Etkin tema: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Çatalla ve düzenle</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Yerleşik temalar doğrudan düzenlenemez. Çatallama, düzenleyip kaydedebileceğiniz özel bir kopya oluşturur.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Temayı düzenle</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Düzenleniyor: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Yüzeyler</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Kenarlıklar</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Metin</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Kimlik</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Durum</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Kaydet</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>İptal</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Kaynağa sıfırla</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Bir çatal düzenlenirken sıfırlama kullanılamaz. Önce kaydedin veya iptal edin.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Önce değişikliklerinizi kaydedin veya atın</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Özel ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Etkin temayı çatalla</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Tema dosyası içe aktar…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>JSON dosyasının yolu (veya klasöre sürükleyip bırakın)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Temayı dışa aktar</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Soğuk</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Doğal</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Klasik</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Retro</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (birlikte gelir)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Oyun yazı tipi</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Genel: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Etkin: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Bir ileti yazın...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Çoğalt</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>önizleme</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>bakım</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} sekme</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} sekme</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} tell</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} ileti</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k ileti</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Şampiyon» Önizleme</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>açık</value>
</data>
</root>
+335 -123
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>Страховий захід для типів ChatTypes, доданих у майбутніх патчах FFXIV, які плагін ще не знає. За замовчуванням ВИМК (мінімізація даних). Увімкніть, якщо хочете, щоб майбутні канали також повністю журналювались.</value> <value>Страховий захід для типів ChatTypes, доданих у майбутніх патчах FFXIV, які плагін ще не знає. За замовчуванням ВИМК (мінімізація даних). Увімкніть, якщо хочете, щоб майбутні канали також повністю журналювались.</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>Застосувати фільтр до наявної бази даних</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>Фільтр конфіденційності діє лише на нові повідомлення. Очищення нижче дозволяє заднім числом видалити вже збережені повідомлення, які не відповідають збереженому білому списку.</value> <value>Фільтр конфіденційності діє лише на нові повідомлення. Очищення нижче дозволяє заднім числом видалити вже збережені повідомлення, які не відповідають збереженому білому списку.</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>Очищення використовує ЗБЕРЕЖЕНИЙ білий список (Plugin.Config), а не незбережені зміни вище. Спочатку натисніть «Зберегти», якщо хочете застосувати поточні зміни.</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>Ручний запуск використовує ЗБЕРЕЖЕНУ політику зберігання, а не значення повзунків вище. Спочатку натисніть «Зберегти», якщо хочете застосувати поточні зміни.</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>Попередній перегляд застарів — Ваш білий список змінився після останнього оновлення. Натисніть «Оновити» для перерахунку.</value> <value>Попередній перегляд застарів — Ваш білий список змінився після останнього оновлення. Натисніть «Оновити» для перерахунку.</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>Застосувати термін зберігання зараз</value> <value>Застосувати термін зберігання зараз</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: Негайно запускає очищення за терміном зберігання, використовуючи ЗБЕРЕЖЕНУ політику. Спочатку збережіть зміни.</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>Очищення за терміном зберігання виконується у фоновому режимі…</value> <value>Очищення за терміном зберігання виконується у фоновому режимі…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>Зовнішній вигляд</value> <value>Зовнішній вигляд</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>Завантажувати попередній сеанс при запуску</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>Застосовувати фільтри до повідомлень з попередніх сеансів</value> <value>Застосовувати фільтри до повідомлень з попередніх сеансів</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Налаштування → Hellion Chat для подальшого тонкого налаштування</value> <value>Налаштування → Hellion Chat для подальшого тонкого налаштування</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>Експорт (GDPR Art. 15 — Право доступу)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>Експорт збережених повідомлень у форматі Markdown, JSON або CSV. Це дозволяє виконати запит на доступ від особи, чиї повідомлення Ви зберегли, або взяти власну історію з собою.</value> <value>Експорт збережених повідомлень у форматі Markdown, JSON або CSV. Це дозволяє виконати запит на доступ від особи, чиї повідомлення Ви зберегли, або взяти власну історію з собою.</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Перекладачі спільноти Chat 2 (upstream)</value> <value>Перекладачі спільноти Chat 2 (upstream)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>Активні tells</value> <value>Активні tells</value>
</data> </data>
@@ -504,7 +486,7 @@
<value>Закріплено — виживає після релогу.</value> <value>Закріплено — виживає після релогу.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>Auto-Tell-Tabs</value>
</data> </data>
@@ -545,7 +527,7 @@
<value>Примітка: якщо XIV Messenger або подібний плагін пригнічує tells, вимкніть там опцію «Suppress DMs», щоб Hellion Chat міг отримувати tells і відкривати авто-вкладки.</value> <value>Примітка: якщо XIV Messenger або подібний плагін пригнічує tells, вимкніть там опцію «Suppress DMs», щоб Hellion Chat міг отримувати tells і відкривати авто-вкладки.</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Історія tells в авто-вкладках</value> <value>Історія tells в авто-вкладках</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>Діє лише тоді, коли авто-tell-вкладки увімкнено на вкладці «Чат».</value> <value>Діє лише тоді, коли авто-tell-вкладки увімкнено на вкладці «Чат».</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>Налаштування реструктуровано</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 реструктурував налаштування на тематичні вкладки. База даних чату та історія повідомлень залишились без змін. Налаштування скинуто до стандартних. Якщо Ви хочете повторно вибрати профіль конфіденційності, кнопка «Показати знову» знаходиться на вкладці «Конфіденційність». Резервна копія попередньої конфігурації знаходиться за адресою HellionChat.json.pre-v10-backup поруч із активним конфігураційним файлом.</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>Загальні</value> <value>Загальні</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>Про плагін</value> <value>Про плагін</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>Тема</value> <value>Тема</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>Мітки часу</value> <value>Мітки часу</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>Рамка вікна</value> <value>Рамка вікна</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>Показувати кнопку вибору символів поруч із полем введення чату</value> <value>Показувати кнопку вибору символів поруч із полем введення чату</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>Додає невелику кнопку ліворуч від індикатора каналу, що відкриває спливаюче вікно з іконками FFXIV і підібраним списком символів. Вимкніть, якщо надаєте перевагу більш компактній панелі введення.</value> <value>Додає невелику кнопку ліворуч від індикатора каналу, що відкриває спливаюче вікно з іконками FFXIV і підібраним списком символів. Вимкніть, якщо надаєте перевагу більш компактній панелі введення.</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>Зберігання</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>Огляд</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>Обслуговування</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>Система</value> <value>Система</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>Якщо Ви використовуєте кілька linkshells, розробник рекомендує по одній вкладці на кожну для зручнішого огляду. Продублюйте вкладку й обмежте вибір каналів у кожній копії.</value> <value>Якщо Ви використовуєте кілька linkshells, розробник рекомендує по одній вкладці на кожну для зручнішого огляду. Продублюйте вкладку й обмежте вибір каналів у кожній копії.</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>Іконка вкладки</value> <value>Іконка вкладки</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>Переміщує вікно чату та всі активні спливаючі вікна назад у верхній лівий кут основного монітора. Корисно, коли вікно опинилось поза видимою областю після зміни конфігурації дисплея (монітор від'єднано, змінено роздільну здатність). Плагін також виконує автоматичну перевірку меж один раз за сеанс; ця кнопка є ручним запасним виходом, якщо щось все одно залишається недоступним.</value> <value>Переміщує вікно чату та всі активні спливаючі вікна назад у верхній лівий кут основного монітора. Корисно, коли вікно опинилось поза видимою областю після зміни конфігурації дисплея (монітор від'єднано, змінено роздільну здатність). Плагін також виконує автоматичну перевірку меж один раз за сеанс; ця кнопка є ручним запасним виходом, якщо щось все одно залишається недоступним.</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>Нове у v0.6.0: тепер можна вводити текст безпосередньо у спливаючих вікнах. Увімкніть головний перемикач у налаштуваннях вікна.</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>Зрозуміло</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>Відкрити налаштування вікна</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>Будь-яку вкладку чату можна відкрити як окреме вікно. Натисніть іконку вікна у верхньому правому куті або клацніть правою кнопкою миші по вкладці. Нове у v0.6.1: введення у спливаючих вікнах активне за замовчуванням (можна вимкнути в розділі Налаштування → Вікно).</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>Зрозуміло</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>Відкрити налаштування</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat не може запуститись, поки завантажено Chat 2.</value> <value>Hellion Chat не може запуститись, поки завантажено Chat 2.</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>Вимкніть Chat 2 у /xlplugins, а потім знову увімкніть Hellion Chat.</value> <value>Вимкніть Chat 2 у /xlplugins, а потім знову увімкніть Hellion Chat.</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>Загальні</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>Мова, введення, звук і продуктивність.</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>Зовнішній вигляд</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>Непрозорість вікна, шрифти, анімація</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>Теми</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>Вибрати тему або імпортувати власну</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>Вікно</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>Коли вікно видиме та чи можна його переміщати.</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>Чат</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>Tells, попередній перегляд, поведінка повідомлень та емоції.</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>Вкладки</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>Створення та налаштування власних вкладок чату.</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>База даних</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>Зберігання, міграція, видалення застарілого</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>Про плагін</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>Розширення, версія, інформація про проект, перекладачі та changelog.</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>Теми</value> <value>Теми</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>Залишити</value> <value>Залишити</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>Приватність передусім</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>Відкрито</value> <value>Відкрито</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>Дані та конфіденційність</value> <value>Дані та конфіденційність</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>Фільтр конфіденційності, термін зберігання, очищення, експорт та статистика бази даних.</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>Тема</value> <value>Тема</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>Додаткові параметри (Shift+клік для відкриття)</value> <value>Додаткові параметри (Shift+клік для відкриття)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 реорганізував меню налаштувань і видалив стару опцію «Перевизначити стиль» (замінена системою тем із версії 1.1.0). Решта налаштувань залишилась без змін. Прозорість вікна перенесена до розділу «Тема &amp; Макет». Резервна копія попередньої конфігурації знаходиться за адресою pluginConfigs/HellionChat.json.pre-v16-backup поруч із активним HellionChat.json.</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>Інтеграції плагінів дозволяють HellionChat взаємодіяти з іншими встановленими плагінами Dalamud. Кожна інтеграція автоматично визначає свою ціль і тихо вимикається, якщо цільовий плагін відсутній.</value> <value>Інтеграції плагінів дозволяють HellionChat взаємодіяти з іншими встановленими плагінами Dalamud. Кожна інтеграція автоматично визначає свою ціль і тихо вимикається, якщо цільовий плагін відсутній.</value>
</data> </data>
@@ -955,7 +850,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>Введення</value> <value>Введення</value>
</data> </data>
@@ -1145,4 +1040,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>Це повідомлення містить символи плагіна, які інші гр��вці можуть бачити як порожні квадрати. Натисніть Enter ще раз, щоб надіслати все одно.</value> <value>Це повідомлення містить символи плагіна, які інші гр��вці можуть бачити як порожні квадрати. Натисніть Enter ще раз, щоб надіслати все одно.</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>Вставити символ</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>Налаштування</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>Сховати чат (Enter поверне його)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>Повернути цю вкладку до головного вікна</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>Уже виконується інша операція з базою даних: {0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>очищення за строком зберігання</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>експорт</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>очищення</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>видалення історії</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>Фільтр приватності вимкнено, тому зберігаються всі канали і ніщо в базі даних не суперечить вашим налаштуванням. Спершу увімкніть фільтр і виберіть канали.</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>Не вибрано жодного каналу, тому очищення видалить усю історію. Виберіть канали, які хочете зберегти, або скористайтеся кнопкою видалення, якщо справді хочете стерти все.</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>Збережено {0:N0} повідомлень. Якщо хочете залишити копію, експортуйте їх перед видаленням.</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift: виконує очищення за строком зберігання одразу, не чекаючи щоденного проходу. Видаляє повідомлення, старші за вказані вище межі.</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>Не вдалося видалити історію. Нічого не було видалено, див. /xllog.</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>Телеметрія</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>Телеметрія не збирається. Плагін нікуди не надсилає дані про вас або про ваше використання.</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>Автопереклад</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>Заблокувати</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>Перейти до останнього повідомлення</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>Вставити позначку карти &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>Вставити пов'язаний предмет &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>вимкнено</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>Поведінка</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>Гарячі клавіші</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>Сповіщення</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>Режими відображення</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>Історія</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>Довідка команд</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>Повідомлення про плагін</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>Режим компонування</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>Непрозорість</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>Зміна розміру</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>Автовідкриття tell</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>Бічна панель</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>Бренд</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>Посилання</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>Інтеграції</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>Подяки</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>Ліцензія</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>Натисніть кнопку, потім натисніть комбінацію клавіш. Esc очищає.</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>Перейти до наступної вкладки</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>Перейти до попередньої вкладки</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>Перемикання перебудовує атлас шрифтів, тому чат на мить порожніє.</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24-годинний формат</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>Показувати історію попередніх сеансів</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>Вимкнено: журнал починається порожнім за кожного запуску гри й заповнюється лише повідомленнями, отриманими після цього.</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>Сторона довідки команд</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>З якого боку з'являється список підказок під час введення.</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>Режим автовідкриття tell</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>Де відкривається tell, коли надходить.</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>Перемикатися на вкладку за кожного tell</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>Інакше вкладка після першого відкривається у фоні.</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>Бічна панель</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>Вкладки згори</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>Розташування вкладок</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>Де в головному вікні розташований список вкладок.</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>Показувати рядок заголовка</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>Показувати рядок заголовка у відокремлених вікнах</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>Дозволити переміщення</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>Дозволити зміну розміру</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>Поріг автоперемикання бічної панелі</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>Нижче цієї ширини бічна панель згортається у вкладки згори, у пікселях.</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>Розташування попереднього перегляду</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>Перегляд лише під час введення</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Репозиторій Gitea</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>Маніфест власного репозиторію</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>Активна тема: {0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>Відгалузити та редагувати</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>Вбудовані теми не можна редагувати напряму. Відгалуження створює власну копію, яку можна редагувати та зберегти.</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>Редагувати тему</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>Редагується: {0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>Поверхні</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>Межі</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>Текст</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>Ідентичність</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>Стан</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>Зберегти</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>Скасувати</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>Скинути до джерела</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>Скидання недоступне під час редагування відгалуження. Спершу збережіть або скасуйте.</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>Спершу збережіть або скасуйте зміни</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>Власні ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>Відгалузити активну тему</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>Імпортувати файл теми…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>Шлях до файлу JSON (або перетягніть у теку)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>Експорт теми</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>Холодні</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>Природні</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>Класичні</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>Ретро</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter (у комплекті)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>Ігровий шрифт</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>Загальний: {0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>Активний: {0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>Введіть повідомлення...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>Дублювати</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>попередній перегляд</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>обслуговування</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} вкладка</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} вкладок</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} шепіт</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} шепотів</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} повід.</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k повід.</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«Чемпіон» Перегляд</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>відкрито</value>
</data>
</root>
+337 -125
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>用于兜底处理未来 FFXIV 补丁新增而插件尚未识别的 ChatType。默认为关闭(数据最小化)。如需完整记录未来新增频道,请启用此选项。</value> <value>用于兜底处理未来 FFXIV 补丁新增而插件尚未识别的 ChatType。默认为关闭(数据最小化)。如需完整记录未来新增频道,请启用此选项。</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>将过滤器应用到现有数据库</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>隐私过滤器仅对新消息生效。下方的清理功能可让你追溯删除已保存但不符合当前白名单的消息。</value> <value>隐私过滤器仅对新消息生效。下方的清理功能可让你追溯删除已保存但不符合当前白名单的消息。</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>清理使用的是已保存的白名单(Plugin.Config),而非上方未保存的更改。如需应用当前更改,请先点击保存。</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>手动运行使用的是已保存的保留策略,而非上方滑块的当前值。如需应用当前更改,请先点击保存。</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>预览已过时,白名单自上次刷新后有所更改。请点击刷新以重新计算。</value> <value>预览已过时,白名单自上次刷新后有所更改。请点击刷新以重新计算。</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>立即应用保留策略</value> <value>立即应用保留策略</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift:使用已保存的策略立即执行保留清理。请先保存更改。</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>保留清理正在后台运行…</value> <value>保留清理正在后台运行…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>视觉</value> <value>视觉</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>启动时加载上次会话</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>将过滤器应用到上次会话的消息</value> <value>将过滤器应用到上次会话的消息</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>进入设置 → Hellion Chat 可进一步微调</value> <value>进入设置 → Hellion Chat 可进一步微调</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>导出(GDPR Art. 15 — 访问权)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>将已保存的消息导出为 Markdown、JSON 或 CSV 格式。可用于响应他人的数据访问请求,或将自己的聊天历史带走备份。</value> <value>将已保存的消息导出为 Markdown、JSON 或 CSV 格式。可用于响应他人的数据访问请求,或将自己的聊天历史带走备份。</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 社区翻译者(上游)</value> <value>Chat 2 社区翻译者(上游)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>活跃密语</value> <value>活跃密语</value>
</data> </data>
@@ -504,9 +486,9 @@
<value>已固定,重新登录后仍保留。</value> <value>已固定,重新登录后仍保留。</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>自动密语标签页</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve">
<value>为每条 /tell 自动为对话伙伴开启专属标签页</value> <value>为每条 /tell 自动为对话伙伴开启专属标签页</value>
@@ -545,7 +527,7 @@
<value>注意:如果 XIV Messenger 或类似插件屏蔽了密语,请在该插件中禁用"Suppress DMs"选项,以便 Hellion Chat 能正常接收密语并打开自动标签页。</value> <value>注意:如果 XIV Messenger 或类似插件屏蔽了密语,请在该插件中禁用"Suppress DMs"选项,以便 Hellion Chat 能正常接收密语并打开自动标签页。</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>自动标签页中的密语历史</value> <value>自动标签页中的密语历史</value>
</data> </data>
@@ -559,15 +541,9 @@
<value>仅在聊天标签页中启用了自动密语标签页时生效。</value> <value>仅在聊天标签页中启用了自动密语标签页时生效。</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>设置已重新整理</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 已将设置重新整理为分主题的标签页。你的聊天数据库和消息历史保持不变。设置已重置为默认值。如需重新选择隐私方案,可在隐私标签页中找到"再次显示向导"按钮。上一版配置的备份文件位于活动配置文件旁边,文件名为 HellionChat.json.pre-v10-backup。</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>通用</value> <value>通用</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>关于</value> <value>关于</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>主题</value> <value>主题</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>时间戳</value> <value>时间戳</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>窗口边框</value> <value>窗口边框</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>在聊天输入框旁显示符号选取按钮</value> <value>在聊天输入框旁显示符号选取按钮</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>在频道指示器左侧添加一个小按钮,点击可打开包含 FFXIV 图标和精选符号列表的弹出窗口。如果你希望输入栏更简洁,可以禁用此功能。</value> <value>在频道指示器左侧添加一个小按钮,点击可打开包含 FFXIV 图标和精选符号列表的弹出窗口。如果你希望输入栏更简洁,可以禁用此功能。</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>存储</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>概览</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>维护</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>系统</value> <value>系统</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>如果你使用多个通讯贝,维护者建议为每个通讯贝单独创建一个标签页,以获得更清晰的概览。复制该标签页,并在每个副本中分别限定频道选择即可。</value> <value>如果你使用多个通讯贝,维护者建议为每个通讯贝单独创建一个标签页,以获得更清晰的概览。复制该标签页,并在每个副本中分别限定频道选择即可。</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>标签页图标</value> <value>标签页图标</value>
</data> </data>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>将聊天窗口及所有活跃的弹出窗口移回主显示器左上角。当显示器布局更改(断开显示器、更改分辨率)导致窗口超出可见区域时很有用。插件每次会话也会自动进行一次边界检查;如果仍有窗口无法访问,可使用此按钮手动修复。</value> <value>将聊天窗口及所有活跃的弹出窗口移回主显示器左上角。当显示器布局更改(断开显示器、更改分辨率)导致窗口超出可见区域时很有用。插件每次会话也会自动进行一次边界检查;如果仍有窗口无法访问,可使用此按钮手动修复。</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>v0.6.0 新功能:现在可以直接在弹出窗口中输入。请在窗口设置中启用主开关。</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>知道了</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>打开窗口设置</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>可以将任意聊天标签页作为独立窗口打开。点击右上角的窗口图标,或右键点击标签页即可。v0.6.1 新功能:弹出窗口输入默认已启用(可在设置 → 窗口中关闭)。</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>知道了</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>打开设置</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Chat 2 处于加载状态时 Hellion Chat 无法启动。</value> <value>Chat 2 处于加载状态时 Hellion Chat 无法启动。</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>请在 /xlplugins 中禁用 Chat 2,然后重新启用 Hellion Chat。</value> <value>请在 /xlplugins 中禁用 Chat 2,然后重新启用 Hellion Chat。</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>通用</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>语言、输入、音频与性能。</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>外观</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>窗口不透明度、字体、动效</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>主题</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>选择主题或导入自定义主题</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>窗口</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>窗口的显示时机以及是否可移动。</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>聊天</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>密语、预览、消息行为与情感动作。</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>标签页</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>创建并配置自定义聊天标签页。</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>数据库</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>存储、迁移、历史清理</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>关于</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>扩展、版本、项目信息、译者与更新日志。</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>主题</value> <value>主题</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>保留</value> <value>保留</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>隐私优先</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>开放</value> <value>开放</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>数据与隐私</value> <value>数据与隐私</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>隐私过滤器、保留策略、清理、导出与数据库统计。</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>主题</value> <value>主题</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>高级选项(Shift+点击展开)</value> <value>高级选项(Shift+点击展开)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 已重新整理设置菜单,并移除了旧版"覆盖样式"选项(已由 1.1.0 引入的主题系统取代)。其余设置保持不变。窗口透明度已迁移至"Theme &amp; Layout"。上一版配置的备份文件位于活动 HellionChat.json 旁边,文件名为 pluginConfigs/HellionChat.json.pre-v16-backup。</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>插件集成功能让 HellionChat 能与其他已安装的 Dalamud 插件协同工作。每项集成会自动检测目标插件,目标插件缺失时将静默禁用。</value> <value>插件集成功能让 HellionChat 能与其他已安装的 Dalamud 插件协同工作。每项集成会自动检测目标插件,目标插件缺失时将静默禁用。</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>输入</value> <value>输入</value>
</data> </data>
@@ -997,7 +892,7 @@
<value>输入与预览</value> <value>输入与预览</value>
</data> </data>
<data name="Settings_Section_AutoTellTabs" xml:space="preserve"> <data name="Settings_Section_AutoTellTabs" xml:space="preserve">
<value>自动Tell标签页</value> <value>自动密语标签页</value>
</data> </data>
<data name="Settings_Section_Emotes" xml:space="preserve"> <data name="Settings_Section_Emotes" xml:space="preserve">
<value>表情动作</value> <value>表情动作</value>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>该消息包含插件专用符号,其他玩家可能看到空白方框。再次按 Enter 键强制发送。</value> <value>该消息包含插件专用符号,其他玩家可能看到空白方框。再次按 Enter 键强制发送。</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>插入符号</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>设置</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>隐藏聊天(按 Enter 恢复)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>将此标签页放回主窗口</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>另一项数据库操作正在进行:{0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>保留期清理</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>导出</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>清理</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>清空历史记录</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>隐私过滤器已关闭,因此所有频道都会保存,数据库中没有与设置冲突的内容。请先开启过滤器并选择频道。</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>未选择任何频道,因此清理会删除全部历史记录。请选择要保留的频道,若确实想全部删除,请使用清空按钮。</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>已保存 {0:N0} 条消息。若想留一份副本,请在清空前先导出。</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift:立即执行保留期清理,无需等待每日运行。删除早于上方期限的消息。</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>清空历史记录失败。没有删除任何内容,请查看 /xllog。</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>遥测</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>不收集任何遥测数据。插件不会向任何地方发送关于你或你使用情况的信息。</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>自动翻译</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>屏蔽</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>跳到最新消息</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>插入地图标记 &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>插入关联物品 &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>关闭</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>行为</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>快捷键</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>通知</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>显示模式</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>历史记录</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>命令帮助</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>插件提示</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>布局模式</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>不透明度</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>调整大小行为</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>自动打开密语</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>侧边栏</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>品牌</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>链接</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>集成</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>制作人员</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>许可证</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>点击按钮后按下组合键。Esc 清除。</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>切换到下一个标签</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>切换到上一个标签</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>切换会重建字体图集,因此聊天会短暂空白。</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24 小时制</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>显示之前会话的历史记录</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>关闭时,日志在每次启动游戏时都是空的,只会填入此后收到的消息。</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>命令帮助的位置</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>输入时命令提示列表出现在哪一侧。</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>密语自动打开方式</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>密语到达时在何处打开。</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>每条密语都切换到标签</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>否则标签会在第一条之后于后台打开。</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>侧边栏</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>顶部标签</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>标签位置</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>标签列表在主窗口中的位置。</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>显示标题栏</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>显示弹出窗口的标题栏</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>允许移动</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>允许调整大小</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>侧边栏自动切换阈值</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>低于此宽度时侧边栏会折叠为顶部标签,单位为像素。</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>预览位置</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>仅在输入时显示预览</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea 仓库</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>自定义仓库清单</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>当前主题:{0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>复制并编辑</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>内置主题无法直接编辑。复制会创建一份可编辑并保存的自定义副本。</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>编辑主题</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>正在编辑:{0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>表面</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>边框</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>文本</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>身份</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>状态</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>保存</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>取消</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>重置为原始主题</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>编辑副本时无法重置。请先保存或取消。</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>请先保存或放弃你的更改</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>自定义 ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>复制当前主题</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>导入主题文件…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>JSON 文件路径(或拖放到文件夹)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>导出主题</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>冷色</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>自然</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>经典</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>复古</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter(内置)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>游戏字体</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>全局:{0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>使用中:{0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>输入消息...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>复制</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>预览</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>维护</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} 标签</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} 标签</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} 密语</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} 密语</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} 条</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k 条</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«冠军» 预览</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>打开</value>
</data>
</root>
+343 -131
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='utf-8'?>
<root> <root>
<resheader name="resmimetype"> <resheader name="resmimetype">
<value>text/microsoft-resx</value> <value>text/microsoft-resx</value>
@@ -63,18 +63,9 @@
<data name="Privacy_PersistUnknown_Description" xml:space="preserve"> <data name="Privacy_PersistUnknown_Description" xml:space="preserve">
<value>為未來 FFXIV 更新新增而插件尚未識別的 ChatType 提供安全網。預設為關閉(資料最小化)。若想讓未來的頻道也完整記錄,請啟用此選項。</value> <value>為未來 FFXIV 更新新增而插件尚未識別的 ChatType 提供安全網。預設為關閉(資料最小化)。若想讓未來的頻道也完整記錄,請啟用此選項。</value>
</data> </data>
<data name="Cleanup_Heading" xml:space="preserve">
<value>將篩選器套用至現有資料庫</value>
</data>
<data name="Cleanup_Help_Intro" xml:space="preserve"> <data name="Cleanup_Help_Intro" xml:space="preserve">
<value>隱私篩選器只影響新訊息。下方的清理功能讓你可以追溯刪除已儲存但不符合白名單的訊息。</value> <value>隱私篩選器只影響新訊息。下方的清理功能讓你可以追溯刪除已儲存但不符合白名單的訊息。</value>
</data> </data>
<data name="Cleanup_Help_SavedNote" xml:space="preserve">
<value>清理使用的是已儲存的白名單(Plugin.Config),而非上方未儲存的變更。若要套用目前的變更,請先點擊儲存。</value>
</data>
<data name="Retention_Help_SavedNote" xml:space="preserve">
<value>手動執行使用的是已儲存的保留原則,而非上方的滑桿數值。若要套用目前的變更,請先點擊儲存。</value>
</data>
<data name="Cleanup_Preview_Stale" xml:space="preserve"> <data name="Cleanup_Preview_Stale" xml:space="preserve">
<value>預覽已過時,你的白名單自上次重新整理後已有變更。點擊重新整理以重新計算。</value> <value>預覽已過時,你的白名單自上次重新整理後已有變更。點擊重新整理以重新計算。</value>
</data> </data>
@@ -159,9 +150,6 @@
<data name="Retention_Apply_Label" xml:space="preserve"> <data name="Retention_Apply_Label" xml:space="preserve">
<value>立即套用保留原則</value> <value>立即套用保留原則</value>
</data> </data>
<data name="Retention_Apply_Tooltip" xml:space="preserve">
<value>Ctrl+Shift:使用已儲存的原則立即執行保留清理。請先儲存你的變更。</value>
</data>
<data name="Retention_Running" xml:space="preserve"> <data name="Retention_Running" xml:space="preserve">
<value>保留清理正在背景執行中…</value> <value>保留清理正在背景執行中…</value>
</data> </data>
@@ -273,9 +261,6 @@
<data name="Wizard_Step3_Section_Visual" xml:space="preserve"> <data name="Wizard_Step3_Section_Visual" xml:space="preserve">
<value>視覺</value> <value>視覺</value>
</data> </data>
<data name="Wizard_Step3_LoadPreviousSession_Label" xml:space="preserve">
<value>啟動時載入上次的會話</value>
</data>
<data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve"> <data name="Wizard_Step3_FilterIncludePreviousSessions_Label" xml:space="preserve">
<value>將篩選器套用至先前會話的訊息</value> <value>將篩選器套用至先前會話的訊息</value>
</data> </data>
@@ -318,9 +303,6 @@
<data name="Wizard_Step4_SettingsHint" xml:space="preserve"> <data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>設定 → Hellion Chat 可在之後進行細部調整</value> <value>設定 → Hellion Chat 可在之後進行細部調整</value>
</data> </data>
<data name="Export_Heading" xml:space="preserve">
<value>匯出(GDPR Art. 15 — 查閱權)</value>
</data>
<data name="Export_Help" xml:space="preserve"> <data name="Export_Help" xml:space="preserve">
<value>將已儲存的訊息匯出為 Markdown、JSON 或 CSV 格式。這讓你可以回應你所儲存訊息的當事人提出的查閱請求,或帶走自己的聊天歷史。</value> <value>將已儲存的訊息匯出為 Markdown、JSON 或 CSV 格式。這讓你可以回應你所儲存訊息的當事人提出的查閱請求,或帶走自己的聊天歷史。</value>
</data> </data>
@@ -457,7 +439,7 @@
<value>Chat 2 社群譯者(上游)</value> <value>Chat 2 社群譯者(上游)</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Runtime strings) -->
<data name="AutoTellTabs_SectionHeader" xml:space="preserve"> <data name="AutoTellTabs_SectionHeader" xml:space="preserve">
<value>進行中的悄悄話</value> <value>進行中的悄悄話</value>
</data> </data>
@@ -504,9 +486,9 @@
<value>已釘選,重新登入後仍會保留。</value> <value>已釘選,重新登入後仍會保留。</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Chat settings tab) -->
<data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Section_Title" xml:space="preserve">
<value>Auto-Tell-Tabs</value> <value>自動悄悄話標籤頁</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Enable_Name" xml:space="preserve">
<value>每收發一則 /tell 自動為對話對象開啟標籤頁</value> <value>每收發一則 /tell 自動為對話對象開啟標籤頁</value>
@@ -515,7 +497,7 @@
<value>一旦收到或傳送 /tell,就會自動為該玩家開啟暫時標籤頁。登出時標籤頁將被移除。</value> <value>一旦收到或傳送 /tell,就會自動為該玩家開啟暫時標籤頁。登出時標籤頁將被移除。</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Limit_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Limit_Name" xml:space="preserve">
<value>Auto-Tell 標籤頁的最大數量</value> <value>Auto-悄悄話 標籤頁的最大數量</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Limit_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Limit_Description" xml:space="preserve">
<value>達到上限時,活動最舊的已問候標籤頁會優先關閉。變更在下次 /tell 時生效。此限制僅適用於自動管理的標籤頁池。釘選的悄悄話標籤頁(右鍵 → 釘選標籤頁)存在於獨立的最多 5 個標籤頁池中,並在重新登入後保留。</value> <value>達到上限時,活動最舊的已問候標籤頁會優先關閉。變更在下次 /tell 時生效。此限制僅適用於自動管理的標籤頁池。釘選的悄悄話標籤頁(右鍵 → 釘選標籤頁)存在於獨立的最多 5 個標籤頁池中,並在重新登入後保留。</value>
@@ -524,13 +506,13 @@
<value>緊湊顯示</value> <value>緊湊顯示</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_Compact_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_Compact_Description" xml:space="preserve">
<value>在一般標籤頁和 Auto-Tell 標籤頁之間只顯示一條細分隔線,不顯示區段標題。</value> <value>在一般標籤頁和 Auto-悄悄話 標籤頁之間只顯示一條細分隔線,不顯示區段標題。</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_GreetedToggle_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_GreetedToggle_Name" xml:space="preserve">
<value>顯示「標記為已問候」按鈕</value> <value>顯示「標記為已問候」按鈕</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_GreetedToggle_Description" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_GreetedToggle_Description" xml:space="preserve">
<value>在每個 Auto-Tell 標籤頁旁新增一個點擊按鈕,用於將對話對象標記為已問候,標籤頁名稱將隨之變暗。適合同時管理多個對話的接待人員使用。預設為關閉。</value> <value>在每個 Auto-悄悄話 標籤頁旁新增一個點擊按鈕,用於將對話對象標記為已問候,標籤頁名稱將隨之變暗。適合同時管理多個對話的接待人員使用。預設為關閉。</value>
</data> </data>
<data name="ChatLog_AutoTellTabs_OpenAsPopout_Name" xml:space="preserve"> <data name="ChatLog_AutoTellTabs_OpenAsPopout_Name" xml:space="preserve">
<value>直接以彈出視窗開啟新的 /tell 標籤頁</value> <value>直接以彈出視窗開啟新的 /tell 標籤頁</value>
@@ -545,7 +527,7 @@
<value>注意:若 XIV Messenger 或類似插件抑制了悄悄話,請在該插件中停用「Suppress DMs」選項,使 Hellion Chat 能夠接收悄悄話並開啟自動標籤頁。</value> <value>注意:若 XIV Messenger 或類似插件抑制了悄悄話,請在該插件中停用「Suppress DMs」選項,使 Hellion Chat 能夠接收悄悄話並開啟自動標籤頁。</value>
</data> </data>
<!-- Hellion Chat — Auto-Tell-Tabs (Privacy settings tab) -->
<data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Section_Title" xml:space="preserve">
<value>自動標籤頁中的悄悄話歷史</value> <value>自動標籤頁中的悄悄話歷史</value>
</data> </data>
@@ -553,21 +535,15 @@
<value>預載悄悄話的數量</value> <value>預載悄悄話的數量</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Description" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Description" xml:space="preserve">
<value>開啟 Auto-Tell 標籤頁時從資料庫載入的先前悄悄話訊息數量。設為 0 則停用預載。</value> <value>開啟 Auto-悄悄話 標籤頁時從資料庫載入的先前悄悄話訊息數量。設為 0 則停用預載。</value>
</data> </data>
<data name="Privacy_AutoTellTabs_Preload_Hint" xml:space="preserve"> <data name="Privacy_AutoTellTabs_Preload_Hint" xml:space="preserve">
<value>只有在聊天標籤頁中啟用 Auto-Tell 標籤頁後才會生效。</value> <value>只有在聊天標籤頁中啟用 Auto-悄悄話 標籤頁後才會生效。</value>
</data> </data>
<!-- Hellion Chat — Settings UX Polish v10 Wipe migration -->
<data name="SettingsRefactor_Migration_Title" xml:space="preserve">
<value>設定已重新整理</value>
</data>
<data name="SettingsRefactor_Migration_Content" xml:space="preserve">
<value>Hellion Chat 0.5.0 已將設定重新整理為主題式標籤頁。你的聊天資料庫和訊息歷史保持不變。設定已重設為預設值。若要重新選擇隱私設定檔,請至隱私標籤頁中點擊重新開啟按鈕。先前設定的備份位於 HellionChat.json.pre-v10-backup,與目前使用中的設定檔位於同一目錄。</value>
</data>
<!-- Hellion Chat — Settings UX Polish 8-tab structure -->
<data name="Settings_Tab_General" xml:space="preserve"> <data name="Settings_Tab_General" xml:space="preserve">
<value>一般</value> <value>一般</value>
</data> </data>
@@ -590,9 +566,9 @@
<value>關於</value> <value>關於</value>
</data> </data>
<!-- Hellion Chat — General tab section headings -->
<!-- Hellion Chat — Appearance tab section headings -->
<data name="Settings_Appearance_Theme_Heading" xml:space="preserve"> <data name="Settings_Appearance_Theme_Heading" xml:space="preserve">
<value>佈景主題</value> <value>佈景主題</value>
</data> </data>
@@ -606,14 +582,14 @@
<value>時間戳</value> <value>時間戳</value>
</data> </data>
<!-- Hellion Chat — Window tab section headings -->
<data name="Settings_Window_Frame_Heading" xml:space="preserve"> <data name="Settings_Window_Frame_Heading" xml:space="preserve">
<value>視窗框架</value> <value>視窗框架</value>
</data> </data>
<!-- Hellion Chat — Chat tab section headings -->
<!-- Hellion Chat — Chat tab SymbolPicker -->
<data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve"> <data name="Settings_Chat_SymbolPicker_Enable_Name" xml:space="preserve">
<value>在聊天輸入框旁顯示符號選擇器按鈕</value> <value>在聊天輸入框旁顯示符號選擇器按鈕</value>
</data> </data>
@@ -621,20 +597,11 @@
<value>在頻道指示器左側新增一個小按鈕,點擊後開啟含有 FFXIV 圖示和精選符號列表的彈出視窗。若偏好更簡潔的輸入列,可停用此選項。</value> <value>在頻道指示器左側新增一個小按鈕,點擊後開啟含有 FFXIV 圖示和精選符號列表的彈出視窗。若偏好更簡潔的輸入列,可停用此選項。</value>
</data> </data>
<!-- Hellion Chat — Database tab section headings -->
<data name="Settings_Database_Storage_Heading" xml:space="preserve">
<value>儲存</value>
</data>
<data name="Settings_Database_Viewer_Heading" xml:space="preserve">
<value>概覽</value>
</data>
<data name="Settings_Database_Stats_Heading" xml:space="preserve">
<value>維護</value>
</data>
<!-- Hellion Chat — Information tab section headings -->
<!-- Hellion Chat — Default tab presets (channel-specific) -->
<data name="Tabs_Presets_System" xml:space="preserve"> <data name="Tabs_Presets_System" xml:space="preserve">
<value>系統</value> <value>系統</value>
</data> </data>
@@ -654,7 +621,7 @@
<value>若你使用多個通訊貝,維護者建議每個通訊貝建立一個標籤頁以獲得更清晰的概覽。複製標籤頁,並在每個副本中限制頻道選擇。</value> <value>若你使用多個通訊貝,維護者建議每個通訊貝建立一個標籤頁以獲得更清晰的概覽。複製標籤頁,並在每個副本中限制頻道選擇。</value>
</data> </data>
<!-- Hellion Chat — v1.2.0 per-tab icon override -->
<data name="Tabs_Icon_Label" xml:space="preserve"> <data name="Tabs_Icon_Label" xml:space="preserve">
<value>標籤頁圖示</value> <value>標籤頁圖示</value>
</data> </data>
@@ -692,7 +659,7 @@
<value>啟用彈出視窗中的輸入功能</value> <value>啟用彈出視窗中的輸入功能</value>
</data> </data>
<data name="Settings_Window_PopOutInputEnabled_Description" xml:space="preserve"> <data name="Settings_Window_PopOutInputEnabled_Description" xml:space="preserve">
<value>主開關:允許在任何彈出視窗(包括 Auto-Tell 標籤頁)中直接輸入和傳送訊息。彈出視窗中的頻道切換會像主視窗一樣全域生效;文字緩衝區和歷史游標則各彈出視窗獨立。</value> <value>主開關:允許在任何彈出視窗(包括 Auto-悄悄話 標籤頁)中直接輸入和傳送訊息。彈出視窗中的頻道切換會像主視窗一樣全域生效;文字緩衝區和歷史游標則各彈出視窗獨立。</value>
</data> </data>
<data name="Settings_Window_ResetPosition_Name" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Name" xml:space="preserve">
<value>重設視窗位置</value> <value>重設視窗位置</value>
@@ -700,24 +667,6 @@
<data name="Settings_Window_ResetPosition_Description" xml:space="preserve"> <data name="Settings_Window_ResetPosition_Description" xml:space="preserve">
<value>將聊天視窗和所有已開啟的彈出視窗移回主螢幕左上角。適用於在顯示器配置變更後(螢幕斷線、解析度變更)視窗跑到可見區域外的情況。插件每次工作階段也會自動執行一次邊界檢查;若仍有視窗無法存取,此按鈕是手動應急出口。</value> <value>將聊天視窗和所有已開啟的彈出視窗移回主螢幕左上角。適用於在顯示器配置變更後(螢幕斷線、解析度變更)視窗跑到可見區域外的情況。插件每次工作階段也會自動執行一次邊界檢查;若仍有視窗無法存取,此按鈕是手動應急出口。</value>
</data> </data>
<data name="Popout_v060_HintText" xml:space="preserve">
<value>v0.6.0 新功能:現在可以直接在彈出視窗中輸入。請在視窗設定中啟用主開關。</value>
</data>
<data name="Popout_v060_HintAck" xml:space="preserve">
<value>了解</value>
</data>
<data name="Popout_v060_HintOpenSettings" xml:space="preserve">
<value>開啟視窗設定</value>
</data>
<data name="Hint_v061_PopOutHeader_Body" xml:space="preserve">
<value>你可以將任何聊天標籤頁以獨立視窗開啟。點擊右上角的視窗圖示,或右鍵點擊標籤頁。v0.6.1 新功能:彈出視窗輸入功能預設為啟用(可在設定 → 視窗中停用)。</value>
</data>
<data name="Hint_v061_PopOutHeader_Ack" xml:space="preserve">
<value>了解</value>
</data>
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
<value>開啟設定</value>
</data>
<data name="ChatTwoConflictTitle" xml:space="preserve"> <data name="ChatTwoConflictTitle" xml:space="preserve">
<value>Hellion Chat 無法在 Chat 2 已載入的情況下啟動。</value> <value>Hellion Chat 無法在 Chat 2 已載入的情況下啟動。</value>
</data> </data>
@@ -727,54 +676,6 @@
<data name="ChatTwoConflictAction" xml:space="preserve"> <data name="ChatTwoConflictAction" xml:space="preserve">
<value>請在 /xlplugins 中停用 Chat 2,然後重新啟用 Hellion Chat。</value> <value>請在 /xlplugins 中停用 Chat 2,然後重新啟用 Hellion Chat。</value>
</data> </data>
<data name="Settings_Card_General_Title" xml:space="preserve">
<value>一般</value>
</data>
<data name="Settings_Card_General_Subtext" xml:space="preserve">
<value>語言、輸入、音效和效能。</value>
</data>
<data name="Settings_Card_Appearance_Title" xml:space="preserve">
<value>外觀</value>
</data>
<data name="Settings_Card_Appearance_Subtext" xml:space="preserve">
<value>視窗不透明度、字型、動態效果</value>
</data>
<data name="Settings_Card_Themes_Title" xml:space="preserve">
<value>佈景主題</value>
</data>
<data name="Settings_Card_Themes_Subtext" xml:space="preserve">
<value>選擇佈景主題或匯入自訂主題</value>
</data>
<data name="Settings_Card_Window_Title" xml:space="preserve">
<value>視窗</value>
</data>
<data name="Settings_Card_Window_Subtext" xml:space="preserve">
<value>視窗的顯示時機以及是否可以移動。</value>
</data>
<data name="Settings_Card_Chat_Title" xml:space="preserve">
<value>聊天</value>
</data>
<data name="Settings_Card_Chat_Subtext" xml:space="preserve">
<value>悄悄話、預覽、訊息行為和情感動作。</value>
</data>
<data name="Settings_Card_Tabs_Title" xml:space="preserve">
<value>標籤頁</value>
</data>
<data name="Settings_Card_Tabs_Subtext" xml:space="preserve">
<value>建立和設定自訂聊天標籤頁。</value>
</data>
<data name="Settings_Card_Database_Title" xml:space="preserve">
<value>資料庫</value>
</data>
<data name="Settings_Card_Database_Subtext" xml:space="preserve">
<value>儲存、遷移、舊版清理</value>
</data>
<data name="Settings_Card_Information_Title" xml:space="preserve">
<value>關於</value>
</data>
<data name="Settings_Card_Information_Subtext" xml:space="preserve">
<value>擴充功能、版本、專案資訊、譯者和 Changelog。</value>
</data>
<data name="Settings_Tab_Themes" xml:space="preserve"> <data name="Settings_Tab_Themes" xml:space="preserve">
<value>佈景主題</value> <value>佈景主題</value>
</data> </data>
@@ -803,7 +704,7 @@
<value>保留</value> <value>保留</value>
</data> </data>
<data name="StatusBar_Privacy_Enabled" xml:space="preserve"> <data name="StatusBar_Privacy_Enabled" xml:space="preserve">
<value>Privacy-First</value> <value>隱私優先</value>
</data> </data>
<data name="StatusBar_Privacy_Open" xml:space="preserve"> <data name="StatusBar_Privacy_Open" xml:space="preserve">
<value>開放</value> <value>開放</value>
@@ -817,9 +718,6 @@
<data name="Settings_Card_DataManagement_Title" xml:space="preserve"> <data name="Settings_Card_DataManagement_Title" xml:space="preserve">
<value>資料與隱私</value> <value>資料與隱私</value>
</data> </data>
<data name="Settings_Card_DataManagement_Subtext" xml:space="preserve">
<value>隱私過濾器、保留、清理、匯出和資料庫統計資訊。</value>
</data>
<data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve"> <data name="Settings_ThemeAndLayout_Theme_Heading" xml:space="preserve">
<value>佈景主題</value> <value>佈景主題</value>
</data> </data>
@@ -838,9 +736,6 @@
<data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve"> <data name="Settings_DataManagement_Advanced_Heading" xml:space="preserve">
<value>進階(Shift+點擊開啟)</value> <value>進階(Shift+點擊開啟)</value>
</data> </data>
<data name="Migration_v16_OverrideStyle_Toast" xml:space="preserve">
<value>Hellion Chat 1.2.1 已重新整理設定選單,並移除舊的「覆寫樣式」選項(已由 1.1.0 的佈景主題系統取代)。你其餘的設定保持不變。視窗透明度已遷移至「Theme &amp; Layout」。先前設定的備份位於 pluginConfigs/HellionChat.json.pre-v16-backup,與目前使用中的 HellionChat.json 位於同一目錄。</value>
</data>
<data name="Settings_Integrations_Intro" xml:space="preserve"> <data name="Settings_Integrations_Intro" xml:space="preserve">
<value>插件整合功能讓 HellionChat 能與其他已安裝的 Dalamud 插件協同運作。每個整合功能會自動偵測目標插件,並在目標插件不存在時靜默停用自身。</value> <value>插件整合功能讓 HellionChat 能與其他已安裝的 Dalamud 插件協同運作。每個整合功能會自動偵測目標插件,並在目標插件不存在時靜默停用自身。</value>
</data> </data>
@@ -956,7 +851,7 @@
<comment>AI-assisted machine translation. Pending native-speaker review.</comment> <comment>AI-assisted machine translation. Pending native-speaker review.</comment>
</data> </data>
<!-- Hellion Chat — Settings Overhaul section titles (v1.5.6) -->
<data name="Settings_Section_Input" xml:space="preserve"> <data name="Settings_Section_Input" xml:space="preserve">
<value>輸入</value> <value>輸入</value>
</data> </data>
@@ -997,7 +892,7 @@
<value>輸入與預覽</value> <value>輸入與預覽</value>
</data> </data>
<data name="Settings_Section_AutoTellTabs" xml:space="preserve"> <data name="Settings_Section_AutoTellTabs" xml:space="preserve">
<value>自動Tell標籤頁</value> <value>自動悄悄話標籤頁</value>
</data> </data>
<data name="Settings_Section_Emotes" xml:space="preserve"> <data name="Settings_Section_Emotes" xml:space="preserve">
<value>情感動作</value> <value>情感動作</value>
@@ -1146,4 +1041,321 @@
<data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve"> <data name="ChatInput_PluginDisclosure_Warning" xml:space="preserve">
<value>該訊息包含插件專用符號,其他玩家可能看到空白方框。再次按 Enter 鍵強制發送。</value> <value>該訊息包含插件專用符號,其他玩家可能看到空白方框。再次按 Enter 鍵強制發送。</value>
</data> </data>
</root> <data name="InputBar_InsertSymbol_Tooltip" xml:space="preserve">
<value>插入符號</value>
</data>
<data name="InputBar_Settings_Tooltip" xml:space="preserve">
<value>設定</value>
</data>
<data name="InputBar_HideChat_Tooltip" xml:space="preserve">
<value>隱藏聊天(按 Enter 恢復)</value>
</data>
<data name="InputBar_PopIn_Tooltip" xml:space="preserve"><value>將此分頁移回主視窗</value>
</data>
<data name="Settings_Database_Busy" xml:space="preserve">
<value>另一項資料庫作業正在進行:{0}</value>
</data>
<data name="Settings_Database_Op_RetentionSweep" xml:space="preserve">
<value>保留期清理</value>
</data>
<data name="Settings_Database_Op_Export" xml:space="preserve">
<value>匯出</value>
</data>
<data name="Settings_Database_Op_Cleanup" xml:space="preserve">
<value>清理</value>
</data>
<data name="Settings_Database_Op_Clear" xml:space="preserve">
<value>清除歷史紀錄</value>
</data>
<data name="Cleanup_Unavailable_FilterOff" xml:space="preserve">
<value>隱私篩選器已關閉,因此所有頻道都會保存,資料庫中沒有與設定衝突的內容。請先開啟篩選器並選擇頻道。</value>
</data>
<data name="Cleanup_Unavailable_NothingListed" xml:space="preserve">
<value>未選擇任何頻道,因此清理會刪除全部歷史紀錄。請選擇要保留的頻道,若確實想全部刪除,請使用清除按鈕。</value>
</data>
<data name="Settings_Database_ClearHint" xml:space="preserve">
<value>已保存 {0:N0} 則訊息。若想留一份副本,請在清除前先匯出。</value>
</data>
<data name="Retention_RunNow_Tooltip" xml:space="preserve">
<value>Ctrl+Shift:立即執行保留期清理,無需等待每日執行。刪除早於上方期限的訊息。</value>
</data>
<data name="Settings_Database_ClearError" xml:space="preserve">
<value>清除歷史紀錄失敗。沒有刪除任何內容,請查看 /xllog。</value>
</data>
<data name="Settings_Section_Telemetry" xml:space="preserve">
<value>遙測</value>
</data>
<data name="Settings_Telemetry_None" xml:space="preserve">
<value>不收集任何遙測資料。外掛不會向任何地方傳送關於你或你使用情況的資訊。</value>
</data>
<data name="Settings_Section_AutoTranslate" xml:space="preserve">
<value>自動翻譯</value>
</data>
<data name="Settings_Emotes_Block" xml:space="preserve">
<value>封鎖</value>
</data>
<data name="ChatLog_ScrollToBottom_Tooltip" xml:space="preserve">
<value>跳到最新訊息</value>
</data>
<data name="ChatLog_Insert_MapFlag" xml:space="preserve">
<value>插入地圖標記 &lt;flag&gt;</value>
</data>
<data name="ChatLog_Insert_ItemLink" xml:space="preserve">
<value>插入關聯物品 &lt;item&gt;</value>
</data>
<data name="Wizard_Step4_Summary_Off" xml:space="preserve">
<value>關閉</value>
</data>
<data name="Settings_Section_Behaviour" xml:space="preserve">
<value>行為</value>
</data>
<data name="Settings_Section_Keybinds" xml:space="preserve">
<value>快速鍵</value>
</data>
<data name="Settings_Section_Notifications" xml:space="preserve">
<value>通知</value>
</data>
<data name="Settings_Section_DisplayModes" xml:space="preserve">
<value>顯示模式</value>
</data>
<data name="Settings_Section_History" xml:space="preserve">
<value>歷史紀錄</value>
</data>
<data name="Settings_Section_CommandHelp" xml:space="preserve">
<value>指令說明</value>
</data>
<data name="Settings_Section_PluginDisclosure" xml:space="preserve">
<value>外掛提示</value>
</data>
<data name="Settings_Section_LayoutMode" xml:space="preserve">
<value>版面模式</value>
</data>
<data name="Settings_Section_Opacity" xml:space="preserve">
<value>不透明度</value>
</data>
<data name="Settings_Section_ResizeBehaviour" xml:space="preserve">
<value>調整大小行為</value>
</data>
<data name="Settings_Section_TellAutoOpen" xml:space="preserve">
<value>自動開啟悄悄話</value>
</data>
<data name="Settings_Section_Sidebar" xml:space="preserve">
<value>側邊欄</value>
</data>
<data name="Settings_Section_Brand" xml:space="preserve">
<value>品牌</value>
</data>
<data name="Settings_Section_Links" xml:space="preserve">
<value>連結</value>
</data>
<data name="Settings_Section_Integrations" xml:space="preserve">
<value>整合</value>
</data>
<data name="Settings_Section_Credits" xml:space="preserve">
<value>製作人員</value>
</data>
<data name="Settings_Section_License" xml:space="preserve">
<value>授權</value>
</data>
<data name="Settings_Keybinds_Hint" xml:space="preserve">
<value>點擊按鈕後按下組合鍵。Esc 清除。</value>
</data>
<data name="Settings_Keybinds_CycleNext" xml:space="preserve">
<value>切換到下一個分頁</value>
</data>
<data name="Settings_Keybinds_CyclePrevious" xml:space="preserve">
<value>切換到上一個分頁</value>
</data>
<data name="Settings_General_Language_Description" xml:space="preserve">
<value>切換會重建字型圖集,因此聊天會短暫空白。</value>
</data>
<data name="Settings_Chat_Clock24_Name" xml:space="preserve">
<value>24 小時制</value>
</data>
<data name="Settings_Chat_PreviousSessions_Name" xml:space="preserve">
<value>顯示先前工作階段的歷史紀錄</value>
</data>
<data name="Settings_Chat_PreviousSessions_Description" xml:space="preserve">
<value>關閉時,日誌在每次啟動遊戲時都是空的,只會填入此後收到的訊息。</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Name" xml:space="preserve">
<value>指令說明的位置</value>
</data>
<data name="Settings_Chat_CommandHelpSide_Description" xml:space="preserve">
<value>輸入時指令提示清單出現在哪一側。</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Name" xml:space="preserve">
<value>悄悄話自動開啟方式</value>
</data>
<data name="Settings_Channels_TellAutoOpenMode_Description" xml:space="preserve">
<value>悄悄話到達時在何處開啟。</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Name" xml:space="preserve">
<value>每則悄悄話都切換到分頁</value>
</data>
<data name="Settings_Channels_TellSwitchAlways_Description" xml:space="preserve">
<value>否則分頁會在第一則之後於背景開啟。</value>
</data>
<data name="Settings_Window_LayoutSidebar" xml:space="preserve">
<value>側邊欄</value>
</data>
<data name="Settings_Window_LayoutTopTabs" xml:space="preserve">
<value>頂部分頁</value>
</data>
<data name="Settings_Window_TabPlacement_Name" xml:space="preserve">
<value>分頁位置</value>
</data>
<data name="Settings_Window_TabPlacement_Description" xml:space="preserve">
<value>分頁清單在主視窗中的位置。</value>
</data>
<data name="Settings_Window_TitleBar_Name" xml:space="preserve">
<value>顯示標題列</value>
</data>
<data name="Settings_Window_PopoutTitleBar_Name" xml:space="preserve">
<value>顯示彈出視窗的標題列</value>
</data>
<data name="Settings_Window_AllowMove_Name" xml:space="preserve">
<value>允許移動</value>
</data>
<data name="Settings_Window_AllowResize_Name" xml:space="preserve">
<value>允許調整大小</value>
</data>
<data name="Settings_Window_SidebarThreshold_Name" xml:space="preserve">
<value>側邊欄自動切換閾值</value>
</data>
<data name="Settings_Window_SidebarThreshold_Description" xml:space="preserve">
<value>低於此寬度時側邊欄會摺疊為頂部分頁,單位為像素。</value>
</data>
<data name="Settings_Window_PreviewPosition_Name" xml:space="preserve">
<value>預覽位置</value>
</data>
<data name="Settings_Window_PreviewOnlyTyping_Name" xml:space="preserve">
<value>僅在輸入時顯示預覽</value>
</data>
<data name="Settings_About_GiteaRepo" xml:space="preserve">
<value>Gitea 儲存庫</value>
</data>
<data name="Settings_About_CustomRepo" xml:space="preserve">
<value>自訂儲存庫資訊清單</value>
</data>
<data name="Settings_Theme_ActiveTheme" xml:space="preserve">
<value>目前主題:{0}</value>
</data>
<data name="Settings_Theme_ForkAndEdit" xml:space="preserve">
<value>複製並編輯</value>
</data>
<data name="Settings_Theme_ForkTooltip" xml:space="preserve">
<value>內建主題無法直接編輯。複製會建立一份可編輯並儲存的自訂副本。</value>
</data>
<data name="Settings_Theme_EditTheme" xml:space="preserve">
<value>編輯主題</value>
</data>
<data name="Settings_Theme_Editing" xml:space="preserve">
<value>正在編輯:{0}</value>
</data>
<data name="Settings_Theme_Group_Surfaces" xml:space="preserve">
<value>表面</value>
</data>
<data name="Settings_Theme_Group_Borders" xml:space="preserve">
<value>邊框</value>
</data>
<data name="Settings_Theme_Group_Text" xml:space="preserve">
<value>文字</value>
</data>
<data name="Settings_Theme_Group_Identity" xml:space="preserve">
<value>身分</value>
</data>
<data name="Settings_Theme_Group_Status" xml:space="preserve">
<value>狀態</value>
</data>
<data name="Settings_Theme_Save" xml:space="preserve">
<value>儲存</value>
</data>
<data name="Settings_Theme_Cancel" xml:space="preserve">
<value>取消</value>
</data>
<data name="Settings_Theme_ResetToSource" xml:space="preserve">
<value>重設為原始主題</value>
</data>
<data name="Settings_Theme_ResetUnavailable" xml:space="preserve">
<value>編輯副本時無法重設。請先儲存或取消。</value>
</data>
<data name="Settings_Theme_LockedTooltip" xml:space="preserve">
<value>請先儲存或捨棄你的變更</value>
</data>
<data name="Settings_Theme_Custom" xml:space="preserve">
<value>自訂 ({0})</value>
</data>
<data name="Settings_Theme_ForkActive" xml:space="preserve">
<value>複製目前主題</value>
</data>
<data name="Settings_Theme_ImportFile" xml:space="preserve">
<value>匯入主題檔案…</value>
</data>
<data name="Settings_Theme_ImportPathHint" xml:space="preserve">
<value>JSON 檔案路徑(或拖放到資料夾)</value>
</data>
<data name="Settings_Theme_ExportDialogTitle" xml:space="preserve">
<value>匯出主題</value>
</data>
<data name="Settings_Theme_Category_Cool" xml:space="preserve">
<value>冷色</value>
</data>
<data name="Settings_Theme_Category_Natural" xml:space="preserve">
<value>自然</value>
</data>
<data name="Settings_Theme_Category_Classic" xml:space="preserve">
<value>經典</value>
</data>
<data name="Settings_Theme_Category_Retro" xml:space="preserve">
<value>復古</value>
</data>
<data name="Settings_Fonts_Bundled" xml:space="preserve">
<value>Hellion Inter(內建)</value>
</data>
<data name="Settings_Fonts_GameFont" xml:space="preserve">
<value>遊戲字型</value>
</data>
<data name="Settings_Fonts_Global" xml:space="preserve">
<value>全域:{0}</value>
</data>
<data name="Settings_Fonts_Active" xml:space="preserve">
<value>使用中:{0}</value>
</data>
<data name="Settings_Preview_TypeAMessage" xml:space="preserve">
<value>輸入訊息...</value>
</data>
<data name="Settings_Tabs_Duplicate" xml:space="preserve">
<value>複製</value>
</data>
<data name="Settings_Database_Op_Preview" xml:space="preserve">
<value>預覽</value>
</data>
<data name="Settings_Database_Op_Maintenance" xml:space="preserve">
<value>維護</value>
</data>
<data name="StatusBar_Tabs_One" xml:space="preserve">
<value>{0} 分頁</value>
</data>
<data name="StatusBar_Tabs_Other" xml:space="preserve">
<value>{0} 分頁</value>
</data>
<data name="StatusBar_Tells_One" xml:space="preserve">
<value>{0} 悄悄話</value>
</data>
<data name="StatusBar_Tells_Other" xml:space="preserve">
<value>{0} 悄悄話</value>
</data>
<data name="StatusBar_Messages" xml:space="preserve">
<value>{0} 則</value>
</data>
<data name="StatusBar_MessagesThousands" xml:space="preserve">
<value>{0:0.0}k 則</value>
</data>
<data name="Settings_Preview_TitleMock" xml:space="preserve">
<value>«冠軍» 預覽</value>
</data>
<data name="Settings_Preview_StatusOpen" xml:space="preserve">
<value>開啟</value>
</data>
</root>
+111
View File
@@ -0,0 +1,111 @@
using System.Collections.Generic;
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Util;
namespace HellionChat.SelfTests;
// B2: behavioural check that the card path feeds CardClipPlanner AND that a
// layout change clears the height cache — not a non-null check. Pure plan math
// is pinned headless by CardClipPlanTests; this drives the live accessors.
internal sealed class CardClipPlanStep : ISelfTestStep
{
private readonly Plugin plugin;
public CardClipPlanStep(Plugin plugin) => this.plugin = plugin;
public string Name => "Hellion Chat - Card clip plan + cache invalidation";
public SelfTestStepResult RunStep()
{
var messages = plugin.MainWindow.GetMessageListForSelfTest();
if (messages is null)
{
ImGui.Text("MessageList null");
SelfTestReport.Append(Name, "FAIL", new[] { "MessageList null" });
return SelfTestStepResult.Fail;
}
// 5 rows of 20, viewport 50, scroll 45 -> skip rows 0,1 (lead 40), see 2..4.
IReadOnlyList<float> heights = [20f, 20f, 20f, 20f, 20f];
var plan = messages.PlanCardClipForSelfTest(heights, scrollY: 45f, viewportHeight: 50f);
if (
plan.FirstVisible != 2
|| plan.LastVisible != 4
|| plan.LeadDummyHeight is < 39.9f or > 40.1f
)
{
var msg =
$"Plan wrong: first={plan.FirstVisible} last={plan.LastVisible} lead={plan.LeadDummyHeight}";
ImGui.Text(msg);
SelfTestReport.Append(Name, "FAIL", new[] { msg });
return SelfTestStepResult.Fail;
}
var tab = plugin.MainWindow.ActiveTab;
if (tab is null)
{
ImGui.Text("No active tab");
SelfTestReport.Append(Name, "FAIL", new[] { "No active tab" });
return SelfTestStepResult.Fail;
}
// Flip a height-affecting mode -> fingerprint changes -> cache must drop.
// Config restored in finally (live-singleton discipline).
var savedForm = Plugin.Config.NameFormMode;
int remaining;
try
{
// v1.10.0/A1: the fingerprint gate waits for the value to settle, so
// the step walks a synthetic clock past the window instead of sleeping.
var clock = Environment.TickCount64;
messages.RunHeightCacheInvalidationForSelfTest(tab, 400f, clock);
using (var snap = tab.Messages.GetReadOnly(3))
{
if (snap.Count > 0)
snap[0].Height[tab.Identifier] = 42f;
}
Plugin.Config.NameFormMode =
savedForm == NameFormMode.Full ? NameFormMode.Initials : NameFormMode.Full;
messages.RunHeightCacheInvalidationForSelfTest(tab, 400f, clock);
clock += LayoutFingerprintGate.SettleMs;
remaining = messages.RunHeightCacheInvalidationForSelfTest(tab, 400f, clock);
}
finally
{
Plugin.Config.NameFormMode = savedForm;
var restore = Environment.TickCount64 + LayoutFingerprintGate.SettleMs * 2;
messages.RunHeightCacheInvalidationForSelfTest(tab, 400f, restore);
messages.RunHeightCacheInvalidationForSelfTest(
tab,
400f,
restore + LayoutFingerprintGate.SettleMs
);
}
if (remaining != 0)
{
var msg = $"Cache not cleared after layout change: {remaining} left";
ImGui.Text(msg);
SelfTestReport.Append(Name, "FAIL", new[] { msg });
return SelfTestStepResult.Fail;
}
SelfTestReport.Append(
Name,
"PASS",
new[]
{
$"Plan range [{plan.FirstVisible}..{plan.LastVisible}], lead={plan.LeadDummyHeight}",
"Height cache cleared after layout-fingerprint change",
}
);
ImGui.Text(
$"PASS — plan [{plan.FirstVisible}..{plan.LastVisible}], cache cleared on layout change."
);
return SelfTestStepResult.Pass;
}
public void CleanUp() { }
}
@@ -3,25 +3,41 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// Pins the post-migration shape of the v23 config. By /xlperf time the schema // Pins the post-migration shape of the config. By /xlperf time the schema gate
// gate has already stamped Config.Version = 23 and run the SidebarTabView→ // has already stamped Config.Version and run both migrations, so the fields
// TopTabs migration, so MainWindowLayoutMode must carry a valid value here. // below must carry valid values here. This probe never rewrites config; the
// This probe never rewrites config; the actual migration (false → TopTabs) is // migrations themselves are load-time and verified by the prepared-config smoke
// load-time and verified by the prepared-config smoke in the plan. // in the plan.
internal sealed class ConfigMigrationV23Step : ISelfTestStep internal sealed class ConfigMigrationV25Step : ISelfTestStep
{ {
public ConfigMigrationV23Step(Plugin plugin) public ConfigMigrationV25Step(Plugin plugin)
{ {
_ = plugin; _ = plugin;
} }
public string Name => "Hellion Chat - Config v23 migration"; public string Name => "Hellion Chat - Config v25 migration";
public SelfTestStepResult RunStep() public SelfTestStepResult RunStep()
{ {
if (Plugin.Config.Version != 23) if (Plugin.Config.Version != 25)
{ {
ImGui.Text($"Config.Version is {Plugin.Config.Version}, expected 23"); ImGui.Text($"Config.Version is {Plugin.Config.Version}, expected 25");
return SelfTestStepResult.Fail;
}
// The state the v24 migration exists to prevent: filter on, failsafe on,
// nothing picked. Under the corrected rule that combination stores no
// messages at all, so reaching /xlperf in it means the migration did not
// run.
if (
Privacy.StorageRule.ShouldDisableFilterOnV24(
Plugin.Config.PrivacyFilterEnabled,
Plugin.Config.PrivacyPersistUnknownChannels,
Plugin.Config.PrivacyPersistChannels.Count
)
)
{
ImGui.Text("Privacy filter is on with no channels picked — v24 migration did not run");
return SelfTestStepResult.Fail; return SelfTestStepResult.Fail;
} }
@@ -69,7 +69,10 @@ internal sealed class CurrentTabCouplingStep : ISelfTestStep
// Insert a victim at index 0: a regressed index-0 getter would return // Insert a victim at index 0: a regressed index-0 getter would return
// THIS instead of ActiveTab, so ReferenceEquals would catch it. // THIS instead of ActiveTab, so ReferenceEquals would catch it.
var victim = new Tab { Name = "selftest-coupling-victim" }; var victim = new Tab { Name = "selftest-coupling-victim" };
Plugin.Config.Tabs.Insert(0, victim); // Insert shifts every index; the worker's DropOldestTempTab holds
// TabsListLock across its index lookup and removal.
lock (_plugin.TabsListLock)
Plugin.Config.Tabs.Insert(0, victim);
try try
{ {
if (!ReferenceEquals(_plugin.CurrentTab, _plugin.MainWindow.ActiveTab)) if (!ReferenceEquals(_plugin.CurrentTab, _plugin.MainWindow.ActiveTab))
@@ -97,7 +100,8 @@ internal sealed class CurrentTabCouplingStep : ISelfTestStep
} }
finally finally
{ {
Plugin.Config.Tabs.Remove(victim); lock (_plugin.TabsListLock)
Plugin.Config.Tabs.Remove(victim);
} }
} }
finally finally
+131
View File
@@ -0,0 +1,131 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Ui.Components.Settings;
using HellionChat.Util;
namespace HellionChat.SelfTests;
// The gate has fourteen unit tests. None of them prove that the workers are
// wired to it, and that is where the mistake actually happened: the metadata
// refresh reached the store without taking the gate at all, and its flag was
// missing from the tab's shared busy state, so a wipe could start while it held
// the read lock.
//
// A unit test cannot see that. It needs the real singleton, the real flags and
// the real settings tab, so it lives here.
internal sealed class DbGateWiringStep : ISelfTestStep
{
private readonly Plugin _plugin;
public DbGateWiringStep(Plugin plugin) => _plugin = plugin;
public string Name => "Hellion Chat - DB gate wiring";
public SelfTestStepResult RunStep()
{
var failures = new List<string>();
// Nothing may be running when the probe starts, or the assertions below
// measure somebody else's operation.
if (_plugin.DbOperations.IsBusy)
failures.Add($"gate already held by {_plugin.DbOperations.Current} before the probe");
CheckEveryOperationRoundTrips(failures);
CheckRefusalLeavesTheOwnerAlone(failures);
CheckRevisionTracksMutations(failures);
CheckTabSeesTheGate(failures);
foreach (var f in failures)
ImGui.Text(f);
SelfTestReport.Append(Name, failures.Count == 0 ? "PASS" : "FAIL", failures);
return failures.Count == 0 ? SelfTestStepResult.Pass : SelfTestStepResult.Fail;
}
private void CheckEveryOperationRoundTrips(List<string> failures)
{
foreach (var op in EnumValues<DbOperation>.All)
{
if (op == DbOperation.None)
continue;
if (!_plugin.DbOperations.TryBegin(op))
{
failures.Add($"{op}: TryBegin refused on a free gate");
continue;
}
if (_plugin.DbOperations.Current != op)
failures.Add($"{op}: gate reports {_plugin.DbOperations.Current} after TryBegin");
if (_plugin.DbOperations.TryBegin(op))
failures.Add($"{op}: a second TryBegin succeeded while it was held");
_plugin.DbOperations.End(op);
if (_plugin.DbOperations.IsBusy)
failures.Add($"{op}: still busy after End");
}
}
private void CheckRefusalLeavesTheOwnerAlone(List<string> failures)
{
if (!_plugin.DbOperations.TryBegin(DbOperation.Export))
{
failures.Add("could not take the gate for the refusal check");
return;
}
// A worker whose TryBegin was refused still runs its finally. Releasing
// there must not hand away somebody else's lock.
_plugin.DbOperations.End(DbOperation.Clear);
if (_plugin.DbOperations.Current != DbOperation.Export)
failures.Add("a foreign End released the gate");
_plugin.DbOperations.End(DbOperation.Export);
}
private void CheckRevisionTracksMutations(List<string> failures)
{
var before = _plugin.DbOperations.Revision;
_plugin.DbOperations.TryBegin(DbOperation.Export);
_plugin.DbOperations.End(DbOperation.Export);
if (_plugin.DbOperations.Revision != before)
failures.Add("export moved the revision; it cannot change a row");
_plugin.DbOperations.TryBegin(DbOperation.Cleanup);
_plugin.DbOperations.End(DbOperation.Cleanup);
if (_plugin.DbOperations.Revision == before)
failures.Add(
"cleanup did not move the revision; a stale preview would pass as current"
);
}
// The settings tab decides whether the destructive buttons are live. If it
// cannot see a held gate, two of them are clickable at once.
private void CheckTabSeesTheGate(List<string> failures)
{
if (!_plugin.DbOperations.TryBegin(DbOperation.Clear))
{
failures.Add("could not take the gate for the tab check");
return;
}
try
{
if (!_plugin.DataPrivacyTab.AnythingRunningForSelfTest)
failures.Add("the data and privacy tab does not see a held gate");
}
finally
{
_plugin.DbOperations.End(DbOperation.Clear);
}
if (_plugin.DataPrivacyTab.AnythingRunningForSelfTest)
failures.Add("the tab still reports busy after the gate was released");
}
public void CleanUp() { }
}
@@ -0,0 +1,187 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text;
using Dalamud.Plugin.SelfTest;
using HellionChat.Code;
using HellionChat.Export;
using HellionChat.Util;
namespace HellionChat.SelfTests;
// v1.12.0/A2: the exporter now reads text from the chunk lists instead of the
// raw SeStrings. That change is invisible to the build suite -- ExportToFile
// takes IEnumerable<Message>, Message needs SeString, and xUnit cannot load
// Dalamud.dll, so even an empty list fails before the body runs.
//
// So it is verified here, against real messages, with the three properties that
// actually matter:
//
// 1. Text survives the round trip. If the exporter ever reads SenderSource or
// ContentSource again, a message built without them comes out blank.
// 2. The format guard runs before the file is opened. It used to run after,
// so an unknown format left a zero-byte file where a previous export had
// been.
// 3. The write is atomic. A failure partway must not leave a file that opens
// cleanly and is quietly incomplete -- this is the path a GDPR access
// request goes out on.
internal sealed class ExportRoundTripStep : ISelfTestStep
{
public string Name => "Hellion Chat - Export round trip";
public SelfTestStepResult RunStep()
{
var dir = Path.Combine(Path.GetTempPath(), $"hellionchat-selftest-{Guid.NewGuid():N}");
Directory.CreateDirectory(dir);
var failures = new List<string>();
try
{
CheckTextSurvives(dir, failures);
CheckUnknownFormatKeepsExistingFile(dir, failures);
CheckNoLeftoverPartFile(dir, failures);
}
catch (Exception e)
{
failures.Add($"threw: {e.GetType().Name}: {e.Message}");
}
finally
{
TryCleanup(dir);
}
foreach (var f in failures)
ImGui.Text(f);
SelfTestReport.Append(Name, failures.Count == 0 ? "PASS" : "FAIL", failures);
return failures.Count == 0 ? SelfTestStepResult.Pass : SelfTestStepResult.Fail;
}
private static void CheckTextSurvives(string dir, List<string> failures)
{
const string sender = "Selftest Sender";
const string content = "selftest content marker";
var path = Path.Combine(dir, "roundtrip.csv");
var written = MessageExporter.ExportToFile(
path,
ExportFormat.Csv,
[Probe(sender, content)],
new MessageExporter.FilterDescription(null, null, null, null)
);
if (written != 1)
failures.Add($"expected 1 message written, got {written}");
var text = File.ReadAllText(path);
if (!text.Contains(sender, StringComparison.Ordinal))
failures.Add("sender missing from export -- reading SeString again?");
if (!text.Contains(content, StringComparison.Ordinal))
failures.Add("content missing from export -- reading SeString again?");
// The sender filter runs inside the exporter and must see the same text.
var filtered = Path.Combine(dir, "filtered.csv");
var hits = MessageExporter.ExportToFile(
filtered,
ExportFormat.Csv,
[Probe(sender, content), Probe("Someone Else", "other")],
new MessageExporter.FilterDescription(null, null, null, "selftest sen")
);
if (hits != 1)
failures.Add($"sender filter matched {hits} messages, expected 1");
}
private static void CheckUnknownFormatKeepsExistingFile(string dir, List<string> failures)
{
var path = Path.Combine(dir, "previous.md");
File.WriteAllText(path, "an earlier export");
try
{
MessageExporter.ExportToFile(
path,
(ExportFormat)99,
[],
new MessageExporter.FilterDescription(null, null, null, null)
);
failures.Add("unknown format did not throw");
}
catch (ArgumentOutOfRangeException) { }
if (File.ReadAllText(path) != "an earlier export")
failures.Add("unknown format destroyed the existing file");
}
private static void CheckNoLeftoverPartFile(string dir, List<string> failures)
{
var path = Path.Combine(dir, "clean.json");
MessageExporter.ExportToFile(
path,
ExportFormat.Json,
[Probe("A", "b")],
new MessageExporter.FilterDescription(null, null, null, null)
);
if (!File.Exists(path))
failures.Add("export produced no file");
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
// rejects. A test that only checks the file exists would have passed.
try
{
using var doc = System.Text.Json.JsonDocument.Parse(File.ReadAllText(path));
if (!doc.RootElement.TryGetProperty("messages", out var messages))
failures.Add("export JSON has no messages array");
else if (messages.GetArrayLength() != 1)
failures.Add($"export JSON holds {messages.GetArrayLength()} messages, expected 1");
}
catch (System.Text.Json.JsonException e)
{
failures.Add($"export JSON does not parse: {e.Message}");
}
}
// Built with empty SeStrings on purpose: the whole point is that the text
// comes from the chunks.
private static Message Probe(string sender, string content)
{
static List<Chunk> Text(string s) =>
[new TextChunk(ChunkSource.Content, null, null, null, null, false, s)];
return new Message(
0,
0,
0,
new ChatCode(XivChatType.Say, 0, 0),
Text(sender),
Text(content),
new Dalamud.Game.Text.SeStringHandling.SeString(),
new Dalamud.Game.Text.SeStringHandling.SeString()
);
}
// Nothing to undo between runs: every file lives in a fresh temp directory
// that RunStep deletes in its own finally.
public void CleanUp() { }
private static void TryCleanup(string dir)
{
try
{
if (Directory.Exists(dir))
Directory.Delete(dir, recursive: true);
}
catch (IOException) { }
catch (UnauthorizedAccessException) { }
}
}
@@ -86,6 +86,49 @@ internal sealed class FontManagerCtorSmokeStep : ISelfTestStep
return SelfTestStepResult.Fail; return SelfTestStepResult.Fail;
} }
// B1: assert the atlas actually finished building all required handles,
// not just that the references are non-null. FontsReady is the observable
// state the trimmed-fallback rebuild must still reach; a half-built atlas
// would pass the null/exception checks above but fail here.
if (!fm.FontsReady)
{
ImGui.Text("FontManager.FontsReady is false (atlas not fully built).");
SelfTestReport.Append(
Name,
"FAIL",
new[] { "FontsReady is false — atlas not fully built." }
);
return SelfTestStepResult.Fail;
}
// Report what was actually verified (Flo's request: don't just show Pass).
// The glyph-range entry counts make the B1 dedup visible — the cjk-fallback
// range is now a small trimmed remainder next to the large primary range.
var counts = fm.GlyphRangeLengths;
var italicState =
fm.ItalicFont is null ? "disabled (null)"
: fm.ItalicFont.Available ? "available"
: "NOT available";
var path = SelfTestReport.Append(
Name,
"PASS",
new[]
{
$"Axis available: {fm.Axis.Available}",
$"AxisItalic available: {fm.AxisItalic.Available}",
$"FontAwesome available: {fm.FontAwesome.Available}",
$"RegularFont available: {fm.RegularFont.Available}",
$"ItalicFont: {italicState}",
$"FontsReady: {fm.FontsReady}",
$"Glyph-range entries: primary={counts.Ranges}, jp={counts.JpRange}, "
+ $"cjk-fallback={counts.CjkFallback} (B1 trimmed)",
$"UseHellionFont={Plugin.Config.UseHellionFont}, ItalicEnabled={Plugin.Config.ItalicEnabled}",
}
);
ImGui.Text(
$"PASS — FontsReady, ranges primary={counts.Ranges}/jp={counts.JpRange}/"
+ $"cjk-fallback={counts.CjkFallback}. Report: {path}"
);
return SelfTestStepResult.Pass; return SelfTestStepResult.Pass;
} }
@@ -0,0 +1,63 @@
using System;
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Ui.StyleEngine;
namespace HellionChat.SelfTests;
// GC-reserve probe for the v1.9.0 B4a refactor: GlobalStyleScope.Push runs
// once per draw frame, so its StackHandle must allocate nothing. This step drives a
// real Push()->Dispose() cycle and asserts the per-thread allocation delta is
// ~0 (not a non-null-handle check — feedback_hellion_chat_fontmanager_push_trap).
// A warm-up cycle pays the one-time JIT/first-touch cost so the measured cycle
// reflects steady state, matching the real per-frame hot path.
internal sealed class GlobalStyleScopeAllocStep : ISelfTestStep
{
// Headroom for incidental managed noise (GC bookkeeping, boxing inside
// ImGui bindings we do not control). The pre-fix path allocated ~1-2 KB
// per cycle (44 boxes + List), so anything under this threshold proves
// the StackHandle itself stopped allocating. Tighten only if a future
// binding upgrade removes all incidental noise.
private const long AllocBudgetBytes = 256;
private readonly Plugin _plugin;
public GlobalStyleScopeAllocStep(Plugin plugin)
{
_plugin = plugin;
}
public string Name => "Hellion Chat - GlobalStyleScope GC reserve";
public SelfTestStepResult RunStep()
{
var registry = _plugin.ThemeRegistry;
var theme = registry.Active;
var opacity = Plugin.Config.WindowOpacity;
// Warm-up: JIT the Push/Dispose path + first-touch any lazy ImGui
// stack growth, so the measured cycle is steady-state only.
GlobalStyleScope.Push(theme, registry, opacity).Dispose();
var before = GC.GetAllocatedBytesForCurrentThread();
GlobalStyleScope.Push(theme, registry, opacity).Dispose();
var delta = GC.GetAllocatedBytesForCurrentThread() - before;
// Report the measured figure on BOTH outcomes (Flo's request: don't just
// show Pass) — the byte delta is the whole point of the GC-reserve probe.
var ok = delta <= AllocBudgetBytes;
var status = ok ? "PASS" : "FAIL";
SelfTestReport.Append(
Name,
status,
new[] { $"Push/Dispose allocated {delta} bytes/cycle (budget {AllocBudgetBytes})" }
);
ImGui.Text(
$"GlobalStyleScope.Push allocated {delta} bytes/cycle "
+ $"(budget {AllocBudgetBytes}) — {status}."
);
return ok ? SelfTestStepResult.Pass : SelfTestStepResult.Fail;
}
public void CleanUp() { }
}
@@ -1,50 +0,0 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
namespace HellionChat.SelfTests;
// Master-spec scope note: the hover-sheen key dictionary must not grow
// frame-by-frame on a constant-key call site. This probe drives 100
// hovered frames against three constant keys and asserts the dictionary
// only holds those three keys at the end — re-hover does not duplicate
// entries, and the un-hover branch clears the stale start timestamp.
internal sealed class HoverSheenAllocStep : ISelfTestStep
{
private readonly Plugin plugin;
public HoverSheenAllocStep(Plugin plugin)
{
this.plugin = plugin;
}
public string Name => "Hellion Chat - HoverSheen dictionary footprint";
public SelfTestStepResult RunStep()
{
// Probe runs outside a regular draw frame, so the sheen path
// would normally not have a window draw-list. We pull the
// foreground draw-list directly — it accepts AddRectFilled
// even without an active window scope.
var dl = ImGui.GetForegroundDrawList();
var theme = plugin.ThemeRegistry.Active;
var resolver = new TokenResolver();
var accent = resolver.Resolve(Token.AccentPrimary, theme.Colors);
var min = new System.Numerics.Vector2(0, 0);
var max = new System.Numerics.Vector2(10, 10);
string[] keys = ["selftest.row.a", "selftest.row.b", "selftest.row.c"];
for (var frame = 0; frame < 100; frame++)
foreach (var key in keys)
dl.DrawHoverSheen(min, max, accent, key, hovered: true);
// Un-hover sweep to verify the cleanup path drops the entries.
foreach (var key in keys)
dl.DrawHoverSheen(min, max, accent, key, hovered: false);
return SelfTestStepResult.Pass;
}
public void CleanUp() { }
}
@@ -0,0 +1,102 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Ui.StyleEngine;
namespace HellionChat.SelfTests;
// Master-spec §7.5 scope note: the hover registry must not grow frame by frame.
// Successor to HoverSheenAllocStep, which pinned the same contract against the
// old sheen start-timestamp dictionary.
//
// Two properties matter. Repeated queries for the same element must not add
// entries, and once an element stops being queried its entry must actually
// leave the map -- the old dictionary only cleared on an explicit un-hover
// call, so a row that vanished while hovered leaked until the plugin reloaded.
internal sealed class HoverStateFootprintStep : ISelfTestStep
{
public string Name => "Hellion Chat - HoverState registry footprint";
public SelfTestStepResult RunStep()
{
// Runs outside a normal draw frame, so the clock is stepped by hand
// rather than by HoverState.BeginFrame.
const float Frame = 1f / 60f;
var saved = Plugin.Config.ReduceMotion;
try
{
// The short-circuit would skip the map entirely and make this probe
// vacuous.
Plugin.Config.ReduceMotion = false;
HoverState.Reset();
uint[] ids =
[
ImGui.GetID("selftest.row.a"),
ImGui.GetID("selftest.row.b"),
ImGui.GetID("selftest.row.c"),
];
for (var frame = 0; frame < 100; frame++)
{
foreach (var id in ids)
HoverState.Query(id, hovered: true);
HoverState.AdvanceForTest(Frame);
}
if (HoverState.TrackedCount != ids.Length)
{
var msg = $"Registry holds {HoverState.TrackedCount}, expected {ids.Length}";
ImGui.Text(msg);
SelfTestReport.Append(Name, "FAIL", new[] { msg });
return SelfTestStepResult.Fail;
}
// Stop querying entirely, the way a removed row would. Fade-out runs
// at 8/s, so 1s of frames is comfortably past zero.
for (var frame = 0; frame < 60; frame++)
HoverState.AdvanceForTest(Frame);
if (HoverState.TrackedCount != 0)
{
var leak = $"Registry leaked {HoverState.TrackedCount} entries after fade-out";
ImGui.Text(leak);
SelfTestReport.Append(Name, "FAIL", new[] { leak });
return SelfTestStepResult.Fail;
}
// The case a hovered-only probe cannot see: querying an element that
// is NOT hovered must not create an entry. Otherwise every row in
// the window allocates one per frame and loses it again in the next
// BeginFrame, forever.
for (var frame = 0; frame < 10; frame++)
{
foreach (var id in ids)
HoverState.Query(id, hovered: false);
HoverState.AdvanceForTest(Frame);
if (HoverState.TrackedCount == 0)
continue;
var churn = $"Unhovered query created {HoverState.TrackedCount} entries";
ImGui.Text(churn);
SelfTestReport.Append(Name, "FAIL", new[] { churn });
return SelfTestStepResult.Fail;
}
SelfTestReport.Append(
Name,
"PASS",
new[] { "no growth while hovered, empty after fade-out, no churn when idle" }
);
return SelfTestStepResult.Pass;
}
finally
{
Plugin.Config.ReduceMotion = saved;
HoverState.Reset();
}
}
public void CleanUp() { }
}
@@ -0,0 +1,68 @@
using System.Globalization;
using System.IO;
namespace HellionChat.SelfTests;
// Disk sink for the B5 performance baseline. Kept separate from the SelfTest
// step so the per-frame hot path never references file IO. Writes one
// perf-baseline.json into the plugin ConfigDirectory, atomically (tmp + move)
// like ThemeRegistry's theme writer, so a mid-write crash leaves either the
// old file or the new file, never a half JSON. Field names track §7.5:
// steady-state Draw cost (avg/max ms), the quad-proxy draw-call count
// (avg/max), and frame delta (avg/max). First-frame-HITCH is read off
// drawMs max/avg by the human author, platform-annotated in the notes.
internal static class PerformanceBaselineLog
{
internal static string Write(
double avgDrawMs,
double maxDrawMs,
double avgDrawCallsProxy,
double maxDrawCallsProxy,
double avgDeltaMs,
double maxDeltaMs,
int frames
)
{
var targetPath = Path.Join(Plugin.Interface.ConfigDirectory.FullName, "perf-baseline.json");
var json =
"{\n"
+ $" \"frames\": {frames},\n"
+ $" \"avgDrawMs\": {Fmt(avgDrawMs)},\n"
+ $" \"maxDrawMs\": {Fmt(maxDrawMs)},\n"
+ $" \"avgDrawCallsProxy\": {Fmt(avgDrawCallsProxy)},\n"
+ $" \"maxDrawCallsProxy\": {Fmt(maxDrawCallsProxy)},\n"
+ $" \"avgDeltaMs\": {Fmt(avgDeltaMs)},\n"
+ $" \"maxDeltaMs\": {Fmt(maxDeltaMs)}\n"
+ "}\n";
// Atomic replace — same volume rename is atomic on POSIX and Windows.
var tmpPath = targetPath + ".tmp";
File.WriteAllText(tmpPath, json);
try
{
File.Move(tmpPath, targetPath, overwrite: true);
}
catch
{
// Avoid .tmp litter if Move fails (target locked).
try
{
File.Delete(tmpPath);
}
catch
{
// best effort
}
throw;
}
return targetPath;
}
private static string Fmt(double value)
{
return value.ToString("F2", CultureInfo.InvariantCulture);
}
}
@@ -1,19 +1,41 @@
using System.Diagnostics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest; using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests; namespace HellionChat.SelfTests;
// Optional metric capture. Walks one frame's ImGui IO counters and // Optional metric capture. Accumulates 1000 steady-state frames of ImGui IO
// prints a single JSON block so the cycle-notes author can copy/paste // counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs, B5-1),
// the snapshot without standing up a separate profiling harness. // then writes a single perf-baseline.json into the plugin ConfigDirectory so
// Investigations themselves are deferred to the polish cycle — this // the cycle-notes author can copy the §7.5 figures without a separate
// step only records, it never fails on threshold. // profiling harness. The step only records — it never fails on a threshold
// (the budgets are evaluated by a human against the JSON, §7.5 "optional,
// manual"). It returns Waiting until the sample window fills, mirroring the
// per-frame poll idiom of ThemeSwitchSelfTestStep.
internal sealed class PerformanceBaselineStep : ISelfTestStep internal sealed class PerformanceBaselineStep : ISelfTestStep
{ {
// §7.5 steady-state window. 1000 frames ≈ 16s at 60fps, long enough to
// average out GC blips without making the manual step tedious.
private const int TargetFrames = 1000;
// Rough draw-call proxy: ImGui emits 6 indices per quad, so vertices/6 is an
// intentional under-count of draw work, not the exact quad count (API-3).
private const int VerticesPerQuadProxy = 6;
private readonly Plugin _plugin;
private int _frames;
private ulong _lastFrameCount;
private double _drawMsSum;
private double _drawMsMax;
private long _vertexSum;
private long _vertexMax;
private double _deltaMsSum;
private double _deltaMsMax;
private string? _logPath;
public PerformanceBaselineStep(Plugin plugin) public PerformanceBaselineStep(Plugin plugin)
{ {
_ = plugin; _plugin = plugin;
} }
public string Name => "Hellion Chat - Performance baseline capture"; public string Name => "Hellion Chat - Performance baseline capture";
@@ -21,25 +43,69 @@ internal sealed class PerformanceBaselineStep : ISelfTestStep
public SelfTestStepResult RunStep() public SelfTestStepResult RunStep()
{ {
var io = ImGui.GetIO(); var io = ImGui.GetIO();
var stopwatch = Stopwatch.StartNew();
// No actual probe — we just sample the counters that ImGui keeps
// updated each frame. Stopwatch is started so the JSON line
// includes a non-zero wall-time figure even when ImGui has not
// accumulated frame stats yet.
stopwatch.Stop();
ImGui.Text( // Count each real frame once. Without the FrameCount gate a step that
"{ " // is polled more than once per frame would inflate the sample count.
+ $"\"renderVertices\": {io.MetricsRenderVertices}, " var frameCount = Plugin.Interface.UiBuilder.FrameCount;
+ $"\"renderIndices\": {io.MetricsRenderIndices}, " if (frameCount != _lastFrameCount)
+ $"\"renderWindows\": {io.MetricsRenderWindows}, " {
+ $"\"activeWindows\": {io.MetricsActiveWindows}, " _lastFrameCount = frameCount;
+ $"\"deltaTimeMs\": {io.DeltaTime * 1000f:F2}, " _frames++;
+ $"\"sampleWallTimeMs\": {stopwatch.Elapsed.TotalMilliseconds:F2}"
+ " }" var drawMs = _plugin.LastDrawMs;
); _drawMsSum += drawMs;
if (drawMs > _drawMsMax)
_drawMsMax = drawMs;
long vertices = io.MetricsRenderVertices;
_vertexSum += vertices;
if (vertices > _vertexMax)
_vertexMax = vertices;
var deltaMs = io.DeltaTime * 1000f;
_deltaMsSum += deltaMs;
if (deltaMs > _deltaMsMax)
_deltaMsMax = deltaMs;
}
if (_frames < TargetFrames)
{
ImGui.Text(
$"Sampling steady-state… {_frames}/{TargetFrames} frames. "
+ "Keep the chat window visible and idle."
);
return SelfTestStepResult.Waiting;
}
_logPath ??= WriteBaselineLog();
ImGui.Text($"Baseline captured ({TargetFrames} frames). Wrote: {_logPath}");
return SelfTestStepResult.Pass; return SelfTestStepResult.Pass;
} }
public void CleanUp() { } public void CleanUp()
{
_frames = 0;
_lastFrameCount = 0;
_drawMsSum = 0;
_drawMsMax = 0;
_vertexSum = 0;
_vertexMax = 0;
_deltaMsSum = 0;
_deltaMsMax = 0;
_logPath = null;
}
private string WriteBaselineLog()
{
// Disk write happens here, never in the per-frame hot path.
return PerformanceBaselineLog.Write(
avgDrawMs: _drawMsSum / TargetFrames,
maxDrawMs: _drawMsMax,
avgDrawCallsProxy: _vertexSum / (double)TargetFrames / VerticesPerQuadProxy,
maxDrawCallsProxy: _vertexMax / (double)VerticesPerQuadProxy,
avgDeltaMs: _deltaMsSum / TargetFrames,
maxDeltaMs: _deltaMsMax,
frames: TargetFrames
);
}
} }
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
namespace HellionChat.SelfTests;
// Shared report sink so manual self-test steps leave a readable trace on disk. Each
// call appends a timestamped block to selftest-report.log in the plugin ConfigDirectory;
// the human reads the tail after running the self-test runner.
// Append (not atomic tmp+move) is fine: the runner is single-threaded on the
// draw thread and a torn trailing line on a crash is acceptable for a debug log.
internal static class SelfTestReport
{
internal static string Append(string stepName, string status, IReadOnlyList<string> details)
{
var path = Path.Join(Plugin.Interface.ConfigDirectory.FullName, "selftest-report.log");
var stamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
var sb = new StringBuilder();
sb.Append("=== ")
.Append(stamp)
.Append(" | ")
.Append(stepName)
.Append(" | ")
.Append(status)
.Append(" ===\n");
foreach (var line in details)
sb.Append(" ").Append(line).Append('\n');
sb.Append('\n');
File.AppendAllText(path, sb.ToString());
return path;
}
}
@@ -0,0 +1,91 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Code;
namespace HellionChat.SelfTests;
// v1.10.0/C3: the active row gets a surface and an accent bar, so exactly the
// row the user is on must be marked -- and only that one. Drives the real
// Sidebar.Draw and reads the render-observability counter, so a regression in
// the draw path fails rather than a parallel calculation passing.
//
// "At most one", not "exactly one": PickMainActiveTab returns null when every
// tab is popped out, which is a legitimate state with zero marked rows.
internal sealed class SidebarActiveSurfaceStep : ISelfTestStep
{
private readonly Plugin _plugin;
public SidebarActiveSurfaceStep(Plugin plugin) => _plugin = plugin;
public string Name => "Hellion Chat - Sidebar active surface";
public SelfTestStepResult RunStep()
{
var sidebar = _plugin.MainWindow.GetSidebarForSelfTest();
if (sidebar is null)
{
ImGui.Text("Sidebar null");
SelfTestReport.Append(Name, "FAIL", new[] { "Sidebar null" });
return SelfTestStepResult.Fail;
}
var a = NewProbe("Surface Probe A@SelfTest");
var b = NewProbe("Surface Probe B@SelfTest");
var list = new List<Tab> { a, b };
var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f;
var savedWidth = Plugin.Config.SidebarWidth;
try
{
Plugin.Config.SidebarWidth = 220;
// (a) one of two tabs is active -> exactly one surface
Tab? active = a;
sidebar.Draw(width, list, ref active);
if (sidebar.LastRenderedActiveSurfaceCount != 1)
return Fail(
$"active tab drew {sidebar.LastRenderedActiveSurfaceCount}, expected 1"
);
// (b) no active tab (every tab popped out) -> zero, not a crash
active = null;
sidebar.Draw(width, list, ref active);
if (sidebar.LastRenderedActiveSurfaceCount != 0)
return Fail(
$"null active drew {sidebar.LastRenderedActiveSurfaceCount}, expected 0"
);
// (c) icon-only mode still marks the active row
active = b;
sidebar.Draw((float)Plugin.Config.SidebarAutoSwitchThresholdPx - 1f, list, ref active);
if (sidebar.LastRenderedActiveSurfaceCount != 1)
return Fail($"icon-only drew {sidebar.LastRenderedActiveSurfaceCount}, expected 1");
SelfTestReport.Append(Name, "PASS", new[] { "1 / 0 / 1 across the three cases" });
return SelfTestStepResult.Pass;
}
finally
{
Plugin.Config.SidebarWidth = savedWidth;
}
}
private static Tab NewProbe(string name) =>
new()
{
Name = name,
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>
{
[ChatType.Say] = (ChatSourceExt.All, ChatSourceExt.All),
},
};
private SelfTestStepResult Fail(string message)
{
ImGui.Text(message);
SelfTestReport.Append(Name, "FAIL", new[] { message });
return SelfTestStepResult.Fail;
}
public void CleanUp() { }
}
@@ -49,7 +49,10 @@ internal sealed class SidebarGreetedGlyphStep : ISelfTestStep
[ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.TellOutgoing] = (ChatSourceExt.All, ChatSourceExt.All),
}, },
}; };
Plugin.Config.Tabs.Add(injected); // Config.Tabs is mutated by the message worker under TabsListLock; a
// framework-thread writer must take the same lock.
lock (plugin.TabsListLock)
Plugin.Config.Tabs.Add(injected);
Tab? active = null; Tab? active = null;
var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded
try try
@@ -91,7 +94,8 @@ internal sealed class SidebarGreetedGlyphStep : ISelfTestStep
} }
finally finally
{ {
Plugin.Config.Tabs.Remove(injected); lock (plugin.TabsListLock)
Plugin.Config.Tabs.Remove(injected);
Plugin.Config.AutoTellTabsShowGreetedToggle = savedFlag; Plugin.Config.AutoTellTabsShowGreetedToggle = savedFlag;
Plugin.Config.SidebarWidth = savedSidebarWidth; Plugin.Config.SidebarWidth = savedSidebarWidth;
} }
@@ -50,8 +50,13 @@ internal sealed class SidebarSectionHeaderStep : ISelfTestStep
} }
injected.Add(BuildTempProbe("Tell Probe@SelfTest", pinned: false)); injected.Add(BuildTempProbe("Tell Probe@SelfTest", pinned: false));
injected.Add(BuildTempProbe("Pinned Probe@SelfTest", pinned: true)); injected.Add(BuildTempProbe("Pinned Probe@SelfTest", pinned: true));
foreach (var tab in injected) // Config.Tabs is mutated by the message worker under TabsListLock; a
Plugin.Config.Tabs.Add(tab); // framework-thread writer must take the same lock.
lock (plugin.TabsListLock)
{
foreach (var tab in injected)
Plugin.Config.Tabs.Add(tab);
}
Tab? active = null; Tab? active = null;
var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded var width = (float)Plugin.Config.SidebarAutoSwitchThresholdPx + 100f; // expanded
@@ -84,8 +89,11 @@ internal sealed class SidebarSectionHeaderStep : ISelfTestStep
} }
finally finally
{ {
foreach (var tab in injected) lock (plugin.TabsListLock)
Plugin.Config.Tabs.Remove(tab); {
foreach (var tab in injected)
Plugin.Config.Tabs.Remove(tab);
}
Plugin.Config.AutoTellTabsCompactDisplay = savedCompact; Plugin.Config.AutoTellTabsCompactDisplay = savedCompact;
Plugin.Config.SidebarWidth = savedSidebarWidth; Plugin.Config.SidebarWidth = savedSidebarWidth;
} }
@@ -0,0 +1,76 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Plugin.SelfTest;
using HellionChat.Code;
namespace HellionChat.SelfTests;
// v1.10.0/D1: the top-tab strip marks the active tab with a fill plus an accent
// underline. Drives the real TopTabBar.Draw and reads the render counter.
//
// "At most one", not "exactly one": the strip skips popped-out tabs, so zero
// underlines is a legitimate state.
internal sealed class TopTabUnderlineStep : ISelfTestStep
{
private readonly Plugin _plugin;
public TopTabUnderlineStep(Plugin plugin) => _plugin = plugin;
public string Name => "Hellion Chat - Top tab underline";
public SelfTestStepResult RunStep()
{
var strip = _plugin.MainWindow.GetTopTabsForSelfTest();
if (strip is null)
{
ImGui.Text("TopTabBar null");
SelfTestReport.Append(Name, "FAIL", new[] { "TopTabBar null" });
return SelfTestStepResult.Fail;
}
var a = NewProbe("Underline Probe A@SelfTest");
var b = NewProbe("Underline Probe B@SelfTest");
var list = new List<Tab> { a, b };
// (a) one of two tabs active -> exactly one underline
Tab? active = a;
strip.Draw(list, ref active);
if (strip.LastRenderedUnderlineCount != 1)
return Fail(
$"active tab drew {strip.LastRenderedUnderlineCount} underlines, expected 1"
);
// (b) no active tab -> zero, not a crash
active = null;
strip.Draw(list, ref active);
if (strip.LastRenderedUnderlineCount != 0)
return Fail($"null active drew {strip.LastRenderedUnderlineCount}, expected 0");
// (c) an active tab that is not in the list -> still zero
active = NewProbe("Absent@SelfTest");
strip.Draw(list, ref active);
if (strip.LastRenderedUnderlineCount != 0)
return Fail($"absent active drew {strip.LastRenderedUnderlineCount}, expected 0");
SelfTestReport.Append(Name, "PASS", new[] { "1 / 0 / 0 across the three cases" });
return SelfTestStepResult.Pass;
}
private static Tab NewProbe(string name) =>
new()
{
Name = name,
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>
{
[ChatType.Say] = (ChatSourceExt.All, ChatSourceExt.All),
},
};
private SelfTestStepResult Fail(string message)
{
ImGui.Text(message);
SelfTestReport.Append(Name, "FAIL", new[] { message });
return SelfTestStepResult.Fail;
}
public void CleanUp() { }
}
+5 -11
View File
@@ -62,11 +62,11 @@ internal sealed class WizardStateSmokeStep : ISelfTestStep
// Variant 2: skip Step 3 explicitly. Picks Roleplay on Step 2, // Variant 2: skip Step 3 explicitly. Picks Roleplay on Step 2,
// jumps straight to Step 4 (no Step-3 entry → no seed for // jumps straight to Step 4 (no Step-3 entry → no seed for
// LoadPreviousSession / FilterIncludePreviousSessions), commits, // FilterIncludePreviousSessions), commits, and asserts the history
// and asserts the two coupled history toggles remained on their // toggle remained on its pre-test value. Pins the null-semantics
// pre-test value. Pins the null-semantics from Spec Z.176 so a // from Spec Z.176 so a regression in CommitPending that started
// regression in CommitPending that started writing seeded // writing seeded recommendations unconditionally would surface
// recommendations unconditionally would surface here. // here.
// CommitPending → ApplyRoleplay overwrites six privacy / // CommitPending → ApplyRoleplay overwrites six privacy /
// retention fields, so snapshot them first and let CleanUp // retention fields, so snapshot them first and let CleanUp
// restore them after the assert. Keeps /xlperf idempotent. // restore them after the assert. Keeps /xlperf idempotent.
@@ -79,17 +79,11 @@ internal sealed class WizardStateSmokeStep : ISelfTestStep
this.snapshotRetentionDefaultDays = Plugin.Config.RetentionDefaultDays; this.snapshotRetentionDefaultDays = Plugin.Config.RetentionDefaultDays;
this.snapshotRetentionPerChannelDays = Plugin.Config.RetentionPerChannelDays; this.snapshotRetentionPerChannelDays = Plugin.Config.RetentionPerChannelDays;
var loadPrevBefore = Plugin.Config.LoadPreviousSession;
var filterPrevBefore = Plugin.Config.FilterIncludePreviousSessions; var filterPrevBefore = Plugin.Config.FilterIncludePreviousSessions;
wizard.TestOnly_AdvanceTo(2); wizard.TestOnly_AdvanceTo(2);
wizard.TestOnly_SetPendingProfile(FirstRunWizard.PrivacyProfile.Roleplay); wizard.TestOnly_SetPendingProfile(FirstRunWizard.PrivacyProfile.Roleplay);
wizard.TestOnly_AdvanceTo(4); wizard.TestOnly_AdvanceTo(4);
wizard.CommitPending(); wizard.CommitPending();
if (Plugin.Config.LoadPreviousSession != loadPrevBefore)
{
ImGui.Text("Skip-Step-3 path overwrote LoadPreviousSession");
return SelfTestStepResult.Fail;
}
if (Plugin.Config.FilterIncludePreviousSessions != filterPrevBefore) if (Plugin.Config.FilterIncludePreviousSessions != filterPrevBefore)
{ {
ImGui.Text("Skip-Step-3 path overwrote FilterIncludePreviousSessions"); ImGui.Text("Skip-Step-3 path overwrote FilterIncludePreviousSessions");
+28 -26
View File
@@ -75,36 +75,38 @@ internal sealed class TellRouterService : IDisposable
if (tab == null) if (tab == null)
return; // nothing to reveal (auto-tell-tabs off -> no tab created) return; // nothing to reveal (auto-tell-tabs off -> no tab created)
switch (mode) // Switching to the tab on every tell is user-gated
{ // (TellAutoOpenSwitchAlways, default on); when off the tab still
case TellAutoOpenMode.Sidebar: // appears with its unread badge but the active tab is left alone. A
case TellAutoOpenMode.TopTab: // tab that is already popped out needs no reveal at all -- it is on
// Switching to the tab on every tell is user-gated // screen, and pulling the main window onto it costs the user the tab
// (TellAutoOpenSwitchAlways, default on); when off the tab still // they were reading.
// appears with its unread badge but the active tab is left alone. var reveal = TabLifecycleHelpers.PlanTellReveal(
// The mode also picks the layout, so Sidebar vs TopTab are actually mode,
// distinct outcomes, not the same ActivateTab. Plugin.Config.TellAutoOpenSwitchAlways,
if (Plugin.Config.TellAutoOpenSwitchAlways) Plugin.Instance.ChannelPopoutPool.IsOpen(tab.Identifier)
{ );
var wantLayout =
mode == TellAutoOpenMode.TopTab
? MainWindowLayoutMode.TopTabs
: MainWindowLayoutMode.Sidebar;
if (Plugin.Config.MainWindowLayoutMode != wantLayout)
{
Plugin.Config.MainWindowLayoutMode = wantLayout;
Plugin.Instance.SaveConfig();
}
Plugin.Instance.MainWindow?.ActivateTab(tab); switch (reveal)
{
case TabLifecycleHelpers.TellReveal.MainWindow:
// The mode also picks the layout, so Sidebar vs TopTab are
// actually distinct outcomes, not the same ActivateTab.
var wantLayout =
mode == TellAutoOpenMode.TopTab
? MainWindowLayoutMode.TopTabs
: MainWindowLayoutMode.Sidebar;
if (Plugin.Config.MainWindowLayoutMode != wantLayout)
{
Plugin.Config.MainWindowLayoutMode = wantLayout;
Plugin.Instance.SaveConfig();
} }
Plugin.Instance.MainWindow?.ActivateTab(tab);
break; break;
case TellAutoOpenMode.Popout:
// IsOpen-guard: don't double-pop a tab the AutoTellTabsOpenAsPopout case TabLifecycleHelpers.TellReveal.Popout:
// path already opened (the two switches stay decoupled). Plugin.Instance.ChannelPopoutPool.TryOpen(tab);
if (!Plugin.Instance.ChannelPopoutPool.IsOpen(tab.Identifier))
Plugin.Instance.ChannelPopoutPool.TryOpen(tab);
break; break;
} }
}); });
+7 -2
View File
@@ -522,7 +522,10 @@ public sealed class ThemeRegistry
) )
{ {
var t = (float)(now - _crossfadeStartTickMs) / CrossfadeDurationMs; var t = (float)(now - _crossfadeStartTickMs) / CrossfadeDurationMs;
snapshot = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, t); // A2: SmoothStep easing so the fade eases in/out instead of a
// linear ramp. MUST stay in lockstep with TryGetActiveCrossfade (K8).
var te = t * t * (3f - 2f * t);
snapshot = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te);
} }
else else
{ {
@@ -548,7 +551,9 @@ public sealed class ThemeRegistry
return false; return false;
var t = (float)elapsed / CrossfadeDurationMs; var t = (float)elapsed / CrossfadeDurationMs;
lerped = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, t); // A2: SmoothStep easing -- keep identical to ArmCrossfade (K8).
var te = t * t * (3f - 2f * t);
lerped = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te);
return true; return true;
} }
+97
View File
@@ -0,0 +1,97 @@
namespace HellionChat.Ui;
// Deterministic hash-based color and icon tinting for Auto-Tell sidebar tabs.
// Same tell partner (name+world) always produces the same color and icon across
// sessions. Pure string logic, no Dalamud dependency — testable without game refs.
internal static class AutoTellTabTint
{
// Fallback for invalid input (empty name or world=0). White matches
// TextPrimary default so the sidebar stays visually consistent.
public const uint Fallback = 0xFFFFFFFFu;
// 12 saturated mid-bright colors from the built-in theme pool, readable
// on dark backgrounds. Collision risk is low at realistic 1-5 active tells.
// RGBA format, matching ColourUtil.RgbaToAbgr convention.
public static readonly IReadOnlyList<uint> Palette = new uint[]
{
0x00BED2FFu, // Arctic Cyan
0xF97316FFu, // Ember Orange
0xB585FFFFu, // Light Cosmic Purple
0xE374E8FFu, // Bloom Magenta
0x5DD39EFFu, // Mint Green
0xF0AD4EFFu, // Warning Yellow
0xE85C6AFFu, // Coral
0x5CB85CFFu, // Status Green
0x6278FFFFu, // Bloom Blue
0xC9982EFFu, // Warm Gold
0x9CCB7CFFu, // Soft Sage
0xE85D04FFu, // Deep Ember
};
public static uint For(string name, uint world)
{
if (string.IsNullOrEmpty(name) || world == 0)
return Fallback;
return Palette[(int)(StableHash($"{name}@{world}") % Palette.Count)];
}
// 7 visually distinct FA glyphs that make sense in a tell context.
// Excludes cog/comment/users — those read as system or group tabs.
public static readonly IReadOnlyList<string> IconPool = new[]
{
"envelope",
"star",
"heart",
"bell",
"bookmark",
"flag",
"fire",
};
// "envelope" matches the tell context better than the old hardcoded "clock".
public const string IconFallback = "envelope";
public static string IconFor(string name, uint world)
{
if (string.IsNullOrEmpty(name) || world == 0)
return IconFallback;
// Reversed key ("world@name") gives icon and color independent variation
// so the same tell partner doesn't always get the same color+icon pair.
// 7 icons x 12 colors = 84 distinct combinations.
return IconPool[(int)(StableHash($"{world}@{name}") % IconPool.Count)];
}
// FNV-1a, not string.GetHashCode. The header of this file promises the same
// partner produces the same colour "across sessions", and GetHashCode cannot
// keep that: .NET salts string hashing per process, so every game start
// would reshuffle every tell tab. The tests never caught it because they
// only ever compared two calls inside one run.
// Returns the full uint. The old int-based version masked off the sign bit
// before its modulo; on a uint that mask only throws away a bit of entropy.
private static uint StableHash(string key)
{
const uint offsetBasis = 2166136261u;
const uint prime = 16777619u;
var hash = offsetBasis;
foreach (var b in System.Text.Encoding.UTF8.GetBytes(key))
{
hash ^= b;
hash *= prime;
}
// Avalanche step, and not optional. FNV-1a alone leaves the low bits
// correlated for keys that differ only slightly, and the caller takes
// exactly those bits with a modulo -- a probe over 144 near-identical
// keys reached only 6 of the 12 colours. With fmix32 it reaches all 12.
hash ^= hash >> 16;
hash *= 0x7feb352du;
hash ^= hash >> 15;
hash *= 0x846ca68bu;
hash ^= hash >> 16;
return hash;
}
}
@@ -0,0 +1,77 @@
using System.Collections.Generic;
namespace HellionChat.Ui.Components;
// B2 (PERF-B2): variable-height clip plan. ImGuiListClipper needs a constant
// row height, and since v1.10.0/A2 neither density has one (compact rows wrap
// too), so both compute a plan from the cached per-row heights: a lead dummy
// for the rows above
// the viewport, the [first..last] index range that overlaps the viewport, and
// an end dummy for the rows below. This is the prefix-sum analogue of
// OtterGui's GetNecessarySkips, but for non-uniform heights — kept Dalamud-free
// so the Build Suite pins every edge case.
// TEST-MIRROR: ../../../../Hellion Build test/Ui/CardClipPlanTests.cs
internal readonly record struct CardClipPlan(
int FirstVisible,
int LastVisible,
float LeadDummyHeight,
float EndDummyHeight
);
internal static class CardClipPlanner
{
// heights[i] is the cached height of row i in draw order. It carries no
// trailing ItemSpacing: every row ends inside DrawChunks, where spacing is
// pushed to zero, and ImGui writes the advance at item submission time.
// A row overlaps the viewport iff rowTop < windowBottom && rowBottom >
// windowTop. FirstVisible/LastVisible are -1 when nothing overlaps (empty
// list); callers then submit a single end dummy of the full content height
// so the scrollbar stays correct.
internal static CardClipPlan Plan(
IReadOnlyList<float> heights,
float scrollY,
float viewportHeight
)
{
var count = heights.Count;
if (count == 0)
return new CardClipPlan(-1, -1, 0f, 0f);
var windowTop = scrollY;
var windowBottom = scrollY + viewportHeight;
var first = -1;
var last = -1;
var leadDummy = 0f;
var endDummy = 0f;
var cursor = 0f; // running prefix sum = top edge of the current row
for (var i = 0; i < count; i++)
{
var rowTop = cursor;
var rowBottom = cursor + heights[i];
var overlaps = rowTop < windowBottom && rowBottom > windowTop;
if (overlaps)
{
if (first < 0)
first = i;
last = i;
}
else if (first < 0)
{
// Still above the visible window -> grows the lead dummy.
leadDummy += heights[i];
}
else
{
// Already past the visible window -> grows the end dummy.
endDummy += heights[i];
}
cursor = rowBottom;
}
return new CardClipPlan(first, last, leadDummy, endDummy);
}
}
+14 -4
View File
@@ -13,7 +13,8 @@ namespace HellionChat.Ui.Components;
// bracketed title only appears when there is actually a title to show. // bracketed title only appears when there is actually a title to show.
internal sealed class HonorificHeader internal sealed class HonorificHeader
{ {
public const float Height = 30f; // Scaled: MainWindow reserves body height against this, so it follows.
public static float Height => StyleEngine.Metrics.HonorificHeight;
// SelfTest observables — set on the real Draw path so a headless step can // SelfTest observables — set on the real Draw path so a headless step can
// assert the gate/colour/truncation outcome instead of re-implementing it. // assert the gate/colour/truncation outcome instead of re-implementing it.
@@ -68,7 +69,11 @@ internal sealed class HonorificHeader
using (_fonts.FontAwesome.Push()) using (_fonts.FontAwesome.Push())
{ {
crownWidth = ImGui.CalcTextSize(crownGlyph).X; crownWidth = ImGui.CalcTextSize(crownGlyph).X;
dl.AddText(origin + new Vector2(0f, 8f), crownColor, crownGlyph); dl.AddText(
origin + new Vector2(0f, StyleEngine.Metrics.CenterY(Height)),
crownColor,
crownGlyph
);
} }
// Gate the bracketed title through the 1.5.6 contract (toggle, IPC // Gate the bracketed title through the 1.5.6 contract (toggle, IPC
@@ -92,12 +97,17 @@ internal sealed class HonorificHeader
// TruncateToFitWidth measures the *Regular* font, so this must run // TruncateToFitWidth measures the *Regular* font, so this must run
// OUTSIDE the FontAwesome.Push block above (crownWidth was measured // OUTSIDE the FontAwesome.Push block above (crownWidth was measured
// inside it, which is correct). // inside it, which is correct).
var maxTitleWidth = maxWidth - crownWidth - 6f - 8f; var gap = StyleEngine.Metrics.HonorificBracketGap;
var maxTitleWidth = maxWidth - crownWidth - gap - StyleEngine.Metrics.HonorificInset;
if (maxTitleWidth > 0f) if (maxTitleWidth > 0f)
{ {
var rendered = StringUtil.TruncateToFitWidth($"«{current.Title}»", maxTitleWidth); var rendered = StringUtil.TruncateToFitWidth($"«{current.Title}»", maxTitleWidth);
LastRenderedTitle = rendered; LastRenderedTitle = rendered;
dl.AddText(origin + new Vector2(crownWidth + 6f, 8f), titleColor, rendered); dl.AddText(
origin + new Vector2(crownWidth + gap, StyleEngine.Metrics.CenterY(Height)),
titleColor,
rendered
);
LastTitleRendered = true; LastTitleRendered = true;
} }
} }
+170 -27
View File
@@ -5,6 +5,7 @@ using Dalamud.Interface;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using HellionChat._Helpers; using HellionChat._Helpers;
using HellionChat.Code; using HellionChat.Code;
using HellionChat.GameFunctions; using HellionChat.GameFunctions;
@@ -26,11 +27,14 @@ namespace HellionChat.Ui.Components;
// the current game-side channel. // the current game-side channel.
internal sealed class InputBar internal sealed class InputBar
{ {
public const float Height = 32f; // Scaled: MainWindow and ChannelPopoutWindow both reserve against this, so
private const float PillHeight = 22f; // they follow automatically. The pill's own metrics live in PillStyle now.
private const float PillPaddingX = 8f; public static float Height => StyleEngine.Metrics.InputBarHeight;
private const int BufferCapacity = 500; private const int BufferCapacity = 500;
private const float QuickButtonsReserve = 130f;
// Scaled: the buttons themselves grow with the font, so a fixed reserve
// stops fitting them at 150%.
private static float QuickButtonsReserve => StyleEngine.Metrics.InputQuickButtonsReserve;
private readonly SymbolPicker _symbolPicker; private readonly SymbolPicker _symbolPicker;
private readonly FontManager _fonts; private readonly FontManager _fonts;
@@ -48,6 +52,13 @@ internal sealed class InputBar
// Null in pop-outs (those have their own close button). Hides the main window. // Null in pop-outs (those have their own close button). Hides the main window.
private readonly Action? _onHideWindow; private readonly Action? _onHideWindow;
// Set only for pop-outs, and after construction: the window does not exist
// yet while its own input row is being built, and routing it through the DI
// graph would close a factory-callsite cycle MS.DI cannot see. Its presence
// is what puts the pop-in button in the row, so the main window cannot grow
// one by accident.
internal Action? OnPopIn { get; set; }
private string _pendingMessage = string.Empty; private string _pendingMessage = string.Empty;
private bool _isFocused; private bool _isFocused;
private bool _wasInputTextHovered; private bool _wasInputTextHovered;
@@ -257,19 +268,15 @@ internal sealed class InputBar
private void DrawChannelPill(Tab? tab, bool isTell, uint pillAbgr, uint textAbgr) private void DrawChannelPill(Tab? tab, bool isTell, uint pillAbgr, uint textAbgr)
{ {
var label = ResolvePillLabel(tab, isTell); var label = ResolvePillLabel(tab, isTell);
var labelSize = ImGui.CalcTextSize(label); var size = StyleEngine.Widgets.Pill.CalcSize(label, withDot: false);
var width = labelSize.X + PillPaddingX * 2;
var origin = ImGui.GetCursorScreenPos(); var origin = ImGui.GetCursorScreenPos();
var dl = ImGui.GetWindowDrawList();
var max = origin + new Vector2(width, PillHeight);
dl.AddRectFilled(origin, max, pillAbgr, 6f); StyleEngine.Widgets.Pill.Draw(origin, label, pillAbgr, textAbgr);
dl.AddText(origin + new Vector2(PillPaddingX, 3f), textAbgr, label);
// Hit area over the rendered pill so a click opens the channel // Hit area over the rendered pill so a click opens the channel
// picker. InvisibleButton both reserves the layout slot and gives // picker. InvisibleButton both reserves the layout slot and gives
// the popup a stable anchor item. // the popup a stable anchor item.
ImGui.InvisibleButton("##hellion-pill", new Vector2(width, PillHeight)); ImGui.InvisibleButton("##hellion-pill", size);
if (ImGui.IsItemClicked() && tab is not null) if (ImGui.IsItemClicked() && tab is not null)
ImGui.OpenPopup("##hellion-channel-picker"); ImGui.OpenPopup("##hellion-channel-picker");
@@ -305,6 +312,11 @@ internal sealed class InputBar
} }
} }
// -1 means "not browsing". Per input bar, not shared: the history itself is
// global across the main window and every pop-out, but where each of them is
// in it is not.
private int _historyCursor = -1;
private void DrawInputField(Tab? activeTab) private void DrawInputField(Tab? activeTab)
{ {
if (Activate) if (Activate)
@@ -322,7 +334,8 @@ internal sealed class InputBar
ImGuiInputTextFlags.EnterReturnsTrue ImGuiInputTextFlags.EnterReturnsTrue
| ImGuiInputTextFlags.CallbackEdit | ImGuiInputTextFlags.CallbackEdit
| ImGuiInputTextFlags.CallbackCompletion | ImGuiInputTextFlags.CallbackCompletion
| ImGuiInputTextFlags.CallbackAlways, | ImGuiInputTextFlags.CallbackAlways
| ImGuiInputTextFlags.CallbackHistory,
SlashCommandCallback SlashCommandCallback
) )
) )
@@ -330,10 +343,68 @@ internal sealed class InputBar
_commandHelpWindow.IsOpen = false; _commandHelpWindow.IsOpen = false;
TrySend(activeTab); TrySend(activeTab);
} }
DrawInputContextMenu();
_isFocused = ImGui.IsItemFocused(); _isFocused = ImGui.IsItemFocused();
_wasInputTextHovered = ImGui.IsItemHovered(); _wasInputTextHovered = ImGui.IsItemHovered();
} }
// Right-clicking the input field opened this in v1.5.6 and has opened
// nothing since the chat window was retired. Reported by a tester who went
// looking for the map-flag entry.
//
// Must sit immediately after the InputText call: ContextPopupItem binds to
// the last submitted item.
//
// Hiding the chat is not repeated here -- it has its own button two widgets
// to the right, and one way in is enough.
private void DrawInputContextMenu()
{
using var context = ImRaii.ContextPopupItem("##hellion-input-context");
if (!context.Success)
return;
// The game expands <flag> and <item> at send time, so inserting the
// literal token is the whole implementation. Each entry is disabled
// while its precondition is missing, so the token cannot be sent only
// to expand into nothing at the other end.
bool flagSet;
bool itemSet;
unsafe
{
// Null before dereferencing: both agents can be null during a zone
// transition, which is precisely when somebody is most likely to be
// typing a flag into a party chat.
var map = AgentMap.Instance();
var chatLog = AgentChatLog.Instance();
flagSet = map != null && map->FlagMarkerCount > 0;
itemSet = chatLog != null && chatLog->LinkedItem.ItemId != 0;
}
using (ImRaii.Disabled(!flagSet))
{
if (ImGui.Selectable(HellionStrings.ChatLog_Insert_MapFlag))
InsertToken("<flag>");
}
using (ImRaii.Disabled(!itemSet))
{
if (ImGui.Selectable(HellionStrings.ChatLog_Insert_ItemLink))
InsertToken("<item>");
}
}
// Focus returns to the field and the caret lands behind the token, so the
// user can keep typing. Picking from a menu and then having to click back
// into the field is the kind of small friction that makes a feature go
// unused.
private void InsertToken(string token)
{
SetPendingMessage(_pendingMessage + token);
Activate = true;
_activatePos = _pendingMessage.Length;
}
// Dispatches across three ImGui callback events: CallbackAlways (cursor // Dispatches across three ImGui callback events: CallbackAlways (cursor
// restore after popup commit), CallbackCompletion (Tab opens the auto- // restore after popup commit), CallbackCompletion (Tab opens the auto-
// translate picker), CallbackEdit (slash-command help window sync). // translate picker), CallbackEdit (slash-command help window sync).
@@ -354,6 +425,39 @@ internal sealed class InputBar
return 0; return 0;
} }
// Up and down walk the sent-message history, the way 1.5.6 did. ImGui
// only raises this event when CallbackHistory is set on the field, which
// is why the arrows did nothing at all before: the service and the
// cursor maths were both here and tested, with no caller and no flag.
if (data.EventFlag == ImGuiInputTextFlags.CallbackHistory)
{
var direction =
data.EventKey == ImGuiKey.UpArrow
? CompactInputHistoryNavigator.Direction.Up
: CompactInputHistoryNavigator.Direction.Down;
var (cursor, replacement) = CompactInputHistoryNavigator.Navigate(
direction,
_historyCursor,
_pendingMessage,
() => InputHistoryService.Count,
InputHistoryService.Push,
InputHistoryService.GetByCursor
);
_historyCursor = cursor;
if (replacement is null)
return 0;
// The buffer belongs to ImGui inside a callback; writing the managed
// field here would be overwritten on the way out.
data.DeleteChars(0, data.BufTextLen);
if (replacement.Length > 0)
data.InsertChars(0, replacement);
return 0;
}
if (data.EventFlag == ImGuiInputTextFlags.CallbackCompletion) if (data.EventFlag == ImGuiInputTextFlags.CallbackCompletion)
{ {
// CursorPos is a BYTE offset into the UTF-8 buffer. We decode the // CursorPos is a BYTE offset into the UTF-8 buffer. We decode the
@@ -375,6 +479,13 @@ internal sealed class InputBar
// CallbackEdit (or any remaining event): v1.5.6 character-level slash // CallbackEdit (or any remaining event): v1.5.6 character-level slash
// detection keeps CommandHelpWindow in sync with what the user is // detection keeps CommandHelpWindow in sync with what the user is
// typing without a per-frame poll. // typing without a per-frame poll.
//
// Typing also ends the history walk. Without this, down-arrow after
// editing a recalled line would jump to the next entry and throw the
// edit away.
if (data.EventFlag == ImGuiInputTextFlags.CallbackEdit)
_historyCursor = -1;
_commandHelpWindow.IsOpen = false; _commandHelpWindow.IsOpen = false;
var text = Encoding.UTF8.GetString(data.BufTextSpan); var text = Encoding.UTF8.GetString(data.BufTextSpan);
@@ -449,6 +560,12 @@ internal sealed class InputBar
return; return;
} }
ChatBox.SendMessageUnsafe(bytes); ChatBox.SendMessageUnsafe(bytes);
// Pushed before the buffer is cleared, and the trimmed form is what
// goes in: the history is for recalling what you typed, not the
// whitespace around it.
InputHistoryService.Push(text);
_historyCursor = -1;
_pendingMessage = string.Empty; _pendingMessage = string.Empty;
// 1.5.6 parity (1d3b429:ChatLogWindow.cs:1558): clear the temp channel // 1.5.6 parity (1d3b429:ChatLogWindow.cs:1558): clear the temp channel
@@ -512,15 +629,17 @@ internal sealed class InputBar
private void DrawQuickButtons() private void DrawQuickButtons()
{ {
// Collected here and drawn after the icon font is popped. Inside the push
// it rendered against the FontAwesome atlas, which has no ASCII glyphs, so
// every tooltip came out as an empty box.
string? tooltip = null;
using (_fonts.FontAwesome.Push()) using (_fonts.FontAwesome.Push())
{ {
if (ImGui.Button(FontAwesomeIcon.SmileBeam.ToIconString())) if (ImGui.Button(FontAwesomeIcon.SmileBeam.ToIconString()))
_symbolPicker.OpenPopup(); _symbolPicker.OpenPopup();
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ tooltip = HellionStrings.InputBar_InsertSymbol_Tooltip;
using (ImRaii.DefaultFont())
ImGui.SetTooltip("Insert symbol");
}
if (_themeQuickPicker is not null) if (_themeQuickPicker is not null)
{ {
@@ -528,10 +647,7 @@ internal sealed class InputBar
if (ImGui.Button(FontAwesomeIcon.Palette.ToIconString())) if (ImGui.Button(FontAwesomeIcon.Palette.ToIconString()))
_themeQuickPicker.OpenPopup(); _themeQuickPicker.OpenPopup();
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ tooltip = HellionStrings.Settings_QuickPicker_Tooltip;
using (ImRaii.DefaultFont())
ImGui.SetTooltip(HellionStrings.Settings_QuickPicker_Tooltip);
}
} }
ImGui.SameLine(); ImGui.SameLine();
@@ -540,25 +656,52 @@ internal sealed class InputBar
_onOpenSettings(); _onOpenSettings();
} }
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ tooltip = HellionStrings.InputBar_Settings_Tooltip;
using (ImRaii.DefaultFont())
ImGui.SetTooltip("Settings");
}
// Hides the window (1.5.6 UserHide). One-way — Enter brings it back. // Hides the window (1.5.6 UserHide). One-way — Enter brings it back.
// Main window only (pop-outs have their own close); last in the row. // Main window only; last in the row there.
if (Plugin.Config.ShowHideButton && _onHideWindow is not null) if (Plugin.Config.ShowHideButton && _onHideWindow is not null)
{ {
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button(FontAwesomeIcon.EyeSlash.ToIconString())) if (ImGui.Button(FontAwesomeIcon.EyeSlash.ToIconString()))
_onHideWindow(); _onHideWindow();
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
tooltip = HellionStrings.InputBar_HideChat_Tooltip;
}
// Pop-in, in the pop-out windows only. It sits here rather than in a
// header row because a pop-out with its title bar on had no header at
// all -- and the title bar carries no close button, since closing has
// to go through the pool to release the slot.
if (OnPopIn is not null)
{
ImGui.SameLine();
// Red, and contrast-checked against the button plate it sits on
// rather than taken raw: several themes ship a danger colour that
// is nearly invisible on their own button fill.
var danger = ColourUtil.RgbaToAbgr(
_resolver.Resolve(Token.StatusDanger, _themes.Active.Colors)
);
var plate = ColourUtil.Vector4ToAbgr(ImGui.GetStyle().Colors[(int)ImGuiCol.Button]);
using (
ImRaii.PushColor(
ImGuiCol.Text,
ColourUtil.RgbaToVector4(
ColourUtil.RgbaToAbgr(ColourUtil.EnsureContrast(danger, plate, 3f))
)
)
)
{ {
using (ImRaii.DefaultFont()) if (ImGui.Button(FontAwesomeIcon.Times.ToIconString()))
ImGui.SetTooltip("Hide chat (Enter to bring back)"); OnPopIn();
} }
if (ImGui.IsItemHovered())
tooltip = HellionStrings.InputBar_PopIn_Tooltip;
} }
} }
if (tooltip is not null)
ImGui.SetTooltip(tooltip);
} }
// Test-only hook; do not call from production code. // Test-only hook; do not call from production code.
+197 -53
View File
@@ -1,4 +1,5 @@
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
@@ -7,15 +8,11 @@ using HellionChat.Util;
namespace HellionChat.Ui.Components; namespace HellionChat.Ui.Components;
// Virtualised message list. Compact mode reuses ImGuiListClipper because // Virtualised message list. Both densities wrap to arbitrary heights, so both
// rows have a constant line height; card mode falls back to a linear // run the same prefix-sum clipper (CardClipPlanner) over a per-message height
// render with a per-message height cache and an IsItemVisible skip path // cache, dropped whenever the layout fingerprint settles on a new value.
// so off-screen rows place a Dummy of the cached height rather than
// running the full render again.
internal sealed class MessageList internal sealed class MessageList
{ {
private const float CompactRowHeight = 18f;
private readonly FontManager _fonts; private readonly FontManager _fonts;
private readonly ChunkRenderer _chunkRenderer; private readonly ChunkRenderer _chunkRenderer;
@@ -27,6 +24,25 @@ internal sealed class MessageList
private bool _scrolledUp; private bool _scrolledUp;
private bool _scrollToBottomRequested; private bool _scrollToBottomRequested;
// B2: the height cache is only valid while these inputs are unchanged.
// FontManager's own fingerprint covers font sizes only, not density / the two
// name-display modes / width — a stale height would misplace the clipper dummies.
// Per tab, not per list: the old single field let a width change in tab A mark
// itself applied, so tab B kept measuring against the previous width.
private readonly Dictionary<Guid, LayoutFingerprintGate> _fingerprintGates = [];
// Bound once. A method group off an instance method captures `this` and is
// not cached by Roslyn, so `compact ? DrawCompactRow : DrawCardRow` would
// allocate a delegate on every frame of every window.
private readonly Action<Message> _drawCompactRow;
private readonly Action<Message> _drawCardRow;
// Reused across frames: at MessageManager.MessageDisplayLimit a fresh array
// per frame is 40 KB of garbage, and A2 put the default density on this
// path. The old comment named MaxLinesToRender and its 2500 default, a
// config field that had stopped bounding anything.
private float[] _heightScratch = [];
// §6.2: setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle. // §6.2: setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle.
// Wired by PayloadHandlerInitHostedService.StartAsync after both singletons exist. // Wired by PayloadHandlerInitHostedService.StartAsync after both singletons exist.
internal void AttachPayloadHandler(PayloadHandler handler) internal void AttachPayloadHandler(PayloadHandler handler)
@@ -36,6 +52,8 @@ internal sealed class MessageList
public MessageList(FontManager fonts, ChunkRenderer chunkRenderer) public MessageList(FontManager fonts, ChunkRenderer chunkRenderer)
{ {
_drawCompactRow = DrawCompactRow;
_drawCardRow = DrawCardRow;
_fonts = fonts; _fonts = fonts;
_chunkRenderer = chunkRenderer; _chunkRenderer = chunkRenderer;
} }
@@ -54,6 +72,64 @@ internal sealed class MessageList
// post-snap reset can be asserted; without it only the OR branch is testable. // post-snap reset can be asserted; without it only the OR branch is testable.
internal void RequestScrollToBottomForSelfTest() => _scrollToBottomRequested = true; internal void RequestScrollToBottomForSelfTest() => _scrollToBottomRequested = true;
// SelfTest hook (B2): runs the real planner against a caller fixture so the
// step asserts the plan without a live scroll child (GetScrollY is garbage headless).
internal CardClipPlan PlanCardClipForSelfTest(
IReadOnlyList<float> heights,
float scrollY,
float viewportHeight
) => CardClipPlanner.Plan(heights, scrollY, viewportHeight);
// SelfTest hook (B2): drives the live invalidation, returns the tab's remaining
// cached-height count so the step can assert the drop. nowMs is a parameter so
// the step can step past the settle window without sleeping (v1.10.0/A1).
internal int RunHeightCacheInvalidationForSelfTest(Tab tab, float contentWidth, long nowMs)
{
InvalidateHeightCacheIfLayoutChanged(tab, contentWidth, nowMs);
using var messages = tab.Messages.GetReadOnly(3);
return messages.Count(m => m.Height.ContainsKey(tab.Identifier));
}
// Width is passed in (ContentRegionAvail is only valid inside the draw child);
// enum modes widened to int so the record stays comparable. UiScale is in here
// because it feeds CalcWordWrapPositionA -- a scale change rewraps every row.
private LayoutFingerprint BuildLayoutFingerprint(float contentWidth)
{
var (global, symbols) = _fonts.EffectiveFontFingerprint();
return new LayoutFingerprint(
global,
symbols,
Plugin.Config.UseCompactDensity,
(int)Plugin.Config.NameFormMode,
(int)Plugin.Config.WorldSuffixMode,
contentWidth,
ImGuiHelpers.GlobalScale
);
}
// Drop the tab's cached heights once the layout fingerprint has settled — one
// record compare per frame, a clear only after a real settings/resize change
// stopped moving. The gate is what keeps a slider drag from rebuilding the
// whole tab on every frame.
private void InvalidateHeightCacheIfLayoutChanged(Tab tab, float contentWidth, long nowMs)
{
if (!_fingerprintGates.TryGetValue(tab.Identifier, out var gate))
{
gate = new LayoutFingerprintGate();
_fingerprintGates[tab.Identifier] = gate;
}
if (!gate.ShouldInvalidate(BuildLayoutFingerprint(contentWidth), nowMs))
return;
using var messages = tab.Messages.GetReadOnly(3);
foreach (var msg in messages)
{
msg.Height.Remove(tab.Identifier);
msg.IsVisible.Remove(tab.Identifier);
}
}
public void Draw(Tab tab) public void Draw(Tab tab)
{ {
if (!_fonts.FontsReady) if (!_fonts.FontsReady)
@@ -65,18 +141,29 @@ internal sealed class MessageList
// No own ImRaii.Child here — MainWindow already wraps the message // No own ImRaii.Child here — MainWindow already wraps the message
// area in one. Nesting would give the window two stacked scrolls // area in one. Nesting would give the window two stacked scrolls
// and a runaway content-height computation. // and a runaway content-height computation.
using var messages = tab.Messages.GetReadOnly(3);
var compact = Plugin.Config.UseCompactDensity; var compact = Plugin.Config.UseCompactDensity;
// B2: drop stale cached heights before the snapshot draw. Both densities
// need this now -- compact rows are not constant height either, they wrap.
// Width read here while it is valid.
InvalidateHeightCacheIfLayoutChanged(
tab,
ImGui.GetContentRegionAvail().X,
Environment.TickCount64
);
using var messages = tab.Messages.GetReadOnly(3);
// Track whether the user was pinned to the bottom before this frame // Track whether the user was pinned to the bottom before this frame
// so newly arriving rows do not yank them up. The check runs against // so newly arriving rows do not yank them up. The check runs against
// the parent child's scroll state, which is the one MainWindow owns. // the parent child's scroll state, which is the one MainWindow owns.
var pinnedToBottom = ImGui.GetScrollY() >= ImGui.GetScrollMaxY() - 1f; var pinnedToBottom = ImGui.GetScrollY() >= ImGui.GetScrollMaxY() - 1f;
if (compact) // While the gate waits out a continuous change, measurements must not go
DrawCompact(messages); // back into the cache: the rows outside the viewport still carry the old
else // geometry, and mixing the two makes the lead dummy drift every frame.
DrawCard(tab, messages); var frozen = _fingerprintGates[tab.Identifier].IsPending;
DrawRows(tab, messages, compact ? _drawCompactRow : _drawCardRow, frozen);
// B3-5: scroll values are frame-constant inside the child, so this // B3-5: scroll values are frame-constant inside the child, so this
// reflects the current frame's state wherever it runs; kept after the // reflects the current frame's state wherever it runs; kept after the
@@ -143,28 +230,6 @@ internal sealed class MessageList
_scrollToBottomRequested = true; _scrollToBottomRequested = true;
} }
private void DrawCompact(IReadOnlyList<Message> messages)
{
unsafe
{
var clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper());
try
{
clipper.Begin(messages.Count, CompactRowHeight);
while (clipper.Step())
{
for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
DrawCompactRow(messages[i]);
}
clipper.End();
}
finally
{
clipper.Destroy();
}
}
}
private void DrawCompactRow(Message message) private void DrawCompactRow(Message message)
{ {
// B2-1/B2-2: render the sender through DrawChunks (the name-aware path // B2-1/B2-2: render the sender through DrawChunks (the name-aware path
@@ -189,32 +254,111 @@ internal sealed class MessageList
_chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f); _chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f);
} }
private void DrawCard(Tab tab, IReadOnlyList<Message> messages) // Shared by both densities: compact rows wrap too, so neither has a constant
// height the ImGuiListClipper could work with. drawRow is the only difference.
private void DrawRows(
Tab tab,
IReadOnlyList<Message> messages,
Action<Message> drawRow,
bool frozen
)
{ {
var tabId = tab.Identifier; var tabId = tab.Identifier;
for (var i = 0; i < messages.Count; i++) var count = messages.Count;
if (count == 0)
return;
// A row with no cached height yet can't be planned (first frame / post-
// invalidation), so draw everything once to fill the cache, plan next frame.
if (_heightScratch.Length < count)
_heightScratch = new float[Math.Max(count, 256)];
var heights = _heightScratch;
var allCached = true;
for (var i = 0; i < count; i++)
{
if (messages[i].Height.TryGetValue(tabId, out var cached) && cached is float h)
heights[i] = h;
else
allCached = false;
}
if (!allCached)
{
// Always measures, frozen or not: without a filled cache there is
// nothing to plan against at all.
DrawLinearAndMeasure(tabId, messages, drawRow);
return;
}
var plan = CardClipPlanner.Plan(
new ArraySegment<float>(heights, 0, count),
ImGui.GetScrollY(),
ImGui.GetWindowSize().Y
);
// A dummy is submitted outside any style push, so it appends its own
// trailing ItemSpacing.y. Subtracting one spacing per dummy makes the dummy
// advance the cursor by exactly the planned height. (The measured row
// heights carry no trailing spacing: every row ends inside DrawChunks,
// which pushes ItemSpacing to zero, and ImGui writes the advance at item
// submission time.)
var spacingY = ImGui.GetStyle().ItemSpacing.Y;
float CompensatedDummy(float planned) => Math.Max(0f, planned - spacingY);
if (plan.FirstVisible < 0)
{
// Scrolled into a gap: one full-height dummy keeps the scrollbar honest.
var gap = CompensatedDummy(plan.LeadDummyHeight + plan.EndDummyHeight);
ImGui.Dummy(new Vector2(StyleEngine.Metrics.MessageDummyWidth, gap));
return;
}
if (plan.LeadDummyHeight > 0f)
ImGui.Dummy(
new Vector2(
StyleEngine.Metrics.MessageDummyWidth,
CompensatedDummy(plan.LeadDummyHeight)
)
);
for (var i = plan.FirstVisible; i <= plan.LastVisible; i++)
{ {
var msg = messages[i]; var msg = messages[i];
// Cached row: place a Dummy of the known height and skip the
// full render path if the row is off-screen. Mirrors the
// v1.5.6 Card-Mode pattern in ChatLogWindow.DrawMessages.
msg.Height.TryGetValue(tabId, out var cachedHeight);
if (cachedHeight is float h)
{
var beforeDummy = ImGui.GetCursorPos();
ImGui.Dummy(new Vector2(10f, h));
var visible = ImGui.IsItemVisible();
msg.IsVisible[tabId] = visible;
if (!visible)
continue;
ImGui.SetCursorPos(beforeDummy);
}
var before = ImGui.GetCursorPosY(); var before = ImGui.GetCursorPosY();
DrawCardRow(msg); drawRow(msg);
if (frozen)
continue;
var after = ImGui.GetCursorPosY(); var after = ImGui.GetCursorPosY();
msg.Height[tabId] = after - before; msg.Height[tabId] = after - before;
msg.IsVisible[tabId] = true;
}
if (plan.EndDummyHeight > 0f)
ImGui.Dummy(
new Vector2(
StyleEngine.Metrics.MessageDummyWidth,
CompensatedDummy(plan.EndDummyHeight)
)
);
}
// First-frame / post-invalidation fallback: draw + measure every row into the
// cache so the next frame can take the planned path. The settle gate on the
// layout fingerprint is what keeps a resize drag from landing here every frame.
private void DrawLinearAndMeasure(
Guid tabId,
IReadOnlyList<Message> messages,
Action<Message> drawRow
)
{
foreach (var msg in messages)
{
var before = ImGui.GetCursorPosY();
drawRow(msg);
var after = ImGui.GetCursorPosY();
msg.Height[tabId] = after - before;
msg.IsVisible[tabId] = ImGui.IsItemVisible();
} }
} }
@@ -17,11 +17,13 @@ internal sealed class ChatColourPicker
{ {
private readonly Plugin _plugin; private readonly Plugin _plugin;
private readonly ThemeRegistry _themes; private readonly ThemeRegistry _themes;
private readonly SectionRenderer _sections;
private string? _applyDismissedFor; private string? _applyDismissedFor;
private string? _lastSeenSlug; private string? _lastSeenSlug;
public ChatColourPicker(Plugin plugin, ThemeRegistry themes) public ChatColourPicker(Plugin plugin, ThemeRegistry themes, SectionRenderer sections)
{ {
_sections = sections;
_plugin = plugin; _plugin = plugin;
_themes = themes; _themes = themes;
} }
@@ -32,7 +34,13 @@ internal sealed class ChatColourPicker
public void Draw() public void Draw()
{ {
if (!ImGui.CollapsingHeader(HellionStrings.Settings_Section_Colours)) if (
!_sections.Draw(
ImGui.GetID("appearance.chatcolours"u8),
HellionStrings.Settings_Section_Colours,
open: false
)
)
return; return;
DrawPresetButtons(); DrawPresetButtons();
@@ -41,18 +49,6 @@ internal sealed class ChatColourPicker
ImGui.Separator(); ImGui.Separator();
ImGui.Spacing(); ImGui.Spacing();
if (
ImGui.Checkbox(
Language.Options_ColorSelectedInputChannelButton_Name,
ref Plugin.Config.ColorSelectedInputChannelButton
)
)
{
_plugin.SaveConfig();
}
ImGuiUtil.HelpMarker(Language.Options_ColorSelectedInputChannelButton_Description);
ImGui.Spacing();
// Discrete clicks (reset/import) persist at once. The ColorEdit3 drag only // Discrete clicks (reset/import) persist at once. The ColorEdit3 drag only
// recolours live (Refresh, no disk write) and defers SaveConfig to release // recolours live (Refresh, no disk write) and defers SaveConfig to release
// via IsItemDeactivatedAfterEdit, so dragging the colour wheel doesn't fire // via IsItemDeactivatedAfterEdit, so dragging the colour wheel doesn't fire
@@ -71,7 +67,8 @@ internal sealed class ChatColourPicker
) )
) )
{ {
Plugin.Config.ChatColours.Remove(type); lock (_plugin.ConfigMapsLock)
Plugin.Config.ChatColours.Remove(type);
commit = true; commit = true;
} }
@@ -86,7 +83,8 @@ internal sealed class ChatColourPicker
) )
{ {
var gameColour = _plugin.Functions.Chat.GetChannelColor(type); var gameColour = _plugin.Functions.Chat.GetChannelColor(type);
Plugin.Config.ChatColours[type] = gameColour ?? type.DefaultColor() ?? 0; lock (_plugin.ConfigMapsLock)
Plugin.Config.ChatColours[type] = gameColour ?? type.DefaultColor() ?? 0;
commit = true; commit = true;
} }
@@ -97,7 +95,10 @@ internal sealed class ChatColourPicker
: ColourUtil.RgbaToVector3(type.DefaultColor() ?? 0); : ColourUtil.RgbaToVector3(type.DefaultColor() ?? 0);
if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs)) if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs))
{ {
Plugin.Config.ChatColours[type] = ColourUtil.Vector3ToRgba(vec); // First edit of a channel without a default inserts a NEW key --
// that is the case that invalidates a running enumeration.
lock (_plugin.ConfigMapsLock)
Plugin.Config.ChatColours[type] = ColourUtil.Vector3ToRgba(vec);
liveOnly = true; liveOnly = true;
} }
if (ImGui.IsItemDeactivatedAfterEdit()) if (ImGui.IsItemDeactivatedAfterEdit())
@@ -155,7 +156,8 @@ internal sealed class ChatColourPicker
private void ApplyPreset(ChatColourPreset preset) private void ApplyPreset(ChatColourPreset preset)
{ {
foreach (var (channel, colour) in preset.Colours) foreach (var (channel, colour) in preset.Colours)
Plugin.Config.ChatColours[channel] = colour; lock (_plugin.ConfigMapsLock)
Plugin.Config.ChatColours[channel] = colour;
ApplyChatColourChange(); ApplyChatColourChange();
} }
@@ -226,7 +228,8 @@ internal sealed class ChatColourPicker
if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Apply)) if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Apply))
{ {
foreach (var kvp in themeChatColors.Channels) foreach (var kvp in themeChatColors.Channels)
Plugin.Config.ChatColours[kvp.Key] = kvp.Value; lock (_plugin.ConfigMapsLock)
Plugin.Config.ChatColours[kvp.Key] = kvp.Value;
_applyDismissedFor = active.Slug; _applyDismissedFor = active.Slug;
ApplyChatColourChange(); ApplyChatColourChange();
} }
@@ -1,6 +1,7 @@
using System.Numerics; using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources;
using HellionChat.Themes; using HellionChat.Themes;
using HellionChat.Util; using HellionChat.Util;
@@ -9,9 +10,11 @@ namespace HellionChat.Ui.Components.Settings;
internal sealed class ColorPicker internal sealed class ColorPicker
{ {
private readonly ThemeRegistry _themes; private readonly ThemeRegistry _themes;
private readonly SectionRenderer _sections;
public ColorPicker(ThemeRegistry themes) public ColorPicker(ThemeRegistry themes, SectionRenderer sections)
{ {
_sections = sections;
_themes = themes; _themes = themes;
} }
@@ -29,26 +32,24 @@ internal sealed class ColorPicker
private void DrawIdleState() private void DrawIdleState()
{ {
var active = _themes.Active; 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 // 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. // files with the same slug, so an in-place edit would silently no-op.
if (active.IsBuiltIn) if (active.IsBuiltIn)
{ {
if (ImGui.Button("Fork & Edit")) if (ImGui.Button(HellionStrings.Settings_Theme_ForkAndEdit))
{ {
ForkAndBeginEditing(active); ForkAndBeginEditing(active);
} }
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ {
ImGui.SetTooltip( ImGui.SetTooltip(HellionStrings.Settings_Theme_ForkTooltip);
"Built-in themes cannot be edited in place. Fork creates a custom copy you can edit and save."
);
} }
} }
else else
{ {
if (ImGui.Button("Edit theme")) if (ImGui.Button(HellionStrings.Settings_Theme_EditTheme))
{ {
_themes.BeginEditing(active); _themes.BeginEditing(active);
} }
@@ -85,11 +86,12 @@ internal sealed class ColorPicker
private void DrawEditState(Theme buffer) private void DrawEditState(Theme buffer)
{ {
ImGui.TextUnformatted($"Editing: {buffer.Name}"); ImGui.TextUnformatted(string.Format(HellionStrings.Settings_Theme_Editing, buffer.Name));
ImGui.Separator(); ImGui.Separator();
DrawSection( DrawSection(
"Surfaces", ImGui.GetID("colors.surfaces"u8),
HellionStrings.Settings_Theme_Group_Surfaces,
buffer, buffer,
c => c =>
new[] new[]
@@ -112,14 +114,16 @@ internal sealed class ColorPicker
); );
DrawSection( DrawSection(
"Borders", ImGui.GetID("colors.borders"u8),
HellionStrings.Settings_Theme_Group_Borders,
buffer, buffer,
c => new[] { ("Border", c.Border) }, c => new[] { ("Border", c.Border) },
(c, edits) => c with { Border = edits[0].color } (c, edits) => c with { Border = edits[0].color }
); );
DrawSection( DrawSection(
"Text", ImGui.GetID("colors.text"u8),
HellionStrings.Settings_Theme_Group_Text,
buffer, buffer,
c => c =>
new[] new[]
@@ -138,6 +142,7 @@ internal sealed class ColorPicker
); );
DrawSection( DrawSection(
ImGui.GetID("colors.brand.primary"u8),
"Brand — Primary", "Brand — Primary",
buffer, buffer,
c => c =>
@@ -159,6 +164,7 @@ internal sealed class ColorPicker
); );
DrawSection( DrawSection(
ImGui.GetID("colors.brand.accent"u8),
"Brand — Accent", "Brand — Accent",
buffer, buffer,
c => c =>
@@ -178,14 +184,16 @@ internal sealed class ColorPicker
); );
DrawSection( DrawSection(
"Identity", ImGui.GetID("colors.identity"u8),
HellionStrings.Settings_Theme_Group_Identity,
buffer, buffer,
c => new[] { ("Identity", c.Identity) }, c => new[] { ("Identity", c.Identity) },
(c, edits) => c with { Identity = edits[0].color } (c, edits) => c with { Identity = edits[0].color }
); );
DrawSection( DrawSection(
"Status", ImGui.GetID("colors.status"u8),
HellionStrings.Settings_Theme_Group_Status,
buffer, buffer,
c => c =>
new[] new[]
@@ -210,13 +218,17 @@ internal sealed class ColorPicker
} }
private void DrawSection( private void DrawSection(
uint key,
string title, string title,
Theme buffer, Theme buffer,
Func<ThemeColors, (string label, uint color)[]> slots, Func<ThemeColors, (string label, uint color)[]> slots,
Func<ThemeColors, (string label, uint color)[], ThemeColors> writeBack Func<ThemeColors, (string label, uint color)[], ThemeColors> writeBack
) )
{ {
if (!ImGui.CollapsingHeader(title, ImGuiTreeNodeFlags.DefaultOpen)) // Key as a parameter: this runs seven times with seven titles, and
// ImGui would key each header off its label, so a translation could
// collapse two sections onto one shared state.
if (!_sections.Draw(key, title))
{ {
return; return;
} }
@@ -251,12 +263,12 @@ internal sealed class ColorPicker
private void DrawActionButtons(Theme buffer) private void DrawActionButtons(Theme buffer)
{ {
if (ImGui.Button("Save")) if (ImGui.Button(HellionStrings.Settings_Theme_Save))
{ {
_themes.SaveEditingBuffer(out _); _themes.SaveEditingBuffer(out _);
} }
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Cancel")) if (ImGui.Button(HellionStrings.Settings_Theme_Cancel))
{ {
_themes.DiscardEditingBuffer(); _themes.DiscardEditingBuffer();
} }
@@ -269,14 +281,14 @@ internal sealed class ColorPicker
var isForkBuffer = !buffer.IsBuiltIn && _themes.Active.Slug != buffer.Slug; var isForkBuffer = !buffer.IsBuiltIn && _themes.Active.Slug != buffer.Slug;
using (ImRaii.Disabled(isForkBuffer)) using (ImRaii.Disabled(isForkBuffer))
{ {
if (ImGui.Button("Reset to source")) if (ImGui.Button(HellionStrings.Settings_Theme_ResetToSource))
{ {
_themes.BeginEditing(_themes.Active); _themes.BeginEditing(_themes.Active);
} }
} }
if (isForkBuffer && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) 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);
} }
} }
} }
@@ -1,18 +1,36 @@
using System.Numerics; using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using HellionChat.Ui.StyleEngine;
namespace HellionChat.Ui.Components.Settings; namespace HellionChat.Ui.Components.Settings;
internal sealed class ContentArea internal sealed class ContentArea
{ {
private readonly SurfaceBackdrop _backdrop;
public ContentArea(SurfaceBackdrop backdrop)
{
_backdrop = backdrop;
}
public void Draw(string activeTab, Action<string> renderTab) public void Draw(string activeTab, Action<string> renderTab)
{ {
// Transparent child with the ground painted by hand. ChildBg takes one
// flat colour and nothing else, so a gradient, an accent wash or motes
// are all impossible through it -- and the pane had no ground at all
// before, which left settings text sitting on the moving game world.
using var bg = ImRaii.PushColor(ImGuiCol.ChildBg, 0u);
using var child = ImRaii.Child("##settings-content", new Vector2(0, 0), true); using var child = ImRaii.Child("##settings-content", new Vector2(0, 0), true);
if (!child.Success) if (!child.Success)
{ {
return; return;
} }
// Fully opaque: SettingsWindow sets BgAlpha = 1, which the backdrop
// cannot see for itself.
_backdrop.Draw(opacityOverride: 1f);
renderTab(activeTab); renderTab(activeTab);
} }
} }
@@ -15,9 +15,11 @@ internal sealed class FontsSection
{ {
private readonly Plugin _plugin; private readonly Plugin _plugin;
private readonly FontManager _fontManager; private readonly FontManager _fontManager;
private readonly SectionRenderer _sections;
public FontsSection(Plugin plugin, FontManager fontManager) public FontsSection(Plugin plugin, FontManager fontManager, SectionRenderer sections)
{ {
_sections = sections;
_plugin = plugin; _plugin = plugin;
_fontManager = fontManager; _fontManager = fontManager;
} }
@@ -30,16 +32,25 @@ internal sealed class FontsSection
public void Draw() public void Draw()
{ {
if (!ImGui.CollapsingHeader(HellionStrings.Settings_Section_Fonts)) if (
!_sections.Draw(
ImGui.GetID("appearance.fonts"u8),
HellionStrings.Settings_Section_Fonts,
open: false
)
)
return; return;
// Readout so the user can see which font is actually active. // Readout so the user can see which font is actually active.
var active = var active =
Plugin.Config.UseHellionFont ? "Hellion Inter (bundled)" Plugin.Config.UseHellionFont ? HellionStrings.Settings_Fonts_Bundled
: Plugin.Config.FontsEnabled : Plugin.Config.FontsEnabled
? $"Global: {Plugin.Config.GlobalFontV2.FontId.Family.EnglishName}" ? string.Format(
: "FFXIV game font"; HellionStrings.Settings_Fonts_Global,
ImGui.TextDisabled($"Active: {active}"); Plugin.Config.GlobalFontV2.FontId.Family.EnglishName
)
: HellionStrings.Settings_Fonts_GameFont;
ImGui.TextDisabled(string.Format(HellionStrings.Settings_Fonts_Active, active));
ImGui.Spacing(); ImGui.Spacing();
if ( if (
@@ -126,7 +137,13 @@ internal sealed class FontsSection
// ExtraGlyphRanges stays reachable regardless of the font source so the // ExtraGlyphRanges stays reachable regardless of the font source so the
// user can verify/override the per-language auto-activation (v1.5.3 note). // user can verify/override the per-language auto-activation (v1.5.3 note).
ImGui.Spacing(); ImGui.Spacing();
if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name)) if (
_sections.Draw(
ImGui.GetID("appearance.fonts.glyphs"u8),
Language.Options_ExtraGlyphs_Name,
open: false
)
)
{ {
ImGuiUtil.HelpMarker( ImGuiUtil.HelpMarker(
string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName) string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName)
@@ -3,6 +3,8 @@ using System.Threading;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using HellionChat.Code;
using HellionChat.Resources;
using HellionChat.Themes; using HellionChat.Themes;
using HellionChat.Ui.StyleEngine; using HellionChat.Ui.StyleEngine;
using HellionChat.Util; using HellionChat.Util;
@@ -11,6 +13,13 @@ namespace HellionChat.Ui.Components.Settings;
internal sealed class LivePreviewPanel : IDisposable internal sealed class LivePreviewPanel : IDisposable
{ {
// The preview reads the same tokens the real chrome does. Copying their lerp
// formulas here is how it drifted out of sync in the first place.
private static readonly StyleEngine.TokenResolver Tokens = new();
private static uint Abgr(StyleEngine.Token token, ThemeColors colors) =>
ColourUtil.RgbaToAbgr(Tokens.Resolve(token, colors));
// Static counter for S5 reload-stress verification: after 10 reloads the // Static counter for S5 reload-stress verification: after 10 reloads the
// counter must read 0 (plugin disabled) or 1 (plugin enabled). Anything // counter must read 0 (plugin disabled) or 1 (plugin enabled). Anything
// higher signals a Dispose skip and a subscriber leak against ThemeRegistry. // higher signals a Dispose skip and a subscriber leak against ThemeRegistry.
@@ -145,7 +154,7 @@ internal sealed class LivePreviewPanel : IDisposable
// unchanged — but both paths now share one resolver. No truncation here: // unchanged — but both paths now share one resolver. No truncation here:
// the preview draws a fixed, centred "«Champion» Preview" string. // the preview draws a fixed, centred "«Champion» Preview" string.
var textAbgr = HonorificTitleColor.ResolveTitleAbgr(null, theme); var textAbgr = HonorificTitleColor.ResolveTitleAbgr(null, theme);
var title = "«Champion» Preview"; var title = HellionStrings.Settings_Preview_TitleMock;
var crownGlyph = FontAwesomeIcon.Crown.ToIconString(); var crownGlyph = FontAwesomeIcon.Crown.ToIconString();
// Crown is a FontAwesome glyph (matches the real header); measure + draw // Crown is a FontAwesome glyph (matches the real header); measure + draw
@@ -177,22 +186,42 @@ internal sealed class LivePreviewPanel : IDisposable
var rowHeight = MiddleBandHeight / 3f; var rowHeight = MiddleBandHeight / 3f;
var surface = ColourUtil.RgbaToAbgr(theme.Colors.Surface); var surface = ColourUtil.RgbaToAbgr(theme.Colors.Surface);
var surfaceHover = ColourUtil.RgbaToAbgr(theme.Colors.SurfaceHover); var surfaceHover = ColourUtil.RgbaToAbgr(theme.Colors.SurfaceHover);
var surfaceActive = Abgr(StyleEngine.Token.SurfaceActive, theme.Colors);
var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary); var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
var primaryAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Primary); var primaryAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Primary);
var accentAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Accent); var accentAbgr = ColourUtil.RgbaToAbgr(theme.Colors.Accent);
ReadOnlySpan<string> labels = ["Linkshell", "Tell", "FC"]; // Row 0 is the active one and carries BOTH the raised surface and the
// accent bar, the way the real sidebar draws it. Until v1.10.0 those two
// 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);
// Real channel names, not literals: the preview claims to show what the
// sidebar will look like, and the sidebar is localised.
ReadOnlySpan<string> labels =
[
ChatType.Linkshell1.Name(),
ChatType.TellIncoming.Name(),
ChatType.FreeCompany.Name(),
];
for (var i = 0; i < 3; i++) for (var i = 0; i < 3; i++)
{ {
var rowMin = new Vector2(origin.X, origin.Y + i * rowHeight); var rowMin = new Vector2(origin.X, origin.Y + i * rowHeight);
var rowMax = new Vector2(origin.X + SidebarWidth, rowMin.Y + rowHeight); var rowMax = new Vector2(origin.X + SidebarWidth, rowMin.Y + rowHeight);
var bg = i == 1 ? surfaceHover : surface; var isActive = i == 0;
draw.AddRectFilled(rowMin, rowMax, bg);
if (i == 0) draw.AddRectFilled(rowMin, rowMax, isActive ? surfaceActive : surface);
{
if (isActive)
draw.AddRectFilled(rowMin, new Vector2(rowMin.X + 2f, rowMax.Y), primaryAbgr); draw.AddRectFilled(rowMin, new Vector2(rowMin.X + 2f, rowMax.Y), primaryAbgr);
}
draw.AddLine(
new Vector2(rowMin.X, rowMax.Y - 1f),
new Vector2(rowMax.X, rowMax.Y - 1f),
borderAbgr,
1f
);
var labelSize = ImGui.CalcTextSize(labels[i]); var labelSize = ImGui.CalcTextSize(labels[i]);
var textPos = new Vector2(rowMin.X + 6f, rowMin.Y + (rowHeight - labelSize.Y) * 0.5f); var textPos = new Vector2(rowMin.X + 6f, rowMin.Y + (rowHeight - labelSize.Y) * 0.5f);
@@ -200,13 +229,21 @@ internal sealed class LivePreviewPanel : IDisposable
if (i == 1) if (i == 1)
{ {
// Tell row carries an unread-dot in Accent on the right. // Unread marker: a rounded count badge in Accent, not a square.
var dotCenter = new Vector2(rowMax.X - 8f, rowMin.Y + rowHeight * 0.5f); var badgeH = MathF.Min(rowHeight - 4f, 14f);
draw.AddRectFilled( var badgeW = badgeH * 1.4f;
new Vector2(dotCenter.X - 2f, dotCenter.Y - 2f), var badgeMin = new Vector2(
new Vector2(dotCenter.X + 2f, dotCenter.Y + 2f), rowMax.X - badgeW - 4f,
accentAbgr rowMin.Y + (rowHeight - badgeH) * 0.5f
); );
var badgeMax = badgeMin + new Vector2(badgeW, badgeH);
draw.AddRectFilled(
badgeMin,
badgeMax,
(accentAbgr & 0x00FFFFFFu) | 0x38000000u,
badgeH * 0.5f
);
draw.AddRect(badgeMin, badgeMax, accentAbgr, badgeH * 0.5f);
} }
} }
} }
@@ -271,7 +308,7 @@ internal sealed class LivePreviewPanel : IDisposable
var pillMax = new Vector2(origin.X + pillWidth, max.Y - 4f); var pillMax = new Vector2(origin.X + pillWidth, max.Y - 4f);
draw.AddRectFilled(pillMin, pillMax, ColourUtil.RgbaToAbgr(theme.Colors.Primary), 6f); draw.AddRectFilled(pillMin, pillMax, ColourUtil.RgbaToAbgr(theme.Colors.Primary), 6f);
var pillLabel = "Say"; var pillLabel = ChatType.Say.Name();
var pillLabelSize = ImGui.CalcTextSize(pillLabel); var pillLabelSize = ImGui.CalcTextSize(pillLabel);
var pillTextPos = new Vector2( var pillTextPos = new Vector2(
pillMin.X + ((pillMax.X - pillMin.X) - pillLabelSize.X) * 0.5f, pillMin.X + ((pillMax.X - pillMin.X) - pillLabelSize.X) * 0.5f,
@@ -279,7 +316,7 @@ internal sealed class LivePreviewPanel : IDisposable
); );
draw.AddText(pillTextPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), pillLabel); 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 phSize = ImGui.CalcTextSize(placeholder);
var phPos = new Vector2(pillMax.X + 6f, origin.Y + (height - phSize.Y) * 0.5f); var phPos = new Vector2(pillMax.X + 6f, origin.Y + (height - phSize.Y) * 0.5f);
draw.AddText(phPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), placeholder); draw.AddText(phPos, ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary), placeholder);
@@ -298,44 +335,84 @@ internal sealed class LivePreviewPanel : IDisposable
ImGui.Dummy(new Vector2(width, height)); ImGui.Dummy(new Vector2(width, height));
} }
// Mirrors the real status bar: a top rule, then pill-shaped slots, the last
// one right-aligned. The status colours ride along as slot dots so a theme
// still shows what it does to them.
private static void DrawStatusBar(Theme theme) private static void DrawStatusBar(Theme theme)
{ {
const float height = 20f; const float height = 24f;
const float iconSize = 8f; const float pillH = 18f;
const float iconGap = 6f; const float padX = 6f;
const float gap = 6f;
var draw = ImGui.GetWindowDrawList(); var draw = ImGui.GetWindowDrawList();
var origin = ImGui.GetCursorScreenPos(); var origin = ImGui.GetCursorScreenPos();
var width = ImGui.GetContentRegionAvail().X; var width = ImGui.GetContentRegionAvail().X;
var max = new Vector2(origin.X + width, origin.Y + height); var max = new Vector2(origin.X + width, origin.Y + height);
draw.AddRectFilled(origin, max, ColourUtil.RgbaToAbgr(theme.Colors.ChildBg)); draw.AddRectFilled(origin, max, ColourUtil.RgbaToAbgr(theme.Colors.ChildBg));
draw.AddLine(
origin,
new Vector2(max.X, origin.Y),
ColourUtil.RgbaToAbgr(theme.Colors.Border),
1f
);
ReadOnlySpan<uint> statusRgba = var fill = Abgr(StyleEngine.Token.SurfaceRaised, theme.Colors);
var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
var pillY = origin.Y + (height - pillH) * 0.5f;
ReadOnlySpan<string> slots =
[
ChatType.Say.Name(),
HellionStrings.Settings_Preview_StatusOpen,
string.Format(HellionStrings.StatusBar_Tabs_Other, 3),
];
ReadOnlySpan<uint> dots =
[ [
theme.Colors.StatusSuccess, theme.Colors.StatusSuccess,
theme.Colors.StatusDanger,
theme.Colors.StatusWarning, theme.Colors.StatusWarning,
theme.Colors.StatusDanger,
]; ];
var iconY = origin.Y + (height - iconSize) * 0.5f; var x = origin.X + padX;
for (var i = 0; i < statusRgba.Length; i++) for (var i = 0; i < slots.Length; i++)
{ {
var iconX = origin.X + 6f + i * (iconSize + iconGap); var labelSize = ImGui.CalcTextSize(slots[i]);
draw.AddRectFilled( var slotW = labelSize.X + padX * 2f + 10f;
new Vector2(iconX, iconY), var slotMin = new Vector2(x, pillY);
new Vector2(iconX + iconSize, iconY + iconSize), var slotMax = new Vector2(x + slotW, pillY + pillH);
ColourUtil.RgbaToAbgr(statusRgba[i]),
2f draw.AddRectFilled(slotMin, slotMax, fill, pillH * 0.5f);
draw.AddCircleFilled(
new Vector2(x + padX + 2f, pillY + pillH * 0.5f),
2.5f,
ColourUtil.RgbaToAbgr(dots[i]),
10
); );
draw.AddText(
new Vector2(x + padX + 10f, pillY + (pillH - labelSize.Y) * 0.5f),
textAbgr,
slots[i]
);
x += slotW + gap;
} }
var label = "preview"; var label = "preview";
var labelSize = ImGui.CalcTextSize(label); var versionSize = ImGui.CalcTextSize(label);
var labelPos = new Vector2( var versionW = versionSize.X + padX * 2f;
max.X - labelSize.X - 6f, var versionMin = new Vector2(max.X - versionW - padX, pillY);
origin.Y + (height - labelSize.Y) * 0.5f draw.AddRectFilled(
versionMin,
versionMin + new Vector2(versionW, pillH),
fill,
pillH * 0.5f
);
draw.AddText(
new Vector2(versionMin.X + padX, pillY + (pillH - versionSize.Y) * 0.5f),
ColourUtil.RgbaToAbgr(theme.Colors.TextMuted),
label
); );
draw.AddText(labelPos, ColourUtil.RgbaToAbgr(theme.Colors.TextDim), label);
ImGui.Dummy(new Vector2(width, height)); ImGui.Dummy(new Vector2(width, height));
} }
@@ -0,0 +1,39 @@
using Dalamud.Bindings.ImGui;
using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
using HellionChat.Ui.StyleEngine.Widgets;
namespace HellionChat.Ui.Components.Settings;
// Section headings for the components that are not built out of setting rows --
// the theme picker, the colour editor, the font panel. They need the heading and
// nothing else from SettingsWidgets, and pulling that in would hand each of them
// a Plugin reference they have no use for.
internal sealed class SectionRenderer
{
private readonly ThemeRegistry _themes;
private readonly SettingsPalette _palette;
private int _frame = -1;
private SectionHeaderColors _colors;
internal SectionRenderer(ThemeRegistry themes, TokenResolver resolver)
{
_themes = themes;
_palette = new SettingsPalette(resolver);
}
// Key, not title. ImGui's own storage keys a collapsing header off its
// label, so translated titles would reset every open section on a language
// switch, and two categories translating alike would share one state.
internal bool Draw(uint key, string title, bool open = true, bool disabled = false)
{
if (_frame != ImGui.GetFrameCount())
{
_frame = ImGui.GetFrameCount();
_colors = _palette.Section(_themes.Active.Colors);
}
return SectionHeader.Draw(key, title, null, _colors, defaultOpen: open, disabled: disabled);
}
}
@@ -0,0 +1,96 @@
using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
using HellionChat.Ui.StyleEngine.Widgets;
using HellionChat.Util;
namespace HellionChat.Ui.Components.Settings;
// Every styled settings control needs the same four or five theme slots, and
// each widget wants them in its own colour struct. Building those inline turned
// each call site into six lines of plumbing around one line of intent.
//
// Rebuilt per frame rather than cached: the active theme changes while the
// window is open -- that is what the appearance tab is for.
internal sealed class SettingsPalette
{
private readonly WidgetPalette _palette;
internal SettingsPalette(TokenResolver resolver)
{
_palette = new WidgetPalette(resolver);
}
// Everything a caller reads is measured against the surface it lands on
// rather than taken at face value. A theme sets one text colour; the pane
// under it is tinted by the backdrop, and a muted tone that reads on the
// base surface can disappear on the lit edge.
//
// 4.5:1 for the label, 3:1 for the description -- the lower floor keeps the
// rank between the two lines, which is the whole reason the description is
// dimmer, while still guaranteeing it stays readable.
internal SettingRowColors Row(ThemeColors c)
{
var surface = _palette.Abgr(Token.SurfaceBase, c);
return new SettingRowColors
{
LabelAbgr = ColourUtil.EnsureContrast(_palette.Abgr(Token.Text, c), surface, 4.5f),
DescriptionAbgr = ColourUtil.EnsureContrast(
_palette.Abgr(Token.TextFaint, c),
surface,
3f
),
SurfaceHoverAbgr = _palette.Abgr(Token.SurfaceHover, c),
BorderAbgr = _palette.Abgr(Token.Border, c),
};
}
internal ToggleSwitchColors Toggle(ThemeColors c)
{
var surface = _palette.Abgr(Token.SurfaceBase, c);
return new ToggleSwitchColors
{
TrackOffAbgr = surface,
TrackOnAbgr = _palette.Abgr(Token.AccentPrimary, c),
KnobAbgr = _palette.Abgr(Token.Text, c),
// The outline is the only thing that marks an off switch, so it has
// to clear the surface it sits on rather than blend into it.
BorderAbgr = ColourUtil.EnsureContrast(_palette.Abgr(Token.Border, c), surface, 3f),
};
}
internal SectionHeaderColors Section(ThemeColors c)
{
var surface = _palette.Abgr(Token.SurfaceBase, c);
return new SectionHeaderColors
{
TitleAbgr = ColourUtil.EnsureContrast(_palette.Abgr(Token.Text, c), surface, 4.5f),
DescriptionAbgr = ColourUtil.EnsureContrast(
_palette.Abgr(Token.TextMuted, c),
surface,
3f
),
// The chevron and the hover tint of the heading both use this, so it
// has to clear the icon floor even when a theme picks a dim accent.
AccentAbgr = ColourUtil.EnsureContrast(
_palette.Abgr(Token.AccentPrimary, c),
surface,
3f
),
BorderAbgr = _palette.Abgr(Token.Border, c),
HoverAbgr = _palette.Abgr(Token.SurfaceHover, c),
};
}
internal SegmentedControlColors Segmented(ThemeColors c) =>
new()
{
TrackAbgr = _palette.Abgr(Token.SurfaceBase, c),
SelectedAbgr = _palette.Abgr(Token.AccentPrimary, c),
HoverAbgr = _palette.Abgr(Token.SurfaceHover, c),
// Both are re-measured inside the widget against the fill each one
// actually sits on, which differs per segment.
LabelAbgr = _palette.Abgr(Token.TextMuted, c),
SelectedLabelAbgr = _palette.Abgr(Token.Text, c),
BorderAbgr = _palette.Abgr(Token.Border, c),
};
}
@@ -0,0 +1,447 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
using HellionChat.Ui.StyleEngine.Widgets;
namespace HellionChat.Ui.Components.Settings;
// Enum.GetValues allocates a fresh array on every call, and the settings tabs
// were calling it inside Draw -- once per combo, every frame the window is open.
// The set cannot change at runtime, so it is read once per closed generic.
internal static class EnumValues<T>
where T : struct, Enum
{
internal static readonly T[] All = Enum.GetValues<T>();
}
// The four controls every settings tab draws. Six tabs carried a byte-identical
// DrawToggle, four a byte-identical slider, and five hand-rolled the same combo
// loop with different widths.
//
// Deliberately constructed by the tabs rather than injected: the tabs are DI
// singletons, and a new constructor parameter on all seven of them buys nothing
// here beyond a wider blast radius.
internal sealed class SettingsWidgets
{
private readonly Plugin _plugin;
// Shared across every combo. ImGui.Combo copies the strings it needs before
// returning, so the buffer is free again by the time the next call runs.
// Always sliced to the value count when passed on -- see EnumCombo.
private string[] _labelScratch = new string[8];
private readonly SettingsPalette? _colors;
// Cached per frame: twenty rows would otherwise re-resolve the same five
// theme tokens twenty times, and the active theme cannot change mid-frame.
private int _frame = -1;
private SettingRowColors _row;
private ToggleSwitchColors _toggle;
private SectionHeaderColors _section;
private SegmentedControlColors _segmented;
internal SettingsWidgets(Plugin plugin, SettingsPalette? colors = null)
{
_plugin = plugin;
_colors = colors;
}
// Tabs that have not been converted yet pass no palette and keep using the
// plain ImGui helpers below.
private void EnsureFrame()
{
if (_colors is null || _frame == ImGui.GetFrameCount())
return;
_frame = ImGui.GetFrameCount();
var c = _plugin.ThemeRegistry.Active.Colors;
_row = _colors.Row(c);
_toggle = _colors.Toggle(c);
_section = _colors.Section(c);
_segmented = _colors.Segmented(c);
}
internal bool Section(uint key, string title, string? description = null, bool open = true)
{
EnsureFrame();
return SectionHeader.Draw(key, title, description, _section, defaultOpen: open);
}
// The whole row toggles, label included. The switch itself gets its own
// invisible button because SettingRow's hit area stops at the label column,
// and clicking the control is what a user tries first.
// Escape hatch for controls the helpers do not cover -- a keybind capture,
// a picker with side effects. The caller draws whatever it likes into the
// control column and keeps its own save logic.
internal void Row(
uint id,
string label,
string? description,
Action<SettingRowContext> drawControl
)
{
EnsureFrame();
SettingRow.Draw(id, label, description, _row, drawControl);
}
internal void ToggleRow(
uint id,
string label,
string? description,
Func<bool> get,
Action<bool> set
)
{
EnsureFrame();
var value = get();
var hit = false;
var rowClicked = SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
var size = ToggleSwitch.CalcSize();
var pos = ctx.AlignRight(size);
ImGui.SetCursorScreenPos(pos);
if (ImGui.InvisibleButton($"##hc-sw-{id}", size))
hit = true;
ToggleSwitch.Draw(id, pos, value, _toggle);
}
);
if (!rowClicked && !hit)
return;
set(!value);
_plugin.SaveConfig();
}
internal void SliderFloatRow(
uint id,
string label,
string? description,
Func<float> get,
Action<float> set,
float min,
float max
)
{
EnsureFrame();
var current = get();
SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
ImGui.SetNextItemWidth(ctx.ControlWidth);
if (ImGui.SliderFloat($"##hc-sf-{id}", ref current, min, max, "%.2f"))
set(current);
if (ImGui.IsItemDeactivatedAfterEdit())
_plugin.SaveConfig();
}
);
}
internal void SliderIntRow(
uint id,
string label,
string? description,
Func<int> get,
Action<int> set,
int min,
int max
)
{
EnsureFrame();
var current = get();
SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
ImGui.SetNextItemWidth(ctx.ControlWidth);
if (ImGui.SliderInt($"##hc-si-{id}", ref current, min, max, "%d"))
set(current);
if (ImGui.IsItemDeactivatedAfterEdit())
_plugin.SaveConfig();
}
);
}
internal void EnumComboRow<T>(
uint id,
string label,
string? description,
Func<T> get,
Action<T> set,
Func<T, string> labelFor
)
where T : struct, Enum
{
EnsureFrame();
SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
ImGui.SetNextItemWidth(ctx.ControlWidth);
EnumCombo($"##hc-ec-{id}", get, set, labelFor, ctx.ControlWidth);
}
);
}
// One setting, n choices. The control fills the whole control column rather
// than right-aligning, because segments need the room to stay readable.
internal void SegmentRow<T>(
uint id,
string label,
string? description,
T[] values,
string[] labels,
Func<T> get,
Action<T> set
)
where T : struct, Enum
{
EnsureFrame();
var current = get();
var selected = 0;
for (var i = 0; i < values.Length; i++)
if (EqualityComparer<T>.Default.Equals(values[i], current))
selected = i;
var picked = selected;
var colors = _segmented;
SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
ImGui.SetCursorScreenPos(new Vector2(ctx.ControlOrigin.X, ctx.ControlOrigin.Y));
picked = SegmentedControl.Draw(id, ctx.ControlWidth, labels, selected, colors);
}
);
if (picked == selected)
return;
set(values[picked]);
_plugin.SaveConfig();
}
// Transient rows for form state that never reaches the config: an export
// filter, a cleanup preview. They hand the value back instead of taking a
// setter, and they do not call SaveConfig -- there is nothing to save, and
// writing the config file on every keystroke of a sender filter would be
// both pointless and slow.
internal bool ToggleRow(uint id, string label, string? description, bool value)
{
EnsureFrame();
var hit = false;
var rowClicked = SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
var size = ToggleSwitch.CalcSize();
var pos = ctx.AlignRight(size);
ImGui.SetCursorScreenPos(pos);
if (ImGui.InvisibleButton($"##hc-sw-{id}", size))
hit = true;
ToggleSwitch.Draw(id, pos, value, _toggle);
}
);
return rowClicked || hit ? !value : value;
}
internal string TextRow(uint id, string label, string? description, string value)
{
EnsureFrame();
var current = value;
SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
// PushId rather than an interpolated label: the binding only
// offers a ref-string InputText for a literal label, and the ID
// stack separates the rows just as well. RAII because a throw
// inside InputText would otherwise leave the stack unbalanced
// and trip the assert in End().
using var scope = ImRaii.PushId((int)id);
ImGui.SetNextItemWidth(ctx.ControlWidth);
// 511, not 512: the binding reserves maxLength + 1 and rents from
// the array pool once that reaches 512.
ImGui.InputText("##hc-tr", ref current, 511);
}
);
return current;
}
internal int SliderIntRow(
uint id,
string label,
string? description,
int value,
int min,
int max
)
{
EnsureFrame();
var current = value;
SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
ImGui.SetNextItemWidth(ctx.ControlWidth);
ImGui.SliderInt($"##hc-si-{id}", ref current, min, max, "%d");
}
);
return current;
}
internal int SegmentRow(
uint id,
string label,
string? description,
string[] labels,
int selected
)
{
EnsureFrame();
// Clamped rather than trusted: the generic overload derives the index
// from the value and falls back to 0, this one takes whatever the caller
// passes. Array.IndexOf returns -1 on a miss, and the caller then indexes
// its value array with the result.
var current = Math.Clamp(selected, 0, Math.Max(0, labels.Length - 1));
var picked = current;
var colors = _segmented;
SettingRow.Draw(
id,
label,
description,
_row,
ctx =>
{
ImGui.SetCursorScreenPos(new Vector2(ctx.ControlOrigin.X, ctx.ControlOrigin.Y));
picked = SegmentedControl.Draw(id, ctx.ControlWidth, labels, current, colors);
}
);
return picked;
}
internal void Toggle(string label, Func<bool> get, Action<bool> set)
{
var current = get();
if (!ImGui.Checkbox(label, ref current))
return;
set(current);
_plugin.SaveConfig();
}
internal void SliderFloat(
string label,
Func<float> get,
Action<float> set,
float min,
float max,
float width = 200f
)
{
var current = get();
ImGui.SetNextItemWidth(width);
// Sliders report a change every frame while dragging; deferring the write
// to release turns ~30 full-config disk writes per second into one.
if (ImGui.SliderFloat(label, ref current, min, max, "%.2f"))
set(current);
if (ImGui.IsItemDeactivatedAfterEdit())
_plugin.SaveConfig();
}
internal void SliderInt(
string label,
Func<int> get,
Action<int> set,
int min,
int max,
float width = 200f
)
{
var current = get();
ImGui.SetNextItemWidth(width);
if (ImGui.SliderInt(label, ref current, min, max, "%d"))
set(current);
if (ImGui.IsItemDeactivatedAfterEdit())
_plugin.SaveConfig();
}
// labelFor is a parameter rather than a constraint because the display names
// live in extension methods, which bind statically and cannot be reached
// through a generic type parameter.
internal void EnumCombo<T>(
string label,
Func<T> get,
Action<T> set,
Func<T, string> labelFor,
float width = 200f
)
where T : struct, Enum
{
var values = EnumValues<T>.All;
if (values.Length == 0)
return;
if (_labelScratch.Length < values.Length)
_labelScratch = new string[values.Length];
var current = get();
var selected = 0;
for (var i = 0; i < values.Length; i++)
{
_labelScratch[i] = labelFor(values[i]);
if (EqualityComparer<T>.Default.Equals(values[i], current))
selected = i;
}
// Sliced, not passed whole with a count. The binding's fourth parameter
// is popupMaxHeightInItems, not the item count -- that comes from the
// span's own length. Handing over the full buffer would list all eight
// slots, so a three-value enum would show five blank rows.
ImGui.SetNextItemWidth(width);
if (!ImGui.Combo(label, ref selected, _labelScratch.AsSpan(0, values.Length)))
return;
if (selected < 0 || selected >= values.Length)
return;
set(values[selected]);
_plugin.SaveConfig();
}
}
@@ -0,0 +1,423 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Code;
using HellionChat.Resources;
using HellionChat.Util;
namespace HellionChat.Ui.Components.Settings;
// The only way to create a tab, delete one, reorder them, or choose what a tab
// collects. All of it went out with the settings window in May; the labels
// stayed, translated, in all 25 languages.
//
// An accordion rather than the list-and-detail pane the plan sketched. The
// settings content column is narrow, every other tab in this window is a stack
// of collapsible sections, and a split pane inside one of them would be the only
// thing here that reads differently for no gain.
internal sealed class TabEditor
{
private readonly Plugin _plugin;
// The channel matrix mutates the dictionary it is handed, so it never gets
// the tab's own. Edits land here and are published as one reference swap
// when the user leaves the tab -- see Tab.ReplaceChannelFilter for why a
// half-mutated dictionary is worse than a stale one.
private Guid _editing;
private Dictionary<ChatType, (ChatSource, ChatSource)>? _workingChannels;
private HashSet<Guid>? _workingExtraChat;
private bool _workingExtraChatAll;
// Saving on every checkbox would write the config file sixty-odd times per
// matrix. IsItemDeactivatedAfterEdit does not help here: it defers for
// sliders and text fields, which stay active across frames, but a checkbox
// activates and deactivates inside one click, so the event fires exactly as
// often as the return value.
private bool _dirty;
private long _dirtyAt;
private const long SaveIdleMs = 600;
public TabEditor(Plugin plugin) => _plugin = plugin;
public void Draw()
{
List<Tab> tabs;
lock (Plugin.Instance.TabsListLock)
tabs = Plugin.Config.Tabs.ToList();
DrawToolbar(tabs);
ImGui.Spacing();
for (var i = 0; i < tabs.Count; i++)
DrawTabNode(tabs, i);
FlushIfIdle();
}
private void DrawToolbar(List<Tab> tabs)
{
if (ImGuiUtil.IconButton(FontAwesomeIcon.Plus, tooltip: Language.Options_Tabs_Add))
ImGui.OpenPopup("##hc-add-tab");
using var popup = ImRaii.Popup("##hc-add-tab");
if (!popup.Success)
return;
if (ImGui.Selectable(Language.Options_Tabs_NewTab))
Insert(new Tab());
ImGui.Separator();
// Templates that have sat in TabsUtil without a caller. A new tab with
// no channels selected collects nothing, so an empty one is the worst
// possible starting point for anybody who has not read the matrix yet.
foreach (var (label, factory) in Presets)
{
if (ImGui.Selectable(string.Format(Language.Options_Tabs_Preset, label())))
Insert(factory());
}
}
private static readonly (Func<string> Label, Func<Tab> Factory)[] Presets =
[
(() => HellionStrings.Tabs_Presets_Party, () => TabsUtil.HellionParty),
(() => HellionStrings.Tabs_Presets_FreeCompany, () => TabsUtil.HellionFreeCompany),
(() => HellionStrings.Tabs_Presets_Linkshell, () => TabsUtil.HellionLinkshell),
(() => HellionStrings.Tabs_Presets_System, () => TabsUtil.HellionSystem),
(() => HellionStrings.Tabs_Presets_Beginner, () => TabsUtil.HellionBeginner),
];
private void Insert(Tab tab)
{
lock (Plugin.Instance.TabsListLock)
Plugin.Config.Tabs.Insert(
TabLifecycleHelpers.InsertIndexForNewTab(Plugin.Config.Tabs),
tab
);
_plugin.SaveConfig();
RequestRefilter();
}
private void DrawTabNode(List<Tab> tabs, int index)
{
var tab = tabs[index];
// Temp tabs share the list but not the editor: their name is a
// conversation partner, the auto-tell service owns their lifetime, and
// deleting one here would mean deleting a conversation. Pinning is the
// gesture they accept, and that lives in the context menu.
if (!TabLifecycleHelpers.IsEditable(tab))
return;
using var id = ImRaii.PushId(tab.Identifier.ToString());
// ### keeps the node's identity while its label follows the name field.
using var node = ImRaii.TreeNode($"{tab.Name}###hc-tab-node");
if (!node.Success)
{
// Collapsing is a leave: publish whatever was edited in here.
if (_editing == tab.Identifier)
CommitChannels(tab);
return;
}
DrawRowButtons(tabs, index, tab);
ImGui.Spacing();
var name = tab.Name;
ImGui.SetNextItemWidth(240f * ImGuiHelpers.GlobalScale);
if (ImGui.InputText(Language.Options_Tabs_Name, ref name, 512))
{
tab.Name = name;
MarkDirty();
}
DrawIconPicker(tab);
DrawDisplay(tab);
DrawChannels(tab);
}
private void DrawRowButtons(List<Tab> tabs, int index, Tab tab)
{
var canDelete = TabLifecycleHelpers.CanDelete(tabs, index);
using (ImRaii.Disabled(!canDelete))
{
if (
ImGuiUtil.IconButton(
FontAwesomeIcon.TrashAlt,
tooltip: Language.Options_Tabs_Delete
) && canDelete
)
{
Delete(tab, tabs, index);
return;
}
}
ImGui.SameLine();
if (ImGuiUtil.IconButton(FontAwesomeIcon.ArrowUp, tooltip: Language.Options_Tabs_MoveUp))
Move(index, -1);
ImGui.SameLine();
if (
ImGuiUtil.IconButton(FontAwesomeIcon.ArrowDown, tooltip: Language.Options_Tabs_MoveDown)
)
Move(index, +1);
// Duplicating is the cheapest way to build a variant of a tab that
// already has its sixty channels picked, and Tab.Clone has been ready
// for it since v1.8.0.
ImGui.SameLine();
if (
ImGuiUtil.IconButton(
FontAwesomeIcon.Copy,
tooltip: HellionStrings.Settings_Tabs_Duplicate
)
)
{
var copy = tab.Clone();
copy.Identifier = Guid.NewGuid();
Insert(copy);
}
}
private void Delete(Tab tab, List<Tab> tabs, int index)
{
// The pool binds windows by identifier; without this the slot stays
// taken by a tab that no longer exists.
_plugin.ChannelPopoutPool.TryClose(tab.Identifier);
lock (Plugin.Instance.TabsListLock)
Plugin.Config.Tabs.RemoveAll(t => t.Identifier == tab.Identifier);
if (_editing == tab.Identifier)
ClearWorking();
_plugin.SaveConfig();
RequestRefilter();
}
private void Move(int index, int delta)
{
lock (Plugin.Instance.TabsListLock)
{
var list = Plugin.Config.Tabs;
var target = TabLifecycleHelpers.MoveIndex(list, index, delta);
if (target == index)
return;
var tab = list[index];
list.RemoveAt(index);
list.Insert(target, tab);
}
_plugin.SaveConfig();
}
private void DrawIconPicker(Tab tab)
{
var current = tab.Icon ?? HellionStrings.Tabs_Icon_DefaultOption;
using (var combo = ImGuiUtil.BeginComboVertical(HellionStrings.Tabs_Icon_Label, current))
{
if (combo.Success)
{
if (ImGui.Selectable(HellionStrings.Tabs_Icon_DefaultOption, tab.Icon is null))
{
tab.Icon = null;
MarkDirty();
}
foreach (var glyph in IconNames)
{
if (!ImGui.Selectable(glyph, tab.Icon == glyph))
continue;
tab.Icon = glyph;
MarkDirty();
}
}
}
ImGuiUtil.HelpMarker(HellionStrings.Tabs_Icon_HelpMarker);
}
// Same set the sidebar resolves; a name outside it falls back to the
// channel-derived glyph rather than showing nothing.
private static readonly string[] IconNames =
[
"comment",
"comments",
"cog",
"users",
"user-friends",
"link",
"envelope",
"clock",
"hashtag",
"star",
"heart",
"bell",
"bookmark",
"flag",
"fire",
];
private void DrawDisplay(Tab tab)
{
using var node = ImRaii.TreeNode(
$"{HellionStrings.Settings_Section_Tab_Display}###hc-tab-display"
);
if (!node.Success)
return;
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
if (ImGui.Checkbox(Language.Options_Tabs_ShowTimestamps, ref tab.DisplayTimestamp))
MarkDirty();
using (
var combo = ImGuiUtil.BeginComboVertical(
Language.Options_Tabs_UnreadMode,
tab.UnreadMode.Name()
)
)
{
if (combo.Success)
{
foreach (var mode in EnumValues<UnreadMode>.All)
{
if (ImGui.Selectable(mode.Name(), tab.UnreadMode == mode))
{
tab.UnreadMode = mode;
MarkDirty();
}
if (mode.Tooltip() is { } tooltip && ImGui.IsItemHovered())
ImGuiUtil.Tooltip(tooltip);
}
}
}
if (ImGui.Checkbox(Language.Options_Tabs_SenderMessages, ref tab.AllSenderMessages))
MarkDirty();
}
private void DrawChannels(Tab tab)
{
using var node = ImRaii.TreeNode(
$"{HellionStrings.Settings_Section_Tab_Channels}###hc-tab-channels"
);
if (!node.Success)
{
if (_editing == tab.Identifier)
CommitChannels(tab);
return;
}
// Switching tabs without collapsing the previous one still has to
// publish it, or the edits sit in a working copy nobody reads again.
if (_editing != tab.Identifier)
{
CommitPending();
SeedWorking(tab);
}
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, _workingChannels!);
ImGuiUtil.ExtraChatSelector(
Language.Options_Tabs_ExtraChatChannels,
ref _workingExtraChatAll,
_workingExtraChat!
);
}
private void SeedWorking(Tab tab)
{
_editing = tab.Identifier;
_workingChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>(tab.SelectedChannels);
_workingExtraChat = new HashSet<Guid>(tab.ExtraChatChannels);
_workingExtraChatAll = tab.ExtraChatAll;
}
private void CommitPending()
{
if (_editing == Guid.Empty)
return;
Tab? tab;
lock (Plugin.Instance.TabsListLock)
tab = Plugin.Config.Tabs.FirstOrDefault(t => t.Identifier == _editing);
if (tab is not null)
CommitChannels(tab);
else
ClearWorking();
}
private void CommitChannels(Tab tab)
{
if (_workingChannels is null || _workingExtraChat is null)
{
ClearWorking();
return;
}
var changed =
_workingExtraChatAll != tab.ExtraChatAll
|| !_workingExtraChat.SetEquals(tab.ExtraChatChannels)
|| _workingChannels.Count != tab.SelectedChannels.Count
|| _workingChannels.Any(p =>
!tab.SelectedChannels.TryGetValue(p.Key, out var v) || v != p.Value
);
if (changed)
{
tab.ReplaceChannelFilter(_workingChannels, _workingExtraChatAll, _workingExtraChat);
_plugin.SaveConfig();
// Deselecting a channel needs a rebuild, not a filter pass:
// AddSortPrune deduplicates and never removes, so the messages that
// no longer match are already in the list.
RequestRefilter();
}
ClearWorking();
}
private void ClearWorking()
{
_editing = Guid.Empty;
_workingChannels = null;
_workingExtraChat = null;
_workingExtraChatAll = false;
}
private void MarkDirty()
{
_dirty = true;
_dirtyAt = Environment.TickCount64;
}
private void FlushIfIdle()
{
if (!_dirty || _dirtyAt + SaveIdleMs > Environment.TickCount64)
return;
_dirty = false;
_plugin.SaveConfig();
}
// RunOnTick does not offload: it runs on the framework thread, just at the
// start of a later tick rather than inside this draw. That is the point --
// clearing every tab mid-frame is where the hitch would come from. The
// refilter itself then goes to the thread pool.
private void RequestRefilter() =>
Plugin.Framework.RunOnTick(() =>
{
_plugin.MessageManager.ClearAllTabs();
_plugin.MessageManager.FilterAllTabsAsync();
});
}
+161 -14
View File
@@ -2,51 +2,198 @@ using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
using HellionChat.Util;
namespace HellionChat.Ui.Components.Settings; namespace HellionChat.Ui.Components.Settings;
internal sealed class TabSidebar internal sealed class TabSidebar
{ {
private readonly FontManager _fonts; private readonly FontManager _fonts;
private readonly ThemeRegistry _themes;
private readonly TokenResolver _resolver;
public event Action<string>? OnTabSelected; public event Action<string>? OnTabSelected;
public string ActiveTab { get; private set; } = "general"; public string ActiveTab { get; private set; } = "general";
public TabSidebar(FontManager fonts) public TabSidebar(FontManager fonts, ThemeRegistry themes, TokenResolver resolver)
{ {
_fonts = fonts; _fonts = fonts;
_themes = themes;
_resolver = resolver;
} }
public void Draw() public void Draw()
{ {
using var bg = ImRaii.PushColor(ImGuiCol.ChildBg, 0u);
using var child = ImRaii.Child("##settings-tab-sidebar", new Vector2(170, 0), true); using var child = ImRaii.Child("##settings-tab-sidebar", new Vector2(170, 0), true);
if (!child.Success) if (!child.Success)
{ {
return; return;
} }
DrawEntry("general", FontAwesomeIcon.SlidersH, "General"); var colors = _themes.Active.Colors;
DrawEntry("appearance", FontAwesomeIcon.Palette, "Appearance"); var dl = ImGui.GetWindowDrawList();
DrawEntry("chat", FontAwesomeIcon.Comments, "Chat"); var min = ImGui.GetWindowPos();
DrawEntry("window", FontAwesomeIcon.WindowMaximize, "Window"); var max = min + ImGui.GetWindowSize();
DrawEntry("channels", FontAwesomeIcon.Hashtag, "Channels");
DrawEntry("data-privacy", FontAwesomeIcon.Shield, "Data & Privacy");
DrawEntry("about", FontAwesomeIcon.InfoCircle, "About");
}
private void DrawEntry(string id, FontAwesomeIcon icon, string label) var accent = ColourUtil.RgbaToAbgr(_resolver.Resolve(Token.AccentPrimary, colors));
{
// A wash of black rather than a repainted surface. The window has drawn
// its own background already; filling over it stacks a second layer and
// turns a translucent window solid. This only needs to read as the
// darker of two planes, and a tint does that.
// Opaque, matching SettingsWindow.BgAlpha; the pushed WindowBg still
// carries the chat window's transparency and would wash this out.
dl.AddRectFilled(min, max, 0x38u << 24);
// The icon font has no ASCII glyphs, so anything textual drawn inside a
// FontAwesome scope comes out blank -- twice bitten in this plugin. The
// handle is taken here and the glyphs are drawn through the draw list
// instead, which keeps the scope off the labels entirely.
ImFontPtr iconFont;
float iconSize;
using (_fonts.FontAwesome.Push()) using (_fonts.FontAwesome.Push())
{ {
ImGui.TextUnformatted(icon.ToIconString()); iconFont = ImGui.GetFont();
iconSize = ImGui.GetFontSize();
} }
ImGui.SameLine();
var palette = new SidebarPalette
{
Text = ColourUtil.RgbaToAbgr(_resolver.Resolve(Token.Text, colors)),
TextMuted = ColourUtil.RgbaToAbgr(_resolver.Resolve(Token.TextMuted, colors)),
Accent = accent,
Hover = ColourUtil.RgbaToAbgr(_resolver.Resolve(Token.SurfaceHover, colors)),
Surface = ColourUtil.RgbaToAbgr(_resolver.Resolve(Token.SurfaceBase, colors)),
IconFont = iconFont,
IconSize = iconSize,
};
DrawEntry(
"general",
FontAwesomeIcon.SlidersH,
HellionStrings.Settings_Tab_General,
palette
);
DrawEntry(
"appearance",
FontAwesomeIcon.Palette,
HellionStrings.Settings_Tab_Appearance,
palette
);
DrawEntry("chat", FontAwesomeIcon.Comments, HellionStrings.Settings_Tab_Chat, palette);
DrawEntry(
"window",
FontAwesomeIcon.WindowMaximize,
HellionStrings.Settings_Tab_Window,
palette
);
DrawEntry("channels", FontAwesomeIcon.Hashtag, HellionStrings.Settings_Tab_Tabs, palette);
DrawEntry(
"data-privacy",
FontAwesomeIcon.Shield,
HellionStrings.Settings_Card_DataManagement_Title,
palette
);
DrawEntry(
"about",
FontAwesomeIcon.InfoCircle,
HellionStrings.Settings_Tab_Information,
palette
);
}
private readonly record struct SidebarPalette
{
public required uint Text { get; init; }
public required uint TextMuted { get; init; }
public required uint Accent { get; init; }
public required uint Hover { get; init; }
// What the row is actually drawn on, which is what a foreground has to
// clear -- not the theme's nominal background.
public required uint Surface { get; init; }
public required ImFontPtr IconFont { get; init; }
public required float IconSize { get; init; }
}
private void DrawEntry(string id, FontAwesomeIcon icon, string label, SidebarPalette p)
{
var scale = Metrics.Scale;
var origin = ImGui.GetCursorScreenPos();
var width = ImGui.GetContentRegionAvail().X;
var height = ImGui.GetTextLineHeight() + 12f * scale;
var size = new Vector2(width, height);
var selected = ActiveTab == id; var selected = ActiveTab == id;
if (ImGui.Selectable($" {label}##tab-{id}", selected)) var clicked = ImGui.InvisibleButton($"##tab-{id}", size);
var hovered = ImGui.IsItemHovered();
var hoverAmount = HoverState.Query(ImGui.GetID($"sidebar.{id}"), hovered);
if (clicked)
{ {
ActiveTab = id; ActiveTab = id;
OnTabSelected?.Invoke(id); OnTabSelected?.Invoke(id);
} }
var dl = ImGui.GetWindowDrawList();
if (selected)
{
// Chamfered plate plus a solid bar on the leading edge. The bar is
// what survives at a glance; the plate gives it something to sit on.
dl.DrawSlipPolygon(
origin,
origin + size,
ColourUtil.RgbaToAbgr(ColourUtil.ApplyAlpha(p.Accent, 0.22f)),
6f * scale
);
dl.AddRectFilled(
origin,
new Vector2(origin.X + 2.5f * scale, origin.Y + size.Y),
p.Accent
);
}
else if (hoverAmount > 0f)
dl.AddRectFilled(
origin,
origin + size,
ColourUtil.ApplyAlpha(p.Hover, hoverAmount * 0.7f)
);
var contentColour = selected ? p.Text : ColourUtil.Lerp(p.TextMuted, p.Text, hoverAmount);
// The active row is filled with the accent, so the glyph on it has to
// clear that fill rather than the pane behind it. White on a pale violet
// accent measures 2.4:1, well under the 3:1 floor for icons, and that is
// exactly the case that was reported as unreadable.
var iconColour = selected
? ColourUtil.EnsureContrast(p.Accent, p.Surface, 3f)
: ColourUtil.EnsureContrast(contentColour, p.Surface, 3f);
var iconX = origin.X + 12f * scale;
dl.AddText(
p.IconFont,
p.IconSize,
new Vector2(iconX, origin.Y + MetricsMath.Center(height, p.IconSize)),
iconColour,
icon.ToIconString()
);
var labelSize = ImGui.CalcTextSize(label);
dl.AddText(
new Vector2(
iconX + p.IconSize + 9f * scale,
origin.Y + MetricsMath.Center(height, labelSize.Y)
),
ColourUtil.EnsureContrast(contentColour, p.Surface, 4.5f),
label
);
ImGui.SetCursorScreenPos(origin);
ImGuiP.ItemSize(new Vector2(width, size.Y - ImGui.GetStyle().ItemSpacing.Y + 3f * scale));
} }
} }
@@ -13,6 +13,7 @@ internal sealed class AboutTab
{ {
private readonly FontManager _fonts; private readonly FontManager _fonts;
private readonly Plugin _plugin; private readonly Plugin _plugin;
private readonly SettingsWidgets _w;
private readonly HonorificService _honorific; private readonly HonorificService _honorific;
private readonly ThemeRegistry _themes; private readonly ThemeRegistry _themes;
private readonly IPlatformUtil _platformUtil; private readonly IPlatformUtil _platformUtil;
@@ -30,6 +31,7 @@ internal sealed class AboutTab
{ {
_fonts = fonts; _fonts = fonts;
_plugin = plugin; _plugin = plugin;
_w = new SettingsWidgets(plugin);
_honorific = honorific; _honorific = honorific;
_themes = themes; _themes = themes;
_platformUtil = platformUtil; _platformUtil = platformUtil;
@@ -41,15 +43,15 @@ internal sealed class AboutTab
// real render can never let the integrations-status SelfTest pass falsely. // real render can never let the integrations-status SelfTest pass falsely.
LastHonorificStatusKey = null; LastHonorificStatusKey = null;
DrawPluginInfo(); DrawPluginInfo();
DrawSectionHeader("Brand"); DrawSectionHeader(HellionStrings.Settings_Section_Brand);
DrawBrand(); DrawBrand();
DrawSectionHeader("Links"); DrawSectionHeader(HellionStrings.Settings_Section_Links);
DrawLinks(); DrawLinks();
DrawSectionHeader("Integrations"); DrawSectionHeader(HellionStrings.Settings_Section_Integrations);
DrawIntegrations(); DrawIntegrations();
DrawSectionHeader("Credits"); DrawSectionHeader(HellionStrings.Settings_Section_Credits);
DrawCredits(); DrawCredits();
DrawSectionHeader("License"); DrawSectionHeader(HellionStrings.Settings_Section_License);
DrawLicense(); DrawLicense();
} }
@@ -92,8 +94,11 @@ internal sealed class AboutTab
private void DrawLinks() private void DrawLinks()
{ {
DrawLinkButton("Discord (Hellion Forge)", BrandingLinks.HellionForgeDiscordInvite); DrawLinkButton("Discord (Hellion Forge)", BrandingLinks.HellionForgeDiscordInvite);
DrawLinkButton("Gitea repository", BrandingLinks.HellionChatRepo); DrawLinkButton(HellionStrings.Settings_About_GiteaRepo, BrandingLinks.HellionChatRepo);
DrawLinkButton("Custom repo manifest", BrandingLinks.HellionChatCustomRepoManifest); DrawLinkButton(
HellionStrings.Settings_About_CustomRepo,
BrandingLinks.HellionChatCustomRepoManifest
);
} }
private void DrawIntegrations() private void DrawIntegrations()
@@ -103,7 +108,7 @@ internal sealed class AboutTab
ImGui.TextUnformatted(HellionStrings.Settings_Integrations_Honorific_SectionHeader); ImGui.TextUnformatted(HellionStrings.Settings_Integrations_Honorific_SectionHeader);
DrawHonorificStatus(); DrawHonorificStatus();
DrawToggle( _w.Toggle(
HellionStrings.Settings_Integrations_Honorific_Toggle, HellionStrings.Settings_Integrations_Honorific_Toggle,
() => Plugin.Config.ShowHonorificTitleInHeader, () => Plugin.Config.ShowHonorificTitleInHeader,
v => Plugin.Config.ShowHonorificTitleInHeader = v v => Plugin.Config.ShowHonorificTitleInHeader = v
@@ -222,16 +227,6 @@ internal sealed class AboutTab
} }
} }
private void DrawToggle(string label, Func<bool> get, Action<bool> set)
{
var current = get();
if (ImGui.Checkbox(label, ref current))
{
set(current);
_plugin.SaveConfig();
}
}
// URLs are exclusively hardcoded BrandingLinks/IntegrationLinks constants, // URLs are exclusively hardcoded BrandingLinks/IntegrationLinks constants,
// validated to http/https at module-init. OpenLink centralises the browser // validated to http/https at module-init. OpenLink centralises the browser
// open on an off-draw thread (it internally uses the same ShellExecute, so // open on an off-draw thread (it internally uses the same ShellExecute, so
@@ -1,125 +1,134 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using HellionChat.Resources;
using HellionChat.Ui.StyleEngine;
namespace HellionChat.Ui.Components.Settings.Tabs; namespace HellionChat.Ui.Components.Settings.Tabs;
internal sealed class ChannelsTab internal sealed class ChannelsTab
{ {
private readonly Plugin _plugin; private readonly SettingsWidgets _w;
private readonly TabEditor _editor;
public ChannelsTab(Plugin plugin) public ChannelsTab(Plugin plugin, TokenResolver resolver)
{ {
_plugin = plugin; _w = new SettingsWidgets(plugin, new SettingsPalette(resolver));
_editor = new TabEditor(plugin);
} }
public void Draw() public void Draw()
{ {
if (ImGui.CollapsingHeader("Tab management", ImGuiTreeNodeFlags.DefaultOpen)) // First, because it answers the question the rest of this tab assumes
// is already settled: which tabs exist and what do they collect.
if (_w.Section(ImGui.GetID("channels.tabs"u8), Language.Options_Tabs_Tab))
_editor.Draw();
if (
_w.Section(
ImGui.GetID("channels.autotell"u8),
HellionStrings.Settings_Section_AutoTellTabs
)
)
{ {
DrawToggle( _w.ToggleRow(
"Enable auto-tell tabs", ImGui.GetID("channels.autotell.enable"u8),
HellionStrings.ChatLog_AutoTellTabs_Enable_Name,
HellionStrings.ChatLog_AutoTellTabs_Enable_Description,
() => Plugin.Config.EnableAutoTellTabs, () => Plugin.Config.EnableAutoTellTabs,
v => Plugin.Config.EnableAutoTellTabs = v v => Plugin.Config.EnableAutoTellTabs = v
); );
DrawSliderInt( _w.SliderIntRow(
"Auto-tell tabs limit", ImGui.GetID("channels.autotell.limit"u8),
HellionStrings.ChatLog_AutoTellTabs_Limit_Name,
HellionStrings.ChatLog_AutoTellTabs_Limit_Description,
() => Plugin.Config.AutoTellTabsLimit, () => Plugin.Config.AutoTellTabsLimit,
v => Plugin.Config.AutoTellTabsLimit = v, v => Plugin.Config.AutoTellTabsLimit = v,
1, 1,
50 50
); );
DrawToggle( _w.ToggleRow(
"Compact display", ImGui.GetID("channels.autotell.compact"u8),
HellionStrings.ChatLog_AutoTellTabs_Compact_Name,
HellionStrings.ChatLog_AutoTellTabs_Compact_Description,
() => Plugin.Config.AutoTellTabsCompactDisplay, () => Plugin.Config.AutoTellTabsCompactDisplay,
v => Plugin.Config.AutoTellTabsCompactDisplay = v v => Plugin.Config.AutoTellTabsCompactDisplay = v
); );
DrawSliderInt( _w.SliderIntRow(
"History preload", ImGui.GetID("channels.autotell.preload"u8),
HellionStrings.Privacy_AutoTellTabs_Preload_Name,
HellionStrings.Privacy_AutoTellTabs_Preload_Description,
() => Plugin.Config.AutoTellTabsHistoryPreload, () => Plugin.Config.AutoTellTabsHistoryPreload,
v => Plugin.Config.AutoTellTabsHistoryPreload = v, v => Plugin.Config.AutoTellTabsHistoryPreload = v,
0, 0,
200 200
); );
DrawToggle( _w.ToggleRow(
"Show greeted toggle", ImGui.GetID("channels.autotell.greeted"u8),
HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Name,
HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Description,
() => Plugin.Config.AutoTellTabsShowGreetedToggle, () => Plugin.Config.AutoTellTabsShowGreetedToggle,
v => Plugin.Config.AutoTellTabsShowGreetedToggle = v v => Plugin.Config.AutoTellTabsShowGreetedToggle = v
); );
DrawToggle( _w.ToggleRow(
"Open as popout", ImGui.GetID("channels.autotell.popout"u8),
HellionStrings.ChatLog_AutoTellTabs_OpenAsPopout_Name,
HellionStrings.ChatLog_AutoTellTabs_OpenAsPopout_Description,
() => Plugin.Config.AutoTellTabsOpenAsPopout, () => Plugin.Config.AutoTellTabsOpenAsPopout,
v => Plugin.Config.AutoTellTabsOpenAsPopout = v v => Plugin.Config.AutoTellTabsOpenAsPopout = v
); );
// Written for this screen and never shown until this cycle. It names
// the one setting in a third-party plugin that silently stops
// auto-tell tabs from ever opening, which is not something a user
// works out alone.
ImGui.Spacing();
ImGui.TextWrapped(HellionStrings.ChatLog_AutoTellTabs_ConflictHint);
} }
if (ImGui.CollapsingHeader("Tell auto-open mode", ImGuiTreeNodeFlags.DefaultOpen)) if (
_w.Section(
ImGui.GetID("channels.autoopen"u8),
HellionStrings.Settings_Section_TellAutoOpen
)
)
{ {
DrawTellAutoOpenModeCombo(); _w.EnumComboRow(
DrawToggle( ImGui.GetID("channels.autoopen.mode"u8),
"Switch to the tab on every tell", HellionStrings.Settings_Channels_TellAutoOpenMode_Name,
HellionStrings.Settings_Channels_TellAutoOpenMode_Description,
() => Plugin.Config.TellAutoOpenMode,
v => Plugin.Config.TellAutoOpenMode = v,
v => v.Name()
);
_w.ToggleRow(
ImGui.GetID("channels.autoopen.switch"u8),
HellionStrings.Settings_Channels_TellSwitchAlways_Name,
HellionStrings.Settings_Channels_TellSwitchAlways_Description,
() => Plugin.Config.TellAutoOpenSwitchAlways, () => Plugin.Config.TellAutoOpenSwitchAlways,
v => Plugin.Config.TellAutoOpenSwitchAlways = v v => Plugin.Config.TellAutoOpenSwitchAlways = v
); );
} }
if (ImGui.CollapsingHeader("Sidebar")) if (
_w.Section(
ImGui.GetID("channels.sidebar"u8),
HellionStrings.Settings_Section_Sidebar,
open: false
)
)
{ {
// Range matches Sidebar.MinSidebarWidth/MaxSidebarWidth (40-300). The // Bounds come from the constants rather than repeating the numbers,
// lower bound sits just above the 38px icon-only threshold; the // so the slider cannot drift away from the clamp in Sidebar.GetWidth.
// on-disk default (44) and the 150px expanded reference both fit. // The stored value is unscaled; display scaling is applied where the
DrawSliderInt( // sidebar is drawn.
"Sidebar width", _w.SliderIntRow(
ImGui.GetID("channels.sidebar.width"u8),
HellionStrings.Settings_ThemeAndLayout_SidebarWidth_Name,
HellionStrings.Settings_ThemeAndLayout_SidebarWidth_Description,
() => Plugin.Config.SidebarWidth, () => Plugin.Config.SidebarWidth,
v => Plugin.Config.SidebarWidth = v, v => Plugin.Config.SidebarWidth = v,
40, (int)Sidebar.MinSidebarWidth,
300 (int)Sidebar.MaxSidebarWidth
); );
} }
} }
private void DrawTellAutoOpenModeCombo()
{
var labels = new[] { "Off", "Sidebar", "Top tab", "Popout" };
var values = Enum.GetValues<TellAutoOpenMode>();
var current = Plugin.Config.TellAutoOpenMode;
var selected = 0;
for (var i = 0; i < values.Length; i++)
{
if (values[i] == current)
{
selected = i;
break;
}
}
ImGui.SetNextItemWidth(220);
if (ImGui.Combo("Tell auto-open mode", ref selected, labels, labels.Length))
{
if (selected >= 0 && selected < values.Length)
{
Plugin.Config.TellAutoOpenMode = values[selected];
_plugin.SaveConfig();
}
}
}
private void DrawToggle(string label, Func<bool> get, Action<bool> set)
{
var current = get();
if (ImGui.Checkbox(label, ref current))
{
set(current);
_plugin.SaveConfig();
}
}
private void DrawSliderInt(string label, Func<int> get, Action<int> set, int min, int max)
{
var current = get();
ImGui.SetNextItemWidth(200);
if (ImGui.SliderInt(label, ref current, min, max, "%d"))
{
set(current);
_plugin.SaveConfig();
}
}
} }
+163 -142
View File
@@ -1,6 +1,9 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using HellionChat.Code; using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources; using HellionChat.Resources;
using HellionChat.Ui.StyleEngine;
using HellionChat.Util; using HellionChat.Util;
namespace HellionChat.Ui.Components.Settings.Tabs; namespace HellionChat.Ui.Components.Settings.Tabs;
@@ -8,187 +11,205 @@ namespace HellionChat.Ui.Components.Settings.Tabs;
internal sealed class ChatTab internal sealed class ChatTab
{ {
private readonly Plugin _plugin; private readonly Plugin _plugin;
private readonly SettingsWidgets _w;
public ChatTab(Plugin plugin) private string _blockedEmoteInput = string.Empty;
public ChatTab(Plugin plugin, TokenResolver resolver)
{ {
_plugin = plugin; _plugin = plugin;
_w = new SettingsWidgets(plugin, new SettingsPalette(resolver));
} }
public void Draw() public void Draw()
{ {
if (ImGui.CollapsingHeader("Display modes", ImGuiTreeNodeFlags.DefaultOpen)) if (_w.Section(ImGui.GetID("chat.display"u8), HellionStrings.Settings_Section_DisplayModes))
{ {
DrawToggle( _w.ToggleRow(
"Compact density (card vs compact)", ImGui.GetID("chat.display.density"u8),
HellionStrings.Appearance_UseCompactDensity_Name,
HellionStrings.Appearance_UseCompactDensity_Description,
() => Plugin.Config.UseCompactDensity, () => Plugin.Config.UseCompactDensity,
v => Plugin.Config.UseCompactDensity = v v => Plugin.Config.UseCompactDensity = v
); );
DrawToggle( _w.ToggleRow(
"More compact pretty mode", ImGui.GetID("chat.display.clock"u8),
() => Plugin.Config.MoreCompactPretty, HellionStrings.Settings_Chat_Clock24_Name,
v => Plugin.Config.MoreCompactPretty = v null,
);
DrawToggle(
"Prettier timestamps",
() => Plugin.Config.PrettierTimestamps,
v => Plugin.Config.PrettierTimestamps = v
);
DrawToggle(
"Hide same timestamps",
() => Plugin.Config.HideSameTimestamps,
v => Plugin.Config.HideSameTimestamps = v
);
DrawToggle(
"24-hour clock",
() => Plugin.Config.Use24HourClock, () => Plugin.Config.Use24HourClock,
v => Plugin.Config.Use24HourClock = v v => Plugin.Config.Use24HourClock = v
); );
DrawWorldSuffixCombo();
DrawNameFormCombo();
}
if (ImGui.CollapsingHeader("Channel filter")) // Descriptions move out of the help markers and onto the row. They
{ // were written to be read; a (?) the user has to hover is where an
DrawToggle( // explanation goes to be ignored.
"Privacy filter enabled", _w.EnumComboRow(
() => Plugin.Config.PrivacyFilterEnabled, ImGui.GetID("chat.display.worldsuffix"u8),
v => Plugin.Config.PrivacyFilterEnabled = v HellionStrings.Settings_Chat_WorldSuffix_Name,
HellionStrings.Settings_Chat_WorldSuffix_Description,
() => Plugin.Config.WorldSuffixMode,
v => Plugin.Config.WorldSuffixMode = v,
v => v.Name()
);
_w.EnumComboRow(
ImGui.GetID("chat.display.nameform"u8),
HellionStrings.Settings_Chat_NameForm_Name,
HellionStrings.Settings_Chat_NameForm_Description,
() => Plugin.Config.NameFormMode,
v => Plugin.Config.NameFormMode = v,
v => v.Name()
); );
DrawPrivacyPersistChannels();
} }
if (ImGui.CollapsingHeader("Command help")) if (_w.Section(ImGui.GetID("chat.history"u8), HellionStrings.Settings_Section_History))
{ {
DrawCommandHelpSideCombo(); // The one setting that decides whether the chat log shows anything
// from before this game session. It had no control at all: only the
// first-run wizard ever wrote it, and only if the user actually
// reached step 3 -- skip the wizard and it stays on its default of
// false, leaving the log empty on every launch with no way to fix it.
//
// Named for what it does. The stored name describes filtering, and
// the wizard's own "Load previous session on startup" checkbox
// writes a field nothing reads.
_w.ToggleRow(
ImGui.GetID("chat.history.previoussessions"u8),
HellionStrings.Settings_Chat_PreviousSessions_Name,
HellionStrings.Settings_Chat_PreviousSessions_Description,
() => Plugin.Config.FilterIncludePreviousSessions,
v =>
{
Plugin.Config.FilterIncludePreviousSessions = v;
// Takes effect at once rather than on the next launch: the
// window is open and the user just asked for the history.
_plugin.MessageManager.FilterAllTabs();
}
);
} }
if (ImGui.CollapsingHeader("Plugin disclosure")) if (
_w.Section(
ImGui.GetID("chat.commandhelp"u8),
HellionStrings.Settings_Section_CommandHelp,
open: false
)
)
{ {
DrawToggle( _w.EnumComboRow(
ImGui.GetID("chat.commandhelp.side"u8),
HellionStrings.Settings_Chat_CommandHelpSide_Name,
HellionStrings.Settings_Chat_CommandHelpSide_Description,
() => Plugin.Config.CommandHelpSide,
v => Plugin.Config.CommandHelpSide = v,
v => v.Name()
);
}
if (
_w.Section(
ImGui.GetID("chat.disclosure"u8),
HellionStrings.Settings_Section_PluginDisclosure,
open: false
)
)
{
_w.ToggleRow(
ImGui.GetID("chat.disclosure.notify"u8),
HellionStrings.Settings_Chat_NotifyPluginDisclosure_Name, HellionStrings.Settings_Chat_NotifyPluginDisclosure_Name,
HellionStrings.Settings_Chat_NotifyPluginDisclosure_Description,
() => Plugin.Config.NotifyPluginDisclosure, () => Plugin.Config.NotifyPluginDisclosure,
v => Plugin.Config.NotifyPluginDisclosure = v v => Plugin.Config.NotifyPluginDisclosure = v
); );
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_NotifyPluginDisclosure_Description); }
if (
_w.Section(
ImGui.GetID("chat.emotes"u8),
HellionStrings.Settings_Section_Emotes,
open: false
)
)
{
DrawEmotes();
}
if (
_w.Section(
ImGui.GetID("chat.autotranslate"u8),
HellionStrings.Settings_Section_AutoTranslate,
open: false
)
)
{
_w.ToggleRow(
ImGui.GetID("chat.autotranslate.sort"u8),
Language.Options_SortAutoTranslate_Name,
Language.Options_SortAutoTranslate_Description,
() => Plugin.Config.SortAutoTranslate,
v => Plugin.Config.SortAutoTranslate = v
);
} }
} }
private void DrawPrivacyPersistChannels() // PRIVACY.md names the switch below as the way to stop the one outbound
// call the plugin makes, and it had no control at all since May. A
// documented opt-out that lives only in the JSON is not an opt-out.
private void DrawEmotes()
{ {
// Enum.GetValues gives a stable order; HashSet membership is the source _w.ToggleRow(
// of truth, so we toggle via Add/Remove instead of mutating a copy. ImGui.GetID("chat.emotes.show"u8),
ImGui.TextUnformatted("Persist channels:"); Language.Options_ShowEmotes_Name,
foreach (var ct in Enum.GetValues<ChatType>()) Language.Options_ShowEmotes_Desc,
() => Plugin.Config.ShowEmotes,
v => Plugin.Config.ShowEmotes = v
);
if (!Plugin.Config.ShowEmotes)
return;
ImGui.Spacing();
ImGui.TextUnformatted(
EmoteCache.State == EmoteCache.LoadingState.Done
? $"{Language.Options_Emote_Loaded} {EmoteCache.SortedCodeArray.Length:N0} ({Language.Options_Emote_Ready})"
: $"{Language.Options_Emote_Loaded} {Language.Options_Emote_NotReady}"
);
ImGui.Spacing();
ImGui.TextUnformatted(Language.Options_Emote_BlockedEmotes);
// A blocked code stops that one emote from ever being drawn or
// downloaded, which is a finer instrument than switching emotes off
// wholesale. The list has been in the config since v1.0 with nowhere to
// edit it.
ImGui.SetNextItemWidth(220f * ImGuiHelpers.GlobalScale);
ImGui.InputText("##hc-blocked-emote", ref _blockedEmoteInput, 64);
ImGui.SameLine();
var candidate = _blockedEmoteInput.Trim();
using (ImRaii.Disabled(candidate.Length == 0))
{ {
var label = ct.ToString(); if (ImGui.Button(HellionStrings.Settings_Emotes_Block))
var present = Plugin.Config.PrivacyPersistChannels.Contains(ct);
if (ImGui.Checkbox($"{label}##persist-{label}", ref present))
{ {
if (present) lock (_plugin.ConfigMapsLock)
{ Plugin.Config.BlockedEmotes.Add(candidate);
Plugin.Config.PrivacyPersistChannels.Add(ct); _blockedEmoteInput = string.Empty;
}
else
{
Plugin.Config.PrivacyPersistChannels.Remove(ct);
}
_plugin.SaveConfig(); _plugin.SaveConfig();
} }
} }
}
private void DrawCommandHelpSideCombo() // Snapshot: the remove button mutates the set inside the loop.
{ foreach (var blocked in Plugin.Config.BlockedEmotes.OrderBy(e => e).ToList())
var current = Plugin.Config.CommandHelpSide;
var values = Enum.GetValues<CommandHelpSide>();
var labels = new string[values.Length];
var selected = 0;
for (var i = 0; i < values.Length; i++)
{ {
labels[i] = values[i].Name(); using var id = ImRaii.PushId(blocked);
if (values[i] == current) if (ImGuiUtil.IconButton(FontAwesomeIcon.Trash, "##remove"))
{ {
selected = i; lock (_plugin.ConfigMapsLock)
Plugin.Config.BlockedEmotes.Remove(blocked);
_plugin.SaveConfig();
} }
}
ImGui.SetNextItemWidth(200); ImGui.SameLine();
if (ImGui.Combo("Command help side", ref selected, labels, labels.Length)) ImGui.TextUnformatted(blocked);
{
Plugin.Config.CommandHelpSide = values[selected];
_plugin.SaveConfig();
}
}
private void DrawWorldSuffixCombo()
{
var current = Plugin.Config.WorldSuffixMode;
var values = Enum.GetValues<WorldSuffixMode>();
var labels = new string[values.Length];
var selected = 0;
for (var i = 0; i < values.Length; i++)
{
labels[i] = values[i].Name();
if (values[i] == current)
{
selected = i;
}
}
ImGui.SetNextItemWidth(200);
if (
ImGui.Combo(
HellionStrings.Settings_Chat_WorldSuffix_Name,
ref selected,
labels,
labels.Length
)
)
{
Plugin.Config.WorldSuffixMode = values[selected];
_plugin.SaveConfig();
}
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_WorldSuffix_Description);
}
private void DrawNameFormCombo()
{
var current = Plugin.Config.NameFormMode;
var values = Enum.GetValues<NameFormMode>();
var labels = new string[values.Length];
var selected = 0;
for (var i = 0; i < values.Length; i++)
{
labels[i] = values[i].Name();
if (values[i] == current)
{
selected = i;
}
}
ImGui.SetNextItemWidth(200);
if (
ImGui.Combo(
HellionStrings.Settings_Chat_NameForm_Name,
ref selected,
labels,
labels.Length
)
)
{
Plugin.Config.NameFormMode = values[selected];
_plugin.SaveConfig();
}
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_NameForm_Description);
}
private void DrawToggle(string label, Func<bool> get, Action<bool> set)
{
var current = get();
if (ImGui.Checkbox(label, ref current))
{
set(current);
_plugin.SaveConfig();
} }
} }
} }
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,6 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using HellionChat.Resources;
using HellionChat.Ui.StyleEngine;
using HellionChat.Util; using HellionChat.Util;
namespace HellionChat.Ui.Components.Settings.Tabs; namespace HellionChat.Ui.Components.Settings.Tabs;
@@ -6,58 +8,95 @@ namespace HellionChat.Ui.Components.Settings.Tabs;
internal sealed class GeneralTab internal sealed class GeneralTab
{ {
private readonly Plugin _plugin; private readonly Plugin _plugin;
private readonly SettingsWidgets _w;
private readonly FontManager _fonts;
public GeneralTab(Plugin plugin) // Sorted once: the 25 endonyms are fixed literals, so the order never
// changes, and rebuilding it per frame costs an Enum.GetValues plus the
// whole LINQ chain. None is pinned to the front rather than sorted, so its
// localised label never affects the ordering.
private static readonly LanguageOverride[] LanguageOrder = BuildLanguageOrder();
public GeneralTab(Plugin plugin, FontManager fonts, TokenResolver resolver)
{ {
_plugin = plugin; _plugin = plugin;
_w = new SettingsWidgets(plugin, new SettingsPalette(resolver));
_fonts = fonts;
}
private static LanguageOverride[] BuildLanguageOrder()
{
var all = Enum.GetValues<LanguageOverride>()
.Where(l => l != LanguageOverride.None)
.OrderBy(l => l.Name(), StringComparer.CurrentCulture);
return new[] { LanguageOverride.None }.Concat(all).ToArray();
} }
public void Draw() public void Draw()
{ {
if (ImGui.CollapsingHeader("Behavior", ImGuiTreeNodeFlags.DefaultOpen)) if (
_w.Section(
ImGui.GetID("general.behaviour"u8),
HellionStrings.Settings_Section_Behaviour
)
)
{ {
DrawToggle( _w.ToggleRow(
"Reduce motion (no theme crossfade)", ImGui.GetID("general.behaviour.reducemotion"u8),
HellionStrings.Settings_ThemeAndLayout_ReduceMotion_Name,
HellionStrings.Settings_ThemeAndLayout_ReduceMotion_Description,
() => Plugin.Config.ReduceMotion, () => Plugin.Config.ReduceMotion,
v => Plugin.Config.ReduceMotion = v v => Plugin.Config.ReduceMotion = v
); );
DrawToggle( DrawLanguagePicker();
"Print changelog on update",
() => Plugin.Config.PrintChangelog,
v => Plugin.Config.PrintChangelog = v
);
} }
if (ImGui.CollapsingHeader("Keybinds", ImGuiTreeNodeFlags.DefaultOpen)) if (_w.Section(ImGui.GetID("general.keybinds"u8), HellionStrings.Settings_Section_Keybinds))
{ {
ImGui.TextDisabled("Click a button, then press the key combination. Esc clears."); ImGui.TextDisabled(HellionStrings.Settings_Keybinds_Hint);
DrawKeybind( DrawKeybind(
"Cycle to next chat tab", HellionStrings.Settings_Keybinds_CycleNext,
"ChatTabForwardKeybind", "ChatTabForwardKeybind",
() => Plugin.Config.ChatTabForward, () => Plugin.Config.ChatTabForward,
v => Plugin.Config.ChatTabForward = v v => Plugin.Config.ChatTabForward = v
); );
DrawKeybind( DrawKeybind(
"Cycle to previous chat tab", HellionStrings.Settings_Keybinds_CyclePrevious,
"ChatTabBackwardKeybind", "ChatTabBackwardKeybind",
() => Plugin.Config.ChatTabBackward, () => Plugin.Config.ChatTabBackward,
v => Plugin.Config.ChatTabBackward = v v => Plugin.Config.ChatTabBackward = v
); );
DrawKeybindModePicker();
} }
if (ImGui.CollapsingHeader("Notifications", ImGuiTreeNodeFlags.DefaultOpen)) if (
_w.Section(
ImGui.GetID("general.notifications"u8),
HellionStrings.Settings_Section_Notifications
)
)
{ {
DrawToggle( // The game reports a failed tell in the log only, where a player who
"Show novice network", // is typing does not see it. On by default and never reachable.
() => Plugin.Config.ShowNoviceNetwork, _w.ToggleRow(
v => Plugin.Config.ShowNoviceNetwork = v ImGui.GetID("general.notifications.failedtell"u8),
HellionStrings.Settings_Chat_NotifyFailedTell_Name,
HellionStrings.Settings_Chat_NotifyFailedTell_Description,
() => Plugin.Config.NotifyFailedTell,
v => Plugin.Config.NotifyFailedTell = v
); );
}
if (ImGui.CollapsingHeader("Volumes", ImGuiTreeNodeFlags.DefaultOpen)) _w.ToggleRow(
{ ImGui.GetID("general.notifications.sounds"u8),
DrawSlider( Language.Options_PlaySounds_Name,
"Custom sound volume", Language.Options_PlaySounds_Description,
() => Plugin.Config.PlaySounds,
v => Plugin.Config.PlaySounds = v
);
_w.SliderFloatRow(
ImGui.GetID("general.notifications.volume"u8),
HellionStrings.Settings_General_CustomSoundVolume_Name,
null,
() => Plugin.Config.CustomSoundVolume, () => Plugin.Config.CustomSoundVolume,
v => Plugin.Config.CustomSoundVolume = v, v => Plugin.Config.CustomSoundVolume = v,
0f, 0f,
@@ -66,25 +105,96 @@ internal sealed class GeneralTab
} }
} }
private void DrawToggle(string label, Func<bool> get, Action<bool> set) // Four steps, all of them required. The glyph-range activation used to live
// in Settings.Apply, which has not existed since the v1.6.0 rewrite -- see
// the comment at Plugin.cs:290. Without steps 2 and 4 a switch to Korean
// renders empty boxes until the plugin reloads.
private void DrawLanguagePicker()
{ {
var current = get(); var current = Plugin.Config.LanguageOverride;
if (ImGui.Checkbox(label, ref current)) var selected = Array.IndexOf(LanguageOrder, current);
{ var labels = new string[LanguageOrder.Length];
set(current); for (var i = 0; i < LanguageOrder.Length; i++)
_plugin.SaveConfig(); labels[i] = LanguageOrder[i].Name();
}
_w.Row(
ImGui.GetID("general.behaviour.language"u8),
Language.Options_Language_Name,
HellionStrings.Settings_General_Language_Description,
ctx =>
{
ImGui.SetNextItemWidth(ctx.ControlWidth);
if (ImGui.Combo("##hc-language", ref selected, labels, labels.Length))
{
if (selected >= 0 && selected < LanguageOrder.Length)
ApplyLanguage(LanguageOrder[selected]);
}
}
);
} }
private void DrawSlider(string label, Func<float> get, Action<float> set, float min, float max) private void ApplyLanguage(LanguageOverride picked)
{ {
var current = get(); // Combo only reports real changes, but a rebuild is expensive enough
ImGui.SetNextItemWidth(200); // that a future caller should not be able to trigger a no-op one.
if (ImGui.SliderFloat(label, ref current, min, max, "%.2f")) if (picked == Plugin.Config.LanguageOverride)
{ return;
set(current);
_plugin.SaveConfig(); Plugin.Config.LanguageOverride = picked;
}
var required = picked.RequiredGlyphRanges();
if (required != 0 && !Plugin.Config.ExtraGlyphRanges.HasFlag(required))
Plugin.Config.ExtraGlyphRanges |= required;
_plugin.SaveConfig();
// Instance method, and the argument only matters when the override is
// None: LanguageChanged reads the config itself and falls back to the
// parameter only in that case. Passing picked.Code() there yields "",
// so "follow Dalamud" would silently mean English.
_plugin.LanguageChanged(Plugin.Interface.UiLanguage);
// Reads Config.ExtraGlyphRanges via SetUpRanges, so it has to come after
// the OR above.
//
// This runs inside Plugin.Draw's open font push (Plugin.cs:1105) and
// disposes the very handle that is on the ImGui font stack. It is safe
// because Dalamud keeps the ImFont alive under a per-frame lock until
// the frame ends -- not merely because we are on the draw thread, which
// is what the old comment claimed.
//
// The rebuild is asynchronous: FontsReady goes false until the atlas is
// done, and every chat component returns early meanwhile. For CJK that
// is visible as a blank chat window for a moment.
_fonts.RebuildDelegateFonts();
}
private void DrawKeybindModePicker()
{
var values = Enum.GetValues<KeybindMode>();
var current = Plugin.Config.KeybindMode;
var selected = Array.IndexOf(values, current);
var labels = new string[values.Length];
for (var i = 0; i < values.Length; i++)
labels[i] = values[i].Name();
_w.Row(
ImGui.GetID("general.keybinds.mode"u8),
Language.Options_KeybindMode_Name,
Plugin.Config.KeybindMode.Tooltip(),
ctx =>
{
ImGui.SetNextItemWidth(ctx.ControlWidth);
if (ImGui.Combo("##hc-keybindmode", ref selected, labels, labels.Length))
{
if (selected >= 0 && selected < values.Length)
{
Plugin.Config.KeybindMode = values[selected];
_plugin.SaveConfig();
}
}
}
);
} }
// Wires the already-present ImGuiUtil.KeybindInput capture widget (dead/unwired // Wires the already-present ImGuiUtil.KeybindInput capture widget (dead/unwired
@@ -1,63 +1,100 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using HellionChat.Resources;
using HellionChat.Ui.StyleEngine;
namespace HellionChat.Ui.Components.Settings.Tabs; namespace HellionChat.Ui.Components.Settings.Tabs;
// First tab converted to the styled widgets. Everything here used to be stock
// ImGui: framed collapsing bars, checkboxes with their label on the right, and
// sliders glued to the left edge with their name trailing behind.
internal sealed class WindowTab internal sealed class WindowTab
{ {
private readonly Plugin _plugin; private readonly SettingsWidgets _w;
public WindowTab(Plugin plugin) // Held rather than built per frame, and ordered to match the enum values
// passed alongside them.
private static readonly MainWindowLayoutMode[] LayoutValues =
[
MainWindowLayoutMode.Sidebar,
MainWindowLayoutMode.TopTabs,
];
// Built per call rather than cached: a runtime language switch has to reach
// these, and a static array would keep whichever language the plugin
// happened to start in.
private static string[] LayoutLabels =>
[
HellionStrings.Settings_Window_LayoutSidebar,
HellionStrings.Settings_Window_LayoutTopTabs,
];
public WindowTab(Plugin plugin, TokenResolver resolver)
{ {
_plugin = plugin; _w = new SettingsWidgets(plugin, new SettingsPalette(resolver));
} }
public void Draw() public void Draw()
{ {
if (ImGui.CollapsingHeader("Layout mode", ImGuiTreeNodeFlags.DefaultOpen)) // ASCII literals, not the visible titles: the keys have to survive a
// language change, and u8 literals cost no allocation.
if (_w.Section(ImGui.GetID("window.layout"u8), HellionStrings.Settings_Section_LayoutMode))
{ {
var mode = Plugin.Config.MainWindowLayoutMode; _w.SegmentRow(
if (ImGui.RadioButton("Sidebar", mode == MainWindowLayoutMode.Sidebar)) ImGui.GetID("window.layout.mode"u8),
{ HellionStrings.Settings_Window_TabPlacement_Name,
Plugin.Config.MainWindowLayoutMode = MainWindowLayoutMode.Sidebar; HellionStrings.Settings_Window_TabPlacement_Description,
_plugin.SaveConfig(); LayoutValues,
} LayoutLabels,
if (ImGui.RadioButton("Top tabs", mode == MainWindowLayoutMode.TopTabs)) () => Plugin.Config.MainWindowLayoutMode,
{ v => Plugin.Config.MainWindowLayoutMode = v
Plugin.Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs; );
_plugin.SaveConfig();
}
} }
if (ImGui.CollapsingHeader("Window style", ImGuiTreeNodeFlags.DefaultOpen)) if (
_w.Section(
ImGui.GetID("window.style"u8),
HellionStrings.Settings_ThemeAndLayout_WindowStyle_Heading
)
)
{ {
DrawToggle( _w.ToggleRow(
"Show title bar", ImGui.GetID("window.style.titlebar"u8),
HellionStrings.Settings_Window_TitleBar_Name,
null,
() => Plugin.Config.ShowTitleBar, () => Plugin.Config.ShowTitleBar,
v => Plugin.Config.ShowTitleBar = v v => Plugin.Config.ShowTitleBar = v
); );
DrawToggle( _w.ToggleRow(
"Show title bar for pop-outs", ImGui.GetID("window.style.popouttitlebar"u8),
HellionStrings.Settings_Window_PopoutTitleBar_Name,
null,
() => Plugin.Config.ShowPopOutTitleBar, () => Plugin.Config.ShowPopOutTitleBar,
v => Plugin.Config.ShowPopOutTitleBar = v v => Plugin.Config.ShowPopOutTitleBar = v
); );
DrawToggle( _w.ToggleRow(
"Show hide button", ImGui.GetID("window.style.hidebutton"u8),
Language.Options_ShowHideButton_Name,
null,
() => Plugin.Config.ShowHideButton, () => Plugin.Config.ShowHideButton,
v => Plugin.Config.ShowHideButton = v v => Plugin.Config.ShowHideButton = v
); );
} }
if (ImGui.CollapsingHeader("Opacity", ImGuiTreeNodeFlags.DefaultOpen)) if (_w.Section(ImGui.GetID("window.opacity"u8), HellionStrings.Settings_Section_Opacity))
{ {
DrawSlider( _w.SliderFloatRow(
"Window opacity", ImGui.GetID("window.opacity.active"u8),
HellionStrings.Theme_WindowOpacity_Label,
null,
() => Plugin.Config.WindowOpacity, () => Plugin.Config.WindowOpacity,
v => Plugin.Config.WindowOpacity = v, v => Plugin.Config.WindowOpacity = v,
0.1f, 0.1f,
1f 1f
); );
DrawSlider( _w.SliderFloatRow(
"Inactive opacity", ImGui.GetID("window.opacity.inactive"u8),
HellionStrings.Settings_ThemeAndLayout_WindowOpacityInactive_Name,
HellionStrings.Settings_ThemeAndLayout_WindowOpacityInactive_Description,
() => Plugin.Config.WindowOpacityInactive, () => Plugin.Config.WindowOpacityInactive,
v => Plugin.Config.WindowOpacityInactive = v, v => Plugin.Config.WindowOpacityInactive = v,
0.1f, 0.1f,
@@ -65,20 +102,31 @@ internal sealed class WindowTab
); );
} }
if (ImGui.CollapsingHeader("Resize behavior", ImGuiTreeNodeFlags.DefaultOpen)) if (
_w.Section(
ImGui.GetID("window.resize"u8),
HellionStrings.Settings_Section_ResizeBehaviour
)
)
{ {
DrawToggle( _w.ToggleRow(
"Allow movement", ImGui.GetID("window.resize.move"u8),
HellionStrings.Settings_Window_AllowMove_Name,
null,
() => Plugin.Config.CanMove, () => Plugin.Config.CanMove,
v => Plugin.Config.CanMove = v v => Plugin.Config.CanMove = v
); );
DrawToggle( _w.ToggleRow(
"Allow resize", ImGui.GetID("window.resize.resize"u8),
HellionStrings.Settings_Window_AllowResize_Name,
null,
() => Plugin.Config.CanResize, () => Plugin.Config.CanResize,
v => Plugin.Config.CanResize = v v => Plugin.Config.CanResize = v
); );
DrawSliderInt( _w.SliderIntRow(
"Sidebar auto-switch threshold (px)", ImGui.GetID("window.resize.threshold"u8),
HellionStrings.Settings_Window_SidebarThreshold_Name,
HellionStrings.Settings_Window_SidebarThreshold_Description,
() => Plugin.Config.SidebarAutoSwitchThresholdPx, () => Plugin.Config.SidebarAutoSwitchThresholdPx,
v => Plugin.Config.SidebarAutoSwitchThresholdPx = v, v => Plugin.Config.SidebarAutoSwitchThresholdPx = v,
200, 200,
@@ -86,69 +134,128 @@ internal sealed class WindowTab
); );
} }
if (ImGui.CollapsingHeader("Input preview")) if (
_w.Section(
ImGui.GetID("window.preview"u8),
HellionStrings.Settings_Section_InputPreview,
open: false
)
)
{ {
DrawPreviewPositionCombo(); _w.EnumComboRow(
DrawToggle( ImGui.GetID("window.preview.position"u8),
"Only show preview when typing", HellionStrings.Settings_Window_PreviewPosition_Name,
null,
() => Plugin.Config.PreviewPosition,
v => Plugin.Config.PreviewPosition = v,
v => v.Name()
);
_w.ToggleRow(
ImGui.GetID("window.preview.onlyif"u8),
HellionStrings.Settings_Window_PreviewOnlyTyping_Name,
null,
() => Plugin.Config.OnlyPreviewIf, () => Plugin.Config.OnlyPreviewIf,
v => Plugin.Config.OnlyPreviewIf = v v => Plugin.Config.OnlyPreviewIf = v
); );
}
}
private void DrawPreviewPositionCombo() // Partner of the toggle above and useless without it: how many
{ // characters have to be typed before the preview appears.
var current = Plugin.Config.PreviewPosition; _w.SliderIntRow(
var values = Enum.GetValues<PreviewPosition>(); ImGui.GetID("window.preview.minimum"u8),
var labels = new string[values.Length]; Language.Options_PreviewMinimum_Name,
var selected = 0; Language.Options_PreviewMinimum_Description,
for (var i = 0; i < values.Length; i++) () => Plugin.Config.PreviewMinimum,
{ v => Plugin.Config.PreviewMinimum = v,
labels[i] = values[i].Name(); 0,
if (values[i] == current) 20
{ );
selected = i;
}
} }
ImGui.SetNextItemWidth(200); // HideChat is the one with real bite: it defaults to on, it suppresses
if (ImGui.Combo("Preview position", ref selected, labels, labels.Length)) // the game's own chat window, and the only other way to it is a
// right-click item that can turn it on and never off. Somebody who used
// that item once had to edit JSON to get their chat back.
if (_w.Section(ImGui.GetID("window.hide"u8), HellionStrings.Settings_Section_Hide))
{ {
Plugin.Config.PreviewPosition = values[selected]; _w.ToggleRow(
_plugin.SaveConfig(); ImGui.GetID("window.hide.chat"u8),
} Language.Options_HideChat_Name,
} Language.Options_HideChat_Description,
() => Plugin.Config.HideChat,
v => Plugin.Config.HideChat = v
);
_w.ToggleRow(
ImGui.GetID("window.hide.uihidden"u8),
Language.Options_HideWhenUiHidden_Name,
Language.Options_HideWhenUiHidden_Description,
() => Plugin.Config.HideWhenUiHidden,
v => Plugin.Config.HideWhenUiHidden = v
);
_w.ToggleRow(
ImGui.GetID("window.hide.loading"u8),
Language.Options_HideInLoadingScreens_Name,
Language.Options_HideInLoadingScreens_Description,
() => Plugin.Config.HideInLoadingScreens,
v => Plugin.Config.HideInLoadingScreens = v
);
_w.ToggleRow(
ImGui.GetID("window.hide.newgameplus"u8),
Language.Options_HideInNewGamePlusMenu_Name,
Language.Options_HideInNewGamePlusMenu_Description,
() => Plugin.Config.HideInNewGamePlusMenu,
v => Plugin.Config.HideInNewGamePlusMenu = v
);
private void DrawToggle(string label, Func<bool> get, Action<bool> set) // The three that lost their reader with the chat window. Pressing
{ // the chat key during a cutscene brings the chat back for that
var current = get(); // cutscene; combat has no such escape, exactly as in v1.5.6.
if (ImGui.Checkbox(label, ref current)) _w.ToggleRow(
{ ImGui.GetID("window.hide.cutscenes"u8),
set(current); Language.Options_HideDuringCutscenes_Name,
_plugin.SaveConfig(); Language.Options_HideDuringCutscenes_Description,
() => Plugin.Config.HideDuringCutscenes,
v => Plugin.Config.HideDuringCutscenes = v
);
_w.ToggleRow(
ImGui.GetID("window.hide.battle"u8),
Language.Options_HideInBattle_Name,
Language.Options_HideInBattle_Description,
() => Plugin.Config.HideInBattle,
v => Plugin.Config.HideInBattle = v
);
_w.ToggleRow(
ImGui.GetID("window.hide.notloggedin"u8),
Language.Options_HideWhenNotLoggedIn_Name,
Language.Options_HideWhenNotLoggedIn_Description,
() => Plugin.Config.HideWhenNotLoggedIn,
v => Plugin.Config.HideWhenNotLoggedIn = v
);
} }
}
private void DrawSlider(string label, Func<float> get, Action<float> set, float min, float max) if (
{ _w.Section(
var current = get(); ImGui.GetID("window.tooltips"u8),
ImGui.SetNextItemWidth(200); HellionStrings.Settings_Section_LinksTooltips,
if (ImGui.SliderFloat(label, ref current, min, max, "%.2f")) open: false
)
)
{ {
set(current); _w.ToggleRow(
_plugin.SaveConfig(); ImGui.GetID("window.tooltips.native"u8),
} Language.Options_NativeItemTooltips_Name,
} Language.Options_NativeItemTooltips_Description,
() => Plugin.Config.NativeItemTooltips,
private void DrawSliderInt(string label, Func<int> get, Action<int> set, int min, int max) v => Plugin.Config.NativeItemTooltips = v
{ );
var current = get(); _w.SliderIntRow(
ImGui.SetNextItemWidth(200); ImGui.GetID("window.tooltips.offset"u8),
if (ImGui.SliderInt(label, ref current, min, max, "%d")) Language.Options_TooltipOffset_Name,
{ Language.Options_TooltipOffset_Desc,
set(current); () => (int)Plugin.Config.TooltipOffset,
_plugin.SaveConfig(); v => Plugin.Config.TooltipOffset = v,
0,
200
);
} }
} }
} }
@@ -1,6 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Security; using System.Security;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using HellionChat.Resources;
using HellionChat.Themes; using HellionChat.Themes;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -20,25 +21,25 @@ internal sealed class ThemeImportExportRow
public void Draw() public void Draw()
{ {
if (ImGui.Button("Fork active theme")) if (ImGui.Button(HellionStrings.Settings_Theme_ForkActive))
{ {
ForkActive(); ForkActive();
} }
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Import theme file…")) if (ImGui.Button(HellionStrings.Settings_Theme_ImportFile))
{ {
ImportFromPath(_importPath); ImportFromPath(_importPath);
} }
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Open themes folder")) if (ImGui.Button(HellionStrings.Settings_Themes_OpenFolder))
{ {
OpenThemesFolder(); OpenThemesFolder();
} }
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.Button("Export active theme…")) if (ImGui.Button(HellionStrings.Settings_Themes_ExportActive))
{ {
ExportActive(); ExportActive();
} }
@@ -46,7 +47,7 @@ internal sealed class ThemeImportExportRow
ImGui.SetNextItemWidth(-1); ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint( ImGui.InputTextWithHint(
"##theme-import-path", "##theme-import-path",
"Path to JSON file (or drag-and-drop into the folder)", HellionStrings.Settings_Theme_ImportPathHint,
ref _importPath, ref _importPath,
512 512
); );
@@ -308,7 +309,7 @@ internal sealed class ThemeImportExportRow
var defaultName = $"{theme.Slug}.json"; var defaultName = $"{theme.Slug}.json";
Plugin.FileDialogManager.SaveFileDialog( Plugin.FileDialogManager.SaveFileDialog(
"Export theme", HellionStrings.Settings_Theme_ExportDialogTitle,
".json", ".json",
defaultName, defaultName,
".json", ".json",
@@ -1,6 +1,7 @@
using System.Numerics; using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources;
using HellionChat.Themes; using HellionChat.Themes;
using HellionChat.Util; using HellionChat.Util;
@@ -8,22 +9,25 @@ namespace HellionChat.Ui.Components.Settings;
internal sealed class ThemePicker internal sealed class ThemePicker
{ {
private static readonly (string Category, string[] Slugs, bool DefaultExpanded)[] CategoryMap = // 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.
"Hellion Brand", private static (string Category, string[] Slugs, bool DefaultExpanded)[] CategoryMap =>
new[] { "hellion-arctic", "hellion-spectrum", "forge-merchantman" }, [
true (
), "Hellion Brand",
( new[] { "hellion-arctic", "hellion-spectrum", "forge-merchantman" },
"Cool", true
new[] { "night-blue", "event-horizon", "indigo-violet", "crystal-nocturne" }, ),
false (
), HellionStrings.Settings_Theme_Category_Cool,
("Natural", new[] { "mint-grove" }, false), new[] { "night-blue", "event-horizon", "indigo-violet", "crystal-nocturne" },
("Classic", new[] { "chat2-classic" }, false), 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 // T2 ThemePickerCategoryStep diffs this against ThemeRegistry.BuiltinSlugs
// to enforce coverage. Kept on the static map so the test does not pierce instance state. // to enforce coverage. Kept on the static map so the test does not pierce instance state.
@@ -32,10 +36,12 @@ internal sealed class ThemePicker
private const float CardHeight = 132f; private const float CardHeight = 132f;
private readonly ThemeRegistry _themes; private readonly ThemeRegistry _themes;
private readonly SectionRenderer _sections;
private readonly Plugin _plugin; private readonly Plugin _plugin;
public ThemePicker(ThemeRegistry themes, Plugin plugin) public ThemePicker(ThemeRegistry themes, Plugin plugin, SectionRenderer sections)
{ {
_sections = sections;
_themes = themes; _themes = themes;
_plugin = plugin; _plugin = plugin;
} }
@@ -46,12 +52,20 @@ internal sealed class ThemePicker
using (ImRaii.Disabled(locked)) using (ImRaii.Disabled(locked))
{ {
foreach (var (category, slugs, defaultExpanded) in CategoryMap) // Keyed by position, not by name. CategoryMap is a fixed literal
// list, so the index is stable, and it survives the category names
// being translated later -- which the titles themselves would not.
for (var i = 0; i < CategoryMap.Length; i++)
{ {
var flags = defaultExpanded var (category, slugs, defaultExpanded) = CategoryMap[i];
? ImGuiTreeNodeFlags.DefaultOpen if (
: ImGuiTreeNodeFlags.None; _sections.Draw(
if (ImGui.CollapsingHeader(category, flags)) ImGui.GetID($"theme.category.{i}"),
category,
open: defaultExpanded,
disabled: locked
)
)
{ {
DrawThemeGrid(Resolve(slugs)); DrawThemeGrid(Resolve(slugs));
} }
@@ -62,10 +76,14 @@ internal sealed class ThemePicker
var customs = _themes.AllCustom().ToList(); var customs = _themes.AllCustom().ToList();
if (customs.Count > 0) if (customs.Count > 0)
{ {
// Fixed key although the label carries a count: keying off the
// text would reset the section every time a theme is imported
// or deleted.
if ( if (
ImGui.CollapsingHeader( _sections.Draw(
$"Custom ({customs.Count})", ImGui.GetID("theme.category.custom"u8),
ImGuiTreeNodeFlags.DefaultOpen string.Format(HellionStrings.Settings_Theme_Custom, customs.Count),
disabled: locked
) )
) )
{ {
@@ -76,7 +94,7 @@ internal sealed class ThemePicker
if (locked && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) if (locked && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{ {
ImGui.SetTooltip("Save or discard your edits first"); ImGui.SetTooltip(HellionStrings.Settings_Theme_LockedTooltip);
} }
} }
+242 -99
View File
@@ -6,6 +6,7 @@ using HellionChat.Code;
using HellionChat.Resources; using HellionChat.Resources;
using HellionChat.Themes; using HellionChat.Themes;
using HellionChat.Ui.StyleEngine; using HellionChat.Ui.StyleEngine;
using HellionChat.Ui.StyleEngine.Widgets;
using HellionChat.Util; using HellionChat.Util;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -27,9 +28,15 @@ internal sealed class Sidebar
public const float MinSidebarWidth = 40f; public const float MinSidebarWidth = 40f;
public const float MaxSidebarWidth = 300f; public const float MaxSidebarWidth = 300f;
private const float RowHeight = 32f; private static float RowHeight => Metrics.SidebarRowHeight;
private const float PopOutHitWidth = 22f; private static float PopOutHitWidth => Metrics.SidebarPopOutHitWidth;
private const float GreetedHitWidth = 22f; private static float GreetedHitWidth => Metrics.SidebarGreetedHitWidth;
// Counts rows that got the active surface this frame. At most one, with two
// exceptions: zero when every tab is popped out (PickMainActiveTab returns
// null), and two in the frame a click lands on a row drawn after the
// previously active one -- that row was still active when it was painted.
internal int LastRenderedActiveSurfaceCount { get; private set; }
// B3-2 render observability: counts greeted glyphs actually drawn this frame. // B3-2 render observability: counts greeted glyphs actually drawn this frame.
// Incremented ONLY in the real glyph branch in DrawRow; reset at Draw start. // Incremented ONLY in the real glyph branch in DrawRow; reset at Draw start.
@@ -37,6 +44,10 @@ internal sealed class Sidebar
internal int LastRenderedGreetedGlyphCount; internal int LastRenderedGreetedGlyphCount;
internal int LastRenderedUnreadDotCount; internal int LastRenderedUnreadDotCount;
// Small enough to read as a marker on the icon rather than as a second icon
// beside it.
private const float PinGlyphScale = 0.6f;
// B3-4 render observability: section headers actually drawn this frame. // B3-4 render observability: section headers actually drawn this frame.
// Incremented only in the real header branch; reset at Draw start. // Incremented only in the real header branch; reset at Draw start.
internal int LastDrawnSectionHeaderCount; internal int LastDrawnSectionHeaderCount;
@@ -66,6 +77,7 @@ internal sealed class Sidebar
private readonly ThemeRegistry _themes; private readonly ThemeRegistry _themes;
private readonly TokenResolver _resolver; private readonly TokenResolver _resolver;
private readonly WidgetPalette _palette;
private readonly FontManager _fonts; private readonly FontManager _fonts;
private readonly ILogger<Sidebar> _logger; private readonly ILogger<Sidebar> _logger;
private readonly Windows.ChannelPopoutPool _pool; private readonly Windows.ChannelPopoutPool _pool;
@@ -80,11 +92,16 @@ internal sealed class Sidebar
{ {
_themes = themes; _themes = themes;
_resolver = resolver; _resolver = resolver;
_palette = new WidgetPalette(resolver);
_fonts = fonts; _fonts = fonts;
_logger = logger; _logger = logger;
_pool = pool; _pool = pool;
} }
// Both stay unscaled. The stored width and the switch threshold are user
// settings in design pixels, and SidebarModeAutoSwitchStep compares this
// return value against the raw bounds. Display scaling is applied once, at
// the single draw call site below.
public bool IsExpanded(float windowWidth) => public bool IsExpanded(float windowWidth) =>
windowWidth >= Plugin.Config.SidebarAutoSwitchThresholdPx; windowWidth >= Plugin.Config.SidebarAutoSwitchThresholdPx;
@@ -103,20 +120,24 @@ internal sealed class Sidebar
Plugin.Instance.AutoTellTabsService.MarkGreeted(tab); Plugin.Instance.AutoTellTabsService.MarkGreeted(tab);
} }
public void Draw(float windowWidth, IList<Tab> tabs, ref Tab? activeTab) public void Draw(float windowWidth, IReadOnlyList<Tab> tabs, ref Tab? activeTab)
{ {
LastRenderedGreetedGlyphCount = 0; LastRenderedGreetedGlyphCount = 0;
LastRenderedUnreadDotCount = 0; LastRenderedUnreadDotCount = 0;
LastDrawnSectionHeaderCount = 0; LastDrawnSectionHeaderCount = 0;
LastRenderedActiveSurfaceCount = 0;
if (!_fonts.FontsReady) if (!_fonts.FontsReady)
{ {
ImGui.Dummy(new Vector2(IconOnlyWidth, 0)); // A scale change triggers a font rebuild, so this branch is really
// hit while GlobalScale is moving -- an unscaled width here makes
// the sidebar jump.
ImGui.Dummy(new Vector2(Metrics.SidebarIconOnlyWidth, 0));
return; return;
} }
var expanded = IsExpanded(windowWidth); var expanded = IsExpanded(windowWidth);
var width = GetWidth(windowWidth); var width = GetWidth(windowWidth) * Metrics.Scale;
using var child = ImRaii.Child("##hellion-sidebar", new Vector2(width, 0)); using var child = ImRaii.Child("##hellion-sidebar", new Vector2(width, 0));
if (!child.Success) if (!child.Success)
return; return;
@@ -126,7 +147,6 @@ internal sealed class Sidebar
var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary); var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
var mutedAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextMuted); var mutedAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextMuted);
var dimAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextDim); var dimAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextDim);
var dangerAbgr = ColourUtil.RgbaToAbgr(theme.Colors.StatusDanger);
var dl = ImGui.GetWindowDrawList(); var dl = ImGui.GetWindowDrawList();
// B3-4 sectioned render order (1.5.6 parity): persistent → pinned // B3-4 sectioned render order (1.5.6 parity): persistent → pinned
@@ -134,9 +154,18 @@ internal sealed class Sidebar
// the tab list itself stays untouched and every row keeps its // the tab list itself stays untouched and every row keeps its
// ORIGINAL list index for PushID, so an open context-menu popup // ORIGINAL list index for PushID, so an open context-menu popup
// stays bound to its tab when sectioning moves it visually. // stays bound to its tab when sectioning moves it visually.
var renderOrder = BuildRenderOrder(tabs); var renderOrder = TabLifecycleHelpers.BuildRenderOrder(
tabs,
t => _pool.IsOpen(t.Identifier)
);
var pinnedHeaderRendered = false; var pinnedHeaderRendered = false;
var unpinnedHeaderRendered = false; var unpinnedHeaderRendered = false;
// Rows carry their own full-height surface now, so the default gap
// between them would read as a stripe of window background. The section
// headers are unaffected: LineDivider brings its own padding.
using var rowSpacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
foreach (var i in renderOrder) foreach (var i in renderOrder)
{ {
var tab = tabs[i]; var tab = tabs[i];
@@ -144,7 +173,7 @@ internal sealed class Sidebar
{ {
DrawSectionHeader( DrawSectionHeader(
HellionStrings.PinTab_SectionHeader, HellionStrings.PinTab_SectionHeader,
Plugin.Instance.AutoTellTabsService.PinnedTempTabCount TabLifecycleHelpers.CountPinnedPool(tabs, t => _pool.IsOpen(t.Identifier))
); );
pinnedHeaderRendered = true; pinnedHeaderRendered = true;
} }
@@ -152,78 +181,49 @@ internal sealed class Sidebar
{ {
DrawSectionHeader( DrawSectionHeader(
HellionStrings.AutoTellTabs_SectionHeader, HellionStrings.AutoTellTabs_SectionHeader,
Plugin.Instance.AutoTellTabsService.ActiveTempTabCount TabLifecycleHelpers.CountUnpinnedPool(tabs, t => _pool.IsOpen(t.Identifier))
); );
unpinnedHeaderRendered = true; unpinnedHeaderRendered = true;
} }
DrawRow( DrawRow(tab, expanded, accentRgba, textAbgr, mutedAbgr, dimAbgr, dl, ref activeTab);
tab,
i,
expanded,
accentRgba,
textAbgr,
mutedAbgr,
dimAbgr,
dangerAbgr,
dl,
ref activeTab
);
} }
} }
// Section transition marker (1.5.6 parity): the separator always renders, // Section transition marker (1.5.6 parity): the rule always renders, compact
// compact mode suppresses only the header text. Real cursor-advancing // mode suppresses only the caption. LineDivider submits its own layout item
// widgets on purpose — rows advance the cursor via InvisibleButton, so a // and carries its own padding, which is what lets the rows below sit flush
// drawlist-only header would overlap the next row. // without the header collapsing onto them.
private void DrawSectionHeader(string header, int count) private void DrawSectionHeader(string header, int count)
{ {
ImGui.Separator(); var colors = _themes.Active.Colors;
if (Plugin.Config.AutoTellTabsCompactDisplay) var compact = Plugin.Config.AutoTellTabsCompactDisplay;
return;
ImGui.TextDisabled($"{header} ({count})"); LineDivider.Draw(
LastDrawnSectionHeaderCount++; compact ? null : $"{header} ({count})",
} _palette.Abgr(Token.Border, colors),
_palette.Abgr(Token.TextMuted, colors)
);
// Mirror of 1.5.6's BuildSidebarRenderOrder: returns indices into the if (!compact)
// live tab list grouped by section, so the list order itself is never LastDrawnSectionHeaderCount++;
// mutated and headers gate on the first tab actually reached per pool
// (an empty pool draws neither separator nor header).
private static List<int> BuildRenderOrder(IList<Tab> tabs)
{
var persistent = new List<int>(tabs.Count);
var pinned = new List<int>();
var unpinned = new List<int>();
for (var i = 0; i < tabs.Count; i++)
{
if (TabLifecycleHelpers.IsInPinnedPool(tabs[i]))
pinned.Add(i);
else if (TabLifecycleHelpers.IsInUnpinnedPool(tabs[i]))
unpinned.Add(i);
else
persistent.Add(i);
}
persistent.AddRange(pinned);
persistent.AddRange(unpinned);
return persistent;
} }
private void DrawRow( private void DrawRow(
Tab tab, Tab tab,
int index,
bool expanded, bool expanded,
uint accentRgba, uint accentRgba,
uint textAbgr, uint textAbgr,
uint mutedAbgr, uint mutedAbgr,
uint dimAbgr, uint dimAbgr,
uint dangerAbgr,
ImDrawListPtr dl, ImDrawListPtr dl,
ref Tab? activeTab ref Tab? activeTab
) )
{ {
ImGui.PushID(index); // Identity, not position: ImGui keeps popup state across frames under this
// ID, so an index would re-bind an open context menu to a different tab as
// soon as the list shifts. String, not GetHashCode — hashes collide.
ImGui.PushID(tab.Identifier.ToString());
var origin = ImGui.GetCursorScreenPos(); var origin = ImGui.GetCursorScreenPos();
var avail = ImGui.GetContentRegionAvail().X; var avail = ImGui.GetContentRegionAvail().X;
@@ -231,7 +231,7 @@ internal sealed class Sidebar
// Drop the row entirely when the sidebar is dragged below the width // Drop the row entirely when the sidebar is dragged below the width
// of a single hit target. ImGui's InvisibleButton asserts on a // of a single hit target. ImGui's InvisibleButton asserts on a
// zero-width size, which crashes the whole window at min-drag. // zero-width size, which crashes the whole window at min-drag.
if (avail < 2f) if (avail < Metrics.SidebarMinDrawWidth)
{ {
ImGui.PopID(); ImGui.PopID();
return; return;
@@ -243,12 +243,18 @@ internal sealed class Sidebar
// icon-only or min-drag mode it is skipped entirely. // icon-only or min-drag mode it is skipped entirely.
var greetedConfigured = tab.IsTempTab && Plugin.Config.AutoTellTabsShowGreetedToggle; var greetedConfigured = tab.IsTempTab && Plugin.Config.AutoTellTabsShowGreetedToggle;
var showGreeted = var showGreeted =
greetedConfigured && expanded && avail > GreetedHitWidth + PopOutHitWidth + 4f; greetedConfigured
&& expanded
&& avail > GreetedHitWidth + PopOutHitWidth + Metrics.SidebarHitSlack;
// Only split off a separate pop-out hit area when there's room for // Only split off a separate pop-out hit area when there's room for
// both buttons. Below that, the whole row stays as a single // both buttons. Below that, the whole row stays as a single
// selectable strip without the pop-out affordance. // selectable strip without the pop-out affordance.
var hasPopOut = avail > PopOutHitWidth + 4f; // A3: gate the pop-out affordance on the expanded sidebar too. In
// icon-only mode avail still clears the width threshold, which used to
// paint the pop-out glyph over the tab icon. The row stays a single
// selectable strip when collapsed; right-click pop-out is unaffected.
var hasPopOut = expanded && avail > PopOutHitWidth + Metrics.SidebarHitSlack;
var tabHitWidth = hasPopOut ? avail - PopOutHitWidth : avail; var tabHitWidth = hasPopOut ? avail - PopOutHitWidth : avail;
if (showGreeted) if (showGreeted)
{ {
@@ -259,7 +265,6 @@ internal sealed class Sidebar
} }
ImGui.InvisibleButton("row", new Vector2(tabHitWidth, RowHeight)); ImGui.InvisibleButton("row", new Vector2(tabHitWidth, RowHeight));
var rowHovered = ImGui.IsItemHovered();
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
{ {
var previous = activeTab; var previous = activeTab;
@@ -267,20 +272,69 @@ internal sealed class Sidebar
TabLifecycleHelpers.OnTabActivated(tab, previous); TabLifecycleHelpers.OnTabActivated(tab, previous);
} }
dl.DrawHoverSheen( // Not IsItemHovered: the row button is up to two hit widths narrower than
// the row, so a full-width surface driven by it would flicker at the
// edges. AllowWhenBlockedByActiveItem keeps the surface while the button
// is held down; without it the fill vanishes on press.
var rowMax = origin + new Vector2(avail, RowHeight);
var surfaceHovered =
ImGui.IsMouseHoveringRect(origin, rowMax)
&& ImGui.IsWindowHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem);
// GetID is seeded from the window's ID stack, so the same "row" literal
// stays distinct per window and per PushID'd tab. The old interpolated
// key allocated two strings per row per frame.
var hoverId = ImGui.GetID("row"u8);
var hoverAmount = HoverState.Query(hoverId, surfaceHovered);
var isActiveRow = ReferenceEquals(tab, activeTab);
if (isActiveRow)
LastRenderedActiveSurfaceCount++;
var colors = _themes.Active.Colors;
// Row fills follow the window's own opacity. Theme surfaces are fully
// opaque and the window is translucent by default (0.85 focused, 0.65
// not), so unscaled fills would sit on top as solid blocks.
var opacity = ImGui.IsWindowFocused(ImGuiFocusedFlags.RootWindow)
? Plugin.Config.WindowOpacity
: Plugin.Config.WindowOpacityInactive;
Row.Draw(
origin, origin,
origin + new Vector2(avail, RowHeight), new Vector2(avail, RowHeight),
accentRgba, new RowVisualState
$"sidebar.tab.{tab.Identifier}", {
rowHovered IsActive = isActiveRow,
HoverAmount = hoverAmount,
SurfaceHoverAbgr = ColourUtil.ApplyAlpha(
_palette.Abgr(Token.SurfaceHover, colors),
opacity
),
SurfaceActiveAbgr = ColourUtil.ApplyAlpha(
_palette.Abgr(Token.SurfaceActive, colors),
opacity
),
AccentAbgr = _palette.Abgr(Token.AccentPrimary, colors),
BorderAbgr = ColourUtil.ApplyAlpha(_palette.Abgr(Token.Border, colors), opacity),
}
); );
dl.DrawHoverSheen(origin, rowMax, accentRgba, hoverAmount, surfaceHovered);
var icon = ResolveTabIcon(tab); var icon = ResolveTabIcon(tab);
// Dim precedence (1.5.6): the active tab always keeps its regular // Dim precedence (1.5.6): the active tab never dims; only greeted,
// color; only greeted, non-active tabs drop to TextDim. // non-active tabs drop to TextDim. "Regular colour" is the tint for an
// auto-tell tab and the theme text colour for everything else.
//
// Below that, an auto-tell tab is tinted from its partner. Twelve
// colours against seven glyphs is 84 combinations, which is plenty for
// the one to five conversations anybody actually runs in parallel. The
// greeted dim still wins: it says something about this tab right now,
// the tint only says who it belongs to.
var isCurrentTab = tab == activeTab; var isCurrentTab = tab == activeTab;
var iconColor = textAbgr; var iconColor = tab.IsTempTab ? ColourUtil.RgbaToAbgr(TabTintCache.GetTint(tab)) : textAbgr;
if ( if (
!isCurrentTab !isCurrentTab
&& greetedConfigured && greetedConfigured
@@ -290,44 +344,116 @@ internal sealed class Sidebar
// Icon and label shift right by the greeted slot when it is shown. // Icon and label shift right by the greeted slot when it is shown.
var contentX = showGreeted ? GreetedHitWidth : 0f; var contentX = showGreeted ? GreetedHitWidth : 0f;
var scale = Metrics.Scale;
var iconInset = 10f * scale;
// Centred, not a frozen offset: the old 8f was (32 - 16) / 2 for a 16px
// font and stays wrong at any other Config.FontSizeV2.
var contentY = Metrics.CenterY(RowHeight);
float iconRight;
using (_fonts.FontAwesome.Push()) using (_fonts.FontAwesome.Push())
{ {
// Measured inside the push: FontAwesome is a fixed-width icon handle
// that does not follow Config.FontSizeV2, so the text font's line
// height would misplace the glyph at any other body size.
var iconStr = icon.ToIconString(); var iconStr = icon.ToIconString();
dl.AddText(origin + new Vector2(10f + contentX, 8f), iconColor, iconStr); var iconSize = ImGui.CalcTextSize(iconStr);
var iconPos =
origin
+ new Vector2(iconInset + contentX, MetricsMath.CenterY(RowHeight, iconSize.Y));
dl.AddText(iconPos, iconColor, iconStr);
iconRight = iconInset + contentX + iconSize.X;
// 1.5.6-parity unread dot, top-right of the icon. The active tab is // Pinned marker: a small thumbtack tucked into the icon's lower
// zeroed every frame (MainWindow.Draw), so the dot never shows on the // left. Drawn from the same font push and inside the row rectangle
// tab you're viewing; UnreadMode.None opts a tab out entirely. // the InvisibleButton already reserved, so it claims no layout of
if (!isCurrentTab && tab.UnreadMode != UnreadMode.None && tab.Unread > 0) // its own -- badges in this very sidebar are where "draw into
// unreserved space" caught this project last.
//
// Lower left because the unread dot owns the upper right.
if (tab.IsPinned)
{ {
var iconRight = 10f + contentX + ImGui.CalcTextSize(iconStr).X; var pinStr = FontAwesomeIcon.Thumbtack.ToIconString();
dl.AddCircleFilled(origin + new Vector2(iconRight - 2f, 6f), 4f, dangerAbgr, 12); var pinFontSize = ImGui.GetFontSize() * PinGlyphScale;
LastRenderedUnreadDotCount++; var pinSize = ImGui.CalcTextSize(pinStr) * PinGlyphScale;
dl.AddText(
ImGui.GetFont(),
pinFontSize,
iconPos + new Vector2(-pinSize.X * 0.45f, iconSize.Y - pinSize.Y * 0.75f),
_palette.Abgr(Token.AccentPrimary, colors),
pinStr
);
} }
} }
// Only for pinned rows, and only in the sidebar's own hover state --
// this is the one place a user meets the marker without having opened
// the menu that produced it.
if (tab.IsPinned && surfaceHovered)
ImGuiUtil.Tooltip(HellionStrings.PinTab_PinnedTooltip);
if (expanded) if (expanded)
dl.AddText(origin + new Vector2(32f + contentX, 8f), textAbgr, tab.Name); dl.AddText(origin + new Vector2(iconRight + 6f * scale, contentY), textAbgr, tab.Name);
// Unread count. Drawn outside the icon-font scope on purpose: the
// FontAwesome atlas carries no ASCII digits, so the number would come out
// blank. The active tab is zeroed every frame (MainWindow.Draw), so it
// never shows on the tab you are viewing; UnreadMode.None opts out.
if (!isCurrentTab && tab.UnreadMode != UnreadMode.None && tab.Unread > 0)
{
var unread = (int)Math.Min(tab.Unread, int.MaxValue);
var badgeSize = Badge.CalcSize(unread);
var accentAbgr = _palette.Abgr(Token.AccentEmber, colors);
var slack = Metrics.SidebarHitSlack;
var reserved = hasPopOut ? PopOutHitWidth : 0f;
var badgeX = avail - reserved - badgeSize.X - slack;
// The count only fits where it can sit clear of the icon. At the
// default sidebar width of 44 it cannot, so a plain dot takes over
// rather than the badge landing on the glyph.
if (badgeX >= iconRight + slack)
{
Badge.Draw(
origin + new Vector2(badgeX, MetricsMath.CenterY(RowHeight, badgeSize.Y)),
unread,
accentAbgr,
textAbgr
);
}
else
{
var r = Metrics.SidebarUnreadRadius;
dl.AddCircleFilled(
origin + new Vector2(iconRight - r * 0.5f, RowHeight * 0.5f - r),
r,
accentAbgr,
12
);
}
LastRenderedUnreadDotCount++;
}
TabContextMenu.Draw(tab, "ctx", _pool); TabContextMenu.Draw(tab, "ctx", _pool);
var popHovered = false;
if (hasPopOut) if (hasPopOut)
{ {
ImGui.SameLine(0f, 0f); ImGui.SameLine(0f, 0f);
ImGui.InvisibleButton("popout", new Vector2(PopOutHitWidth, RowHeight));
popHovered = ImGui.IsItemHovered();
if (ImGui.IsItemClicked())
_pool.TryOpen(tab);
}
if (hasPopOut && (rowHovered || popHovered)) // Glyph follows the row surface, not the button's own hover: the
{ // button sits inside the row, and the old pairing needed a hover
using (_fonts.FontAwesome.Push()) // state one line before it existed.
{ var (popClicked, _) = IconButton.Draw(
var glyph = FontAwesomeIcon.ArrowUpRightFromSquare.ToIconString(); ImGui.GetID("popout"u8),
dl.AddText(origin + new Vector2(avail - PopOutHitWidth + 4f, 8f), mutedAbgr, glyph); new Vector2(PopOutHitWidth, RowHeight),
} surfaceHovered ? FontAwesomeIcon.ArrowUpRightFromSquare : null,
mutedAbgr,
_palette.Abgr(Token.SurfaceHover, colors),
_fonts.FontAwesome
);
if (popClicked)
_pool.TryOpen(tab);
} }
if (showGreeted) if (showGreeted)
@@ -337,16 +463,23 @@ internal sealed class Sidebar
// between the row button and the popup call would steal the // between the row button and the popup call would steal the
// right-click trigger (B3-1 ordering constraint). // right-click trigger (B3-1 ordering constraint).
ImGui.SetCursorScreenPos(origin); ImGui.SetCursorScreenPos(origin);
ImGui.InvisibleButton("greeted", new Vector2(GreetedHitWidth, RowHeight));
if (ImGui.IsItemClicked())
ToggleGreetedForSelfTest(tab);
// CheckCircle = greeted, plain Check = still pending (1.5.6 mapping). // CheckCircle = greeted, plain Check = still pending (1.5.6 mapping).
var greetedGlyph = Plugin.Instance.AutoTellTabsService.IsGreeted(tab) var greetedGlyph = Plugin.Instance.AutoTellTabsService.IsGreeted(tab)
? FontAwesomeIcon.CheckCircle ? FontAwesomeIcon.CheckCircle
: FontAwesomeIcon.Check; : FontAwesomeIcon.Check;
using (_fonts.FontAwesome.Push())
dl.AddText(origin + new Vector2(4f, 8f), mutedAbgr, greetedGlyph.ToIconString()); var (greetedClicked, _) = IconButton.Draw(
ImGui.GetID("greeted"u8),
new Vector2(GreetedHitWidth, RowHeight),
greetedGlyph,
mutedAbgr,
_palette.Abgr(Token.SurfaceHover, colors),
_fonts.FontAwesome
);
if (greetedClicked)
ToggleGreetedForSelfTest(tab);
LastRenderedGreetedGlyphCount++; LastRenderedGreetedGlyphCount++;
} }
@@ -360,10 +493,20 @@ internal sealed class Sidebar
) )
return mapped; return mapped;
// Auto-tell tabs always show the envelope, regardless of what their // Auto-tell tabs get one of seven glyphs derived from the partner, not
// SelectedChannels filter is set to. // one envelope for all of them. With four tells open, identical rows in
// identical colour are four rows you have to read to tell apart.
if (tab.IsTempTab) if (tab.IsTempTab)
return FontAwesomeIcon.Envelope; {
// TryGetValue rather than an indexer: every glyph in the pool is in
// the table today, and a lookup miss would be somebody editing one
// of the two lists without the other. A wrong envelope beats a
// KeyNotFoundException on the draw thread.
var hashed = TabTintCache.GetIcon(tab);
return IconByName.TryGetValue(hashed, out var tellGlyph)
? tellGlyph
: FontAwesomeIcon.Envelope;
}
// Channel-type fallback. Walk every selected key, not just the first, // Channel-type fallback. Walk every selected key, not just the first,
// so a System tab that filters multiple system-flavoured ChatTypes // so a System tab that filters multiple system-flavoured ChatTypes
+114 -70
View File
@@ -2,11 +2,11 @@ using System.Globalization;
using System.Numerics; using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Code; using HellionChat.Code;
using HellionChat.Resources; using HellionChat.Resources;
using HellionChat.Themes; using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
using HellionChat.Util; using HellionChat.Util;
namespace HellionChat.Ui.Components; namespace HellionChat.Ui.Components;
@@ -21,13 +21,24 @@ internal sealed class StatusBar
// scaling above 100% — GetTextLineHeightWithSpacing scales with the // scaling above 100% — GetTextLineHeightWithSpacing scales with the
// active ImGui font, the 2px spacer rounds against GlobalScale so the // active ImGui font, the 2px spacer rounds against GlobalScale so the
// result lands on integer pixel boundaries. // result lands on integer pixel boundaries.
// Derived from the pill, never a second constant: MainWindow reserves the
// body height against this property, so the two drifting apart is the whole
// failure mode. Slots are pills now, and a pill is taller than a text line.
public static float Height => public static float Height =>
ImGui.GetTextLineHeightWithSpacing() + MathF.Round(2f * ImGuiHelpers.GlobalScale); StyleEngine.Widgets.Pill.CalcSize(string.Empty, withDot: false).Y
+ StyleEngine.Metrics.StatusTopSpacer * 2f;
private const long UpdateIntervalMs = 1000; private const long UpdateIntervalMs = 1000;
private readonly ThemeRegistry _themes; private readonly ThemeRegistry _themes;
private readonly FontManager _fonts; private readonly FontManager _fonts;
private readonly StyleEngine.Widgets.WidgetPalette _palette = new(
new StyleEngine.TokenResolver()
);
// Never changes at runtime; it used to be rebuilt on every frame.
private static readonly string VersionText =
$"v{Plugin.Interface.Manifest.AssemblyVersion} · Hellion";
private long _lastUpdateMs = -UpdateIntervalMs; private long _lastUpdateMs = -UpdateIntervalMs;
private string _cachedCountsText = string.Empty; private string _cachedCountsText = string.Empty;
@@ -45,9 +56,16 @@ internal sealed class StatusBar
{ {
var msgPart = var msgPart =
messages >= 1000 messages >= 1000
? string.Format(CultureInfo.InvariantCulture, "{0:0.0}k msg", messages / 1000.0) ? string.Format(
: $"{messages} msg"; CultureInfo.CurrentCulture,
var tabsPart = $"{tabs} {(tabs == 1 ? "tab" : "tabs")}"; 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}"; return $"{tabsPart} · {msgPart}";
} }
@@ -55,12 +73,15 @@ internal sealed class StatusBar
{ {
if (count <= 0) if (count <= 0)
return string.Empty; 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 // Single-pass aggregator — same shape as the previous helper so the
// build-suite test continues to pin the contract. // build-suite test continues to pin the contract.
internal static (int messages, int tells) AggregateForStatusBar(IList<Tab> tabs) internal static (int messages, int tells) AggregateForStatusBar(IReadOnlyList<Tab> tabs)
{ {
int messages = 0, int messages = 0,
tells = 0; tells = 0;
@@ -93,7 +114,7 @@ internal sealed class StatusBar
_lastUpdateMs = now; _lastUpdateMs = now;
} }
public void Draw(Tab? activeTab) public void Draw(Tab? activeTab, IReadOnlyList<Tab> tabs)
{ {
if (!_fonts.FontsReady) if (!_fonts.FontsReady)
{ {
@@ -105,89 +126,112 @@ internal sealed class StatusBar
var now = Environment.TickCount64; var now = Environment.TickCount64;
if (now - _lastUpdateMs >= UpdateIntervalMs) if (now - _lastUpdateMs >= UpdateIntervalMs)
{ {
var (messages, tells) = AggregateForStatusBar(Plugin.Config.Tabs); var (messages, tells) = AggregateForStatusBar(tabs);
UpdateCacheIfDue(now, Plugin.Config.Tabs.Count, messages, tells); UpdateCacheIfDue(now, tabs.Count, messages, tells);
} }
// Top border via DrawList — ImGui.Separator has too much padding for // Top border via DrawList — ImGui.Separator has too much padding for
// a tight bottom strip. // a tight bottom strip.
var cursorY = ImGui.GetCursorScreenPos().Y; var origin = ImGui.GetCursorScreenPos();
var winLeft = ImGui.GetWindowPos().X; var winLeft = ImGui.GetWindowPos().X;
var winRight = winLeft + ImGui.GetWindowSize().X; var winRight = winLeft + ImGui.GetWindowSize().X;
var palette = _palette;
var colors = theme.Colors;
ImGui ImGui
.GetWindowDrawList() .GetWindowDrawList()
.AddLine( .AddLine(
new Vector2(winLeft, cursorY), new Vector2(winLeft, origin.Y),
new Vector2(winRight, cursorY), new Vector2(winRight, origin.Y),
ColourUtil.RgbaToAbgr(theme.Colors.Border), palette.Abgr(Token.Border, colors),
1f StyleEngine.Metrics.StatusBorderThickness
); );
ImGui.Dummy(new Vector2(0, 2));
// Slot 1: active channel indicator var pillFill = palette.Abgr(Token.SurfaceRaised, colors);
var pillText = palette.Abgr(Token.Text, colors);
var mutedText = palette.Abgr(Token.TextMuted, colors);
var gap = StyleEngine.Metrics.StatusTopSpacer * 3f;
var top = origin.Y + StyleEngine.Metrics.StatusTopSpacer;
// Slot 1: active channel. The dot doubles as the connection indicator.
var inputCh = activeTab?.CurrentChannel?.Channel ?? InputChannel.Invalid; var inputCh = activeTab?.CurrentChannel?.Channel ?? InputChannel.Invalid;
var hasChannel = inputCh != InputChannel.Invalid; var hasChannel = inputCh != InputChannel.Invalid;
var chatType = inputCh.ToChatType(); var channelName = hasChannel ? inputCh.ToChatType().Name() : "—";
var channelName = hasChannel ? chatType.Name() : "—"; var dotAbgr = hasChannel
var dotColor = hasChannel ? theme.Colors.Primary : theme.Colors.TextMuted; ? palette.Abgr(Token.AccentPrimary, colors)
DrawDot(dotColor); : palette.Abgr(Token.TextMuted, colors);
ImGui.SameLine();
ImGui.TextUnformatted(channelName);
// Slot 2: privacy badge // Slot 2 label, resolved before measuring so the run width is exact.
ImGui.SameLine();
DrawSeparator();
ImGui.SameLine();
using (_fonts.FontAwesome.Push())
ImGui.TextUnformatted(FontAwesomeIcon.Lock.ToIconString());
ImGui.SameLine();
var privacyLabel = Plugin.Config.PrivacyFilterEnabled var privacyLabel = Plugin.Config.PrivacyFilterEnabled
? HellionStrings.StatusBar_Privacy_Enabled ? HellionStrings.StatusBar_Privacy_Enabled
: HellionStrings.StatusBar_Privacy_Open; : HellionStrings.StatusBar_Privacy_Open;
ImGui.TextUnformatted(privacyLabel);
// Slot 3: counts // Every slot checks its own room. Only the right-hand one used to, so at
ImGui.SameLine(); // 150% scaling with the window at its 480px minimum the counts and tells
DrawSeparator(); // pills ran off the edge instead of dropping out.
ImGui.SameLine(); var regionRight = origin.X + ImGui.GetContentRegionAvail().X;
ImGui.TextUnformatted(_cachedCountsText); var x = origin.X;
// Slot 4: tells (hidden at 0) bool Fits(string label, bool withDot, float iconWidth = 0f) =>
if (!string.IsNullOrEmpty(_cachedTellsText)) x + StyleEngine.Widgets.Pill.CalcSize(label, withDot, iconWidth: iconWidth).X
{ <= regionRight;
ImGui.SameLine();
DrawSeparator();
ImGui.SameLine();
ImGui.TextUnformatted(_cachedTellsText);
}
// Slot 5: version + brand, right-aligned, muted. Hidden when the if (Fits(channelName, withDot: true))
// window cannot fit all five slots without overlap. x += DrawSlot(new Vector2(x, top), channelName, pillFill, pillText, dotAbgr) + gap;
var versionText = $"v{Plugin.Interface.Manifest.AssemblyVersion} · Hellion"; var lockWidth = StyleEngine.Widgets.Pill.MeasureIcon(
var versionWidth = ImGui.CalcTextSize(versionText).X; FontAwesomeIcon.Lock,
var contentRegionMax = ImGui.GetContentRegionMax().X; _fonts.FontAwesome
const float MinOtherSlotsWidth = 200f; );
if (contentRegionMax - versionWidth > MinOtherSlotsWidth) if (Fits(privacyLabel, withDot: false, lockWidth))
{ x +=
ImGui.SameLine(contentRegionMax - versionWidth); DrawSlot(
using (ImRaii.PushColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(theme.Colors.TextMuted))) new Vector2(x, top),
ImGui.TextUnformatted(versionText); privacyLabel,
} pillFill,
} pillText,
null,
(FontAwesomeIcon.Lock, _fonts.FontAwesome)
) + gap;
private static void DrawDot(uint rgba) if (Fits(_cachedCountsText, withDot: false))
{ x += DrawSlot(new Vector2(x, top), _cachedCountsText, pillFill, mutedText, null) + gap;
var pos = ImGui.GetCursorScreenPos();
const float radius = 4f; if (!string.IsNullOrEmpty(_cachedTellsText) && Fits(_cachedTellsText, withDot: false))
ImGui x += DrawSlot(new Vector2(x, top), _cachedTellsText, pillFill, pillText, null) + gap;
.GetWindowDrawList()
.AddCircleFilled( // Slot 5: version + brand, right-aligned. Dropped when the left-hand run
new Vector2(pos.X + radius, pos.Y + ImGui.GetTextLineHeight() / 2f), // would actually collide with it -- the old check compared against a flat
radius, // 200px and never measured the left slots at all.
ColourUtil.RgbaToAbgr(rgba) var versionWidth = StyleEngine.Widgets.Pill.CalcSize(VersionText, withDot: false).X;
var leftRunEnd = x - gap;
if (regionRight - versionWidth - gap > leftRunEnd)
DrawSlot(
new Vector2(regionRight - versionWidth, top),
VersionText,
pillFill,
mutedText,
null
); );
ImGui.Dummy(new Vector2(radius * 2 + 4, ImGui.GetTextLineHeight()));
ImGui.Dummy(new Vector2(0, Height));
} }
private static void DrawSeparator() => ImGui.TextDisabled("·"); // Returns the slot width so the caller can run them left to right and know
// where the run ends.
private static float DrawSlot(
Vector2 origin,
string label,
uint fillAbgr,
uint textAbgr,
uint? dotAbgr,
(FontAwesomeIcon Icon, IFontHandle Font)? icon = null
)
{
StyleEngine.Widgets.Pill.Draw(origin, label, fillAbgr, textAbgr, dotAbgr, icon: icon);
var iconWidth = icon is { } ic
? StyleEngine.Widgets.Pill.MeasureIcon(ic.Icon, ic.Font)
: 0f;
return StyleEngine.Widgets.Pill.CalcSize(label, dotAbgr.HasValue, iconWidth: iconWidth).X;
}
} }
+119 -3
View File
@@ -1,6 +1,7 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using HellionChat.Resources; using HellionChat.Resources;
using HellionChat.Util; using HellionChat.Util;
@@ -12,6 +13,12 @@ namespace HellionChat.Ui.Components;
// state and reaches the live Config/Plugin through Plugin.Instance/Plugin.Config. // state and reaches the live Config/Plugin through Plugin.Instance/Plugin.Config.
internal static class TabContextMenu internal static class TabContextMenu
{ {
// Pending rename, scoped to one tab. ImGui never re-submits the input when the
// popup is dismissed by clicking outside, so IsItemDeactivatedAfterEdit never
// fires there — without this the rename would be lost.
private static Guid _renamingTab;
private static bool _renameDirty;
// MUST be called immediately after the row-carrying ImGui item (Sidebar // MUST be called immediately after the row-carrying ImGui item (Sidebar
// "row" InvisibleButton / TopTabBar Selectable). popupId only names the // "row" InvisibleButton / TopTabBar Selectable). popupId only names the
// popup; the open trigger is a right-click on the LAST submitted item // popup; the open trigger is a right-click on the LAST submitted item
@@ -20,15 +27,60 @@ internal static class TabContextMenu
public static void Draw(Tab tab, string popupId, Windows.ChannelPopoutPool pool) public static void Draw(Tab tab, string popupId, Windows.ChannelPopoutPool pool)
{ {
if (!ImGui.BeginPopupContextItem(popupId)) if (!ImGui.BeginPopupContextItem(popupId))
return; {
// Popup gone: flush a pending rename. Scoped to the OWNING tab — every
// other tab's Draw lands here too and would flush foreign state.
if (_renamingTab == tab.Identifier)
{
if (_renameDirty)
Plugin.Instance.SaveConfig();
ClearPendingRename();
}
return;
}
// The sidebar pushes ItemSpacing to zero so its rows sit flush, and style
// vars are a global stack the popup inherits. Reading GetStyle() here
// would read that zero back, so the popup sets its own spacing outright
// -- including X, which HelpMarker's SameLine depends on.
//
// Scoped block, not a `using var`: that would pop after EndPopup, and
// ImGui asserts when a popup closes with a style var still on the stack.
using (
ImRaii.PushStyle(
ImGuiStyleVar.ItemSpacing,
new System.Numerics.Vector2(8f, 4f) * Ui.StyleEngine.Metrics.Scale
)
)
{
DrawBody(tab, pool);
}
ImGui.EndPopup();
}
private static void DrawBody(Tab tab, Windows.ChannelPopoutPool pool)
{
// Rename: focus the field the first frame the popup appears. // Rename: focus the field the first frame the popup appears.
if (ImGui.IsWindowAppearing()) if (ImGui.IsWindowAppearing())
ImGui.SetKeyboardFocusHere(); ImGui.SetKeyboardFocusHere();
ImGui.SetNextItemWidth(250f * ImGuiHelpers.GlobalScale); ImGui.SetNextItemWidth(250f * ImGuiHelpers.GlobalScale);
var name = tab.Name; var name = tab.Name;
if (ImGui.InputText("##tab-name", ref name, 512) && ApplyTabRename(tab, name)) if (ImGui.InputText("##tab-name", ref name, 512) && ApplyTabRename(tab, name))
Plugin.Instance.SaveConfig(); {
_renamingTab = tab.Identifier;
_renameDirty = true;
}
// Covers leaving the field while the popup stays open; the dismissed-popup
// case is handled above.
if (ImGui.IsItemDeactivatedAfterEdit() && _renamingTab == tab.Identifier)
{
if (_renameDirty)
Plugin.Instance.SaveConfig();
ClearPendingRename();
}
// Per-tab notification sound (B3-3). The checkbox gates the picker so // Per-tab notification sound (B3-3). The checkbox gates the picker so
// tabs that never want a sound keep the popup short. // tabs that never want a sound keep the popup short.
@@ -46,7 +98,71 @@ internal static class TabContextMenu
if (ImGui.MenuItem("Pop Out")) if (ImGui.MenuItem("Pop Out"))
pool.TryOpen(tab); pool.TryOpen(tab);
ImGui.EndPopup(); DrawPinControls(tab);
}
// Pinning has been complete since v1.4.7 -- pools, cap, persistence, logout
// symmetry, the notification -- and has had no way in since the menu that
// called it was removed.
//
// That left a dead end in saved data, which is the real reason this is here:
// a tab pinned in v1.5.6 survives every save and load, permanently occupying
// one of five pool slots, with nothing anywhere to release it.
//
// Promote-to-permanent deliberately does not come back. It was removed on
// purpose after a tester kept hitting it by accident, and reconnecting every
// caller-less method without asking why it lost its caller would rebuild the
// problem.
private static void DrawPinControls(Tab tab)
{
if (!tab.IsTempTab)
return;
// Instance property today, not the static the old menu reached for.
var service = Plugin.Instance.AutoTellTabsService;
if (service is null)
return;
ImGui.Separator();
if (tab.IsPinned)
{
if (ImGui.MenuItem(HellionStrings.PinTab_MenuUnpin))
{
service.Unpin(tab);
ImGui.CloseCurrentPopup();
}
return;
}
var atCap = service.PinnedTempTabCount >= AutoTellTabsService.MaxPinnedTempTabs;
// Disabled rather than absent: the cap is a state the user can undo by
// unpinning something, and the tooltip below is what says so.
if (ImGui.MenuItem(HellionStrings.PinTab_MenuPin, enabled: !atCap) && service.TryPin(tab))
ImGui.CloseCurrentPopup();
if (!ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
return;
ImGuiUtil.Tooltip(
atCap
? string.Format(
HellionStrings.PinTab_LimitReached,
AutoTellTabsService.MaxPinnedTempTabs
)
: HellionStrings.PinTab_PinTooltip
);
}
// The flush depends on Draw running once more for this tab. If it never does —
// LRU eviction, logout, window closed or collapsed, plugin unload, game exit —
// the name only lives in memory until some other SaveConfig happens to run.
private static void ClearPendingRename()
{
_renamingTab = Guid.Empty;
_renameDirty = false;
} }
// Sound picker: 16 numbered game sounds, a separator, then the 3 bundled // Sound picker: 16 numbered game sounds, a separator, then the 3 bundled
@@ -86,7 +86,12 @@ internal sealed class ThemeQuickPicker
ImGui.Separator(); ImGui.Separator();
// Snapshot so a worker-thread temp-tab strip can't shift the list mid-loop. // Snapshot so a worker-thread temp-tab strip can't shift the list mid-loop.
var tabs = Plugin.Config.Tabs.ToList(); // The copy itself needs the lock, otherwise it tears the same way. Not the
// frame snapshot from MainWindow: reaching it would mean threading a
// parameter through InputBar, which popouts share and which has no tab list.
List<Tab> tabs;
lock (_plugin.TabsListLock)
tabs = Plugin.Config.Tabs.ToList();
var height = MathF.Min(tabs.Count * RowHeight, MaxSectionHeight); var height = MathF.Min(tabs.Count * RowHeight, MaxSectionHeight);
using var child = ImRaii.Child( using var child = ImRaii.Child(
"##hellion-quick-picker-tabs", "##hellion-quick-picker-tabs",
+145 -36
View File
@@ -1,5 +1,8 @@
using System.Numerics; using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using HellionChat.Themes;
using HellionChat.Ui.StyleEngine;
using HellionChat.Ui.StyleEngine.Widgets;
using HellionChat.Util; using HellionChat.Util;
namespace HellionChat.Ui.Components; namespace HellionChat.Ui.Components;
@@ -10,63 +13,169 @@ namespace HellionChat.Ui.Components;
internal sealed class TopTabBar internal sealed class TopTabBar
{ {
private readonly Windows.ChannelPopoutPool _pool; private readonly Windows.ChannelPopoutPool _pool;
private readonly ThemeRegistry _themes;
private readonly WidgetPalette _palette;
public TopTabBar(Windows.ChannelPopoutPool pool) // Slightly smaller than the sidebar default: the strip is horizontal, so
// every pixel of badge width costs a tab.
private static readonly BadgeStyle TabBadge = new() { Height = 12f, PaddingX = 3f };
// Render observability. At most one underline per frame, except in the frame
// a click lands: a tab drawn before the clicked one was still the active tab
// when it was painted, so that frame legitimately shows two.
internal int LastRenderedUnderlineCount { get; private set; }
public TopTabBar(Windows.ChannelPopoutPool pool, ThemeRegistry themes, TokenResolver resolver)
{ {
_pool = pool; _pool = pool;
_themes = themes;
_palette = new WidgetPalette(resolver);
} }
public void Draw(IList<Tab> tabs, ref Tab? activeTab) public void Draw(IReadOnlyList<Tab> tabs, ref Tab? activeTab)
{ {
LastRenderedUnderlineCount = 0;
var colors = _themes.Active.Colors;
var surfaceActive = _palette.Abgr(Token.SurfaceActive, colors);
var surfaceHover = _palette.Abgr(Token.SurfaceHover, colors);
var accent = _palette.Abgr(Token.AccentPrimary, colors);
var textAbgr = _palette.Abgr(Token.Text, colors);
var mutedAbgr = _palette.Abgr(Token.TextMuted, colors);
var borderAbgr = _palette.Abgr(Token.Border, colors);
var height = Metrics.TopTabHeight;
var padX = Metrics.TopTabPaddingX;
var dl = ImGui.GetWindowDrawList();
var firstDrawn = true;
for (var i = 0; i < tabs.Count; i++) for (var i = 0; i < tabs.Count; i++)
{ {
var tab = tabs[i]; var tab = tabs[i];
if (i > 0) // POP-1b: a popped-out tab is owned by its pop-out window, not the main
// strip (1.5.6 exclusivity). Gate on the pool, not Tab.PopOut (stale flag).
if (_pool.IsOpen(tab.Identifier))
continue;
if (!firstDrawn)
ImGui.SameLine(); ImGui.SameLine();
firstDrawn = false;
var selected = ReferenceEquals(tab, activeTab); var selected = ReferenceEquals(tab, activeTab);
// Size the selectable to its own label width. A zero width makes ImGui var origin = ImGui.GetCursorScreenPos();
// stretch the selectable's box to the full remaining window width
// (imgui_widgets.cpp:7378), so in this SameLine row every tab overlaps // The badge has to be part of the width, not painted over it. Padding
// into one giant bar and clicking never lands on the intended tab. // alone is 10px and the badge is at least 14 wide, so placing it in
var tabWidth = ImGui.CalcTextSize(tab.Name).X; // the trailing padding covered the label on every tab that had one.
if ( var showUnread =
ImGui.Selectable( !ReferenceEquals(tab, activeTab)
$"{tab.Name}###hellion_toptab_{i}", && tab.UnreadMode != UnreadMode.None
selected, && tab.Unread > 0;
ImGuiSelectableFlags.None, var unread = showUnread ? (int)Math.Min(tab.Unread, int.MaxValue) : 0;
new Vector2(tabWidth, 0) var badgeSize = showUnread ? Badge.CalcSize(unread, TabBadge) : Vector2.Zero;
)
) var width =
ImGui.CalcTextSize(tab.Name).X
+ padX * 2f
+ (showUnread ? badgeSize.X + Metrics.TopTabUnreadInset : 0f);
var size = WidgetGeometry.IconButton(width, height);
// The ### keeps the ImGui id stable across a rename; without it the
// context menu loses its binding the moment the label changes. Built
// once and reused for the hover key, since GetID hashes the same
// string the button registers under.
var buttonId = $"###hellion_toptab_{tab.Identifier}";
var hoverId = ImGui.GetID(buttonId);
var pressed = ImGui.InvisibleButton(buttonId, size);
var hovered = ImGui.IsItemHovered();
var hoverAmount = HoverState.Query(hoverId, hovered);
if (pressed)
{ {
var previous = activeTab; var previous = activeTab;
activeTab = tab; activeTab = tab;
TabLifecycleHelpers.OnTabActivated(tab, previous); TabLifecycleHelpers.OnTabActivated(tab, previous);
selected = true;
} }
// 1.5.6-parity unread dot at the item's top-right. Gate on the DrawTab(
// POST-click selection (not the frame-start 'selected') so clicking a dl,
// tab suppresses its dot the same frame, like the sidebar. The active origin,
// tab is also zeroed every frame (MainWindow.Draw). size,
if ( tab.Name,
!ReferenceEquals(tab, activeTab) selected,
&& tab.UnreadMode != UnreadMode.None hoverAmount,
&& tab.Unread > 0 surfaceActive,
) surfaceHover,
{ accent,
var max = ImGui.GetItemRectMax(); selected || hovered ? textAbgr : mutedAbgr,
var min = ImGui.GetItemRectMin(); padX
var danger = ColourUtil.RgbaToAbgr( );
Plugin.Instance.ThemeRegistry.Active.Colors.StatusDanger
if (selected)
LastRenderedUnderlineCount++;
// Clicking a tab clears its marker in the same frame, like the
// sidebar: showUnread was resolved before the click was handled, so
// re-check against the post-click selection.
if (showUnread && !ReferenceEquals(tab, activeTab))
Badge.Draw(
new Vector2(
origin.X + size.X - badgeSize.X - Metrics.TopTabUnreadInset,
origin.Y + MetricsMath.CenterY(size.Y, badgeSize.Y)
),
unread,
_palette.Abgr(Token.AccentEmber, colors),
textAbgr,
TabBadge
); );
ImGui
.GetWindowDrawList()
.AddCircleFilled(new Vector2(max.X - 4f, min.Y + 4f), 3.5f, danger, 12);
}
TabContextMenu.Draw(tab, $"toptab_ctx_{i}", _pool); TabContextMenu.Draw(tab, $"toptab_ctx_{tab.Identifier}", _pool);
} }
ImGui.Separator(); LineDivider.Draw(null, borderAbgr, mutedAbgr);
}
// Keeps the fill Selectable used to provide (ImGuiCol.Header) and adds the
// underline on top. Dropping the fill for the underline alone would make the
// active tab harder to spot, not easier.
private static void DrawTab(
ImDrawListPtr dl,
Vector2 origin,
Vector2 size,
string label,
bool selected,
float hoverAmount,
uint surfaceActive,
uint surfaceHover,
uint accent,
uint labelAbgr,
float padX
)
{
var max = origin + size;
if (selected)
dl.AddRectFilled(origin, max, surfaceActive);
if (hoverAmount > 0f)
{
var a = (uint)
Math.Clamp(MathF.Round(((surfaceHover >> 24) & 0xFF) * hoverAmount), 0f, 255f);
dl.AddRectFilled(origin, max, (surfaceHover & 0x00FFFFFFu) | (a << 24));
}
if (selected)
{
var thickness = Metrics.TopTabUnderline;
dl.AddRectFilled(new Vector2(origin.X, max.Y - thickness), max, accent);
}
var textSize = ImGui.CalcTextSize(label);
dl.AddText(
new Vector2(origin.X + padX, origin.Y + MetricsMath.CenterY(size.Y, textSize.Y)),
labelAbgr,
label
);
} }
} }
+29 -45
View File
@@ -341,17 +341,16 @@ public sealed class FirstRunWizard : Window
private void DrawStepPowerSettings() private void DrawStepPowerSettings()
{ {
// Seed only the two recommendation fields here. Other fields remain // Seed only the recommendation field here. Other fields remain null
// null until the user touches the corresponding control. // until the user touches the corresponding control.
// Spec FR-4: the wizard explicitly recommends LoadPreviousSession = // Spec FR-4: the wizard explicitly recommends
// true and FilterIncludePreviousSessions = true (Config defaults are // FilterIncludePreviousSessions = true (the Config default is false).
// false). The other four fields (AutoTellTabsHistoryPreload, // The other four fields (AutoTellTabsHistoryPreload, UseCompactDensity,
// UseCompactDensity, PrettierTimestamps, Theme) follow the generic // PrettierTimestamps, Theme) follow the generic null-semantics from
// null-semantics from Spec Z.176: a null pending means the user did // Spec Z.176: a null pending means the user did not touch that control,
// not touch that control, so CommitPending must not write back. They // so CommitPending must not write back. They are read live from
// are read live from Plugin.Config below for the ImGui ref-binding // Plugin.Config below for the ImGui ref-binding but never seeded into
// but never seeded into Pending* without a user gesture. // Pending* without a user gesture.
_state.PendingLoadPreviousSession ??= true;
_state.PendingFilterIncludePreviousSessions ??= true; _state.PendingFilterIncludePreviousSessions ??= true;
ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Title); ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Title);
@@ -361,18 +360,11 @@ public sealed class FirstRunWizard : Window
using (ImRaii.PushColor(ImGuiCol.Text, ForgeBronze)) using (ImRaii.PushColor(ImGuiCol.Text, ForgeBronze))
ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Section_History); ImGui.TextUnformatted(HellionStrings.Wizard_Step3_Section_History);
var loadPrev = _state.PendingLoadPreviousSession ?? true; // One checkbox, not two. LoadPreviousSession was asked for here, shown
if (ImGui.Checkbox(HellionStrings.Wizard_Step3_LoadPreviousSession_Label, ref loadPrev)) // as applied in the summary and written to the config, and no code in
{ // the plugin has ever read it -- so the wizard was collecting a
_state.PendingLoadPreviousSession = loadPrev; // decision and reporting an effect that never happened. Its partner
// Mirror the DataAndPrivacy coupling: turning load-previous on // does the work on its own.
// also turns filter-include on (otherwise old messages bypass
// the filter chain), and turning filter-include off forces
// load-previous off. Same idiom as Ui/SettingsTabs/DataAndPrivacy.cs.
if (loadPrev)
_state.PendingFilterIncludePreviousSessions = true;
}
var filterPrev = _state.PendingFilterIncludePreviousSessions ?? true; var filterPrev = _state.PendingFilterIncludePreviousSessions ?? true;
if ( if (
ImGui.Checkbox( ImGui.Checkbox(
@@ -382,8 +374,6 @@ public sealed class FirstRunWizard : Window
) )
{ {
_state.PendingFilterIncludePreviousSessions = filterPrev; _state.PendingFilterIncludePreviousSessions = filterPrev;
if (!filterPrev)
_state.PendingLoadPreviousSession = false;
} }
ImGui.Spacing(); ImGui.Spacing();
@@ -414,10 +404,6 @@ public sealed class FirstRunWizard : Window
if (ImGui.Checkbox(HellionStrings.Wizard_Step3_UseCompactDensity_Label, ref compact)) if (ImGui.Checkbox(HellionStrings.Wizard_Step3_UseCompactDensity_Label, ref compact))
_state.PendingUseCompactDensity = compact; _state.PendingUseCompactDensity = compact;
var pretty = _state.PendingPrettierTimestamps ?? Plugin.Config.PrettierTimestamps;
if (ImGui.Checkbox(HellionStrings.Wizard_Step3_PrettierTimestamps_Label, ref pretty))
_state.PendingPrettierTimestamps = pretty;
// Theme dropdown — built-ins only. Custom themes are power-user // Theme dropdown — built-ins only. Custom themes are power-user
// territory and would clutter the first-run flow. // territory and would clutter the first-run flow.
var currentSlug = _state.PendingTheme ?? Plugin.Config.Theme; var currentSlug = _state.PendingTheme ?? Plugin.Config.Theme;
@@ -497,10 +483,16 @@ public sealed class FirstRunWizard : Window
string.Format(HellionStrings.Wizard_Step4_Summary_Profile, profileLabel) string.Format(HellionStrings.Wizard_Step4_Summary_Profile, profileLabel)
); );
var historyLabel = // Three states, not two. "Unchanged" is for a step the user
(_state.PendingLoadPreviousSession ?? false) // never entered; an unticked box is a decision that gets
? HellionStrings.Wizard_Step3_LoadPreviousSession_Label // committed, and reporting it as unchanged was the same kind of
: HellionStrings.Wizard_Step4_Summary_Unchanged; // lie the deleted LoadPreviousSession told.
var historyLabel = _state.PendingFilterIncludePreviousSessions switch
{
true => HellionStrings.Wizard_Step3_FilterIncludePreviousSessions_Label,
false => HellionStrings.Wizard_Step4_Summary_Off,
null => HellionStrings.Wizard_Step4_Summary_Unchanged,
};
ImGui.TextWrapped( ImGui.TextWrapped(
string.Format(HellionStrings.Wizard_Step4_Summary_History, historyLabel) string.Format(HellionStrings.Wizard_Step4_Summary_History, historyLabel)
); );
@@ -513,14 +505,11 @@ public sealed class FirstRunWizard : Window
); );
var compact = _state.PendingUseCompactDensity ?? Plugin.Config.UseCompactDensity; var compact = _state.PendingUseCompactDensity ?? Plugin.Config.UseCompactDensity;
var pretty = _state.PendingPrettierTimestamps ?? Plugin.Config.PrettierTimestamps;
var themeSlug = _state.PendingTheme ?? Plugin.Config.Theme; var themeSlug = _state.PendingTheme ?? Plugin.Config.Theme;
var themeName = Plugin.ThemeRegistry.Get(themeSlug).Name; var themeName = Plugin.ThemeRegistry.Get(themeSlug).Name;
var visualParts = new List<string>(); var visualParts = new List<string>();
if (compact) if (compact)
visualParts.Add(HellionStrings.Wizard_Step3_UseCompactDensity_Label); visualParts.Add(HellionStrings.Wizard_Step3_UseCompactDensity_Label);
if (pretty)
visualParts.Add(HellionStrings.Wizard_Step3_PrettierTimestamps_Label);
visualParts.Add(themeName); visualParts.Add(themeName);
ImGui.TextWrapped( ImGui.TextWrapped(
string.Format( string.Format(
@@ -578,9 +567,6 @@ public sealed class FirstRunWizard : Window
break; break;
} }
if (_state.PendingLoadPreviousSession.HasValue)
Plugin.Config.LoadPreviousSession = _state.PendingLoadPreviousSession.Value;
if (_state.PendingFilterIncludePreviousSessions.HasValue) if (_state.PendingFilterIncludePreviousSessions.HasValue)
Plugin.Config.FilterIncludePreviousSessions = _state Plugin.Config.FilterIncludePreviousSessions = _state
.PendingFilterIncludePreviousSessions .PendingFilterIncludePreviousSessions
@@ -594,9 +580,6 @@ public sealed class FirstRunWizard : Window
if (_state.PendingUseCompactDensity.HasValue) if (_state.PendingUseCompactDensity.HasValue)
Plugin.Config.UseCompactDensity = _state.PendingUseCompactDensity.Value; Plugin.Config.UseCompactDensity = _state.PendingUseCompactDensity.Value;
if (_state.PendingPrettierTimestamps.HasValue)
Plugin.Config.PrettierTimestamps = _state.PendingPrettierTimestamps.Value;
if (!string.IsNullOrWhiteSpace(_state.PendingTheme)) if (!string.IsNullOrWhiteSpace(_state.PendingTheme))
{ {
Plugin.Config.Theme = _state.PendingTheme; Plugin.Config.Theme = _state.PendingTheme;
@@ -655,7 +638,10 @@ public sealed class FirstRunWizard : Window
Plugin.Config.PrivacyPersistUnknownChannels = true; Plugin.Config.PrivacyPersistUnknownChannels = true;
Plugin.Config.RetentionEnabled = false; Plugin.Config.RetentionEnabled = false;
Plugin.Config.RetentionPerChannelDays.Clear(); // Clear does not throw during enumeration, it just cuts it short -- the
// retention sweep would then run on half a policy.
lock (Plugin.ConfigMapsLock)
Plugin.Config.RetentionPerChannelDays.Clear();
} }
// Test-only entry point so SelfTests/WizardStateSmokeStep can advance // Test-only entry point so SelfTests/WizardStateSmokeStep can advance
@@ -680,11 +666,9 @@ public sealed class FirstRunWizard : Window
{ {
public int CurrentStep { get; set; } = 1; public int CurrentStep { get; set; } = 1;
public PrivacyProfile? PendingProfile { get; set; } public PrivacyProfile? PendingProfile { get; set; }
public bool? PendingLoadPreviousSession { get; set; }
public bool? PendingFilterIncludePreviousSessions { get; set; } public bool? PendingFilterIncludePreviousSessions { get; set; }
public int? PendingAutoTellTabsHistoryPreload { get; set; } public int? PendingAutoTellTabsHistoryPreload { get; set; }
public bool? PendingUseCompactDensity { get; set; } public bool? PendingUseCompactDensity { get; set; }
public bool? PendingPrettierTimestamps { get; set; }
public string? PendingTheme { get; set; } public string? PendingTheme { get; set; }
} }
} }
@@ -0,0 +1,118 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using HellionChat.Util;
namespace HellionChat.Ui.StyleEngine;
// Slow motes drifting up behind a surface. Character Select+ does its fog with
// an image sequence; this is procedural instead, so there are no assets to ship,
// no texture handles to keep alive, and it takes the theme's accent colour
// rather than whatever was baked into a PNG.
//
// Deliberately faint and deliberately slow. The brief was "modern, not
// overloaded" -- at this opacity it reads as depth rather than as motion, and
// nothing here competes with the text on top of it.
internal sealed class AmbientParticles
{
private readonly struct Mote
{
public required float X { get; init; } // 0..1 across the surface
public required float Speed { get; init; } // fractions of height per second
public required float Radius { get; init; }
public required float Phase { get; init; } // sway offset, keeps them out of lockstep
public required float Sway { get; init; }
}
private readonly Mote[] _motes;
private readonly float[] _y;
private int _lastFrame = -1;
internal AmbientParticles(int count = 70, int seed = 0x48454C4C)
{
// Seeded rather than time-based: the layout is identical every session,
// so a screenshot taken today matches one taken tomorrow.
var rng = new Random(seed);
_motes = new Mote[count];
_y = new float[count];
for (var i = 0; i < count; i++)
{
_motes[i] = new Mote
{
X = (float)rng.NextDouble(),
Speed = 0.014f + (float)rng.NextDouble() * 0.030f,
Radius = 1.1f + (float)rng.NextDouble() * 2.6f,
Phase = (float)rng.NextDouble() * MathF.Tau,
Sway = 0.004f + (float)rng.NextDouble() * 0.010f,
};
_y[i] = (float)rng.NextDouble();
}
}
internal void Draw(
ImDrawListPtr dl,
Vector2 min,
Vector2 max,
uint accentAbgr,
float intensity = 1f
)
{
if (Plugin.Config.ReduceMotion)
return;
var size = max - min;
if (size.X <= 0f || size.Y <= 0f)
return;
// One advance per frame, not per call: the settings window draws this
// once, but a caller that draws two surfaces would otherwise run the
// simulation at double speed.
var frame = ImGui.GetFrameCount();
var advance = frame != _lastFrame;
if (advance)
_lastFrame = frame;
var dt = Math.Min(ImGui.GetIO().DeltaTime, 0.1f);
var time = (float)ImGui.GetTime();
for (var i = 0; i < _motes.Length; i++)
{
var m = _motes[i];
if (advance)
{
_y[i] -= m.Speed * dt;
if (_y[i] < -0.05f)
_y[i] += 1.1f;
}
var sway = MathF.Sin(time * 0.35f + m.Phase) * m.Sway;
var pos = new Vector2(min.X + (m.X + sway) * size.X, min.Y + _y[i] * size.Y);
if (pos.X < min.X || pos.X > max.X || pos.Y < min.Y || pos.Y > max.Y)
continue;
// Fades out at both ends of its travel, so motes appear and vanish
// instead of popping at the edge.
var edge = Math.Clamp(MathF.Min(_y[i], 1f - _y[i]) * 6f, 0f, 1f);
var pulse = 0.55f + 0.45f * MathF.Sin(time * 0.6f + m.Phase);
var radius = m.Radius * Metrics.Scale;
// Halo first, core on top. A flat disc at this size reads as a
// speck of dirt on the screen; the soft ring around it is what makes
// it look lit.
dl.AddCircleFilled(
pos,
radius * 2.1f,
ColourUtil.ApplyAlpha(accentAbgr, 0.075f * edge * pulse * intensity),
10
);
dl.AddCircleFilled(
pos,
radius,
ColourUtil.ApplyAlpha(accentAbgr, 0.20f * edge * pulse * intensity),
10
);
}
}
}
+161 -29
View File
@@ -9,44 +9,42 @@ namespace HellionChat.Ui.StyleEngine;
// Custom-drawing primitives for the v2.x style layer. Callers feed RGBA // Custom-drawing primitives for the v2.x style layer. Callers feed RGBA
// uints (typically resolved via TokenResolver) and these methods convert to // uints (typically resolved via TokenResolver) and these methods convert to
// ABGR before delegating to ImDrawList. Hover-sheen state lives in a small // ABGR before delegating to ImDrawList.
// static dictionary keyed by constant strings — keep keys constant and
// scope to static UI elements so the per-key footprint stays bounded.
internal static class DrawListExtensions internal static class DrawListExtensions
{ {
private const float SheenDurationSeconds = 0.65f; // A1 accent-tint (Variante A): how far the white sweep is pulled toward
private static readonly Dictionary<string, DateTime> SheenStarts = new(); // the element's accent hue. Kept low so the sheen reads as a tinted
// highlight, not a saturated accent flash (effect level "subtle").
private const float SheenTintStrength = 0.35f;
// Peak sheen alpha (low so the highlight stays subtle); DrawHoverSheen applies the falloff.
private const byte SheenPeakAlpha = 0x40;
// Rides the held hover intensity instead of its own timer, so it can no
// longer leak an entry when an element disappears while hovered. Drawn on
// the rising edge only: the alpha falls off as the value climbs, so the
// sweep has faded out by the time the surface underneath is fully in.
// On the way out the surface fades and the sweep simply does not run,
// which is what keeps it from travelling backwards.
public static void DrawHoverSheen( public static void DrawHoverSheen(
this ImDrawListPtr dl, this ImDrawListPtr dl,
Vector2 min, Vector2 min,
Vector2 max, Vector2 max,
uint accentRgba, uint accentRgba,
string elementId, float intensity,
bool hovered bool hovered
) )
{ {
if (!hovered) if (!hovered || intensity <= 0f || intensity >= 1f)
{
// Reset so re-hover restarts the sweep instead of catching the
// tail end of a stale animation.
SheenStarts.Remove(elementId);
return;
}
if (!SheenStarts.TryGetValue(elementId, out var started))
{
started = DateTime.UtcNow;
SheenStarts[elementId] = started;
}
var elapsed = (DateTime.UtcNow - started).TotalSeconds;
if (elapsed > SheenDurationSeconds)
return; return;
var t = (float)(elapsed / SheenDurationSeconds); var t = intensity;
var alpha = (byte)Math.Round(0x40 * (1f - t)); var alpha = (byte)Math.Round(SheenPeakAlpha * (1f - t));
var sheenAbgr = ((uint)alpha << 24) | 0x00FFFFFFu; // Tint the sweep toward the accent hue, then stamp the falloff alpha.
// accentRgba is RGBA; convert to ABGR FIRST or the draw-list swaps R/B.
var accentAbgr = ColourUtil.RgbaToAbgr(accentRgba);
var tintedRgb = ColourUtil.LerpTowardWhite(accentAbgr, SheenTintStrength) & 0x00FFFFFFu;
var sheenAbgr = ((uint)alpha << 24) | tintedRgb;
var sweepX = min.X + (max.X - min.X) * t; var sweepX = min.X + (max.X - min.X) * t;
dl.AddRectFilled( dl.AddRectFilled(
new Vector2(sweepX - 12f, min.Y), new Vector2(sweepX - 12f, min.Y),
@@ -54,11 +52,145 @@ internal static class DrawListExtensions
sheenAbgr, sheenAbgr,
2f 2f
); );
}
// Accent currently unused — reserved for a tinted-sweep variant that // ImGui has no letter-spacing, so tracked text is drawn one glyph at a time
// tracks the element's accent hue. Keeping it in the signature so // with an extra gap between them. Small caps with wide tracking is what
// call-sites don't churn when the tinted path lands. // separates a heading from a label when both use the same font size -- and
_ = accentRgba; // unlike colour, it reads the same in every theme.
//
// One char at a time through a stack buffer, so a heading costs no
// allocation per frame.
public static float DrawTrackedText(
this ImDrawListPtr dl,
Vector2 pos,
ReadOnlySpan<char> text,
uint abgr,
float trackPx
)
{
Span<char> one = stackalloc char[1];
var x = pos.X;
for (var i = 0; i < text.Length; i++)
{
one[0] = text[i];
dl.AddText(new Vector2(x, pos.Y), abgr, one);
x += ImGui.CalcTextSize(one).X;
if (i < text.Length - 1)
x += trackPx;
}
return x - pos.X;
}
public static float MeasureTrackedText(ReadOnlySpan<char> text, float trackPx)
{
Span<char> one = stackalloc char[1];
var w = 0f;
for (var i = 0; i < text.Length; i++)
{
one[0] = text[i];
w += ImGui.CalcTextSize(one).X;
if (i < text.Length - 1)
w += trackPx;
}
return w;
}
// Vertical gradient from a single base colour, brightened at the top and
// darkened at the bottom. Deriving both ends from one tone keeps it working
// across every theme, where a hardcoded pair would only suit one of them.
//
// ImGui cannot put a gradient behind a child window -- ChildBg takes a flat
// colour -- so surfaces that want depth have to paint it themselves.
public static void DrawVerticalGradient(
this ImDrawListPtr dl,
Vector2 min,
Vector2 max,
uint baseAbgr,
float topLift = 0.14f,
float bottomDrop = 0.10f
)
{
// Lerp, not a multiplier: these surfaces sit near black, and scaling a
// channel of 12 by 1.14 lands back on 13.
var top = ColourUtil.LerpTowardWhite(baseAbgr, topLift);
var bottom = ColourUtil.LerpTowardBlack(baseAbgr, bottomDrop);
dl.AddRectFilledMultiColor(min, max, top, top, bottom, bottom);
}
// A vertical tint that stays under the banding threshold.
//
// Banding is quantisation: an alpha ramp has as many steps as it has
// distinct values, and across a tall pane each step owns a stripe tens of
// pixels high. The eye then sharpens those edges into scanlines.
//
// Splitting the ramp into faint layers makes it worse, not better -- each
// layer carries a fraction of the alpha and therefore a fraction of the
// steps, so the stripes get coarser and their blend adds interference.
//
// What actually works is running the whole ramp over a short distance
// instead of a tall one. The same handful of steps then falls within a
// couple of hundred pixels, each stripe is a few pixels tall rather than
// twenty, and they read as a smooth falloff. Below the fade the surface is
// flat, where a constant alpha cannot band at all.
public static void DrawEdgeTint(
this ImDrawListPtr dl,
Vector2 min,
Vector2 max,
uint edgeAbgr,
float fadeHeight,
bool fromBottom = false
)
{
var height = max.Y - min.Y;
if (height <= 0f)
return;
var fade = MathF.Min(fadeHeight, height);
var clear = edgeAbgr & 0x00FFFFFFu;
if (fromBottom)
dl.AddRectFilledMultiColor(
new Vector2(min.X, max.Y - fade),
max,
clear,
clear,
edgeAbgr,
edgeAbgr
);
else
dl.AddRectFilledMultiColor(
min,
new Vector2(max.X, min.Y + fade),
edgeAbgr,
edgeAbgr,
clear,
clear
);
}
// A rule that fades out along its length instead of stopping dead. A hard
// line boxes content in; a fading one suggests a boundary without drawing a
// wall, which is the whole difference between a heading and a header bar.
public static void DrawFadeRule(
this ImDrawListPtr dl,
Vector2 start,
float width,
uint abgr,
float thickness
)
{
var transparent = abgr & 0x00FFFFFFu;
dl.AddRectFilledMultiColor(
start,
new Vector2(start.X + width, start.Y + thickness),
abgr,
transparent,
transparent,
abgr
);
} }
public static void DrawGlowBorder( public static void DrawGlowBorder(
+89 -55
View File
@@ -1,5 +1,4 @@
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using HellionChat.Themes; using HellionChat.Themes;
using HellionChat.Util; using HellionChat.Util;
@@ -33,60 +32,73 @@ internal static class GlobalStyleScope
var childBgWithAlpha = ResolveChildBgAlpha(c.ChildBg, windowOpacity); var childBgWithAlpha = ResolveChildBgAlpha(c.ChildBg, windowOpacity);
var stack = new StackHandle(); var stack = new StackHandle();
stack.PushStyleVar(ImGuiStyleVar.WindowRounding, l.WindowRounding); // Hard contract: every push must be balanced by a pop. The pushes below
stack.PushStyleVar(ImGuiStyleVar.ChildRounding, l.ChildRounding); // are pure, practically non-throwing ImGui calls, but if one ever threw
stack.PushStyleVar(ImGuiStyleVar.PopupRounding, l.PopupRounding); // mid-stack we must still pop what we pushed before the exception escapes,
stack.PushStyleVar(ImGuiStyleVar.FrameRounding, l.FrameRounding); // or the global style stack stays corrupt for every other window this
stack.PushStyleVar(ImGuiStyleVar.GrabRounding, l.GrabRounding); // frame. Dispose then rethrow.
stack.PushStyleVar(ImGuiStyleVar.TabRounding, l.TabRounding); try
stack.PushStyleVar(ImGuiStyleVar.ScrollbarRounding, l.ScrollbarRounding); {
stack.PushStyleVar(ImGuiStyleVar.WindowBorderSize, l.WindowBorderSize); stack.PushStyleVar(ImGuiStyleVar.WindowRounding, l.WindowRounding);
stack.PushStyleVar(ImGuiStyleVar.FrameBorderSize, l.FrameBorderSize); stack.PushStyleVar(ImGuiStyleVar.ChildRounding, l.ChildRounding);
stack.PushStyleVar(ImGuiStyleVar.PopupRounding, l.PopupRounding);
stack.PushStyleVar(ImGuiStyleVar.FrameRounding, l.FrameRounding);
stack.PushStyleVar(ImGuiStyleVar.GrabRounding, l.GrabRounding);
stack.PushStyleVar(ImGuiStyleVar.TabRounding, l.TabRounding);
stack.PushStyleVar(ImGuiStyleVar.ScrollbarRounding, l.ScrollbarRounding);
stack.PushStyleVar(ImGuiStyleVar.WindowBorderSize, l.WindowBorderSize);
stack.PushStyleVar(ImGuiStyleVar.FrameBorderSize, l.FrameBorderSize);
stack.PushColor(ImGuiCol.WindowBg, windowBgWithAlpha); stack.PushColor(ImGuiCol.WindowBg, windowBgWithAlpha);
stack.PushColor(ImGuiCol.ChildBg, childBgWithAlpha); stack.PushColor(ImGuiCol.ChildBg, childBgWithAlpha);
stack.PushColorAbgr(ImGuiCol.PopupBg, a.ChildBg); stack.PushColorAbgr(ImGuiCol.PopupBg, a.ChildBg);
stack.PushColorAbgr(ImGuiCol.Border, a.Border); stack.PushColorAbgr(ImGuiCol.Border, a.Border);
stack.PushColorAbgr(ImGuiCol.BorderShadow, 0u); stack.PushColorAbgr(ImGuiCol.BorderShadow, 0u);
stack.PushColorAbgr(ImGuiCol.FrameBg, a.FrameBg); stack.PushColorAbgr(ImGuiCol.FrameBg, a.FrameBg);
stack.PushColorAbgr(ImGuiCol.FrameBgHovered, a.SurfaceHover); stack.PushColorAbgr(ImGuiCol.FrameBgHovered, a.SurfaceHover);
stack.PushColorAbgr(ImGuiCol.FrameBgActive, a.Surface); stack.PushColorAbgr(ImGuiCol.FrameBgActive, a.Surface);
stack.PushColorAbgr(ImGuiCol.TitleBg, a.WindowBg); stack.PushColorAbgr(ImGuiCol.TitleBg, a.WindowBg);
stack.PushColorAbgr(ImGuiCol.TitleBgActive, a.Identity); stack.PushColorAbgr(ImGuiCol.TitleBgActive, a.Identity);
stack.PushColorAbgr(ImGuiCol.TitleBgCollapsed, a.WindowBg); stack.PushColorAbgr(ImGuiCol.TitleBgCollapsed, a.WindowBg);
stack.PushColorAbgr(ImGuiCol.Button, a.Primary); stack.PushColorAbgr(ImGuiCol.Button, a.Primary);
stack.PushColorAbgr(ImGuiCol.ButtonHovered, a.PrimaryLight); stack.PushColorAbgr(ImGuiCol.ButtonHovered, a.PrimaryLight);
stack.PushColorAbgr(ImGuiCol.ButtonActive, a.PrimaryDark); stack.PushColorAbgr(ImGuiCol.ButtonActive, a.PrimaryDark);
stack.PushColorAbgr(ImGuiCol.Header, a.Surface); stack.PushColorAbgr(ImGuiCol.Header, a.Surface);
stack.PushColorAbgr(ImGuiCol.HeaderHovered, a.SurfaceHover); stack.PushColorAbgr(ImGuiCol.HeaderHovered, a.SurfaceHover);
stack.PushColorAbgr(ImGuiCol.HeaderActive, a.Identity); stack.PushColorAbgr(ImGuiCol.HeaderActive, a.Identity);
stack.PushColorAbgr(ImGuiCol.Tab, a.FrameBg); stack.PushColorAbgr(ImGuiCol.Tab, a.FrameBg);
stack.PushColorAbgr(ImGuiCol.TabHovered, a.PrimaryLight); stack.PushColorAbgr(ImGuiCol.TabHovered, a.PrimaryLight);
stack.PushColorAbgr(ImGuiCol.TabActive, a.Identity); stack.PushColorAbgr(ImGuiCol.TabActive, a.Identity);
stack.PushColorAbgr(ImGuiCol.TabUnfocused, a.ChildBg); stack.PushColorAbgr(ImGuiCol.TabUnfocused, a.ChildBg);
stack.PushColorAbgr(ImGuiCol.TabUnfocusedActive, a.PrimaryDark); stack.PushColorAbgr(ImGuiCol.TabUnfocusedActive, a.PrimaryDark);
stack.PushColorAbgr(ImGuiCol.ScrollbarBg, a.WindowBg); stack.PushColorAbgr(ImGuiCol.ScrollbarBg, a.WindowBg);
stack.PushColorAbgr(ImGuiCol.ScrollbarGrab, a.Surface); stack.PushColorAbgr(ImGuiCol.ScrollbarGrab, a.Surface);
stack.PushColorAbgr(ImGuiCol.ScrollbarGrabHovered, a.AccentLight); stack.PushColorAbgr(ImGuiCol.ScrollbarGrabHovered, a.AccentLight);
stack.PushColorAbgr(ImGuiCol.ScrollbarGrabActive, a.Accent); stack.PushColorAbgr(ImGuiCol.ScrollbarGrabActive, a.Accent);
stack.PushColorAbgr(ImGuiCol.ResizeGrip, a.FrameBg); stack.PushColorAbgr(ImGuiCol.ResizeGrip, a.FrameBg);
stack.PushColorAbgr(ImGuiCol.ResizeGripHovered, a.AccentLight); stack.PushColorAbgr(ImGuiCol.ResizeGripHovered, a.AccentLight);
stack.PushColorAbgr(ImGuiCol.ResizeGripActive, a.Accent); stack.PushColorAbgr(ImGuiCol.ResizeGripActive, a.Accent);
stack.PushColorAbgr(ImGuiCol.CheckMark, a.Primary); stack.PushColorAbgr(ImGuiCol.CheckMark, a.Primary);
stack.PushColorAbgr(ImGuiCol.SliderGrab, a.Primary); stack.PushColorAbgr(ImGuiCol.SliderGrab, a.Primary);
stack.PushColorAbgr(ImGuiCol.SliderGrabActive, a.PrimaryLight); stack.PushColorAbgr(ImGuiCol.SliderGrabActive, a.PrimaryLight);
stack.PushColorAbgr(ImGuiCol.Separator, a.Border); stack.PushColorAbgr(ImGuiCol.Separator, a.Border);
stack.PushColorAbgr(ImGuiCol.SeparatorHovered, a.PrimaryLight); stack.PushColorAbgr(ImGuiCol.SeparatorHovered, a.PrimaryLight);
stack.PushColorAbgr(ImGuiCol.SeparatorActive, a.Primary); stack.PushColorAbgr(ImGuiCol.SeparatorActive, a.Primary);
}
catch
{
stack.Dispose();
throw;
}
return stack; return stack;
} }
@@ -101,24 +113,46 @@ internal static class GlobalStyleScope
return (themeChildBgRgba & 0xFFFFFF00u) | childBgAlpha; return (themeChildBgRgba & 0xFFFFFF00u) | childBgAlpha;
} }
// Counter-based scope: pushes go straight onto the global ImGui style
// stack and we only remember how many of each kind we pushed. Dispose
// pops them in one batched PopStyleColor(count)/PopStyleVar(count) call.
// This replaces the old List<IDisposable> + per-push boxed ImRaii structs
// (44 allocations/frame) with two ints — zero per-frame GC. Every style
// var pushed here is a single-float var, so PopStyleVar(count) is valid;
// both colour paths funnel into one PushStyleColor, so one colour counter
// covers them. Symmetry is the whole contract: the pop counts must equal
// the push counts or the global stack corrupts for every other window.
private sealed class StackHandle : IDisposable private sealed class StackHandle : IDisposable
{ {
private readonly List<IDisposable> _items = new(64); private int _colorCount;
private int _styleVarCount;
internal void PushColor(ImGuiCol slot, uint rgba) => internal void PushColor(ImGuiCol slot, uint rgba)
_items.Add(ImRaii.PushColor(slot, ColourUtil.RgbaToAbgr(rgba))); {
ImGui.PushStyleColor(slot, ColourUtil.RgbaToAbgr(rgba));
_colorCount++;
}
internal void PushColorAbgr(ImGuiCol slot, uint abgr) => internal void PushColorAbgr(ImGuiCol slot, uint abgr)
_items.Add(ImRaii.PushColor(slot, abgr)); {
ImGui.PushStyleColor(slot, abgr);
_colorCount++;
}
internal void PushStyleVar(ImGuiStyleVar var, float value) => internal void PushStyleVar(ImGuiStyleVar var, float value)
_items.Add(ImRaii.PushStyle(var, value)); {
ImGui.PushStyleVar(var, value);
_styleVarCount++;
}
public void Dispose() public void Dispose()
{ {
for (var i = _items.Count - 1; i >= 0; i--) if (_styleVarCount > 0)
_items[i].Dispose(); ImGui.PopStyleVar(_styleVarCount);
_items.Clear(); if (_colorCount > 0)
ImGui.PopStyleColor(_colorCount);
_styleVarCount = 0;
_colorCount = 0;
} }
} }
} }
+93
View File
@@ -0,0 +1,93 @@
using Dalamud.Bindings.ImGui;
using HellionChat.Util;
namespace HellionChat.Ui.StyleEngine;
// Held hover intensity per element, 0..1. Replaces the one-shot sheen timer,
// which stopped after 0.65s while the pointer was still on the row.
//
// Query and advance are deliberately separate. Several SelfTest steps call
// Sidebar.Draw against the live tab list, so the same element can be submitted
// three times in one frame -- twice from a window the mouse is not over. If the
// query advanced the value, the last caller would win and the fade would run
// backwards. Query only marks; BeginFrame does all the moving. Pattern anchor:
// LightlessSync Selune.cs:106-146.
internal static class HoverState
{
private sealed class Entry
{
internal float Value;
internal bool Hovered;
}
private static readonly Dictionary<uint, Entry> Entries = [];
private static readonly List<uint> Evicted = [];
internal static int TrackedCount => Entries.Count;
// Once per frame from Plugin.Draw, before any window renders.
internal static void BeginFrame() => Advance(ImGui.GetIO().DeltaTime);
// SelfTest seam: the alloc step submits many elements inside a single frame,
// so it needs to step the clock without waiting for real frames.
internal static void AdvanceForTest(float deltaTime) => Advance(deltaTime);
internal static void Reset()
{
Entries.Clear();
Evicted.Clear();
}
internal static float Query(uint id, bool hovered)
{
// ReduceMotion short-circuits before touching the map: no entry, no fade,
// nothing to evict. Returning a hard 0/1 is also NaN-safe, which setting
// an infinite rate would not be.
if (Plugin.Config.ReduceMotion)
return hovered ? 1f : 0f;
if (!Entries.TryGetValue(id, out var entry))
{
// Nothing to fade from, so nothing to track. Without this a
// never-hovered element allocates an entry in Query and loses it
// again in the next BeginFrame, every frame, for every row.
if (!hovered)
return 0f;
entry = new Entry();
Entries[id] = entry;
}
// OR, never assign: a second caller in the same frame that is not hovered
// must not cancel the first one that is.
entry.Hovered |= hovered;
return entry.Value;
}
// Never re-entrant: BeginFrame runs once from the draw thread, AdvanceForTest
// only from a self-test step. Evicted is shared, so overlapping calls would
// corrupt it.
private static void Advance(float deltaTime)
{
if (deltaTime <= 0f)
{
// Still clear the flags: a zero-delta frame otherwise carries the
// previous frame's hover state forward.
foreach (var entry in Entries.Values)
entry.Hovered = false;
return;
}
Evicted.Clear();
foreach (var (id, entry) in Entries)
{
entry.Value = HoverMath.Step(entry.Value, entry.Hovered, deltaTime);
if (HoverMath.ShouldEvict(entry.Value, entry.Hovered))
Evicted.Add(id);
entry.Hovered = false;
}
foreach (var id in Evicted)
Entries.Remove(id);
}
}
+109
View File
@@ -0,0 +1,109 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using HellionChat.Util;
namespace HellionChat.Ui.StyleEngine;
// Layout values, authored at 100% display scaling. Draw code reads the scaled
// properties; the Raw constants exist only for the few places that must store or
// compare an unscaled value (Sidebar.GetWidth, the width slider bounds).
//
// Deliberately NOT part of ThemeLayout: that record is serialised into theme
// JSON, and layout customisation is out of scope per the master spec.
internal static class Metrics
{
// --- Sidebar ---
internal const float SidebarIconOnlyWidthRaw = 38f;
internal const float SidebarMinWidthRaw = 40f;
internal const float SidebarMaxWidthRaw = 300f;
internal const float SidebarRowHeightRaw = 32f;
internal const float SidebarPopOutHitWidthRaw = 22f;
internal const float SidebarGreetedHitWidthRaw = 22f;
internal const float SidebarHitSlackRaw = 4f;
internal const float SidebarMinDrawWidthRaw = 2f;
internal const float SidebarUnreadRadiusRaw = 4f;
internal const float SidebarGlyphInsetRaw = 4f;
// --- Input bar ---
internal const float InputBarHeightRaw = 32f;
internal const float InputQuickButtonsReserveRaw = 130f;
// --- Honorific header ---
internal const float HonorificHeightRaw = 30f;
internal const float HonorificInsetRaw = 8f;
internal const float HonorificBracketGapRaw = 6f;
// --- Top tab bar ---
internal const float TopTabUnreadInsetRaw = 4f;
internal const float TopTabPaddingXRaw = 10f;
internal const float TopTabUnderlineRaw = 2f;
// --- Status bar ---
internal const float StatusBorderThicknessRaw = 1f;
internal const float StatusTopSpacerRaw = 2f;
// --- Message list ---
internal const float MessageDummyWidthRaw = 10f;
private static float _cachedScale = 1f;
private static int _cachedFrame = -1;
// GlobalScale resolves to ImGui.GetIO().FontGlobalScale, which the Dalamud
// settings slider moves on every frame while it is dragged. Pinning it once
// per frame keeps a CalcSize and its matching Draw on the same value, and
// saves three native calls per access.
internal static float Scale
{
get
{
var frame = ImGui.GetFrameCount();
if (frame == _cachedFrame)
return _cachedScale;
// Safe variant: GlobalScale throws while the interface manager is
// still coming up, and Block F pulls Metrics into more call sites.
_cachedScale = ImGuiHelpers.GlobalScaleSafe;
_cachedFrame = frame;
return _cachedScale;
}
}
internal static float SidebarIconOnlyWidth => MetricsMath.Scale(SidebarIconOnlyWidthRaw, Scale);
internal static float SidebarRowHeight => MetricsMath.Scale(SidebarRowHeightRaw, Scale);
internal static float SidebarPopOutHitWidth =>
MetricsMath.Scale(SidebarPopOutHitWidthRaw, Scale);
internal static float SidebarGreetedHitWidth =>
MetricsMath.Scale(SidebarGreetedHitWidthRaw, Scale);
internal static float SidebarHitSlack => MetricsMath.Scale(SidebarHitSlackRaw, Scale);
internal static float SidebarMinDrawWidth => MetricsMath.Scale(SidebarMinDrawWidthRaw, Scale);
internal static float SidebarUnreadRadius => MetricsMath.Scale(SidebarUnreadRadiusRaw, Scale);
internal static float SidebarGlyphInset => MetricsMath.Scale(SidebarGlyphInsetRaw, Scale);
internal static float InputBarHeight => MetricsMath.Scale(InputBarHeightRaw, Scale);
internal static float InputQuickButtonsReserve =>
MetricsMath.Scale(InputQuickButtonsReserveRaw, Scale);
internal static float HonorificHeight => MetricsMath.Scale(HonorificHeightRaw, Scale);
internal static float HonorificInset => MetricsMath.Scale(HonorificInsetRaw, Scale);
internal static float HonorificBracketGap => MetricsMath.Scale(HonorificBracketGapRaw, Scale);
internal static float TopTabUnreadInset => MetricsMath.Scale(TopTabUnreadInsetRaw, Scale);
internal static float TopTabPaddingX => MetricsMath.Scale(TopTabPaddingXRaw, Scale);
internal static float TopTabUnderline => MetricsMath.Scale(TopTabUnderlineRaw, Scale);
// Measured, not scaled: the tab has to fit the text, and the font comes from
// Config.FontSizeV2 which display scaling does not feed into.
internal static float TopTabHeight => ImGui.GetTextLineHeight() + MathF.Round(10f * Scale);
internal static float StatusBorderThickness =>
MetricsMath.Scale(StatusBorderThicknessRaw, Scale);
internal static float StatusTopSpacer => MathF.Round(StatusTopSpacerRaw * Scale);
internal static float MessageDummyWidth => MetricsMath.Scale(MessageDummyWidthRaw, Scale);
// Text-dependent heights are measured, never scaled: the font is built from
// Config.FontSizeV2, which GlobalScale does not feed into. StatusBar.Height
// has done it this way since 1.8.x.
internal static float CenterY(float rowHeight) =>
MetricsMath.CenterY(rowHeight, ImGui.GetTextLineHeight());
}
@@ -0,0 +1,88 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using HellionChat.Themes;
using HellionChat.Util;
namespace HellionChat.Ui.StyleEngine;
// The ground under a scrolling surface: gradient, accent wash, drifting motes.
// Pulled out of the settings pane so the chat log can stand on the same floor --
// two windows in one plugin looking like two different products was half of why
// the settings window read as untouched.
//
// Each instance owns its motes, so two surfaces on screen at once do not share
// one drift pattern.
internal sealed class SurfaceBackdrop
{
private readonly ThemeRegistry _themes;
private readonly TokenResolver _resolver;
private readonly AmbientParticles _motes;
internal SurfaceBackdrop(ThemeRegistry themes, TokenResolver resolver, int moteCount = 70)
{
_themes = themes;
_resolver = resolver;
_motes = new AmbientParticles(moteCount);
}
// Call right after entering a child, before its content. GetWindowDrawList
// is then that child's own list, so everything lands behind the content
// without a channel split.
internal void Draw(
float accentWashHeight = 0.38f,
float darken = 0f,
float moteIntensity = 1f,
float strength = 1f,
float? opacityOverride = null
)
{
var dl = ImGui.GetWindowDrawList();
var min = ImGui.GetWindowPos();
var max = min + ImGui.GetWindowSize();
var colors = _themes.Active.Colors;
// Modulation, not a second ground. The window has already painted its
// own background; filling the same area again stacked two layers and
// made a deliberately translucent window read as solid. Worse, an opaque
// fill had to carry the whole gradient by itself, which is what produced
// the banding -- a shallow ramp across an opaque surface crosses few
// enough 8-bit values that each covers a visible stripe.
//
// Near-transparent white over black instead: the window colour stays
// visible underneath, the ramp only tints it, and the game showing
// through breaks up any step that is left.
// Read from the pushed WindowBg by default, because BgAlpha only reaches
// the window fill and not the draw list. A window that overrides its own
// opacity has to say so -- SetNextWindowBgAlpha is invisible from here,
// so the settings pane would otherwise tint itself for the chat window's
// transparency while being fully opaque.
var opacity =
opacityOverride ?? ((ImGui.GetColorU32(ImGuiCol.WindowBg) >> 24) & 0xFFu) / 255f;
// Two short fades rather than one tall ramp: light from the top edge,
// shadow gathering at the bottom, flat in between. A ramp stretched over
// the full height is exactly the case that bands, because its handful of
// alpha steps each cover twenty-odd pixels.
var fade = MathF.Min(190f * Metrics.Scale, (max.Y - min.Y) * 0.45f);
dl.DrawEdgeTint(min, max, 0x00FFFFFFu | ((uint)(0x26 * strength * opacity) << 24), fade);
dl.DrawEdgeTint(min, max, (uint)(0x44 * strength * opacity) << 24, fade, fromBottom: true);
if (darken > 0f)
dl.AddRectFilled(min, max, (uint)(0xFF * darken * opacity) << 24);
var accent = ColourUtil.RgbaToAbgr(_resolver.Resolve(Token.AccentPrimary, colors));
if (accentWashHeight > 0f)
dl.AddRectFilledMultiColor(
min,
new Vector2(max.X, min.Y + (max.Y - min.Y) * accentWashHeight),
ColourUtil.ApplyAlpha(accent, 0.075f * opacity),
ColourUtil.ApplyAlpha(accent, 0.035f * opacity),
accent & 0x00FFFFFFu,
accent & 0x00FFFFFFu
);
if (moteIntensity > 0f)
_motes.Draw(dl, min, max, accent, moteIntensity * opacity);
}
}

Some files were not shown because too many files have changed in this diff Show More