Browse Source

wip i18n l10n

master
vincent 1 day ago
parent
commit
041b89652e
27 changed files with 416 additions and 189 deletions
  1. +1
    -0
      composer.json
  2. +10
    -10
      composer.lock
  3. +4
    -4
      src/Controller/BadgeController.php
  4. +26
    -21
      src/Controller/ProjectController.php
  5. +43
    -42
      src/Controller/TaskController.php
  6. +6
    -2
      src/Controller/ToolsController.php
  7. +2
    -1
      src/Form/CityToolType.php
  8. +3
    -3
      src/Form/CsvType.php
  9. +7
    -7
      src/Form/OsmoseToolType.php
  10. +15
    -11
      src/Form/ProjectType.php
  11. +11
    -11
      src/Form/TaskType.php
  12. +5
    -1
      templates/comment/cancel.md.twig
  13. +5
    -1
      templates/comment/finish.md.twig
  14. +5
    -1
      templates/comment/reset.md.twig
  15. +1
    -1
      templates/comment/start.md.twig
  16. +1
    -1
      templates/macro.html.twig
  17. +1
    -1
      templates/partials/_task-title.html.twig
  18. +3
    -3
      templates/project/create.html.twig
  19. +5
    -5
      templates/project/index.html.twig
  20. +29
    -29
      templates/project/show.html.twig
  21. +2
    -2
      templates/project/update.html.twig
  22. +4
    -4
      templates/task/create.html.twig
  23. +26
    -22
      templates/task/show.html.twig
  24. +2
    -2
      templates/task/update.html.twig
  25. +2
    -2
      templates/tools/base.html.twig
  26. +79
    -0
      translations/messages.en.yaml
  27. +118
    -2
      translations/messages.fr.yaml

+ 1
- 0
composer.json View File

@ -47,6 +47,7 @@
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.6",
"friendsofphp/php-cs-fixer": "^3.69",
"nikic/php-parser": "^5.5",
"symfony/debug-bundle": "7.3.*",
"symfony/maker-bundle": "^1.60",
"symfony/stopwatch": "7.3.*",


+ 10
- 10
composer.lock 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": "d035db9a327bfae49b87a5a0e08c5198",
"content-hash": "985698a147f2debf556533cf94d2217d",
"packages": [
{
"name": "behat/transliterator",
@ -8974,16 +8974,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.81.0",
"version": "v3.82.2",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "e00a39c729aced6c3771b9530c6e58c2efa87592"
"reference": "684ed3ab41008a2a4848de8bde17eb168c596247"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/e00a39c729aced6c3771b9530c6e58c2efa87592",
"reference": "e00a39c729aced6c3771b9530c6e58c2efa87592",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/684ed3ab41008a2a4848de8bde17eb168c596247",
"reference": "684ed3ab41008a2a4848de8bde17eb168c596247",
"shasum": ""
},
"require": {
@ -9067,7 +9067,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.81.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.82.2"
},
"funding": [
{
@ -9075,7 +9075,7 @@
"type": "github"
}
],
"time": "2025-07-07T15:27:54+00:00"
"time": "2025-07-08T21:13:15+00:00"
},
{
"name": "nikic/php-parser",
@ -10041,7 +10041,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
@ -10049,6 +10049,6 @@
"ext-ctype": "*",
"ext-iconv": "*"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

+ 4
- 4
src/Controller/BadgeController.php View File

@ -16,7 +16,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
#[Route('/badge')]
class BadgeController extends AbstractController
{
#[Route('/project-status/{projectSlug}/{status}', name: 'app_badge_project_status')]
#[Route('/project-status/{projectSlug}/{status}/{_locale}', name: 'app_badge_project_status', requirements: ['_locale' => '(fr|en)*'])]
public function index(
EntityManagerInterface $entityManager,
TaskLifecycleManager $taskLifecycleManager,
@ -54,9 +54,9 @@ class BadgeController extends AbstractController
$xml = new \DOMDocument();
$left = $translator->trans($stats['title']).' ('.$request->getLocale().')';
$left = $translator->trans($stats['title'], locale: $request->getLocale()).' ';
$leftColor = $colors['secondary'];
$right = sprintf(' % 3.0f%%', $stats['percentage']);
$right = sprintf('% 3.0f%%', $stats['percentage']);
$rightColor = $colors[$stats['color']];
$charWidth = 7;
@ -66,7 +66,7 @@ class BadgeController extends AbstractController
$verticalMargin = round(($totalHeight - $fontSize) / 2);
$horizontalMargin = round($fontSize / 3);
$totalWidth = round(strlen($left.$right) * $charWidth + 2 * $horizontalMargin);
$middle = floor(strlen($left) * $charWidth);
$middle = round(strlen($left) * $charWidth);
$svg = $xml->createElement('svg');
$svg->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns', 'http://www.w3.org/2000/svg');


+ 26
- 21
src/Controller/ProjectController.php View File

@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface;
#[Route('/project')]
class ProjectController extends AbstractController
@ -33,12 +34,12 @@ class ProjectController extends AbstractController
// Formulaire de création d’un projet
#[Route('/create', name: 'app_project_create')]
#[IsGranted('ROLE_USER')]
public function create(Request $request, EntityManagerInterface $entityManager): Response
public function create(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator): Response
{
$project = new Project();
$createForm = $this->createForm(ProjectType::class, $project);
$createForm->add('submit', SubmitType::class, [
'label' => 'Créer',
'label' => 'button.create_project',
]);
$createForm->handleRequest($request);
@ -50,11 +51,11 @@ class ProjectController extends AbstractController
$entityManager->persist($project);
$entityManager->flush();
$this->addFlash('success', 'Projet créé !');
$this->addFlash('success', $translator->trans('alert.project_created'));
return $this->redirectToRoute('app_project');
} catch (\Exception $exception) {
$this->addFlash('danger', 'Impossible de créer le projet !');
$this->addFlash('danger', $translator->trans('alert.project_not_created'));
}
}
@ -65,11 +66,11 @@ class ProjectController extends AbstractController
// Page d’un prtojet donné (où l’on voit ses tâches)
#[Route('/{slug}', name: 'app_project_show')]
public function show(EntityManagerInterface $entityManager, $slug): Response
public function show(EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$project = $entityManager->getRepository(Project::class)->findOneBySlug($slug);
if (!$project) {
$this->addFlash('warning', 'Projet non trouvé !');
$this->addFlash('warning', $translator->trans('alert.project_not_found'));
return $this->redirectToRoute('app_project');
}
@ -80,7 +81,9 @@ class ProjectController extends AbstractController
$csvForm = $this->createForm(CsvType::class, null, [
'action' => $this->generateUrl('app_project_import', ['slug' => $slug]),
]);
$csvForm->add('submit', SubmitType::class, ['label' => 'Importer']);
$csvForm->add('submit', SubmitType::class, [
'label' => 'button.import',
]);
$comments = $entityManager->getRepository(Comment::class)->findLatestByProject($project); // Commentaires agrégés de toutes les tâches du projet
@ -96,11 +99,11 @@ class ProjectController extends AbstractController
// Import de tâches dans un projet
#[Route('/{slug}/import', name: 'app_project_import')]
#[IsGranted('ROLE_USER')]
public function import(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function import(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$project = $entityManager->getRepository(Project::class)->findOneBySlug($slug);
if (!$project) {
$this->addFlash('warning', 'Projet non trouvé !');
$this->addFlash('warning', $translator->trans('alert.project_not_found'));
return $this->redirectToRoute('app_project');
}
@ -143,20 +146,20 @@ class ProjectController extends AbstractController
// Page de modification du projet
#[Route('/{slug}/update', name: 'app_project_update')]
#[IsGranted('ROLE_USER')]
public function update(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function update(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Project::class);
$project = $repository->findOneBySlug($slug);
if (!$project) {
$this->addFlash('warning', 'Projet non trouvé !');
$this->addFlash('warning', $translator->trans('alert.project_not_found'));
return $this->redirectToRoute('app_project');
}
$updateForm = $this->createForm(ProjectType::class, $project);
$updateForm->add('submit', SubmitType::class, [
'label' => 'Modifier',
'label' => 'button.update_project',
]);
$updateForm->handleRequest($request);
@ -167,11 +170,11 @@ class ProjectController extends AbstractController
$entityManager->persist($project);
$entityManager->flush();
$this->addFlash('success', 'Projet modifié !');
$this->addFlash('success', $translator->trans('alert.project_updated'));
return $this->redirectToRoute('app_project_show', ['slug' => $slug]);
} catch (\Exception $exception) {
$this->addFlash('danger', 'Impossible de mopdifier le projet !');
$this->addFlash('danger', $translator->trans('alert.project_not_updated'));
}
}
@ -184,13 +187,13 @@ class ProjectController extends AbstractController
// La suppression d’un projet passe par là
#[Route('/{slug}/remove', name: 'app_project_remove')]
#[IsGranted('ROLE_USER')]
public function remove(EntityManagerInterface $entityManager, $slug): Response
public function remove(EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Project::class);
$project = $repository->findOneBySlug($slug);
if (!$project) {
$this->addFlash('warning', 'Projet non trouvé !');
$this->addFlash('warning', $translator->trans('alert.project_not_found'));
return $this->redirectToRoute('app_project');
}
@ -199,9 +202,11 @@ class ProjectController extends AbstractController
$entityManager->remove($project);
$entityManager->flush();
$this->addFlash('success', $translator->trans('alert.project_removed'));
return $this->redirectToRoute('app_project');
} catch (\Exception $exception) {
$this->addFlash('danger', 'Impossible de supprimer le projet !');
$this->addFlash('danger', $translator->trans('alert.project_not_removed'));
}
return $this->redirectToRoute('app_project');
@ -210,25 +215,25 @@ class ProjectController extends AbstractController
// Effectue la requête Overpass liée au projet
#[Route('/{slug}/overpass', name: 'app_project_overpass')]
#[IsGranted('ROLE_USER')]
public function overpass(OverpassClient $overpassClient, EntityManagerInterface $entityManager, $slug): Response
public function overpass(OverpassClient $overpassClient, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Project::class);
$project = $repository->findOneBySlug($slug);
if (!$project) {
$this->addFlash('warning', 'Projet non trouvé !');
$this->addFlash('warning', $translator->trans('alert.project_not_found'));
return $this->redirectToRoute('app_project');
}
if (!$project->hasOverpassQuery()) {
$this->addFlash('warning', 'Ce projet n’a pas de requête Overpass !');
$this->addFlash('warning', $translator->trans('alert.no_overpass_query'));
return $this->redirectToRoute('app_project_show', ['slug' => $project->getSlug()]);
}
if ($project->hasOverpassResult() and !$project->isOverpassResultOutdated()) {
$this->addFlash('warning', 'Merci d’attendre un peu avant de requêter de nouveau Overpass !');
$this->addFlash('warning', $translator->trans('alert.overpass_please_wait'));
return $this->redirectToRoute('app_project_show', ['slug' => $project->getSlug()]);
}


+ 43
- 42
src/Controller/TaskController.php View File

@ -20,6 +20,7 @@ use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Workflow\WorkflowInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
#[Route('/task')]
class TaskController extends AbstractController
@ -27,10 +28,10 @@ class TaskController extends AbstractController
// Page de créatiom d’une tâche
#[Route('/create', name: 'app_task_create')]
#[IsGranted('ROLE_USER')]
public function create(Request $request, EntityManagerInterface $entityManager): Response
public function create(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator): Response
{
if (!$request->query->has('slug')) {
$this->addFlash('warning', 'Projet non spécifié !');
$this->addFlash('warning', $translator->trans('alert.missing_project'));
return $this->redirectToRoute('app_project');
}
@ -41,7 +42,7 @@ class TaskController extends AbstractController
$project = $repository->findOneBySlug($slug);
if (!$project) {
$this->addFlash('warning', 'Projet non trouvé !');
$this->addFlash('warning', $translator->trans('alert.project_not_found'));
return $this->redirectToRoute('app_project');
}
@ -50,7 +51,7 @@ class TaskController extends AbstractController
$task->setProject($project);
$createForm = $this->createForm(TaskType::class, $task);
$createForm->add('submit', SubmitType::class, [
'label' => 'Créer',
'label' => 'button.create',
]);
$createForm->handleRequest($request);
@ -65,11 +66,11 @@ class TaskController extends AbstractController
$entityManager->persist($task);
$entityManager->flush();
$this->addFlash('success', 'Tâche créée !');
$this->addFlash('success', $translator->trans('alert.task_created'));
return $this->redirectToRoute('app_project_show', ['slug' => $slug]);
} catch (\Exception $exception) {
$this->addFlash('danger', 'Impossible de créer la tâche !');
$this->addFlash('danger', $translator->trans('alert.task_not_created'));
}
}
@ -81,16 +82,16 @@ class TaskController extends AbstractController
// Page spécifique à une tâche, où l’on trouve tout ce qui la concerne
#[Route('/{slug}', name: 'app_task_show')]
public function show(Request $request, EntityManagerInterface $entityManager, GeoJsonManager $geoJsonManager, $slug): Response
public function show(Request $request, EntityManagerInterface $entityManager, GeoJsonManager $geoJsonManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
if (!$task) {
$this->addFlash('warning', 'Tâche non trouvée !');
$this->addFlash('warning', $translator->trans('alert.task_not_found'));
if (!$request->headers->has('Referer')) {
throw $this->createNotFoundException('Task not found');
throw $this->createNotFoundException($translator->trans('exception.task_not_found'));
}
return $this->redirect($request->headers->get('Referer'));
@ -104,7 +105,7 @@ class TaskController extends AbstractController
'action' => $this->generateUrl('app_task_comment', ['slug' => $slug]),
]);
$commentForm->add('submit', SubmitType::class, [
'label' => 'Commenter',
'label' => 'button.comment',
]);
// Programmation de la télécommande JOSM
@ -157,13 +158,13 @@ class TaskController extends AbstractController
// Ajoute un commentaire à la tâche
#[Route('/{slug}/comment', name: 'app_task_comment')]
#[IsGranted('ROLE_USER')]
public function comment(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function comment(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
if (!$task) {
$this->addFlash('warning', 'Tâche non trouvée !');
$this->addFlash('warning', $translator->trans('alert.task_not_found'));
return $this->redirect($request->headers->get('Referer'));
}
@ -173,7 +174,7 @@ class TaskController extends AbstractController
$comment->setTask($task);
$commentForm = $this->createForm(CommentType::class, $comment);
$commentForm->add('submit', SubmitType::class, [
'label' => 'Commenter',
'label' => 'button.comment',
]);
$commentForm->handleRequest($request);
@ -184,7 +185,7 @@ class TaskController extends AbstractController
$entityManager->persist($comment);
$entityManager->flush();
} catch (\Exception $exception) {
$this->addFlash('danger', 'Impossible de commenter ! '.$exception->getMessage());
$this->addFlash('danger', $translator->trans('alert.can_not_comment'));
}
}
@ -194,20 +195,20 @@ class TaskController extends AbstractController
// Modifie les informations d’une tâche
#[Route('/{slug}/update', name: 'app_task_update')]
#[IsGranted('ROLE_USER')]
public function update(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function update(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
if (!$task) {
$this->addFlash('warning', 'Tâche non trouvée !');
$this->addFlash('warning', $translator->trans('alert.task_not_found'));
return $this->redirect($request->headers->get('Referer'));
}
$updateForm = $this->createForm(TaskType::class, $task);
$updateForm->add('submit', SubmitType::class, [
'label' => 'Modifier',
'label' => 'button.update',
]);
$updateForm->handleRequest($request);
@ -220,7 +221,7 @@ class TaskController extends AbstractController
return $this->redirectToRoute('app_task_show', ['slug' => $slug]);
} catch (\Exception $exception) {
$this->addFlash('danger', 'Impossible de modifier la tâche !');
$this->addFlash('danger', $translator->trans('alert.task_not_updated'));
}
}
@ -234,13 +235,13 @@ class TaskController extends AbstractController
// Supprimer une tâche
#[Route('/{slug}/remove', name: 'app_task_remove')]
#[IsGranted('ROLE_USER')]
public function remove(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function remove(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
if (!$task) {
$this->addFlash('warning', 'Tâche non trouvée !');
$this->addFlash('warning', $translator->trans('alert.task_not_found'));
return $this->redirect($request->headers->get('Referer'));
}
@ -253,20 +254,20 @@ class TaskController extends AbstractController
return $this->redirectToRoute('app_project_show', ['slug' => $project->getSlug()]);
} catch (\Exception $exception) {
$this->addFlash('danger', 'Impossible de supprimer la tâche !');
$this->addFlash('danger', $translator->trans('alert.task_not_removed'));
}
return $this->redirectToRoute('app_project_show', ['slug' => $slug]);
}
// Passe une tâche d’un état à un autre
private function transition(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug, $transitionName): Response
private function transition(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug, $transitionName): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
if (!$task) {
$this->addFlash('warning', 'Tâche non trouvée !');
$this->addFlash('warning', $translator->trans('alert.task_not_found'));
return $this->redirectToRoute('app_project');
}
@ -278,7 +279,7 @@ class TaskController extends AbstractController
$entityManager->flush();
} catch (Exception $exception) {
$this->addFlash('warning', 'Impossible de modifier la tâche !');
$this->addFlash('warning', $translator->trans('exception.task_not_updated'));
}
return $this->redirectToRoute('app_task_show', ['slug' => $slug]);
@ -287,48 +288,48 @@ class TaskController extends AbstractController
// Commence une tâche
#[Route('/{slug}/start', name: 'app_task_start')]
#[IsGranted('ROLE_USER')]
public function start(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response
public function start(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_START);
return $this->transition($taskLifecycleStateMachine, $entityManager, $translator, $slug, Task::TRANSITION_START);
}
// Termine une tâche
#[Route('/{slug}/finish', name: 'app_task_finish')]
#[IsGranted('ROLE_USER')]
public function finish(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response
public function finish(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_FINISH);
return $this->transition($taskLifecycleStateMachine, $entityManager, $translator, $slug, Task::TRANSITION_FINISH);
}
// Abandonne une tâche
#[Route('/{slug}/cancel', name: 'app_task_cancel')]
#[IsGranted('ROLE_USER')]
public function cancel(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response
public function cancel(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_CANCEL);
return $this->transition($taskLifecycleStateMachine, $entityManager, $translator, $slug, Task::TRANSITION_CANCEL);
}
// Recommence une tâche
#[Route('/{slug}/reset', name: 'app_task_reset')]
#[IsGranted('ROLE_USER')]
public function reset(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, $slug): Response
public function reset(WorkflowInterface $taskLifecycleStateMachine, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
return $this->transition($taskLifecycleStateMachine, $entityManager, $slug, Task::TRANSITION_RESET);
return $this->transition($taskLifecycleStateMachine, $entityManager, $translator, $slug, Task::TRANSITION_RESET);
}
// Renvoie le geojson associé à une tâche
#[Route('/download/{slug}.geojson', name: 'app_task_geojson')]
#[IsGranted('ROLE_USER')]
public function geojson(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function geojson(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
if (!$task) {
$this->addFlash('warning', 'Tâche non trouvée !');
$this->addFlash('warning', $translator->trans('alert.task_not_found'));
if (!$request->headers->has('Referer')) {
throw $this->createNotFoundException('Task not found');
throw $this->createNotFoundException($translator->trans('exception.task_not_found'));
}
return $this->redirect($request->headers->get('Referer'));
@ -351,15 +352,15 @@ class TaskController extends AbstractController
// Renvoie le gpx associé ã une tâche (concrètement il s’agit juste du geojson converti automqtiquement)
#[Route('/download/{slug}.gpx', name: 'app_task_gpx')]
#[IsGranted('ROLE_USER')]
public function gpx(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function gpx(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
if (!$task) {
$this->addFlash('warning', 'Tâche non trouvée !');
$this->addFlash('warning', $translator->trans('alert.task_not_found'));
if (!$request->headers->has('Referer')) {
throw $this->createNotFoundException('Task not found');
throw $this->createNotFoundException($translator->trans('exception.task_not_found'));
}
return $this->redirect($request->headers->get('Referer'));
@ -390,7 +391,7 @@ class TaskController extends AbstractController
// Renvoie le XML OSM associé à la tâche
#[Route('/download/{slug}.osm', name: 'app_task_osm')]
#[IsGranted('PUBLIC_ACCESS')]
public function osm(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function osm(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Task::class);
$task = $repository->findOneBySlug($slug);
@ -398,7 +399,7 @@ class TaskController extends AbstractController
if (!$task) {
$this->addFlash(
'warning',
'Tâche non trouvée !'
$translator->trans('alert.task_not_found')
);
return $this->redirect($request->headers->get('referer'));
@ -424,13 +425,13 @@ class TaskController extends AbstractController
// corresponddre à ce que l’on a pu importer)
#[Route('/download/{slug}.csv', name: 'app_task_csv')]
#[IsGranted('ROLE_USER')]
public function csv(Request $request, EntityManagerInterface $entityManager, $slug): Response
public function csv(Request $request, EntityManagerInterface $entityManager, TranslatorInterface $translator, $slug): Response
{
$repository = $entityManager->getRepository(Project::class);
$project = $repository->findOneBySlug($slug);
if (!$project) {
$this->addFlash('warning', 'Projet non trouvé !');
$this->addFlash('warning', $translator->trans('alert.project_not_found'));
return $this->redirect($request->headers->get('referer'));
}


+ 6
- 2
src/Controller/ToolsController.php View File

@ -31,7 +31,9 @@ class ToolsController extends AbstractController
OverpassClient $overpass,
): Response {
$form = $this->createForm(CityToolType::class, []);
$form->add('submit', SubmitType::class, ['label' => 'Générer']);
$form->add('submit', SubmitType::class, [
'label' => 'button.generate',
]);
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
@ -101,7 +103,9 @@ class ToolsController extends AbstractController
LoggerInterface $logger,
): Response {
$form = $this->createForm(OsmoseToolType::class, array_merge(['limit' => 500], $request->query->all()));
$form->add('submit', SubmitType::class, ['label' => 'Générer']);
$form->add('submit', SubmitType::class, [
'label' => 'button.generate',
]);
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {


+ 2
- 1
src/Form/CityToolType.php View File

@ -46,8 +46,9 @@ class CityToolType extends AbstractType
$builder
->add('area', ChoiceType::class, [
'label' => 'Département',
'label' => 'field.city_tool.area',
'choices' => $choices,
'choice_translation_domain' => false,
])
;
}


+ 3
- 3
src/Form/CsvType.php View File

@ -14,7 +14,7 @@ class CsvType extends AbstractType
$builder
->add('csv', FileType::class, [
'mapped' => false,
'label' => 'Fichier CSV',
'label' => 'field.csv.csv',
'constraints' => [
new File([
'maxSize' => '40M',
@ -22,10 +22,10 @@ class CsvType extends AbstractType
'text/csv',
'text/plain',
],
'mimeTypesMessage' => 'Type MIME inattendu',
'mimeTypesMessage' => 'field.csv.csv_mime_constraint',
]),
],
'help' => 'Fichier CSV classique (encodé en UTF8, séparé par des virgules, entouré avec des doubles guillemets, échappé avec des barres obliques et avec des retours chariots UNIX) contenant une ligne de noms de colonnes « name,description,osm,geojson,status » et enfin dans la colonne « status » la valeur « todo », « doing » ou « done ».',
'help' => 'field.csv.csv_help',
])
;
}


+ 7
- 7
src/Form/OsmoseToolType.php View File

@ -13,22 +13,22 @@ class OsmoseToolType extends AbstractType
{
$builder
->add('item', IntegerType::class, [
'label' => 'Item',
'label' => 'field.osmose_tool.item',
'help_html' => true,
'help' => 'Voir la liste sur <a href="https://wiki.openstreetmap.org/wiki/FR:Osmose/issues" target="_blank">le wiki</a>',
'help' => 'field.osmose_tool.item_help',
])
->add('source', IntegerType::class, [
'label' => 'Identifiant source',
'label' => 'field.osmose_tool.source',
])
->add('class', IntegerType::class, [
'label' => 'Identifiant de la classe',
'label' => 'field.osmose_tool.class',
])
->add('limit', IntegerType::class, [
'label' => 'Limite',
'help' => 'Nombre mmaximal de signalements à renvoyer',
'label' => 'field.osmose_tool.limit',
'help' => 'field.osmose_tool.limit_help',
])
->add('group_by_city', CheckboxType::class, [
'label' => 'Grouper par commune',
'label' => 'field.osmose_tool.group_by_city',
'required' => false,
])
;


+ 15
- 11
src/Form/ProjectType.php View File

@ -14,31 +14,35 @@ class ProjectType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', null, ['label' => 'Nom'])
->add('description', null, ['label' => 'Description'])
->add('name', null, [
'label' => 'field.project.name',
])
->add('description', null, [
'label' => 'field.project.description',
])
->add('hashtags', null, [
'label' => 'Hashtags',
'label' => 'field.project.hashtags',
'help_html' => true,
'help' => 'Mots séparés par des espaces, sans <samp>#</samp>',
'help' => 'field.project.hashtags_help',
])
->add('source', null, [
'label' => 'Source',
'label' => 'field.project.source',
'help_html' => true,
'help' => 'On peut en préciser plusieurs, en les séparant par des <samp>;</samp>',
'help' => 'field.project.source_help',
])
->add('imagery', null, [
'label' => 'Imagerie',
'label' => 'field.project.imagery',
'help_html' => true,
'help' => 'Cf <a href="https://josm.openstreetmap.de/wiki/Maps" target="_blank">le wiki</a>',
'help' => 'field.project.imagery_help',
])
->add('overpassQuery', null, [
'label' => 'Requête Overpass',
'label' => 'field.project.overpass',
'required' => false,
'help_html' => true,
'help' => 'Cf <a href="https://overpass-turbo.eu/" target="_blank">le frontal</a>. Se limiter à la requête stricto sensu, sans <samp>[out:json][timeout:25];</samp> avant ou <samp>out geom;</samp> après.',
'help' => 'field.project.overpass_help',
])
->add('tags', EntityType::class, [
'label' => 'Étiquettes',
'label' => 'field.project.tags',
'class' => Tag::class,
'choice_label' => 'name',
'multiple' => true,


+ 11
- 11
src/Form/TaskType.php View File

@ -14,36 +14,36 @@ class TaskType extends AbstractType
{
$builder
->add('name', null, [
'label' => 'Nom',
'label' => 'field.task.name',
])
->add('description', null, [
'label' => 'Description',
'label' => 'field.task.description',
])
->add('geojson', TextareaType::class, [
'label' => 'GeoJSON',
'label' => 'field.task.geojson',
'required' => false,
'help_html' => true,
'help' => 'Ce qu’il faut dessiner sur la carte au format <a href="https://fr.wikipedia.org/wiki/GeoJSON" target="_blank">GeoJSON</a> et mettre à disposition à l’export sous forme de <a href="https://fr.wikipedia.org/wiki/GPX_(format_de_fichier)" target="_blank">GPX</a> pour l’import dans JOSM. Outil pratique&nbsp;: <a href="https://geojson.io/" target="_blank">geojson.io</a>.',
'help' => 'field.task.geojson_help',
])
->add('osm', TextareaType::class, [
'label' => 'OSM',
'label' => 'field.task.osm',
'required' => false,
'help_html' => true,
'help' => 'XML décrivant ce qu’il faut charger dans la feuille de données de JOSM (cf <a href="https://wiki.openstreetmap.org/wiki/FR:OSM_XML" target="_blank">FR:OSM XML - OpenStreetMap Wiki</a>)',
'help' => 'field.task.osm_help',
'empty_data' => '',
])
->add('status', TaskLifecycleType::class, [
'label' => 'État',
'label' => 'field.task.status',
])
->add('urgent', null, [
'label' => 'Urgence',
'label' => 'field.task.urgent',
'required' => false,
'help' => 'Un entier signé optionnel.',
'help' => 'field.task.urgent_help',
])
->add('important', null, [
'label' => 'Importance',
'label' => 'field.task.important',
'required' => false,
'help' => 'Un entier signé optionnel.',
'help' => 'field.task.important_help',
])
;
}


+ 5
- 1
templates/comment/cancel.md.twig View File

@ -1,2 +1,6 @@
{% import 'macro.html.twig' as macro %}
{{ macro.osmLinkTo(user.username) }} abandonne la tâche [{{ task.name }}]({{ path('app_task_show', {'slug': task.slug}) }})
{{ 'comment.cancel'|trans({
'%username%': macro.osmLinkTo(user.username),
'%task_name%': task.name,
'%task_url%': path('app_task_show', {'slug': task.slug})
}) }}

+ 5
- 1
templates/comment/finish.md.twig View File

@ -1,2 +1,6 @@
{% import 'macro.html.twig' as macro %}
{{ macro.osmLinkTo(user.username) }} termine la tâche [{{ task.name }}]({{ path('app_task_show', {'slug': task.slug}) }})
{{ 'comment.finish'|trans({
'%username%': macro.osmLinkTo(user.username),
'%task_name%': task.name,
'%task_url%': path('app_task_show', {'slug': task.slug})
}) }}

+ 5
- 1
templates/comment/reset.md.twig View File

@ -1,2 +1,6 @@
{% import 'macro.html.twig' as macro %}
{{ macro.osmLinkTo(user.username) }} recommence la tâche [{{ task.name }}]({{ path('app_task_show', {'slug': task.slug}) }})
{{ 'comment.reset'|trans({
'%username%': macro.osmLinkTo(user.username),
'%task_name%': task.name,
'%task_url%': path('app_task_show', {'slug': task.slug})
}) }}

+ 1
- 1
templates/comment/start.md.twig View File

@ -1,6 +1,6 @@
{% import 'macro.html.twig' as macro %}
{{ 'comment.start'|trans({
'%username%': macro.osmLinkTo(user.username),
'%task_name%': task.name }},
'%task_name%': task.name,
'%task_url%': path('app_task_show', {'slug': task.slug})
}) }}

+ 1
- 1
templates/macro.html.twig View File

@ -53,7 +53,7 @@ Où :
{% endfor %}
</details>
<p>
<a href="#" target="_blank" data-map-target="openLink">{{ 'map.open_with'|trans }}</a> {{ 'map.or'|trans }}
<a href="#" target="_blank" data-map-target="openLink">{{ 'map.open_with'|trans }}</a>
<button class="btn btn-sm btn-link" data-action="map#openInOsm">{{ 'map.osm'|trans }}</button>
<button class="btn btn-sm btn-link" data-action="map#openInPanoramax">{{ 'map.panoramax'|trans }}</button>
<button class="btn btn-sm btn-link" data-action="map#openInPifomap">{{ 'map.pifomap'|trans }}</button>


+ 1
- 1
templates/partials/_task-title.html.twig View File

@ -1,2 +1,2 @@
{{ task.name }}
<span class="badge {{ 'text-bg-' ~ workflow_metadata(task, 'color', task.status) }} ms-2">{{ workflow_metadata(task, 'title', task.status) }}</span>
<span class="badge {{ 'text-bg-' ~ workflow_metadata(task, 'color', task.status) }} ms-2">{{ workflow_metadata(task, 'title', task.status)|trans }}</span>

+ 3
- 3
templates/project/create.html.twig View File

@ -1,11 +1,11 @@
{% extends 'base.html.twig' %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_create') }}">Créer un projet</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">{{ 'breadcrumb.projects'|trans }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_create') }}">{{ 'breadcrumb.create_project'|trans }}</a></li>
{% endblock %}
{% block page_title %}Créer un projet{% endblock %}
{% block page_title %}{{ 'title.create_project'|trans }}{% endblock %}
{% block page_content %}
{{ form(create_form) }}


+ 5
- 5
templates/project/index.html.twig View File

@ -1,17 +1,17 @@
{% extends 'base.html.twig' %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">{{ 'breadcrumb.projects'|trans }}</a></li>
{% endblock %}
{% block page_title %}Projets{% endblock %}
{% block page_title %}{{ 'title.projects'|trans }}{% endblock %}
{% block page_content %}
{% if is_granted('IS_AUTHENTICATED_FULLY') %}
<div class="row">
<div class="col mb-3">
<div class="btn-group">
<a href="{{ path('app_project_create') }}" class="btn btn-secondary" accesskey="C">Créer un projet</a>
<a href="{{ path('app_project_create') }}" class="btn btn-secondary" accesskey="C">{{ 'button.create_project'|trans }}</a>
</div>
</div>
</div>
@ -31,7 +31,7 @@
</h2>
<p class="card-subtitle mb-2 text-muted">{% include 'partials/_project-metadata.html.twig' %}</p>
{% if project.description %}<p class="card-text">{{ project.description|markdown_to_html }}</p>{% endif %}
<a href="{{ path('app_project_show', {'slug': project.slug}) }}" class="btn btn-primary">Voir le détail</a>
<a href="{{ path('app_project_show', {'slug': project.slug}) }}" class="btn btn-primary">{{ 'button.details'|trans }}</a>
</div>
</div>
</div>
@ -39,7 +39,7 @@
</div>
{% else %}
{% if not is_granted('IS_AUTHENTICATED_FULLY') %}
<p>Il n’y a pas encore de projet. Connectez-vous pour pouvoir en créer un.</p>
<p>{{ 'text.login_to_create_project'|trans }}</p>
{% endif %}
{% endif %}
{% endblock %}

+ 29
- 29
templates/project/show.html.twig View File

@ -2,7 +2,7 @@
{% import 'macro.html.twig' as macro %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">{{ 'breadcrumb.projects'|trans }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_show', {'slug': project.slug}) }}">{{ project.name }}</a></li>
{% endblock %}
@ -18,73 +18,73 @@
<div class="col mb-3">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group me-2">
<a href="{{ path('app_project') }}" class="btn btn-secondary" title="Revenir aux projets" accesskey="P">
<a href="{{ path('app_project') }}" class="btn btn-secondary" title="{{ 'button.back_to_projects'|trans }}" accesskey="P">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house" viewBox="0 0 16 16">
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293zM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5z"/>
</svg>
Projets
{{ 'button.projects'|trans }}
</a>
</div>
{% if is_granted('IS_AUTHENTICATED_FULLY') %}
{% if app.user is same as(project.createdBy) %}
<div class="btn-group me-2">
<a href="{{ path('app_project_update', {'slug': project.slug}) }}" class="btn btn-secondary" title="Modifier le projet" accesskey="U">
<a href="{{ path('app_project_update', {'slug': project.slug}) }}" class="btn btn-secondary" title="{{ 'button.update_project'|trans }}" accesskey="U">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325"/>
</svg>
Modifier
{{ 'button.update'|trans }}
</a>
<a href="{{ path('app_project_remove', {'slug': project.slug}) }}" class="btn btn-secondary" title="Supprimer le projet" accesskey="R">
<a href="{{ path('app_project_remove', {'slug': project.slug}) }}" class="btn btn-secondary" title="{{ 'button.remove_project'|trans }}" accesskey="R">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z"/>
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z"/>
</svg>
Supprimer
{{ 'button.remove'|trans }}
</a>
</div>
{% endif %}
{% if project.overpassQuery %}
<div class="btn-group me-2">
<a href="{{ path('app_project_overpass', {'slug': project.slug}) }}" class="btn btn-secondary" title="Requêter Overpass" accesskey="O">
<a href="{{ path('app_project_overpass', {'slug': project.slug}) }}" class="btn btn-secondary" title="{{ 'button.query_overpass'|trans }}" accesskey="O">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/>
</svg>
Overpass
{{ 'button.overpass'|trans }}
</a>
</div>
{% endif %}
<div class="btn-group me-2">
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#modal" title="Importer des tâches" accesskey="I">
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#modal" title="{{ 'button.import_tasks'|trans }}" accesskey="I">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-arrow-down" viewBox="0 0 16 16">
<path d="M8.5 6.5a.5.5 0 0 0-1 0v3.793L6.354 9.146a.5.5 0 1 0-.708.708l2 2a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0-.708-.708L8.5 10.293z"/>
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2M9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
</svg>
Importer
{{ 'button.import'|trans }}
</button>
<a href="{{ path('app_task_csv', {'slug': project.slug}) }}" class="btn btn-secondary" title="Exporter les tâches" accesskey="E">
<a href="{{ path('app_task_csv', {'slug': project.slug}) }}" class="btn btn-secondary" title="{{ 'button.export_tasks'|trans }}" accesskey="E">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-arrow-up" viewBox="0 0 16 16">
<path d="M8.5 11.5a.5.5 0 0 1-1 0V7.707L6.354 8.854a.5.5 0 1 1-.708-.708l2-2a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 7.707z"/>
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2M9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5z"/>
</svg>
Exporter
{{ 'button.export'|trans }}
</a>
<a href="{{ path('app_task_create', {'slug': project.slug}) }}" class="btn btn-secondary" title="Créer une tâche" accesskey="C">
<a href="{{ path('app_task_create', {'slug': project.slug}) }}" class="btn btn-secondary" title="{{ 'button.create_task'|trans }}" accesskey="C">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus" viewBox="0 0 16 16">
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4"/>
</svg>
Créer
{{ 'button.create'|trans }}
</a>
</div>
{% endif %}
{% if randomTask %}
<div class="btn-group">
<a href="{{ path('app_task_show', {'slug': randomTask.slug}) }}" class="btn btn-secondary" title="Piocher une tâche" accesskey="?">
<a href="{{ path('app_task_show', {'slug': randomTask.slug}) }}" class="btn btn-secondary" title="{{ 'button.pick_task'|trans }}" accesskey="?">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-shuffle" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M0 3.5A.5.5 0 0 1 .5 3H1c2.202 0 3.827 1.24 4.874 2.418.49.552.865 1.102 1.126 1.532.26-.43.636-.98 1.126-1.532C9.173 4.24 10.798 3 13 3v1c-1.798 0-3.173 1.01-4.126 2.082A9.6 9.6 0 0 0 7.556 8a9.6 9.6 0 0 0 1.317 1.918C9.828 10.99 11.204 12 13 12v1c-2.202 0-3.827-1.24-4.874-2.418A10.6 10.6 0 0 1 7 9.05c-.26.43-.636.98-1.126 1.532C4.827 11.76 3.202 13 1 13H.5a.5.5 0 0 1 0-1H1c1.798 0 3.173-1.01 4.126-2.082A9.6 9.6 0 0 0 6.444 8a9.6 9.6 0 0 0-1.317-1.918C4.172 5.01 2.796 4 1 4H.5a.5.5 0 0 1-.5-.5"/>
<path d="M13 5.466V1.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384l-2.36 1.966a.25.25 0 0 1-.41-.192m0 9v-3.932a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384l-2.36 1.966a.25.25 0 0 1-.41-.192"/>
</svg>
Piocher
{{ 'button.pick'|trans }}
</a>
</div>
{% endif %}
@ -115,7 +115,7 @@
{% endif %}
{% if project.tasks is not empty %}
<h2 class="mb-3">Carte</h2>
<h2 class="mb-3">{{ 'title.map'|trans }}</h2>
<div class="row">
<div class="col mb-3">
{{ macro.map(project, project.overpassResult ? project.overpassResult : '') }}
@ -125,13 +125,13 @@
{% if project.tasks is not empty %}
{% set dummy = tasks.setParam('_fragment', 'tasks') %}
<h2 id="tasks" class="mb-3">Tâches</h2>
<h2 id="tasks" class="mb-3">{{ 'title.tasks'|trans }}</h2>
<div class="row">
<div class="col mb-3">
<div class="progress-stacked">
{% set stats = taskLifecycleManager.getProjectStats(project) %}
{% for place, data in stats %}
<div class="progress" role="progressbar" data-bs-toggle="tooltip" data-bs-title="{{ data.title }} {{ data.value ~ '/' ~ data.max }}" aria-label="{{ data.title }}" aria-valuenow="{{ data.value }}" aria-valuemin="0" aria-valuemax="{{ data.max }}" style="width:{{ data.percentage ~ '%' }}">
<div class="progress" role="progressbar" data-bs-toggle="tooltip" data-bs-title="{{ data.title|trans }} {{ data.value ~ '/' ~ data.max }}" aria-label="{{ data.title|trans }}" aria-valuenow="{{ data.value }}" aria-valuemin="0" aria-valuemax="{{ data.max }}" style="width:{{ data.percentage ~ '%' }}">
<div class="progress-bar {{ 'bg-' ~ data.color }}">{{ data.percentage|format_number({fraction_digit: 0}) ~ '%' }}</div>
</div>
{% endfor %}
@ -144,17 +144,17 @@
<table class="table table-sm table-hover">
<thead>
<tr>
<th scope="col">{{ macro.paginated(tasks, 'Nom', 't.name') }}</th>
<th scope="col">{{ macro.paginated(tasks, 'État', 't.status') }}</th>
<th scope="col">{{ macro.paginated(tasks, 'Importance', 't.important') }}</th>
<th scope="col">{{ macro.paginated(tasks, 'Urgence', 't.urgent') }}</th>
<th scope="col">{{ macro.paginated(tasks, 'column.task.name'|trans, 't.name') }}</th>
<th scope="col">{{ macro.paginated(tasks, 'column.task.status'|trans, 't.status') }}</th>
<th scope="col">{{ macro.paginated(tasks, 'column.task.important'|trans, 't.important') }}</th>
<th scope="col">{{ macro.paginated(tasks, 'column.task.urgent'|trans, 't.urgent') }}</th>
</tr>
</thead>
<tbody>
{% for task in tasks %}
<tr class="{{ 'table-' ~ workflow_metadata(task, 'color', task.status) }}">
<td><a href="{{ path('app_task_show', {'slug': task.slug}) }}">{{ task.name }}</a></td>
<td>{{ workflow_metadata(task, 'title', task.status) }}</td>
<td>{{ workflow_metadata(task, 'title', task.status)|trans }}</td>
<td>{{ task.important }}</td>
<td>{{ task.urgent }}</td>
</tr>
@ -168,7 +168,7 @@
{% if comments is not empty %}
<details>
<summary class="mb-3"><h2 class="d-inline">Commentaires <span class="badge text-bg-secondary">{{ comments|length }}</span></h2></summary>
<summary class="mb-3"><h2 class="d-inline">{{ 'title.comments'|trans }} <span class="badge text-bg-secondary">{{ comments|length }}</span></h2></summary>
<div class="row">
<div class="col mb-3">
{% for comment in comments %}
@ -192,14 +192,14 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="modalLabel">Importer des tâches</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<h1 class="modal-title fs-5" id="modalLabel">{{ 'title.import_tasks'|trans }}</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ 'button.close'|trans }}"></button>
</div>
<div class="modal-body">
{{ form_row(csv_form.csv) }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ 'button.close'|trans }}</button>
{{ form_widget(csv_form.submit) }}
</div>
</div>


+ 2
- 2
templates/project/update.html.twig View File

@ -1,11 +1,11 @@
{% extends 'base.html.twig' %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">{{ 'breadcrumb.projects'|trans }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_show', {'slug': project.slug}) }}">{{ project.name }}</a></li>
{% endblock %}
{% block page_title %}Modifier le projet{% endblock %}
{% block page_title %}{{ 'title.update_project'|trans }}{% endblock %}
{% block page_content %}
{{ form(update_form) }}


+ 4
- 4
templates/task/create.html.twig View File

@ -1,12 +1,12 @@
{% extends 'base.html.twig' %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_show', {'slug': project.slug}) }}">Projet {{ project.name }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_task_create', {'slug': project.slug}) }}">Créer une tâche</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">{{ 'breadcrumb.projects'|trans }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_show', {'slug': project.slug}) }}">{{ project.name }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_task_create', {'slug': project.slug}) }}">{{ 'breadcrumb.create_task'|trans }}</a></li>
{% endblock %}
{% block page_title %}Créer une tâche{% endblock %}
{% block page_title %}{{ 'title.create_task'|trans }}{% endblock %}
{% block page_content %}
{{ form(create_form) }}


+ 26
- 22
templates/task/show.html.twig View File

@ -2,7 +2,7 @@
{% import 'macro.html.twig' as macro %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">{{ 'breadcrumb.projects'|trans }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_show', {'slug': project.slug}) }}">{{ project.name }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_task_show', {'slug': task.slug}) }}">{{ task.name }}</a></li>
{% endblock %}
@ -16,35 +16,35 @@
<div class="col mb-3">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group me-2">
<a href="{{ path('app_project_show', {'slug': project.slug}) }}" class="btn btn-secondary" title="Revenir au projet" accesskey="P">
<a href="{{ path('app_project_show', {'slug': project.slug}) }}" class="btn btn-secondary" title="{{ 'button.back_to_project'|trans }}" accesskey="P">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house" viewBox="0 0 16 16">
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293zM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5z"/>
</svg>
Projet
{{ 'button.project'|trans }}
</a>
</div>
{% if is_granted('IS_AUTHENTICATED_FULLY') %}
{% if app.user is same as(task.createdBy) %}
<div class="btn-group me-2">
<a href="{{ path('app_task_update', {'slug': task.slug}) }}" class="btn btn-secondary" title="Modifier la tâche" accesskey="U">
<a href="{{ path('app_task_update', {'slug': task.slug}) }}" class="btn btn-secondary" title="{{ 'button.update_task'|trans }}" accesskey="U">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325"/>
</svg>
Modifier
{{ 'button.update'|trans }}
</a>
<a href="{{ path('app_task_remove', {'slug': task.slug}) }}" target="_blank" class="btn btn-secondary" title="Supprimer la tâche" accesskey="R">
<a href="{{ path('app_task_remove', {'slug': task.slug}) }}" target="_blank" class="btn btn-secondary" title="{{ 'button.remove_task'|trans }}" accesskey="R">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5m3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z"/>
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4zM2.5 3h11V2h-11z"/>
</svg>
Supprimer
{{ 'button.remove'|trans }}
</a>
</div>
{% endif %}
<div class="btn-group me-2">
{% for transition in workflow_transitions(task) %}
{% if not workflow_metadata(task, 'locking', task.status) or app.user is same as(task.lockedBy) %}
<a href="{{ path(workflow_metadata(task, 'route', transition), {'slug': task.slug}) }}" class="btn btn-secondary" title="{{ workflow_metadata(task, 'title', transition) }}"{% if workflow_metadata(task, 'accesskey', transition) %} accesskey="{{ workflow_metadata(task, 'accesskey', transition) }}"{% endif %}>
<a href="{{ path(workflow_metadata(task, 'route', transition), {'slug': task.slug}) }}" class="btn btn-secondary" title="{{ workflow_metadata(task, 'title', transition)|trans }}"{% if workflow_metadata(task, 'accesskey', transition) %} accesskey="{{ workflow_metadata(task, 'accesskey', transition) }}"{% endif %}>
{% if transition.name == 'start' %}
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-play-fill" viewBox="0 0 16 16">
<path d="m11.596 8.697-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393"/>
@ -62,7 +62,7 @@
<path d="M.5 3.5A.5.5 0 0 0 0 4v8a.5.5 0 0 0 1 0V8.753l6.267 3.636c.54.313 1.233-.066 1.233-.697v-2.94l6.267 3.636c.54.314 1.233-.065 1.233-.696V4.308c0-.63-.693-1.01-1.233-.696L8.5 7.248v-2.94c0-.63-.692-1.01-1.233-.696L1 7.248V4a.5.5 0 0 0-.5-.5"/>
</svg>
{% endif %}
{{ workflow_metadata(task, 'short', transition) }}
{{ workflow_metadata(task, 'short', transition)|trans }}
</a>
{% endif %}
{% endfor %}
@ -70,12 +70,12 @@
{% if workflow_metadata(task, 'locking', task.status) and app.user is same as(task.lockedBy) %}
<div class="btn-group me-2">
<div class="btn-group">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="Télécharger la tâche">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="{{ 'button.download_task'|trans }}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16">
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5"/>
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708z"/>
</svg>
Télécharger
{{ 'button.download'|trans }}
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ path('app_task_geojson', {'slug': task.slug}) }}">.geojson</a></li>
@ -88,25 +88,25 @@
data-controller="josm"
data-action="click->josm#remoteControl"
data-josm-commands-value="{{ josmCommands|escape('html_attr') }}"
title="Télécommande JOSM"
title="{{ 'button.josm_remote_control'|trans }}"
accesskey="J"
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-broadcast" viewBox="0 0 16 16">
<path d="M3.05 3.05a7 7 0 0 0 0 9.9.5.5 0 0 1-.707.707 8 8 0 0 1 0-11.314.5.5 0 0 1 .707.707m2.122 2.122a4 4 0 0 0 0 5.656.5.5 0 1 1-.708.708 5 5 0 0 1 0-7.072.5.5 0 0 1 .708.708m5.656-.708a.5.5 0 0 1 .708 0 5 5 0 0 1 0 7.072.5.5 0 1 1-.708-.708 4 4 0 0 0 0-5.656.5.5 0 0 1 0-.708m2.122-2.12a.5.5 0 0 1 .707 0 8 8 0 0 1 0 11.313.5.5 0 0 1-.707-.707 7 7 0 0 0 0-9.9.5.5 0 0 1 0-.707zM10 8a2 2 0 1 1-4 0 2 2 0 0 1 4 0"/>
</svg>
JOSM
{{ 'button.josm'|trans }}
</button>
</div>
{% endif %}
{% endif %}
{% if randomTask %}
<div class="btn-group me-2">
<a href="{{ path('app_task_show', {'slug': randomTask.slug}) }}" class="btn btn-secondary" title="Piocher une tâche" accesskey="?">
<a href="{{ path('app_task_show', {'slug': randomTask.slug}) }}" class="btn btn-secondary" title="{{ 'button.pick_task'|trans }}" accesskey="?">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-shuffle" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M0 3.5A.5.5 0 0 1 .5 3H1c2.202 0 3.827 1.24 4.874 2.418.49.552.865 1.102 1.126 1.532.26-.43.636-.98 1.126-1.532C9.173 4.24 10.798 3 13 3v1c-1.798 0-3.173 1.01-4.126 2.082A9.6 9.6 0 0 0 7.556 8a9.6 9.6 0 0 0 1.317 1.918C9.828 10.99 11.204 12 13 12v1c-2.202 0-3.827-1.24-4.874-2.418A10.6 10.6 0 0 1 7 9.05c-.26.43-.636.98-1.126 1.532C4.827 11.76 3.202 13 1 13H.5a.5.5 0 0 1 0-1H1c1.798 0 3.173-1.01 4.126-2.082A9.6 9.6 0 0 0 6.444 8a9.6 9.6 0 0 0-1.317-1.918C4.172 5.01 2.796 4 1 4H.5a.5.5 0 0 1-.5-.5"/>
<path d="M13 5.466V1.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384l-2.36 1.966a.25.25 0 0 1-.41-.192m0 9v-3.932a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384l-2.36 1.966a.25.25 0 0 1-.41-.192"/>
</svg>
Piocher
{{ 'button.pick'|trans }}
</a>
</div>
{% endif %}
@ -124,7 +124,7 @@
</div>
{% if task.description is not empty %}
<h2 class="mb-3">Description</h2>
<h2 class="mb-3">{{ 'title.description'|trans }}</h2>
<div class="row">
<div class="col mb-3 lead">{{ task.description|markdown_to_html }}</div>
</div>
@ -136,13 +136,13 @@
<table class="table table-bordered table-sm">
<tbody>
<tr>
<th scope="row">Commentaire</th>
<th scope="row">{{ 'title.comment'|trans }}</th>
<td>
{{ macro.clipboard(sourceGenerator.generate(task)) }}
</td>
</tr>
<tr>
<th scope="row">Source</th>
<th scope="row">{{ 'title.source'|trans }}</th>
<td>
{{ macro.clipboard(project.source) }}
</td>
@ -163,12 +163,16 @@
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="{{ '#' ~ collapseId }}" aria-expanded="false" aria-controls="{{ collapseId }}">
{{ 'Groupe de modifications ' ~ changeset.id }}
{{ 'button.changeset'|trans({'%changeset_id%': changeset.id}) }}
</button>
</h2>
<div id="{{ collapseId }}" class="accordion-collapse collapse" data-bs-parent="#changesetsAccordion">
<div class="accordion-body">
<p> Voir sur <a href="{{ 'https://www.openstreetmap.org/changeset/' ~ changeset.id }}" target="_blank">OSM</a> ou <a href="{{ 'https://osmcha.org/changesets/' ~ changeset.id }}" target="_blank">OSMCha</a></p>
<p>
{{ 'text.see_also'|trans }}
<a href="{{ 'https://www.openstreetmap.org/changeset/' ~ changeset.id }}" target="_blank">{{ 'text.osm'|trans }}</a>
<a href="{{ 'https://osmcha.org/changesets/' ~ changeset.id }}" target="_blank">{{ 'text.osmcha'|trans }}</a>
</p>
<table class="table table-bordered table-sm">
<tbody>
{% for key, value in changeset %}
@ -188,7 +192,7 @@
</div>
{% endif %}
<h2 class="mb-3">Carte</h2>
<h2 class="mb-3">{{ 'title.map'|trans }}</h2>
<div class="row">
<div class="col mb-3">
{{ macro.map(task, task.project.overpassResult) }}
@ -198,7 +202,7 @@
{% if task.comments is not empty %}
<details>
<summary class="mb-3"><h2 class="d-inline">Commentaires <span class="badge text-bg-secondary">{{ task.comments|length }}</span></h2></summary>
<summary class="mb-3"><h2 class="d-inline">{{ 'title.comments'|trans }} <span class="badge text-bg-secondary">{{ task.comments|length }}</span></h2></summary>
{% endif %}
{% if task.comments is not empty %}
<div class="row">


+ 2
- 2
templates/task/update.html.twig View File

@ -1,12 +1,12 @@
{% extends 'base.html.twig' %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">Projets</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project') }}">{{ 'breadcrumb.project'|trans }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_project_show', {'slug': project.slug}) }}">{{ project.name }}</a></li>
<li class="breadcrumb-item"><a href="{{ path('app_task_show', {'slug': task.slug}) }}">{{ task.name }}</a></li>
{% endblock %}
{% block page_title %}Modifier la tâche{% endblock %}
{% block page_title %}{{ 'title.update_task'|trans }}{% endblock %}
{% block page_content %}
{{ form(update_form) }}


+ 2
- 2
templates/tools/base.html.twig View File

@ -1,12 +1,12 @@
{% extends 'base.html.twig' %}
{% block page_title %}Outils{% endblock %}
{% block page_title %}{{ 'title.tools'|trans }}{% endblock %}
{% block page_content %}
<ul class="nav nav-tabs mb-3">
{% for tool in tools %}
<li class="nav-item">
<a class="nav-link{% if app.current_route == tool.route %} active" aria-current="page{% endif %}" href="{{ path(tool.route) }}">{{ tool.label }}</a>
<a class="nav-link{% if app.current_route == tool.route %} active" aria-current="page{% endif %}" href="{{ path(tool.route) }}">{{ tool.label[app.request.locale] }}</a>
</li>
{% endfor %}
</ul>


+ 79
- 0
translations/messages.en.yaml View File

@ -1,20 +1,83 @@
exception:
project_not_found: 'Project not found'
status_not_found: 'Status not found'
task_not_found: __exception.task_not_found
task_not_updated: __exception.task_not_updated
alert:
auth_failure: 'Authentification failed'
auth_success: 'Authentification successfull !'
project_created: __alert.project_created
project_not_created: __alert.project_not_created
project_not_found: __alert.project_not_found
project_updated: __alert.project_updated
project_not_updated: __alert.project_not_updated
project_removed: __alert.project_removed
project_not_removed: __alert.project_not_removed
no_overpass_query: __alert.no_overpass_query
overpass_please_wait: __alert.overpass_please_wait
missing_project: __alert.missing_project
task_created: __alert.task_created
task_not_created: __alert.task_not_created
task_not_found: __alert.task_not_found
can_not_comment: __alert.can_not_comment
task_not_updated: __alert.task_not_updated
task_not_removed: __alert.task_not_removed
task_not_modified: __alert.task_not_modified
title:
popular_projects: 'Popular projects'
update_project: __title.update_project
create_project: __title.create_project
projects: __title.projects
map: __title.map
tasks: __title.tasks
comments: __title.comments
import_tasks: __title.import_tasks
update_task: __title.update_task
create_task: __title.create_task
description: __title.description
comment: __title.comment
source: __title.source
tools: __title.tools
text:
home1: 'Handle tasks, even with nodes (or not).'
home2: __text.home2
home3: 'In order to contact the developer, please send an email to %mailto_link%'
login_to_create_project: __text.login_to_create_project
see_also: __text.see_also
osm: __text.osm
osmcha: __text.osmcha
button:
close: Close
toggle_navigation: 'Toggle navigation'
more: __button.more
goto_projects: __button.goto_projects
create_project: __button.create_project
details: __button.details
back_to_projects: __button.back_to_projects
projects: __button.projects
update_project: __button.update_project
update: __button.update
remove_project: __button.remove_project
remove: __button.remove
query_overpass: __button.query_overpass
overpass: __button.overpass
import_tasks: __button.import_tasks
import: __button.import
export_tasks: __button.export_tasks
export: __button.export
create_task: __button.create_task
create: __button.create
pick_task: __button.pick_task
pick: __button.pick
back_to_project: __button.back_to_project
project: __button.project
update_task: __button.update_task
remove_task: __button.remove_task
download_task: __button.download_task
download: __button.download
josm_remote_control: __button.josm_remote_control
josm: __button.josm
changeset: __button.changeset
menu:
on_osm: 'OSM Profile'
logout: Logout
@ -56,3 +119,19 @@ workflow:
cancel: Cancel
reset_task: 'Reset task'
reset: Reset
breadcrumb:
projects: __breadcrumb.projects
create_project: __breadcrumb.create_project
project: __breadcrumb.project
create_task: __breadcrumb.create_task
column:
task:
name: __column.task.name
status: __column.task.status
important: __column.task.important
urgent: __column.task.urgent
comment:
start: __comment.start
reset: __comment.reset
finish: __comment.finish
cancel: __comment.cancel

+ 118
- 2
translations/messages.fr.yaml View File

@ -1,20 +1,123 @@
exception:
project_not_found: 'Projet introuvable'
status_not_found: 'État introuvable'
task_not_found: 'Tâche introuvable'
task_not_updated: 'La tâche n’a pas été modifiée'
alert:
auth_failure: 'Échec de l’authentification'
auth_success: 'Authentification OSM réussie !'
auth_success: 'Authentification OSM réussie'
project_created: 'Le projet a bien été créé'
project_not_created: 'Impossible de créer le projet'
project_not_found: 'Projet introuvable'
project_updated: 'Le projet a bien été modifié'
project_not_updated: 'Impossible de modifier le projet'
project_removed: 'Le projet a bien été supprimé'
project_not_removed: 'Impossible de supprimer le projet'
no_overpass_query: 'Le projet n’a pas de requête Overpass'
overpass_please_wait: 'Merci de patienter entre les requêtes Overpass'
missing_project: 'Projet introuvable'
task_created: 'La tâche a bien été créée'
task_not_created: 'Impossible de créer la tâche'
task_not_found: 'Tâche introuvable'
can_not_comment: 'Impossible de commenter'
task_not_updated: 'Impossible de modifier la tâche'
task_not_removed: 'Impossible de supprimer la tâche'
title:
popular_projects: 'Projets populaires'
update_project: 'Mettre à jour le projet'
create_project: 'Créer un projet'
projects: Projets
map: Carte
tasks: Tâches
comments: Commentaires
import_tasks: 'Importer des tâches'
update_task: 'Modifier la tâche'
create_task: 'Créer une tâche'
description: Description
comment: Commentaire
source: Source
tools: Outils
text:
home1: 'Gère les tâches, même avec des nœuds (ou pas).'
home2: 'L’idée c’est d’avoir un petit outil collaboratif simple et facile à administrer/utiliser pour mapper des trucs de façon coordonnée à un endroit.'
home3: 'Si d’aventure vous souhaitez contacter le développeur, merci d’écrire à %mailto_link%'
login_to_create_project: 'Il n’y a pas encore de projet. Connectez-vous pour pouvoir en créer un.'
see_also: 'Voir aussi'
osm: OSM
osmcha: OSMCha
field:
project:
name: 'Nom du projet'
description: 'Description du projet'
hashtags: Mots-dièses
hashtags_help: 'Mots séparés par des espaces, sans <samp>#</samp>'
source: Source
source_help: 'On peut en préciser plusieurs, en les séparant par des <samp>;</samp>'
imagery: Imagerie
imagery_help: 'Cf <a href="https://josm.openstreetmap.de/wiki/Maps" target="_blank">le wiki</a>'
overpass: 'Requête Overpass'
overpass_help: 'Cf <a href="https://overpass-turbo.eu/" target="_blank">le frontal</a>. Se limiter à la requête stricto sensu, sans <samp>[out:json][timeout:25];</samp> avant ou <samp>out geom;</samp> après.'
tags: Étiquettes
csv:
csv: 'Fichier CSV'
csv_mime_constraint: 'Type MIME inattendu'
csv_help: 'Fichier CSV classique (encodé en UTF8, séparé par des virgules, entouré avec des doubles guillemets, échappé avec des barres obliques et avec des retours chariots UNIX) contenant une ligne de noms de colonnes « name,description,osm,geojson,status » et enfin dans la colonne « status » la valeur « todo », « doing » ou « done ».'
task:
name: 'Nom de la tâche'
description: 'Description de la tâche'
geojson: GeoJSON
geojson_help: 'Ce qu’il faut dessiner sur la carte au format <a href="https://fr.wikipedia.org/wiki/GeoJSON" target="_blank">GeoJSON</a> et mettre à disposition à l’export sous forme de <a href="https://fr.wikipedia.org/wiki/GPX_(format_de_fichier)" target="_blank">GPX</a> pour l’import dans JOSM. Outil pratique&nbsp;: <a href="https://geojson.io/" target="_blank">geojson.io</a>.'
osm: 'Données OSM'
osm_help: 'XML décrivant ce qu’il faut charger dans la feuille de données de JOSM (cf <a href="https://wiki.openstreetmap.org/wiki/FR:OSM_XML" target="_blank">FR:OSM XML - OpenStreetMap Wiki</a>)'
status: État
urgent: Urgence
urgent_help: 'Un entier signé optionnel.'
important: Importance
important_help: 'Un entier signé optionnel.'
city_tool:
area: Département
osmose_tool:
item: Item
item_help: 'Voir la liste sur <a href="https://wiki.openstreetmap.org/wiki/FR:Osmose/issues" target="_blank">le wiki</a>'
source: 'Identifiant de source'
class: 'Identifiant de classe'
limit: Limite
limit_help: 'Nombre mmaximal de signalements à renvoyer'
group_by_city: 'Regrouper les signalements par commune'
button:
close: Fermer
toggle_navigation: 'Basculer le menu'
more: 'En savoir plus'
goto_projects: 'Voir les projets'
create_project: 'Créer un projet'
details: 'Voir les détails'
projects: Projets
back_to_projects: 'Retour aux projets'
update: Modifier
update_project: 'Modifier le projet'
remove: Supprimer
remove_project: 'Supprimer le projet'
overpass: Overpass
query_overpass: 'Requêter Overpass'
import: Importer
import_tasks: 'Importer des tâches'
export: Export
export_tasks: 'Exporter les tâches'
create: Créer
create_task: 'Créer une tâche'
pick: Piocher
pick_task: 'Piocher une tâche'
back_to_project: 'Retour au projet'
project: Projet
update_task: 'Modifier la tâche'
remove_task: 'Supprimer la tâche'
download_task: 'Télécharger la tâche'
download: Télécharger
josm_remote_control: 'Télécommande JOSM'
josm: JOSM
changeset: 'Groupe de modification %changeset_id%'
comment: Commenter
generate: Générer
menu:
on_osm: 'Profil OSM'
logout: Déconnexion
@ -23,7 +126,6 @@ map:
legend: Légende
overpass: Overpass
open_with: 'Ouvrir avec…'
or: ou
osm: OSM
panoramax: Panoramax
pifomap: Pifomap
@ -44,6 +146,9 @@ popup:
josm: JOSM
comment:
start: '%username% commence la tâche [%task_name%](%task_url%)'
reset: '%username% recommence la tâche [%task_name%](%task_url%)'
finish: '%username% termine la tâche [%task_name%](%task_url%)'
cancel: '%username% abandonne la tâche [%task_name%](%task_url%)'
workflow:
place:
todo: 'À faire'
@ -58,3 +163,14 @@ workflow:
cancel: Abandonner
reset_task: 'Recommencer la tâche'
reset: Recommencer
breadcrumb:
projects: Projets
create_project: 'Créer un projet'
project: Projet
create_task: 'Créer une tâche'
column:
task:
name: Tâche
status: État
important: Importance
urgent: Urgence

Loading…
Cancel
Save