From 09b10c746bbfcbfaeb2d079354ff89039d9da13b Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Thu, 13 Mar 2025 16:37:47 +1100 Subject: Client-side date/time formatter library Add a library providing date/time formatting according to the user's preferred time zone and date preference. I tested formatting of an example date with all defined formats in all languages, and I found that it gives identical output to PHP in about 90% of our ~500 languages. Resolve some of the outstanding issues by aliasing the problematic date formats on the client side, so that the user will see the date in another acceptable format for the same language. The remaining issues mostly relate to the use of a fallback language to display weekdays and non-Gregorian month names. Details: * Add Language::getJsDateFormats(), which converts existing date formats to an options array that can be interpreted by the client. * In Messages*.php, add $numberingSystem, which is the CLDR numbering system ID. I set it for all languages that had overriden $digitTransformTable. This is sent to the client in the library's JSON config and is used as the default numberingSystem option when formatting dates. * In Messages*.php, add $jsDateFormats, which overrides the automatically generated date format options. Bug: T389161 Change-Id: Ib6bc8ebd4d01317aaf32225c6006ea2dc7a1b39e --- includes/ResourceLoader/DateFormatterConfig.php | 84 +++++++++++++++++++++++++ includes/ResourceLoader/UserOptionsModule.php | 12 ++++ 2 files changed, 96 insertions(+) create mode 100644 includes/ResourceLoader/DateFormatterConfig.php (limited to 'includes/ResourceLoader') diff --git a/includes/ResourceLoader/DateFormatterConfig.php b/includes/ResourceLoader/DateFormatterConfig.php new file mode 100644 index 000000000000..4fa261532950 --- /dev/null +++ b/includes/ResourceLoader/DateFormatterConfig.php @@ -0,0 +1,84 @@ +getLanguageFactory() + ->getLanguage( $context->getLanguage() ); + return self::getDataForLang( $lang, $config ); + } + + /** + * Get configuration data for DateFormatter given parameters + * + * @internal + * @param Language $lang + * @param Config $config + * @return array + */ + public static function getDataForLang( Language $lang, Config $config ) { + $locales = [ $lang->getHtmlCode() ]; + $fallbacks = $lang->getFallbackLanguages(); + foreach ( $fallbacks as $code ) { + $locales[] = LanguageCode::bcp47( $code ); + } + + // Discover which fields are required + $formats = $lang->getJsDateFormats(); + $haveField = []; + foreach ( $formats as $format ) { + $pattern = $format['pattern'] ?? ''; + foreach ( [ 'mwMonth', 'mwMonthGen', 'mwMonthAbbrev' ] as $field ) { + if ( str_contains( $pattern, "{$field}" ) ) { + $haveField[$field] = true; + } + } + } + + // Include only the required month data + if ( $haveField ) { + $months = [ [] ]; + for ( $i = 1; $i <= 12; $i++ ) { + $data = [ + isset( $haveField['mwMonth'] ) ? $lang->getMonthName( $i ) : '', + isset( $haveField['mwMonthGen'] ) ? $lang->getMonthNameGen( $i ) : '', + isset( $haveField['mwMonthAbbrev'] ) ? $lang->getMonthAbbreviation( $i ) : '' + ]; + // Trim the end of the array + while ( end( $data ) === '' ) { + unset( $data[ array_key_last( $data ) ] ); + } + $months[] = $data; + } + } else { + $months = []; + } + + return [ + 'locales' => $locales, + 'formats' => $formats, + 'defaultStyle' => $lang->getDefaultDateFormat(), + 'localZone' => $config->get( MainConfigNames::Localtimezone ), + 'localOffset' => (int)$config->get( MainConfigNames::LocalTZoffset ), + 'months' => $months + ]; + } +} diff --git a/includes/ResourceLoader/UserOptionsModule.php b/includes/ResourceLoader/UserOptionsModule.php index 383a9905e404..58e74fb9b36f 100644 --- a/includes/ResourceLoader/UserOptionsModule.php +++ b/includes/ResourceLoader/UserOptionsModule.php @@ -2,8 +2,10 @@ namespace MediaWiki\ResourceLoader; +use MediaWiki\MainConfigNames; use MediaWiki\MediaWikiServices; use MediaWiki\User\Options\UserOptionsLookup; +use MediaWiki\User\UserTimeCorrection; /** * This program is free software; you can redistribute it and/or modify @@ -68,6 +70,16 @@ class UserOptionsModule extends Module { unset( $options[ $excludedKey ] ); } + // Update timezone offset (T323193) + if ( isset( $options['timecorrection'] ) ) { + $corr = new UserTimeCorrection( + $options['timecorrection'], + null, + $this->getConfig()->get( MainConfigNames::LocalTZoffset ) + ); + $options['timecorrection'] = $corr->toString(); + } + // Optimisation: Only output this function call if the user has non-default settings. if ( $options ) { $script .= 'mw.user.options.set(' . $context->encodeJson( $options ) . ');' . "\n"; -- cgit v1.2.3