Closed mcliment closed 2 years ago
Thanks for reporting this. We use a trick for fast performance. We leverage the interpolation handler of the string builder.
I always assumed that it uses the Invariant culture by default, but it seems it uses null
(aka CurrentCulture). There is an overload which accepts the culture. I guess we have to use that. It is this line: https://github.com/SebastianStehle/mjml-net/blob/main/Mjml.Net/IHtmlStyleRenderer.cs#L38
There are a 3-4 lines like this.
PR is very welcome.
Have you seen my answer above. It should work if you just pass the CultureInfo.InvariantCulture to the interpolation handler. I am going to test that.
Have you seen my answer above. It should work if you just pass the CultureInfo.InvariantCulture to the interpolation handler. I am going to test that.
Apologies, I didn't see the comment above. Ignore my rambling :D
Awesome @SebastianStehle!
@SebastianStehle I see that it still happens in some cases, although most of them were fixed.
For example, for tempalte arturia.mjml
, using es-ES culture shows some errors. This is the first:
1 The text in style(7) > #text(0) is different.
* Actual: '
@media only screen and (min-width:480px) {
.mj-column-per-33-333333333333336 {
width:33,333333333333336% !important;
max-width: 33,333333333333336%;
}
.mj-column-per-100 {
width:100% !important;
max-width: 100%;
}
.mj-column-per-25 {
width:25% !important;
max-width: 25%;
}
}
'.
* Should: '
@media only screen and (min-width:480px) {
.mj-column-per-33-333333333333336 {
width: 33.333333333333336% !important;
max-width: 33.333333333333336%;
}
.mj-column-per-100 {
width: 100% !important;
max-width: 100%;
}
.mj-column-per-25 {
width: 25% !important;
max-width: 25%;
}
}
'.
It seems that not all paths go though the interpolator.
I can take a look tomorrow if you need some help. The easiest way I found to reproduce is setting the culture before rendering in the ComplexTests
class.
I believe this is the offending line: https://github.com/SebastianStehle/mjml-net/blob/main/Mjml.Net/Components/Body/ColumnComponent.cs#L284.
private string GetColumnClass (GlobalContext context) {
string className;
// Correctly uses ToInvariantString();
if (CurrentWidth.Unit == Unit.Percent) {
className = $"mj-column-per-{CurrentWidth.Value.ToInvariantString().Replace('.', '-')}";
} else {
className = $"mj-column-px-{CurrentWidth.Value.ToInvariantString().Replace('.', '-')}";
}
// CurrentWidth.WidthString lacks the ToInvariantString() usage when set in Measure().
context.SetGlobalData (className, MediaQuery.Width (className, CurrentWidth.WidthString));
return className;
}
However, CurrentWidth
is set by the Measure()
function before the component is rendered:
https://github.com/SebastianStehle/mjml-net/blob/main/Mjml.Net/Components/Body/ColumnComponent.cs#L91-L132
if (Width != null)
{
(widthValue, widthUnit) = UnitParser.Parse(Width);
widthString = Width;
}
else
{
widthValue = 100d / Math.Max(1, numNonRawSiblings);
widthUnit = Unit.Percent;
widthString = $"{widthValue}%";
}
The above lacks usage of ToInvariantString()
when using string interpolation.
Change the following line:
widthString = $"{widthValue}%";
to
widthString = $"{widthValue.ToInvariantString()}%";
We can also check other occurrences of Measure()
to find the same issue. Quick search suggests the following components are affected:
I believe we should update the ComplexTests
to follow the same approach as the original multi-culture tests we implemented for MjmlRenderer
:
[Theory]
[MemberData (nameof (Cultures))]
public void Should_render_interolated_content_with_invariant_culture (CultureInfo culture) {
var currentCulture = CultureInfo.CurrentCulture;
var currentUICulture = CultureInfo.CurrentUICulture;
try {
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
sut.Content ($"<div class=\"{0.3333}px\">");
AssertText (new [] {
"<div class=\"0.3333px\">"
});
} finally {
CultureInfo.CurrentCulture = currentCulture;
CultureInfo.CurrentUICulture = currentUICulture;
}
}
Yes, I can update that.
Just ran the tests locally and works like a charm, nice! Thanks a lot @LiamRiddell @SebastianStehle 🎉 💯
Thanks for your feedback :)
What have you used before?
What have you used before?
More or less what you use in the ComplexTests, launch an external node process to do the job and in case of failure, call the API, but nothing can compare to a native .NET library.
Our implementation is also 30times faster :)
Hi! It's me again!
If I run the tests on my machine (with "es-ES" locale), they won't work as the decimal separator in this locale is
,
instead of.
, that is the invariant.For example the first error for
amario.mjml
is:If I set the thread to use invariant culture the tests pass but I can imagine that it's not a good solution.
I tried to check where
"${width}px"
and similar stuff is done but putting{width.ToInvariantString()}
everywhere is not very fool proof... may be the thread culture should be temporarily set to invariant when rendering and then restored.I may add a PR if you want to.