diff --git a/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs b/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs
index 1087efd51d..1904e2b340 100644
--- a/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasFilterBoundUserInterface.cs
@@ -1,8 +1,8 @@
using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Trinary.Components;
+using Content.Shared.Localizations;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
namespace Content.Client.Atmos.UI
{
@@ -50,7 +50,7 @@ namespace Content.Client.Atmos.UI
private void OnFilterTransferRatePressed(string value)
{
- float rate = float.TryParse(value, out var parsed) ? parsed : 0f;
+ var rate = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
SendMessage(new GasFilterChangeRateMessage(rate));
}
diff --git a/Content.Client/Atmos/UI/GasFilterWindow.xaml b/Content.Client/Atmos/UI/GasFilterWindow.xaml
index 6963a71d3d..861d447308 100644
--- a/Content.Client/Atmos/UI/GasFilterWindow.xaml
+++ b/Content.Client/Atmos/UI/GasFilterWindow.xaml
@@ -9,7 +9,7 @@
-
+
diff --git a/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs b/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs
index 74cef4b39b..28766c688a 100644
--- a/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs
+++ b/Content.Client/Atmos/UI/GasFilterWindow.xaml.cs
@@ -49,7 +49,7 @@ namespace Content.Client.Atmos.UI
public void SetTransferRate(float rate)
{
- FilterTransferRateInput.Text = rate.ToString(CultureInfo.InvariantCulture);
+ FilterTransferRateInput.Text = rate.ToString(CultureInfo.CurrentCulture);
}
public void SetFilterStatus(bool enabled)
diff --git a/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs b/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs
index cfec8c3495..709c06517c 100644
--- a/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs
+++ b/Content.Client/Atmos/UI/GasMixerBoundUserInteface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Trinary.Components;
+using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -47,7 +48,7 @@ namespace Content.Client.Atmos.UI
private void OnMixerOutputPressurePressed(string value)
{
- var pressure = float.TryParse(value, out var parsed) ? parsed : 0f;
+ var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
if (pressure > MaxPressure)
pressure = MaxPressure;
@@ -57,7 +58,7 @@ namespace Content.Client.Atmos.UI
private void OnMixerSetPercentagePressed(string value)
{
// We don't need to send both nodes because it's just 100.0f - node
- var node = float.TryParse(value, out var parsed) ? parsed : 1.0f;
+ var node = UserInputParser.TryFloat(value, out var parsed) ? parsed : 1.0f;
node = Math.Clamp(node, 0f, 100.0f);
diff --git a/Content.Client/Atmos/UI/GasMixerWindow.xaml b/Content.Client/Atmos/UI/GasMixerWindow.xaml
index 7e74348962..cebca59747 100644
--- a/Content.Client/Atmos/UI/GasMixerWindow.xaml
+++ b/Content.Client/Atmos/UI/GasMixerWindow.xaml
@@ -10,7 +10,7 @@
-
+
diff --git a/Content.Client/Atmos/UI/GasMixerWindow.xaml.cs b/Content.Client/Atmos/UI/GasMixerWindow.xaml.cs
index f3f8293bc8..8ff7b8cfa3 100644
--- a/Content.Client/Atmos/UI/GasMixerWindow.xaml.cs
+++ b/Content.Client/Atmos/UI/GasMixerWindow.xaml.cs
@@ -45,7 +45,7 @@ namespace Content.Client.Atmos.UI
SetMaxPressureButton.OnPressed += _ =>
{
- MixerPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.InvariantCulture);
+ MixerPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
SetOutputPressureButton.Disabled = false;
};
@@ -69,16 +69,16 @@ namespace Content.Client.Atmos.UI
public void SetOutputPressure(float pressure)
{
- MixerPressureOutputInput.Text = pressure.ToString(CultureInfo.InvariantCulture);
+ MixerPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
}
public void SetNodePercentages(float nodeOne)
{
nodeOne *= 100.0f;
- MixerNodeOneInput.Text = nodeOne.ToString("0.##", CultureInfo.InvariantCulture);
+ MixerNodeOneInput.Text = nodeOne.ToString("0.##", CultureInfo.CurrentCulture);
float nodeTwo = 100.0f - nodeOne;
- MixerNodeTwoInput.Text = nodeTwo.ToString("0.##", CultureInfo.InvariantCulture);
+ MixerNodeTwoInput.Text = nodeTwo.ToString("0.##", CultureInfo.CurrentCulture);
}
public void SetMixerStatus(bool enabled)
diff --git a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs
index 69cd6cb587..6eba2e0d21 100644
--- a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Binary.Components;
+using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -46,7 +47,7 @@ namespace Content.Client.Atmos.UI
private void OnPumpOutputPressurePressed(string value)
{
- float pressure = float.TryParse(value, out var parsed) ? parsed : 0f;
+ var pressure = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
if (pressure > MaxPressure) pressure = MaxPressure;
SendMessage(new GasPressurePumpChangeOutputPressureMessage(pressure));
diff --git a/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml b/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml
index 8be7a84e86..a0896a7b41 100644
--- a/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml
+++ b/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml
@@ -11,7 +11,7 @@
-
+
diff --git a/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml.cs b/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml.cs
index 22d8a1e9df..b5ffcd1072 100644
--- a/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml.cs
+++ b/Content.Client/Atmos/UI/GasPressurePumpWindow.xaml.cs
@@ -39,14 +39,14 @@ namespace Content.Client.Atmos.UI
SetMaxPressureButton.OnPressed += _ =>
{
- PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.InvariantCulture);
+ PumpPressureOutputInput.Text = Atmospherics.MaxOutputPressure.ToString(CultureInfo.CurrentCulture);
SetOutputPressureButton.Disabled = false;
};
}
public void SetOutputPressure(float pressure)
{
- PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.InvariantCulture);
+ PumpPressureOutputInput.Text = pressure.ToString(CultureInfo.CurrentCulture);
}
public void SetPumpStatus(bool enabled)
diff --git a/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs
index 5ccc79a410..1b39306181 100644
--- a/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs
+++ b/Content.Client/Atmos/UI/GasVolumePumpBoundUserInterface.cs
@@ -1,5 +1,6 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Binary.Components;
+using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -46,7 +47,7 @@ namespace Content.Client.Atmos.UI
private void OnPumpTransferRatePressed(string value)
{
- var rate = float.TryParse(value, out var parsed) ? parsed : 0f;
+ var rate = UserInputParser.TryFloat(value, out var parsed) ? parsed : 0f;
if (rate > MaxTransferRate)
rate = MaxTransferRate;
diff --git a/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml b/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml
index 93ebc35a93..7275b34b3a 100644
--- a/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml
+++ b/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml
@@ -11,7 +11,7 @@
-
+
diff --git a/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml.cs b/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml.cs
index 2ca567302d..83a530c5c5 100644
--- a/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml.cs
+++ b/Content.Client/Atmos/UI/GasVolumePumpWindow.xaml.cs
@@ -39,14 +39,14 @@ namespace Content.Client.Atmos.UI
SetMaxRateButton.OnPressed += _ =>
{
- PumpTransferRateInput.Text = Atmospherics.MaxTransferRate.ToString(CultureInfo.InvariantCulture);
+ PumpTransferRateInput.Text = Atmospherics.MaxTransferRate.ToString(CultureInfo.CurrentCulture);
SetTransferRateButton.Disabled = false;
};
}
public void SetTransferRate(float rate)
{
- PumpTransferRateInput.Text = rate.ToString(CultureInfo.InvariantCulture);
+ PumpTransferRateInput.Text = rate.ToString(CultureInfo.CurrentCulture);
}
public void SetPumpStatus(bool enabled)
diff --git a/Content.Shared/Localizations/UserInputParser.cs b/Content.Shared/Localizations/UserInputParser.cs
new file mode 100644
index 0000000000..0aaf44af16
--- /dev/null
+++ b/Content.Shared/Localizations/UserInputParser.cs
@@ -0,0 +1,54 @@
+using System.Globalization;
+
+namespace Content.Shared.Localizations;
+
+///
+/// Helpers for user input parsing.
+///
+///
+/// A wrapper around the API,
+/// with the goal of trying more options to make the user input parsing less restrictive.
+/// For culture-invariant parsing use .
+///
+public static class UserInputParser
+{
+ private static readonly NumberFormatInfo[] StandardDecimalNumberFormats = new[]
+ {
+ new NumberFormatInfo()
+ {
+ NumberDecimalSeparator = "."
+ },
+ new NumberFormatInfo()
+ {
+ NumberDecimalSeparator = ","
+ }
+ };
+
+ public static bool TryFloat(ReadOnlySpan text, out float result)
+ {
+ foreach (var format in StandardDecimalNumberFormats)
+ {
+ if (float.TryParse(text, NumberStyles.Integer | NumberStyles.AllowDecimalPoint, format, out result))
+ {
+ return true;
+ }
+ }
+
+ result = 0f;
+ return false;
+ }
+
+ public static bool TryDouble(ReadOnlySpan text, out double result)
+ {
+ foreach (var format in StandardDecimalNumberFormats)
+ {
+ if (double.TryParse(text, NumberStyles.Integer | NumberStyles.AllowDecimalPoint, format, out result))
+ {
+ return true;
+ }
+ }
+
+ result = 0d;
+ return false;
+ }
+}
diff --git a/Content.Tests/Shared/Localizations/UserInputParserTest.cs b/Content.Tests/Shared/Localizations/UserInputParserTest.cs
new file mode 100644
index 0000000000..883fb0035d
--- /dev/null
+++ b/Content.Tests/Shared/Localizations/UserInputParserTest.cs
@@ -0,0 +1,41 @@
+using Content.Shared.Localizations;
+using NUnit.Framework;
+
+namespace Content.Tests.Shared.Localizations
+{
+ [TestFixture]
+ public sealed class UserInputParserTest
+ {
+ [Test]
+ [TestCase("1234.56", 1234.56f, true)]
+ [TestCase("1234,56", 1234.56f, true)]
+ [TestCase(" +1234.56 ", 1234.56f, true)]
+ [TestCase(" -1234.56 ", -1234.56f, true)]
+ [TestCase("1234.56e7", 0f, false)]
+ [TestCase("1,234.56", 0f, false)]
+ [TestCase("1 234,56", 0f, false)]
+ public void TryFloatTest(string input, float expectedOutput, bool expectedResult)
+ {
+ var result = UserInputParser.TryFloat(input, out var output);
+
+ Assert.That(result, Is.EqualTo(expectedResult));
+ Assert.That(output, Is.EqualTo(expectedOutput).Within(float.Epsilon));
+ }
+
+ [Test]
+ [TestCase("1234.56", 1234.56d, true)]
+ [TestCase("1234,56", 1234.56d, true)]
+ [TestCase(" +1234.56 ", 1234.56d, true)]
+ [TestCase(" -1234.56 ", -1234.56d, true)]
+ [TestCase("1234.56e7", 0d, false)]
+ [TestCase("1,234.56", 0d, false)]
+ [TestCase("1 234,56", 0d, false)]
+ public void TryDoubleTest(string input, double expectedOutput, bool expectedResult)
+ {
+ var result = UserInputParser.TryDouble(input, out var output);
+
+ Assert.That(result, Is.EqualTo(expectedResult));
+ Assert.That(output, Is.EqualTo(expectedOutput).Within(double.Epsilon));
+ }
+ }
+}