This area is very complex. IMO, there are too many abstractions for culture handling and too many options to solve this in a good way. Also, the fact that many potential entry points has been made internal does not make it easier. Your solution might become something very different from mine.
In the end I only used a custom IResourceFilter and placed it before the existing CurrentCultureFilter. So, this only applies for the MVC pipeline - not the entire middlewares pipeline. This only works if RequestLocalizationMiddleware is NOT used.
If you create your own IResourceFilter, you can apply your own logic and figure out what culture to use for the request. First set content language via IUpdateCurrentLanguage, then set culture and UI culture via ICurrentCultureContext. ICurrentCultureContext will subsequently be used in EPiServer.Globalization.Internal.CurrentCultureFilter, and given that you have provided cultures - these cultures will be set on Thread.CurrentThread.CurrentCulture and Thread.CurrentThread.CurrentUICulture - which is what you want.
Given that all your requests pass through the MVC pipeline, this should hopefully give you full control over content language and system language, which should be enough.
Thank you Andreas J for the information.
Actually I was not doing with URL customization, but to add additional note - while I was investigating the topic - I've found that IContentUrlGeneratorEvents and IContentUrlResolverEvents might help to solve the problem.
As for the routing, is there any way to have a full control over the priority of languages?
I need to have a custom priority list that is based on route segments, browser language and site default language, but it's different than ContentLanguageImplementation.LanguagePreferenceList.
Maybe there's a way to override the LanguagePreferenceList and use it for the app logic, but the question is - how can this be achieved?