selfstore 1.6.8 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/widgets.d.ts CHANGED
@@ -18,13 +18,31 @@ declare abstract class FlowWidget extends HTMLElement {
18
18
  protected unsub: (() => void) | null;
19
19
  /** The widget's EN default strings; subclasses provide theirs. */
20
20
  protected abstract defaults(): WidgetLabels;
21
+ /** Translations shipped with the widget, by language subtag ('fr'). A widget
22
+ * without any stays English until the host passes `labels`. */
23
+ protected packs(): Record<string, WidgetLabels>;
21
24
  /** Build the current view into the given container. */
22
25
  protected abstract view(into: HTMLElement): void;
23
26
  constructor();
24
27
  /** Partial override map, merged over the EN defaults (also the i18n hook). */
25
28
  get labels(): WidgetLabels;
26
29
  set labels(map: WidgetLabels);
27
- protected t(key: string): string;
30
+ /** The shipped translation for the page's language, if the widget has one.
31
+ * An explicit `lang` on the element (or on any ancestor) wins over the
32
+ * document's, which wins over the browser's, so a host that already sets
33
+ * `<html lang>` gets translated copy without writing a single label. */
34
+ private localized;
35
+ /**
36
+ * The copy for a key, with `{placeholders}` filled in.
37
+ *
38
+ * A status that cannot name WHERE it saved has to be read twice: once for the
39
+ * state, once for the destination on another line. Interpolation lets a pack
40
+ * write one sentence instead - and lets each language put the place where its
41
+ * own grammar wants it, which a fixed "state, then label" layout cannot.
42
+ * A placeholder with no value is left as written rather than blanked, so a
43
+ * missing variable shows up instead of producing a sentence with a hole.
44
+ */
45
+ protected t(key: string, vars?: Record<string, string | null | undefined>): string;
28
46
  /** A heading an empty-string label removes: labels = { 'share.title': '' }
29
47
  * hides that heading (the host page already says it) without a fork.
30
48
  * Append through put(), which skips the null. */
@@ -83,6 +101,7 @@ declare class SelfstoreConnectElement extends FlowWidget {
83
101
  #private;
84
102
  static get observedAttributes(): string[];
85
103
  protected defaults(): WidgetLabels;
104
+ protected packs(): Record<string, WidgetLabels>;
86
105
  /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
87
106
  get store(): StoreLike | null;
88
107
  set store(v: StoreLike | null);
@@ -169,6 +188,7 @@ declare class SelfstoreShareElement extends FlowWidget {
169
188
  #private;
170
189
  static get observedAttributes(): string[];
171
190
  protected defaults(): WidgetLabels;
191
+ protected packs(): Record<string, WidgetLabels>;
172
192
  /** The app-side transport of links and memberships (see ShareEngine). */
173
193
  get engine(): ShareEngine | null;
174
194
  set engine(v: ShareEngine | null);
@@ -238,6 +258,7 @@ declare class SelfstoreJoinElement extends FlowWidget {
238
258
  #private;
239
259
  static get observedAttributes(): string[];
240
260
  protected defaults(): WidgetLabels;
261
+ protected packs(): Record<string, WidgetLabels>;
241
262
  get engine(): JoinEngine | null;
242
263
  set engine(v: JoinEngine | null);
243
264
  /** The invitation. Also settable as the `link` attribute. */
@@ -268,6 +289,7 @@ declare class SelfstoreStatusElement extends FlowWidget {
268
289
  #private;
269
290
  static get observedAttributes(): string[];
270
291
  protected defaults(): WidgetLabels;
292
+ protected packs(): Record<string, WidgetLabels>;
271
293
  /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
272
294
  get store(): StoreLike | null;
273
295
  set store(v: StoreLike | null);
@@ -310,6 +332,7 @@ declare class SelfstoreBackupsElement extends FlowWidget {
310
332
  #private;
311
333
  static get observedAttributes(): string[];
312
334
  protected defaults(): WidgetLabels;
335
+ protected packs(): Record<string, WidgetLabels>;
313
336
  /** The headless manager this panel drives (see 'selfstore/backups'). */
314
337
  get manager(): BackupsManager | null;
315
338
  set manager(v: BackupsManager | null);
@@ -447,6 +470,7 @@ declare class SelfstoreGateElement extends FlowWidget {
447
470
  #private;
448
471
  static get observedAttributes(): string[];
449
472
  protected defaults(): WidgetLabels;
473
+ protected packs(): Record<string, WidgetLabels>;
450
474
  /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
451
475
  get store(): StoreLike | null;
452
476
  set store(v: StoreLike | null);
package/dist/widgets.js CHANGED
@@ -343,6 +343,11 @@ var FlowWidget = class extends HTMLElement {
343
343
  body;
344
344
  overrides = {};
345
345
  unsub = null;
346
+ /** Translations shipped with the widget, by language subtag ('fr'). A widget
347
+ * without any stays English until the host passes `labels`. */
348
+ packs() {
349
+ return {};
350
+ }
346
351
  constructor() {
347
352
  super();
348
353
  this.root = this.attachShadow({ mode: "open" });
@@ -360,8 +365,28 @@ var FlowWidget = class extends HTMLElement {
360
365
  this.overrides = map ?? {};
361
366
  this.rerender();
362
367
  }
363
- t(key) {
364
- return this.overrides[key] ?? this.defaults()[key] ?? key;
368
+ /** The shipped translation for the page's language, if the widget has one.
369
+ * An explicit `lang` on the element (or on any ancestor) wins over the
370
+ * document's, which wins over the browser's, so a host that already sets
371
+ * `<html lang>` gets translated copy without writing a single label. */
372
+ localized() {
373
+ const tag = this.closest("[lang]")?.getAttribute("lang") || document.documentElement.lang || navigator.language || "";
374
+ return this.packs()[tag.slice(0, 2).toLowerCase()] ?? {};
375
+ }
376
+ /**
377
+ * The copy for a key, with `{placeholders}` filled in.
378
+ *
379
+ * A status that cannot name WHERE it saved has to be read twice: once for the
380
+ * state, once for the destination on another line. Interpolation lets a pack
381
+ * write one sentence instead - and lets each language put the place where its
382
+ * own grammar wants it, which a fixed "state, then label" layout cannot.
383
+ * A placeholder with no value is left as written rather than blanked, so a
384
+ * missing variable shows up instead of producing a sentence with a hole.
385
+ */
386
+ t(key, vars) {
387
+ const copy = this.overrides[key] ?? this.localized()[key] ?? this.defaults()[key] ?? key;
388
+ if (!vars) return copy;
389
+ return copy.replace(/\{(\w+)\}/g, (whole, name) => vars[name] ?? whole);
365
390
  }
366
391
  /** A heading an empty-string label removes: labels = { 'share.title': '' }
367
392
  * hides that heading (the host page already says it) without a fork.
@@ -374,7 +399,7 @@ var FlowWidget = class extends HTMLElement {
374
399
  * ('error.targetUnavailable', ...), with a generic fallback. */
375
400
  errorText(labelKey) {
376
401
  if (labelKey) {
377
- const specific = this.overrides[labelKey] ?? this.defaults()[labelKey];
402
+ const specific = this.overrides[labelKey] ?? this.localized()[labelKey] ?? this.defaults()[labelKey];
378
403
  if (specific) return specific;
379
404
  }
380
405
  return this.t("error.generic");
@@ -470,6 +495,66 @@ var EN = {
470
495
  "error.decryptFailed": "This backup could not be opened with that password.",
471
496
  "error.badFormat": "This file does not look like a readable backup."
472
497
  };
498
+ var FR = {
499
+ "connect.title": "O\xF9 enregistrer vos donn\xE9es ?",
500
+ "connect.recommended": "Recommand\xE9",
501
+ "connect.drive": "Google Drive",
502
+ "connect.drive.sub": "Disponible sur tous vos appareils",
503
+ "connect.file": "Un fichier sur cet appareil",
504
+ "connect.file.sub": "Hors ligne, le fichier reste chez vous",
505
+ "connect.file.new": "Nouvelle sauvegarde",
506
+ "connect.file.open": "Ouvrir un fichier existant",
507
+ "connect.webdav": "Mon serveur (WebDAV)",
508
+ "connect.webdav.sub": "Nextcloud, ownCloud, le v\xF4tre",
509
+ "connect.s3": "Mon bucket (S3)",
510
+ "connect.s3.sub": "Amazon S3, R2, B2, MinIO",
511
+ "connect.server": "Mon serveur",
512
+ "connect.server.sub": "WebDAV ou un bucket S3 que vous contr\xF4lez",
513
+ "connect.tab.webdav": "WebDAV",
514
+ "connect.tab.s3": "S3",
515
+ "connect.s3.endpoint": "URL du point d'acc\xE8s",
516
+ "connect.s3.region": "R\xE9gion",
517
+ "connect.s3.bucket": "Bucket",
518
+ "connect.s3.key": "Chemin du fichier",
519
+ "connect.s3.accessKeyId": "Identifiant de la cl\xE9 d'acc\xE8s",
520
+ "connect.s3.secret": "Cl\xE9 secr\xE8te",
521
+ "connect.s3.submit": "Connecter",
522
+ "connect.connecting": "Connexion en cours...",
523
+ "connect.connecting.drive": "Autorisez l'acc\xE8s dans la fen\xEAtre Google.",
524
+ "connect.cancel": "Annuler",
525
+ "connect.retry": "R\xE9essayer",
526
+ "connect.password.title": "Cette sauvegarde est prot\xE9g\xE9e",
527
+ "connect.password.hint": "Entrez son mot de passe pour l\u2019ouvrir.",
528
+ "connect.password.placeholder": "Mot de passe",
529
+ "connect.password.wrong": "Mot de passe incorrect. R\xE9essayez.",
530
+ "connect.password.submit": "Ouvrir",
531
+ "connect.password.show": "Afficher le mot de passe",
532
+ "connect.password.hide": "Masquer le mot de passe",
533
+ "connect.password.forgot": "Mot de passe oubli\xE9 ?",
534
+ "connect.password.forgot.warn": "Sans le mot de passe, cette sauvegarde ne peut pas \xEAtre ouverte. Vous pouvez repartir d\u2019une sauvegarde vide : l\u2019ancienne sera perdue d\xE9finitivement.",
535
+ "connect.password.forgot.confirm": "Remplacer la sauvegarde",
536
+ "connect.password.forgot.back": "Retour",
537
+ "connect.conflict.title": "Cette destination contient d\xE9j\xE0 une sauvegarde",
538
+ "connect.conflict.merge": "Tout garder",
539
+ "connect.conflict.merge.sub": "R\xE9unir la sauvegarde et les donn\xE9es de cet appareil",
540
+ "connect.conflict.resume": "Utiliser la sauvegarde",
541
+ "connect.conflict.resume.sub": "Cet appareil adopte la sauvegarde existante",
542
+ "connect.conflict.replace": "Remplacer la sauvegarde",
543
+ "connect.conflict.replace.sub": "Les donn\xE9es de cet appareil \xE9crasent la sauvegarde",
544
+ "connect.webdav.url": "URL du serveur",
545
+ "connect.webdav.user": "Identifiant",
546
+ "connect.webdav.password": "Mot de passe",
547
+ "connect.webdav.submit": "Connecter",
548
+ "connect.webdav.signup": "Cr\xE9er un compte",
549
+ "connect.webdav.help.more": "Comment faire ?",
550
+ "connect.done": "Connect\xE9. Vos donn\xE9es sont enregistr\xE9es.",
551
+ "connect.done.manual": "Mode fichier : exportez apr\xE8s chaque changement.",
552
+ "error.generic": "Cela n'a pas fonctionn\xE9. V\xE9rifiez la connexion et r\xE9essayez.",
553
+ "error.targetUnavailable": "La destination ne r\xE9pond pas. R\xE9essayez dans un instant.",
554
+ "error.authExpired": "Acc\xE8s expir\xE9 : reconnectez-vous pour continuer.",
555
+ "error.decryptFailed": "Cette sauvegarde n'a pas pu \xEAtre ouverte avec ce mot de passe.",
556
+ "error.badFormat": "Ce fichier ne ressemble pas \xE0 une sauvegarde lisible."
557
+ };
473
558
  var EYE_SHOW = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3-8 10-8 10 8 10 8-3 8-10 8-10-8-10-8Z"/><circle cx="12" cy="12" r="3"/></svg>';
474
559
  var EYE_HIDE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 10 8 10 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><path d="M6.61 6.61A18.45 18.45 0 0 0 2 12s3 8 10 8a9.12 9.12 0 0 0 5.39-1.61"/><line x1="2" y1="2" x2="22" y2="22"/></svg>';
475
560
  function eyeIcon(visible) {
@@ -508,6 +593,9 @@ var SelfstoreConnectElement = class extends FlowWidget {
508
593
  defaults() {
509
594
  return EN;
510
595
  }
596
+ packs() {
597
+ return { fr: FR };
598
+ }
511
599
  /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
512
600
  get store() {
513
601
  return this.#store;
@@ -1181,6 +1269,26 @@ var EN2 = {
1181
1269
  "error.generic": "That did not work. Check the connection and try again.",
1182
1270
  "error.targetUnavailable": "The share service did not answer. Try again in a moment."
1183
1271
  };
1272
+ var FR2 = {
1273
+ "share.title": "Partage",
1274
+ "share.create.read": "Cr\xE9er un lien de lecture",
1275
+ "share.create.write": "Cr\xE9er un lien de modification",
1276
+ "share.creating": "Cr\xE9ation...",
1277
+ "share.level.read": "Peut consulter",
1278
+ "share.level.write": "Peut modifier",
1279
+ "share.copy": "Copier",
1280
+ "share.copied": "Copi\xE9",
1281
+ "share.revoke": "R\xE9voquer",
1282
+ "share.members": "Personnes ayant acc\xE8s",
1283
+ "share.member.you": "vous",
1284
+ "share.member.owner": "propri\xE9taire",
1285
+ "share.remove": "Retirer",
1286
+ "share.stop": "Arr\xEAter le partage",
1287
+ "share.empty": "Personne d'autre n'a acc\xE8s pour le moment.",
1288
+ "share.stale": "Connexion instable : cette vue est peut-\xEAtre en retard.",
1289
+ "error.generic": "Cela n'a pas fonctionn\xE9. V\xE9rifiez la connexion et r\xE9essayez.",
1290
+ "error.targetUnavailable": "Le service de partage ne r\xE9pond pas. R\xE9essayez dans un instant."
1291
+ };
1184
1292
  var isOff = (v) => v != null && ["off", "false", "no", "0"].includes(v.trim().toLowerCase());
1185
1293
  var SelfstoreShareElement = class extends FlowWidget {
1186
1294
  static get observedAttributes() {
@@ -1201,6 +1309,9 @@ var SelfstoreShareElement = class extends FlowWidget {
1201
1309
  defaults() {
1202
1310
  return EN2;
1203
1311
  }
1312
+ packs() {
1313
+ return { fr: FR2 };
1314
+ }
1204
1315
  /** The app-side transport of links and memberships (see ShareEngine). */
1205
1316
  get engine() {
1206
1317
  return this.#engine;
@@ -1494,6 +1605,22 @@ var EN3 = {
1494
1605
  "error.generic": "That did not work. Check the connection and try again.",
1495
1606
  "error.targetUnavailable": "The invitation could not be read. Try again in a moment."
1496
1607
  };
1608
+ var FR3 = {
1609
+ "join.previewing": "Lecture de l'invitation...",
1610
+ "join.title": "Vous \xEAtes invit\xE9",
1611
+ "join.from": "Partag\xE9 par {from}",
1612
+ "join.level.read": "Vous pourrez consulter.",
1613
+ "join.level.write": "Vous pourrez consulter et modifier.",
1614
+ "join.accept": "Rejoindre",
1615
+ "join.joining": "Connexion en cours...",
1616
+ "join.joined": "C'est fait. Les donn\xE9es partag\xE9es se synchronisent sur cet appareil.",
1617
+ "join.mismatch": "Cet appareil suit d\xE9j\xE0 un autre partage. Quittez-le d'abord, ou utilisez un autre profil.",
1618
+ "join.noInvite": "Cette invitation a d\xE9j\xE0 servi, ou elle vise un autre compte.",
1619
+ "join.switchAccount": "Utiliser un autre compte",
1620
+ "join.retry": "R\xE9essayer",
1621
+ "error.generic": "Cela n'a pas fonctionn\xE9. V\xE9rifiez la connexion et r\xE9essayez.",
1622
+ "error.targetUnavailable": "L'invitation n'a pas pu \xEAtre lue. R\xE9essayez dans un instant."
1623
+ };
1497
1624
  var SelfstoreJoinElement = class extends FlowWidget {
1498
1625
  static get observedAttributes() {
1499
1626
  return ["link", "variant"];
@@ -1506,6 +1633,9 @@ var SelfstoreJoinElement = class extends FlowWidget {
1506
1633
  defaults() {
1507
1634
  return EN3;
1508
1635
  }
1636
+ packs() {
1637
+ return { fr: FR3 };
1638
+ }
1509
1639
  get engine() {
1510
1640
  return this.#engine;
1511
1641
  }
@@ -1706,17 +1836,32 @@ var SelfstoreJoinElement = class extends FlowWidget {
1706
1836
  // src/widgets/status.ts
1707
1837
  var EN4 = {
1708
1838
  "status.ephemeral": "Nothing is saved",
1709
- "status.cacheOnly": "Only on this device",
1839
+ "status.cacheOnly": "Never saved anywhere yet",
1710
1840
  "status.saving": "Saving...",
1711
- "status.saved": "Saved",
1841
+ "status.saved": "Saved to {label}, at every change",
1712
1842
  "status.needsAttention": "Reconnect to continue",
1713
1843
  "status.locked": "Locked",
1714
1844
  "status.pendingDownload": "Changes to download",
1845
+ "status.saved.placeless": "Saved",
1715
1846
  "status.action.choose-destination": "Choose a destination",
1716
1847
  "status.action.download": "Download",
1717
1848
  "status.action.reconnect": "Reconnect",
1718
1849
  "status.action.unlock": "Unlock"
1719
1850
  };
1851
+ var FR4 = {
1852
+ "status.ephemeral": "Rien n'est enregistr\xE9",
1853
+ "status.cacheOnly": "Vos donn\xE9es n'ont jamais \xE9t\xE9 enregistr\xE9es ailleurs",
1854
+ "status.saving": "Enregistrement...",
1855
+ "status.saved": "Enregistr\xE9 dans {label}, \xE0 chaque changement",
1856
+ "status.needsAttention": "Acc\xE8s \xE0 retrouver",
1857
+ "status.locked": "Sauvegarde verrouill\xE9e",
1858
+ "status.pendingDownload": "Modifications \xE0 exporter",
1859
+ "status.saved.placeless": "Sauvegarde \xE0 jour",
1860
+ "status.action.choose-destination": "Choisir une sauvegarde",
1861
+ "status.action.download": "Exporter",
1862
+ "status.action.reconnect": "Retrouver l'acc\xE8s",
1863
+ "status.action.unlock": "D\xE9verrouiller"
1864
+ };
1720
1865
  var SelfstoreStatusElement = class extends FlowWidget {
1721
1866
  static get observedAttributes() {
1722
1867
  return ["variant"];
@@ -1727,6 +1872,9 @@ var SelfstoreStatusElement = class extends FlowWidget {
1727
1872
  defaults() {
1728
1873
  return EN4;
1729
1874
  }
1875
+ packs() {
1876
+ return { fr: FR4 };
1877
+ }
1730
1878
  /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
1731
1879
  get store() {
1732
1880
  return this.#store;
@@ -1778,7 +1926,12 @@ var SelfstoreStatusElement = class extends FlowWidget {
1778
1926
  const host = this.host();
1779
1927
  if (!host) return;
1780
1928
  const { status, targetKind, label } = host.engine.state;
1781
- const text = this.t(status.labelKey);
1929
+ const sansLieu = (cle) => {
1930
+ const jumelle = `${cle}.placeless`;
1931
+ const copie = this.t(jumelle);
1932
+ return copie === jumelle ? this.t(cle) : copie;
1933
+ };
1934
+ const text = label ? this.t(status.labelKey, { label }) : sansLieu(status.labelKey);
1782
1935
  const dot = h("span", {
1783
1936
  part: `status-dot sev-${status.severity}`,
1784
1937
  "aria-hidden": "true"
@@ -1885,6 +2038,63 @@ var EN5 = {
1885
2038
  "backups.replica.s3.secret": "Secret access key",
1886
2039
  "error.generic": "That did not work. Check the connection and try again."
1887
2040
  };
2041
+ var FR5 = {
2042
+ "backups.mine.heading": "Sauvegardes",
2043
+ "backups.mine.personal": "Sauvegarde principale",
2044
+ "backups.active": "Active",
2045
+ "backups.pill.encrypted": "Chiffr\xE9e",
2046
+ "backups.pill.plain": "Non chiffr\xE9e",
2047
+ "backups.pill.shared": "Partag\xE9e",
2048
+ "backups.pill.private": "Non partag\xE9e",
2049
+ "backups.modified": "modifi\xE9e {when}",
2050
+ "backups.replaceHint": "L'ouvrir remplace les donn\xE9es de cet appareil.",
2051
+ "backups.loading": "Ouverture...",
2052
+ "backups.menu": "Actions",
2053
+ "backups.open": "Ouvrir",
2054
+ "backups.encrypt": "Chiffrer",
2055
+ "backups.share": "Partager",
2056
+ "backups.rename": "Renommer",
2057
+ "backups.delete": "Supprimer",
2058
+ "backups.new": "+ Nouvelle sauvegarde",
2059
+ "backups.newTitle": "Nouvelle sauvegarde",
2060
+ "backups.startsBlank": "Elle d\xE9marre vide.",
2061
+ "backups.namePh": "Son nom",
2062
+ "backups.willBe": "Fichier : {file}",
2063
+ "backups.taken": "Ce nom est d\xE9j\xE0 pris.",
2064
+ "backups.create": "Cr\xE9er",
2065
+ "backups.renameTitle": "Renommer cette sauvegarde",
2066
+ "backups.pwHint": "Cette sauvegarde est prot\xE9g\xE9e : entrez son mot de passe.",
2067
+ "backups.pwShow": "Afficher le mot de passe",
2068
+ "backups.pwHide": "Masquer le mot de passe",
2069
+ "backups.cancel": "Annuler",
2070
+ "backups.shared.heading": "Partag\xE9es avec moi",
2071
+ "backups.shared.by": "Partag\xE9e par {who}",
2072
+ "backups.shared.leave": "Quitter",
2073
+ "backups.replica.add": "Seconde sauvegarde",
2074
+ "backups.replica.title": "Seconde sauvegarde",
2075
+ "backups.replica.intro": "Le m\xEAme fichier chiffr\xE9, \xE9crit aussi \xE0 un second endroit. Si l\u2019un tombe, l\u2019autre reste.",
2076
+ "backups.replica.dest.drive": "Google Drive",
2077
+ "backups.replica.dest.file": "Un fichier sur cet appareil",
2078
+ "backups.replica.dest.webdav": "Un serveur WebDAV",
2079
+ "backups.replica.dest.s3": "Un bucket S3",
2080
+ "backups.replica.card": "Seconde sauvegarde - {label}",
2081
+ "backups.replica.uptodate": "\xC0 jour, {when}",
2082
+ "backups.replica.pending": "Premi\xE8re copie au prochain enregistrement",
2083
+ "backups.replica.error": "Injoignable - nouvel essai au prochain enregistrement",
2084
+ "backups.replica.remove": "Retirer",
2085
+ "backups.replica.save": "Ajouter la copie",
2086
+ "backups.replica.webdav.url": "URL du fichier sur le serveur (https://hote:8443/dav/fichier.zip)",
2087
+ "backups.replica.webdav.help": "L'URL compl\xE8te du fichier de sauvegarde, port personnalis\xE9 compris. Le serveur doit autoriser cette application \xE0 le joindre (CORS) : certains h\xE9bergeurs ne le font pas et ne se connecteront pas depuis un navigateur.",
2088
+ "backups.replica.webdav.user": "Identifiant",
2089
+ "backups.replica.webdav.password": "Mot de passe",
2090
+ "backups.replica.s3.endpoint": "URL du point d'acc\xE8s",
2091
+ "backups.replica.s3.region": "R\xE9gion",
2092
+ "backups.replica.s3.bucket": "Bucket",
2093
+ "backups.replica.s3.key": "Chemin du fichier",
2094
+ "backups.replica.s3.accessKeyId": "Identifiant de la cl\xE9 d'acc\xE8s",
2095
+ "backups.replica.s3.secret": "Cl\xE9 secr\xE8te",
2096
+ "error.generic": "Cela n'a pas fonctionn\xE9. V\xE9rifiez la connexion et r\xE9essayez."
2097
+ };
1888
2098
  var isOff2 = (v) => v != null && ["off", "false", "no", "0"].includes(v.trim().toLowerCase());
1889
2099
  var EYE_OPEN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>';
1890
2100
  var EYE_SHUT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 3 18 18"/><path d="M10.6 5.1A10.9 10.9 0 0 1 12 5c6.5 0 10 7 10 7a17.4 17.4 0 0 1-3.4 4.4M6.6 6.6A17 17 0 0 0 2 12s3.5 7 10 7a10.7 10.7 0 0 0 5.4-1.4"/></svg>';
@@ -1939,6 +2149,9 @@ var SelfstoreBackupsElement = class extends FlowWidget {
1939
2149
  defaults() {
1940
2150
  return EN5;
1941
2151
  }
2152
+ packs() {
2153
+ return { fr: FR5 };
2154
+ }
1942
2155
  /** The headless manager this panel drives (see 'selfstore/backups'). */
1943
2156
  get manager() {
1944
2157
  return this.#manager;
@@ -2958,6 +3171,13 @@ var EN6 = {
2958
3171
  "gate.defer": "Later - keep it in this browser only",
2959
3172
  "gate.defer.note": "Data kept only in this browser is lost if its storage is cleared."
2960
3173
  };
3174
+ var FR6 = {
3175
+ "gate.title": "O\xF9 enregistrer vos donn\xE9es ?",
3176
+ "gate.hint": "Elles sont chiffr\xE9es sur cet appareil. Choisissez une sauvegarde : tout y sera enregistr\xE9 automatiquement.",
3177
+ "gate.fine": "",
3178
+ "gate.defer": "Plus tard - garder sur cet appareil seulement",
3179
+ "gate.defer.note": "Sans sauvegarde, les donn\xE9es disparaissent si le stockage de cet appareil est vid\xE9."
3180
+ };
2961
3181
  var GATE_STYLES = `
2962
3182
  :host { display: contents; }
2963
3183
  [part~='gate'] {
@@ -3017,6 +3237,9 @@ var SelfstoreGateElement = class extends FlowWidget {
3017
3237
  defaults() {
3018
3238
  return EN6;
3019
3239
  }
3240
+ packs() {
3241
+ return { fr: FR6 };
3242
+ }
3020
3243
  /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
3021
3244
  get store() {
3022
3245
  return this.#store;