From 0aa4da98e371c23bbfca8900a48ed263f3555f9c Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 16 Sep 2026 01:50:09 +0930 Subject: [PATCH] larastan fix last commit --- app/Util/Localization/Localization.php | 86 ++++++++++++++++++++++++++ config/app.php | 5 +- tests/Feature/LocalizationTest.php | 83 +++++++++++-------------- 3 files changed, 124 insertions(+), 50 deletions(-) diff --git a/app/Util/Localization/Localization.php b/app/Util/Localization/Localization.php index 4595eead1..a358a17f8 100644 --- a/app/Util/Localization/Localization.php +++ b/app/Util/Localization/Localization.php @@ -6,6 +6,92 @@ use Illuminate\Support\Arr; class Localization { + /** + * Legacy two-letter (and legacy region) language codes mapped to the + * current locale-coded folder names under lang/. + * + * Pixelfed historically shipped translations under short codes (e.g. "es", + * "zh-cn"). Those folders were renamed to full locale codes (e.g. "es-ES", + * "zh-CN"). An instance upgrading with an old APP_LOCALE would otherwise + * resolve to a missing folder and silently fall back to English. This map + * lets an old APP_LOCALE keep working without any .env change on upgrade. + * + * The two codes that were ambiguous after the rename are pinned: + * - "en" => "en-US" (not the "en-x-pirate" novelty locale) + * - "sr" => "sr-CS" (Serbian; the prior single Serbian translation) + * + * DELETE NEXT MAJOR RELEASE + * + * @var array + */ + const LEGACY_LOCALE_MAP = [ + 'af' => 'af-ZA', + 'ar' => 'ar-SA', + 'bn' => 'bn-BD', + 'bs' => 'bs-BA', + 'ca' => 'ca-ES', + 'cs' => 'cs-CZ', + 'cy' => 'cy-GB', + 'da' => 'da-DK', + 'de' => 'de-DE', + 'el' => 'el-GR', + 'en' => 'en-US', + 'eo' => 'eo-UY', + 'es' => 'es-ES', + 'eu' => 'eu-ES', + 'fa' => 'fa-IR', + 'fi' => 'fi-FI', + 'fr' => 'fr-FR', + 'gd' => 'gd-GB', + 'gl' => 'gl-ES', + 'he' => 'he-IL', + 'hi' => 'hi-IN', + 'hr' => 'hr-HR', + 'hu' => 'hu-HU', + 'id' => 'id-ID', + 'it' => 'it-IT', + 'ja' => 'ja-JP', + 'ko' => 'ko-KR', + 'me' => 'me-ME', + 'mk' => 'mk-MK', + 'ms' => 'ms-MY', + 'nl' => 'nl-NL', + 'no' => 'no-NO', + 'oc' => 'oc-FR', + 'pl' => 'pl-PL', + 'pt' => 'pt-PT', + 'ro' => 'ro-RO', + 'ru' => 'ru-RU', + 'sk' => 'sk-SK', + 'sr' => 'sr-CS', + 'sv' => 'sv-SE', + 'th' => 'th-TH', + 'tr' => 'tr-TR', + 'uk' => 'uk-UA', + 'vi' => 'vi-VN', + 'zh-cn' => 'zh-CN', + 'zh-tw' => 'zh-TW', + ]; + + /** + * Normalize a configured locale to a current locale-coded value. + * + * Empty input returns "en-US"; a known legacy short code is mapped; + * anything else (already-current or custom codes) is returned unchanged so + * the framework's own fallback_locale still applies. Always returns a + * plain string, so it is safe to use in config values that get cached. + */ + public static function normalizeLocale(?string $locale): string + { + $locale = is_string($locale) ? trim($locale) : ''; + + if ($locale === '') { + return 'en-US'; + } + + return self::LEGACY_LOCALE_MAP[strtolower($locale)] ?? $locale; + } + /** * List of available UI language codes. * diff --git a/config/app.php b/config/app.php index bbc519872..f134d348c 100644 --- a/config/app.php +++ b/config/app.php @@ -1,6 +1,7 @@ pixelfed_normalize_locale($pixelfedLegacyLocaleMap, env('APP_LOCALE', 'en-US')), + 'locale' => Localization::normalizeLocale(env('APP_LOCALE', 'en-US')), /* |-------------------------------------------------------------------------- @@ -87,7 +88,7 @@ return [ | */ - 'fallback_locale' => pixelfed_normalize_locale($pixelfedLegacyLocaleMap, env('APP_FALLBACK_LOCALE', 'en-US')), + 'fallback_locale' => Localization::normalizeLocale(env('APP_FALLBACK_LOCALE', 'en-US')), /* |-------------------------------------------------------------------------- diff --git a/tests/Feature/LocalizationTest.php b/tests/Feature/LocalizationTest.php index d3eaaf094..8989365cf 100644 --- a/tests/Feature/LocalizationTest.php +++ b/tests/Feature/LocalizationTest.php @@ -319,70 +319,57 @@ describe('locale-aware caching of rendered site pages', function () { })->skip(fn () => firstNonDefaultLocale() === 'en-US', 'needs a second locale'); }); -describe('legacy APP_LOCALE normalization (config/app.php)', function () { +describe('legacy locale normalization', function () { /* - | The normalization lives in config/app.php: it maps a legacy short code - | (e.g. "es") from APP_LOCALE to the current locale folder (e.g. "es-ES") - | so instances upgrading with an old APP_LOCALE do not silently fall back - | to English. The config value is resolved once at boot, so these tests - | re-run the config file with a given APP_LOCALE and assert the result. + | Localization::normalizeLocale maps a legacy short code (e.g. "es") to the + | current locale folder (e.g. "es-ES"). config/app.php runs APP_LOCALE and + | APP_FALLBACK_LOCALE through it so instances upgrading with an old + | APP_LOCALE do not silently fall back to English. */ - $resolveLocale = function (?string $appLocale): string { - // Set APP_LOCALE the way the config file reads it (via env()). - if ($appLocale === null) { - putenv('APP_LOCALE'); - unset($_ENV['APP_LOCALE'], $_SERVER['APP_LOCALE']); - } else { - putenv('APP_LOCALE='.$appLocale); - $_ENV['APP_LOCALE'] = $appLocale; - $_SERVER['APP_LOCALE'] = $appLocale; - } - - try { - $config = require base_path('config/app.php'); - - return $config['locale']; - } finally { - putenv('APP_LOCALE'); - unset($_ENV['APP_LOCALE'], $_SERVER['APP_LOCALE']); - } - }; - - it('maps a legacy two-letter APP_LOCALE to its current locale code', function () use ($resolveLocale) { - expect($resolveLocale('es'))->toBe('es-ES') - ->and($resolveLocale('de'))->toBe('de-DE') - ->and($resolveLocale('fr'))->toBe('fr-FR'); + it('maps a legacy two-letter code to its current locale code', function () { + expect(Localization::normalizeLocale('es'))->toBe('es-ES') + ->and(Localization::normalizeLocale('de'))->toBe('de-DE') + ->and(Localization::normalizeLocale('fr'))->toBe('fr-FR'); }); - it('maps a legacy region APP_LOCALE to its current locale code', function () use ($resolveLocale) { - expect($resolveLocale('zh-cn'))->toBe('zh-CN') - ->and($resolveLocale('zh-tw'))->toBe('zh-TW'); + it('maps a legacy region code to its current locale code', function () { + expect(Localization::normalizeLocale('zh-cn'))->toBe('zh-CN') + ->and(Localization::normalizeLocale('zh-tw'))->toBe('zh-TW'); }); - it('pins the ambiguous legacy codes deliberately', function () use ($resolveLocale) { + it('pins the ambiguous legacy codes deliberately', function () { // "en" must resolve to en-US, not the en-x-pirate novelty locale. - expect($resolveLocale('en'))->toBe('en-US') + expect(Localization::normalizeLocale('en'))->toBe('en-US') // "sr" must resolve to the prior single Serbian translation. - ->and($resolveLocale('sr'))->toBe('sr-CS'); + ->and(Localization::normalizeLocale('sr'))->toBe('sr-CS'); }); - it('is case-insensitive on legacy short codes', function () use ($resolveLocale) { - expect($resolveLocale('ES'))->toBe('es-ES') - ->and($resolveLocale('Zh-Cn'))->toBe('zh-CN'); + it('is case-insensitive on legacy short codes', function () { + expect(Localization::normalizeLocale('ES'))->toBe('es-ES') + ->and(Localization::normalizeLocale('Zh-Cn'))->toBe('zh-CN'); }); - it('leaves an already-current locale code unchanged', function () use ($resolveLocale) { - expect($resolveLocale('es-ES'))->toBe('es-ES') - ->and($resolveLocale('en-US'))->toBe('en-US'); + it('leaves an already-current locale code unchanged', function () { + expect(Localization::normalizeLocale('es-ES'))->toBe('es-ES') + ->and(Localization::normalizeLocale('en-US'))->toBe('en-US'); }); - it('leaves unknown or custom locale codes unchanged', function () use ($resolveLocale) { - expect($resolveLocale('xx-YY'))->toBe('xx-YY') - ->and($resolveLocale('en-x-pirate'))->toBe('en-x-pirate'); + it('leaves unknown or custom locale codes unchanged', function () { + expect(Localization::normalizeLocale('xx-YY'))->toBe('xx-YY') + ->and(Localization::normalizeLocale('en-x-pirate'))->toBe('en-x-pirate'); }); - it('falls back to en-US when APP_LOCALE is unset', function () use ($resolveLocale) { - expect($resolveLocale(null))->toBe('en-US'); + it('falls back to en-US for empty or whitespace input', function () { + expect(Localization::normalizeLocale(''))->toBe('en-US') + ->and(Localization::normalizeLocale(' '))->toBe('en-US') + ->and(Localization::normalizeLocale(null))->toBe('en-US'); + }); + + it('resolves the configured app locale from a legacy APP_LOCALE', function () { + // Simulate an upgraded instance whose APP_LOCALE is still "es". + Config::set('app.locale', Localization::normalizeLocale('es')); + + expect(config('app.locale'))->toBe('es-ES'); }); });