diff --git a/scripts/langindex.json b/scripts/langindex.json index bca4fe470..b245d6a4e 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -2167,6 +2167,8 @@ "core.settings.cannotsyncloggedout": "local_moodlemobileapp", "core.settings.cannotsyncoffline": "local_moodlemobileapp", "core.settings.cannotsyncwithoutwifi": "local_moodlemobileapp", + "core.settings.changelanguage": "local_moodlemobileapp", + "core.settings.changelanguagealert": "local_moodlemobileapp", "core.settings.colorscheme": "local_moodlemobileapp", "core.settings.colorscheme-dark": "local_moodlemobileapp", "core.settings.colorscheme-light": "local_moodlemobileapp", diff --git a/src/core/features/settings/lang.json b/src/core/features/settings/lang.json index 29c25bbff..42114fcd0 100644 --- a/src/core/features/settings/lang.json +++ b/src/core/features/settings/lang.json @@ -6,11 +6,13 @@ "cannotsyncloggedout": "This site cannot be synchronised because you've logged out. Please try again when you're logged in the site again.", "cannotsyncoffline": "Cannot synchronise offline.", "cannotsyncwithoutwifi": "Cannot synchronise because the current settings only allow to synchronise when connected to Wi-Fi. Please connect to a Wi-Fi network.", - "colorscheme": "Color Scheme", + "changelanguage": "Change to {{$a}}", + "changelanguagealert": "Changing the language will restart the app.", "colorscheme-dark": "Dark", "colorscheme-light": "Light", - "colorscheme-system": "System default", "colorscheme-system-notice": "System default mode will depend on your device support.", + "colorscheme-system": "System default", + "colorscheme": "Color Scheme", "compilationinfo": "Compilation info", "copyinfo": "Copy device info on the clipboard", "cordovadevicemodel": "Cordova device model", diff --git a/src/core/features/settings/pages/general/general.ts b/src/core/features/settings/pages/general/general.ts index e8ef89874..a1487f255 100644 --- a/src/core/features/settings/pages/general/general.ts +++ b/src/core/features/settings/pages/general/general.ts @@ -22,9 +22,10 @@ import { CorePushNotifications } from '@features/pushnotifications/services/push import { CoreSettingsHelper, CoreColorScheme, CoreZoomLevel } from '../../services/settings-helper'; import { CoreApp } from '@services/app'; import { CoreIframeUtils } from '@services/utils/iframe'; -import { Diagnostic } from '@singletons'; +import { Diagnostic, Translate } from '@singletons'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; +import { AlertButton } from '@ionic/angular'; /** * Page that displays the general settings. @@ -110,14 +111,64 @@ export class CoreSettingsGeneralPage { /** * Called when a new language is selected. */ - languageChanged(): void { - CoreLang.changeCurrentLanguage(this.selectedLanguage).finally(async () => { - // Invalidate cache for all sites to get the content in the right language. - const sites = await CoreSites.getSitesInstances(); - await CoreUtils.ignoreErrors(Promise.all(sites.map((site) => site.invalidateWsCache()))); + async languageChanged(): Promise { + const previousLanguage = await CoreLang.getCurrentLanguage(); + if (this.selectedLanguage === previousLanguage) { + // Prevent opening again. - CoreEvents.trigger(CoreEvents.LANGUAGE_CHANGED, this.selectedLanguage); - }); + return; + } + + const previousLanguageCancel = Translate.instant('core.cancel'); + + try { + await CoreLang.changeCurrentLanguage(this.selectedLanguage); + } finally { + const langName = this.languages.find((lang) => lang.code == this.selectedLanguage)?.name; + + const buttons: AlertButton[] = [ + { + text: previousLanguageCancel, + role: 'cancel', + handler: (): void => { + clearTimeout(timeout); + this.selectedLanguage = previousLanguage; + CoreLang.changeCurrentLanguage(this.selectedLanguage); + }, + }, + { + text: Translate.instant('core.settings.changelanguage', { $a: langName }), + cssClass: 'timed-button', + handler: (): void => { + clearTimeout(timeout); + this.applyLanguageAndRestart(); + }, + }, + ]; + + const alert = await CoreDomUtils.showAlertWithOptions( + { + message: Translate.instant('core.settings.changelanguagealert'), + buttons, + }, + ); + const timeout = window.setTimeout(async () => { + await alert.dismiss(); + this.applyLanguageAndRestart(); + }, 10000); + } + } + + /** + * Apply language changes and restart the app. + */ + protected async applyLanguageAndRestart(): Promise { + // Invalidate cache for all sites to get the content in the right language. + const sites = await CoreSites.getSitesInstances(); + await CoreUtils.ignoreErrors(Promise.all(sites.map((site) => site.invalidateWsCache()))); + + CoreEvents.trigger(CoreEvents.LANGUAGE_CHANGED, this.selectedLanguage); + window.location.reload(); } /** diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index fe26a3881..bfca7c48e 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -461,6 +461,34 @@ ion-alert.core-nohead { } } +@keyframes scaleFrom0 { + from { + /* More performant than animating `width` */ + transform: scaleX(0); + } +} + +ion-alert .alert-button.timed-button{ + position: relative; + + &::before { + content: ''; + position: absolute; + width: 100%; + left: 0; + right: 0; + bottom: 0; + top: 0; + background-color: var(--primary-tint); + animation: scaleFrom0 10s forwards linear; + transform-origin: left; + @include rtl() { + transform-origin: right; + } + z-index: -1; + } +} + ion-alert { --border-radius: var(--huge-radius);