Convert nfc input to a web component

This commit is contained in:
Antoine Bartuccio 2024-11-14 12:01:57 +01:00
parent 346439076e
commit 63736a6c60
6 changed files with 353 additions and 231 deletions

View File

@ -0,0 +1,39 @@
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components";
@registerComponent("nfc-input")
export class NfcInput extends inheritHtmlElement("input") {
connectedCallback() {
super.connectedCallback();
/* Disable feature if browser is not supported or if not HTTPS */
// biome-ignore lint/correctness/noUndeclaredVariables: browser API
if (typeof NDEFReader === "undefined") {
return;
}
const button = document.createElement("button");
const logo = document.createElement("i");
logo.classList.add("fa-brands", "fa-nfc-symbol");
button.setAttribute("type", "button"); // Prevent form submission on click
button.appendChild(logo);
button.addEventListener("click", async () => {
// biome-ignore lint/correctness/noUndeclaredVariables: browser API
const ndef = new NDEFReader();
await ndef.scan();
ndef.addEventListener("readingerror", () => {
window.alert(gettext("Unsupported NFC card"));
});
// biome-ignore lint/correctness/noUndeclaredVariables: browser API
ndef.addEventListener("reading", (event: NDEFReadingEvent) => {
this.node.value = event.serialNumber.replace(/:/g, "").toUpperCase();
/* Auto submit form, we need another button to not trigger our previously defined click event */
const submit = document.createElement("button");
this.node.appendChild(submit);
submit.click();
submit.remove();
});
});
this.appendChild(button);
}
}

106
core/static/webpack/types/web-nfc.d.ts vendored Normal file
View File

@ -0,0 +1,106 @@
// Type definitions for Web NFC
// Project: https://github.com/w3c/web-nfc
// Definitions by: Takefumi Yoshii <https://github.com/takefumi-yoshii>
// TypeScript Version: 3.9
// This type definitions referenced to WebIDL.
// https://w3c.github.io/web-nfc/#actual-idl-index
// This has been modified to not trigger biome linting
// biome-ignore lint/correctness/noUnusedVariables: this is the official definition
interface Window {
// biome-ignore lint/style/useNamingConvention: this is the official API name
NDEFMessage: NDEFMessage;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare class NDEFMessage {
constructor(messageInit: NDEFMessageInit);
records: readonly NDEFRecord[];
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare interface NDEFMessageInit {
records: NDEFRecordInit[];
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare type NDEFRecordDataSource = string | BufferSource | NDEFMessageInit;
// biome-ignore lint/correctness/noUnusedVariables: this is the official definition
interface Window {
// biome-ignore lint/style/useNamingConvention: this is the official API name
NDEFRecord: NDEFRecord;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare class NDEFRecord {
constructor(recordInit: NDEFRecordInit);
readonly recordType: string;
readonly mediaType?: string;
readonly id?: string;
readonly data?: DataView;
readonly encoding?: string;
readonly lang?: string;
toRecords?: () => NDEFRecord[];
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare interface NDEFRecordInit {
recordType: string;
mediaType?: string;
id?: string;
encoding?: string;
lang?: string;
data?: NDEFRecordDataSource;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare type NDEFMessageSource = string | BufferSource | NDEFMessageInit;
// biome-ignore lint/correctness/noUnusedVariables: this is the official definition
interface Window {
// biome-ignore lint/style/useNamingConvention: this is the official API name
NDEFReader: NDEFReader;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare class NDEFReader extends EventTarget {
constructor();
// biome-ignore lint/suspicious/noExplicitAny: who am I to doubt the w3c definitions ?
onreading: (this: this, event: NDEFReadingEvent) => any;
// biome-ignore lint/suspicious/noExplicitAny: who am I to doubt the w3c definitions ?
onreadingerror: (this: this, error: Event) => any;
scan: (options?: NDEFScanOptions) => Promise<void>;
write: (message: NDEFMessageSource, options?: NDEFWriteOptions) => Promise<void>;
makeReadOnly: (options?: NDEFMakeReadOnlyOptions) => Promise<void>;
}
// biome-ignore lint/correctness/noUnusedVariables: this is the official definition
interface Window {
// biome-ignore lint/style/useNamingConvention: this is the official API name
NDEFReadingEvent: NDEFReadingEvent;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
declare class NDEFReadingEvent extends Event {
constructor(type: string, readingEventInitDict: NDEFReadingEventInit);
serialNumber: string;
message: NDEFMessage;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
interface NDEFReadingEventInit extends EventInit {
serialNumber?: string;
message: NDEFMessageInit;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
interface NDEFWriteOptions {
overwrite?: boolean;
signal?: AbortSignal;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
interface NDEFMakeReadOnlyOptions {
signal?: AbortSignal;
}
// biome-ignore lint/style/useNamingConvention: this is the official API name
interface NDEFScanOptions {
signal: AbortSignal;
}

View File

@ -1,33 +1,5 @@
<script-once src="{{ statics.js }}" defer></script-once>
<span>
<input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %}{% include "django/forms/widgets/attrs.html" %}>
<!-- NFC icon not available in fontawesome 4.7 -->
<button type="button" id="{{ widget.attrs.id }}_button"><i class="fa-brands fa-nfc-symbol"></i></button>
<nfc-input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %}{% include "django/forms/widgets/attrs.html" %}></nfc-input>
</span>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
let button = document.getElementById("{{ widget.attrs.id }}_button");
button.addEventListener("click", async () => {
let input = document.getElementById("{{ widget.attrs.id }}");
const ndef = new NDEFReader();
await ndef.scan();
ndef.addEventListener("readingerror", () => {
alert("{{ translations.unsupported }}")
});
ndef.addEventListener("reading", ({ message, serialNumber }) => {
input.value = serialNumber.replaceAll(":", "").toUpperCase();
/* Auto submit form */
b = document.createElement("button");
input.appendChild(b)
b.click()
b.remove()
});
});
/* Disable feature if browser is not supported or if not HTTPS */
if (typeof NDEFReader === "undefined") {
button.remove();
}
});
</script>

View File

@ -27,6 +27,9 @@ from captcha.fields import CaptchaField
from django import forms
from django.conf import settings
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.contrib.staticfiles.management.commands.collectstatic import (
staticfiles_storage,
)
from django.core.exceptions import ValidationError
from django.db import transaction
from django.forms import (
@ -72,7 +75,9 @@ class NFCTextInput(TextInput):
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context["translations"] = {"unsupported": _("Unsupported NFC card")}
context["statics"] = {
"js": staticfiles_storage.url("webpack/core/components/nfc-input-index.ts")
}
return context

View File

@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-10 15:57+0100\n"
"POT-Creation-Date: 2024-11-14 10:26+0100\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
@ -18,8 +18,8 @@ msgstr ""
#: accounting/models.py:62 accounting/models.py:101 accounting/models.py:132
#: accounting/models.py:190 club/models.py:55 com/models.py:274
#: com/models.py:293 counter/models.py:265 counter/models.py:296
#: counter/models.py:449 forum/models.py:60 launderette/models.py:29
#: com/models.py:293 counter/models.py:289 counter/models.py:320
#: counter/models.py:473 forum/models.py:60 launderette/models.py:29
#: launderette/models.py:80 launderette/models.py:116
msgid "name"
msgstr "nom"
@ -65,8 +65,8 @@ msgid "account number"
msgstr "numéro de compte"
#: accounting/models.py:107 accounting/models.py:136 club/models.py:345
#: com/models.py:74 com/models.py:259 com/models.py:299 counter/models.py:319
#: counter/models.py:451 trombi/models.py:209
#: com/models.py:74 com/models.py:259 com/models.py:299 counter/models.py:343
#: counter/models.py:475 trombi/models.py:209
msgid "club"
msgstr "club"
@ -87,12 +87,12 @@ msgstr "Compte club"
msgid "%(club_account)s on %(bank_account)s"
msgstr "%(club_account)s sur %(bank_account)s"
#: accounting/models.py:188 club/models.py:351 counter/models.py:929
#: accounting/models.py:188 club/models.py:351 counter/models.py:953
#: election/models.py:16 launderette/models.py:165
msgid "start date"
msgstr "date de début"
#: accounting/models.py:189 club/models.py:352 counter/models.py:930
#: accounting/models.py:189 club/models.py:352 counter/models.py:954
#: election/models.py:17
msgid "end date"
msgstr "date de fin"
@ -105,8 +105,8 @@ msgstr "est fermé"
msgid "club account"
msgstr "compte club"
#: accounting/models.py:199 accounting/models.py:255 counter/models.py:57
#: counter/models.py:647
#: accounting/models.py:199 accounting/models.py:255 counter/models.py:91
#: counter/models.py:671
msgid "amount"
msgstr "montant"
@ -128,18 +128,18 @@ msgstr "classeur"
#: accounting/models.py:256 core/models.py:945 core/models.py:1456
#: core/models.py:1501 core/models.py:1530 core/models.py:1554
#: counter/models.py:657 counter/models.py:761 counter/models.py:965
#: counter/models.py:681 counter/models.py:785 counter/models.py:989
#: eboutic/models.py:57 eboutic/models.py:193 forum/models.py:312
#: forum/models.py:413
msgid "date"
msgstr "date"
#: accounting/models.py:257 counter/models.py:267 counter/models.py:966
#: accounting/models.py:257 counter/models.py:291 counter/models.py:990
#: pedagogy/models.py:208
msgid "comment"
msgstr "commentaire"
#: accounting/models.py:259 counter/models.py:659 counter/models.py:763
#: accounting/models.py:259 counter/models.py:683 counter/models.py:787
#: subscription/models.py:56
msgid "payment method"
msgstr "méthode de paiement"
@ -166,7 +166,7 @@ msgstr "type comptable"
#: accounting/models.py:294 accounting/models.py:429 accounting/models.py:460
#: accounting/models.py:492 core/models.py:1529 core/models.py:1555
#: counter/models.py:727
#: counter/models.py:751
msgid "label"
msgstr "étiquette"
@ -210,7 +210,7 @@ msgstr "Utilisateur"
msgid "Club"
msgstr "Club"
#: accounting/models.py:305 core/views/user.py:284
#: accounting/models.py:305 core/views/user.py:283
msgid "Account"
msgstr "Compte"
@ -264,7 +264,7 @@ msgstr ""
"Vous devez fournir soit un type comptable simplifié ou un type comptable "
"standard"
#: accounting/models.py:421 counter/models.py:306 pedagogy/models.py:41
#: accounting/models.py:421 counter/models.py:330 pedagogy/models.py:41
msgid "code"
msgstr "code"
@ -360,7 +360,7 @@ msgstr "Compte en banque : "
#: com/templates/com/screen_edit.jinja:16 com/templates/com/weekmail.jinja:33
#: com/templates/com/weekmail.jinja:62 core/templates/core/file_detail.jinja:25
#: core/templates/core/file_detail.jinja:62
#: core/templates/core/file_moderation.jinja:24
#: core/templates/core/file_moderation.jinja:48
#: core/templates/core/group_detail.jinja:26
#: core/templates/core/group_list.jinja:25 core/templates/core/macros.jinja:96
#: core/templates/core/macros.jinja:115 core/templates/core/page_prop.jinja:14
@ -385,7 +385,7 @@ msgid "Delete"
msgstr "Supprimer"
#: accounting/templates/accounting/bank_account_details.jinja:18
#: club/views.py:79 core/views/user.py:202 sas/templates/sas/picture.jinja:91
#: club/views.py:79 core/views/user.py:201 sas/templates/sas/picture.jinja:91
msgid "Infos"
msgstr "Infos"
@ -416,10 +416,10 @@ msgstr "Nouveau compte club"
#: com/templates/com/poster_list.jinja:43
#: com/templates/com/poster_list.jinja:45
#: com/templates/com/screen_list.jinja:26 com/templates/com/weekmail.jinja:32
#: com/templates/com/weekmail.jinja:61 core/templates/core/file.jinja:38
#: com/templates/com/weekmail.jinja:61 core/templates/core/file.jinja:42
#: core/templates/core/group_list.jinja:24 core/templates/core/page.jinja:35
#: core/templates/core/poster_list.jinja:40
#: core/templates/core/user_tools.jinja:71 core/views/user.py:232
#: core/templates/core/user_tools.jinja:71 core/views/user.py:231
#: counter/templates/counter/cash_summary_list.jinja:53
#: counter/templates/counter/counter_list.jinja:17
#: counter/templates/counter/counter_list.jinja:33
@ -556,7 +556,7 @@ msgstr "Non"
#: com/templates/com/news_admin_list.jinja:231
#: com/templates/com/news_admin_list.jinja:272
#: com/templates/com/news_admin_list.jinja:307
#: core/templates/core/file.jinja:36 core/templates/core/page.jinja:31
#: core/templates/core/file.jinja:40 core/templates/core/page.jinja:31
msgid "View"
msgstr "Voir"
@ -1041,7 +1041,7 @@ msgstr "Vous ne pouvez pas faire de boucles dans les clubs"
msgid "A club with that unix_name already exists"
msgstr "Un club avec ce nom UNIX existe déjà."
#: club/models.py:337 counter/models.py:920 counter/models.py:956
#: club/models.py:337 counter/models.py:944 counter/models.py:980
#: eboutic/models.py:53 eboutic/models.py:189 election/models.py:183
#: launderette/models.py:130 launderette/models.py:184 sas/models.py:273
#: trombi/models.py:205
@ -1053,8 +1053,8 @@ msgstr "nom d'utilisateur"
msgid "role"
msgstr "rôle"
#: club/models.py:359 core/models.py:89 counter/models.py:266
#: counter/models.py:297 election/models.py:13 election/models.py:115
#: club/models.py:359 core/models.py:89 counter/models.py:290
#: counter/models.py:321 election/models.py:13 election/models.py:115
#: election/models.py:188 forum/models.py:61 forum/models.py:245
msgid "description"
msgstr "description"
@ -1146,7 +1146,7 @@ msgid "There are no members in this club."
msgstr "Il n'y a pas de membres dans ce club."
#: club/templates/club/club_members.jinja:80
#: core/templates/core/file_detail.jinja:19 core/views/forms.py:301
#: core/templates/core/file_detail.jinja:19 core/views/forms.py:308
#: launderette/views.py:210 trombi/templates/trombi/detail.jinja:19
msgid "Add"
msgstr "Ajouter"
@ -1369,8 +1369,9 @@ msgstr "Anciens membres"
msgid "History"
msgstr "Historique"
#: club/views.py:116 core/templates/core/base.jinja:106 core/views/user.py:225
#: sas/templates/sas/picture.jinja:110 trombi/views.py:62
#: club/views.py:116 core/templates/core/base/header.jinja:61
#: core/views/user.py:224 sas/templates/sas/picture.jinja:110
#: trombi/views.py:62
msgid "Tools"
msgstr "Outils"
@ -1512,7 +1513,7 @@ msgstr "Administration des mailing listes"
#: com/templates/com/news_admin_list.jinja:309
#: com/templates/com/news_detail.jinja:39
#: core/templates/core/file_detail.jinja:65
#: core/templates/core/file_moderation.jinja:23
#: core/templates/core/file_moderation.jinja:42
#: sas/templates/sas/moderation.jinja:17 sas/templates/sas/picture.jinja:68
msgid "Moderate"
msgstr "Modérer"
@ -1655,7 +1656,7 @@ msgid "Calls to moderate"
msgstr "Appels à modérer"
#: com/templates/com/news_admin_list.jinja:242
#: core/templates/core/base.jinja:221
#: core/templates/core/base/navbar.jinja:14
msgid "Events"
msgstr "Événements"
@ -2392,18 +2393,44 @@ msgstr "500, Erreur Serveur"
msgid "Welcome!"
msgstr "Bienvenue !"
#: core/templates/core/base.jinja:58 core/templates/core/login.jinja:8
#: core/templates/core/login.jinja:18 core/templates/core/login.jinja:51
#: core/templates/core/base.jinja:104 core/templates/core/base/navbar.jinja:43
msgid "Contacts"
msgstr "Contacts"
#: core/templates/core/base.jinja:105
msgid "Legal notices"
msgstr "Mentions légales"
#: core/templates/core/base.jinja:106
msgid "Intellectual property"
msgstr "Propriété intellectuelle"
#: core/templates/core/base.jinja:107
msgid "Help & Documentation"
msgstr "Aide & Documentation"
#: core/templates/core/base.jinja:108
msgid "R&D"
msgstr "R&D"
#: core/templates/core/base.jinja:111
msgid "Site created by the IT Department of the AE"
msgstr "Site réalisé par le Pôle Informatique de l'AE"
#: core/templates/core/base/header.jinja:13 core/templates/core/login.jinja:8
#: core/templates/core/login.jinja:18 core/templates/core/login.jinja:50
#: core/templates/core/password_reset_complete.jinja:5
msgid "Login"
msgstr "Connexion"
#: core/templates/core/base.jinja:59 core/templates/core/register.jinja:7
#: core/templates/core/register.jinja:16 core/templates/core/register.jinja:22
#: core/templates/core/base/header.jinja:14
#: core/templates/core/register.jinja:7 core/templates/core/register.jinja:16
#: core/templates/core/register.jinja:22
msgid "Register"
msgstr "Inscription"
#: core/templates/core/base.jinja:65 core/templates/core/base.jinja:66
#: core/templates/core/base/header.jinja:20
#: core/templates/core/base/header.jinja:21
#: forum/templates/forum/macros.jinja:179
#: forum/templates/forum/macros.jinja:183
#: matmat/templates/matmat/search_form.jinja:39
@ -2412,52 +2439,53 @@ msgstr "Inscription"
msgid "Search"
msgstr "Recherche"
#: core/templates/core/base.jinja:107
#: core/templates/core/base/header.jinja:62
msgid "Logout"
msgstr "Déconnexion"
#: core/templates/core/base.jinja:155
#: core/templates/core/base/header.jinja:110
msgid "You do not have any unread notification"
msgstr "Vous n'avez aucune notification non lue"
#: core/templates/core/base.jinja:160
#: core/templates/core/base/header.jinja:115
msgid "View more"
msgstr "Voir plus"
#: core/templates/core/base.jinja:163
#: core/templates/core/base/header.jinja:118
#: forum/templates/forum/last_unread.jinja:21
msgid "Mark all as read"
msgstr "Marquer tout comme lu"
#: core/templates/core/base.jinja:211
#: core/templates/core/base/navbar.jinja:4
msgid "Main"
msgstr "Accueil"
#: core/templates/core/base.jinja:213
#: core/templates/core/base/navbar.jinja:6
msgid "Associations & Clubs"
msgstr "Associations & Clubs"
#: core/templates/core/base.jinja:215
#: core/templates/core/base/navbar.jinja:8
msgid "AE"
msgstr "L'AE"
#: core/templates/core/base.jinja:216
#: core/templates/core/base/navbar.jinja:9
msgid "AE's clubs"
msgstr "Les clubs de L'AE"
#: core/templates/core/base.jinja:217
#: core/templates/core/base/navbar.jinja:10
msgid "Others UTBM's Associations"
msgstr "Les autres associations de l'UTBM"
#: core/templates/core/base.jinja:223 core/templates/core/user_tools.jinja:172
#: core/templates/core/base/navbar.jinja:16
#: core/templates/core/user_tools.jinja:172
msgid "Elections"
msgstr "Élections"
#: core/templates/core/base.jinja:224
#: core/templates/core/base/navbar.jinja:17
msgid "Big event"
msgstr "Grandes Activités"
#: core/templates/core/base.jinja:227
#: core/templates/core/base/navbar.jinja:20
#: forum/templates/forum/favorite_topics.jinja:18
#: forum/templates/forum/last_unread.jinja:18
#: forum/templates/forum/macros.jinja:90 forum/templates/forum/main.jinja:6
@ -2466,11 +2494,11 @@ msgstr "Grandes Activités"
msgid "Forum"
msgstr "Forum"
#: core/templates/core/base.jinja:228
#: core/templates/core/base/navbar.jinja:21
msgid "Gallery"
msgstr "Photos"
#: core/templates/core/base.jinja:229 counter/models.py:459
#: core/templates/core/base/navbar.jinja:22 counter/models.py:483
#: counter/templates/counter/counter_list.jinja:11
#: eboutic/templates/eboutic/eboutic_main.jinja:4
#: eboutic/templates/eboutic/eboutic_main.jinja:22
@ -2480,78 +2508,55 @@ msgstr "Photos"
msgid "Eboutic"
msgstr "Eboutic"
#: core/templates/core/base.jinja:231
#: core/templates/core/base/navbar.jinja:24
msgid "Services"
msgstr "Services"
#: core/templates/core/base.jinja:233
#: core/templates/core/base/navbar.jinja:26
msgid "Matmatronch"
msgstr "Matmatronch"
#: core/templates/core/base.jinja:234 launderette/models.py:38
#: core/templates/core/base/navbar.jinja:27 launderette/models.py:38
#: launderette/templates/launderette/launderette_book.jinja:5
#: launderette/templates/launderette/launderette_book_choose.jinja:4
#: launderette/templates/launderette/launderette_main.jinja:4
msgid "Launderette"
msgstr "Laverie"
#: core/templates/core/base.jinja:235 core/templates/core/file.jinja:20
#: core/views/files.py:120
#: core/templates/core/base/navbar.jinja:28 core/templates/core/file.jinja:24
#: core/views/files.py:121
msgid "Files"
msgstr "Fichiers"
#: core/templates/core/base.jinja:236 core/templates/core/user_tools.jinja:163
#: core/templates/core/base/navbar.jinja:29
#: core/templates/core/user_tools.jinja:163
msgid "Pedagogy"
msgstr "Pédagogie"
#: core/templates/core/base.jinja:240
#: core/templates/core/base/navbar.jinja:33
msgid "My Benefits"
msgstr "Mes Avantages"
#: core/templates/core/base.jinja:242
#: core/templates/core/base/navbar.jinja:35
msgid "Sponsors"
msgstr "Partenaires"
#: core/templates/core/base.jinja:243
#: core/templates/core/base/navbar.jinja:36
msgid "Subscriber benefits"
msgstr "Les avantages cotisants"
#: core/templates/core/base.jinja:247
#: core/templates/core/base/navbar.jinja:40
msgid "Help"
msgstr "Aide"
#: core/templates/core/base.jinja:249
#: core/templates/core/base/navbar.jinja:42
msgid "FAQ"
msgstr "FAQ"
#: core/templates/core/base.jinja:250 core/templates/core/base.jinja:290
msgid "Contacts"
msgstr "Contacts"
#: core/templates/core/base.jinja:251
#: core/templates/core/base/navbar.jinja:44
msgid "Wiki"
msgstr "Wiki"
#: core/templates/core/base.jinja:291
msgid "Legal notices"
msgstr "Mentions légales"
#: core/templates/core/base.jinja:292
msgid "Intellectual property"
msgstr "Propriété intellectuelle"
#: core/templates/core/base.jinja:293
msgid "Help & Documentation"
msgstr "Aide & Documentation"
#: core/templates/core/base.jinja:294
msgid "R&D"
msgstr "R&D"
#: core/templates/core/base.jinja:297
msgid "Site created by the IT Department of the AE"
msgstr "Site réalisé par le Pôle Informatique de l'AE"
#: core/templates/core/create.jinja:4 core/templates/core/create.jinja:8
#, python-format
msgid "Create %(name)s"
@ -2560,23 +2565,23 @@ msgstr "Créer %(name)s"
#: core/templates/core/delete_confirm.jinja:4
#: core/templates/core/delete_confirm.jinja:14
#: core/templates/core/file_delete_confirm.jinja:4
#: core/templates/core/file_delete_confirm.jinja:8
#: core/templates/core/file_delete_confirm.jinja:18
msgid "Delete confirmation"
msgstr "Confirmation de suppression"
#: core/templates/core/delete_confirm.jinja:16
#: core/templates/core/file_delete_confirm.jinja:10
#: core/templates/core/file_delete_confirm.jinja:29
#, python-format
msgid "Are you sure you want to delete \"%(obj)s\"?"
msgstr "Êtes-vous sûr de vouloir supprimer \"%(obj)s\" ?"
#: core/templates/core/delete_confirm.jinja:17
#: core/templates/core/file_delete_confirm.jinja:11
#: core/templates/core/file_delete_confirm.jinja:36
msgid "Confirm"
msgstr "Confirmation"
#: core/templates/core/delete_confirm.jinja:20
#: core/templates/core/file_delete_confirm.jinja:14
#: core/templates/core/file_delete_confirm.jinja:46
#: counter/templates/counter/counter_click.jinja:121
#: sas/templates/sas/ask_picture_removal.jinja:20
msgid "Cancel"
@ -2589,28 +2594,28 @@ msgstr "Annuler"
msgid "Edit %(obj)s"
msgstr "Éditer %(obj)s"
#: core/templates/core/file.jinja:7 core/templates/core/file_list.jinja:6
#: core/templates/core/file.jinja:11 core/templates/core/file_list.jinja:6
msgid "File list"
msgstr "Liste de fichiers"
#: core/templates/core/file.jinja:9
#: core/templates/core/file.jinja:13
msgid "New file"
msgstr "Nouveau fichier"
#: core/templates/core/file.jinja:11 core/templates/core/page.jinja:11
#: core/templates/core/file.jinja:15 core/templates/core/page.jinja:11
msgid "Not found"
msgstr "Non trouvé"
#: core/templates/core/file.jinja:32
#: core/templates/core/file.jinja:36
msgid "My files"
msgstr "Mes fichiers"
#: core/templates/core/file.jinja:41 core/templates/core/page.jinja:38
#: core/templates/core/file.jinja:45 core/templates/core/page.jinja:38
msgid "Prop"
msgstr "Propriétés"
#: core/templates/core/file_detail.jinja:13
#: core/templates/core/file_moderation.jinja:20
#: core/templates/core/file_moderation.jinja:35
#: sas/templates/sas/picture.jinja:103
msgid "Owner: "
msgstr "Propriétaire : "
@ -2638,7 +2643,7 @@ msgid "Real name: "
msgstr "Nom réel : "
#: core/templates/core/file_detail.jinja:54
#: core/templates/core/file_moderation.jinja:21
#: core/templates/core/file_moderation.jinja:36
#: sas/templates/sas/picture.jinja:94
msgid "Date: "
msgstr "Date : "
@ -2663,12 +2668,12 @@ msgstr "Télécharger"
msgid "There is no file in this website."
msgstr "Il n'y a pas de fichier sur ce site web."
#: core/templates/core/file_moderation.jinja:4
#: core/templates/core/file_moderation.jinja:8
#: core/templates/core/file_moderation.jinja:16
#: core/templates/core/file_moderation.jinja:20
msgid "File moderation"
msgstr "Modération des fichiers"
#: core/templates/core/file_moderation.jinja:19
#: core/templates/core/file_moderation.jinja:34
msgid "Full name: "
msgstr "Nom complet : "
@ -2731,11 +2736,11 @@ msgstr ""
"Votre nom d'utilisateur et votre mot de passe ne correspondent pas. Merci de "
"réessayer."
#: core/templates/core/login.jinja:56
#: core/templates/core/login.jinja:55
msgid "Lost password?"
msgstr "Mot de passe perdu ?"
#: core/templates/core/login.jinja:58
#: core/templates/core/login.jinja:57
msgid "Create account"
msgstr "Créer un compte"
@ -2765,11 +2770,11 @@ msgstr "Créneau"
msgid "Tokens"
msgstr "Jetons"
#: core/templates/core/macros.jinja:213
#: core/templates/core/macros.jinja:258
msgid "Select All"
msgstr "Tout sélectionner"
#: core/templates/core/macros.jinja:214
#: core/templates/core/macros.jinja:259
msgid "Unselect All"
msgstr "Tout désélectionner"
@ -2995,7 +3000,7 @@ msgstr "Résultat de la recherche"
msgid "Users"
msgstr "Utilisateurs"
#: core/templates/core/search.jinja:20 core/views/user.py:247
#: core/templates/core/search.jinja:20 core/views/user.py:246
msgid "Clubs"
msgstr "Clubs"
@ -3040,7 +3045,7 @@ msgstr "Facture eboutic"
msgid "Etickets"
msgstr "Etickets"
#: core/templates/core/user_account.jinja:69 core/views/user.py:639
#: core/templates/core/user_account.jinja:69 core/views/user.py:638
msgid "User has no account"
msgstr "L'utilisateur n'a pas de compte"
@ -3268,7 +3273,7 @@ msgid "To be moderated"
msgstr "A modérer"
#: core/templates/core/user_preferences.jinja:8
#: core/templates/core/user_preferences.jinja:13 core/views/user.py:239
#: core/templates/core/user_preferences.jinja:13 core/views/user.py:238
msgid "Preferences"
msgstr "Préférences"
@ -3343,7 +3348,7 @@ msgstr "Outils utilisateurs"
msgid "Sith management"
msgstr "Gestion de Sith"
#: core/templates/core/user_tools.jinja:21 core/views/user.py:255
#: core/templates/core/user_tools.jinja:21 core/views/user.py:254
msgid "Groups"
msgstr "Groupes"
@ -3398,7 +3403,7 @@ msgstr "Relevés de caisse"
msgid "Invoices call"
msgstr "Appels à facture"
#: core/templates/core/user_tools.jinja:72 core/views/user.py:277
#: core/templates/core/user_tools.jinja:72 core/views/user.py:276
#: counter/templates/counter/counter_list.jinja:18
#: counter/templates/counter/counter_list.jinja:34
#: counter/templates/counter/counter_list.jinja:50
@ -3483,42 +3488,38 @@ msgid_plural "%(nb_days)d days, %(remainder)s"
msgstr[0] ""
msgstr[1] ""
#: core/views/files.py:117
#: core/views/files.py:118
msgid "Add a new folder"
msgstr "Ajouter un nouveau dossier"
#: core/views/files.py:137
#: core/views/files.py:138
#, python-format
msgid "Error creating folder %(folder_name)s: %(msg)s"
msgstr "Erreur de création du dossier %(folder_name)s : %(msg)s"
#: core/views/files.py:157 core/views/forms.py:266 core/views/forms.py:273
#: core/views/files.py:158 core/views/forms.py:273 core/views/forms.py:280
#: sas/forms.py:60
#, python-format
msgid "Error uploading file %(file_name)s: %(msg)s"
msgstr "Erreur d'envoi du fichier %(file_name)s : %(msg)s"
#: core/views/files.py:231 sas/forms.py:83
#: core/views/files.py:232 sas/forms.py:83
msgid "Apply rights recursively"
msgstr "Appliquer les droits récursivement"
#: core/views/forms.py:75
msgid "Unsupported NFC card"
msgstr "Carte NFC non supportée"
#: core/views/forms.py:89 core/views/forms.py:97
#: core/views/forms.py:96 core/views/forms.py:104
msgid "Choose file"
msgstr "Choisir un fichier"
#: core/views/forms.py:113 core/views/forms.py:121
#: core/views/forms.py:120 core/views/forms.py:128
msgid "Choose user"
msgstr "Choisir un utilisateur"
#: core/views/forms.py:153
#: core/views/forms.py:160
msgid "Username, email, or account number"
msgstr "Nom d'utilisateur, email, ou numéro de compte AE"
#: core/views/forms.py:216
#: core/views/forms.py:223
msgid ""
"Profile: you need to be visible on the picture, in order to be recognized (e."
"g. by the barmen)"
@ -3526,44 +3527,44 @@ msgstr ""
"Photo de profil: vous devez être visible sur la photo afin d'être reconnu "
"(par exemple par les barmen)"
#: core/views/forms.py:221
#: core/views/forms.py:228
msgid "Avatar: used on the forum"
msgstr "Avatar : utilisé sur le forum"
#: core/views/forms.py:225
#: core/views/forms.py:232
msgid "Scrub: let other know how your scrub looks like!"
msgstr "Blouse : montrez aux autres à quoi ressemble votre blouse !"
#: core/views/forms.py:277
#: core/views/forms.py:284
msgid "Bad image format, only jpeg, png, webp and gif are accepted"
msgstr "Mauvais format d'image, seuls les jpeg, png, webp et gif sont acceptés"
#: core/views/forms.py:298
#: core/views/forms.py:305
msgid "Godfather / Godmother"
msgstr "Parrain / Marraine"
#: core/views/forms.py:299
#: core/views/forms.py:306
msgid "Godchild"
msgstr "Fillot / Fillote"
#: core/views/forms.py:304 counter/forms.py:82 trombi/views.py:151
#: core/views/forms.py:311 counter/forms.py:82 trombi/views.py:151
msgid "Select user"
msgstr "Choisir un utilisateur"
#: core/views/forms.py:318
#: core/views/forms.py:325
msgid "This user does not exist"
msgstr "Cet utilisateur n'existe pas"
#: core/views/forms.py:320
#: core/views/forms.py:327
msgid "You cannot be related to yourself"
msgstr "Vous ne pouvez pas être relié à vous-même"
#: core/views/forms.py:332
#: core/views/forms.py:339
#, python-format
msgid "%s is already your godfather"
msgstr "%s est déjà votre parrain/marraine"
#: core/views/forms.py:338
#: core/views/forms.py:345
#, python-format
msgid "%s is already your godchild"
msgstr "%s est déjà votre fillot/fillote"
@ -3576,26 +3577,26 @@ msgstr "Utilisateurs à ajouter au groupe"
msgid "Users to remove from group"
msgstr "Utilisateurs à retirer du groupe"
#: core/views/user.py:184
#: core/views/user.py:183
msgid "We couldn't verify that this email actually exists"
msgstr "Nous n'avons pas réussi à vérifier que cette adresse mail existe."
#: core/views/user.py:207
#: core/views/user.py:206
msgid "Family"
msgstr "Famille"
#: core/views/user.py:212 sas/templates/sas/album.jinja:67
#: core/views/user.py:211 sas/templates/sas/album.jinja:67
#: trombi/templates/trombi/export.jinja:25
#: trombi/templates/trombi/user_profile.jinja:11
msgid "Pictures"
msgstr "Photos"
#: core/views/user.py:220
#: core/views/user.py:219
msgid "Galaxy"
msgstr "Galaxie"
#: counter/apps.py:30 counter/models.py:475 counter/models.py:926
#: counter/models.py:962 launderette/models.py:32
#: counter/apps.py:30 counter/models.py:499 counter/models.py:950
#: counter/models.py:986 launderette/models.py:32
msgid "counter"
msgstr "comptoir"
@ -3615,181 +3616,181 @@ msgstr "Vidange de votre compte AE"
msgid "Ecocup regularization"
msgstr "Régularization des ecocups"
#: counter/models.py:56
#: counter/models.py:90
msgid "account id"
msgstr "numéro de compte"
#: counter/models.py:58
#: counter/models.py:92
msgid "recorded product"
msgstr "produits consignés"
#: counter/models.py:61
#: counter/models.py:97
msgid "customer"
msgstr "client"
#: counter/models.py:62
#: counter/models.py:98
msgid "customers"
msgstr "clients"
#: counter/models.py:74 counter/views.py:261
#: counter/models.py:110 counter/views.py:261
msgid "Not enough money"
msgstr "Solde insuffisant"
#: counter/models.py:172
#: counter/models.py:196
msgid "First name"
msgstr "Prénom"
#: counter/models.py:173
#: counter/models.py:197
msgid "Last name"
msgstr "Nom de famille"
#: counter/models.py:174
#: counter/models.py:198
msgid "Address 1"
msgstr "Adresse 1"
#: counter/models.py:175
#: counter/models.py:199
msgid "Address 2"
msgstr "Adresse 2"
#: counter/models.py:176
#: counter/models.py:200
msgid "Zip code"
msgstr "Code postal"
#: counter/models.py:177
#: counter/models.py:201
msgid "City"
msgstr "Ville"
#: counter/models.py:178
#: counter/models.py:202
msgid "Country"
msgstr "Pays"
#: counter/models.py:186
#: counter/models.py:210
msgid "Phone number"
msgstr "Numéro de téléphone"
#: counter/models.py:228
#: counter/models.py:252
msgid "When the mail warning that the account was about to be dumped was sent."
msgstr "Quand le mail d'avertissement de la vidange du compte a été envoyé."
#: counter/models.py:233
#: counter/models.py:257
msgid "Set this to True if the warning mail received an error"
msgstr "Mettre à True si le mail a reçu une erreur"
#: counter/models.py:240
#: counter/models.py:264
msgid "The operation that emptied the account."
msgstr "L'opération qui a vidé le compte."
#: counter/models.py:277 counter/models.py:301
#: counter/models.py:301 counter/models.py:325
msgid "product type"
msgstr "type du produit"
#: counter/models.py:307
#: counter/models.py:331
msgid "purchase price"
msgstr "prix d'achat"
#: counter/models.py:308
#: counter/models.py:332
msgid "selling price"
msgstr "prix de vente"
#: counter/models.py:309
#: counter/models.py:333
msgid "special selling price"
msgstr "prix de vente spécial"
#: counter/models.py:316
#: counter/models.py:340
msgid "icon"
msgstr "icône"
#: counter/models.py:321
#: counter/models.py:345
msgid "limit age"
msgstr "âge limite"
#: counter/models.py:322
#: counter/models.py:346
msgid "tray price"
msgstr "prix plateau"
#: counter/models.py:326
#: counter/models.py:350
msgid "parent product"
msgstr "produit parent"
#: counter/models.py:332
#: counter/models.py:356
msgid "buying groups"
msgstr "groupe d'achat"
#: counter/models.py:334 election/models.py:50
#: counter/models.py:358 election/models.py:50
msgid "archived"
msgstr "archivé"
#: counter/models.py:337 counter/models.py:1060
#: counter/models.py:361 counter/models.py:1084
msgid "product"
msgstr "produit"
#: counter/models.py:454
#: counter/models.py:478
msgid "products"
msgstr "produits"
#: counter/models.py:457
#: counter/models.py:481
msgid "counter type"
msgstr "type de comptoir"
#: counter/models.py:459
#: counter/models.py:483
msgid "Bar"
msgstr "Bar"
#: counter/models.py:459
#: counter/models.py:483
msgid "Office"
msgstr "Bureau"
#: counter/models.py:462
#: counter/models.py:486
msgid "sellers"
msgstr "vendeurs"
#: counter/models.py:470 launderette/models.py:178
#: counter/models.py:494 launderette/models.py:178
msgid "token"
msgstr "jeton"
#: counter/models.py:665
#: counter/models.py:689
msgid "bank"
msgstr "banque"
#: counter/models.py:667 counter/models.py:768
#: counter/models.py:691 counter/models.py:792
msgid "is validated"
msgstr "est validé"
#: counter/models.py:672
#: counter/models.py:696
msgid "refilling"
msgstr "rechargement"
#: counter/models.py:745 eboutic/models.py:249
#: counter/models.py:769 eboutic/models.py:249
msgid "unit price"
msgstr "prix unitaire"
#: counter/models.py:746 counter/models.py:1040 eboutic/models.py:250
#: counter/models.py:770 counter/models.py:1064 eboutic/models.py:250
msgid "quantity"
msgstr "quantité"
#: counter/models.py:765
#: counter/models.py:789
msgid "Sith account"
msgstr "Compte utilisateur"
#: counter/models.py:765 sith/settings.py:411 sith/settings.py:416
#: counter/models.py:789 sith/settings.py:411 sith/settings.py:416
#: sith/settings.py:436
msgid "Credit card"
msgstr "Carte bancaire"
#: counter/models.py:773
#: counter/models.py:797
msgid "selling"
msgstr "vente"
#: counter/models.py:877
#: counter/models.py:901
msgid "Unknown event"
msgstr "Événement inconnu"
#: counter/models.py:878
#: counter/models.py:902
#, python-format
msgid "Eticket bought for the event %(event)s"
msgstr "Eticket acheté pour l'événement %(event)s"
#: counter/models.py:880 counter/models.py:893
#: counter/models.py:904 counter/models.py:917
#, python-format
msgid ""
"You bought an eticket for the event %(event)s.\n"
@ -3801,63 +3802,63 @@ msgstr ""
"Vous pouvez également retrouver tous vos e-tickets sur votre page de compte "
"%(url)s."
#: counter/models.py:931
#: counter/models.py:955
msgid "last activity date"
msgstr "dernière activité"
#: counter/models.py:934
#: counter/models.py:958
msgid "permanency"
msgstr "permanence"
#: counter/models.py:967
#: counter/models.py:991
msgid "emptied"
msgstr "coffre vidée"
#: counter/models.py:970
#: counter/models.py:994
msgid "cash register summary"
msgstr "relevé de caisse"
#: counter/models.py:1036
#: counter/models.py:1060
msgid "cash summary"
msgstr "relevé"
#: counter/models.py:1039
#: counter/models.py:1063
msgid "value"
msgstr "valeur"
#: counter/models.py:1042
#: counter/models.py:1066
msgid "check"
msgstr "chèque"
#: counter/models.py:1044
#: counter/models.py:1068
msgid "True if this is a bank check, else False"
msgstr "Vrai si c'est un chèque, sinon Faux."
#: counter/models.py:1048
#: counter/models.py:1072
msgid "cash register summary item"
msgstr "élément de relevé de caisse"
#: counter/models.py:1064
#: counter/models.py:1088
msgid "banner"
msgstr "bannière"
#: counter/models.py:1066
#: counter/models.py:1090
msgid "event date"
msgstr "date de l'événement"
#: counter/models.py:1068
#: counter/models.py:1092
msgid "event title"
msgstr "titre de l'événement"
#: counter/models.py:1070
#: counter/models.py:1094
msgid "secret"
msgstr "secret"
#: counter/models.py:1109
#: counter/models.py:1133
msgid "uid"
msgstr "uid"
#: counter/models.py:1114
#: counter/models.py:1138
msgid "student cards"
msgstr "cartes étudiante"

View File

@ -7,7 +7,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-10 16:00+0100\n"
"POT-Creation-Date: 2024-11-14 10:24+0100\n"
"PO-Revision-Date: 2024-09-17 11:54+0200\n"
"Last-Translator: Sli <antoine@bartuccio.fr>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
@ -23,17 +23,14 @@ msgid "captured.%s"
msgstr "capture.%s"
#: core/static/webpack/core/components/ajax-select-base.ts:68
#: staticfiles/generated/webpack/core/static/webpack/core/components/ajax-select-base.js:57
msgid "Remove"
msgstr "Retirer"
#: core/static/webpack/core/components/ajax-select-base.ts:88
#: staticfiles/generated/webpack/core/static/webpack/core/components/ajax-select-base.js:77
msgid "You need to type %(number)s more characters"
msgstr "Vous devez taper %(number)s caractères de plus"
#: core/static/webpack/core/components/ajax-select-base.ts:92
#: staticfiles/generated/webpack/core/static/webpack/core/components/ajax-select-base.js:81
msgid "No results found"
msgstr "Aucun résultat trouvé"
@ -113,6 +110,10 @@ msgstr "Activer le plein écran"
msgid "Markdown guide"
msgstr "Guide markdown"
#: core/static/webpack/core/components/nfc-input-index.ts:24
msgid "Unsupported NFC card"
msgstr "Carte NFC non supportée"
#: core/static/webpack/user/family-graph-index.js:233
msgid "family_tree.%(extension)s"
msgstr "arbre_genealogique.%(extension)s"
@ -126,11 +127,9 @@ msgid "Incorrect value"
msgstr "Valeur incorrecte"
#: sas/static/webpack/sas/viewer-index.ts:271
#: staticfiles/generated/webpack/sas/static/webpack/sas/viewer-index.js:234
msgid "Couldn't moderate picture"
msgstr "Il n'a pas été possible de modérer l'image"
#: sas/static/webpack/sas/viewer-index.ts:284
#: staticfiles/generated/webpack/sas/static/webpack/sas/viewer-index.js:248
msgid "Couldn't delete picture"
msgstr "Il n'a pas été possible de supprimer l'image"