turquoiseowl / i18n

Smart internationalization for ASP.NET
Other
556 stars 156 forks source link

ParseAndTranslate change headers. #338

Closed jeancroy closed 3 years ago

jeancroy commented 7 years ago

The recommended way to translate a segment of text is

    string entity = HttpContext.ParseAndTranslate("Hi - [[[Sign in]]]");

However it does a bit more that just translate the text, it try to setup headers as a side effect. It make a call to ProcessOutgoing which aim to rewrite url consistently with url localisation scheme. BUT that method also try to rewrite url in headers

Possible problem of those hidden side effect include error when there are no http response. (for example sending email at scheduled time)

System.PlatformNotSupportedException: This operation requires IIS integrated pipeline mode.
   at System.Web.HttpResponse.get_Headers()
   at System.Web.HttpResponseWrapper.get_Headers()
   at i18n.EarlyUrlLocalizer.ProcessOutgoing(String entity, String langtag, HttpContextBase context)
   at i18n.HttpContextExtensions.ParseAndTranslate(HttpContextBase context, String entity

Possible solution include splitting ProcessOutgoing as ProcessOutgoingNuggets and ProcessOutgoingHeaders. Then call header localization only when filtering request.

turquoiseowl commented 7 years ago

Yes, I agree with your suggestion. That feature was an afterthought... I would be happy to merge a PR on this. Thanks.

vhatuncev commented 4 years ago

@jeancroy looks like you call ParseAndTranslate method inside environment without http context, is it correct?

jeancroy commented 4 years ago

I considered it. My use case is producing emails and reports in a background process. It looks like I side-stepped the issue by calling some methods used by the internal of ParseAndTranslate.

 public static string TranslateNugget(string input, ILanguageTag lt = null)
        {
            if (lt == null) lt = HttpContext.Current.GetPrincipalAppLanguageForRequest();

            var langs = new[] { new LanguageItem(lt, LanguageItem.PalQualitySetting, 0) };

            var nuggetLocalizer = LocalizedApplication.Current.NuggetLocalizerForApp;
            if (nuggetLocalizer != null)
            {
                return LocalizedApplication.Current
                    .NuggetLocalizerForApp.ProcessNuggets(input, langs)
                    .Replace("\x5b\x5b\x5b","")   // Remove any remaining [[[ ]]]
                    .Replace("\x5d\x5d\x5d", "");

            }
            return input;
        }

Now if you truly don't have any context the above lt==null case wont work. It think I ended up creating a minimal context with

var oldHttpContext = HttpContext.Current;
var fakeHttpContext = new HttpContext(new HttpRequest(null, "fakeurl", null), new HttpResponse(null));
HttpContext.Current = fakeHttpContext;
var langTag = LanguageHelpers.GetMatchingAppLanguage(lang);
fakeHttpContext.SetPrincipalAppLanguageForRequest(langTag);
vhatuncev commented 4 years ago

@jeancroy I made the change as you initially suggested. But I don't sure if anyone's code already rely on existing side effect when calling manually HttpContext.ParseAndTranslate method. I assume it won't if i18n used by asp.net request pipeline, otherwise headers leave intact. I expect @turquoiseowl can provide any thoughts about it? Regarding producing translations inside background process, my project requires same functionality as well. Making a stub for this purpose is not clean solution and I'm thinking to implement clean method which does not depend on http context

jeancroy commented 4 years ago

Making a stub for this purpose is not clean solution and I'm thinking to implement clean method which does not depend on http context

Another reason I've made a stub is that I'm trying to render views to string, and the view engine need such stub for things like @Url.Action() to point to the proper place. I also use it to provide context to format date & numbers with user locale.

vhatuncev commented 4 years ago

@jeancroy just interesting, how i18n helps with date & numbers format, can you provide an example?

turquoiseowl commented 3 years ago

@vhatuncev i18n does the following by default for a request that matches an application language:

Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = langtag.GetCultureInfo();

This is in the default LocalizedApplication.SetPrincipalAppLanguageForRequestHandlers delegate.

turquoiseowl commented 3 years ago

Apologies for slow response to the PR.

I've now merged it and tested ParseAndTranslate and normal request handling, and all looks good.

Suggest this can be closed.