diff --git a/app/templates/projects/project.html b/app/templates/projects/project.html index cdd25b8..85e0382 100644 --- a/app/templates/projects/project.html +++ b/app/templates/projects/project.html @@ -22,6 +22,11 @@

{% translate 'catalogs'|capfirst %}

{% endfor %} +

{% translate 'Export' %}

+

+ {% translate 'Export messages as CSV' %} +

+

{% translate 'users'|capfirst %}

{% for user in project.all_users %}{{ user.get_full_name }}{% if not forloop.last %}, {% endif %}{% endfor %} diff --git a/projects/foreign.py b/projects/foreign.py new file mode 100644 index 0000000..56c398c --- /dev/null +++ b/projects/foreign.py @@ -0,0 +1,65 @@ +from collections import defaultdict +from itertools import chain + + +def msg_key(entry): + return (entry.msgid, entry.msgid_plural, entry.msgctxt) + + +def merge_catalogs(project): + """ + Return a nested dictionary of the form: + + ``messages[msg_key][language, domain] = entry`` + """ + + messages = defaultdict(dict) + + for catalog in project.catalogs.all(): + for entry in catalog.po: + messages[msg_key(entry)][catalog.language_code, catalog.domain] = entry + + return messages + + +def messages_as_table(project): + merged = merge_catalogs(project) + language_domain_combinations = sorted( + set(chain.from_iterable(thing.keys() for thing in merged.values())) + ) + + header = [ + "msgctxt", + "msgid", + "msgid_plural", + "comment", + "tcomment", + "flags", + *( + f"{language_code}:{domain}" + for language_code, domain in language_domain_combinations + ), + ] + data = [] + + for entries in merged.values(): + any_entry = next(iter(entries.values())) + + row = [ + any_entry.msgctxt, + any_entry.msgid, + any_entry.msgid_plural, + any_entry.comment, + any_entry.tcomment, + ", ".join(any_entry.flags), + ] + + for language_code_domain in language_domain_combinations: + if entry := entries.get(language_code_domain): + row.append(entry.msgstr) + else: + row.append("") + + data.append(row) + + return [header, *data] diff --git a/projects/test_views.py b/projects/test_views.py index 196412a..5197cca 100644 --- a/projects/test_views.py +++ b/projects/test_views.py @@ -70,6 +70,9 @@ def test_project_access(self): r = su_client.get("/traduire.toml") self.assertContains(r, f'token = "{superuser.token}"') + r = su_client.get("/test/messages.csv") + self.assertContains(r, ",Continue,") + r = u_client.get(p.get_absolute_url(), headers={"accept-language": "en"}) self.assertEqual(r.status_code, 404) diff --git a/projects/urls.py b/projects/urls.py index 887ec5e..739b051 100644 --- a/projects/urls.py +++ b/projects/urls.py @@ -23,4 +23,5 @@ views.traduire_toml, name="traduire_toml", ), + path("/messages.csv", views.messages_csv, name="messages_csv"), ] diff --git a/projects/views.py b/projects/views.py index 06dbb57..a1ce5f5 100644 --- a/projects/views.py +++ b/projects/views.py @@ -1,7 +1,10 @@ +import csv + import polib from django import http from django.conf import settings from django.contrib.auth.decorators import login_required +from django.http import HttpResponse from django.shortcuts import get_object_or_404, render from django.template.defaulttags import querystring from django.views.decorators.csrf import csrf_exempt @@ -10,6 +13,7 @@ from accounts.models import User from form_rendering import adapt_rendering from projects import translators +from projects.foreign import messages_as_table from projects.forms import EntriesForm, FilterForm, SuggestForm from projects.models import Catalog, Event, Project @@ -211,3 +215,13 @@ def traduire_toml(request): for project in Project.objects.for_user(request.user) ) return http.HttpResponse(toml, content_type="text/plain; charset=utf-8") + + +@login_required +def messages_csv(request, slug): + project = get_object_or_404(Project.objects.for_user(request.user), slug=slug) + + response = HttpResponse(content_type="text/csv") + csv.writer(response).writerows(messages_as_table(project)) + + return response