diff --git a/ChangeLog.md b/ChangeLog.md index b1839a8b..34d76981 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,21 @@ # Changelog: +* **23.12.18** + - Added query account management (since server 1.2.32b) + +* **18.12.18** + - Added bookmarks and bookmarks management + - Added query user visibility button and creation (Query management will follow soon) + - Fixed overflow within the group assignment dialog + +* **17.12.18** + - Implemented group prefix and suffix + +* **15.12.18** + - Implemented a translation system with default translated language sets for + - German + - Turkish + - Russian + * **3.12.18** - Fixed url connect parameters diff --git a/shared/css/control_bar.scss b/shared/css/control_bar.scss index bad8261d..5b8188a3 100644 --- a/shared/css/control_bar.scss +++ b/shared/css/control_bar.scss @@ -103,9 +103,7 @@ $background:lightgray; background-color: $background; border-radius: 5px; align-items: center; - border: 2px solid rgba(0, 0, 0, 0); - - border-color: $border_color_activated; + border: 2px solid $border_color_activated; width: 230px; user-select: none; @@ -134,6 +132,10 @@ $background:lightgray; & > div:last-of-type { border-radius: 0 0 2px 2px; } + + &.display_left { + margin-left: -165px; + } } &:hover { @@ -142,4 +144,10 @@ $background:lightgray; } } } + + .bookmark-dropdown { + hr:last-child { + display: none; + } + } } \ No newline at end of file diff --git a/shared/css/modal-query.scss b/shared/css/modal-query.scss new file mode 100644 index 00000000..16c70228 --- /dev/null +++ b/shared/css/modal-query.scss @@ -0,0 +1,173 @@ +.query-create { + display: flex; + flex-direction: column; + + .row-name { + width: 100%; + + display: flex; + flex-direction: row; + justify-content: stretch; + + input { + flex-grow: 1; + flex-shrink: 1; + margin-left: 5px; + } + } + + .buttons { + margin-top: 5px; + text-align: right; + } +} + +.query-created { + display: flex; + flex-direction: column; + + .property-row { + width: 100%; + + display: flex; + flex-direction: row; + justify-content: stretch; + align-items: center; + + margin-top: 2px; + + input { + flex-grow: 1; + flex-shrink: 1; + margin-left: 5px; + } + + a:first-of-type { + width: 150px; + } + + div:last-of-type { + margin-left: 5px; + cursor: pointer; + } + } + + .buttons { + margin-top: 5px; + text-align: right; + } +} + +.query-management { + height: 100%; + display: flex; + flex-direction: column; + + .container { + display: flex; + flex-direction: column; + justify-content: stretch; + + .header, .footer { + flex-grow: 0; + flex-shrink: 0; + } + + .header { + display: flex; + flex-direction: row; + justify-content: stretch; + + .buttons { + flex-grow: 0; + } + + .search { + margin-left: 5px; + flex-grow: 1; + + input { + width: 100%; + } + } + } + + .query-list { + margin-top: 5px; + + display: flex; + flex-grow: 1; + flex-direction: column; + justify-content: stretch; + + .column { + &.column-username { + width: calc(50% - 75px) + } + + &.column-unique-id { + width: calc(50% - 75px) + } + + &.column-bound-server { + width: 150px; + flex-grow: 0; + } + } + + .query-list-header { + flex-grow: 0; + flex-shrink: 0; + display: flex; + flex-direction: row; + height: 20px; + + .column { + border: 1px solid lightgray; + text-align: center; + } + } + + .query-list-entries-container { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: start; + overflow-y: auto; + min-height: 250px; + + .entry { + display: flex; + flex-direction: row; + + .column { + margin-left: 2px; + } + + cursor: pointer; + + &.selected { + background-color: blue; + } + } + + &.scrollbar { + .column-username { + width: calc(50% - 75px + 30px) + } + + .column-unique-id { + width: calc(50% - 75px + 30px) + } + } + } + } + + .footer { + margin-top: 5px; + display: flex; + flex-direction: row; + justify-content: space-between; + } + } +} \ No newline at end of file diff --git a/shared/css/modals.scss b/shared/css/modals.scss index f69b52b5..4658a269 100644 --- a/shared/css/modals.scss +++ b/shared/css/modals.scss @@ -520,6 +520,7 @@ .group-list { border: lightgray solid 1px; padding: 3px; + overflow-y: auto; .group-entry { display: flex; diff --git a/shared/css/ts/client.scss b/shared/css/ts/client.scss index 21b82459..e05c6a6a 100644 --- a/shared/css/ts/client.scss +++ b/shared/css/ts/client.scss @@ -53,11 +53,21 @@ .channelTree div * {vertical-align: middle;display:inline-block;height: 16px;padding: 0px;} .channelTree div img {border: 0;} .channelTree div > span {position: absolute; right: 0;} -.channelTree .name { - vertical-align: middle; - margin-top: 1px; - height: 14px; - display: inline; +.channelTree { + .name, .group_prefix, .group_suffix, .away { + vertical-align: middle; + margin-top: 1px; + height: 14px; + display: inline; + } + + .group_prefix { + margin-right: 3px; + } + + .group_suffix { + margin-left: 3px; + } } .channelTree .own_name { font-weight: bold; diff --git a/shared/html/index.php b/shared/html/index.php index f30960aa..4a9ba89d 100644 --- a/shared/html/index.php +++ b/shared/html/index.php @@ -50,6 +50,7 @@ + diff --git a/shared/html/templates.html b/shared/html/templates.html index 15c92438..364ab5d7 100644 --- a/shared/html/templates.html +++ b/shared/html/templates.html @@ -19,7 +19,24 @@ - + +
@@ -62,6 +79,21 @@
+ + +
+
+
+
+
+
+
+ +
@@ -1074,7 +1106,7 @@
- +9 + + + + + + + + \ No newline at end of file diff --git a/shared/i18n/fr_google_translate.translation b/shared/i18n/fr_google_translate.translation new file mode 100644 index 00000000..10b86cbd --- /dev/null +++ b/shared/i18n/fr_google_translate.translation @@ -0,0 +1,5728 @@ +{ + "info": { + "name": "Auto translated messages for language fr", + "contributors": [ + { + "name": "Google Translate, via script by Markus Hadenfeldt", + "email": "gtr.i18n.client@teaspeak.de" + } + ] + }, + "translations": [ + { + "translated": "\u00c9chec d'initialisation du contr\u00f4leur audio!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to initialize audio controller!" + } + }, + { + "translated": "Semble initialis\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Sounds initialitzed" + } + }, + { + "translated": "[AudioController] Vous avez un tampon vide ou ind\u00e9fini! Le laisser tomber", + "flags": [ + "google-translate" + ], + "key": { + "message": "[AudioController] Got empty or undefined buffer! Dropping it" + } + }, + { + "translated": "[AudioController] Impossible de relire l'audio. Le contexte audio global n'est pas encore initialis\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "[AudioController] Failed to replay audio. Global audio context not initialized yet!" + } + }, + { + "translated": "[AudioController] Le taux d'\u00e9chantillonnage source n'est pas \u00e9gal au taux d'\u00e9chantillonnage de lecture! (% o |% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "[AudioController] Source sample rate isn't equal to playback sample rate! (%o | %o)" + } + }, + { + "translated": "[Audio] Commencer une nouvelle lecture", + "flags": [ + "google-translate" + ], + "key": { + "message": "[Audio] Starting new playback" + } + }, + { + "translated": "[Audio] Le pr\u00e9-enregistrement a r\u00e9ussi (Replaying maintenant)", + "flags": [ + "google-translate" + ], + "key": { + "message": "[Audio] Prebuffering succeeded (Replaying now)" + } + }, + { + "translated": "[Audio] La mise en m\u00e9moire tampon a r\u00e9ussi (rejou\u00e9e maintenant)", + "flags": [ + "google-translate" + ], + "key": { + "message": "[Audio] Buffering succeeded (Replaying now)" + } + }, + { + "translated": "Suppression de la m\u00e9moire tampon parce que la file d'attente en cours de lecture devient trop importante", + "flags": [ + "google-translate" + ], + "key": { + "message": "Dropping buffer because playing queue grows to much" + } + }, + { + "translated": "[Audio] D\u00e9tection d'un d\u00e9passement de tampon!", + "flags": [ + "google-translate" + ], + "key": { + "message": "[Audio] Detected a buffer underflow!" + } + }, + { + "translated": "[Audio] La mise en m\u00e9moire tampon a d\u00e9pass\u00e9 le d\u00e9lai d'attente. Flushing et arr\u00eater de rejouer", + "flags": [ + "google-translate" + ], + "key": { + "message": "[Audio] Buffering exceeded timeout. Flushing and stopping replay" + } + }, + { + "translated": "Json ne contient pas% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Json does not contains %s" + } + }, + { + "translated": "Le validateur donne false pour% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Validator results in false for %s" + } + }, + { + "translated": "Type d'objet non valide% s pour l'entr\u00e9e% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid object type %s for entry %s" + } + }, + { + "translated": "ann\u00e9es", + "flags": [ + "google-translate" + ], + "key": { + "message": "years" + } + }, + { + "translated": "journ\u00e9es", + "flags": [ + "google-translate" + ], + "key": { + "message": "days" + } + }, + { + "translated": "heures", + "flags": [ + "google-translate" + ], + "key": { + "message": "hours" + } + }, + { + "translated": "minutes", + "flags": [ + "google-translate" + ], + "key": { + "message": "minutes" + } + }, + { + "translated": "secondes", + "flags": [ + "google-translate" + ], + "key": { + "message": "seconds" + } + }, + { + "translated": "\u00e0 pr\u00e9sent", + "flags": [ + "google-translate" + ], + "key": { + "message": "now" + } + }, + { + "translated": "Vous avez un type ind\u00e9fini!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got undefined type!" + } + }, + { + "translated": "VAD a chang\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "VAD changed!" + } + }, + { + "translated": "La d\u00e9tection de la cl\u00e9 VAD a chang\u00e9.
Veuillez r\u00e9initialiser votre cl\u00e9 PPT!", + "flags": [ + "google-translate" + ], + "key": { + "message": "VAD key detection changed.
Please reset your PPT key!" + } + }, + { + "translated": "Gestionnaire VAD (d\u00e9tecteur d'activation vocale) non valide! (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid VAD (Voice activation detector) handler! (%o)" + } + }, + { + "translated": "[VoiceRecorder] Commencez l'enregistrement! (Appareil:% o | Groupe:% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "[VoiceRecorder] Start recording! (Device: %o | Group: %o)" + } + }, + { + "translated": "Impossible de r\u00e9soudre le microphone!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not resolve microphone!" + } + }, + { + "translated": "Impossible de r\u00e9soudre le microphone!
Message:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not resolve microphone!
Message: " + } + }, + { + "translated": "Impossible d'avoir un micro!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not get microphone!" + } + }, + { + "translated": "Arr\u00eate d'enregistrer!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Stop recording!" + } + }, + { + "translated": "[VoiceRecorder] Vous avez un flux de microphone, mais pas de contexte audio. En attendant son initialisation", + "flags": [ + "google-translate" + ], + "key": { + "message": "[VoiceRecorder] Got microphone stream, but havn't a audio context. Waiting until its initialized" + } + }, + { + "translated": "Rel\u00e2chez encore! (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Release again! (%o)" + } + }, + { + "translated": "D\u00e9sactivation de la prise en charge du codec pour", + "flags": [ + "google-translate" + ], + "key": { + "message": "Disabling codec support for " + } + }, + { + "translated": "Impossible de charger le pilote de codec", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not load codec driver" + } + }, + { + "translated": "Impossible de charger ou d'initialiser le codec", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not load or initialize codec " + } + }, + { + "translated": "codec non support\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "unsupported codec!" + } + }, + { + "translated": "Impossible d'initialiser le codec! Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not initialize codec!\nError: %o" + } + }, + { + "translated": "Impossible d'initialiser le codec!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not initialize codec!" + } + }, + { + "translated": "Speex Narrowband", + "flags": [ + "google-translate" + ], + "key": { + "message": "Speex Narrowband" + } + }, + { + "translated": "Speex large bande", + "flags": [ + "google-translate" + ], + "key": { + "message": "Speex Wideband" + } + }, + { + "translated": "Speex Ultra Wideband", + "flags": [ + "google-translate" + ], + "key": { + "message": "Speex Ultra Wideband" + } + }, + { + "translated": "CELT Mono", + "flags": [ + "google-translate" + ], + "key": { + "message": "CELT Mono" + } + }, + { + "translated": "Opus Voice", + "flags": [ + "google-translate" + ], + "key": { + "message": "Opus Voice" + } + }, + { + "translated": "Opus Music", + "flags": [ + "google-translate" + ], + "key": { + "message": "Opus Music" + } + }, + { + "translated": "Initialisation du gestionnaire de voix apr\u00e8s l\u2019initialisation de AudioController!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Initializing voice handler after AudioController has been initialized!" + } + }, + { + "translated": "Configuration du flux de voix natif!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Setting up native voice stream!" + } + }, + { + "translated": "Le codec natif n'est pas support\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Native codec isnt supported!" + } + }, + { + "translated": "Impossible de transf\u00e9rer l'audio (non connect\u00e9)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not transfer audio (not connected)" + } + }, + { + "translated": "Ajout de flux (% o)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Adding stream (%o)!" + } + }, + { + "translated": "Impossible de cr\u00e9er une offre de glace!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not create ice offer!" + } + }, + { + "translated": "D\u00e9finissez sdp \u00e0 distance! (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Set remote sdp! (%o)" + } + }, + { + "translated": "\u00c9chec de l'application de la description \u00e0 distance:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to apply remote description: %o" + } + }, + { + "translated": "\u00c9chec de l'ajout du candidat de glace en cache distant% s:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to add remote cached ice candidate %s: %o" + } + }, + { + "translated": "Ajouter de la glace \u00e0 distance! (% s |% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Add remote ice! (%s | %o)" + } + }, + { + "translated": "\u00c9chec de l'ajout du candidat glace distant% s:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to add remote ice candidate %s: %o" + } + }, + { + "translated": "Cache glace \u00e0 distance! (% s |% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Cache remote ice! (%s | %o)" + } + }, + { + "translated": "\u00c9chec de la configuration du pont vocal ({}). Autoriser la reconnexion: {}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to setup voice bridge ({}). Allow reconnect: {}" + } + }, + { + "translated": "\u00c9chec de la configuration du pont vocal (% s). Autoriser la reconnexion:% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to setup voice bridge (%s). Allow reconnect: %s" + } + }, + { + "translated": "Vous avez un candidat glace! Un \u00e9v\u00e9nement:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got ice candidate! Event:" + } + }, + { + "translated": "Offre cr\u00e9\u00e9e et accept\u00e9e", + "flags": [ + "google-translate" + ], + "key": { + "message": "Offer created and accepted" + } + }, + { + "translated": "\u00c9chec de l'application de la description locale:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to apply local description: %o" + } + }, + { + "translated": "Envoyer l'offre:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Send offer: %o" + } + }, + { + "translated": "Vous avez un nouveau canal de donn\u00e9es! (% s)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got new data channel! (%s)" + } + }, + { + "translated": "Avoir la voix d'un client inconnu? (ClientID:% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Having voice from unknown client? (ClientID: %o)" + } + }, + { + "translated": "Impossible de lire le codec% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not playback codec %o" + } + }, + { + "translated": "Impossible de lire l'audio (% o) du client (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not playback client's (%o) audio (%o)" + } + }, + { + "translated": "La voix locale s'est termin\u00e9e", + "flags": [ + "google-translate" + ], + "key": { + "message": "Local voice ended" + } + }, + { + "translated": "La voix locale a commenc\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Local voice started" + } + }, + { + "translated": "Passer au canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Switch to channel" + } + }, + { + "translated": "Editer le canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Edit channel" + } + }, + { + "translated": "Modification des propri\u00e9t\u00e9s du canal% s:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Changed channel properties of channel %s: %o" + } + }, + { + "translated": "Supprimer le canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Delete channel" + } + }, + { + "translated": "Cr\u00e9er de la musique bot", + "flags": [ + "google-translate" + ], + "key": { + "message": "Create music bot" + } + }, + { + "translated": "Bot cr\u00e9\u00e9 avec succ\u00e8s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Bot successfully created" + } + }, + { + "translated": "Mais a \u00e9t\u00e9 cr\u00e9\u00e9 avec succ\u00e8s.", + "flags": [ + "google-translate" + ], + "key": { + "message": "But has been successfully created." + } + }, + { + "translated": "Impossible de cr\u00e9er le bot", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to create bot" + } + }, + { + "translated": "Cr\u00e9er un sous-canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Create sub channel" + } + }, + { + "translated": "Cr\u00e9er un canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Create channel" + } + }, + { + "translated": "Options de canal: '% o'", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel options: '%o'" + } + }, + { + "translated": "Nom du canal obtenu:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got channel name: %o" + } + }, + { + "translated": "R\u00e9p\u00e9ter spacer a pris trop de r\u00e9p\u00e9titions!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Repeating spacer took too much repeats!" + } + }, + { + "translated": "Aligner:% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Align: %s" + } + }, + { + "translated": "Mettre \u00e0 jour les propri\u00e9t\u00e9s (% i) de% s (% i)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Update properties (%i) of %s (%i)" + } + }, + { + "translated": "Mise \u00e0 jour de la propri\u00e9t\u00e9% s = '% s' ->% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Updating property %s = '%s' -> %o" + } + }, + { + "translated": "Mot de passe de la cha\u00eene", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel password" + } + }, + { + "translated": "Mot de passe de la cha\u00eene:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel password:" + } + }, + { + "translated": "Changer le volume", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change volume" + } + }, + { + "translated": "R\u00e9initialiser", + "flags": [ + "google-translate" + ], + "key": { + "message": "Reset" + } + }, + { + "translated": "Annuler", + "flags": [ + "google-translate" + ], + "key": { + "message": "Cancel" + } + }, + { + "translated": "D'accord", + "flags": [ + "google-translate" + ], + "key": { + "message": "OK" + } + }, + { + "translated": "Groupes de serveurs", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server Groups" + } + }, + { + "translated": "Impossible de r\u00e9soudre le groupe cible!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not resolve target group!" + } + }, + { + "translated": "Fermer", + "flags": [ + "google-translate" + ], + "key": { + "message": "Close" + } + }, + { + "translated": "D\u00e9marrage du d\u00e9placement de la souris", + "flags": [ + "google-translate" + ], + "key": { + "message": "Starting mouse move" + } + }, + { + "translated": "D\u00e9finir le groupe de serveurs", + "flags": [ + "google-translate" + ], + "key": { + "message": "Set server group" + } + }, + { + "translated": "D\u00e9finir le groupe de canaux", + "flags": [ + "google-translate" + ], + "key": { + "message": "Set channel group" + } + }, + { + "translated": "Les permissions", + "flags": [ + "google-translate" + ], + "key": { + "message": "Permissions" + } + }, + { + "translated": "Ouvrir le chat textuel", + "flags": [ + "google-translate" + ], + "key": { + "message": "Open text chat" + } + }, + { + "translated": "Poke client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Poke client" + } + }, + { + "translated": "Poke message:
", + "flags": [ + "google-translate" + ], + "key": { + "message": "Poke message:
" + } + }, + { + "translated": "Changer la description", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change description" + } + }, + { + "translated": "Changer la description du client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change client description" + } + }, + { + "translated": "Nouvelle description:
", + "flags": [ + "google-translate" + ], + "key": { + "message": "New description:
" + } + }, + { + "translated": "D\u00e9placer le client vers votre canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Move client to your channel" + } + }, + { + "translated": "Kick client du canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kick client from channel" + } + }, + { + "translated": "Raison du coup de pied:
", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kick reason:
" + } + }, + { + "translated": "Kick client du serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kick client fom server" + } + }, + { + "translated": "Kick client du serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kick client from server" + } + }, + { + "translated": "Ban client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Ban client" + } + }, + { + "translated": "Changer le volume", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change Volume" + } + }, + { + "translated": "Changer de nom", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change name" + } + }, + { + "translated": "Changer sa propre description", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change own description" + } + }, + { + "translated": "Changer sa propre description en% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Changing own description to %s" + } + }, + { + "translated": "Pseudo chang\u00e9 avec succ\u00e8s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Nickname successfully changed" + } + }, + { + "translated": "Impossible de changer le pseudo ({})", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not change nickname ({})" + } + }, + { + "translated": "Changer le nom du bot", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change bot name" + } + }, + { + "translated": "Changer le surnom de Music Bots", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change music bots nickname" + } + }, + { + "translated": "Nouveau pseudo:
", + "flags": [ + "google-translate" + ], + "key": { + "message": "New nickname:
" + } + }, + { + "translated": "Changer la description du bot", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change bot description" + } + }, + { + "translated": "Changer la description des robots musicaux", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change music bots description" + } + }, + { + "translated": "Panneau de musique ouvert", + "flags": [ + "google-translate" + ], + "key": { + "message": "Open music panel" + } + }, + { + "translated": "R\u00e9p\u00e9tition rapide de l'URL", + "flags": [ + "google-translate" + ], + "key": { + "message": "Quick url replay" + } + }, + { + "translated": "S'il vous pla\u00eet entrer l'URL", + "flags": [ + "google-translate" + ], + "key": { + "message": "Please enter the URL" + } + }, + { + "translated": "URL:", + "flags": [ + "google-translate" + ], + "key": { + "message": "URL:" + } + }, + { + "translated": "Impossible de rejouer l'URL", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to replay url" + } + }, + { + "translated": "Renvoyer le client% o du canal avec la raison% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kicking client %o from channel with reason %o" + } + }, + { + "translated": "Supprimer bot", + "flags": [ + "google-translate" + ], + "key": { + "message": "Delete bot" + } + }, + { + "translated": "Voulez-vous vraiment supprimer {0}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Do you really want to delete {0}" + } + }, + { + "translated": "\u00cates-vous s\u00fbr?", + "flags": [ + "google-translate" + ], + "key": { + "message": "Are you sure?" + } + }, + { + "translated": "D'accord", + "flags": [ + "google-translate" + ], + "key": { + "message": "Ok" + } + }, + { + "translated": "\u00c9chec de r\u00e9solution de l'autorisation de canal pour le nom% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to resolve channel permission for name %o" + } + }, + { + "translated": "Autorisations mises \u00e0 jour% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Updated permissions %o" + } + }, + { + "translated": "Autorisations obtenues:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got permissions: %o" + } + }, + { + "translated": "Permission d\u00e9clench\u00e9e! % o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Permission triggered! %o" + } + }, + { + "translated": "Changement de cha\u00eene invalide (parents diff\u00e9rents! (% O |% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid channel move (different parents! (%o|%o)" + } + }, + { + "translated": "Canal multis\u00e9lection", + "flags": [ + "google-translate" + ], + "key": { + "message": "Multiselect channel" + } + }, + { + "translated": "Client multis\u00e9lection", + "flags": [ + "google-translate" + ], + "key": { + "message": "Multiselect client" + } + }, + { + "translated": "Musique seulement:% o | Musique de conteneur:% o | Conteneur local:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Music only: %o | Container music: %o | Container local: %o" + } + }, + { + "translated": "Poke clients", + "flags": [ + "google-translate" + ], + "key": { + "message": "Poke clients" + } + }, + { + "translated": "D\u00e9placer les clients vers votre canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Move clients to your channel" + } + }, + { + "translated": "Kick clients de la cha\u00eene", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kick clients from channel" + } + }, + { + "translated": "Kick clients du serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kick clients fom server" + } + }, + { + "translated": "Kick clients du serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Kick clients from server" + } + }, + { + "translated": "Bannir les clients", + "flags": [ + "google-translate" + ], + "key": { + "message": "Ban clients" + } + }, + { + "translated": "Supprimer les robots", + "flags": [ + "google-translate" + ], + "key": { + "message": "Delete bots" + } + }, + { + "translated": "Voulez-vous vraiment supprimer", + "flags": [ + "google-translate" + ], + "key": { + "message": "Do you really want to delete " + } + }, + { + "translated": "Cr\u00e9er un nouveau canal. Propri\u00e9t\u00e9s:% o Autorisations:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Creating a new channel.\nProperties: %o\nPermissions: %o" + } + }, + { + "translated": "Impossible de r\u00e9soudre le canal apr\u00e8s la cr\u00e9ation. Impossible d'appliquer les autorisations!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to resolve channel after creation. Could not apply permissions!" + } + }, + { + "translated": "Cha\u00eene {} cr\u00e9\u00e9e avec succ\u00e8s!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel {} successfully created!" + } + }, + { + "translated": "son de lecture% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "playback sound %o" + } + }, + { + "translated": "Son manquant% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing sound %o" + } + }, + { + "translated": "Utilisation du tampon en cache:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Using cached buffer: %o" + } + }, + { + "translated": "D\u00e9codage des donn\u00e9es", + "flags": [ + "google-translate" + ], + "key": { + "message": "Decoding data" + } + }, + { + "translated": "Donn\u00e9es d\u00e9cod\u00e9es", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got decoded data" + } + }, + { + "translated": "Impossible de d\u00e9coder les donn\u00e9es audio pour% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to decode audio data for %o" + } + }, + { + "translated": "Impossible de charger le fichier audio. (Code de r\u00e9ponse% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load audio file. (Response code %o)" + } + }, + { + "translated": "\u00c9chec du chargement du fichier audio", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load audio file " + } + }, + { + "translated": "Rejouer% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Replaying %s" + } + }, + { + "translated": "Votre navigateur ne supporte pas decodeAudioData! Utiliser un n\u0153ud pour la lecture! Cela contourne la sortie audio et la r\u00e9gulation du volume!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Your browser does not support decodeAudioData! Using a node to playback! This bypasses the audio output and volume regulation!" + } + }, + { + "translated": "Vous avez \u00e9t\u00e9 piqu\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "You have been poked!" + } + }, + { + "translated": "Prise connect\u00e9e", + "flags": [ + "google-translate" + ], + "key": { + "message": "Socket connected" + } + }, + { + "translated": "Se connecter...", + "flags": [ + "google-translate" + ], + "key": { + "message": "Logging in..." + } + }, + { + "translated": "Connexion \u00e0 {0}: {1}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connecting to {0}:{1}" + } + }, + { + "translated": "Connect timeout d\u00e9clench\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connect timeout triggered!" + } + }, + { + "translated": "Vous avez une erreur: (% s)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got error: (%s)" + } + }, + { + "translated": "demande de d\u00e9connexion", + "flags": [ + "google-translate" + ], + "key": { + "message": "request disconnect" + } + }, + { + "translated": "Connexion ferm\u00e9e", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connection closed" + } + }, + { + "translated": "Impossible d'analyser le message json!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not parse message json!" + } + }, + { + "translated": "Type de donn\u00e9es manquant!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing data type!" + } + }, + { + "translated": "Type de commande inconnu% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown command type %o" + } + }, + { + "translated": "Gestion de la commande '% s'", + "flags": [ + "google-translate" + ], + "key": { + "message": "Handling command '%s'" + } + }, + { + "translated": "Json:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Json:" + } + }, + { + "translated": "Commande manquante '% s'", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing command '%s'" + } + }, + { + "translated": "Autorisations client insuffisantes. \u00c9chec sur permission", + "flags": [ + "google-translate" + ], + "key": { + "message": "Insufficient client permissions. Failed on permission " + } + }, + { + "translated": "Autorisations client insuffisantes. Echec sur permission {}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Insufficient client permissions. Failed on permission {}" + } + }, + { + "translated": "L'ex\u00e9cution de la commande entra\u00eene", + "flags": [ + "google-translate" + ], + "key": { + "message": "Command execution results in " + } + }, + { + "translated": "Type de r\u00e9sultat de promesse non valide:% o. R\u00e9sultat:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid promise result type: %o. Result:" + } + }, + { + "translated": "Vous avez une erreur de connexion", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got login error" + } + }, + { + "translated": "Impossible d'obtenir la version:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to get version:" + } + }, + { + "translated": "Code de retour invalide! (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid return code! (%o)" + } + }, + { + "translated": "Mise en place de la voix", + "flags": [ + "google-translate" + ], + "key": { + "message": "Setting up voice" + } + }, + { + "translated": "Connect\u00e9 en tant que {0}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connected as {0}" + } + }, + { + "translated": "Identifiant d'ordre de cha\u00eene invalide!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid channel order id!" + } + }, + { + "translated": "Canal parent invalide", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid channel parent" + } + }, + { + "translated": "Vous avez% d nouvelles cha\u00eenes", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got %d new channels" + } + }, + { + "translated": "Suppression de% d canaux", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got %d channel deletions" + } + }, + { + "translated": "Canal invalide onDelete (canal inconnu)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid channel onDelete (Unknown channel)" + } + }, + { + "translated": "Got% d canal cache", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got %d channel hides" + } + }, + { + "translated": "Canal non valide sur masqu\u00e9 (canal inconnu)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid channel on hide (Unknown channel)" + } + }, + { + "translated": "{0} est apparu du {1} au {2}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} appeared from {1} to {2}" + } + }, + { + "translated": "{0} connect\u00e9 au canal {1}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} connected to channel {1}" + } + }, + { + "translated": "{0} est apparu de {1} \u00e0 {2}, d\u00e9plac\u00e9 de {3}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} appeared from {1} to {2}, moved by {3}" + } + }, + { + "translated": "{0} est apparu du {1} au {2}, suivi du {3} {4}.", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} appeared from {1} to {2}, kicked by {3}{4}" + } + }, + { + "translated": "Motivation inconnue pour% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown reasonid for %o" + } + }, + { + "translated": "Client inconnu laiss\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown client left!" + } + }, + { + "translated": "{0} a disparu de {1} \u00e0 {2}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} disappeared from {1} to {2}" + } + }, + { + "translated": "{0} a quitt\u00e9 le serveur {1}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} left the server{1}" + } + }, + { + "translated": "{0} a \u00e9t\u00e9 expuls\u00e9 du serveur par {1}. {2}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} was kicked from the server by {1}.{2}" + } + }, + { + "translated": "{0} a \u00e9t\u00e9 exclu de votre cha\u00eene par {1}. {2}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} was kicked from your channel by {1}.{2}" + } + }, + { + "translated": "{0} a \u00e9t\u00e9 banni {1} par {2}. {3}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} was banned {1} by {2}.{3}" + } + }, + { + "translated": "Un client inconnu a laiss\u00e9 la raison!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown client left reason!" + } + }, + { + "translated": "D\u00e9placement client inconnu (Client)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown client move (Client)!" + } + }, + { + "translated": "D\u00e9placement client inconnu (canal vers)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown client move (Channel to)!" + } + }, + { + "translated": "D\u00e9placement client inconnu (Cha\u00eene de)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown client move (Channel from)!" + } + }, + { + "translated": "Vous avez \u00e9t\u00e9 d\u00e9plac\u00e9 par {3} du canal {1} vers {2}.", + "flags": [ + "google-translate" + ], + "key": { + "message": "You was moved by {3} from channel {1} to {2}" + } + }, + { + "translated": "{0} a \u00e9t\u00e9 d\u00e9plac\u00e9 du canal {1} vers {2} par {3}.", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} was moved from channel {1} to {2} by {3}" + } + }, + { + "translated": "Vous \u00eates pass\u00e9 du canal {1} au {2}", + "flags": [ + "google-translate" + ], + "key": { + "message": "You switched from channel {1} to {2}" + } + }, + { + "translated": "{0} a chang\u00e9 de canal {1} \u00e0 {2}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} switched from channel {1} to {2}" + } + }, + { + "translated": "Vous avez \u00e9t\u00e9 expuls\u00e9 du canal {1} vers le canal {2} par {3} {4}.", + "flags": [ + "google-translate" + ], + "key": { + "message": "You got kicked out of the channel {1} to channel {2} by {3}{4}" + } + }, + { + "translated": "{0} a \u00e9t\u00e9 vir\u00e9 du canal {1} \u00e0 {2} par {3} {4}", + "flags": [ + "google-translate" + ], + "key": { + "message": "{0} got kicked from channel {1} to {2} by {3}{4}" + } + }, + { + "translated": "Motif inconnu id% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown reason id %o" + } + }, + { + "translated": "Canal inconnu d\u00e9placer (canal)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown channel move (Channel)!" + } + }, + { + "translated": "Changement de canal inconnu (prev)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown channel move (prev)!" + } + }, + { + "translated": "D\u00e9placement de canal inconnu (parent)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown channel move (parent)!" + } + }, + { + "translated": "Canal inconnu modifier (canal)!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unknown channel edit (Channel)!" + } + }, + { + "translated": "Vous avez un message priv\u00e9 de client invalide!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got private message from invalid client!" + } + }, + { + "translated": "J'ai essay\u00e9 de mettre \u00e0 jour un client non existant", + "flags": [ + "google-translate" + ], + "key": { + "message": "Tried to update an non existing client" + } + }, + { + "translated": "Vous avez des informations sur le lecteur de musique pour un bot inconnu ou invalide! (ID:% i, entr\u00e9e:% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)" + } + }, + { + "translated": "Utilisation du gestionnaire d'informations:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Using info manager: %o" + } + }, + { + "translated": "Impossible de charger hostbanner:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load hostbanner: %o" + } + }, + { + "translated": "Hostbanner a \u00e9t\u00e9 charg\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Hostbanner has been loaded" + } + }, + { + "translated": "chargement...", + "flags": [ + "google-translate" + ], + "key": { + "message": "loading..." + } + }, + { + "translated": "Pas de titre ou d'URL", + "flags": [ + "google-translate" + ], + "key": { + "message": "No title or url" + } + }, + { + "translated": "Impossible d'ex\u00e9cuter le jeu", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute play" + } + }, + { + "translated": "Impossible d'ex\u00e9cuter le jeu.
{}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute play.
{}" + } + }, + { + "translated": "Impossible d'ex\u00e9cuter la pause", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute pause" + } + }, + { + "translated": "Impossible d'ex\u00e9cuter la pause.
{}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute pause.
{}" + } + }, + { + "translated": "\u00c9chec d'ex\u00e9cution", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute stop" + } + }, + { + "translated": "\u00c9chec d'ex\u00e9cution.
{}", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute stop.
{}" + } + }, + { + "translated": "\u00c9chec d'ex\u00e9cution en avant", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute forward" + } + }, + { + "translated": "\u00c9chec de l'ex\u00e9cution du rembobinage", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to execute rewind" + } + }, + { + "translated": "Pas mis en \u0153uvre", + "flags": [ + "google-translate" + ], + "key": { + "message": "Not implemented" + } + }, + { + "translated": "Cette fonction n'est pas encore impl\u00e9ment\u00e9e!", + "flags": [ + "google-translate" + ], + "key": { + "message": "This function is not implemented yet!" + } + }, + { + "translated": "Parents:% o | % o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Parents: %o | %o" + } + }, + { + "translated": "Joueur:% o | oo", + "flags": [ + "google-translate" + ], + "key": { + "message": "Player: %o | %o" + } + }, + { + "translated": "\u00c9chelle:% f => traduire:% o | % o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Scale: %f => translate: %o | %o" + } + }, + { + "translated": "Test requis Requis:% o | % i | % o =>% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Test needed required: %o | %i | %o => %o" + } + }, + { + "translated": "Vous avez un identifiant de permission inconnu (% o /% o (% o))!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got unknown permission id (%o/%o (%o))!" + } + }, + { + "translated": "Cartographie des autorisations", + "flags": [ + "google-translate" + ], + "key": { + "message": "Permission mapping" + } + }, + { + "translated": "Groupe", + "flags": [ + "google-translate" + ], + "key": { + "message": "Group " + } + }, + { + "translated": "% i <>% s ->% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "%i <> %s -> %s" + } + }, + { + "translated": "Obtenu% i autorisations", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got %i permissions" + } + }, + { + "translated": "Vous avez les permissions n\u00e9cessaires mais vous n'avez pas de liste de permissions!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got needed permissions but don't have a permission list!" + } + }, + { + "translated": "Vous avez% d les autorisations n\u00e9cessaires.", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got %d needed permissions." + } + }, + { + "translated": "Impossible de r\u00e9soudre la perm pour id% s (% o |% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not resolve perm for id %s (%o|%o)" + } + }, + { + "translated": "A obtenu des autorisations de canal pour le canal% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got channel permissions for channel %o" + } + }, + { + "translated": "Identificateur d'autorisation de canal manquant pour l'ID de canal demand\u00e9% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing channel permission handle for requested channel id %o" + } + }, + { + "translated": "Impossible de r\u00e9soudre l'autorisation d'accorder% o. Cr\u00e9er un nouveau.", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not resolve grant permission %o. Creating a new one." + } + }, + { + "translated": "Requiert l'autorisation requise avec une cl\u00e9 invalide! (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Requested needed permission with invalid key! (%o)" + } + }, + { + "translated": "ordre invalide!", + "flags": [ + "google-translate" + ], + "key": { + "message": "invalid order!" + } + }, + { + "translated": "Impossible de r\u00e9soudre la cible du groupe! =>% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not resolve group target! => %o" + } + }, + { + "translated": "A obtenu les autorisations de groupe pour le groupe% o /% o, mais ce n'est pas un groupe enregistr\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got group permissions for group %o/%o, but its not a registered group!" + } + }, + { + "translated": "A obtenu les autorisations de groupe pour le groupe% o /% o, mais cela n'a jamais \u00e9t\u00e9 demand\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got group permissions for group %o/%o, but it was never requested!" + } + }, + { + "translated": "\u00c9chec du chargement du fichier de traduction% s. \u00c9chec d'analyse ou de traitement de JSON:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load translation file %s. Failed to parse or process json: %o" + } + }, + { + "translated": "\u00c9chec du traitement ou de l'analyse de Json!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to process or parse json!" + } + }, + { + "translated": "\u00c9chec du chargement du fichier:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load file: " + } + }, + { + "translated": "Initialisation r\u00e9ussie du fichier de traduction \u00e0 partir de% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Successfully initialized up translation file from %s" + } + }, + { + "translated": "Impossible de charger le fichier de traduction \u00e0 partir de "% s". Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load translation file from \"%s\". Error: %o" + } + }, + { + "translated": "Le r\u00e9f\u00e9rentiel par d\u00e9faut ajout\u00e9 avec succ\u00e8s \u00e0 partir de "% s".", + "flags": [ + "google-translate" + ], + "key": { + "message": "Successfully added default repository from \"%s\"." + } + }, + { + "translated": "\u00c9chec de l'ajout du r\u00e9f\u00e9rentiel par d\u00e9faut. Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to add default repository. Error: %o" + } + }, + { + "translated": "\u00c9chec du chargement du fichier de traduction pour le r\u00e9f\u00e9rentiel% s. Traduction:% s (% s) Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load translation file for repository %s. Translation: %s (%s) Error: %o" + } + }, + { + "translated": "\u00c9chec de chargement du r\u00e9f\u00e9rentiel lors de l'it\u00e9ration:% s (% s). Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load repository while iteration: %s (%s). Error: %o" + } + }, + { + "translated": "Syst\u00e8me de traduction", + "flags": [ + "google-translate" + ], + "key": { + "message": "Translation System" + } + }, + { + "translated": "\u00c9chec du chargement du fichier de traduction s\u00e9lectionn\u00e9 actuel.", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load current selected translation file." + } + }, + { + "translated": "Utiliser des traductions de secours par d\u00e9faut.", + "flags": [ + "google-translate" + ], + "key": { + "message": "Using default fallback translations." + } + }, + { + "translated": "Impossible de d\u00e9fier l'onglet customElements!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not defied tab customElements!" + } + }, + { + "translated": "R\u00e9glages", + "flags": [ + "google-translate" + ], + "key": { + "message": "Settings" + } + }, + { + "translated": "Tapez la cl\u00e9 que vous souhaitez", + "flags": [ + "google-translate" + ], + "key": { + "message": "Type the key you wish" + } + }, + { + "translated": "Vous avez la cl\u00e9% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got key %o" + } + }, + { + "translated": "Aucun dispositif", + "flags": [ + "google-translate" + ], + "key": { + "message": "No device" + } + }, + { + "translated": "P\u00e9riph\u00e9rique obtenu% s (% s):% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got device %s (%s): %s" + } + }, + { + "translated": "Impossible d'\u00e9num\u00e9rer les p\u00e9riph\u00e9riques!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not enumerate over devices!" + } + }, + { + "translated": "Impossible d'obtenir la liste des p\u00e9riph\u00e9riques!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not get device list!" + } + }, + { + "translated": "Microphone s\u00e9lectionn\u00e9: id:% o groupe:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Selected microphone device: id: %o group: %o" + } + }, + { + "translated": "Haut-parleur s\u00e9lectionn\u00e9: id:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Selected speaker device: id: %o" + } + }, + { + "translated": "Impossible de changer de p\u00e9riph\u00e9rique!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to change device!" + } + }, + { + "translated": "Informations de d\u00e9p\u00f4t", + "flags": [ + "google-translate" + ], + "key": { + "message": "Repository info" + } + }, + { + "translated": "Voulez-vous vraiment supprimer ce r\u00e9f\u00e9rentiel?", + "flags": [ + "google-translate" + ], + "key": { + "message": "Do you really want to delete this repository?" + } + }, + { + "translated": "Informations de traduction", + "flags": [ + "google-translate" + ], + "key": { + "message": "Translation info" + } + }, + { + "translated": "Entrez l'URL du r\u00e9f\u00e9rentiel:
", + "flags": [ + "google-translate" + ], + "key": { + "message": "Enter repository URL:
" + } + }, + { + "translated": "\u00c9chec d'interrogation du r\u00e9f\u00e9rentiel.
Assurez-vous que ce r\u00e9f\u00e9rentiel est valide et accessible.
Erreur:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to query repository.
Ensure that this repository is valid and reachable.
Error: " + } + }, + { + "translated": "Ajout de l'interdiction% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Adding ban %o" + } + }, + { + "translated": "Impossible d'ajouter l'interdiction", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to add ban" + } + }, + { + "translated": "Modification de l'interdiction% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Editing ban %o" + } + }, + { + "translated": "Appliquer les modifications de modification% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Apply edit changes %o" + } + }, + { + "translated": "Impossible d'\u00e9diter l'interdiction", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to edit ban" + } + }, + { + "translated": "Suppression de l'interdiction% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Deleting ban %o" + } + }, + { + "translated": "Impossible de supprimer l'interdiction", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to delete ban" + } + }, + { + "translated": "Vous avez banni:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got banlist: %o" + } + }, + { + "translated": "Entr\u00e9e d'interdiction manquante avec l'id% d", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing ban entry with id %d" + } + }, + { + "translated": "banlist", + "flags": [ + "google-translate" + ], + "key": { + "message": "Banlist" + } + }, + { + "translated": "Recherche de filtre% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Search for filter %s" + } + }, + { + "translated": "Message d'absence", + "flags": [ + "google-translate" + ], + "key": { + "message": "Set away message" + } + }, + { + "translated": "S'il vous pla\u00eet entrez le message d'absence", + "flags": [ + "google-translate" + ], + "key": { + "message": "Please enter the away message" + } + }, + { + "translated": "Codec de canal non pris en charge", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel codec unsupported" + } + }, + { + "translated": "Ce canal a un codec non support\u00e9.
Vous ne pouvez ni parler ni \u00e9couter personne de ce canal!", + "flags": [ + "google-translate" + ], + "key": { + "message": "This channel has an unsupported codec.
You cant speak or listen to anybody within this channel!" + } + }, + { + "translated": "Utiliser un jeton", + "flags": [ + "google-translate" + ], + "key": { + "message": "Use token" + } + }, + { + "translated": "Veuillez entrer votre cl\u00e9 de jeton / privil\u00e8ge", + "flags": [ + "google-translate" + ], + "key": { + "message": "Please enter your token/priviledge key" + } + }, + { + "translated": "Toke utilis\u00e9 avec succ\u00e8s!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Toke successfully used!" + } + }, + { + "translated": "La liste de jetons n'est pas encore impl\u00e9ment\u00e9e!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Token list is not implemented yet!" + } + }, + { + "translated": "D\u00e9marrer la connexion \u00e0% s:% d", + "flags": [ + "google-translate" + ], + "key": { + "message": "Start connection to %s:%d" + } + }, + { + "translated": "Erreur lors du hachage du mot de passe", + "flags": [ + "google-translate" + ], + "key": { + "message": "Error while hashing password" + } + }, + { + "translated": "Impossible de hacher le mot de passe du serveur!
", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to hash server password!
" + } + }, + { + "translated": "Le type d'encodage de codec n'est pas support\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Codec encode type not supported!" + } + }, + { + "translated": "Impossible de se connecter \u00e0 l'h\u00f4te distant! Exception", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not connect to remote host! Exception" + } + }, + { + "translated": "N'a pas pu se connecter", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not connect" + } + }, + { + "translated": "Impossible de se connecter \u00e0 l'h\u00f4te distant (connexion refus\u00e9e)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not connect to remote host (Connection refused)" + } + }, + { + "translated": "Connexion perdue sur le serveur distant!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Lost connection to remote server!" + } + }, + { + "translated": "La connexion a \u00e9t\u00e9 ferm\u00e9e par l'h\u00f4te distant", + "flags": [ + "google-translate" + ], + "key": { + "message": "The connection was closed by remote host" + } + }, + { + "translated": "D\u00e9lai de connexion au ping", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connection ping timeout" + } + }, + { + "translated": "Connexion perdue", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connection lost" + } + }, + { + "translated": "Connexion perdue avec l'h\u00f4te distant (d\u00e9lai d'attente Ping)
M\u00eame possible?", + "flags": [ + "google-translate" + ], + "key": { + "message": "Lost connection to remote host (Ping timeout)
Even possible?" + } + }, + { + "translated": "Serveur ferm\u00e9 ({0})", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server closed ({0})" + } + }, + { + "translated": "Serveur ferm\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server closed" + } + }, + { + "translated": "Le serveur n\u00e9cessite un mot de passe", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server requires password" + } + }, + { + "translated": "Mot de passe du serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server password" + } + }, + { + "translated": "Entrez le mot de passe du serveur:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Enter server password:" + } + }, + { + "translated": "Vous avez \u00e9t\u00e9 expuls\u00e9 du serveur par {0} {1}", + "flags": [ + "google-translate" + ], + "key": { + "message": "You got kicked from the server by {0}{1}" + } + }, + { + "translated": "Vous avez \u00e9t\u00e9 banni du serveur par {0} {1}", + "flags": [ + "google-translate" + ], + "key": { + "message": "You got banned from the server by {0}{1}" + } + }, + { + "translated": "Vous avez d\u00e9branch\u00e9 d\u00e9connect\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got uncaught disconnect!" + } + }, + { + "translated": "Type:% o Donn\u00e9es:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Type: %o Data:" + } + }, + { + "translated": "Cr\u00e9er un nouveau fichier t\u00e9l\u00e9charg\u00e9 dans% s:% s (cl\u00e9:% s,% d octets attendus)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Create new file download to %s:%s (Key: %s, Expect %d bytes)" + } + }, + { + "translated": "Vous avez des donn\u00e9es, mais le socket est ferm\u00e9?", + "flags": [ + "google-translate" + ], + "key": { + "message": "Got data, but socket closed?" + } + }, + { + "translated": "une erreur survient", + "flags": [ + "google-translate" + ], + "key": { + "message": "an error occurent" + } + }, + { + "translated": "fermeture inattendue (ferm\u00e9e \u00e0 distance)", + "flags": [ + "google-translate" + ], + "key": { + "message": "unexpected close (remote closed)" + } + }, + { + "translated": "Entr\u00e9e de liste de fichiers non valide. Chemin:% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid file list entry. Path: %s" + } + }, + { + "translated": "Fin de la saisie de la liste de fichiers non valide. Chemin:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid file list entry finish. Path: " + } + }, + { + "translated": "Impossible de t\u00e9l\u00e9charger l'ic\u00f4ne% s ->% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not download icon %s -> %s" + } + }, + { + "translated": "\u00c9chec du t\u00e9l\u00e9chargement de l'ic\u00f4ne {0}. ({1})", + "flags": [ + "google-translate" + ], + "key": { + "message": "Fail to download icon {0}. ({1})" + } + }, + { + "translated": "Erreur lors du t\u00e9l\u00e9chargement de l'ic\u00f4ne! (% s)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Error while downloading icon! (%s)" + } + }, + { + "translated": "\u00c9chec de la demande de t\u00e9l\u00e9chargement pour l'ic\u00f4ne {0}. ({1})", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to request download for icon {0}. ({1})" + } + }, + { + "translated": "L'ic\u00f4ne a un type d'image de% o (m\u00e9dia:% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Icon has an image type of %o (media: %o)" + } + }, + { + "translated": "Ic\u00f4ne% o charg\u00e9 :)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Icon %o loaded :)" + } + }, + { + "translated": "Impossible de charger l'ic\u00f4ne% o. Raison:% p", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not load icon %o. Reason: %p" + } + }, + { + "translated": "T\u00e9l\u00e9chargement de l'avatar% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Downloading avatar %s" + } + }, + { + "translated": "Impossible de t\u00e9l\u00e9charger l'avatar% o ->% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not download avatar %o -> %s" + } + }, + { + "translated": "\u00c9chec du t\u00e9l\u00e9chargement de l'avatar pour {0}. ({1})", + "flags": [ + "google-translate" + ], + "key": { + "message": "Fail to download avatar for {0}. ({1})" + } + }, + { + "translated": "Erreur lors du t\u00e9l\u00e9chargement de l'avatar! (% s)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Error while downloading avatar! (%s)" + } + }, + { + "translated": "\u00c9chec de la demande de t\u00e9l\u00e9chargement d'avatar pour {0}. ({1})", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to request avatar download for {0}. ({1})" + } + }, + { + "translated": "avatar a un type d'image de% o (m\u00e9dia:% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "avatar has an image type of %o (media: %o)" + } + }, + { + "translated": "Avatar a un type d'image de% o (m\u00e9dia:% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Avatar has an image type of %o (media: %o)" + } + }, + { + "translated": "Impossible de charger l'avatar pour% s. Les raisons", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not load avatar for %s. Reason: %s" + } + }, + { + "translated": "Impossible de charger l'avatar", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not load avatar " + } + }, + { + "translated": "Manquant requis fonction wasm!
Veuillez recharger la page!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing required wasm function!
Please reload the page!" + } + }, + { + "translated": "D\u00e9calage", + "flags": [ + "google-translate" + ], + "key": { + "message": "Shift" + } + }, + { + "translated": "tout", + "flags": [ + "google-translate" + ], + "key": { + "message": "Alt" + } + }, + { + "translated": "CTRL", + "flags": [ + "google-translate" + ], + "key": { + "message": "CTRL" + } + }, + { + "translated": "Gagner", + "flags": [ + "google-translate" + ], + "key": { + "message": "Win" + } + }, + { + "translated": "non fix\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "unset" + } + }, + { + "translated": "Le message au format contient un index invalide (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Message to format contains invalid index (%o)" + } + }, + { + "translated": "D\u00e9calage:% d Nombre:% d", + "flags": [ + "google-translate" + ], + "key": { + "message": "Offset: %d Number: %d" + } + }, + { + "translated": "Clair", + "flags": [ + "google-translate" + ], + "key": { + "message": "Clear" + } + }, + { + "translated": "Fermer tous les onglets priv\u00e9s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Close all private tabs" + } + }, + { + "translated": "Changer de nom!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Change name!" + } + }, + { + "translated": "Set fermable:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Set closeable: " + } + }, + { + "translated": "Impossible d'envoyer un message de chant (Non connect\u00e9)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not send chant message (Not connected)" + } + }, + { + "translated": "Chat sur le serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server chat" + } + }, + { + "translated": "Canal chat", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel chat" + } + }, + { + "translated": "Cr\u00e9er une nouvelle connexion", + "flags": [ + "google-translate" + ], + "key": { + "message": "Create a new connection" + } + }, + { + "translated": "Impossible de lire l'identit\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not read identity! " + } + }, + { + "translated": "Impossible de lire le fichier d'identit\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not read identity file!" + } + }, + { + "translated": "Veuillez s\u00e9lectionner une identit\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Please select an identity!" + } + }, + { + "translated": "Mise \u00e0 jour via affich\u00e9e", + "flags": [ + "google-translate" + ], + "key": { + "message": "Updating via shown" + } + }, + { + "translated": "Relier", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connect" + } + }, + { + "translated": "Modifier dans", + "flags": [ + "google-translate" + ], + "key": { + "message": "Edit ban" + } + }, + { + "translated": "Ajouter l'interdiction", + "flags": [ + "google-translate" + ], + "key": { + "message": "Add ban" + } + }, + { + "translated": "Oui", + "flags": [ + "google-translate" + ], + "key": { + "message": "Yes" + } + }, + { + "translated": "Non", + "flags": [ + "google-translate" + ], + "key": { + "message": "No" + } + }, + { + "translated": "Impossible d'initialiser le syst\u00e8me de traduction! Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to initialized the translation system!\nError: %o" + } + }, + { + "translated": "Impossible d'installer jsrender", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to setup jsrender" + } + }, + { + "translated": "Impossible de charger jsrender! % o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to load jsrender! %o" + } + }, + { + "translated": "Impossible d\u2019installer la page principale!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to setup main page!" + } + }, + { + "translated": "Impossible de configurer l'analyseur d'identit\u00e9 TeamSpeak!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not setup the TeamSpeak identity parser!" + } + }, + { + "translated": "\u00c9chec d'initialisation de ppt! Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to initialize ppt!\nError: %o" + } + }, + { + "translated": "\u00c9chec d'initialisation de ppt!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Failed to initialize ppt!" + } + }, + { + "translated": "Initialiser le contr\u00f4leur audio plus tard!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Initialize audio controller later!" + } + }, + { + "translated": "Audio.player.initializeFromGesture manquant", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing audio.player.initializeFromGesture" + } + }, + { + "translated": "Impossible de r\u00e9\u00e9chantillonner les donn\u00e9es PCM pour le codec. Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not resample PCM data for codec. Error: %o" + } + }, + { + "translated": "Impossible de coder les donn\u00e9es PCM pour le codec. Erreur:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Could not encode PCM data for codec. Error: %o" + } + }, + { + "translated": "Temps requis:% d", + "flags": [ + "google-translate" + ], + "key": { + "message": "Required time: %d" + } + }, + { + "translated": "Temps de stock du message du travailleur:% d", + "flags": [ + "google-translate" + ], + "key": { + "message": "Worker message stock time: %d" + } + }, + { + "translated": "Jeton de travailleur invalide!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Invalid worker token!" + } + }, + { + "translated": "[Codec] R\u00e9ponse de travailleur initial obtenue: Succ\u00e8s:% o Message:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "[Codec] Got worker init response: Success: %o Message: %o" + } + }, + { + "translated": "Rappel de costume! (% o)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Costume callback! (%o)" + } + }, + { + "translated": "G\u00e9rer le serveur virtuel", + "flags": [ + "google-translate" + ], + "key": { + "message": "Manager the Virtual Server" + } + }, + { + "translated": "modifier", + "flags": [ + "google-translate" + ], + "key": { + "message": "Edit" + } + }, + { + "translated": "Modification des propri\u00e9t\u00e9s du serveur% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Changing server properties %o" + } + }, + { + "translated": "Propri\u00e9t\u00e9s modifi\u00e9es:% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Changed properties: %o" + } + }, + { + "translated": "Mettre \u00e0 jour les propri\u00e9t\u00e9s (% i)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Update properties (%i)" + } + }, + { + "translated": "Autorisations du serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server Permissions" + } + }, + { + "translated": "Groupe de d\u00e9penses", + "flags": [ + "google-translate" + ], + "key": { + "message": "Expend group" + } + }, + { + "translated": "Tout d\u00e9penser", + "flags": [ + "google-translate" + ], + "key": { + "message": "Expend all" + } + }, + { + "translated": "Groupe d'effondrement", + "flags": [ + "google-translate" + ], + "key": { + "message": "Collapse group" + } + }, + { + "translated": "R\u00e9duire tout", + "flags": [ + "google-translate" + ], + "key": { + "message": "Collapse all" + } + }, + { + "translated": "Ajouter une permission", + "flags": [ + "google-translate" + ], + "key": { + "message": "Add permission" + } + }, + { + "translated": "Supprimer la permission", + "flags": [ + "google-translate" + ], + "key": { + "message": "Remove permission" + } + }, + { + "translated": "Ajouter l'autorisation d'accorder", + "flags": [ + "google-translate" + ], + "key": { + "message": "Add grant permission" + } + }, + { + "translated": "Afficher la description de l'autorisation", + "flags": [ + "google-translate" + ], + "key": { + "message": "Show permission description" + } + }, + { + "translated": "Pas mis en \u0153uvre!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Not implemented!" + } + }, + { + "translated": "Cette fonction n'est pas encore impl\u00e9ment\u00e9e!", + "flags": [ + "google-translate" + ], + "key": { + "message": "This function isnt implemented yet!" + } + }, + { + "translated": "Copier le nom de permission", + "flags": [ + "google-translate" + ], + "key": { + "message": "Copy permission name" + } + }, + { + "translated": "Tentative de modifier une permission inconnue! (% s)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Attempted to edit a not known permission! (%s)" + } + }, + { + "translated": "Identifiant de canal s\u00e9lectionn\u00e9 manquant pour l'action de l'\u00e9diteur de permission!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing selected channel id for permission editor action!" + } + }, + { + "translated": "canal invalide", + "flags": [ + "google-translate" + ], + "key": { + "message": "invalid channel" + } + }, + { + "translated": "Ajout de la permission% s avec les propri\u00e9t\u00e9s:% o% o% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Added permission %s with properties: %o %o %o" + } + }, + { + "translated": "Autorisation supprim\u00e9e% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Removed permission %s" + } + }, + { + "translated": "Ajout de la subvention de% o pour% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Added grant of %o for %s" + } + }, + { + "translated": "Suppression de l'autorisation d'accorder pour% s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Removed grant permission for %s" + } + }, + { + "translated": "Ajout de la permission% o avec propri\u00e9t\u00e9s:% o% o% o", + "flags": [ + "google-translate" + ], + "key": { + "message": "Added permission %o with properties: %o %o %o" + } + }, + { + "translated": "Identifiant de groupe s\u00e9lectionn\u00e9 manquant pour l'action de l'\u00e9diteur de permission!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing selected group id for permission editor action!" + } + }, + { + "translated": "Identifiant de groupe s\u00e9lectionn\u00e9 manquant pour l'\u00e9diteur d'autorisations!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Missing selected group id for permission editor!" + } + }, + { + "translated": "La fr\u00e9quence d'\u00e9chantillonnage cible est en dehors de la plage [3000, 384000].", + "flags": [ + "google-translate" + ], + "key": { + "message": "The target sample rate is outside the range [3000, 384000]." + } + }, + { + "translated": "Tampon vide re\u00e7u en entr\u00e9e! Renvoyer une sortie vide!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Received empty buffer as input! Returning empty output!" + } + }, + { + "translated": "Se connecter \u00e0 un serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Connect to a server" + } + }, + { + "translated": "D\u00e9connecter du serveur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Disconnect from server" + } + }, + { + "translated": "Activer / d\u00e9sactiver le statut", + "flags": [ + "google-translate" + ], + "key": { + "message": "Toggle away status" + } + }, + { + "translated": "Activer / d\u00e9sactiver le microphone", + "flags": [ + "google-translate" + ], + "key": { + "message": "Mute/unmute microphone" + } + }, + { + "translated": "Casque muet / son muet", + "flags": [ + "google-translate" + ], + "key": { + "message": "Mute/unmute headphones" + } + }, + { + "translated": "Liste des jetons", + "flags": [ + "google-translate" + ], + "key": { + "message": "List tokens" + } + }, + { + "translated": "Afficher / modifier les autorisations", + "flags": [ + "google-translate" + ], + "key": { + "message": "View/edit permissions" + } + }, + { + "translated": "Modifier les param\u00e8tres globaux du client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Edit global client settings" + } + }, + { + "translated": "Adresse distante et port:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Remote address and port:" + } + }, + { + "translated": "Mot de passe du serveur:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server password:" + } + }, + { + "translated": "Surnom:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Nickname:" + } + }, + { + "translated": "Param\u00e8tres d'identit\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Identity Settings" + } + }, + { + "translated": "Compte Forum", + "flags": [ + "google-translate" + ], + "key": { + "message": "Forum Account" + } + }, + { + "translated": "TeamSpeak", + "flags": [ + "google-translate" + ], + "key": { + "message": "TeamSpeak" + } + }, + { + "translated": "Pseudo (uniquement pour le d\u00e9bogage!)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Nickname (Debug purposes only!)" + } + }, + { + "translated": "Veuillez saisir votre cha\u00eene d'identit\u00e9 TS3 export\u00e9e ci-dessous ou s\u00e9lectionnez votre identit\u00e9 export\u00e9e.", + "flags": [ + "google-translate" + ], + "key": { + "message": "Please enter your exported TS3 Identity string bellow or select your exported Identity" + } + }, + { + "translated": "Vous utilisez votre compte de forum comme v\u00e9rification", + "flags": [ + "google-translate" + ], + "key": { + "message": "You're using your forum account as verification" + } + }, + { + "translated": "Ceci est juste pour le d\u00e9bogage et utilise le nom comme identifiant unique", + "flags": [ + "google-translate" + ], + "key": { + "message": "This is just for debug and uses the name as unique identifier" + } + }, + { + "translated": "Pr\u00e9nom:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Name:" + } + }, + { + "translated": "Mot de passe:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Password:" + } + }, + { + "translated": "Sujet:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Topic:" + } + }, + { + "translated": "La description:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Description:" + } + }, + { + "translated": "la norme", + "flags": [ + "google-translate" + ], + "key": { + "message": "Standard" + } + }, + { + "translated": "Type de canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel Type" + } + }, + { + "translated": "Temporaire", + "flags": [ + "google-translate" + ], + "key": { + "message": "Temporary" + } + }, + { + "translated": "Semi-Permanent", + "flags": [ + "google-translate" + ], + "key": { + "message": "Semi-Permanent" + } + }, + { + "translated": "Permanent", + "flags": [ + "google-translate" + ], + "key": { + "message": "Permanent" + } + }, + { + "translated": "Cha\u00eene par d\u00e9faut", + "flags": [ + "google-translate" + ], + "key": { + "message": "Default Channel" + } + }, + { + "translated": "Trier cette cha\u00eene apr\u00e8s:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Sort this channel after:" + } + }, + { + "translated": "Besoin de parler pouvoir:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Needed Talk Power:" + } + }, + { + "translated": "l'audio", + "flags": [ + "google-translate" + ], + "key": { + "message": "Audio" + } + }, + { + "translated": "Pr\u00e9r\u00e9glages", + "flags": [ + "google-translate" + ], + "key": { + "message": "Presets" + } + }, + { + "translated": "Voice Mobile", + "flags": [ + "google-translate" + ], + "key": { + "message": "Voice Mobile" + } + }, + { + "translated": "Voice Desktop", + "flags": [ + "google-translate" + ], + "key": { + "message": "Voice Desktop" + } + }, + { + "translated": "La musique", + "flags": [ + "google-translate" + ], + "key": { + "message": "Music" + } + }, + { + "translated": "Douane", + "flags": [ + "google-translate" + ], + "key": { + "message": "Custom" + } + }, + { + "translated": "Param\u00e8tres personnalis\u00e9s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Custom Settings" + } + }, + { + "translated": "Codec:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Codec:" + } + }, + { + "translated": "Speex Ultra-Wideband", + "flags": [ + "google-translate" + ], + "key": { + "message": "Speex Ultra-Wideband" + } + }, + { + "translated": "Qualit\u00e9:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Quality:" + } + }, + { + "translated": "Pouvoirs r\u00e9guliers n\u00e9cessaires:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Regular needed powers:" + } + }, + { + "translated": "Joindre:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Join:" + } + }, + { + "translated": "Vue:", + "flags": [ + "google-translate" + ], + "key": { + "message": "View:" + } + }, + { + "translated": "Souscrire:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Subscribe:" + } + }, + { + "translated": "Desc. vue:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Desc. view:" + } + }, + { + "translated": "Modifier:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Modify:" + } + }, + { + "translated": "Effacer:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Delete:" + } + }, + { + "translated": "Transfert de fichier n\u00e9cessaire pouvoirs:", + "flags": [ + "google-translate" + ], + "key": { + "message": "File transfer needed powers:" + } + }, + { + "translated": "Avanc\u00e9e", + "flags": [ + "google-translate" + ], + "key": { + "message": "Advanced" + } + }, + { + "translated": "Autres r\u00e9glages", + "flags": [ + "google-translate" + ], + "key": { + "message": "Other Settings" + } + }, + { + "translated": "Nom phon\u00e9tique:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Phonetic Name:" + } + }, + { + "translated": "Supprimer le d\u00e9lai:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Delete delay:" + } + }, + { + "translated": "Donn\u00e9es vocales crypt\u00e9es", + "flags": [ + "google-translate" + ], + "key": { + "message": "Voice Data encrypted" + } + }, + { + "translated": "Max utilisateurs", + "flags": [ + "google-translate" + ], + "key": { + "message": "Max users" + } + }, + { + "translated": "Illimit\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unlimited" + } + }, + { + "translated": "Limit\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Limited" + } + }, + { + "translated": "Utilisateurs Family Max", + "flags": [ + "google-translate" + ], + "key": { + "message": "Family Max users" + } + }, + { + "translated": "H\u00e9rit\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Inherited" + } + }, + { + "translated": "v\u00e9rifi\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "checked" + } + }, + { + "translated": "Maximum Clients:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Maximum Clients:" + } + }, + { + "translated": "Places r\u00e9serv\u00e9es:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Reserved slots:" + } + }, + { + "translated": "Message de bienvenue:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Welcome Message:" + } + }, + { + "translated": "H\u00f4te", + "flags": [ + "google-translate" + ], + "key": { + "message": "Host" + } + }, + { + "translated": "Contraignant", + "flags": [ + "google-translate" + ], + "key": { + "message": "Binding" + } + }, + { + "translated": "H\u00f4te:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Host:" + } + }, + { + "translated": "Port:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Port:" + } + }, + { + "translated": "Remarque: ces param\u00e8tres n\u00e9cessitent un red\u00e9marrage du serveur virtuel pour prendre effet!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Note: These settings require a virtual server restart to take effect!" + } + }, + { + "translated": "Message d'h\u00f4te", + "flags": [ + "google-translate" + ], + "key": { + "message": "Host message" + } + }, + { + "translated": "Message:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Message:" + } + }, + { + "translated": "Mode de message:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Message Mode:" + } + }, + { + "translated": "Pas de message", + "flags": [ + "google-translate" + ], + "key": { + "message": "No message" + } + }, + { + "translated": "Afficher le message dans le journal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Show message in log" + } + }, + { + "translated": "Afficher le message comme modal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Show message as modal" + } + }, + { + "translated": "Afficher le message comme modal et d\u00e9connecter le client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Show message as modal and disconnect the client" + } + }, + { + "translated": "Banni\u00e8re d'accueil", + "flags": [ + "google-translate" + ], + "key": { + "message": "Host banner" + } + }, + { + "translated": "URL de la banni\u00e8re Gfx:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Banner Gfx URL:" + } + }, + { + "translated": "Intervalle Gfx:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Gfx Interval:" + } + }, + { + "translated": "Redimensionner:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Resize:" + } + }, + { + "translated": "Ne pas ajuster", + "flags": [ + "google-translate" + ], + "key": { + "message": "Do not adjust" + } + }, + { + "translated": "Ajuster mais ignorer l'aspect ratio", + "flags": [ + "google-translate" + ], + "key": { + "message": "Adjust but ignore ratio aspect" + } + }, + { + "translated": "Ajuster et conserver l'aspect du rapport", + "flags": [ + "google-translate" + ], + "key": { + "message": "Adjust and keep ratio aspect" + } + }, + { + "translated": "Bouton d'h\u00f4te", + "flags": [ + "google-translate" + ], + "key": { + "message": "Host Button" + } + }, + { + "translated": "Info-bulle:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Tooltip:" + } + }, + { + "translated": "URL de l'ic\u00f4ne:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Icon URL:" + } + }, + { + "translated": "Transferts", + "flags": [ + "google-translate" + ], + "key": { + "message": "Transfers" + } + }, + { + "translated": "T\u00e9l\u00e9charger", + "flags": [ + "google-translate" + ], + "key": { + "message": "Upload" + } + }, + { + "translated": "Limite de bande passante:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Bandwidth Limit:" + } + }, + { + "translated": "Octets / s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Bytes/s" + } + }, + { + "translated": "Upload Quota:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Upload Quota:" + } + }, + { + "translated": "MiB", + "flags": [ + "google-translate" + ], + "key": { + "message": "MiB" + } + }, + { + "translated": "T\u00e9l\u00e9charger", + "flags": [ + "google-translate" + ], + "key": { + "message": "Download" + } + }, + { + "translated": "Quota de t\u00e9l\u00e9chargement:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Download Quota:" + } + }, + { + "translated": "Anti-inondation", + "flags": [ + "google-translate" + ], + "key": { + "message": "Anti-Flood" + } + }, + { + "translated": "Points r\u00e9duits par tick:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Reduced points per tick:" + } + }, + { + "translated": "Points n\u00e9cessaires pour bloquer les commandes:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Points needed to block commands:" + } + }, + { + "translated": "Points n\u00e9cessaires pour bloquer l'IP:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Points needed to block IP:" + } + }, + { + "translated": "S\u00e9curit\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Security" + } + }, + { + "translated": "Niveau de s\u00e9curit\u00e9 requis:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Needed Security Level:" + } + }, + { + "translated": "Cryptage des donn\u00e9es vocales de la cha\u00eene:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel voice data encryption:" + } + }, + { + "translated": "Modifier chaque canal individuellement", + "flags": [ + "google-translate" + ], + "key": { + "message": "Edit per channel individually" + } + }, + { + "translated": "D\u00e9sactiv\u00e9 dans le monde entier", + "flags": [ + "google-translate" + ], + "key": { + "message": "Globally disabled" + } + }, + { + "translated": "Globalement activ\u00e9 (recommand\u00e9)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Globally enabled (recommended)" + } + }, + { + "translated": "Misc", + "flags": [ + "google-translate" + ], + "key": { + "message": "Misc" + } + }, + { + "translated": "Groupes par d\u00e9faut", + "flags": [ + "google-translate" + ], + "key": { + "message": "Default groups" + } + }, + { + "translated": "Groupe de serveurs", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server group" + } + }, + { + "translated": "Groupe de musique bot", + "flags": [ + "google-translate" + ], + "key": { + "message": "Music bot group" + } + }, + { + "translated": "Groupe de canaux", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel group" + } + }, + { + "translated": "Groupe d'administration de la cha\u00eene", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel Admin group" + } + }, + { + "translated": "Se plaindre", + "flags": [ + "google-translate" + ], + "key": { + "message": "Complain" + } + }, + { + "translated": "Compte Autoban", + "flags": [ + "google-translate" + ], + "key": { + "message": "Autoban Count" + } + }, + { + "translated": "Heure Autoban", + "flags": [ + "google-translate" + ], + "key": { + "message": "Autoban Time" + } + }, + { + "translated": "Retirer le temps", + "flags": [ + "google-translate" + ], + "key": { + "message": "Remove Time" + } + }, + { + "translated": "Le nombre minimum de clients est le canal avant le silence:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Minimum clients is channel before silence:" + } + }, + { + "translated": "Modificateur dim", + "flags": [ + "google-translate" + ], + "key": { + "message": "Priority speaker dim modificator:" + } + }, + { + "translated": "Supprimer le d\u00e9lai pour les canaux temporaires", + "flags": [ + "google-translate" + ], + "key": { + "message": "Delete delay for temporary channels" + } + }, + { + "translated": "Liste de serveurs", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server list" + } + }, + { + "translated": "Activer la cr\u00e9ation de rapports sur la liste de serveurs TeamSpeak", + "flags": [ + "google-translate" + ], + "key": { + "message": "Enable reporting to the TeamSpeak server list" + } + }, + { + "translated": " Activer les rapports sur la liste de serveurs TeaSpeak (TeaSpeak ne supporte pas ce param\u00e8tre)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Enable reporting to the TeaSpeak server list (TeaSpeak does not support this setting)" + } + }, + { + "translated": "messages", + "flags": [ + "google-translate" + ], + "key": { + "message": "Messages" + } + }, + { + "translated": "Canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel" + } + }, + { + "translated": "Sujet par d\u00e9faut:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Default Topic:" + } + }, + { + "translated": "Description par d\u00e9faut", + "flags": [ + "google-translate" + ], + "key": { + "message": "Default Description" + } + }, + { + "translated": "Client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Client" + } + }, + { + "translated": "G\u00e9n\u00e9ral", + "flags": [ + "google-translate" + ], + "key": { + "message": "General" + } + }, + { + "translated": "Je n'ai pas encore install\u00e9!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Didnt setuped yet!" + } + }, + { + "translated": "Microphone", + "flags": [ + "google-translate" + ], + "key": { + "message": "Microphone" + } + }, + { + "translated": "Dispositif:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Device:" + } + }, + { + "translated": "D\u00e9tection d'activit\u00e9 vocale", + "flags": [ + "google-translate" + ], + "key": { + "message": "Voice Activity Detection" + } + }, + { + "translated": "Toujours actif", + "flags": [ + "google-translate" + ], + "key": { + "message": "Always active" + } + }, + { + "translated": "D\u00e9tection d'activit\u00e9 vocale", + "flags": [ + "google-translate" + ], + "key": { + "message": "Voice activity detection" + } + }, + { + "translated": "Push to talk", + "flags": [ + "google-translate" + ], + "key": { + "message": "Push to talk" + } + }, + { + "translated": "Il n'y a pas d'entr\u00e9es de r\u00e9glage pour une d\u00e9tection vocale toujours en ligne.", + "flags": [ + "google-translate" + ], + "key": { + "message": "There are no setting entries for an always online voice detection." + } + }, + { + "translated": "Touche Push-To-Talk:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Push to talk key:" + } + }, + { + "translated": "Non initialis\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Uninitialised" + } + }, + { + "translated": "Seuil d'activit\u00e9 vocale ( 20 %)", + "flags": [ + "google-translate" + ], + "key": { + "message": "Voice activity threshold (20%)" + } + }, + { + "translated": "Orateur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Speaker" + } + }, + { + "translated": "Traductions", + "flags": [ + "google-translate" + ], + "key": { + "message": "Translations" + } + }, + { + "translated": "Traductions disponibles", + "flags": [ + "google-translate" + ], + "key": { + "message": "Available translations" + } + }, + { + "translated": "Anglais (par d\u00e9faut / de secours)", + "flags": [ + "google-translate" + ], + "key": { + "message": "English (Default / Fallback)" + } + }, + { + "translated": "Ajouter un r\u00e9f\u00e9rentiel", + "flags": [ + "google-translate" + ], + "key": { + "message": "Add repository" + } + }, + { + "translated": "Attention: ces param\u00e8tres ne sont affect\u00e9s qu\u2019apr\u00e8s un red\u00e9marrage ou un rechargement!", + "flags": [ + "google-translate" + ], + "key": { + "message": "Attention: These settings get only affected after a restart or reload!" + } + }, + { + "translated": "recharger maintenant", + "flags": [ + "google-translate" + ], + "key": { + "message": "reload now" + } + }, + { + "translated": "Groupes de canaux", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel Groups" + } + }, + { + "translated": "Autorisations de canal", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel permissions" + } + }, + { + "translated": "Autorisations client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Client permissions" + } + }, + { + "translated": "Identifiant unique du client:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Client unique ID:" + } + }, + { + "translated": "Identifiant unique:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unique ID:" + } + }, + { + "translated": "ID de la base de donn\u00e9es client:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Client database ID:" + } + }, + { + "translated": "Autorisations du canal client", + "flags": [ + "google-translate" + ], + "key": { + "message": "Client channel permissions" + } + }, + { + "translated": "Changer de groupe de", + "flags": [ + "google-translate" + ], + "key": { + "message": "Changing groups of" + } + }, + { + "translated": "Filtre:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Filter:" + } + }, + { + "translated": "Afficher uniquement les autorisations accord\u00e9es", + "flags": [ + "google-translate" + ], + "key": { + "message": "Show granted permissions only" + } + }, + { + "translated": "Nom de la permission", + "flags": [ + "google-translate" + ], + "key": { + "message": "Permission Name" + } + }, + { + "translated": "Valeur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Value" + } + }, + { + "translated": "Sauter", + "flags": [ + "google-translate" + ], + "key": { + "message": "Skip" + } + }, + { + "translated": "Nier", + "flags": [ + "google-translate" + ], + "key": { + "message": "Negate" + } + }, + { + "translated": "Accord\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Granted" + } + }, + { + "translated": "Mettre \u00e0 jour", + "flags": [ + "google-translate" + ], + "key": { + "message": "Update" + } + }, + { + "translated": "Ajouter", + "flags": [ + "google-translate" + ], + "key": { + "message": "Add" + } + }, + { + "translated": "Retirer", + "flags": [ + "google-translate" + ], + "key": { + "message": "Remove" + } + }, + { + "translated": "Nom / IP / UID / HWID", + "flags": [ + "google-translate" + ], + "key": { + "message": "Name/IP/UID/HWID" + } + }, + { + "translated": "Raison", + "flags": [ + "google-translate" + ], + "key": { + "message": "Reason" + } + }, + { + "translated": "Cr\u00e9ateur", + "flags": [ + "google-translate" + ], + "key": { + "message": "Creator" + } + }, + { + "translated": "Cr\u00e9\u00e9 / Expire", + "flags": [ + "google-translate" + ], + "key": { + "message": "Created / Expires" + } + }, + { + "translated": "Recharger", + "flags": [ + "google-translate" + ], + "key": { + "message": "Reload" + } + }, + { + "translated": "Afficher uniquement les interdictions propres", + "flags": [ + "google-translate" + ], + "key": { + "message": "Show only own bans" + } + }, + { + "translated": "Mettez en surbrillance vos propres interdictions", + "flags": [ + "google-translate" + ], + "key": { + "message": "Highlight own bans" + } + }, + { + "translated": "Raison:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Reason:" + } + }, + { + "translated": "Dur\u00e9e:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Duration:" + } + }, + { + "translated": "permanent", + "flags": [ + "google-translate" + ], + "key": { + "message": "permanent" + } + }, + { + "translated": "Ban client par", + "flags": [ + "google-translate" + ], + "key": { + "message": "Ban client by" + } + }, + { + "translated": "Surnom", + "flags": [ + "google-translate" + ], + "key": { + "message": "Nickname" + } + }, + { + "translated": "Bans le client par son pseudo actuel.
Le pseudo actuel ne peut pas \u00eatre utilis\u00e9 jusqu'\u00e0 l'expiration de l'interdiction.", + "flags": [ + "google-translate" + ], + "key": { + "message": "Bans the client by his current nickname.
The currently nickname cant be used until the ban expired" + } + }, + { + "translated": "Identifiant du mat\u00e9riel", + "flags": [ + "google-translate" + ], + "key": { + "message": "Hardware ID" + } + }, + { + "translated": "Interdit le client par son identifiant mat\u00e9riel.
L'identifiant mat\u00e9riel a diff\u00e9rentes significations, d\u00e9pend de l'agent utilisateur
TeaClient: L'identifiant mat\u00e9riel sera \u00e9gal \u00e0 l'adresse mac
TeaWeb: Le client Web TeaSpeak n'a pas d'identifiant mat\u00e9riel, ce sera al\u00e9atoire
Client TeamSpeak 3: l'ID mat\u00e9riel r\u00e9sulte de hachages de propri\u00e9t\u00e9s sp\u00e9cifiques au mat\u00e9riel.", + "flags": [ + "google-translate" + ], + "key": { + "message": "Bans the client by his hardware id.
The hardware id has different meanings, depends on the users agent
TeaClient: The hardware id will be equal to the mac address
TeaWeb: The TeaSpeak web client hasn't a hardware id, it will be random
TeamSpeak 3 client: The hardware id will be a result of some hashes from hardware specific properties" + } + }, + { + "translated": "Adresse IP", + "flags": [ + "google-translate" + ], + "key": { + "message": "IP Address" + } + }, + { + "translated": "Cr\u00e9\u00e9:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Created:" + } + }, + { + "translated": "Expirer:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Expire:" + } + }, + { + "translated": "IP:", + "flags": [ + "google-translate" + ], + "key": { + "message": "IP:" + } + }, + { + "translated": "Interpr\u00e9ter IP / Nom comme:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Interpret IP/Name as:" + } + }, + { + "translated": "Wildcard IPv4", + "flags": [ + "google-translate" + ], + "key": { + "message": "Wildcard IPv4" + } + }, + { + "translated": "Wildcard IPv6", + "flags": [ + "google-translate" + ], + "key": { + "message": "Wildcard IPv6" + } + }, + { + "translated": "Cha\u00eene fixe", + "flags": [ + "google-translate" + ], + "key": { + "message": "Fixed string" + } + }, + { + "translated": "Expression r\u00e9guli\u00e8re", + "flags": [ + "google-translate" + ], + "key": { + "message": "Regular Expression" + } + }, + { + "translated": "Identifiant du mat\u00e9riel:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Hardware ID:" + } + }, + { + "translated": "Dur\u00e9e", + "flags": [ + "google-translate" + ], + "key": { + "message": "Duration" + } + }, + { + "translated": "Utilisez cette interdiction comme une interdiction mondiale", + "flags": [ + "google-translate" + ], + "key": { + "message": "Use this ban as a global ban" + } + }, + { + "translated": "Les interdictions globales sont des interdictions qui s'appliquent \u00e0 l'ensemble de l'instance.
Cela signifie que (si cette r\u00e8gle s'applique \u00e0 une victime), vous ne pouvez rejoindre aucun serveur virtuel!
Les interdictions globales sont affich\u00e9es par d\u00e9faut \u00e0 chaque groupe d\u2019administrateurs du serveur,
mais ne peut \u00eatre cr\u00e9\u00e9 qu'avec des droits de requ\u00eate", + "flags": [ + "google-translate" + ], + "key": { + "message": "Global bans are bans which apply instance wide.
This means that (if this rule apply to a victim) cant join any virtual server!
Global bans are by default shown to every server admin group,
but could only be created with query rights" + } + }, + { + "translated": "Ne joue aucune musique", + "flags": [ + "google-translate" + ], + "key": { + "message": "Not playing any music" + } + }, + { + "translated": "Version:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Version:" + } + }, + { + "translated": "En ligne depuis:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Online since:" + } + }, + { + "translated": "Le volume:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Volume:" + } + }, + { + "translated": "Compte TeaSpeak:", + "flags": [ + "google-translate" + ], + "key": { + "message": "TeaSpeak Account:" + } + }, + { + "translated": "Groupes de serveurs:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Server groups:" + } + }, + { + "translated": "Groupe de canaux:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Channel group:" + } + }, + { + "translated": "Une fa\u00e7on", + "flags": [ + "google-translate" + ], + "key": { + "message": "Away" + } + }, + { + "translated": "Haut-parleurs / \u00e9couteurs d\u00e9sactiv\u00e9s", + "flags": [ + "google-translate" + ], + "key": { + "message": "Speakers/Headphones disabled" + } + }, + { + "translated": "Microphone d\u00e9sactiv\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Microphone disabled" + } + }, + { + "translated": "Haut-parleurs / \u00e9couteurs en sourdine", + "flags": [ + "google-translate" + ], + "key": { + "message": "Speakers/Headphones Muted" + } + }, + { + "translated": "Microphone en sourdine", + "flags": [ + "google-translate" + ], + "key": { + "message": "Microphone Muted" + } + }, + { + "translated": "Temps en direct:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Livetime:" + } + }, + { + "translated": "Volume \u00e0 distance:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Remote Volume:" + } + }, + { + "translated": "Volume local:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Local Volume:" + } + }, + { + "translated": "En train de rejouer:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Currently replaying:" + } + }, + { + "translated": "Adresse:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Address:" + } + }, + { + "translated": "Type:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Type:" + } + }, + { + "translated": "Temps de disponibilit\u00e9:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Uptime:" + } + }, + { + "translated": "Canaux actuels:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Current Channels:" + } + }, + { + "translated": "Clients actuels:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Current Clients:" + } + }, + { + "translated": "Requ\u00eates actuelles:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Current Queries:" + } + }, + { + "translated": "Mettre \u00e0 jour les informations", + "flags": [ + "google-translate" + ], + "key": { + "message": "Update info" + } + }, + { + "translated": "Qualit\u00e9 du codec:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Codec Quality:" + } + }, + { + "translated": "Clients actuels:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Current clients:" + } + }, + { + "translated": "\u00c9tat de l'abonnement:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Subscription Status:" + } + }, + { + "translated": "Abonn\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Subscribed" + } + }, + { + "translated": "D\u00e9sabonn\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unsubscribed" + } + }, + { + "translated": "Cryptage de donn\u00e9es vocales:", + "flags": [ + "google-translate" + ], + "key": { + "message": "Voice Data Encryption:" + } + }, + { + "translated": "Non crypt\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Unencrypted" + } + }, + { + "translated": "Crypt\u00e9", + "flags": [ + "google-translate" + ], + "key": { + "message": "Encrypted" + } + }, + { + "translated": "(\u00c9cras\u00e9 par le serveur avec crypt\u00e9)", + "flags": [ + "google-translate" + ], + "key": { + "message": "(Overridden by the server with Encrypted)" + } + }, + { + "translated": "(\u00c9cras\u00e9 par le serveur avec non chiffr\u00e9)", + "flags": [ + "google-translate" + ], + "key": { + "message": "(Overridden by the server with Unencrypted)" + } + }, + { + "translated": "La description", + "flags": [ + "google-translate" + ], + "key": { + "message": "Description" + } + }, + { + "translated": "Vous avez \u00e9t\u00e9 piqu\u00e9 par", + "flags": [ + "google-translate" + ], + "key": { + "message": "You have been poked by" + } + } + ] +} \ No newline at end of file diff --git a/shared/i18n/info.json b/shared/i18n/info.json index 80b5ca6f..4cd17cf2 100644 --- a/shared/i18n/info.json +++ b/shared/i18n/info.json @@ -11,6 +11,10 @@ { "key": "tr_gt", "path": "tr_google_translate.translation" + }, + { + "key": "fr_gt", + "path": "fr_google_translate.translation" } ], "name": "Default TeaSpeak repository", diff --git a/shared/js/bookmarks.ts b/shared/js/bookmarks.ts new file mode 100644 index 00000000..34257327 --- /dev/null +++ b/shared/js/bookmarks.ts @@ -0,0 +1,156 @@ +namespace bookmarks { + function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + } + + export interface ConnectIdentity { + identity_type: IdentitifyType; + } + + export interface ForumConnectIdentity extends ConnectIdentity { } + export interface NicknameConnectIdentity extends ConnectIdentity { } + export interface TeamSpeakConnectIdentity extends ConnectIdentity { } + + export interface ServerProperties { + server_address: string; + server_port: number; + server_password_hash?: string; + server_password?: string; + } + + export enum BookmarkType { + ENTRY, + DIRECTORY + } + + export interface Bookmark { + type: /* BookmarkType.ENTRY */ BookmarkType; + + /* readonly directory: DirectoryBookmark; */ + server_properties: ServerProperties; + display_name: string; + unique_id: string; + + nickname: string; + default_channel?: number | string; + default_channel_password_hash?: string; + default_channel_password?: string; + + } + + export interface DirectoryBookmark { + type: /* BookmarkType.DIRECTORY */ BookmarkType; + + readonly content: (Bookmark | DirectoryBookmark)[]; + unique_id: string; + display_name: string; + } + + interface BookmarkConfig { + root_bookmark?: DirectoryBookmark; + default_added?: boolean; + } + + let _bookmark_config: BookmarkConfig; + + function bookmark_config() : BookmarkConfig { + if(_bookmark_config) + return _bookmark_config; + + let bookmark_json = localStorage.getItem("bookmarks"); + let bookmarks = JSON.parse(bookmark_json) || {} as BookmarkConfig; + + _bookmark_config = bookmarks; + _bookmark_config.root_bookmark = _bookmark_config.root_bookmark || { content: [], display_name: "root", type: BookmarkType.DIRECTORY} as DirectoryBookmark; + + if(!_bookmark_config.default_added) { + _bookmark_config.default_added = true; + create_bookmark("TeaSpeak official Test-Server", _bookmark_config.root_bookmark, { + server_address: "ts.teaspeak.de", + server_port: 9987 + }, "Another TeaSpeak user"); + + save_config(); + } + return _bookmark_config; + } + + function save_config() { + localStorage.setItem("bookmarks", JSON.stringify(bookmark_config())); + } + + export function bookmarks() : DirectoryBookmark { + return bookmark_config().root_bookmark; + } + + function find_bookmark_recursive(parent: DirectoryBookmark, uuid: string) : Bookmark | DirectoryBookmark { + for(const entry of parent.content) { + if(entry.unique_id == uuid) + return entry; + if(entry.type == BookmarkType.DIRECTORY) { + const result = find_bookmark_recursive(entry as DirectoryBookmark, uuid); + if(result) return result; + } + } + return undefined; + } + + export function find_bookmark(uuid: string) : Bookmark | DirectoryBookmark | undefined { + return find_bookmark_recursive(bookmarks(), uuid); + } + + export function create_bookmark(display_name: string, directory: DirectoryBookmark, server_properties: ServerProperties, nickname: string) : Bookmark { + const bookmark = { + display_name: display_name, + server_properties: server_properties, + nickname: nickname, + type: BookmarkType.ENTRY, + unique_id: guid() + } as Bookmark; + + directory.content.push(bookmark); + return bookmark; + } + + export function create_bookmark_directory(parent: DirectoryBookmark, name: string) : DirectoryBookmark { + const bookmark = { + type: BookmarkType.DIRECTORY, + + display_name: name, + content: [], + unique_id: guid() + } as DirectoryBookmark; + + parent.content.push(bookmark); + return bookmark; + } + + //TODO test if the new parent is within the old bookmark + export function change_directory(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { + delete_bookmark(bookmark) + parent.content.push(bookmark) + } + + export function save_bookmark(bookmark?: Bookmark | DirectoryBookmark) { + save_config(); /* nvm we dont give a fuck... saving everything */ + } + + function delete_bookmark_recursive(parent: DirectoryBookmark, bookmark: Bookmark | DirectoryBookmark) { + const index = parent.content.indexOf(bookmark); + if(index != -1) + parent.content.remove(bookmark); + else + for(const entry of parent.content) + if(entry.type == BookmarkType.DIRECTORY) + delete_bookmark_recursive(entry as DirectoryBookmark, bookmark) + } + + export function delete_bookmark(bookmark: Bookmark | DirectoryBookmark) { + delete_bookmark_recursive(bookmarks(), bookmark) + } +} \ No newline at end of file diff --git a/shared/js/connection.ts b/shared/js/connection.ts index 2adfe796..06fc815f 100644 --- a/shared/js/connection.ts +++ b/shared/js/connection.ts @@ -402,10 +402,24 @@ interface ClientNameFromUid { response: ClientNameInfo[] } +interface QueryListEntry { + username: string; + unique_id: string; + bounded_server: number; +} + +interface QueryList { + flag_own: boolean; + flag_all: boolean; + + queries: QueryListEntry[]; +} + class CommandHelper { readonly connection: ServerConnection; private _callbacks_namefromuid: ClientNameFromUid[] = []; + private _who_am_i: any; constructor(connection) { this.connection = connection; @@ -432,6 +446,63 @@ class CommandHelper { return req.promise; } + request_query_list(server_id: number = undefined) : Promise { + return new Promise((resolve, reject) => { + this.connection.commandHandler["notifyquerylist"] = json => { + const result = {} as QueryList; + + result.flag_all = json[0]["flag_all"]; + result.flag_own = json[0]["flag_own"]; + result.queries = []; + + for(const entry of json) { + const rentry = {} as QueryListEntry; + rentry.bounded_server = entry["client_bounded_server"]; + rentry.username = entry["client_login_name"]; + rentry.unique_id = entry["client_unique_identifier"]; + + result.queries.push(rentry); + } + + resolve(result); + this.connection.commandHandler["notifyquerylist"] = undefined; + }; + + let data = {}; + if(server_id !== undefined) + data["server_id"] = server_id; + + this.connection.sendCommand("querylist", data).catch(error => { + if(error instanceof CommandResult) { + if(error.id == 0x0501) { + resolve(undefined); + return; + } + } + reject(error); + }) + }); + } + + /** + * @deprecated + * Its just a workaround for the query management. + * There is no garante that the whoami trick will work forever + */ + current_virtual_server_id() : Promise { + if(this._who_am_i) + return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); + + return new Promise((resolve, reject) => { + this.connection.commandHandler[""] = json => { + this._who_am_i = json[0]; + resolve(parseInt(this._who_am_i["virtualserver_id"])); + this.connection.commandHandler[""] = undefined; + }; + this.connection.sendCommand("whoami"); + }); + } + private handle_notifyclientnamefromuid(json: any[]) { for(let entry of json) { let info: ClientNameInfo = {} as any; diff --git a/shared/js/load.ts b/shared/js/load.ts index 48fb5fe9..a29a383d 100644 --- a/shared/js/load.ts +++ b/shared/js/load.ts @@ -180,6 +180,8 @@ function loadDebug() { "js/crypto/hex.js", //Load UI + "js/ui/modal/ModalQuery.js", + "js/ui/modal/ModalQueryManage.js", "js/ui/modal/ModalConnect.js", "js/ui/modal/ModalSettings.js", "js/ui/modal/ModalCreateChannel.js", @@ -219,6 +221,7 @@ function loadDebug() { //Load general stuff "js/settings.js", + "js/bookmarks.js", "js/contextMenu.js", "js/connection.js", "js/FileManager.js", diff --git a/shared/js/main.ts b/shared/js/main.ts index 719ad559..e7acb5c0 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -21,9 +21,9 @@ const js_render = window.jsrender || $; const native_client = window.require !== undefined; function getUserMediaFunction() { - if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) - return (settings, success, fail) => { navigator.mediaDevices.getUserMedia(settings).then(success).catch(fail); }; - return navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + if((navigator as any).mediaDevices && (navigator as any).mediaDevices.getUserMedia) + return (settings, success, fail) => { (navigator as any).mediaDevices.getUserMedia(settings).then(success).catch(fail); }; + return (navigator as any).getUserMedia || (navigator as any).webkitGetUserMedia || (navigator as any).mozGetUserMedia; } function setup_close() { diff --git a/shared/js/permission/GroupManager.ts b/shared/js/permission/GroupManager.ts index e931a86d..da21b49d 100644 --- a/shared/js/permission/GroupManager.ts +++ b/shared/js/permission/GroupManager.ts @@ -143,9 +143,9 @@ class GroupManager { group.updateProperty(key, groupData[key]); } - group.requiredMemberRemovePower = groupData["n_member_removep"]; - group.requiredMemberAddPower = groupData["n_member_addp"]; - group.requiredModifyPower = groupData["n_modifyp"]; + group.requiredMemberRemovePower = parseInt(groupData["n_member_removep"]); + group.requiredMemberAddPower = parseInt(groupData["n_member_addp"]); + group.requiredModifyPower = parseInt(groupData["n_modifyp"]); if(target == GroupTarget.SERVER) this.serverGroups.push(group); @@ -154,6 +154,8 @@ class GroupManager { } console.log("Got " + json.length + " new " + target + " groups:"); + for(const client of this.handle.channelTree.clients) + client.update_displayed_client_groups(); } request_permissions(group: Group) : Promise { //database_empty_result diff --git a/shared/js/permission/PermissionManager.ts b/shared/js/permission/PermissionManager.ts index 4372d735..e27be066 100644 --- a/shared/js/permission/PermissionManager.ts +++ b/shared/js/permission/PermissionManager.ts @@ -644,11 +644,12 @@ class PermissionManager { for(let perm of this.neededPermissions) if(perm.type.id == key || perm.type.name == key || perm.type == key) return perm; + log.debug(LogCategory.PERMISSIONS, tr("Could not resolve grant permission %o. Creating a new one."), key); let info = key instanceof PermissionInfo ? key : this.resolveInfo(key); if(!info) { log.warn(LogCategory.PERMISSIONS, tr("Requested needed permission with invalid key! (%o)"), key); - return undefined; + return new NeededPermissionValue(undefined, -2); } let result = new NeededPermissionValue(info, -2); this.neededPermissions.push(result); diff --git a/shared/js/proto.ts b/shared/js/proto.ts index fa1c855d..ea2c4910 100644 --- a/shared/js/proto.ts +++ b/shared/js/proto.ts @@ -13,6 +13,7 @@ interface JSON { interface JQuery { render(values?: any) : string; renderTag(values?: any) : JQuery; + hasScrollBar() : boolean; } interface JQueryStatic { @@ -106,8 +107,8 @@ if(typeof ($) !== "undefined") { return $(document.createElement(tagName) as any); } } - if(!$.prototype.renderTag) { - $.prototype.renderTag = function (values?: any) : JQuery { + if(!$.fn.renderTag) { + $.fn.renderTag = function (values?: any) : JQuery { let result; if(this.render) { result = $(this.render(values)); @@ -126,6 +127,11 @@ if(typeof ($) !== "undefined") { return result; } } + if(!$.fn.hasScrollBar) + $.fn.hasScrollBar = function() { + return this.get(0).scrollHeight > this.height(); + } + } if (!String.prototype.format) { diff --git a/shared/js/ui/channel.ts b/shared/js/ui/channel.ts index 6d15f73a..f250cc9d 100644 --- a/shared/js/ui/channel.ts +++ b/shared/js/ui/channel.ts @@ -294,12 +294,14 @@ class ChannelEntry { const sub = this.siblings(false); sub.forEach(function (e) { - subSize += e.rootTag().outerHeight(true); + if(e.rootTag().is(":visible")) + subSize += e.rootTag().outerHeight(true); }); const clients = this.clients(false); clients.forEach(function (e) { - clientSize += e.tag.outerHeight(true); + if(e.tag.is(":visible")) + clientSize += e.tag.outerHeight(true); }); this._tag_root.css({height: size + subSize + clientSize}); diff --git a/shared/js/ui/client.ts b/shared/js/ui/client.ts index e7852d73..c74e7bf4 100644 --- a/shared/js/ui/client.ts +++ b/shared/js/ui/client.ts @@ -408,7 +408,9 @@ class ClientEntry { tag.append($.spawn("div").addClass("icon_client_state").attr("title", "Client state")); + tag.append($.spawn("div").addClass("group_prefix").attr("title", "Server groups prefixes").hide()); tag.append($.spawn("div").addClass("name").text(this.clientNickName())); + tag.append($.spawn("div").addClass("group_suffix").attr("title", "Server groups suffix").hide()); tag.append($.spawn("div").addClass("away").text(this.clientNickName())); let clientIcons = $.spawn("span"); @@ -561,7 +563,7 @@ class ClientEntry { if(variable.key == "client_icon_id") this.updateClientIcon(); if(variable.key =="client_channel_group_id" || variable.key == "client_servergroups") - this.updateGroupIcons(); + this.update_displayed_client_groups(); } /* process updates after variables have been set */ @@ -577,11 +579,39 @@ class ClientEntry { group.end(); } - updateGroupIcons() { + update_displayed_client_groups() { this.tag.find("span .group_icons").children().detach(); + for(let id of this.assignedServerGroupIds()) this.updateGroupIcon(this.channelTree.client.groups.serverGroup(id)); + this.updateGroupIcon(this.channelTree.client.groups.channelGroup(this.properties.client_channel_group_id)); + + let prefix_groups: string[] = []; + let suffix_groups: string[] = []; + for(const group_id of this.assignedServerGroupIds()) { + const group = this.channelTree.client.groups.serverGroup(group_id); + if(!group) continue; + + if(group.properties.namemode == 1) + prefix_groups.push(group.name); + else if(group.properties.namemode == 2) + suffix_groups.push(group.name); + } + + const tag_group_prefix = this.tag.find(".group_prefix"); + const tag_group_suffix = this.tag.find(".group_suffix"); + if(prefix_groups.length > 0) { + tag_group_prefix.text("[" + prefix_groups.join("][") + "]").show(); + } else { + tag_group_prefix.hide() + } + + if(suffix_groups.length > 0) { + tag_group_suffix.text("[" + suffix_groups.join("][") + "]").show(); + } else { + tag_group_suffix.hide() + } } updateClientVariables(){ diff --git a/shared/js/ui/frames/ControlBar.ts b/shared/js/ui/frames/ControlBar.ts index d0268340..8264d393 100644 --- a/shared/js/ui/frames/ControlBar.ts +++ b/shared/js/ui/frames/ControlBar.ts @@ -13,11 +13,13 @@ client_away_message Value: '' */ import openBanList = Modals.openBanList; +import spawnConnectModal = Modals.spawnConnectModal; class ControlBar { private _muteInput: boolean; private _muteOutput: boolean; private _away: boolean; + private _query_visible: boolean; private _awayMessage: string; private codec_supported: boolean = false; @@ -64,10 +66,35 @@ class ControlBar { away.find(".btn_away_toggle").on('click', this.on_away_toggle.bind(this)); away.find(".btn_away_message").on('click', this.on_away_set_message.bind(this)); } + { + let bookmark = this.htmlTag.find(".btn_bookmark"); + bookmark.find(".button-dropdown").on('click', () => { + bookmark.find(".dropdown").addClass("displayed"); + }); + bookmark.on('mouseleave', () => { + bookmark.find(".dropdown").removeClass("displayed"); + }); + + this.update_bookmarks() + } + { + let query = this.htmlTag.find(".btn_query"); + query.find(".button-dropdown").on('click', () => { + query.find(".dropdown").addClass("displayed"); + }); + query.on('mouseleave', () => { + query.find(".dropdown").removeClass("displayed"); + }); + + query.find(".btn_query_toggle").on('click', this.on_query_visibility_toggle.bind(this)); + query.find(".btn_query_create").on('click', this.on_query_create.bind(this)); + query.find(".btn_query_manage").on('click', this.on_query_manage.bind(this)); + } //Need an initialise this.muteInput = settings.global("mute_input") == "1"; this.muteOutput = settings.global("mute_output") == "1"; + this.query_visibility = settings.global("show_server_queries") == "1"; } @@ -269,6 +296,77 @@ class ControlBar { private onBanlist() { if(!this.handle.serverConnection) return; - openBanList(this.handle); + if(this.handle.permissions.neededPermission(PermissionType.B_CLIENT_BAN_LIST).granted(1)) { + openBanList(this.handle); + } else { + createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to view the ban list")).open(); + sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + + update_bookmarks() { + // + let tag_bookmark = this.htmlTag.find(".btn_bookmark .dropdown"); + tag_bookmark.find(".bookmark, .bookmark_directory").detach(); + + for(const bookmark of bookmarks.bookmarks().content) { + if(bookmark.type == bookmarks.BookmarkType.ENTRY) { + tag_bookmark.append( + $.spawn("div") + .addClass("bookmark") + /* /.attr("bookmark-uuid", bookmark.unique_id) */ + .text(bookmark.display_name) + .on('click', event => { + spawnConnectModal() + + }) + ) + } + //TODO add bookmark directories here + } + } + + get query_visibility() { + return this._query_visible; + } + + set query_visibility(flag: boolean) { + if(this._query_visible == flag) return; + + this._query_visible = flag; + settings.global("show_server_queries", flag); + this.update_query_visibility_button(); + this.handle.channelTree.toggle_server_queries(flag); + } + + private on_query_visibility_toggle() { + this.query_visibility = !this._query_visible; + this.update_query_visibility_button(); + } + + private update_query_visibility_button() { + let tag = this.htmlTag.find(".btn_query_toggle"); + if(this._query_visible) { + tag.addClass("activated"); + } else { + tag.removeClass("activated"); + } + } + + private on_query_create() { + if(this.handle.permissions.neededPermission(PermissionType.B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN).granted(1)) { + Modals.spawnQueryCreate(); + } else { + createErrorModal(tr("You dont have the permission"), tr("You dont have the permission to create a server query login")).open(); + sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + + private on_query_manage() { + if(globalClient && globalClient.connected) { + Modals.spawnQueryManage(globalClient); + } else { + createErrorModal(tr("You have to be connected"), tr("You have to be connected!")).open(); + } } } \ No newline at end of file diff --git a/shared/js/ui/modal/ModalQuery.ts b/shared/js/ui/modal/ModalQuery.ts new file mode 100644 index 00000000..ef4974ed --- /dev/null +++ b/shared/js/ui/modal/ModalQuery.ts @@ -0,0 +1,84 @@ +/// +/// +/// + +namespace Modals { + export function spawnQueryCreate(callback_created?: (user, pass) => any) { + let modal; + modal = createModal({ + header: tr("Create a server query login"), + body: () => { + let template = $("#tmpl_query_create").renderTag(); + template = $.spawn("div").append(template); + + template.find(".button-close").on('click', event => modal.close()); + template.find(".button-create").on('click', event => { + const name = template.find(".input-name").val() as string; + if(name.length < 3 || name.length > 64) { + createErrorModal(tr("Invalid username"), tr("Please enter a valid name!")).open(); + return; + } + + //client_login_password + globalClient.serverConnection.commandHandler["notifyquerycreated"] = json => { + json = json[0]; + + spawnQueryCreated({ + username: name, + password: json.client_login_password + }, true); + + if(callback_created) + callback_created(name, json.client_login_password); + }; + + globalClient.serverConnection.sendCommand("querycreate", { + client_login_name: name + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to create account"), tr("Failed to create account
Message: ") + error).open(); + }); + + modal.close(); + //TODO create account + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } + + export function spawnQueryCreated(credentials: { + username: string, + password: string + }, yust_created: boolean) { + let modal; + modal = createModal({ + header: yust_created ? tr("Server query credentials") : tr("New server query credentials"), + body: () => { + let template = $("#tmpl_query_created").renderTag(credentials); + template = $.spawn("div").append(template); + + template.find(".button-close").on('click', event => modal.close()); + template.find(".query_name").text(credentials.username); + template.find(".query_password").text(credentials.password); + + template.find(".btn_copy_name").on('click', () => { + template.find(".query_name").select(); + document.execCommand("copy"); + }); + template.find(".btn_copy_password").on('click', () => { + template.find(".query_password").select(); + document.execCommand("copy"); + }); + return template; + }, + footer: undefined, + width: 750 + }); + modal.open(); + } +} \ No newline at end of file diff --git a/shared/js/ui/modal/ModalQueryManage.ts b/shared/js/ui/modal/ModalQueryManage.ts new file mode 100644 index 00000000..7b990d7c --- /dev/null +++ b/shared/js/ui/modal/ModalQueryManage.ts @@ -0,0 +1,156 @@ +/// +/// +/// + +namespace Modals { + export function spawnQueryManage(client: TSClient) { + let modal: Modal; + let selected_query: QueryListEntry; + + const update_selected = () => { + const buttons = modal.htmlTag.find(".header .buttons"); + + //TODO gray out if no permissions (Server needs to send that... :D) + buttons.find(".button-query-delete").prop("disabled", selected_query === undefined); + buttons.find(".button-query-rename").prop("disabled", selected_query === undefined); + buttons.find(".button-query-change-password").prop("disabled", selected_query === undefined); + }; + + const update_list = () => { + const info_tag = modal.htmlTag.find(".footer .info a"); + info_tag.text("loading..."); + client.serverConnection.helper.current_virtual_server_id().then(server_id => { + client.serverConnection.helper.request_query_list(server_id).then(result => { + selected_query = undefined; + + const entries_tag = modal.htmlTag.find(".query-list-entries"); + const entry_template = $("#tmpl_query_manager-list_entry"); + entries_tag.empty(); + + for(const query of result.queries || []) { + entries_tag.append(entry_template.renderTag(query).on('click', event => { + entries_tag.find(".entry.selected").removeClass("selected"); + $(event.target).parent(".entry").addClass("selected"); + selected_query = query; + update_selected(); + })); + } + + const entry_container = modal.htmlTag.find(".query-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + + if(!result || result.flag_all) { + info_tag.text("Showing all server queries"); + } else { + info_tag.text("Showing your server queries") + } + update_selected(); + }); + }); + //TODO error handling + }; + + modal = createModal({ + header: tr("Manage query accounts"), + body: () => { + let template = $("#tmpl_query_manager").renderTag(); + template = $.spawn("div").append(template); + + /* first open the modal */ + setTimeout(() => { + const entry_container = template.find(".query-list-entries-container"); + if(entry_container.hasScrollBar()) + entry_container.addClass("scrollbar"); + }, 100); + + template.find(".footer .buttons .button-refresh").on('click', update_list); + template.find(".button-query-create").on('click', () => { + Modals.spawnQueryCreate((user, pass) => update_list()); + }); + template.find(".button-query-rename").on('click', () => { + if(!selected_query) return; + + createInputModal(tr("Change account name"), tr("Enter the new name for the login:
"), text => text.length >= 3, result => { + if(result) { + client.serverConnection.sendCommand("queryrename", { + client_login_name: selected_query.username, + client_new_login_name: result + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to rename account"), tr("Failed to rename account
Message: ") + error).open(); + }).then(() => { + createInfoModal(tr("Account successfully renamed"), tr("The query account has been renamed!")).open(); + update_list(); + }); + } + }).open(); + }); + template.find(".button-query-change-password").on('click', () => { + if(!selected_query) return; + + createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):
"), text => true, result => { + if(result !== false) { + client.serverConnection.sendCommand("querychangepassword", { + client_login_name: selected_query.username, + client_login_password: result + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to change password"), tr("Failed to change password
Message: ") + error).open(); + }); + + client.serverConnection.commandHandler["notifyquerypasswordchanges"] = json => { + Modals.spawnQueryCreated({ + username: json[0]["client_login_name"], + password: json[0]["client_login_password"] + }, false); + + client.serverConnection.commandHandler["notifyquerypasswordchanges"] = undefined; + }; + } + }).open(); + }); + template.find(".button-query-delete").on('click', () => { + if(!selected_query) return; + + Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => { + if(result) { + client.serverConnection.sendCommand("querydelete", { + client_login_name: selected_query.username + }).catch(error => { + if(error instanceof CommandResult) + error = error.extra_message || error.message; + createErrorModal(tr("Unable to delete account"), tr("Failed to delete account
Message: ") + error).open(); + }).then(() => { + createInfoModal(tr("Account successfully deleted"), tr("The query account has been successfully deleted!")).open(); + update_list(); + }); + } + }); + }); + template.find(".input-search").on('change keyup', () => { + const text = (template.find(".input-search").val() as string || "").toLowerCase(); + if(text.length == 0) { + template.find(".query-list-entries .entry").show(); + } else { + template.find(".query-list-entries .entry").each((_, e) => { + const element = $(e); + if(element.text().toLowerCase().indexOf(text) == -1) + element.hide(); + else + element.show(); + }) + } + }); + return template; + }, + footer: undefined, + width: 750 + }); + + update_list(); + modal.open(); + } +} \ No newline at end of file diff --git a/shared/js/ui/view.ts b/shared/js/ui/view.ts index 403373c9..e828b918 100644 --- a/shared/js/ui/view.ts +++ b/shared/js/ui/view.ts @@ -18,6 +18,7 @@ class ChannelTree { currently_selected_context_callback: (event) => any = undefined; readonly client_mover: ClientMover; + private _show_queries: boolean; private channel_last?: ChannelEntry; private channel_first?: ChannelEntry; @@ -290,6 +291,10 @@ class ChannelTree { if(newClient) client = newClient; //Got new client :) else this.clients.push(client); + + if(!this._show_queries && client.properties.client_type == ClientType.CLIENT_QUERY) + client.tag.hide(); + client.channelTree = this; client["_channel"] = channel; @@ -709,4 +714,23 @@ class ChannelTree { } } } + + toggle_server_queries(flag: boolean) { + if(this._show_queries == flag) return; + this._show_queries = flag; + + //FIXME resize channels + const channels: ChannelEntry[] = [] + for(const client of this.clients) + if(client.properties.client_type == ClientType.CLIENT_QUERY) { + if(this._show_queries) + client.tag.show(); + else + client.tag.hide(); + if(channels.indexOf(client.currentChannel()) == -1) + channels.push(client.currentChannel()); + } + for(const channel of channels) + channel.adjustSize(); + } } \ No newline at end of file diff --git a/todo.md b/todo.md index 0982929c..eb905562 100644 --- a/todo.md +++ b/todo.md @@ -1 +1,2 @@ -nan \ No newline at end of file +Add connect profiles for bookmarks, like TeaSpeak-Forum or multiple TeamSpeak Identities +Fix server group management dialog for a lots of groups (May even add a search function?) \ No newline at end of file diff --git a/tools/dtsgen/declarator.ts b/tools/dtsgen/declarator.ts index 9f381814..6b05dfc4 100644 --- a/tools/dtsgen/declarator.ts +++ b/tools/dtsgen/declarator.ts @@ -321,11 +321,30 @@ generators[SyntaxKind.ClassDeclaration] = (settings, stack, node: ts.ClassDeclar return ts.createClassDeclaration(node.decorators, append_export(append_declare(node.modifiers, !stack.flag_declare), stack.flag_namespace), node.name, node.typeParameters, node.heritageClauses, members as any); }; +generators[SyntaxKind.PropertySignature] = (settings, stack, node: ts.PropertySignature) => { + console.log(SyntaxKind[node.type.kind]); + let type: ts.TypeNode = node.type; + switch (node.type.kind) { + case SyntaxKind.LiteralType: + type = ts.createIdentifier("any") as any; + } + + return ts.createPropertySignature(node.modifiers, node.name, node.questionToken, type, undefined); +}; + generators[SyntaxKind.InterfaceDeclaration] = (settings, stack, node: ts.InterfaceDeclaration) => { if(settings.remove_private.field && has_private(node.modifiers)) return; if(stack.flag_namespace && !has_modifier(node.modifiers, SyntaxKind.ExportKeyword)) return; - return node; + const members: any[] = []; + for(const member of node.members) { + if(generators[member.kind]) + members.push(generators[member.kind](settings, stack, member)); + else + members.push(member); + } + + return ts.createInterfaceDeclaration(undefined, append_export(append_declare(node.modifiers, !stack.flag_declare), stack.flag_namespace), node.name, node.typeParameters, node.heritageClauses, members); }; generators[SyntaxKind.VariableDeclaration] = (settings, stack, node: ts.VariableDeclaration) => { diff --git a/tools/dtsgen/test/test_03.ts b/tools/dtsgen/test/test_03.ts new file mode 100644 index 00000000..d41ecbfd --- /dev/null +++ b/tools/dtsgen/test/test_03.ts @@ -0,0 +1,9 @@ +enum YY { + H = "C", + B = "Y" +} + +interface X { + type: "", + c: YY.B +} \ No newline at end of file diff --git a/vendor/bbcode b/vendor/bbcode index 7b931ed6..0221bd13 160000 --- a/vendor/bbcode +++ b/vendor/bbcode @@ -1 +1 @@ -Subproject commit 7b931ed61cf265937dc742579f9070e7c4e50775 +Subproject commit 0221bd137ef5bbc846018ff86deda0aca38aed26