From bc89ec0010c9203f88f3dd9a5362cc36629c3289 Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Sat, 15 Aug 2026 17:59:31 +0100 Subject: [PATCH] i18n(android): the interface speaks English, like the desktop app It was in French because the conversation is, and I never asked. The desktop app the client mirrors says Add field, Attach file, Show previous versions - someone moving between the two would notice the seam. Every visible string moved: buttons, statuses, detail row captions, log lines and the biometric prompt, plus the messages the modules surface through LastStoreError and LastBiometricError. Code comments stay in French. They address the author, not the user, and that is already the language of CLAUDE.md and much of the repo. Co-Authored-By: Claude Opus 5 --- android-client/PMA.Biometric.pas | 12 +-- android-client/PMA.Clipboard.pas | 2 +- android-client/PMA.Store.pas | 4 +- android-client/PMA.Vault.pas | 6 +- mainApk.pas | 146 +++++++++++++++---------------- 5 files changed, 85 insertions(+), 85 deletions(-) diff --git a/android-client/PMA.Biometric.pas b/android-client/PMA.Biometric.pas index 0ff3846..5ce4a59 100644 --- a/android-client/PMA.Biometric.pas +++ b/android-client/PMA.Biometric.pas @@ -104,12 +104,12 @@ begin try if not AcquireService(LSvc) then begin - FLastError := 'service biometrique absent de la plateforme'; + FLastError := 'biometric service missing from the platform'; Exit(False); end; Result := LSvc.CanAuthenticate; if not Result then - FLastError := 'CanAuthenticate: aucune protection utilisable'; + FLastError := 'CanAuthenticate: no usable protection'; except on E: Exception do begin @@ -136,7 +136,7 @@ begin FLastError := ''; if not AcquireService(LSvc) then begin - FLastError := 'service biometrique absent de la plateforme'; + FLastError := 'biometric service missing from the platform'; AThen(boFailed); // pas boUnavailable : on ne sait rien de l'appareil Exit; end; @@ -144,7 +144,7 @@ begin if not LSvc.CanAuthenticate then begin // Reponse NETTE du systeme : l'appareil n'a rien pour authentifier. - FLastError := 'CanAuthenticate: aucune protection utilisable'; + FLastError := 'CanAuthenticate: no usable protection'; AThen(boUnavailable); Exit; end; @@ -158,9 +158,9 @@ begin end; LProps := Default(TBiometricPromptProperties); - LProps.Title := 'Deverrouiller le coffre'; + LProps.Title := 'Unlock the vault'; LProps.Description := APrompt; - LProps.CancelButtonText := 'Annuler'; + LProps.CancelButtonText := 'Cancel'; LSvc.SetPromptProperties(LProps); // Meme raison que plus haut : ce qui remonte de la couche Java doit diff --git a/android-client/PMA.Clipboard.pas b/android-client/PMA.Clipboard.pas index 94ba175..c782cf4 100644 --- a/android-client/PMA.Clipboard.pas +++ b/android-client/PMA.Clipboard.pas @@ -68,7 +68,7 @@ begin // contenu, seulement son origine. // Les DEUX arguments sont des CharSequence : passer un JString au premier // compile ailleurs mais pas ici. - LClip := TJClipData.JavaClass.newPlainText(StrToJCharSequence('coffre'), + LClip := TJClipData.JavaClass.newPlainText(StrToJCharSequence('vault'), StrToJCharSequence(AText)); LExtras := TJPersistableBundle.JavaClass.init; LExtras.putBoolean(StringToJString(EXTRA_IS_SENSITIVE), True); diff --git a/android-client/PMA.Store.pas b/android-client/PMA.Store.pas index 0bc5003..7948158 100644 --- a/android-client/PMA.Store.pas +++ b/android-client/PMA.Store.pas @@ -594,7 +594,7 @@ begin LStore := OpenKeyStore; LCert := LStore.getCertificate(StringToJString(KEY_ALIAS_WRAP)); if LCert = nil then - raise EPMAStore.Create('Cle publique introuvable dans le Keystore'); + raise EPMAStore.Create('Public key not found in the Keystore'); LPub := LCert.getPublicKey; LFactory := TJKeyFactoryX.JavaClass.getInstance(LPub.getAlgorithm); Result := LFactory.generatePublic( @@ -804,7 +804,7 @@ begin end; {$ELSE} begin - raise EPMAStore.Create('Le stockage Keystore est propre a Android'); + raise EPMAStore.Create('Keystore storage is Android-only'); end; {$ENDIF} diff --git a/android-client/PMA.Vault.pas b/android-client/PMA.Vault.pas index 86c06b9..2eb8201 100644 --- a/android-client/PMA.Vault.pas +++ b/android-client/PMA.Vault.pas @@ -110,7 +110,7 @@ begin Result := Title.Trim; if Result = '' then Result := Site.Trim; if Result = '' then Result := Username.Trim; - if Result = '' then Result := '(sans nom)'; + if Result = '' then Result := '(no name)'; end; { TVault } @@ -206,7 +206,7 @@ begin Clear; LRoot := TJSONObject.ParseJSONValue(APlainJson) as TJSONObject; if LRoot = nil then - raise Exception.Create('Instantane illisible : ce n''est pas du JSON'); + raise Exception.Create('Snapshot unreadable: this is not JSON'); try FUsername := LRoot.GetValue('username', ''); FExportedAt := LRoot.GetValue('exported_at', @@ -228,7 +228,7 @@ begin end; if not LRoot.TryGetValue('entries', LArr) then - raise Exception.Create('Instantane sans tableau "entries"'); + raise Exception.Create('Snapshot has no "entries" array'); SetLength(FEntries, LArr.Count); N := 0; diff --git a/mainApk.pas b/mainApk.pas index 94eb352..c1fc5e2 100644 --- a/mainApk.pas +++ b/mainApk.pas @@ -1,4 +1,4 @@ -unit mainApk; +unit mainApk; { Client Android en LECTURE SEULE : deverrouiller, chercher, copier. @@ -218,7 +218,7 @@ var LEvents: IFMXApplicationEventService; begin inherited; - Caption := 'Coffre'; + Caption := 'Vault'; // Le fond de la forme, et non celui du style : tout le reste est peint // par-dessus, et un gris herite se verrait entre les blocs. Fill.Kind := TBrushKind.Solid; @@ -328,8 +328,8 @@ begin FLaySettings.Align := TAlignLayout.Top; FLaySettings.Height := 116; FLaySettings.Visible := False; - FToken := TopEdit(FLaySettings, 'jeton Bearer', FIRSTRUN_TOKEN); - FUrl := TopEdit(FLaySettings, 'URL de l''endpoint', FIRSTRUN_URL); + FToken := TopEdit(FLaySettings, 'bearer token', FIRSTRUN_TOKEN); + FUrl := TopEdit(FLaySettings, 'endpoint URL', FIRSTRUN_URL); FToken.Margins.Rect := RectF(16, 8, 16, 0); FUrl.Margins.Rect := RectF(16, 8, 16, 0); // Ce que l'appareil a retenu l'emporte sur les valeurs de premier lancement. @@ -347,20 +347,20 @@ begin LRow.Height := 48; LRow.Margins.Rect := RectF(16, 8, 16, 0); - FBtnLog := FlatButton(LRow, 'Journal', OnToggleLog); + FBtnLog := FlatButton(LRow, 'Log', OnToggleLog); FBtnLog.Align := TAlignLayout.Right; FBtnLog.Width := 120; FBtnLog.Margins.Left := 8; - FBtnSettings := FlatButton(LRow, 'Reglages du serveur', OnToggleSettings); + FBtnSettings := FlatButton(LRow, 'Server settings', OnToggleSettings); FBtnSettings.Align := TAlignLayout.Client; - FBtnSync := TopButton(FLayUnlock, 'Synchroniser', DoSync); + FBtnSync := TopButton(FLayUnlock, 'Sync', DoSync); // Accentue : c'est l'action attendue quand aucun coffre n'est encore la. - FBtnOpen := TopButton(FLayUnlock, 'Ouvrir le coffre', DoOpenLocal, 52, True); + FBtnOpen := TopButton(FLayUnlock, 'Open vault', DoOpenLocal, 52, True); // Pre-rempli depuis le fichier local, jamais depuis ce fichier-ci. - FPwd := TopEdit(FLayUnlock, 'mot de passe de sync', TEST_PWD, True); + FPwd := TopEdit(FLayUnlock, 'sync password', TEST_PWD, True); FPwd.Margins.Rect := RectF(16, 16, 16, 0); FStatus := TLabel.Create(Self); @@ -386,7 +386,7 @@ begin LBar.Align := TAlignLayout.Bottom; LBar.Height := 52; - LBtnLock := FlatButton(LBar, 'Verrouiller', OnBackTap); + LBtnLock := FlatButton(LBar, 'Lock', OnBackTap); LBtnLock.Align := TAlignLayout.Left; LBtnLock.Margins.Rect := RectF(16, 4, 0, 8); LBtnLock.Width := 140; @@ -422,7 +422,7 @@ begin // // Bouton peint et non TComboBox : le combo etait l'un des deux derniers // controles a porter encore le style, et il jurait avec le reste. - FBtnFolder := FlatButton(LTop, 'Tous les dossiers', OnFolderTap); + FBtnFolder := FlatButton(LTop, 'All folders', OnFolderTap); FBtnFolder.Align := TAlignLayout.Right; FBtnFolder.Width := 170; FBtnFolder.Margins.Left := 8; @@ -445,7 +445,7 @@ begin FSearchHint.Margins.Left := 10; FSearchHint.HitTest := False; FSearchHint.Opacity := 0.45; - FSearchHint.Text := 'Rechercher'; + FSearchHint.Text := 'Search'; end; /// Intitule discret au-dessus d'une valeur. Meme forme pour toutes les @@ -504,7 +504,7 @@ begin FLayDetail.Align := TAlignLayout.Client; FLayDetail.Visible := False; - LBtnBack := FlatButton(FLayDetail, 'Retour', OnBackTap); + LBtnBack := FlatButton(FLayDetail, 'Back', OnBackTap); LBtnBack.Align := TAlignLayout.Bottom; LBtnBack.Height := 52; LBtnBack.Margins.Rect := RectF(16, 8, 16, 8); @@ -547,15 +547,15 @@ begin if HasVault then begin if VaultNeedsAuth then - Status('Coffre present sur cet appareil - protege') + Status('Vault on this device - protected') else - Status('Coffre present sur cet appareil'); - SetButtonText(FBtnSync, 'Synchroniser'); + Status('Vault on this device'); + SetButtonText(FBtnSync, 'Sync'); end else begin - Status('Aucun coffre local - premier telechargement'); - SetButtonText(FBtnSync, 'Telecharger le coffre'); + Status('No local vault - first download'); + SetButtonText(FBtnSync, 'Download vault'); end; end; @@ -635,10 +635,10 @@ begin // Le corps d'une note est stocke dans le champ mot de passe : meme donnee, // autre intitule, et pas de raison de la masquer comme un secret. - if FCurrent.IsNote then LPwdCaption := 'Contenu' else LPwdCaption := 'Mot de passe'; + if FCurrent.IsNote then LPwdCaption := 'Content' else LPwdCaption := 'Password'; Add('Site', FCurrent.Site, False); - Add('Identifiant', FCurrent.Username, False); + Add('Username', FCurrent.Username, False); Add(LPwdCaption, FCurrent.Password, not FCurrent.IsNote); for I := 0 to High(FCurrent.Fields) do Add(FCurrent.Fields[I].Caption, FCurrent.Fields[I].Value, @@ -658,16 +658,16 @@ begin // rendu bidirectionnel de FMX en fait n'importe quoi. Separes, chacun se // lit dans son sens. for I := 0 to High(FCurrent.Attachments) do - Add('Piece jointe - ' + HumanSize(FCurrent.Attachments[I].SizeBytes), + Add('Attachment - ' + HumanSize(FCurrent.Attachments[I].SizeBytes), FCurrent.Attachments[I].FileName, False); - Add('Dossier / etiquettes', LMeta, False); + Add('Folder / tags', LMeta, False); // Le sous-type n'existe que pour les modeles (carte, cle SSH, serveur...) ; // Add ignore une valeur vide, donc rien a tester ici. Add('Type', FCurrent.Template, False); // Horodatage tel quel : c'est de l'ISO 8601 UTC, lisible, et le reformater // demanderait de decider d'un fuseau alors qu'il n'y a rien a arbitrer. - Add('Modifie le', FCurrent.UpdatedAt, False); + Add('Updated', FCurrent.UpdatedAt, False); SetLength(FRowValues, Length(FRows)); SetLength(FRowShown, Length(FRows)); @@ -678,12 +678,12 @@ begin LOrder := 0; if FCurrent.Totp <> '' then begin - LRow := MakeRow(FDetBody, 'Code 2FA', LOrder, FTotpValue); + LRow := MakeRow(FDetBody, '2FA code', LOrder, FTotpValue); Inc(LOrder); SetLength(FRowCtls, Length(FRowCtls) + 1); FRowCtls[High(FRowCtls)] := LRow; FTotpValue.TextSettings.Font.Size := 26; - AddRowButton(LRow, 'Copier', -1, OnCopyTap); + AddRowButton(LRow, 'Copy', -1, OnCopyTap); OnTotpTick(nil); // pas d'attente d'une seconde a l'ouverture FTotpTimer.Enabled := True; end; @@ -698,11 +698,11 @@ begin if FRows[I].Secret then begin LValue.Text := MASK; - AddRowButton(LRow, 'Afficher', I, OnRevealTap); + AddRowButton(LRow, 'Show', I, OnRevealTap); end else LValue.Text := FRows[I].Value; - AddRowButton(LRow, 'Copier', I, OnCopyTap); + AddRowButton(LRow, 'Copy', I, OnCopyTap); end; // La derniere ligne affichee touche sinon le bord du defilement, ce qui la // fait passer pour tronquee alors qu'elle est juste collee. @@ -728,7 +728,7 @@ begin if FTotpCode = '' then begin // Un secret illisible ne doit pas passer pour un code : le dire. - FTotpValue.Text := 'secret illisible'; + FTotpValue.Text := 'unreadable secret'; FTotpTimer.Enabled := False; Exit; end; @@ -810,8 +810,8 @@ end; procedure TForm8.AdoptVault(const APlainJson: string); begin FVault.LoadFromJson(APlainJson); - Log(' ' + FVault.LiveCount.ToString + ' entrees, ' + - FVault.TrashCount.ToString + ' en corbeille'); + Log(' ' + FVault.LiveCount.ToString + ' entries, ' + + FVault.TrashCount.ToString + ' in trash'); RebuildIcons; PopulateFolders; Touch; @@ -845,7 +845,7 @@ begin on E: Exception do begin // Un coffre local illisible se remplace, il ne se repare pas. - Log('Coffre local corrompu : ' + E.Message); + Log('Local vault corrupt: ' + E.Message); ClearVault; ShowUnlock; end; @@ -856,8 +856,8 @@ begin // ShowUnlock AVANT le message : il repositionne le statut selon ce que // l'appareil detient, et ecraserait donc l'explication. ShowUnlock; - Log('Coffre local illisible : ' + LastStoreError); - Status('Coffre local illisible - voir le journal'); + Log('Local vault unreadable: ' + LastStoreError); + Status('Local vault unreadable - see log'); end; end; @@ -877,13 +877,13 @@ begin Exit; end; - if not AAuto then Status('Authentification...'); - AuthenticateThen('Acceder au coffre', + if not AAuto then Status('Authenticating...'); + AuthenticateThen('Unlock the vault', procedure(AOutcome: TBioOutcome) begin case AOutcome of boOK: OpenLocalNow; - boCancelled: Status('Annule'); + boCancelled: Status('Cancelled'); boUnavailable: // Reponse nette du systeme : plus rien pour authentifier sur cet // appareil, donc la cle est perdue et le cache avec elle. @@ -895,13 +895,13 @@ begin // masquerait la vraie cause. L'utilisateur tranche en reessayant. if AAuto then begin - Log('Authentification indisponible au lancement - cache conserve'); + Log('Authentication unavailable at launch - cache kept'); Log(' ' + LastBiometricError); - Status('Authentification indisponible - reessayer'); + Status('Authentication unavailable - try again'); end else begin - Log('Plus aucune protection sur l''appareil - cache efface'); + Log('No protection left on this device - cache cleared'); Log(' ' + LastBiometricError); ClearVault; ShowUnlock; @@ -909,8 +909,8 @@ begin else // boFailed : la couche Java a leve quelque chose. Cela ne dit rien de // l'appareil, donc on ne touche a rien - on rapporte. - Log('Echec de l''authentification : ' + LastBiometricError); - Status('Echec de l''authentification'); + Log('Authentication failed: ' + LastBiometricError); + Status('Authentication failed'); end; end); end; @@ -930,7 +930,7 @@ begin if LPwd = '' then LPwd := FSyncPwd; if LPwd = '' then begin - Status('Mot de passe de sync requis'); + Status('Sync password required'); Exit; end; FLog.Lines.Clear; @@ -942,16 +942,16 @@ begin // cet appareil - c'est lui qui exprime le choix de l'utilisateur. if VaultNeedsAuth and (FVault.Count = 0) then begin - Status('Authentification...'); - AuthenticateThen('Acceder au coffre', + Status('Authenticating...'); + AuthenticateThen('Unlock the vault', procedure(AOutcome: TBioOutcome) begin if AOutcome = boOK then FetchAndDecrypt(LUrl, LTok, LPwd) else begin - Log('Synchronisation refusee : ' + LastBiometricError); - Status('Authentification requise'); + Log('Sync refused: ' + LastBiometricError); + Status('Authentication required'); end; end); Exit; @@ -966,7 +966,7 @@ var LCanAuth: Boolean; begin SetBusy(True); - if not ASilent then Status('Telechargement...'); + if not ASilent then Status('Downloading...'); // NE JAMAIS FERMER LE CACHE DERRIERE UNE PORTE QU'ON NE SAIT PAS OUVRIR. // Demander une cle protegee par authentification alors que l'invite ne peut // pas s'afficher (permission absente, aucune protection configuree) produit @@ -997,7 +997,7 @@ begin LSw := TStopwatch.StartNew; LRes := LClient.Fetch(FETag); LSw.Stop; - Log(' HTTP ' + LRes.HttpCode.ToString + ' en ' + + Log(' HTTP ' + LRes.HttpCode.ToString + ' in ' + LSw.ElapsedMilliseconds.ToString + ' ms'); case LRes.Status of @@ -1018,14 +1018,14 @@ begin // rafraichissement de fond c'est le cas courant, et il // doit passer inapercu - surtout pas de retour a la liste // alors que l'utilisateur consulte une entree. - Log(' 304 : inchange, coffre deja ouvert') + Log(' 304: unchanged, vault already open') else if LCached <> '' then begin - Log(' 304 : inchange, coffre servi depuis le cache'); + Log(' 304: unchanged, vault served from cache'); try AdoptVault(LCached); except - on E: Exception do Status('Cache illisible : ' + E.Message); + on E: Exception do Status('Cache unreadable: ' + E.Message); end; end else @@ -1034,7 +1034,7 @@ begin // On le jette pour que l'appel suivant retelecharge. FETag := ''; if not ASilent then - Status('Cache absent - appuyer a nouveau pour telecharger'); + Status('No cache - tap again to download'); end; end); Exit; @@ -1042,10 +1042,10 @@ begin ssNoSnapshot: begin // Le serveur repond pareil dans les deux cas, par conception. - Log(' 404 : pas d''instantane OU jeton invalide'); + Log(' 404: no snapshot OR invalid token'); if not ASilent then TThread.Queue(nil, procedure - begin Status('Rien a telecharger (404)'); end); + begin Status('Nothing to download (404)'); end); Exit; end; ssRefused, ssHttpError, ssNetworkError: @@ -1053,17 +1053,17 @@ begin // En fond, un echec reseau ne se signale pas : le coffre local // est affiche, hors ligne est un etat normal, et une alerte ici // apprendrait a l'utilisateur a ignorer les alertes. - Log(' echec : ' + LRes.Error); + Log(' failed: ' + LRes.Error); if not ASilent then TThread.Queue(nil, procedure - begin Status('Echec reseau'); end); + begin Status('Network failure'); end); Exit; end; end; - Log(' recu ' + Length(LRes.Container).ToString + ' octets'); + Log(' received ' + Length(LRes.Container).ToString + ' bytes'); if not ASilent then - TThread.Queue(nil, procedure begin Status('Dechiffrement...'); end); + TThread.Queue(nil, procedure begin Status('Decrypting...'); end); LSw := TStopwatch.StartNew; // Volontairement AVANT de retenir l'ETag : un echec ici doit laisser @@ -1071,7 +1071,7 @@ begin LPlain := DecryptContainer(LRes.Container, APwd); LSw.Stop; FETag := LRes.ETag; - Log(' dechiffre en ' + LSw.ElapsedMilliseconds.ToString + ' ms'); + Log(' decrypted in ' + LSw.ElapsedMilliseconds.ToString + ' ms'); // Ecrit APRES un dechiffrement reussi seulement, et avec son ETag : // les deux doivent rester coherents. @@ -1083,16 +1083,16 @@ begin // pas des reglages saisis, on enregistre des reglages EPROUVES. SaveDeviceSettings(AUrl, ATok); if VaultNeedsAuth then - Log(' cache ecrit - protege par biometrie/code') + Log(' cache written - protected by biometrics/PIN') else if LCanAuth then - Log(' cache ecrit - cle simple (Keystore a refuse la contrainte)') + Log(' cache written - plain key (Keystore refused the constraint)') else - Log(' cache ecrit - AUCUNE protection : pas d''authentification ici'); + Log(' cache written - NO protection: no authentication here'); except // La classe compte autant que le message : "methode introuvable" // designe une signature JNI fausse, pas un probleme de stockage. on E: Exception do - Log(' cache non ecrit : ' + E.ClassName + ' - ' + E.Message); + Log(' cache not written: ' + E.ClassName + ' - ' + E.Message); end; TThread.Queue(nil, @@ -1104,13 +1104,13 @@ begin except on E: Exception do if not ASilent then - Status('Instantane illisible : ' + E.Message); + Status('Snapshot unreadable: ' + E.Message); end; end); except on E: Exception do begin - Log('ERREUR ' + E.ClassName + ' : ' + E.Message); + Log('ERROR ' + E.ClassName + ' : ' + E.Message); // Le cas de loin le plus frequent, et le message par defaut ne le // dit pas : GCM echoue a la verification quand la cle est fausse. // @@ -1120,7 +1120,7 @@ begin // le champ prerempli, et le coffre local reste consultable. if not ASilent then TThread.Queue(nil, procedure - begin Status('Echec - mot de passe de sync incorrect ?'); end); + begin Status('Failed - wrong sync password?'); end); end; end; finally @@ -1188,7 +1188,7 @@ begin LPrev := FFolderNames[FFolderSel]; SetLength(FFolderNames, 1); - FFolderNames[0] := 'Tous les dossiers'; + FFolderNames[0] := 'All folders'; for LF in FVault.Folders do begin SetLength(FFolderNames, Length(FFolderNames) + 1); @@ -1341,7 +1341,7 @@ begin end; if (FSearch.Text.Trim = '') and (LFolder = '') then - FCount.Text := FVault.LiveCount.ToString + ' entrees' + FCount.Text := FVault.LiveCount.ToString + ' entries' else FCount.Text := Length(FFiltered).ToString + ' / ' + FVault.LiveCount.ToString; @@ -1369,12 +1369,12 @@ begin if FRowShown[I] then begin FRowValues[I].Text := FRows[I].Value; - SetButtonText(TRectangle(Sender), 'Masquer'); + SetButtonText(TRectangle(Sender), 'Hide'); end else begin FRowValues[I].Text := MASK; - SetButtonText(TRectangle(Sender), 'Afficher'); + SetButtonText(TRectangle(Sender), 'Show'); end; end; @@ -1388,7 +1388,7 @@ begin // -1 : la ligne du TOTP, qui n'est pas dans FRows - sa valeur change chaque // periode et ne vit que dans FTotpCode. Copie sans l'espace d'affichage. if I = -1 then - CopyToClipboard(FTotpCode, 'Code 2FA') + CopyToClipboard(FTotpCode, '2FA code') else if (I >= 0) and (I <= High(FRows)) then CopyToClipboard(FRows[I].Value, FRows[I].Caption); end; @@ -1408,7 +1408,7 @@ begin // ponytail: le message reste jusqu'a la prochaine navigation, pas de minuterie // pour l'effacer. Un deuxieme TTimer pour deux secondes d'affichage serait // plus de code que ce que ca apporte. - FDetToast.Text := ALabel + ' copie - presse-papiers efface dans ' + + FDetToast.Text := ALabel + ' copied - clipboard cleared in ' + CLIPBOARD_CLEAR_SECONDS.ToString + ' s'; end; @@ -1477,7 +1477,7 @@ begin end; if MinutesBetween(Now, FLastActivity) >= AUTOLOCK_MINUTES then begin - Log('Verrouille apres ' + AUTOLOCK_MINUTES.ToString + ' min d''inactivite'); + Log('Locked after ' + AUTOLOCK_MINUTES.ToString + ' min idle'); LockVault; end; end;