From 398fdb85b8ea93c60034232d34f4835fffd43516 Mon Sep 17 00:00:00 2001 From: Umherirrender Date: Fri, 13 Dec 2024 20:50:18 +0100 Subject: [PATCH] language: Use fallback chain to create NumberFormatter php8.4 validates the locale and rejects invalid one. Just use the fallback chain to find an usable language, mostly en. Cache the created NumberFormatter instance, to avoid performance impact for some languages due to more attempts to create a NumberFormatter. Also C is rejected, so resolve it directly with Locale::getDefault() Bug: T376711 Change-Id: I686f77baea33ea2852c546e30e9cc08618d44321 (cherry picked from commit 8b2cc12bd3724cd213ac581d220b50f91e282630) --- includes/language/Language.php | 70 +++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/includes/language/Language.php b/includes/language/Language.php index 05e61eb1ed4..d1689e34146 100644 --- a/includes/language/Language.php +++ b/includes/language/Language.php @@ -36,6 +36,7 @@ use DateTime; use DateTimeImmutable; use DateTimeZone; use InvalidArgumentException; +use Locale; use LocalisationCache; use MediaWiki\Config\Config; use MediaWiki\Context\RequestContext; @@ -175,6 +176,12 @@ class Language implements Bcp47Code { */ private $overrideUcfirstCharacters; + /** + * @var NumberFormatter|null + * @noVarDump + */ + private $numberFormatter = null; + /** * @since 1.35 */ @@ -3177,20 +3184,7 @@ class Language implements Bcp47Code { if ( !$noSeparators ) { $separatorTransformTable = $this->separatorTransformTable(); - $digitGroupingPattern = $this->digitGroupingPattern(); - $code = $this->getCode(); - if ( !( $translateNumerals && $this->langNameUtils->isValidCode( $code ) ) ) { - $code = 'C'; // POSIX system default locale - } - - if ( $digitGroupingPattern ) { - $fmt = new NumberFormatter( - $code, NumberFormatter::PATTERN_DECIMAL, $digitGroupingPattern - ); - } else { - /** @suppress PhanParamTooFew Phan thinks this always requires 3 parameters, that's wrong */ - $fmt = new NumberFormatter( $code, NumberFormatter::DECIMAL ); - } + $fmt = $this->getNumberFormatter(); // minimumGroupingDigits can be used to suppress groupings below a certain value. // This is used for languages such as Polish, where one would only write the grouping @@ -3224,6 +3218,8 @@ class Language implements Bcp47Code { // but it does not know all languages MW // supports. Example: arq. Also, languages like pl have // customisation. So manually set it. + $fmt = clone $fmt; + if ( $noTranslate ) { $fmt->setSymbol( NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, @@ -4450,6 +4446,52 @@ class Language implements Bcp47Code { $this->msg( 'parentheses' )->rawParams( $details )->escaped(); } + private function getNumberFormatter(): NumberFormatter { + if ( $this->numberFormatter === null ) { + $digitGroupingPattern = $this->digitGroupingPattern(); + $code = $this->getCode(); + if ( !( $this->config->get( MainConfigNames::TranslateNumerals ) + && $this->langNameUtils->isValidCode( $code ) ) + ) { + $code = Locale::getDefault(); // POSIX system default locale + } + + $fmt = $this->createNumberFormatter( $code, $digitGroupingPattern ); + if ( !$fmt ) { + $fallbacks = $this->getFallbackLanguages(); + foreach ( $fallbacks as $fallbackCode ) { + $fmt = $this->createNumberFormatter( $fallbackCode, $digitGroupingPattern ); + if ( $fmt ) { + break; + } + } + if ( !$fmt ) { + throw new RuntimeException( + 'Could not instance NumberFormatter for ' . $code . ' and all fallbacks' + ); + } + } + + $this->numberFormatter = $fmt; + } + return $this->numberFormatter; + } + + private function createNumberFormatter( string $code, ?string $digitGroupingPattern ): ?NumberFormatter { + try { + if ( $digitGroupingPattern ) { + return new NumberFormatter( + $code, NumberFormatter::PATTERN_DECIMAL, $digitGroupingPattern + ); + } + // @suppress PhanParamTooFew Phan thinks this always requires 3 parameters, that's wrong + return new NumberFormatter( $code, NumberFormatter::DECIMAL ); + } catch ( \ValueError $_ ) { + // Value Errors are thrown since php8.4 for invalid locales + return null; + } + } + /** * Get the compiled plural rules for the language *