Show a specific error message for each language pack failure

All failure paths of the download/extract/swap flow displayed the same
generic ERROR_DOWNLOADING popup, which made user reports impossible to
map to an actual cause (the recent .DS_Store swap failure surfaced as a
plain "download error" even though the download and extraction had both
succeeded).

- Replace the magic error indices with a DownloadError enum, so call
  sites read _show_error(DownloadError.REPLACE_FAILED) instead of
  _show_error(2) with a comment.
- Add three specific messages with fr/es/pt_BR/it translations:
  - ERROR_EXTRACTING_PACKAGE: archive missing or extraction failed
  - ERROR_INVALID_PACKAGE: extracted pack failed the validity check
    (kept distinct from INVALID_LANGUAGE_DIRECTORY, which describes the
    local pack found while offline)
  - ERROR_REPLACING_PACKAGE: previous pack could not be removed or the
    new one could not be renamed into place; tells the user to restart
    and retry rather than suggesting a download problem
- ERROR_DOWNLOADING is now used only for actual download/server
  failures.

Localization completeness check, naming lint and GUT suite (65/65) all
pass.
This commit is contained in:
Adrien Ufferte
2026-06-11 10:23:14 +02:00
parent 239643c325
commit 5bba77c8ea
2 changed files with 29 additions and 12 deletions
+3
View File
@@ -445,6 +445,9 @@ CHANGE_LANGUAGE_ERROR,"Impossible de changer la langue. Veuillez vérifier votre
DISCONNECTED_ERROR,Vous avez été déconnecté. Veuillez renseigner vos identifiants à nouveau.,Se ha cerrado la sesión. Vuelva a introducir sus datos de acceso.,Você foi desconectado. Digite seus detalhes de login novamente.,Sei stato disconnesso. Inserisci nuovamente le tue credenziali. DISCONNECTED_ERROR,Vous avez été déconnecté. Veuillez renseigner vos identifiants à nouveau.,Se ha cerrado la sesión. Vuelva a introducir sus datos de acceso.,Você foi desconectado. Digite seus detalhes de login novamente.,Sei stato disconnesso. Inserisci nuovamente le tue credenziali.
INVALID_LANGUAGE_DIRECTORY,Dossier de langue invalide,Carpeta de idioma no válida,Pasta de idioma inválida,Cartella lingua non valida INVALID_LANGUAGE_DIRECTORY,Dossier de langue invalide,Carpeta de idioma no válida,Pasta de idioma inválida,Cartella lingua non valida
NO_INTERNET_ACCESS,Vous n'êtes pas connecté à Internet. Veuillez vérifier votre connexion et réessayer.,No está conectado a Internet. Compruebe su conexión e inténtelo de nuevo.,Você não está conectado à Internet. Verifique sua conexão e tente novamente.,Non sei connesso a Internet. Verifica la connessione e riprova. NO_INTERNET_ACCESS,Vous n'êtes pas connecté à Internet. Veuillez vérifier votre connexion et réessayer.,No está conectado a Internet. Compruebe su conexión e inténtelo de nuevo.,Você não está conectado à Internet. Verifique sua conexão e tente novamente.,Non sei connesso a Internet. Verifica la connessione e riprova.
ERROR_EXTRACTING_PACKAGE,Le pack de langue téléchargé n'a pas pu être extrait. Veuillez réessayer.,No se ha podido extraer el paquete de idioma descargado. Inténtelo de nuevo.,Não foi possível extrair o pacote de idioma baixado. Tente novamente.,Impossibile estrarre il pacchetto lingua scaricato. Riprova.
ERROR_INVALID_PACKAGE,Le pack de langue téléchargé est invalide ou incomplet. Veuillez réessayer plus tard.,El paquete de idioma descargado no es válido o está incompleto. Inténtelo de nuevo más tarde.,O pacote de idioma baixado é inválido ou está incompleto. Tente novamente mais tarde.,Il pacchetto lingua scaricato non è valido o è incompleto. Riprova più tardi.
ERROR_REPLACING_PACKAGE,Impossible de remplacer l'ancien pack de langue. Veuillez redémarrer l'application et réessayer.,No se ha podido reemplazar el paquete de idioma anterior. Reinicie la aplicación e inténtelo de nuevo.,Não foi possível substituir o pacote de idioma anterior. Reinicie o aplicativo e tente novamente.,Impossibile sostituire il pacchetto lingua precedente. Riavvia l'applicazione e riprova.
PICK_YOUR_DEVICE,Choisissez votre appareil,Elige tu dispositivo,Escolha seu dispositivo,Scegli il tuo dispositivo PICK_YOUR_DEVICE,Choisissez votre appareil,Elige tu dispositivo,Escolha seu dispositivo,Scegli il tuo dispositivo
SIGN_IN,Connexion,Conexión,Conexão,Accedi SIGN_IN,Connexion,Conexión,Conexão,Accedi
RETURN_TO_MAIN_MENU,Retour à laccueil,Volver a la página principal,Voltar para a página inicial,Torna alla schermata principale RETURN_TO_MAIN_MENU,Retour à laccueil,Volver a la página principal,Voltar para a página inicial,Torna alla schermata principale
Can't render this file because it contains an unexpected character in line 46 and column 273.
@@ -1,15 +1,29 @@
class_name PackageDownloader class_name PackageDownloader
extends Control extends Control
enum DownloadError {
DISCONNECTED,
NO_INTERNET,
DOWNLOAD_FAILED,
INVALID_LOCAL_PACK,
EXTRACTION_FAILED,
INVALID_PACKAGE,
REPLACE_FAILED,
}
const MAIN_MENU_SCENE_PATH: String = "res://sources/menus/main/main_menu.tscn" const MAIN_MENU_SCENE_PATH: String = "res://sources/menus/main/main_menu.tscn"
const DEVICE_SELECTION_SCENE_PATH: String = "res://sources/menus/device_selection/device_selection.tscn" const DEVICE_SELECTION_SCENE_PATH: String = "res://sources/menus/device_selection/device_selection.tscn"
const LOGIN_SCENE_PATH: String = "res://sources/menus/login/login.tscn" const LOGIN_SCENE_PATH: String = "res://sources/menus/login/login.tscn"
const USER_LANGUAGE_RESOURCES_PATH: String = "user://language_resources" const USER_LANGUAGE_RESOURCES_PATH: String = "user://language_resources"
# Translation key shown in the error popup for each DownloadError value
const ERROR_MESSAGES: Array[String] = [ const ERROR_MESSAGES: Array[String] = [
"DISCONNECTED_ERROR", "DISCONNECTED_ERROR",
"NO_INTERNET_ACCESS", "NO_INTERNET_ACCESS",
"ERROR_DOWNLOADING", "ERROR_DOWNLOADING",
"INVALID_LANGUAGE_DIRECTORY", "INVALID_LANGUAGE_DIRECTORY",
"ERROR_EXTRACTING_PACKAGE",
"ERROR_INVALID_PACKAGE",
"ERROR_REPLACING_PACKAGE",
] ]
var language: String var language: String
@@ -41,7 +55,7 @@ func _ready() -> void:
var teacher_settings: TeacherSettings = UserDataManager.teacher_settings var teacher_settings: TeacherSettings = UserDataManager.teacher_settings
if not teacher_settings: if not teacher_settings:
UserDataManager.logout() UserDataManager.logout()
_show_error(0) _show_error(DownloadError.DISCONNECTED)
return return
if teacher_settings.server_language_validated: if teacher_settings.server_language_validated:
@@ -59,10 +73,10 @@ func _ready() -> void:
_go_to_next_scene() _go_to_next_scene()
else: else:
Log.warn("PackageDownloader: Offline and language directory %s is invalid" % current_language_path) Log.warn("PackageDownloader: Offline and language directory %s is invalid" % current_language_path)
_show_error(3) # Error downloading _show_error(DownloadError.INVALID_LOCAL_PACK)
else: else:
Log.warn("PackageDownloader: Offline with no language directory available") Log.warn("PackageDownloader: Offline with no language directory available")
_show_error(1) # No internet access _show_error(DownloadError.NO_INTERNET)
return return
# Gets the info of the language pack on the server # Gets the info of the language pack on the server
@@ -75,12 +89,12 @@ func _ready() -> void:
elif res.code == 401: elif res.code == 401:
UserDataManager.logout() UserDataManager.logout()
Log.warn("PackageDownloader: Authentication failed while fetching language pack URL") Log.warn("PackageDownloader: Authentication failed while fetching language pack URL")
_show_error(0) # Disconnected error _show_error(DownloadError.DISCONNECTED)
return return
else: else:
UserDataManager.logout() UserDataManager.logout()
Log.warn("PackageDownloader: Unexpected response %d while fetching language pack URL" % res.code) Log.warn("PackageDownloader: Unexpected response %d while fetching language pack URL" % res.code)
_show_error(2) # Error downloading _show_error(DownloadError.DOWNLOAD_FAILED)
return return
# If the language pack is not already downloaded or an update is needed # If the language pack is not already downloaded or an update is needed
@@ -106,7 +120,7 @@ func _ready() -> void:
var request_error: Error = http_request.request(res.body.url as String) var request_error: Error = http_request.request(res.body.url as String)
if request_error != OK: if request_error != OK:
Log.error("PackageDownloader: Cannot start language pack download: %s" % error_string(request_error)) Log.error("PackageDownloader: Cannot start language pack download: %s" % error_string(request_error))
_show_error(2) # Error downloading _show_error(DownloadError.DOWNLOAD_FAILED)
else: else:
download_bar.value = 1 download_bar.value = 1
extract_bar.value = 1 extract_bar.value = 1
@@ -152,7 +166,7 @@ func _copy_data(this: PackageDownloader) -> void:
# Check if a zip exists for the complete locale # Check if a zip exists for the complete locale
if not FileAccess.file_exists(USER_LANGUAGE_RESOURCES_PATH.path_join(language + ".zip")): if not FileAccess.file_exists(USER_LANGUAGE_RESOURCES_PATH.path_join(language + ".zip")):
Log.warn("PackageDownloader: No downloaded archive found for %s" % language) Log.warn("PackageDownloader: No downloaded archive found for %s" % language)
this.call_thread_safe("_show_error", 2) # Error downloading this.call_thread_safe("_show_error", DownloadError.EXTRACTION_FAILED)
return return
Log.trace("PackageDownloader: Extracting downloaded package") Log.trace("PackageDownloader: Extracting downloaded package")
@@ -185,7 +199,7 @@ func _copy_data(this: PackageDownloader) -> void:
var subfolder: String = unzipper.extract(language_zip_path, temp_extract_path, false) var subfolder: String = unzipper.extract(language_zip_path, temp_extract_path, false)
if subfolder == "": if subfolder == "":
Log.error("PackageDownloader: Extraction failed for %s" % language_zip_path) Log.error("PackageDownloader: Extraction failed for %s" % language_zip_path)
this.call_thread_safe("_show_error", 2) # Error downloading this.call_thread_safe("_show_error", DownloadError.EXTRACTION_FAILED)
return return
# Check the new pack before replacing the current one # Check the new pack before replacing the current one
@@ -194,7 +208,7 @@ func _copy_data(this: PackageDownloader) -> void:
Log.error("PackageDownloader: Extracted package at %s is invalid, keeping the current language pack" % new_pack_path) Log.error("PackageDownloader: Extracted package at %s is invalid, keeping the current language pack" % new_pack_path)
Utils.delete_directory_recursive(ProjectSettings.globalize_path(temp_extract_path)) Utils.delete_directory_recursive(ProjectSettings.globalize_path(temp_extract_path))
DirAccess.remove_absolute(language_zip_path) DirAccess.remove_absolute(language_zip_path)
this.call_thread_safe("_show_error", 3) # Invalid language directory this.call_thread_safe("_show_error", DownloadError.INVALID_PACKAGE)
return return
# Replace the previous pack, now that the new one is fully extracted # Replace the previous pack, now that the new one is fully extracted
@@ -204,7 +218,7 @@ func _copy_data(this: PackageDownloader) -> void:
if DirAccess.dir_exists_absolute(current_language_path): if DirAccess.dir_exists_absolute(current_language_path):
# Keep the temporary directory so the new pack is not lost # Keep the temporary directory so the new pack is not lost
Log.error("PackageDownloader: Cannot remove the previous language directory, aborting swap") Log.error("PackageDownloader: Cannot remove the previous language directory, aborting swap")
this.call_thread_safe("_show_error", 2) # Error downloading this.call_thread_safe("_show_error", DownloadError.REPLACE_FAILED)
return return
var error: Error = DirAccess.rename_absolute(new_pack_path, current_language_path) var error: Error = DirAccess.rename_absolute(new_pack_path, current_language_path)
@@ -212,7 +226,7 @@ func _copy_data(this: PackageDownloader) -> void:
# Keep the temporary directory so the data is not lost; the next # Keep the temporary directory so the data is not lost; the next
# launch will detect the missing pack and download it again # launch will detect the missing pack and download it again
Log.error("PackageDownloader: Error " + error_string(error) + " while renaming folder from %s to %s" % [new_pack_path, current_language_path]) Log.error("PackageDownloader: Error " + error_string(error) + " while renaming folder from %s to %s" % [new_pack_path, current_language_path])
this.call_thread_safe("_show_error", 2) # Error downloading this.call_thread_safe("_show_error", DownloadError.REPLACE_FAILED)
return return
Log.trace("PackageDownloader: Package extracted to %s" % current_language_path) Log.trace("PackageDownloader: Package extracted to %s" % current_language_path)
@@ -225,7 +239,7 @@ func _copy_data(this: PackageDownloader) -> void:
this.call_thread_safe("_go_to_next_scene") this.call_thread_safe("_go_to_next_scene")
func _show_error(error: int) -> void: func _show_error(error: DownloadError) -> void:
Log.warn("PackageDownloader: Displaying error %d (%s)" % [error, ERROR_MESSAGES[error]]) Log.warn("PackageDownloader: Displaying error %d (%s)" % [error, ERROR_MESSAGES[error]])
error_popup.content_text = ERROR_MESSAGES[error] error_popup.content_text = ERROR_MESSAGES[error]
error_popup.show() error_popup.show()