2020-08-17 10:00:56 -07:00
|
|
|
|
// Copyright (c) Microsoft Corporation
|
|
|
|
|
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
|
|
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
|
2021-01-20 11:38:52 +01:00
|
|
|
|
namespace Microsoft.PowerToys.Run.Plugin.Calculator
|
2020-08-17 10:00:56 -07:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Tries to convert all numbers in a text from one culture format to another.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class NumberTranslator
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly CultureInfo sourceCulture;
|
|
|
|
|
|
private readonly CultureInfo targetCulture;
|
|
|
|
|
|
private readonly Regex splitRegexForSource;
|
|
|
|
|
|
private readonly Regex splitRegexForTarget;
|
|
|
|
|
|
|
|
|
|
|
|
private NumberTranslator(CultureInfo sourceCulture, CultureInfo targetCulture)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.sourceCulture = sourceCulture;
|
|
|
|
|
|
this.targetCulture = targetCulture;
|
|
|
|
|
|
|
|
|
|
|
|
splitRegexForSource = GetSplitRegex(this.sourceCulture);
|
|
|
|
|
|
splitRegexForTarget = GetSplitRegex(this.targetCulture);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2021-01-25 10:51:32 +01:00
|
|
|
|
/// Create a new <see cref="NumberTranslator"/>.
|
2020-08-17 10:00:56 -07:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="sourceCulture">source culture</param>
|
|
|
|
|
|
/// <param name="targetCulture">target culture</param>
|
|
|
|
|
|
/// <returns>Number translator for target culture</returns>
|
|
|
|
|
|
public static NumberTranslator Create(CultureInfo sourceCulture, CultureInfo targetCulture)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sourceCulture == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new ArgumentNullException(paramName: nameof(sourceCulture));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (targetCulture == null)
|
|
|
|
|
|
{
|
2021-01-25 10:51:32 +01:00
|
|
|
|
throw new ArgumentNullException(paramName: nameof(targetCulture));
|
2020-08-17 10:00:56 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-25 10:51:32 +01:00
|
|
|
|
return new NumberTranslator(sourceCulture, targetCulture);
|
2020-08-17 10:00:56 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Translate from source to target culture.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="input">input string to translate</param>
|
|
|
|
|
|
/// <returns>translated string</returns>
|
|
|
|
|
|
public string Translate(string input)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Translate(input, sourceCulture, targetCulture, splitRegexForSource);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Translate from target to source culture.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="input">input string to translate back to source culture</param>
|
|
|
|
|
|
/// <returns>source culture string</returns>
|
|
|
|
|
|
public string TranslateBack(string input)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Translate(input, targetCulture, sourceCulture, splitRegexForTarget);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex)
|
|
|
|
|
|
{
|
|
|
|
|
|
var outputBuilder = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
|
|
string[] tokens = splitRegex.Split(input);
|
|
|
|
|
|
foreach (string token in tokens)
|
|
|
|
|
|
{
|
2023-09-12 11:22:34 +01:00
|
|
|
|
int leadingZeroCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Count leading zero characters.
|
|
|
|
|
|
foreach (char c in token)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (c != '0')
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
leadingZeroCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// number is all zero characters. no need to add zero characters at the end.
|
|
|
|
|
|
if (token.Length == leadingZeroCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
leadingZeroCount = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-17 10:00:56 -07:00
|
|
|
|
decimal number;
|
2023-09-12 11:22:34 +01:00
|
|
|
|
|
2020-08-17 10:00:56 -07:00
|
|
|
|
outputBuilder.Append(
|
|
|
|
|
|
decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number)
|
2023-09-12 11:22:34 +01:00
|
|
|
|
? (new string('0', leadingZeroCount) + number.ToString(cultureTo))
|
2020-08-17 10:00:56 -07:00
|
|
|
|
: token);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return outputBuilder.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static Regex GetSplitRegex(CultureInfo culture)
|
|
|
|
|
|
{
|
2022-06-02 11:44:12 +02:00
|
|
|
|
var splitPattern = $"((?:\\d|[a-fA-F]|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}";
|
2020-08-17 10:00:56 -07:00
|
|
|
|
if (!string.IsNullOrEmpty(culture.NumberFormat.NumberGroupSeparator))
|
|
|
|
|
|
{
|
|
|
|
|
|
splitPattern += $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
splitPattern += ")+)";
|
|
|
|
|
|
return new Regex(splitPattern);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|