From 7a39330a6f304439894d41abb0bab6de45fbc9b0 Mon Sep 17 00:00:00 2001 From: Samuel ORTION Date: Thu, 18 Aug 2022 16:32:29 +0200 Subject: [PATCH] i18n: Add i18n switch and fr translation --- www/TODO | 4 +- www/assets/styles/app.css | 89 ++++++-- www/composer.json | 1 + www/composer.lock | 74 ++++++- .../secrets/dev/dev.decrypt.private.php | 4 + www/config/secrets/dev/dev.encrypt.public.php | 3 + .../secrets/prod/prod.encrypt.public.php | 3 + www/config/services.yaml | 2 + www/src/Controller/AuthController.php | 2 + www/src/Controller/HomeController.php | 4 +- www/src/Controller/LogsController.php | 1 + www/src/Controller/RecordsController.php | 14 +- www/src/Controller/ServicesController.php | 6 +- www/src/Controller/SpectroController.php | 3 +- www/src/Controller/StatsController.php | 22 ++ www/src/Controller/TodayController.php | 8 +- www/templates/base.html.twig | 3 +- www/templates/menu.html.twig | 28 +-- www/templates/records/best.html.twig | 1 + www/templates/stats/index.html.twig | 3 + www/templates/utils/locale-switcher.html.twig | 28 +++ www/templates/utils/nav-item.html.twig | 4 +- www/translations/messages+intl-icu.fr.xlf | 202 +++++++++++++++++- 23 files changed, 458 insertions(+), 51 deletions(-) create mode 100644 www/config/secrets/dev/dev.decrypt.private.php create mode 100644 www/config/secrets/dev/dev.encrypt.public.php create mode 100644 www/config/secrets/prod/prod.encrypt.public.php create mode 100644 www/src/Controller/StatsController.php create mode 100644 www/templates/records/best.html.twig create mode 100644 www/templates/stats/index.html.twig create mode 100644 www/templates/utils/locale-switcher.html.twig diff --git a/www/TODO b/www/TODO index cd6807f..48fddd4 100644 --- a/www/TODO +++ b/www/TODO @@ -1 +1,3 @@ -- i18n switch +- i18n switch (to keep on same page) +- /records pagination +- /records ajax bird contacts retrieval diff --git a/www/assets/styles/app.css b/www/assets/styles/app.css index 9008bd3..7f2b885 100644 --- a/www/assets/styles/app.css +++ b/www/assets/styles/app.css @@ -65,7 +65,9 @@ a { z-index: 1; } -.button, input[type=submit], input[type=button] { +.button, +input[type=submit], +input[type=button] { background-color: #f1f1f1; color: black; border-radius: 5px; @@ -97,7 +99,8 @@ a { word-wrap: break-word; } -.button:hover, input:hover { +.button:hover, +input:hover { background-color: #F3F4F6; text-decoration: none; transition-duration: 0.1s; @@ -111,30 +114,30 @@ a { } .button-main:focus:not(:focus-visible):not(.focus-visible) { - box-shadow: none; - outline: none; + box-shadow: none; + outline: none; } .button-main:hover { - background-color: #2c974b; - padding: 6px 16px; + background-color: #2c974b; + padding: 6px 16px; } .button-main:focus { - box-shadow: rgba(46, 164, 79, .4) 0 0 0 3px; - outline: none; + box-shadow: rgba(46, 164, 79, .4) 0 0 0 3px; + outline: none; } .button-main:disabled { - background-color: #94d3a2; - border-color: rgba(27, 31, 35, .1); - color: rgba(255, 255, 255, .8); - cursor: default; + background-color: #94d3a2; + border-color: rgba(27, 31, 35, .1); + color: rgba(255, 255, 255, .8); + cursor: default; } .button-main:active { - background-color: #298e46; - box-shadow: rgba(20, 70, 32, .2) 0 1px 0 inset; + background-color: #298e46; + box-shadow: rgba(20, 70, 32, .2) 0 1px 0 inset; } .button-main:active { @@ -143,7 +146,8 @@ a { transition: none 0s; } -.button:focus, input:focus { +.button:focus, +input:focus { outline: 1px transparent; border: solid blue 1px; } @@ -179,7 +183,7 @@ header img.logo { } main { - min-height: calc(100vh - (var(--header-height, 4em) + var(--footer-height, 4em)) ); + min-height: calc(100vh - (var(--header-height, 4em) + var(--footer-height, 4em))); padding: 5em; z-index: 0; } @@ -201,11 +205,16 @@ footer a:hover { font-style: italic; } -li, td { +li { + list-style: none; +} + +li, +td { align-items: center; } -/* .dropdown-button:hover { +.dropdown-toggle:hover { background-color: #900; color: white } @@ -213,22 +222,58 @@ li, td { .dropdown:hover .dropdown-content { display: block; } -*/ .dropdown-content { display: none; - position: absolute; - min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); z-index: 1; + width: fit-content; } + canvas { display: block; height: 100%; width: 100%; } +.language-selector { + position: absolute; + top: 0; + right: 0; +} + +.language-selector .toggle { + position: absolute; + top: 0; + right: 0; + opacity: 0; + width: 5em; + height: 1em; +} + +.touch { + cursor: pointer; +} + +.touch::after { + content: "+"; +} + +.slide { + clear: both; + width: 100%; + height: 0px; + overflow: hidden; + text-align: center; + transition: height .4s ease; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); +} + +.toggle:checked + .slide { + height: fit-content; +} + .scientific-name { font-style: italic; } @@ -294,4 +339,4 @@ canvas { main { padding: 1em; } -} +} \ No newline at end of file diff --git a/www/composer.json b/www/composer.json index 3088255..1037790 100644 --- a/www/composer.json +++ b/www/composer.json @@ -23,6 +23,7 @@ "symfony/yaml": "6.1.*", "twig/extra-bundle": "^2.12|^3.0", "twig/intl-extra": "^3.4", + "twig/string-extra": "^3.4", "twig/twig": "^2.12|^3.0" }, "config": { diff --git a/www/composer.lock b/www/composer.lock index 13c77da..3aded40 100644 --- a/www/composer.lock +++ b/www/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "96fd1b588fa4ee292b8be380f2260cc1", + "content-hash": "5ee7614277dd908e6485a36c9b423ace", "packages": [ { "name": "doctrine/annotations", @@ -4951,6 +4951,78 @@ ], "time": "2022-06-10T08:33:05+00:00" }, + { + "name": "twig/string-extra", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/string-extra.git", + "reference": "03608ae2e9c270a961e8cf1b75751e8635ad3e3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/string-extra/zipball/03608ae2e9c270a961e8cf1b75751e8635ad3e3c", + "reference": "03608ae2e9c270a961e8cf1b75751e8635ad3e3c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/string": "^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^2.7|^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\Extra\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Symfony String", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "string", + "twig", + "unicode" + ], + "support": { + "source": "https://github.com/twigphp/string-extra/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2022-01-02T10:02:25+00:00" + }, { "name": "twig/twig", "version": "v3.4.2", diff --git a/www/config/secrets/dev/dev.decrypt.private.php b/www/config/secrets/dev/dev.decrypt.private.php new file mode 100644 index 0000000..61ce849 --- /dev/null +++ b/www/config/secrets/dev/dev.decrypt.private.php @@ -0,0 +1,4 @@ +}/auth", name="auth_i18n") */ public function index(Connection $connection) { @@ -21,6 +22,7 @@ class AuthController extends AbstractController /** * @Route("/auth/login", name="login") + * @Route("/{_locale<%app.supported_locales%>}/auth/login", name="login_i18n") */ public function login() { diff --git a/www/src/Controller/HomeController.php b/www/src/Controller/HomeController.php index 7ececa9..ccf9c63 100644 --- a/www/src/Controller/HomeController.php +++ b/www/src/Controller/HomeController.php @@ -12,7 +12,8 @@ class HomeController extends AbstractController private Connection $connection; /** - * @Route("/", name="home") + * @Route("", name="home") + * @Route("/{_locale<%app.supported_locales%>}/", name="home_i18n") */ public function index(Connection $connection) { @@ -25,6 +26,7 @@ class HomeController extends AbstractController /** * @Route("/about", name="about") + * @Route("/{_locale<%app.supported_locales%>}/about", name="about_i18n") */ public function about() { diff --git a/www/src/Controller/LogsController.php b/www/src/Controller/LogsController.php index a17d7b1..4ce5e2f 100644 --- a/www/src/Controller/LogsController.php +++ b/www/src/Controller/LogsController.php @@ -12,6 +12,7 @@ class LogsController extends AbstractController private $allowed_services = "recording analyzis miner plotter"; /** * @Route("/logs/{service}", name="logs") + * @Route("/{_locale<%app.supported_locales%>}/logs/{service}", name="logs_i18n") */ public function logs($service = "all") { diff --git a/www/src/Controller/RecordsController.php b/www/src/Controller/RecordsController.php index a827eaa..d6827c8 100644 --- a/www/src/Controller/RecordsController.php +++ b/www/src/Controller/RecordsController.php @@ -13,7 +13,8 @@ class RecordsController extends AbstractController private Connection $connection; /** - * @Route("/records/{date}", name="records_index") + * @Route("/records", name="records") + * @Route("/{_locale<%app.supported_locales%>}/records/{date}", name="records_i18n") */ public function records_index($date = "now") { @@ -30,6 +31,7 @@ class RecordsController extends AbstractController /** * @Route("/records/delete/{basename}", name="record_delete") + * @Route("/{_locale<%app.supported_locales%>}/records/delete/{basename}", name="record_delete_i18n") */ public function delete_record(Connection $connection, $basename) { @@ -38,6 +40,16 @@ class RecordsController extends AbstractController return $this->redirectToRoute('records_index'); } + /** + * @Route("/records/best", name="records_best") + * @Route("/{_locale<%app.supported_locales%>}/records/best", name="records_best_i18n") + */ + public function best_records(Connection $connection) + { + $this->render('records/best.html.twig', [ + ]); + } + private function list_records() { $records_path = $this->getParameter('app.records_dir') . "/out/*.wav"; diff --git a/www/src/Controller/ServicesController.php b/www/src/Controller/ServicesController.php index bebddec..a1bc070 100644 --- a/www/src/Controller/ServicesController.php +++ b/www/src/Controller/ServicesController.php @@ -14,7 +14,8 @@ class ServicesController extends AbstractController /** - * @Route("/services/status", name="service_status") + * @Route("/services/status", name="services_status") + * @Route("/{_locale<%app.supported_locales%>}/services/status", name="service_status_i18n") */ public function service_status() { $status = array_map(function($service) { @@ -29,7 +30,8 @@ class ServicesController extends AbstractController } /** - * @Route("/services/manage/{action}/{service}", name="service_manager") + * @Route("/services/manage/{action}/{service}", name="services_manager") + * @Route("/{_locale<%app.supported_locales%>}/services/manage/{action}/{service}", name="service_manager_i18n") */ public function service_manage($action, $service="all") { diff --git a/www/src/Controller/SpectroController.php b/www/src/Controller/SpectroController.php index d407893..a95dfac 100644 --- a/www/src/Controller/SpectroController.php +++ b/www/src/Controller/SpectroController.php @@ -10,8 +10,9 @@ class SpectroController extends AbstractController { /** * @Route("/spectro", name="spectro") + * @Route("/{_locale<%app.supported_locales%>}/spectro", name="spectro_i18n") */ - public function about() + public function spectro() { return $this->render('spectro/index.html.twig', [ diff --git a/www/src/Controller/StatsController.php b/www/src/Controller/StatsController.php new file mode 100644 index 0000000..a3d84af --- /dev/null +++ b/www/src/Controller/StatsController.php @@ -0,0 +1,22 @@ +}/stats", name="stats_i18n") + */ + public function index(Connection $connection) + { + return $this->render("stats/index.html.twig"); + } +} \ No newline at end of file diff --git a/www/src/Controller/TodayController.php b/www/src/Controller/TodayController.php index 2d1167f..452de59 100644 --- a/www/src/Controller/TodayController.php +++ b/www/src/Controller/TodayController.php @@ -14,6 +14,7 @@ class TodayController extends AbstractController /** * @Route("/today", name="today") + * @Route("/{_locale<%app.supported_locales%>}/today", name="today_i18n") */ public function today(Connection $connection) { @@ -22,6 +23,7 @@ class TodayController extends AbstractController /** * @Route("/today/species", name="today_species") + * @Route("/{_locale<%app.supported_locales%>}/today/species", name="today_species_i18n") */ public function today_species_page(Connection $connection) { @@ -35,6 +37,7 @@ class TodayController extends AbstractController /** * @Route("/today/species/{id}", name="today_species_id") + * @Route("/{_locale<%app.supported_locales%>}/today/species/{id}", name="today_species_id_i18n") */ public function today_species_by_id(Connection $connection, $id) { @@ -49,6 +52,7 @@ class TodayController extends AbstractController /** * @Route("/today/{date}", name="today_date") + * @Route("/{_locale<%app.supported_locales%>}/today/{date}", name="today_date_i18n") */ public function today_date(Connection $connection, $date) { @@ -57,6 +61,7 @@ class TodayController extends AbstractController /** * @Route("/today/{date}/species", name="today_species_date") + * @Route("/{_locale<%app.supported_locales%>}/today/{date}/species", name="today_species_date_i18n") */ public function today_species_by_date(Connection $connection, $date) { @@ -69,6 +74,7 @@ class TodayController extends AbstractController /** * @Route("/today/{date}/species/{id}", name="today_species_id_and_date") + * @Route("/{_locale<%app.supported_locales%>}/today/{date}/species/{id}", name="today_species_id_and_date_i18n") */ public function today_species_by_id_and_date(Connection $connection, $date, $id) { @@ -141,4 +147,4 @@ class TodayController extends AbstractController $result = $stmt->executeQuery(); return $result->fetchAllAssociative(); } -} +} \ No newline at end of file diff --git a/www/templates/base.html.twig b/www/templates/base.html.twig index ee10174..efb0f29 100644 --- a/www/templates/base.html.twig +++ b/www/templates/base.html.twig @@ -1,5 +1,5 @@ - + @@ -27,6 +27,7 @@ </div> </a> </header> + {% include "utils/locale-switcher.html.twig" %} <main> {% block content %} <p>Welcome to BirdNET-stream !</p> diff --git a/www/templates/menu.html.twig b/www/templates/menu.html.twig index 39e2398..9b6c0f0 100644 --- a/www/templates/menu.html.twig +++ b/www/templates/menu.html.twig @@ -9,57 +9,51 @@ {% include 'utils/nav-item.html.twig' with { route: 'home', url: '/', - name: 'Home'|trans + text: 'Home'|trans } %} {% include 'utils/nav-item.html.twig' with { route: 'about', url: '/about', - name: 'About'|trans + text: 'About'|trans } %} {% include 'utils/nav-item.html.twig' with { route: 'today', url: '/today', - name: "Today's Detections"|trans + text: "Today's Detections"|trans } %} {% include 'utils/nav-item.html.twig' with { route: 'spectro', url: '/spectro', - name: 'Live Spectrogram'|trans + text: 'Live Spectrogram'|trans } %} {% include 'utils/nav-item.html.twig' with { route: 'stats', url: '/stats', - name: 'Statistics'|trans + text: 'Statistics'|trans } %} <li class="dropdown"> {% include 'utils/nav-item.html.twig' with { route: 'records', url: '/records', - name: 'Recordings'|trans + text: 'Recordings'|trans } %} <ul class="dropdown-content"> {% include 'utils/nav-item.html.twig' with { - route: 'bests', + route: 'records_best', url: '/records/best', - name: 'Best Recordings'|trans + text: 'Best Recordings'|trans } %} </ul> </li> - {% include 'utils/nav-item.html.twig' with { - route: 'bests', - url: '/records/best', - name: 'Best Recordings'|trans - } %} - {% include 'utils/nav-item.html.twig' with { route: 'logs', url: '/logs', - name: 'View Logs'|trans + text: 'View Logs'|trans } %} {% include 'utils/nav-item.html.twig' with { - route: 'status', + route: 'services_status', url: '/services/status', - name: 'Status'|trans + text: 'Status'|trans } %} </ul> </div> diff --git a/www/templates/records/best.html.twig b/www/templates/records/best.html.twig new file mode 100644 index 0000000..4cda376 --- /dev/null +++ b/www/templates/records/best.html.twig @@ -0,0 +1 @@ +<p>{{ "Not implemented yet"|trans </p> \ No newline at end of file diff --git a/www/templates/stats/index.html.twig b/www/templates/stats/index.html.twig new file mode 100644 index 0000000..8b8a65d --- /dev/null +++ b/www/templates/stats/index.html.twig @@ -0,0 +1,3 @@ +{% block content %} + {{ "Not implemented yet"|trans }} +{% endblock %} \ No newline at end of file diff --git a/www/templates/utils/locale-switcher.html.twig b/www/templates/utils/locale-switcher.html.twig new file mode 100644 index 0000000..2c9acd3 --- /dev/null +++ b/www/templates/utils/locale-switcher.html.twig @@ -0,0 +1,28 @@ +{% set current = app.request.get('_route')|replace({"_i18n": ""})~"_i18n" %} +{% macro lang_url(route, locale, name) %} + <a class="language-item" + href="{{ + path( + route, + { + _locale: locale + } + ) + }}">{{ name }} + </a> +{% endmacro %} +<li class="language-selector"> + <label for="toggle" class="touch" + role="button"> + {{ app.request.locale|locale_name(app.request.locale)|u.title }} + </label> + <input type="checkbox" name="dropdown-checkbox" class="toggle"> + <ul class="slide"> + <li> + {{ _self.lang_url(current, "en", "English") }} + </li> + <li> + {{ _self.lang_url(current, "fr", "Français") }} + </li> + </ul> +</li> \ No newline at end of file diff --git a/www/templates/utils/nav-item.html.twig b/www/templates/utils/nav-item.html.twig index 1b0f8e7..6c3d539 100644 --- a/www/templates/utils/nav-item.html.twig +++ b/www/templates/utils/nav-item.html.twig @@ -1,6 +1,6 @@ <li> <a class="{{ app.request.get('_route') matches '{' ~ route ~ '_*}' ? 'active' }}" - href="{{ url }}"> - {{ name }} + href="{{ path(route) }}"> + {{ text }} </a> </li> diff --git a/www/translations/messages+intl-icu.fr.xlf b/www/translations/messages+intl-icu.fr.xlf index 6491285..3536ef7 100644 --- a/www/translations/messages+intl-icu.fr.xlf +++ b/www/translations/messages+intl-icu.fr.xlf @@ -23,7 +23,7 @@ </trans-unit> <trans-unit id="1zd9FJ_" resname="This project is made with &hearts; by Samuel ORTION."> <source>This project is made with &hearts; by Samuel ORTION.</source> - <target><![CDATA[Ce projet est fait avec ♥ par Samuel ORTION]]></target> + <target>Ce projet est fait avec &hearts; par Samuel ORTION.</target> </trans-unit> <trans-unit id="wBHWCXv" resname="License"> <source>License</source> @@ -37,6 +37,206 @@ <source>BirdNET-Analyzer, on which this project relies, is licensed under</source> <target>BirdNET-Analyzer, sur qui ce projet repose, est sous licence</target> </trans-unit> + <trans-unit id="39aF.j_" resname="Frequency charts"> + <source>Frequency charts</source> + <target>Graphe de fréquence</target> + </trans-unit> + <trans-unit id="SJtk1Aa" resname="No charts available"> + <source>No charts available</source> + <target>Pas de graphe disponible</target> + </trans-unit> + <trans-unit id="OnhpU4i" resname="Home"> + <source>Home</source> + <target>Accueil</target> + </trans-unit> + <trans-unit id="Lc4yjyd" resname="Today's Detections"> + <source>Today's Detections</source> + <target>Détection aujourd'hui</target> + </trans-unit> + <trans-unit id="11.I7BE" resname="Live Spectrogram"> + <source>Live Spectrogram</source> + <target>Spectrogramme en temps réel</target> + </trans-unit> + <trans-unit id="plYQfpn" resname="Statistics"> + <source>Statistics</source> + <target>Statistiques</target> + </trans-unit> + <trans-unit id="YMVtZqE" resname="Recordings"> + <source>Recordings</source> + <target>Enregistrements</target> + </trans-unit> + <trans-unit id="GiWvoLV" resname="Best Recordings"> + <source>Best Recordings</source> + <target>Meilleurs Enregistrements</target> + </trans-unit> + <trans-unit id="4IfwRtw" resname="View Logs"> + <source>View Logs</source> + <target>Voir les logs</target> + </trans-unit> + <trans-unit id="kg5BPH1" resname="Status"> + <source>Status</source> + <target>Status</target> + </trans-unit> + <trans-unit id="mcQKtAW" resname="Date"> + <source>Date</source> + <target>Date</target> + </trans-unit> + <trans-unit id="zFIH2Rd" resname="Enter a date in this format YYYY-MM-DD"> + <source>Enter a date in this format YYYY-MM-DD</source> + <target>Entrez une date au format YYYY-MM-DD</target> + </trans-unit> + <trans-unit id="bMhRm5F" resname="Go"> + <source>Go</source> + <target>Go</target> + </trans-unit> + <trans-unit id="6iEA3Im" resname="Logs"> + <source>Logs</source> + <target>Logs</target> + </trans-unit> + <trans-unit id="yIMayeg" resname="No logs available"> + <source>No logs available</source> + <target>Pas de logs disponible</target> + </trans-unit> + <trans-unit id="_RN0U0G" resname="Spectrogram"> + <source>Spectrogram</source> + <target>Spectrogramme</target> + </trans-unit> + <trans-unit id="cOE9kUb" resname="Launch Live Spectrogram"> + <source>Launch Live Spectrogram</source> + <target>Lancer le Spectrogramme en temps réel</target> + </trans-unit> + <trans-unit id="VF2TvYg" resname="No audio sources yet"> + <source>No audio sources yet</source> + <target>Pas de source audio pour le moment</target> + </trans-unit> + <trans-unit id="b3yIXEw" resname="Quick Stats"> + <source>Quick Stats</source> + <target>Statistiques rapides</target> + </trans-unit> + <trans-unit id="EVOzKuV" resname="Most recorded species"> + <source>Most recorded species</source> + <target>Espèce la plus souvent enregistrée</target> + </trans-unit> + <trans-unit id="BpW1Y6z" resname="with"> + <source>with</source> + <target>avec</target> + </trans-unit> + <trans-unit id="qlr0CE8" resname="contacts"> + <source>contacts</source> + <target>contacts</target> + </trans-unit> + <trans-unit id="pHx6Sb8" resname="No species in database."> + <source>No species in database.</source> + <target>Aucune espèce dans la base de donnée.</target> + </trans-unit> + <trans-unit id="kbbkF9." resname="Last detected species"> + <source>Last detected species</source> + <target>Dernière espèce détectée</target> + </trans-unit> + <trans-unit id="UfL722." resname="AI confidence"> + <source>AI confidence</source> + <target>Niveau de confiance</target> + </trans-unit> + <trans-unit id="4PT3Z6y" resname="today"> + <source>today</source> + <target>aujourd'hui</target> + </trans-unit> + <trans-unit id="uNMehSc" resname="on"> + <source>on</source> + <target>le</target> + </trans-unit> + <trans-unit id="UXnWgGD" resname="No species in database"> + <source>No species in database</source> + <target>Aucune espèce dans la base de données</target> + </trans-unit> + <trans-unit id="O.a8OVk" resname="Today's contacts for"> + <source>Today's contacts for</source> + <target>Contacts d'aujourd'hui pour</target> + </trans-unit> + <trans-unit id="1XvGb5I" resname="Contacts on"> + <source>Contacts on</source> + <target>Contacts le</target> + </trans-unit> + <trans-unit id="EMIrz0x" resname="for"> + <source>for</source> + <target>pour</target> + </trans-unit> + <trans-unit id="tKc2V0D" resname="Stats"> + <source>Stats</source> + <target>Stats</target> + </trans-unit> + <trans-unit id="j93Wzbi" resname="Contact count:"> + <source>Contact count:</source> + <target>Nombre de contact:</target> + </trans-unit> + <trans-unit id="ALgyC6W" resname="Max confidence"> + <source>Max confidence</source> + <target>Niveau de confiance maximal</target> + </trans-unit> + <trans-unit id="WTXVn0G" resname="Contact records"> + <source>Contact records</source> + <target>Enregistrement du contact</target> + </trans-unit> + <trans-unit id="lxuXQjW" resname="Filename"> + <source>Filename</source> + <target>Fichier</target> + </trans-unit> + <trans-unit id="M7k0ds9" resname="Time"> + <source>Time</source> + <target>Heure</target> + </trans-unit> + <trans-unit id="ZCLj549" resname="Confidence"> + <source>Confidence</source> + <target>Confiance</target> + </trans-unit> + <trans-unit id="vBuIkH0" resname="Audio"> + <source>Audio</source> + <target>Audio</target> + </trans-unit> + <trans-unit id="I3eaEC5" resname="Select this record"> + <source>Select this record</source> + <target>Sélectionner cet enregistrement</target> + </trans-unit> + <trans-unit id="NBh3Jvf" resname="Download audio file"> + <source>Download audio file</source> + <target>Télécharger le fichier audio</target> + </trans-unit> + <trans-unit id="gp0zWYd" resname="No records this day for this species"> + <source>No records this day for this species</source> + <target>Pas d'entrement ce jour pour cette espèce</target> + </trans-unit> + <trans-unit id="H8mjh2V" resname="Select all"> + <source>Select all</source> + <target>Séléctionner tout</target> + </trans-unit> + <trans-unit id="0qtdRv7" resname="Delete selected"> + <source>Delete selected</source> + <target>Supprimer la sélection</target> + </trans-unit> + <trans-unit id="XdPmSCB" resname="Today's detected species"> + <source>Today's detected species</source> + <target>Espèces détectées aujourd'hui</target> + </trans-unit> + <trans-unit id="ujkYinn" resname="Detected species on"> + <source>Detected species on</source> + <target>Espèces détectées le</target> + </trans-unit> + <trans-unit id="tZEkkkO" resname="No species detected this day"> + <source>No species detected this day</source> + <target>Aucune espèce détectée ce jour</target> + </trans-unit> + <trans-unit id="AmHJKRh" resname="Services status"> + <source>Services status</source> + <target>Status des services</target> + </trans-unit> + <trans-unit id="hb1Ij3K" resname="No status available"> + <source>No status available</source> + <target>Aucun status disponible</target> + </trans-unit> + <trans-unit id="OzJoKh6" resname="Welcome to BirdNET-stream !"> + <source>Welcome to BirdNET-stream !</source> + <target>Bienvenue sur BirdNET-stream !</target> + </trans-unit> </body> </file> </xliff>