From a68c78a0091ffacc8de6595d68c7197c137bf66b Mon Sep 17 00:00:00 2001 From: nanaya Date: Tue, 10 Nov 2026 18:38:32 +0900 Subject: [PATCH] Fix incorrect locale on login --- app/Http/Controllers/SessionsController.php | 3 ++ app/Http/Middleware/SetLocale.php | 39 +++++++++++---------- app/Http/Middleware/SetLocaleApi.php | 2 +- resources/js/app-deps.ts | 10 +++--- resources/js/core/user/user-login.ts | 5 +++ resources/js/utils/html.ts | 13 ++----- resources/js/utils/lang.tsx | 15 ++++++++ 7 files changed, 51 insertions(+), 36 deletions(-) diff --git a/app/Http/Controllers/SessionsController.php b/app/Http/Controllers/SessionsController.php index 275a58597c1..72ec38b8943 100644 --- a/app/Http/Controllers/SessionsController.php +++ b/app/Http/Controllers/SessionsController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; +use App\Http\Middleware\SetLocale; use App\Libraries\User\CountryChange; use App\Libraries\User\DatadogLoginAttempt; use App\Libraries\User\ForceReactivation; @@ -106,9 +107,11 @@ public function store() DatadogLoginAttempt::log(null); $this->login($user); + SetLocale::setLocaleWeb($request); return [ 'csrf_token' => csrf_token(), + 'current_locale' => \App::getLocale(), 'header' => view('layout._header_user')->render(), 'header_popup' => view('layout._popup_user')->render(), 'user' => json_item($user, new CurrentUserTransformer()), diff --git a/app/Http/Middleware/SetLocale.php b/app/Http/Middleware/SetLocale.php index ba80423bfe7..e2aedbbb373 100644 --- a/app/Http/Middleware/SetLocale.php +++ b/app/Http/Middleware/SetLocale.php @@ -14,39 +14,42 @@ class SetLocale { - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * - * @return mixed - */ - public function handle(Request $request, Closure $next) + public static function setLocaleWeb(Request $request): void { - $this->setLocale( - Auth::user()?->user_lang ?? presence($request->cookie('locale')), - $request, - ); - - return $next($request); + $locale = Auth::user()?->user_lang ?? presence($request->cookie('locale')); + static::setLocale($locale, $request); } - protected function setLocale(?string $locale, Request $request): void + protected static function setLocale(?string $locale, Request $request): void { if ($locale !== null) { $locale = get_valid_locale($locale); } - $locale ??= $this->localeFromHeader($request); + $locale ??= static::localeFromHeader($request); App::setLocale($locale); // Carbon setLocale normalizes the locale Carbon::setLocale(locale_meta($locale)->carbon()); } - private function localeFromHeader(Request $request): string + private static function localeFromHeader(Request $request): string { return (new Parser())->languageRegionCompatibleFor($request->server('HTTP_ACCEPT_LANGUAGE')) ?? $GLOBALS['cfg']['app']['fallback_locale']; } + + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * + * @return mixed + */ + public function handle(Request $request, Closure $next) + { + static::setLocaleWeb($request); + + return $next($request); + } } diff --git a/app/Http/Middleware/SetLocaleApi.php b/app/Http/Middleware/SetLocaleApi.php index 630bbb877d4..e5d60e976be 100644 --- a/app/Http/Middleware/SetLocaleApi.php +++ b/app/Http/Middleware/SetLocaleApi.php @@ -20,7 +20,7 @@ public function handle(Request $request, Closure $next) $locale = Auth::user()?->user_lang; } - $this->setLocale($locale ?? null, $request); + static::setLocale($locale ?? null, $request); return $next($request); } diff --git a/resources/js/app-deps.ts b/resources/js/app-deps.ts index 4761a2822b1..8f18ac94468 100644 --- a/resources/js/app-deps.ts +++ b/resources/js/app-deps.ts @@ -10,10 +10,10 @@ import 'setup-jquery'; // imported separately as it requires window jquery (setup by the import above) import 'jquery-ujs'; -import { patchPluralHandler } from 'lang-overrides'; import Lang from 'lang.js'; import { configure as mobxConfigure } from 'mobx'; import * as moment from 'moment'; +import { resetLang } from 'utils/lang'; import { popup } from 'utils/popup'; import { reloadPage } from 'utils/turbolinks'; @@ -52,6 +52,8 @@ declare global { _styles: SharedStyles; currentLocale: string; currentUser: CurrentUserJson | { id: undefined }; + defaultNumberFormatter: Intl.NumberFormat; + defaultSuffixedNumberFormatter: Intl.NumberFormat; fallbackLocale: string; jQuery: JQueryStatic; Lang: Lang; @@ -63,11 +65,7 @@ declare global { } window.LangMessages ??= {}; -window.Lang = patchPluralHandler(new Lang({ - fallback: window.fallbackLocale, - locale: window.currentLocale, - messages: window.LangMessages, -})); +resetLang(); window.moment = moment; window.popup = popup; window.reloadPage = reloadPage; diff --git a/resources/js/core/user/user-login.ts b/resources/js/core/user/user-login.ts index 297ec06543e..9411098ed29 100644 --- a/resources/js/core/user/user-login.ts +++ b/resources/js/core/user/user-login.ts @@ -6,6 +6,7 @@ import UserJson from 'interfaces/user-json'; import core from 'osu-core-singleton'; import { xhrErrorMessage } from 'utils/ajax'; import { createClickCallback } from 'utils/html'; +import { resetLang } from 'utils/lang'; import { reloadPage } from 'utils/turbolinks'; declare global { @@ -21,6 +22,7 @@ interface CaptchaTriggeredResponse { interface LoginSuccessJson { csrf_token: string; + current_locale: string; header: string; header_popup: string; user: UserJson; @@ -114,6 +116,9 @@ export default class UserLogin { } const callback = this.callback; + window.currentLocale = data.current_locale; + resetLang(); + if (callback == null) { reloadPage(); return; diff --git a/resources/js/utils/html.ts b/resources/js/utils/html.ts index 1efa3d778bb..8b58224fe5b 100644 --- a/resources/js/utils/html.ts +++ b/resources/js/utils/html.ts @@ -67,11 +67,9 @@ export function formatDuration(valueSecond: number) { return `${m}:${padTimeComponent(s)}`; } -const defaultNumberFormatter = new Intl.NumberFormat(window.currentLocale); - export function formatNumber(num: number, precision?: number, options?: Intl.NumberFormatOptions, locale?: string) { if (precision == null && options == null && locale == null) { - return defaultNumberFormatter.format(num); + return window.defaultNumberFormatter.format(num); } options ??= {}; @@ -88,17 +86,10 @@ export function formatStarRating(num: number) { return formatNumber(floor(num, 2), 2); } -const defaultSuffixedNumberOptions = { - maximumFractionDigits: 1, - minimumFractionDigits: 0, - notation: 'compact', -} as const; -const defaultSuffixedNumberFormatter = new Intl.NumberFormat(window.currentLocale, defaultSuffixedNumberOptions); - export function formatNumberSuffixed(num?: number) { return num == null ? undefined - : defaultSuffixedNumberFormatter.format(num); + : window.defaultSuffixedNumberFormatter.format(num); } export function htmlElementOrNull(thing: unknown) { diff --git a/resources/js/utils/lang.tsx b/resources/js/utils/lang.tsx index 2866666728e..595ab085029 100644 --- a/resources/js/utils/lang.tsx +++ b/resources/js/utils/lang.tsx @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. // See the LICENCE file in the repository root for full licence text. +import { patchPluralHandler } from 'lang-overrides'; import Lang from 'lang.js'; import * as React from 'react'; import { formatNumber } from 'utils/html'; @@ -31,6 +32,20 @@ export function joinComponents(array: React.ReactElement[], key = 'common.array_ return <>{nodes}; } +export function resetLang() { + window.defaultNumberFormatter = new Intl.NumberFormat(window.currentLocale); + window.defaultSuffixedNumberFormatter = new Intl.NumberFormat(window.currentLocale, { + maximumFractionDigits: 1, + minimumFractionDigits: 0, + notation: 'compact', + }); + window.Lang = patchPluralHandler(new Lang({ + fallback: window.fallbackLocale, + locale: window.currentLocale, + messages: window.LangMessages, + })); +} + export function trans(key: string, replacements: Replacements = {}, locale?: string) { if (!transExists(key, locale)) { locale = fallbackLocale;