i18n: Add i18n switch and fr translation

This commit is contained in:
Samuel Ortion 2022-08-18 16:32:29 +02:00
parent 2516efa805
commit 7a39330a6f
23 changed files with 458 additions and 51 deletions

View File

@ -1 +1,3 @@
- i18n switch
- i18n switch (to keep on same page)
- /records pagination
- /records ajax bird contacts retrieval

View File

@ -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;
}
}
}

View File

@ -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": {

74
www/composer.lock generated
View File

@ -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",

View File

@ -0,0 +1,4 @@
<?php // dev.decrypt.private on Thu, 18 Aug 2022 11:24:08 +0000
// SYMFONY_DECRYPTION_SECRET=Wa3L9epQCNyArZ9uGqChJro5t3n97TKgwVyoqmHHZnCtzcDIHxxI6HD6Vd1HRW46d+I4Pc3aPKh6NMIJ309TeQ==
return "Y\xAD\xCB\xF5\xEAP\x08\xDC\x80\xAD\x9Fn\x1A\xA0\xA1\x26\xBA9\xB7y\xFD\xED2\xA0\xC1\x5C\xA8\xAAa\xC7fp\xAD\xCD\xC0\xC8\x1F\x1CH\xE8p\xFAU\xDDGEn\x3Aw\xE28\x3D\xCD\xDA\x3C\xA8z4\xC2\x09\xDFOSy";

View File

@ -0,0 +1,3 @@
<?php // dev.encrypt.public on Thu, 18 Aug 2022 11:24:08 +0000
return "\xAD\xCD\xC0\xC8\x1F\x1CH\xE8p\xFAU\xDDGEn\x3Aw\xE28\x3D\xCD\xDA\x3C\xA8z4\xC2\x09\xDFOSy";

View File

@ -0,0 +1,3 @@
<?php // prod.encrypt.public on Thu, 18 Aug 2022 11:25:20 +0000
return "\xFBm\xD4\xAF\xF1b\x88\x93\x96\xC3\x9DJSJ\x0A\xDB\xA0\xF6\xBCp\x98\x96\xC7\xA6\xD2\x82\x28\x28y3\xCF\x11";

View File

@ -5,6 +5,8 @@
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
app.records_dir: '%env(resolve:RECORDS_DIR)%'
app.supported_locales: 'en|fr'
app.default_locale: 'en'
services:
# default configuration for services in *this* file

View File

@ -13,6 +13,7 @@ class AuthController extends AbstractController
/**
* @Route("/auth", name="auth")
* @Route("/{_locale<%app.supported_locales%>}/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()
{

View File

@ -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()
{

View File

@ -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")
{

View File

@ -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";

View File

@ -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")
{

View File

@ -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', [

View File

@ -0,0 +1,22 @@
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Doctrine\DBAL\Connection;
class StatsController extends AbstractController
{
private Connection $connection;
/**
* @Route("/stats", name="stats")
* @Route("/{_locale<%app.supported_locales%>}/stats", name="stats_i18n")
*/
public function index(Connection $connection)
{
return $this->render("stats/index.html.twig");
}
}

View File

@ -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();
}
}
}

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ app.request.locale }}">
<head>
<meta charset="UTF-8">
<title>
@ -27,6 +27,7 @@
</div>
</a>
</header>
{% include "utils/locale-switcher.html.twig" %}
<main>
{% block content %}
<p>Welcome to BirdNET-stream !</p>

View File

@ -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>

View File

@ -0,0 +1 @@
<p>{{ "Not implemented yet"|trans </p>

View File

@ -0,0 +1,3 @@
{% block content %}
{{ "Not implemented yet"|trans }}
{% endblock %}

View File

@ -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>

View File

@ -1,6 +1,6 @@
<li>
<a class="{{ app.request.get('_route') matches '{' ~ route ~ '_*}' ? 'active' }}"
href="{{ url }}">
{{ name }}
href="{{ path(route) }}">
{{ text }}
</a>
</li>

View File

@ -23,7 +23,7 @@
</trans-unit>
<trans-unit id="1zd9FJ_" resname="This project is made with &amp;hearts; by Samuel ORTION.">
<source>This project is made with &amp;hearts; by Samuel ORTION.</source>
<target><![CDATA[Ce projet est fait avec &hearts; par Samuel ORTION]]></target>
<target>Ce projet est fait avec &amp;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>