Skip to content

Commit

Permalink
Improvements to handling multiple number formats (#2623)
Browse files Browse the repository at this point in the history
* Recognize "hundert" and "tausend" by themselves as numbers in German.

* FP filter for year in float number.

* Adding sample lists of variant cultures that differ from default in handling number separators.

* Cleanup of multi decimal separators code.

* Fixing bug in assigning subtype for numbers like "1.2b".

* Exposing Spanish.Mexican as culture as workaround before refactoring of model config for culture/requestedCulture.

* Extra separator variant support and test cases in es-MX.

* Re-gen resources across platforms.
  • Loading branch information
tellarin committed May 29, 2021
1 parent 22ed451 commit 4201b79
Show file tree
Hide file tree
Showing 74 changed files with 785 additions and 191 deletions.
4 changes: 2 additions & 2 deletions .NET/Microsoft.Recognizers.Definitions.Common/BaseNumbers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public static class BaseNumbers
{
public const string NumberReplaceToken = @"@builtin.num";
public const string FractionNumberReplaceToken = @"@builtin.num.fraction";
public static readonly Func<string, string, string> IntegerRegexDefinition = (placeholder, thousandsmark) => $@"(((?<!\d+\s*)-\s*)|((?<=\b)(?<!(\d+\.|\d+,))))\d{{1,3}}({thousandsmark}\d{{3}})+(?={placeholder})";
public static readonly Func<string, string, string> IntegerRegexDefinition = (placeholder, thousandsmark) => $@"(((?<!\d+\s*)-\s*)|((?<=\b)(?<!\d+[\.,])))\d{{1,3}}({thousandsmark}\d{{3}})+(?={placeholder})";
public const string FractionNotationRegex = @"((((?<=\W|^)-\s*)|(?<![/-])(?<=\b))\d+[/]\d+(?=(\b[^/]|$))|[\u00BC-\u00BE\u2150-\u215E])";
public static readonly Func<string, string, string, string> DoubleRegexDefinition = (placeholder, thousandsmark, decimalmark) => $@"(((?<!\d+\s*)-\s*)|((?<=\b)(?<!\d+\.|\d+,)))\d{{1,3}}({thousandsmark}\d{{3}})+{decimalmark}\d+(?={placeholder})";
public static readonly Func<string, string, string, string> DoubleRegexDefinition = (placeholder, thousandsmark, decimalmark) => $@"(((?<!\d+\s*)-\s*)|((?<=\b)(?<!\d+[\.,])))\d{{1,3}}(({thousandsmark}\d{{3}})+{decimalmark}|({decimalmark}\d{{3}})+{thousandsmark})\d+(?={placeholder})";
public const string PlaceHolderDefault = @"\D|\b";
public const string CaseSensitiveTerms = @"(?<=(\s|\d))(kB|K[Bb]?|M[BbM]?|G[Bb]?|B)\b";
public const string NumberMultiplierRegex = @"(K|k|MM?|mil|G|T|B|b)";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,9 +819,7 @@ public static class DateTimeDefinitions
public static readonly string[] DurationDateRestrictions = { @"vandaag", @"nu" };
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"\bmorning|afternoon|evening|night|day\b", @"\b(good\s+(morning|afternoon|evening|night|day))|(nighty\s+night)\b" },
{ @"\bnow\b", @"\b(^now,)|\b((is|are)\s+now\s+for|for\s+now)\b" },
{ @"\bmay\b", @"\b((^may i)|(i|you|he|she|we|they)\s+may|(may\s+((((also|not|(also not)|well)\s+)?(be|ask|contain|constitute|email|e-mail|take|have|result|involve|get|work|reply|differ))|(or may not))))\b" },
{ @"^\d{4}$", @"(\d\.\d{4}|\d{4}\.\d)" },
{ @"^(jan|feb|mar|mrt|apr|mei|jun|jul|aug|sept?|oct|okt|nov|dec)$", @"([$%£&!?@#])(jan|feb|mar|mrt|apr|mei|jun|jul|aug|sept?|oct|okt|nov|dec)|(jan|feb|mar|mrt|apr|mei|jun|jul|aug|sept?|oct|okt|nov|dec)([$%£&@#])" }
};
public static readonly IList<string> MorningTermList = new List<string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ public static class DateTimeDefinitions
public static readonly string[] DurationDateRestrictions = { @"today", @"now" };
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"^\d{4}$", @"(\d\.\d{4}|\d{4}\.\d)" },
{ @"^(morning|afternoon|evening|night|day)\b", @"\b(good\s+(morning|afternoon|evening|night|day))|(nighty\s+night)\b" },
{ @"\bnow\b", @"\b(^now,)|\b((is|are)\s+now\s+for|for\s+now)\b" },
{ @"\bmay\b", @"\b((((!|\.|\?|,|;|)\s+|^)may i)|(i|you|he|she|we|they)\s+may|(may\s+((((also|not|(also not)|well)\s+)?(be|ask|contain|constitute|e-?mail|take|have|result|involve|get|work|reply|differ))|(or may not))))\b" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public static class NumbersDefinitions
public const string LangMarker = @"Eng";
public const bool CompoundNumberLanguage = false;
public const bool MultiDecimalSeparatorCulture = true;
public static readonly IList<string> NonStandardSeparatorVariants = new List<string>
{
@"en-za",
@"en-na",
@"en-zw"
};
public const string RoundNumberIntegerRegex = @"(?:hundred|thousand|million|mln|billion|bln|trillion|tln|lakh|crore)s?";
public const string ZeroToNineIntegerRegex = @"(?:three|seven|eight|four|five|zero|nine|one|two|six)";
public const string TwoToNineIntegerRegex = @"(?:three|seven|eight|four|five|nine|two|six)";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ public static class DateTimeDefinitions
public static readonly string[] DurationDateRestrictions = { };
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"^\d{4}$", @"(\d\.\d{4}|\d{4}\.\d)" },
{ @"^([eé]t[eé])$", @"(?<!((l\s*['`]\s*)|(cet(te)?|en)\s+))[eé]t[eé]\b" },
{ @"^(mer)$", @"(?<!((le|ce)\s+))mer\b" },
{ @"^(avr|ao[uû]t|d[eé]c|f[eé]vr?|janv?|jui?[ln]|mars?|mai|nov|oct|sept?)$", @"([$%£&!?@#])(avr|ao[uû]t|d[eé]c|f[eé]vr?|janv?|jui?[ln]|mars?|mai|nov|oct|sept?)|(avr|ao[uû]t|d[eé]c|f[eé]vr?|janv?|jui?[ln]|mars?|mai|nov|oct|sept?)([$%£&@#])" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ public static class DateTimeDefinitions
public static readonly string[] DurationDateRestrictions = { @"today", @"now" };
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"^\d{4}$", @"(\d\.\d{4}|\d{4}\.\d)" },
{ @"\b(morgen|nachmittag|abend|nacht|tag)\b", @"\b(gut(en?)?\s+(morgen|nachmittag|abend|nacht|tag))\b" },
{ @"^(apr|aug|dez|feb|j[äa]n|jul|jun|märz|mai|nov|okt|sept?)$", @"([$%£&!?@#])(apr|aug|dez|feb|j[äa]n|jul|jun|märz|mai|nov|okt|sept?)|(apr|aug|dez|feb|j[äa]n|jul|jun|märz|mai|nov|okt|sept?)([$%£&@#])" },
{ @"^(mo|di|mi|do|fr|sa|so)$", @"\b(mo|di|mi|do|fr|sa|so)\b" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class NumbersDefinitions
public const string RoundNumberIntegerRegex = @"((ein)?hundert|tausend|(\s*(million(en)?|mio|milliarden?|mrd|billion(en)?)\s*))";
public const string AnIntRegex = @"(eine?)(?=\s)";
public const string TenToNineteenIntegerRegex = @"(siebzehn|dreizehn|vierzehn|achtzehn|neunzehn|fuenfzehn|sechzehn|elf|zwoelf|zwölf|zehn)";
public const string TensNumberIntegerRegex = @"(siebzig|zwanzig|dreißig|achtzig|neunzig|vierzig|fuenfzig|fünfzig|sechzig)";
public const string TensNumberIntegerRegex = @"(siebzig|zwanzig|dreißig|achtzig|neunzig|vierzig|fuenfzig|fünfzig|sechzig|hundert|tausend)";
public const string NegativeNumberTermsRegex = @"^[.]";
public static readonly string NegativeNumberSignRegex = $@"^({NegativeNumberTermsRegex}\s+).*";
public static readonly string SeparaIntRegex = $@"((({TenToNineteenIntegerRegex}|({ZeroToNineIntegerRegex}und{TensNumberIntegerRegex})|{TensNumberIntegerRegex}|{ZeroToNineIntegerRegex})(\s*{RoundNumberIntegerRegex})*))|(({AnIntRegex}(\s*{RoundNumberIntegerRegex})+))";
Expand All @@ -54,10 +54,10 @@ public static class NumbersDefinitions
public const string FractionNotationWithSpacesRegex = @"(((?<=\W|^)-\s*)|(?<=\b))\d+\s+\d+[/]\d+(?=(\b[^/]|$))";
public static readonly string FractionNotationRegex = $@"{BaseNumbers.FractionNotationRegex}";
public const string FractionUnitsRegex = @"((?<onehalf>anderthalb|einundhalb)|(?<quarter>dreiviertel))";
public const string FractionHalfRegex = @"(einhalb)$";
public static readonly string[] OneHalfTokens = { @"ein", @"halb" };
public static readonly string FractionNounRegex = $@"(?<=\b)(({AllIntRegex})(\s*|\s*-\s*)((({AllOrdinalRegex})|({RoundNumberOrdinalRegex}))|halb(er|e|es)?|hälfte)|{FractionUnitsRegex})(?=\b)";
public static readonly string FractionNounWithArticleRegex = $@"(?<=\b)(({AllIntRegex}\s+(und\s+)?)?eine?(\s+|\s*-\s*)({AllOrdinalRegex}|{RoundNumberOrdinalRegex}|{FractionUnitsRegex}|({AllIntRegex}ein)?(halb(er|e|es)?|hälfte))|{AllIntRegex}ein(halb))(?=\b)";
public const string FractionHalfRegex = @"(einhalb(es)?)$";
public static readonly string[] OneHalfTokens = { @"ein", @"halb", @"halbes" };
public static readonly string FractionNounRegex = $@"(?<=\b)(({AllIntRegex})(\s*|\s*-\s*)((({AllOrdinalRegex})|({RoundNumberOrdinalRegex}))|halb(e[rs]?)?|hälfte)|{FractionUnitsRegex})(?=\b)";
public static readonly string FractionNounWithArticleRegex = $@"(?<=\b)(({AllIntRegex}\s+(und\s+)?)?eine?(\s+|\s*-\s*)({AllOrdinalRegex}|{RoundNumberOrdinalRegex}|{FractionUnitsRegex}|({AllIntRegex}ein)?(halb(e[rs]?)?|hälfte))|{AllIntRegex}ein(halb))(?=\b)";
public static readonly string FractionPrepositionRegex = $@"(?<!{BaseNumbers.CommonCurrencySymbol}\s*)(?<=\b)(?<numerator>({AllIntRegex})|((?<!\.)\d+))\s+over\s+(?<denominator>({AllIntRegex})|(\d+)(?!\.))(?=\b)";
public static readonly string AllPointRegex = $@"((\s*{ZeroToNineIntegerRegex})+|(\s*{SeparaIntRegex}))";
public static readonly string AllFloatRegex = $@"({AllIntRegex}(\s*komma\s*){AllPointRegex})";
Expand Down Expand Up @@ -411,7 +411,7 @@ public static class NumbersDefinitions
};
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"^[.]", @"" }
{ @"^(tausend|hundert)$", @"(ed(ward(\s+m(\.)?)?)?|mary(\s+c(\.)?)?|joachim|claudia|franz|maria|klaus|prof(\.|essor)?|dr(\.)?|herr|fr[äa]u(lein)?|frl?\.)\s+(tausend|hundert)" }
};
public static readonly Dictionary<string, string> RelativeReferenceOffsetMap = new Dictionary<string, string>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ public static class DateTimeDefinitions
public static readonly string[] DurationDateRestrictions = { };
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"^\d{4}$", @"(\d\.\d{4}|\d{4}\.\d)" },
{ @"\bgiorno|pomeriggio|sera|notte\b", @"\b(buona?\s*(giorno|pomeriggio|sera|notte))\b" },
{ @"^(apr|ago|dic|feb|gen|lug|giu|mar|mag|nov|ott|sett?)$", @"([$%£&!?@#])(apr|ago|dic|feb|gen|lug|giu|mar|mag|nov|ott|sett?)|(apr|ago|dic|feb|gen|lug|giu|mar|mag|nov|ott|sett?)([$%£&@#])" }
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ public static class DateTimeDefinitions
public static readonly string[] DurationDateRestrictions = { };
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"^\d{4}$", @"(\d\.\d{4}|\d{4}\.\d)" },
{ @"^(abr|ago|dez|fev|jan|ju[ln]|mar|maio?|nov|out|sep?t)$", @"([$%£&!?@#])(abr|ago|dez|fev|jan|ju[ln]|mar|maio?|nov|out|sep?t)|(abr|ago|dez|fev|jan|ju[ln]|mar|maio?|nov|out|sep?t)([$%£&@#])" }
};
public static readonly IList<string> EarlyMorningTermList = new List<string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ public static class DateTimeDefinitions
public static readonly string[] DurationDateRestrictions = { @"hoy" };
public static readonly Dictionary<string, string> AmbiguityFiltersDict = new Dictionary<string, string>
{
{ @"^\d{4}$", @"(\d\.\d{4}|\d{4}\.\d)" },
{ @"^(este\s+)?mi(\s+([uú]ltimo|pasado|anterior|pr[oó]ximo|siguiente|que\s+viene))?$", @"\b(este\s+)?mi(\s+([uú]ltimo|pasado|anterior|pr[oó]ximo|siguiente|que\s+viene))?\b" },
{ @"^a[nñ]o$", @"(?<!el\s+)a[nñ]o" },
{ @"^semana$", @"(?<!la\s+)semana" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,23 @@ public static class NumbersDefinitions
public const string LangMarker = @"Spa";
public const bool CompoundNumberLanguage = false;
public const bool MultiDecimalSeparatorCulture = true;
public static readonly IList<string> NonStandardSeparatorVariants = new List<string>
{
@"es-mx",
@"es-do",
@"es-sv",
@"es-gt",
@"es-hn",
@"es-ni",
@"es-pa",
@"es-pr"
};
public const string HundredsNumberIntegerRegex = @"(cuatrocient[ao]s|trescient[ao]s|seiscient[ao]s|setecient[ao]s|ochocient[ao]s|novecient[ao]s|doscient[ao]s|quinient[ao]s|(?<!por\s+)(cien(to)?))";
public const string RoundNumberIntegerRegex = @"(mil millones|millones|mill[oó]n|mil|billones|bill[oó]n|trillones|trill[oó]n|cuatrillones|cuatrill[oó]n|quintillones|quintill[oó]n|sextillones|sextill[oó]n|septillones|septill[oó]n)";
public const string RoundNumberIntegerRegex = @"(mil millones|mill[oó]n(es)?|mil|bill[oó]n(es)?|trill[oó]n(es)?|cuatrill[oó]n(es)?|quintill[oó]n(es)?|sextill[oó]n(es)?|septill[oó]n(es)?)";
public const string ZeroToNineIntegerRegex = @"(cuatro|cinco|siete|nueve|cero|tres|seis|ocho|dos|un[ao]?)";
public const string TenToNineteenIntegerRegex = @"(diecisiete|diecinueve|diecis[eé]is|dieciocho|catorce|quince|trece|diez|once|doce)";
public const string TwentiesIntegerRegex = @"(veinticuatro|veinticinco|veintisiete|veintinueve|veintitr[eé]s|veintis[eé]is|veintiocho|veintid[oó]s|ventiun[ao]|veinti[uú]n[oa]?|veinte)";
public const string TensNumberIntegerRegex = @"(cincuenta|cuarenta|treinta|sesenta|setenta|ochenta|noventa)";
public const string TwentiesIntegerRegex = @"(veinti(cuatro|cinco|siete|nueve|tr[eé]s|s[eé]is|ocho|d[oó]s|[uú]n[oa]?)|ventiun[ao]|veinte)";
public const string TensNumberIntegerRegex = @"(cincuenta|cuarenta|treinta|se[st]enta|ochenta|noventa)";
public const string NegativeNumberTermsRegex = @"(?<negTerm>(?<!(al|lo)\s+)menos\s+)";
public static readonly string NegativeNumberSignRegex = $@"^{NegativeNumberTermsRegex}.*";
public const string DigitsNumberRegex = @"\d|\d{1,3}(\.\d{3})";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

namespace Microsoft.Recognizers.Text.Number.Tests
Expand Down Expand Up @@ -71,6 +72,8 @@ public LongFormTestConfiguration(char decimalSep, char nonDecimalSep)

public bool IsMultiDecimalSeparatorCulture { get; }

public IEnumerable<string> NonStandardSeparatorVariants => Enumerable.Empty<string>();

public IEnumerable<string> NormalizeTokenSet(IEnumerable<string> tokens, ParseResult context)
{
throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.Recognizers.Text.DataDrivenTests;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.Recognizers.Text.Number.Tests
{
[TestClass]
public class TestNumber_SpanishMexican : TestBase
{
[NetCoreTestDataSource]
[TestMethod]
public void NumberModel(TestModel testSpec)
{
TestNumber(testSpec);
}

/*
[NetCoreTestDataSource]
[TestMethod]
public void OrdinalModel(TestModel testSpec)
{
TestNumber(testSpec);
}
[NetCoreTestDataSource]
[TestMethod]
public void PercentModel(TestModel testSpec)
{
TestNumber(testSpec);
}
[NetCoreTestDataSource]
[TestMethod]
public void NumberRangeModel(TestModel testSpec)
{
TestNumber(testSpec);
}
*/
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Microsoft.Recognizers.Text.Config;

namespace Microsoft.Recognizers.Text.DateTime
namespace Microsoft.Recognizers.Text.DateTime
{
public interface IDateTimeOptionsConfiguration : IConfiguration
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ protected override void InitializeConfiguration()
new BaseMergedDateTimeExtractor(
new SpanishMergedExtractorConfiguration(new BaseDateTimeOptionsConfiguration(Culture.Spanish, options)))));

RegisterModel<DateTimeModel>(
Culture.SpanishMexican,
options => new DateTimeModel(
new BaseMergedDateTimeParser(
new SpanishMergedParserConfiguration(new BaseDateTimeOptionsConfiguration(Culture.SpanishMexican, options))),
new BaseMergedDateTimeExtractor(
new SpanishMergedExtractorConfiguration(new BaseDateTimeOptionsConfiguration(Culture.SpanishMexican, options)))));

RegisterModel<DateTimeModel>(
Culture.French,
options => new DateTimeModel(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Microsoft.Recognizers.Text.Config;

namespace Microsoft.Recognizers.Text.Number
namespace Microsoft.Recognizers.Text.Number
{
public interface INumberOptionsConfiguration : IConfiguration
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -54,5 +55,7 @@ public EnglishNumberParserConfiguration(INumberOptionsConfiguration config)
}

public string NonDecimalSeparatorText { get; private set; }

public override IEnumerable<string> NonStandardSeparatorVariants => NumbersDefinitions.NonStandardSeparatorVariants;
}
}
28 changes: 28 additions & 0 deletions .NET/Microsoft.Recognizers.Text.Number/NumberRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,34 @@ protected override void InitializeConfiguration()
new BaseNumberOptionsConfiguration(Culture.Spanish, options))),
new Spanish.NumberRangeExtractor(new BaseNumberOptionsConfiguration(Culture.Spanish, options))));

RegisterModel<NumberModel>(
Culture.SpanishMexican,
(options) => new NumberModel(
AgnosticNumberParserFactory.GetParser(AgnosticNumberParserType.Number, new SpanishNumberParserConfiguration(
new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options))),
Spanish.NumberExtractor.GetInstance(new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options, NumberMode.PureNumber))));

RegisterModel<OrdinalModel>(
Culture.SpanishMexican,
(options) => new OrdinalModel(
AgnosticNumberParserFactory.GetParser(AgnosticNumberParserType.Ordinal, new SpanishNumberParserConfiguration(
new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options))),
Spanish.OrdinalExtractor.GetInstance(new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options))));

RegisterModel<PercentModel>(
Culture.SpanishMexican,
(options) => new PercentModel(
AgnosticNumberParserFactory.GetParser(AgnosticNumberParserType.Percentage, new SpanishNumberParserConfiguration(
new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options))),
new Spanish.PercentageExtractor(new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options))));

RegisterModel<NumberRangeModel>(
Culture.SpanishMexican,
(options) => new NumberRangeModel(
new BaseNumberRangeParser(new SpanishNumberRangeParserConfiguration(
new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options))),
new Spanish.NumberRangeExtractor(new BaseNumberOptionsConfiguration(Culture.SpanishMexican, options))));

RegisterModel<NumberModel>(
Culture.Portuguese,
(options) => new NumberModel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public override ParseResult Parse(ExtractResult extResult)
// TODO: @Refactor this check to determine the subtype for JA and KO
if ((Config.CultureInfo.Name == "ja-JP" || Config.CultureInfo.Name == "ko-KR") && ret != null)
{
ret.Type = DetermineType(extResult);
ret.Type = DetermineType(extResult, ret);
ret.Text = ret.Text.ToLowerInvariant();
}

Expand Down
Loading

0 comments on commit 4201b79

Please sign in to comment.