Localization
The localization concern analyzes incoming requests forlanguage preferences and allows the application logic toserve content based on those.
Basic Usage
By default, the concern will read theAccept-Language
headerand set theCultureInfo.CurrentUICulture
accordingly.
using System.Globalization;using GenHTTP.Engine.Internal;using GenHTTP.Modules.I18n;using GenHTTP.Modules.Layouting;using GenHTTP.Modules.Practices;using GenHTTP.Modules.Webservices;var localization = Localization.Create();var app = Layout.Create() .AddService<LocalizedService>("service") .Add(localization);await Host.Create() .Handler(app) .Defaults() .Development() .Console() .RunAsync();publicclassLocalizedService{ [ResourceMethod]publicstring GetLocalized() => CultureInfo.CurrentUICulture.ToString();}
using System.Globalization;using GenHTTP.Engine.Internal;using GenHTTP.Modules.Functional;using GenHTTP.Modules.I18n;using GenHTTP.Modules.Practices;var localization = Localization.Create();var app = Inline.Create() .Get("/service/", () => CultureInfo.CurrentUICulture.ToString()) .Add(localization);await Host.Create() .Handler(app) .Defaults() .Development() .Console() .RunAsync();
using System.Globalization;using GenHTTP.Api.Protocol;using GenHTTP.Engine.Internal;using GenHTTP.Modules.Controllers;using GenHTTP.Modules.I18n;using GenHTTP.Modules.Layouting;using GenHTTP.Modules.Practices;var localization = Localization.Create();var app = Layout.Create() .AddController<LocalizedController>("service") .Add(localization);await Host.Create() .Handler(app) .Defaults() .Development() .Console() .RunAsync();publicclassLocalizedController{ [ControllerAction(RequestMethod.Get)]publicstring Index() => CultureInfo.CurrentUICulture.ToString();}
Running and accessing this example app via http://localhost:8080/service/ in yourbrowser will print the preferred locale of your client.
Language Negotiation
Typically your application will not supportany language,so the client and the server need to negotiate a languagethat suits the client and the server can provide. Therefore,the client will send a list of supported languages and theirranking in theAccept-Language
header, such asde,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,de-CH;q=0.5
.
On the server side, you need to specify the cultures thatare supported by your application, either statically ordynamically:
var localization = Localization.Create();// statically set supported culturesCultureInfo[] supported = [CultureInfo.CreateSpecificCulture("de"), CultureInfo.CreateSpecificCulture("fr")];localization.Supports(supported);// dynamically evaluate supportlocalization.Supports(culture => culture.EnglishName.Contains("ish"));
The localization concern will then try to find the languagerequested by the client which is supported by the server and hasthe highest priority on the client-side. If there is no intersectionbetween the two, the logic will fallback to the default localethat is used by the server environment. The default value can be overriddenas needed:
var localization = Localization.Create() .Default(CultureInfo.CreateSpecificCulture("pl-PL"));
Parameter Source
As shown above, the server will analyze theAccept-Language
header bydefault to check the languages requested by the client. For API or webapplications you might want a different source instead:
// read a query parameter, e.g. ?language=eslocalization.FromQuery("language");// read a cookielocalization.FromCookie("__my_app_language");// read a custom headerlocalization.FromHeader("X-Custom-Language");// read directly from the request, e.g. /de/...localization.FromRequest(r => r.Target.Current?.Value);
If needed, you can specify multiple sources at the same time.
Consuming the Language
As shown above, the server will set theCurrentUICulture
of the currentthread. This behavior can be adjusted as needed:
// set current culture insteadlocalization.Setter(currentCulture:true, currentUICulture:false);// use a custom logiclocalization.Setter((r, c) => r.GetUser<MyAppUser>()?.Language = c);
Running in Docker
If you run your app in Docker (especially using an alpine-based images),setting the current culture in .NET will throw an exception. For this to work,you will need to set the following settings.
In your.csproj
file:
<InvariantGlobalization>false</InvariantGlobalization>
In your docker file:
RUN apk add --no-cache icu-data-full icu-libsENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false