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 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-15 17:59:31 +01:00
parent b900b5b609
commit bc89ec0010
5 changed files with 85 additions and 85 deletions
+6 -6
View File
@@ -104,12 +104,12 @@ begin
try try
if not AcquireService(LSvc) then if not AcquireService(LSvc) then
begin begin
FLastError := 'service biometrique absent de la plateforme'; FLastError := 'biometric service missing from the platform';
Exit(False); Exit(False);
end; end;
Result := LSvc.CanAuthenticate; Result := LSvc.CanAuthenticate;
if not Result then if not Result then
FLastError := 'CanAuthenticate: aucune protection utilisable'; FLastError := 'CanAuthenticate: no usable protection';
except except
on E: Exception do on E: Exception do
begin begin
@@ -136,7 +136,7 @@ begin
FLastError := ''; FLastError := '';
if not AcquireService(LSvc) then if not AcquireService(LSvc) then
begin 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 AThen(boFailed); // pas boUnavailable : on ne sait rien de l'appareil
Exit; Exit;
end; end;
@@ -144,7 +144,7 @@ begin
if not LSvc.CanAuthenticate then if not LSvc.CanAuthenticate then
begin begin
// Reponse NETTE du systeme : l'appareil n'a rien pour authentifier. // Reponse NETTE du systeme : l'appareil n'a rien pour authentifier.
FLastError := 'CanAuthenticate: aucune protection utilisable'; FLastError := 'CanAuthenticate: no usable protection';
AThen(boUnavailable); AThen(boUnavailable);
Exit; Exit;
end; end;
@@ -158,9 +158,9 @@ begin
end; end;
LProps := Default(TBiometricPromptProperties); LProps := Default(TBiometricPromptProperties);
LProps.Title := 'Deverrouiller le coffre'; LProps.Title := 'Unlock the vault';
LProps.Description := APrompt; LProps.Description := APrompt;
LProps.CancelButtonText := 'Annuler'; LProps.CancelButtonText := 'Cancel';
LSvc.SetPromptProperties(LProps); LSvc.SetPromptProperties(LProps);
// Meme raison que plus haut : ce qui remonte de la couche Java doit // Meme raison que plus haut : ce qui remonte de la couche Java doit
+1 -1
View File
@@ -68,7 +68,7 @@ begin
// contenu, seulement son origine. // contenu, seulement son origine.
// Les DEUX arguments sont des CharSequence : passer un JString au premier // Les DEUX arguments sont des CharSequence : passer un JString au premier
// compile ailleurs mais pas ici. // compile ailleurs mais pas ici.
LClip := TJClipData.JavaClass.newPlainText(StrToJCharSequence('coffre'), LClip := TJClipData.JavaClass.newPlainText(StrToJCharSequence('vault'),
StrToJCharSequence(AText)); StrToJCharSequence(AText));
LExtras := TJPersistableBundle.JavaClass.init; LExtras := TJPersistableBundle.JavaClass.init;
LExtras.putBoolean(StringToJString(EXTRA_IS_SENSITIVE), True); LExtras.putBoolean(StringToJString(EXTRA_IS_SENSITIVE), True);
+2 -2
View File
@@ -594,7 +594,7 @@ begin
LStore := OpenKeyStore; LStore := OpenKeyStore;
LCert := LStore.getCertificate(StringToJString(KEY_ALIAS_WRAP)); LCert := LStore.getCertificate(StringToJString(KEY_ALIAS_WRAP));
if LCert = nil then 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; LPub := LCert.getPublicKey;
LFactory := TJKeyFactoryX.JavaClass.getInstance(LPub.getAlgorithm); LFactory := TJKeyFactoryX.JavaClass.getInstance(LPub.getAlgorithm);
Result := LFactory.generatePublic( Result := LFactory.generatePublic(
@@ -804,7 +804,7 @@ begin
end; end;
{$ELSE} {$ELSE}
begin begin
raise EPMAStore.Create('Le stockage Keystore est propre a Android'); raise EPMAStore.Create('Keystore storage is Android-only');
end; end;
{$ENDIF} {$ENDIF}
+3 -3
View File
@@ -110,7 +110,7 @@ begin
Result := Title.Trim; Result := Title.Trim;
if Result = '' then Result := Site.Trim; if Result = '' then Result := Site.Trim;
if Result = '' then Result := Username.Trim; if Result = '' then Result := Username.Trim;
if Result = '' then Result := '(sans nom)'; if Result = '' then Result := '(no name)';
end; end;
{ TVault } { TVault }
@@ -206,7 +206,7 @@ begin
Clear; Clear;
LRoot := TJSONObject.ParseJSONValue(APlainJson) as TJSONObject; LRoot := TJSONObject.ParseJSONValue(APlainJson) as TJSONObject;
if LRoot = nil then 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 try
FUsername := LRoot.GetValue<string>('username', ''); FUsername := LRoot.GetValue<string>('username', '');
FExportedAt := LRoot.GetValue<string>('exported_at', FExportedAt := LRoot.GetValue<string>('exported_at',
@@ -228,7 +228,7 @@ begin
end; end;
if not LRoot.TryGetValue<TJSONArray>('entries', LArr) then if not LRoot.TryGetValue<TJSONArray>('entries', LArr) then
raise Exception.Create('Instantane sans tableau "entries"'); raise Exception.Create('Snapshot has no "entries" array');
SetLength(FEntries, LArr.Count); SetLength(FEntries, LArr.Count);
N := 0; N := 0;
+73 -73
View File
@@ -1,4 +1,4 @@
unit mainApk; unit mainApk;
{ {
Client Android en LECTURE SEULE : deverrouiller, chercher, copier. Client Android en LECTURE SEULE : deverrouiller, chercher, copier.
@@ -218,7 +218,7 @@ var
LEvents: IFMXApplicationEventService; LEvents: IFMXApplicationEventService;
begin begin
inherited; inherited;
Caption := 'Coffre'; Caption := 'Vault';
// Le fond de la forme, et non celui du style : tout le reste est peint // 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. // par-dessus, et un gris herite se verrait entre les blocs.
Fill.Kind := TBrushKind.Solid; Fill.Kind := TBrushKind.Solid;
@@ -328,8 +328,8 @@ begin
FLaySettings.Align := TAlignLayout.Top; FLaySettings.Align := TAlignLayout.Top;
FLaySettings.Height := 116; FLaySettings.Height := 116;
FLaySettings.Visible := False; FLaySettings.Visible := False;
FToken := TopEdit(FLaySettings, 'jeton Bearer', FIRSTRUN_TOKEN); FToken := TopEdit(FLaySettings, 'bearer token', FIRSTRUN_TOKEN);
FUrl := TopEdit(FLaySettings, 'URL de l''endpoint', FIRSTRUN_URL); FUrl := TopEdit(FLaySettings, 'endpoint URL', FIRSTRUN_URL);
FToken.Margins.Rect := RectF(16, 8, 16, 0); FToken.Margins.Rect := RectF(16, 8, 16, 0);
FUrl.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. // Ce que l'appareil a retenu l'emporte sur les valeurs de premier lancement.
@@ -347,20 +347,20 @@ begin
LRow.Height := 48; LRow.Height := 48;
LRow.Margins.Rect := RectF(16, 8, 16, 0); LRow.Margins.Rect := RectF(16, 8, 16, 0);
FBtnLog := FlatButton(LRow, 'Journal', OnToggleLog); FBtnLog := FlatButton(LRow, 'Log', OnToggleLog);
FBtnLog.Align := TAlignLayout.Right; FBtnLog.Align := TAlignLayout.Right;
FBtnLog.Width := 120; FBtnLog.Width := 120;
FBtnLog.Margins.Left := 8; FBtnLog.Margins.Left := 8;
FBtnSettings := FlatButton(LRow, 'Reglages du serveur', OnToggleSettings); FBtnSettings := FlatButton(LRow, 'Server settings', OnToggleSettings);
FBtnSettings.Align := TAlignLayout.Client; 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. // 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. // 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); FPwd.Margins.Rect := RectF(16, 16, 16, 0);
FStatus := TLabel.Create(Self); FStatus := TLabel.Create(Self);
@@ -386,7 +386,7 @@ begin
LBar.Align := TAlignLayout.Bottom; LBar.Align := TAlignLayout.Bottom;
LBar.Height := 52; LBar.Height := 52;
LBtnLock := FlatButton(LBar, 'Verrouiller', OnBackTap); LBtnLock := FlatButton(LBar, 'Lock', OnBackTap);
LBtnLock.Align := TAlignLayout.Left; LBtnLock.Align := TAlignLayout.Left;
LBtnLock.Margins.Rect := RectF(16, 4, 0, 8); LBtnLock.Margins.Rect := RectF(16, 4, 0, 8);
LBtnLock.Width := 140; LBtnLock.Width := 140;
@@ -422,7 +422,7 @@ begin
// //
// Bouton peint et non TComboBox : le combo etait l'un des deux derniers // 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. // 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.Align := TAlignLayout.Right;
FBtnFolder.Width := 170; FBtnFolder.Width := 170;
FBtnFolder.Margins.Left := 8; FBtnFolder.Margins.Left := 8;
@@ -445,7 +445,7 @@ begin
FSearchHint.Margins.Left := 10; FSearchHint.Margins.Left := 10;
FSearchHint.HitTest := False; FSearchHint.HitTest := False;
FSearchHint.Opacity := 0.45; FSearchHint.Opacity := 0.45;
FSearchHint.Text := 'Rechercher'; FSearchHint.Text := 'Search';
end; end;
/// Intitule discret au-dessus d'une valeur. Meme forme pour toutes les /// Intitule discret au-dessus d'une valeur. Meme forme pour toutes les
@@ -504,7 +504,7 @@ begin
FLayDetail.Align := TAlignLayout.Client; FLayDetail.Align := TAlignLayout.Client;
FLayDetail.Visible := False; FLayDetail.Visible := False;
LBtnBack := FlatButton(FLayDetail, 'Retour', OnBackTap); LBtnBack := FlatButton(FLayDetail, 'Back', OnBackTap);
LBtnBack.Align := TAlignLayout.Bottom; LBtnBack.Align := TAlignLayout.Bottom;
LBtnBack.Height := 52; LBtnBack.Height := 52;
LBtnBack.Margins.Rect := RectF(16, 8, 16, 8); LBtnBack.Margins.Rect := RectF(16, 8, 16, 8);
@@ -547,15 +547,15 @@ begin
if HasVault then if HasVault then
begin begin
if VaultNeedsAuth then if VaultNeedsAuth then
Status('Coffre present sur cet appareil - protege') Status('Vault on this device - protected')
else else
Status('Coffre present sur cet appareil'); Status('Vault on this device');
SetButtonText(FBtnSync, 'Synchroniser'); SetButtonText(FBtnSync, 'Sync');
end end
else else
begin begin
Status('Aucun coffre local - premier telechargement'); Status('No local vault - first download');
SetButtonText(FBtnSync, 'Telecharger le coffre'); SetButtonText(FBtnSync, 'Download vault');
end; end;
end; end;
@@ -635,10 +635,10 @@ begin
// Le corps d'une note est stocke dans le champ mot de passe : meme donnee, // 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. // 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('Site', FCurrent.Site, False);
Add('Identifiant', FCurrent.Username, False); Add('Username', FCurrent.Username, False);
Add(LPwdCaption, FCurrent.Password, not FCurrent.IsNote); Add(LPwdCaption, FCurrent.Password, not FCurrent.IsNote);
for I := 0 to High(FCurrent.Fields) do for I := 0 to High(FCurrent.Fields) do
Add(FCurrent.Fields[I].Caption, FCurrent.Fields[I].Value, 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 // rendu bidirectionnel de FMX en fait n'importe quoi. Separes, chacun se
// lit dans son sens. // lit dans son sens.
for I := 0 to High(FCurrent.Attachments) do 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); 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...) ; // Le sous-type n'existe que pour les modeles (carte, cle SSH, serveur...) ;
// Add ignore une valeur vide, donc rien a tester ici. // Add ignore une valeur vide, donc rien a tester ici.
Add('Type', FCurrent.Template, False); Add('Type', FCurrent.Template, False);
// Horodatage tel quel : c'est de l'ISO 8601 UTC, lisible, et le reformater // 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. // 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(FRowValues, Length(FRows));
SetLength(FRowShown, Length(FRows)); SetLength(FRowShown, Length(FRows));
@@ -678,12 +678,12 @@ begin
LOrder := 0; LOrder := 0;
if FCurrent.Totp <> '' then if FCurrent.Totp <> '' then
begin begin
LRow := MakeRow(FDetBody, 'Code 2FA', LOrder, FTotpValue); LRow := MakeRow(FDetBody, '2FA code', LOrder, FTotpValue);
Inc(LOrder); Inc(LOrder);
SetLength(FRowCtls, Length(FRowCtls) + 1); SetLength(FRowCtls, Length(FRowCtls) + 1);
FRowCtls[High(FRowCtls)] := LRow; FRowCtls[High(FRowCtls)] := LRow;
FTotpValue.TextSettings.Font.Size := 26; 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 OnTotpTick(nil); // pas d'attente d'une seconde a l'ouverture
FTotpTimer.Enabled := True; FTotpTimer.Enabled := True;
end; end;
@@ -698,11 +698,11 @@ begin
if FRows[I].Secret then if FRows[I].Secret then
begin begin
LValue.Text := MASK; LValue.Text := MASK;
AddRowButton(LRow, 'Afficher', I, OnRevealTap); AddRowButton(LRow, 'Show', I, OnRevealTap);
end end
else else
LValue.Text := FRows[I].Value; LValue.Text := FRows[I].Value;
AddRowButton(LRow, 'Copier', I, OnCopyTap); AddRowButton(LRow, 'Copy', I, OnCopyTap);
end; end;
// La derniere ligne affichee touche sinon le bord du defilement, ce qui la // La derniere ligne affichee touche sinon le bord du defilement, ce qui la
// fait passer pour tronquee alors qu'elle est juste collee. // fait passer pour tronquee alors qu'elle est juste collee.
@@ -728,7 +728,7 @@ begin
if FTotpCode = '' then if FTotpCode = '' then
begin begin
// Un secret illisible ne doit pas passer pour un code : le dire. // Un secret illisible ne doit pas passer pour un code : le dire.
FTotpValue.Text := 'secret illisible'; FTotpValue.Text := 'unreadable secret';
FTotpTimer.Enabled := False; FTotpTimer.Enabled := False;
Exit; Exit;
end; end;
@@ -810,8 +810,8 @@ end;
procedure TForm8.AdoptVault(const APlainJson: string); procedure TForm8.AdoptVault(const APlainJson: string);
begin begin
FVault.LoadFromJson(APlainJson); FVault.LoadFromJson(APlainJson);
Log(' ' + FVault.LiveCount.ToString + ' entrees, ' + Log(' ' + FVault.LiveCount.ToString + ' entries, ' +
FVault.TrashCount.ToString + ' en corbeille'); FVault.TrashCount.ToString + ' in trash');
RebuildIcons; RebuildIcons;
PopulateFolders; PopulateFolders;
Touch; Touch;
@@ -845,7 +845,7 @@ begin
on E: Exception do on E: Exception do
begin begin
// Un coffre local illisible se remplace, il ne se repare pas. // Un coffre local illisible se remplace, il ne se repare pas.
Log('Coffre local corrompu : ' + E.Message); Log('Local vault corrupt: ' + E.Message);
ClearVault; ClearVault;
ShowUnlock; ShowUnlock;
end; end;
@@ -856,8 +856,8 @@ begin
// ShowUnlock AVANT le message : il repositionne le statut selon ce que // ShowUnlock AVANT le message : il repositionne le statut selon ce que
// l'appareil detient, et ecraserait donc l'explication. // l'appareil detient, et ecraserait donc l'explication.
ShowUnlock; ShowUnlock;
Log('Coffre local illisible : ' + LastStoreError); Log('Local vault unreadable: ' + LastStoreError);
Status('Coffre local illisible - voir le journal'); Status('Local vault unreadable - see log');
end; end;
end; end;
@@ -877,13 +877,13 @@ begin
Exit; Exit;
end; end;
if not AAuto then Status('Authentification...'); if not AAuto then Status('Authenticating...');
AuthenticateThen('Acceder au coffre', AuthenticateThen('Unlock the vault',
procedure(AOutcome: TBioOutcome) procedure(AOutcome: TBioOutcome)
begin begin
case AOutcome of case AOutcome of
boOK: OpenLocalNow; boOK: OpenLocalNow;
boCancelled: Status('Annule'); boCancelled: Status('Cancelled');
boUnavailable: boUnavailable:
// Reponse nette du systeme : plus rien pour authentifier sur cet // Reponse nette du systeme : plus rien pour authentifier sur cet
// appareil, donc la cle est perdue et le cache avec elle. // 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. // masquerait la vraie cause. L'utilisateur tranche en reessayant.
if AAuto then if AAuto then
begin begin
Log('Authentification indisponible au lancement - cache conserve'); Log('Authentication unavailable at launch - cache kept');
Log(' ' + LastBiometricError); Log(' ' + LastBiometricError);
Status('Authentification indisponible - reessayer'); Status('Authentication unavailable - try again');
end end
else else
begin begin
Log('Plus aucune protection sur l''appareil - cache efface'); Log('No protection left on this device - cache cleared');
Log(' ' + LastBiometricError); Log(' ' + LastBiometricError);
ClearVault; ClearVault;
ShowUnlock; ShowUnlock;
@@ -909,8 +909,8 @@ begin
else else
// boFailed : la couche Java a leve quelque chose. Cela ne dit rien de // boFailed : la couche Java a leve quelque chose. Cela ne dit rien de
// l'appareil, donc on ne touche a rien - on rapporte. // l'appareil, donc on ne touche a rien - on rapporte.
Log('Echec de l''authentification : ' + LastBiometricError); Log('Authentication failed: ' + LastBiometricError);
Status('Echec de l''authentification'); Status('Authentication failed');
end; end;
end); end);
end; end;
@@ -930,7 +930,7 @@ begin
if LPwd = '' then LPwd := FSyncPwd; if LPwd = '' then LPwd := FSyncPwd;
if LPwd = '' then if LPwd = '' then
begin begin
Status('Mot de passe de sync requis'); Status('Sync password required');
Exit; Exit;
end; end;
FLog.Lines.Clear; FLog.Lines.Clear;
@@ -942,16 +942,16 @@ begin
// cet appareil - c'est lui qui exprime le choix de l'utilisateur. // cet appareil - c'est lui qui exprime le choix de l'utilisateur.
if VaultNeedsAuth and (FVault.Count = 0) then if VaultNeedsAuth and (FVault.Count = 0) then
begin begin
Status('Authentification...'); Status('Authenticating...');
AuthenticateThen('Acceder au coffre', AuthenticateThen('Unlock the vault',
procedure(AOutcome: TBioOutcome) procedure(AOutcome: TBioOutcome)
begin begin
if AOutcome = boOK then if AOutcome = boOK then
FetchAndDecrypt(LUrl, LTok, LPwd) FetchAndDecrypt(LUrl, LTok, LPwd)
else else
begin begin
Log('Synchronisation refusee : ' + LastBiometricError); Log('Sync refused: ' + LastBiometricError);
Status('Authentification requise'); Status('Authentication required');
end; end;
end); end);
Exit; Exit;
@@ -966,7 +966,7 @@ var
LCanAuth: Boolean; LCanAuth: Boolean;
begin begin
SetBusy(True); 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. // 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 // Demander une cle protegee par authentification alors que l'invite ne peut
// pas s'afficher (permission absente, aucune protection configuree) produit // pas s'afficher (permission absente, aucune protection configuree) produit
@@ -997,7 +997,7 @@ begin
LSw := TStopwatch.StartNew; LSw := TStopwatch.StartNew;
LRes := LClient.Fetch(FETag); LRes := LClient.Fetch(FETag);
LSw.Stop; LSw.Stop;
Log(' HTTP ' + LRes.HttpCode.ToString + ' en ' + Log(' HTTP ' + LRes.HttpCode.ToString + ' in ' +
LSw.ElapsedMilliseconds.ToString + ' ms'); LSw.ElapsedMilliseconds.ToString + ' ms');
case LRes.Status of case LRes.Status of
@@ -1018,14 +1018,14 @@ begin
// rafraichissement de fond c'est le cas courant, et il // rafraichissement de fond c'est le cas courant, et il
// doit passer inapercu - surtout pas de retour a la liste // doit passer inapercu - surtout pas de retour a la liste
// alors que l'utilisateur consulte une entree. // alors que l'utilisateur consulte une entree.
Log(' 304 : inchange, coffre deja ouvert') Log(' 304: unchanged, vault already open')
else if LCached <> '' then else if LCached <> '' then
begin begin
Log(' 304 : inchange, coffre servi depuis le cache'); Log(' 304: unchanged, vault served from cache');
try try
AdoptVault(LCached); AdoptVault(LCached);
except except
on E: Exception do Status('Cache illisible : ' + E.Message); on E: Exception do Status('Cache unreadable: ' + E.Message);
end; end;
end end
else else
@@ -1034,7 +1034,7 @@ begin
// On le jette pour que l'appel suivant retelecharge. // On le jette pour que l'appel suivant retelecharge.
FETag := ''; FETag := '';
if not ASilent then if not ASilent then
Status('Cache absent - appuyer a nouveau pour telecharger'); Status('No cache - tap again to download');
end; end;
end); end);
Exit; Exit;
@@ -1042,10 +1042,10 @@ begin
ssNoSnapshot: ssNoSnapshot:
begin begin
// Le serveur repond pareil dans les deux cas, par conception. // 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 if not ASilent then
TThread.Queue(nil, procedure TThread.Queue(nil, procedure
begin Status('Rien a telecharger (404)'); end); begin Status('Nothing to download (404)'); end);
Exit; Exit;
end; end;
ssRefused, ssHttpError, ssNetworkError: ssRefused, ssHttpError, ssNetworkError:
@@ -1053,17 +1053,17 @@ begin
// En fond, un echec reseau ne se signale pas : le coffre local // En fond, un echec reseau ne se signale pas : le coffre local
// est affiche, hors ligne est un etat normal, et une alerte ici // est affiche, hors ligne est un etat normal, et une alerte ici
// apprendrait a l'utilisateur a ignorer les alertes. // apprendrait a l'utilisateur a ignorer les alertes.
Log(' echec : ' + LRes.Error); Log(' failed: ' + LRes.Error);
if not ASilent then if not ASilent then
TThread.Queue(nil, procedure TThread.Queue(nil, procedure
begin Status('Echec reseau'); end); begin Status('Network failure'); end);
Exit; Exit;
end; end;
end; end;
Log(' recu ' + Length(LRes.Container).ToString + ' octets'); Log(' received ' + Length(LRes.Container).ToString + ' bytes');
if not ASilent then if not ASilent then
TThread.Queue(nil, procedure begin Status('Dechiffrement...'); end); TThread.Queue(nil, procedure begin Status('Decrypting...'); end);
LSw := TStopwatch.StartNew; LSw := TStopwatch.StartNew;
// Volontairement AVANT de retenir l'ETag : un echec ici doit laisser // Volontairement AVANT de retenir l'ETag : un echec ici doit laisser
@@ -1071,7 +1071,7 @@ begin
LPlain := DecryptContainer(LRes.Container, APwd); LPlain := DecryptContainer(LRes.Container, APwd);
LSw.Stop; LSw.Stop;
FETag := LRes.ETag; 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 : // Ecrit APRES un dechiffrement reussi seulement, et avec son ETag :
// les deux doivent rester coherents. // les deux doivent rester coherents.
@@ -1083,16 +1083,16 @@ begin
// pas des reglages saisis, on enregistre des reglages EPROUVES. // pas des reglages saisis, on enregistre des reglages EPROUVES.
SaveDeviceSettings(AUrl, ATok); SaveDeviceSettings(AUrl, ATok);
if VaultNeedsAuth then if VaultNeedsAuth then
Log(' cache ecrit - protege par biometrie/code') Log(' cache written - protected by biometrics/PIN')
else if LCanAuth then else if LCanAuth then
Log(' cache ecrit - cle simple (Keystore a refuse la contrainte)') Log(' cache written - plain key (Keystore refused the constraint)')
else else
Log(' cache ecrit - AUCUNE protection : pas d''authentification ici'); Log(' cache written - NO protection: no authentication here');
except except
// La classe compte autant que le message : "methode introuvable" // La classe compte autant que le message : "methode introuvable"
// designe une signature JNI fausse, pas un probleme de stockage. // designe une signature JNI fausse, pas un probleme de stockage.
on E: Exception do on E: Exception do
Log(' cache non ecrit : ' + E.ClassName + ' - ' + E.Message); Log(' cache not written: ' + E.ClassName + ' - ' + E.Message);
end; end;
TThread.Queue(nil, TThread.Queue(nil,
@@ -1104,13 +1104,13 @@ begin
except except
on E: Exception do on E: Exception do
if not ASilent then if not ASilent then
Status('Instantane illisible : ' + E.Message); Status('Snapshot unreadable: ' + E.Message);
end; end;
end); end);
except except
on E: Exception do on E: Exception do
begin 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 // 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. // 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. // le champ prerempli, et le coffre local reste consultable.
if not ASilent then if not ASilent then
TThread.Queue(nil, procedure TThread.Queue(nil, procedure
begin Status('Echec - mot de passe de sync incorrect ?'); end); begin Status('Failed - wrong sync password?'); end);
end; end;
end; end;
finally finally
@@ -1188,7 +1188,7 @@ begin
LPrev := FFolderNames[FFolderSel]; LPrev := FFolderNames[FFolderSel];
SetLength(FFolderNames, 1); SetLength(FFolderNames, 1);
FFolderNames[0] := 'Tous les dossiers'; FFolderNames[0] := 'All folders';
for LF in FVault.Folders do for LF in FVault.Folders do
begin begin
SetLength(FFolderNames, Length(FFolderNames) + 1); SetLength(FFolderNames, Length(FFolderNames) + 1);
@@ -1341,7 +1341,7 @@ begin
end; end;
if (FSearch.Text.Trim = '') and (LFolder = '') then if (FSearch.Text.Trim = '') and (LFolder = '') then
FCount.Text := FVault.LiveCount.ToString + ' entrees' FCount.Text := FVault.LiveCount.ToString + ' entries'
else else
FCount.Text := Length(FFiltered).ToString + ' / ' + FCount.Text := Length(FFiltered).ToString + ' / ' +
FVault.LiveCount.ToString; FVault.LiveCount.ToString;
@@ -1369,12 +1369,12 @@ begin
if FRowShown[I] then if FRowShown[I] then
begin begin
FRowValues[I].Text := FRows[I].Value; FRowValues[I].Text := FRows[I].Value;
SetButtonText(TRectangle(Sender), 'Masquer'); SetButtonText(TRectangle(Sender), 'Hide');
end end
else else
begin begin
FRowValues[I].Text := MASK; FRowValues[I].Text := MASK;
SetButtonText(TRectangle(Sender), 'Afficher'); SetButtonText(TRectangle(Sender), 'Show');
end; end;
end; end;
@@ -1388,7 +1388,7 @@ begin
// -1 : la ligne du TOTP, qui n'est pas dans FRows - sa valeur change chaque // -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. // periode et ne vit que dans FTotpCode. Copie sans l'espace d'affichage.
if I = -1 then if I = -1 then
CopyToClipboard(FTotpCode, 'Code 2FA') CopyToClipboard(FTotpCode, '2FA code')
else if (I >= 0) and (I <= High(FRows)) then else if (I >= 0) and (I <= High(FRows)) then
CopyToClipboard(FRows[I].Value, FRows[I].Caption); CopyToClipboard(FRows[I].Value, FRows[I].Caption);
end; end;
@@ -1408,7 +1408,7 @@ begin
// ponytail: le message reste jusqu'a la prochaine navigation, pas de minuterie // ponytail: le message reste jusqu'a la prochaine navigation, pas de minuterie
// pour l'effacer. Un deuxieme TTimer pour deux secondes d'affichage serait // pour l'effacer. Un deuxieme TTimer pour deux secondes d'affichage serait
// plus de code que ce que ca apporte. // 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'; CLIPBOARD_CLEAR_SECONDS.ToString + ' s';
end; end;
@@ -1477,7 +1477,7 @@ begin
end; end;
if MinutesBetween(Now, FLastActivity) >= AUTOLOCK_MINUTES then if MinutesBetween(Now, FLastActivity) >= AUTOLOCK_MINUTES then
begin begin
Log('Verrouille apres ' + AUTOLOCK_MINUTES.ToString + ' min d''inactivite'); Log('Locked after ' + AUTOLOCK_MINUTES.ToString + ' min idle');
LockVault; LockVault;
end; end;
end; end;