diff --git a/MN.L10n.BuildTasks/MN.L10n.BuildTasks.csproj b/MN.L10n.BuildTasks/MN.L10n.BuildTasks.csproj index a4625e6..759b992 100644 --- a/MN.L10n.BuildTasks/MN.L10n.BuildTasks.csproj +++ b/MN.L10n.BuildTasks/MN.L10n.BuildTasks.csproj @@ -6,7 +6,7 @@ Exe MN.L10n.BuildTasks.Program true - 4.0.1 + 4.0.4 Chris Gårdenberg MultiNet Interactive AB diff --git a/MN.L10n.Tests/ParserTests.cs b/MN.L10n.Tests/ParserTests.cs index 9a5ef98..297d2a4 100644 --- a/MN.L10n.Tests/ParserTests.cs +++ b/MN.L10n.Tests/ParserTests.cs @@ -115,5 +115,56 @@ public void LineBreakCharInCall() Assert.Collection(result, x => Assert.Equal("Hello\nBrother", x.Phrase)); } + + [Fact] + public void TestEscapedStringContainerCharacter() + { + var parser = new L10nParser(); + var result = parser.Parse(@"_s(""Hello \""friend\"". How it do?"")"); + + Assert.Collection(result, x => Assert.Equal(@"Hello ""friend"". How it do?", x.Phrase)); + } + + [Fact] + public void TestEscapedStringContainerCharacter2() + { + var parser = new L10nParser(); + var result = parser.Parse(@"_s('Hello \""friend\"". How it do?')"); + + Assert.Collection(result, x => Assert.Equal(@"Hello \""friend\"". How it do?", x.Phrase)); + } + + [Fact] + public void TestEscapedStringContainerCharacter3() + { + var parser = new L10nParser(); + var result = parser.Parse(@"_s('Hello \'friend\'. How it do?')"); + + Assert.Collection(result, x => Assert.Equal(@"Hello 'friend'. How it do?", x.Phrase)); + } + + [Fact] + public void TestEscapedStringContainerCharacterFirstChar() + { + var parser = new L10nParser(); + var result = parser.Parse(@"_s(""\""friend\""!"")"); + + Assert.Collection(result, x => Assert.Equal(@"""friend""!", x.Phrase)); + } + + [Fact] + public void TestEscapedStringContainerCharacterVerbatim() + { + var src = @" + _s( + @""Hej """"bror"""" +Nej"" + ) + "; + var parser = new L10nParser(); + var result = parser.Parse(src).ToList(); + Assert.Single(result); + Assert.Equal(@"Hej ""bror""\nNej", result[0].Phrase.Trim()); + } } } diff --git a/MN.L10n/L10nParser.cs b/MN.L10n/L10nParser.cs index d021e77..889cc2b 100644 --- a/MN.L10n/L10nParser.cs +++ b/MN.L10n/L10nParser.cs @@ -86,6 +86,7 @@ bool TryPeek(int forward) { break; } + isVerbatim = true; } else @@ -115,6 +116,7 @@ bool TryPeek(int forward) continue; } } + if (inToken) { _tokenContent.Append(source[_pos]); @@ -143,7 +145,7 @@ bool TryPeek(int forward) phrase = phrase.Substring(0, phrase.Length - 1); } - yield return new PhraseInvocation + yield return Unescape(new PhraseInvocation { Phrase = phrase, Row = row, @@ -151,7 +153,7 @@ bool TryPeek(int forward) EndChar = _pos, IsEscaped = isEscaped, StringContainer = _stringContainer - }; + }, isVerbatim); inToken = false; } } @@ -159,9 +161,11 @@ bool TryPeek(int forward) { var _tail = source[_pos - 1]; var _peek = source[_pos + 1]; - if (source[_pos] == _stringContainer && _peek != _stringContainer && _tail != _stringContainer) + if (source[_pos] == _stringContainer && _peek != _stringContainer && + _tail != _stringContainer) { - var phrase = _tokenContent.ToString().Replace("\n", "\\n").Replace("\r", "").Replace("\"\"", "\\\""); + var phrase = _tokenContent.ToString().Replace("\n", "\\n").Replace("\r", "") + .Replace("\"\"", "\\\""); // Hoppa över sista \ om den är escape:ad if (isEscaped) @@ -169,31 +173,70 @@ bool TryPeek(int forward) phrase = phrase.Substring(0, phrase.Length - 1); } - yield return new PhraseInvocation + yield return Unescape(new PhraseInvocation { Phrase = phrase, Row = row, StartChar = startChar, EndChar = _pos, StringContainer = _stringContainer - }; + }, isVerbatim); inToken = false; } } - - if (inToken && !isVerbatim && source[_pos] == '\\' && TryPeek(1) && source[_pos + 1] == 'n') - { - _pos++; - _tokenContent.Append('\n'); - } - else - { - _tokenContent.Append(source[_pos]); - } + _tokenContent.Append(source[_pos]); } + break; } } } + + private PhraseInvocation Unescape(PhraseInvocation phraseInvocation, bool isVerbatim) + { + if (isVerbatim) + { + for (var i = 0; i < phraseInvocation.Phrase.Length; i++) + { + if (phraseInvocation.Phrase[i] == '\\' && i + 1 < phraseInvocation.Phrase.Length) + { + if (phraseInvocation.Phrase[i + 1] == phraseInvocation.StringContainer) + { + phraseInvocation.Phrase = phraseInvocation.Phrase.Remove(i, 1); + } + } + } + } + else + { + for (var i = 0; i < phraseInvocation.Phrase.Length; i++) + { + if (phraseInvocation.Phrase[i] == '\\' && i + 1 < phraseInvocation.Phrase.Length) + { + switch (phraseInvocation.Phrase[i + 1]) + { + case 'n': + phraseInvocation.Phrase = phraseInvocation.Phrase.Remove(i, 2).Insert(i, "\n"); + break; + case 't': + phraseInvocation.Phrase = phraseInvocation.Phrase.Remove(i, 2).Insert(i, "\t"); + break; + case '\\': + phraseInvocation.Phrase = phraseInvocation.Phrase.Remove(i, 2).Insert(i, "\\"); + break; + default: + if (phraseInvocation.Phrase[i + 1] == phraseInvocation.StringContainer) + { + phraseInvocation.Phrase = phraseInvocation.Phrase.Remove(i, 1); + } + + break; + } + } + } + } + + return phraseInvocation; + } } } diff --git a/MN.L10n/MN.L10n.csproj b/MN.L10n/MN.L10n.csproj index 2cfe70b..f528924 100644 --- a/MN.L10n/MN.L10n.csproj +++ b/MN.L10n/MN.L10n.csproj @@ -12,14 +12,14 @@ Translation package https://github.com/MultinetInteractive/MN.L10n git © 20XX MultiNet Interactive AB - 4.1.1 + 4.1.3 latest True Now includes analyzer Library - 4.1.1 + 4.1.3