mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
Make DateTime/TimeSpan localizable
This commit is contained in:
parent
edb13c18e3
commit
18c1084fe3
4 changed files with 154 additions and 70 deletions
|
|
@ -2,6 +2,7 @@ using System.Numerics;
|
|||
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
|
|
@ -288,8 +289,8 @@ internal sealed partial class ActiveNotification
|
|||
ImGui.PushStyleColor(ImGuiCol.Text, NotificationConstants.WhenTextColor);
|
||||
ImGui.TextUnformatted(
|
||||
ImGui.IsWindowHovered(ImGuiHoveredFlags.AllowWhenBlockedByActiveItem)
|
||||
? this.CreatedAt.FormatAbsoluteDateTime()
|
||||
: this.CreatedAt.FormatRelativeDateTime());
|
||||
? this.CreatedAt.LocAbsolute()
|
||||
: this.CreatedAt.LocRelativePastLong());
|
||||
ImGui.PopStyleColor();
|
||||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
|
@ -304,7 +305,7 @@ internal sealed partial class ActiveNotification
|
|||
|
||||
ltOffset.X = height;
|
||||
|
||||
var agoText = this.CreatedAt.FormatRelativeDateTimeShort();
|
||||
var agoText = this.CreatedAt.LocRelativePastShort();
|
||||
var agoSize = ImGui.CalcTextSize(agoText);
|
||||
rtOffset.X -= agoSize.X;
|
||||
ImGui.SetCursorPos(rtOffset with { Y = NotificationConstants.ScaledWindowPadding });
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
|
||||
using Dalamud.Interface.Colors;
|
||||
|
|
@ -91,31 +90,6 @@ internal static class NotificationConstants
|
|||
/// <summary>Color for the background progress bar (determinate progress only).</summary>
|
||||
public static readonly Vector4 BackgroundProgressColorMin = new(1f, 1f, 1f, 0.05f);
|
||||
|
||||
/// <summary>Gets the relative time format strings.</summary>
|
||||
private static readonly (TimeSpan MinSpan, string? FormatString)[] RelativeFormatStrings =
|
||||
{
|
||||
(TimeSpan.FromDays(7), null),
|
||||
(TimeSpan.FromDays(2), "{0:%d} days ago"),
|
||||
(TimeSpan.FromDays(1), "yesterday"),
|
||||
(TimeSpan.FromHours(2), "{0:%h} hours ago"),
|
||||
(TimeSpan.FromHours(1), "an hour ago"),
|
||||
(TimeSpan.FromMinutes(2), "{0:%m} minutes ago"),
|
||||
(TimeSpan.FromMinutes(1), "a minute ago"),
|
||||
(TimeSpan.FromSeconds(2), "{0:%s} seconds ago"),
|
||||
(TimeSpan.FromSeconds(1), "a second ago"),
|
||||
(TimeSpan.MinValue, "just now"),
|
||||
};
|
||||
|
||||
/// <summary>Gets the relative time format strings.</summary>
|
||||
private static readonly (TimeSpan MinSpan, string FormatString)[] RelativeFormatStringsShort =
|
||||
{
|
||||
(TimeSpan.FromDays(1), "{0:%d}d"),
|
||||
(TimeSpan.FromHours(1), "{0:%h}h"),
|
||||
(TimeSpan.FromMinutes(1), "{0:%m}m"),
|
||||
(TimeSpan.FromSeconds(1), "{0:%s}s"),
|
||||
(TimeSpan.MinValue, "now"),
|
||||
};
|
||||
|
||||
/// <summary>Gets the scaled padding of the window (dot(.) in the above diagram).</summary>
|
||||
public static float ScaledWindowPadding => MathF.Round(16 * ImGuiHelpers.GlobalScale);
|
||||
|
||||
|
|
@ -142,46 +116,6 @@ internal static class NotificationConstants
|
|||
/// <summary>Gets the string format of the initiator name field, if the initiator is unloaded.</summary>
|
||||
public static string UnloadedInitiatorNameFormat => "{0} (unloaded)";
|
||||
|
||||
/// <summary>Formats an instance of <see cref="DateTime"/> as a relative time.</summary>
|
||||
/// <param name="when">When.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
public static string FormatRelativeDateTime(this DateTime when)
|
||||
{
|
||||
var ts = DateTime.Now - when;
|
||||
foreach (var (minSpan, formatString) in RelativeFormatStrings)
|
||||
{
|
||||
if (ts < minSpan)
|
||||
continue;
|
||||
if (formatString is null)
|
||||
break;
|
||||
return string.Format(formatString, ts);
|
||||
}
|
||||
|
||||
return when.FormatAbsoluteDateTime();
|
||||
}
|
||||
|
||||
/// <summary>Formats an instance of <see cref="DateTime"/> as an absolute time.</summary>
|
||||
/// <param name="when">When.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
public static string FormatAbsoluteDateTime(this DateTime when) => $"{when:G}";
|
||||
|
||||
/// <summary>Formats an instance of <see cref="DateTime"/> as a relative time.</summary>
|
||||
/// <param name="when">When.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
public static string FormatRelativeDateTimeShort(this DateTime when)
|
||||
{
|
||||
var ts = DateTime.Now - when;
|
||||
foreach (var (minSpan, formatString) in RelativeFormatStringsShort)
|
||||
{
|
||||
if (ts < minSpan)
|
||||
continue;
|
||||
return string.Format(formatString, ts);
|
||||
}
|
||||
|
||||
Debug.Assert(false, "must not reach here");
|
||||
return "???";
|
||||
}
|
||||
|
||||
/// <summary>Gets the color corresponding to the notification type.</summary>
|
||||
/// <param name="type">The notification type.</param>
|
||||
/// <returns>The corresponding color.</returns>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ public class Localization : IServiceType
|
|||
/// <param name="useEmbedded">Use embedded loc resource files.</param>
|
||||
public Localization(string locResourceDirectory, string locResourcePrefix = "", bool useEmbedded = false)
|
||||
{
|
||||
this.DalamudLanguageCultureInfo = CultureInfo.InvariantCulture;
|
||||
this.locResourceDirectory = locResourceDirectory;
|
||||
this.locResourcePrefix = locResourcePrefix;
|
||||
this.useEmbedded = useEmbedded;
|
||||
|
|
@ -61,7 +62,24 @@ public class Localization : IServiceType
|
|||
/// <summary>
|
||||
/// Event that occurs when the language is changed.
|
||||
/// </summary>
|
||||
public event LocalizationChangedDelegate LocalizationChanged;
|
||||
public event LocalizationChangedDelegate? LocalizationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="CultureInfo"/> that corresponds to the language configured from Dalamud Settings.
|
||||
/// </summary>
|
||||
public CultureInfo DalamudLanguageCultureInfo { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <see cref="CultureInfo"/> that corresponds to <paramref cref="langCode"/>.
|
||||
/// </summary>
|
||||
/// <param name="langCode">The language code which should be in <see cref="ApplicableLangCodes"/>.</param>
|
||||
/// <returns>The corresponding instance of <see cref="CultureInfo"/>.</returns>
|
||||
public static CultureInfo GetCultureInfoFromLangCode(string langCode) =>
|
||||
CultureInfo.GetCultureInfo(langCode switch
|
||||
{
|
||||
"tw" => "zh-tw",
|
||||
_ => langCode,
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Search the set-up localization data for the provided assembly for the given string key and return it.
|
||||
|
|
@ -108,6 +126,7 @@ public class Localization : IServiceType
|
|||
/// </summary>
|
||||
public void SetupWithFallbacks()
|
||||
{
|
||||
this.DalamudLanguageCultureInfo = CultureInfo.InvariantCulture;
|
||||
this.LocalizationChanged?.Invoke(FallbackLangCode);
|
||||
Loc.SetupWithFallbacks(this.assembly);
|
||||
}
|
||||
|
|
@ -124,6 +143,7 @@ public class Localization : IServiceType
|
|||
return;
|
||||
}
|
||||
|
||||
this.DalamudLanguageCultureInfo = GetCultureInfoFromLangCode(langCode);
|
||||
this.LocalizationChanged?.Invoke(langCode);
|
||||
|
||||
try
|
||||
|
|
|
|||
129
Dalamud/Utility/DateTimeSpanExtensions.cs
Normal file
129
Dalamud/Utility/DateTimeSpanExtensions.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
using CheapLoc;
|
||||
|
||||
using Dalamud.Logging.Internal;
|
||||
|
||||
namespace Dalamud.Utility;
|
||||
|
||||
/// <summary>
|
||||
/// Utility functions for <see cref="DateTime"/> and <see cref="TimeSpan"/>.
|
||||
/// </summary>
|
||||
internal static class DateTimeSpanExtensions
|
||||
{
|
||||
private static readonly ModuleLog Log = new(nameof(DateTimeSpanExtensions));
|
||||
|
||||
private static ParsedRelativeFormatStrings? relativeFormatStringLong;
|
||||
|
||||
private static ParsedRelativeFormatStrings? relativeFormatStringShort;
|
||||
|
||||
/// <summary>Formats an instance of <see cref="DateTime"/> as a localized absolute time.</summary>
|
||||
/// <param name="when">When.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
/// <remarks>The string will be formatted according to Square Enix Account region settings, if Dalamud default
|
||||
/// language is English.</remarks>
|
||||
public static unsafe string LocAbsolute(this DateTime when)
|
||||
{
|
||||
var culture = Service<Localization>.GetNullable()?.DalamudLanguageCultureInfo ?? CultureInfo.InvariantCulture;
|
||||
if (!Equals(culture, CultureInfo.InvariantCulture))
|
||||
return when.ToString("G", culture);
|
||||
|
||||
var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance();
|
||||
var region = 0;
|
||||
if (framework is not null)
|
||||
region = framework->Region;
|
||||
switch (region)
|
||||
{
|
||||
case 0: // jp
|
||||
default:
|
||||
return when.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
case 1: // na
|
||||
return when.ToString("MM/dd/yyyy HH:mm:ss");
|
||||
case 2: // eu
|
||||
return when.ToString("dd-mm-yyyy HH:mm:ss");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Formats an instance of <see cref="DateTime"/> as a localized relative time.</summary>
|
||||
/// <param name="when">When.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
public static string LocRelativePastLong(this DateTime when)
|
||||
{
|
||||
var loc = Loc.Localize(
|
||||
"DateTimeSpanExtensions.RelativeFormatStringsLong",
|
||||
"172800,{0:%d} days ago\n86400,yesterday\n7200,{0:%h} hours ago\n3600,an hour ago\n120,{0:%m} minutes ago\n60,a minute ago\n2,{0:%s} seconds ago\n1,a second ago\n-Infinity,just now");
|
||||
Debug.Assert(loc != null, "loc != null");
|
||||
|
||||
if (relativeFormatStringLong?.FormatStringLoc != loc)
|
||||
relativeFormatStringLong ??= new(loc);
|
||||
|
||||
return relativeFormatStringLong.Format(DateTime.Now - when);
|
||||
}
|
||||
|
||||
/// <summary>Formats an instance of <see cref="DateTime"/> as a localized relative time.</summary>
|
||||
/// <param name="when">When.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
public static string LocRelativePastShort(this DateTime when)
|
||||
{
|
||||
var loc = Loc.Localize(
|
||||
"DateTimeSpanExtensions.RelativeFormatStringsShort",
|
||||
"86400,{0:%d}d\n3600,{0:%h}h\n60,{0:%m}m\n1,{0:%s}s\n-Infinity,now");
|
||||
Debug.Assert(loc != null, "loc != null");
|
||||
|
||||
if (relativeFormatStringShort?.FormatStringLoc != loc)
|
||||
relativeFormatStringShort = new(loc);
|
||||
|
||||
return relativeFormatStringShort.Format(DateTime.Now - when);
|
||||
}
|
||||
|
||||
private sealed class ParsedRelativeFormatStrings
|
||||
{
|
||||
private readonly List<(float MinSeconds, string FormatString)> formatStrings = new();
|
||||
|
||||
public ParsedRelativeFormatStrings(string value)
|
||||
{
|
||||
this.FormatStringLoc = value;
|
||||
foreach (var line in value.Split("\n"))
|
||||
{
|
||||
var sep = line.IndexOf(',');
|
||||
if (sep < 0)
|
||||
{
|
||||
Log.Error("A line without comma has been found: {line}", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!float.TryParse(
|
||||
line.AsSpan(0, sep),
|
||||
NumberStyles.Float,
|
||||
CultureInfo.InvariantCulture,
|
||||
out var seconds))
|
||||
{
|
||||
Log.Error("Could not parse the duration: {line}", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.formatStrings.Add((seconds, line[(sep + 1)..]));
|
||||
}
|
||||
|
||||
this.formatStrings.Sort((a, b) => b.MinSeconds.CompareTo(a.MinSeconds));
|
||||
}
|
||||
|
||||
public string FormatStringLoc { get; }
|
||||
|
||||
/// <summary>Formats an instance of <see cref="TimeSpan"/> as a localized string.</summary>
|
||||
/// <param name="ts">The duration.</param>
|
||||
/// <returns>The formatted string.</returns>
|
||||
public string Format(TimeSpan ts)
|
||||
{
|
||||
foreach (var (minSeconds, formatString) in this.formatStrings)
|
||||
{
|
||||
if (ts.TotalSeconds >= minSeconds)
|
||||
return string.Format(formatString, ts);
|
||||
}
|
||||
|
||||
return this.formatStrings[^1].FormatString.Format(ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue