Ресурсы локализации ASP.NET MVC 5 зависают и не меняют язык, несмотря на изменение CurrentThread.CurrentCulture
У меня есть веб-приложение ASP.NET MVC 5, работающее под веб-сайтом в IIS 8, где мне нужно программно менять язык во время выполнения по предпочтению пользователя, которое я читаю из БД и сохраняю в переменной сеанса, и это значение может изменяться во время выполнения с выпадающим.
Проблема в том, что языковые ресурсы иногда не меняются, несмотря на то, что CurrentCulture делает это, и хуже всего то, что они зависают на уровне приложения, что делает все другие пользовательские сеансы зависшими также на этом языке и будет показывать только во всех сеансах приложения один и тот же язык в который застревает независимо от их предпочтения или если они даже пытаются изменить свой язык во время выполнения, который, когда работает нормально, заставляет CurrentThread.CurrentCulture изменить.
Я использую скрытый текст в представлениях, чтобы отследить, что CurrentThread.CurrentCulture было и изменяется, как и ожидалось, поэтому проблема должна быть в другом месте.
Это блок бритвы, который я использую для проверки CurrentCulture
<div style="display:none; visibility: hidden;">Thread
@System.Threading.Thread.CurrentThread.CurrentCulture</div>
И во время выполнения, когда язык или resx замораживаются / застряли на уровне приложения, значение действительно является правильным и выбранным, но оно не соответствует языку, отображаемому в представлениях, и не будет работать нормально, пока я не внесу, например, изменение в web.config или перезапустите приложение, и тогда оно некоторое время будет работать нормально, но не длится слишком много времени.
<div style="display:none; visibility: hidden;">Thread en-US</div>
Проблема в том, что это происходит на уровне приложения для всех пользовательских сессий, и CurrentCulture фактически изменяется, как и ожидалось, когда приложение работает нормально, и всякий раз, когда перезапускается, это не происходит, но каким-то образом каждый раз, когда что-то срабатывает во время пользовательских сессий, чтобы заморозить язык всех текущих сессий и даже новых сессий.
Я попытался поместить логику изменения культуры в виды бритвы, фильтры действий и базовый контроллер, но это не решило проблему, ранее у меня был базовый контроллер, который унаследовал весь мой контроллер, и логика в Global.asax.cs для изменения и определить культуру.
Мой текущий подход состоял в том, чтобы удалить базовый контроллер и использовать его в Global.asax.cs Application_AcquireRequestState (я тоже пытался использовать Application_BeginRequest, но как-то слишком рано в жизненном цикле) и фильтр действий.
Это в Global.asax.cs
protected void Application_AcquireRequestState(object sender, EventArgs e)
{
if (Context.Session != null && Context.Session["CultureName"] != null)
{
string cultureName = Context.Session["CultureName"].ToString();
CultureInfo cultureInfo = new System.Globalization.CultureInfo(cultureName);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
}
}
И мой фильтр действий, как это
public class CultureActionAttribute : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
string cultureName = null;
string tenant = filterContext.RouteData.Values["Tenant"] as string;
if (!String.IsNullOrWhiteSpace(tenant))
{
TenantService tenantService = new TenantService(tenant);
cultureName = CultureHelper.GetImplementedCulture(tenantService.Tenant.LanguageCulture);
tenantService.Unit.Dispose();
}
//Logic to override cultureName value by getting the user preference language
CultureInfo cultureInfo = new System.Globalization.CultureInfo(cultureName);
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
}
}
И я добавил это в FilterConfig
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new CultureActionAttribute());
//
}
Я даже пытался использовать ClearCachedData, когда пользователь меняет культуру.
Thread.CurrentThread.CurrentCulture.ClearCachedData();
Я думаю, что это может быть связано с кэшированием или утилизацией пула, но у меня нет идей, что попробовать дальше.
Я не использую какой-либо тег глобализации на web.config.
Значение, отслеживаемое в представлениях CurrentThread.CurrentCulture, всегда является правильным, и неправильным является замораживание с использованием ресурсов.
Ресурсы согласуются с практикой помещения их в отдельную выделенную стандартную папку и использования PublicResXFileCodeGenerator, чтобы сделать их общедоступными для возможности компиляции и доступности для контроллеров, представлений и т. Д.
Папка ресурсов
Может ли кто-нибудь предоставить мне другой выбор, чтобы попытаться или подумать о том, что я могу делать неправильно или что еще я могу попытаться сделать так, чтобы ресурсы работали согласованно.
2 ответа
Единственный способ обеспечить правильные ресурсы - это обойти переопределение непосредственно над ресурсами свойства CurrentThreadUICulture для всех поисков ресурсов в конце моего CultureFilter.
Resources.Resource.Culture = cultureInfo;
И это помогло, используя преимущества, чтобы в качестве глобальных ресурсов использовались только отдельные файлы, так что теперь все работает, как и ожидалось, но я понимаю, что это будет неудобно для файлов с несколькими ресурсами и пространств имен. Тем не менее, у меня не было другого способа, чтобы убедиться, что изменение языка будет работать и не зависнет после попытки использовать базовый контроллер и атрибут Iauthorization actionfilter или использования Application_AcquireRequestState в Global.asax.cs, где ни один из трех методов всегда мог правильно изменить язык пользовательского интерфейса из-за зависания, несмотря на то, что Thread.CurrentThread.CurrentUICulture всегда корректно изменялся с помощью трех подходов.
Я считаю, что здесь происходит то, что вы настраиваете культуру в другом потоке, чем то, где вы фактически ее используете. Вы устанавливаете культуру в асинхронном методе BeginExecuteCore
, что может не согласованно передавать культуру в рабочий поток вашего приложения. Это объясняет прерывистое поведение - вы, вероятно, видите его "зависание" во время большой нагрузки, когда потоки приложения работают медленно.
Никогда не используйте базовый контроллер в MVC - это никогда не будет хорошей идеей. Вместо этого для сквозных задач, таких как локализация, вы можете использовать глобальный фильтр. Это не только устраняет проблему асинхронности, но и способствует более слабой связи. Вот пример фильтра, который получает культуру из текущего маршрута:
using System.Globalization;
using System.Threading;
using System.Web.Mvc;
public class CultureFilter : IAuthorizationFilter
{
private readonly string defaultCulture;
public CultureFilter(string defaultCulture)
{
this.defaultCulture = defaultCulture;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var values = filterContext.RouteData.Values;
string culture = (string)values["culture"] ?? this.defaultCulture;
CultureInfo ci = new CultureInfo(culture);
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(ci.Name);
}
}
использование
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CultureFilter(defaultCulture: "nl"));
filters.Add(new HandleErrorAttribute());
}
}
Я также утверждал бы, что использование состояния сеанса для локализации не является хорошей идеей - в конце концов, вы остаетесь в черной дыре, когда сеанс истекает.
Локали не персонализация. Локали - это контент. Правильный способ предоставления уникального контента - дать ему уникальный URL. Если вы сделаете это, выбрать язык так же просто, как выбрать новый URL-адрес, и он будет автоматически "зависать", потому что MVC повторно использует значения маршрута. Это никогда не истечет, даже если пользователь отправляет URL кому-то еще. И самое главное, вся работа, которую вы проделали, переводя ваш сайт на несколько языков, будет видна поисковым системам, поэтому она принесет гораздо большую отдачу.
Посмотрите культуру ASP.NET MVC 5 в route и url для рабочего примера подхода на основе URL.