Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable translation of footnotes #64

Closed
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
"wagtail.search",
"wagtail.admin",
"wagtail.contrib.modeladmin",
"wagtail.contrib.simple_translation",
"wagtail.contrib.redirects",
"wagtail.sites",
"wagtail",
"taggit",
Expand All @@ -54,6 +56,7 @@
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
Expand Down Expand Up @@ -112,6 +115,11 @@

USE_TZ = True

USE_I18N = True
WAGTAIL_I18N_ENABLED = True
LANGUAGE_CODE = "en"


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/stable/howto/static-files/

Expand Down
7 changes: 5 additions & 2 deletions tests/templates/base.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{% load wagtailuserbar %}
{% load wagtailuserbar i18n %}

{% get_current_language as LANGUAGE_CODE %}

<!DOCTYPE html>
<html lang="en">
<html lang="{{ LANGUAGE_CODE }}">

<head>
<meta charset="UTF-8">
Expand Down
157 changes: 157 additions & 0 deletions tests/test/test_translation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import json

from bs4 import BeautifulSoup as bs4
from django.test import override_settings
from django.urls import reverse
from django.utils import translation
from wagtail.models import Locale, Page
from wagtail.test.utils import TestCase, WagtailTestUtils

from wagtail_footnotes.models import Footnote

from ..models import TestPageStreamField


@override_settings(
LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_CONTENT_LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
)
class TestSubmitPageTranslationView(WagtailTestUtils, TestCase):
def setUp(self):
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.de_locale = Locale.objects.create(language_code="de")

self.en_homepage = Page.objects.get(title="Welcome to your new Wagtail site!")
self.fr_homepage = self.en_homepage.copy_for_translation(self.fr_locale)
self.fr_homepage.save_revision().publish()
self.de_homepage = self.en_homepage.copy_for_translation(self.de_locale)
self.de_homepage.save_revision().publish()

self.uuid = "f291a4b7-5ac5-4030-b341-b1993efb2ad2"
self.en_test_page = TestPageStreamField(
title="Test Page With Footnote",
slug="test-page-with-footnote",
body=json.dumps(
[
{
"type": "paragraph",
"value": f'<p>This is a paragraph with a footnote. <footnote id="{self.uuid}">1</footnote></p>',
},
]
),
)
self.en_homepage.add_child(instance=self.en_test_page)
self.en_test_page.save_revision().publish()
self.en_footnote = Footnote.objects.create(
page=self.en_test_page,
uuid=self.uuid,
text="This is a footnote",
)

def test_translating_page_translates_footnote(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_test_page.id,)
)
self.login()

de = Locale.objects.get(language_code="de").id
fr = Locale.objects.get(language_code="fr").id
data = {"locales": [de, fr], "include_subtree": True}
self.client.post(url, data, follow=True)

de_footnote = self.en_footnote.get_translation(de)
self.assertEqual(de_footnote.text, self.en_footnote.text)
self.assertEqual(de_footnote.uuid, self.en_footnote.uuid)
de_test_page = self.en_test_page.get_translation(de)
self.assertCountEqual(de_test_page.footnotes.all(), [de_footnote])

fr_footnote = self.en_footnote.get_translation(fr)
self.assertEqual(fr_footnote.text, self.en_footnote.text)
self.assertEqual(fr_footnote.uuid, self.en_footnote.uuid)
fr_test_page = self.en_test_page.get_translation(fr)
self.assertCountEqual(fr_test_page.footnotes.all(), [fr_footnote])

# Can also change the text:
fr_footnote.text = "This is a French translated footnote"
fr_footnote.save()
fr_footnote.refresh_from_db()
en_footnote = self.en_footnote
en_footnote.refresh_from_db()
self.assertEqual(fr_footnote.text, "This is a French translated footnote")
self.assertNotEqual(fr_footnote.text, en_footnote.text)

def test_translated_page_shows_translated_footnote(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_test_page.id,)
)
self.login()

fr = Locale.objects.get(language_code="fr").id
data = {"locales": [fr], "include_subtree": True}
response = self.client.post(url, data, follow=True)

fr_test_page = self.en_test_page.get_translation(fr)

self.assertRedirects(
response, reverse("wagtailadmin_pages:edit", args=[fr_test_page.pk])
)

self.assertIn(
"The page 'Test Page With Footnote' was successfully created in French",
[msg.message for msg in response.context["messages"]],
)

fr_footnote = self.en_footnote.get_translation(fr)
self.assertEqual(fr_footnote.text, self.en_footnote.text)
self.assertEqual(fr_footnote.uuid, self.en_footnote.uuid)
self.assertCountEqual(fr_test_page.footnotes.all(), [fr_footnote])

fr_test_page.title = ("[FR] Test Page With Footnote",)
fr_test_page.body = json.dumps(
[
{
"type": "paragraph",
"value": f'<p>This is a French paragraph with a footnote. <footnote id="{self.uuid}">1</footnote></p>',
},
]
)
fr_test_page.save_revision().publish()
fr_test_page.refresh_from_db()

# Can also change the text:
fr_footnote.text = "This is a French translated footnote"
fr_footnote.save()

translation.activate("fr")

response = self.client.get(fr_test_page.get_full_url())
self.assertEqual(response.status_code, 200)

soup = bs4(response.content, "html.parser")

# Test that required html tags are present with correct
# attrs that enable the footnotes to respond to clicks
source_anchor = soup.find("a", {"id": "footnote-source-1"})
self.assertTrue(source_anchor)

source_anchor_string = str(source_anchor)
self.assertIn("<sup>[1]</sup>", source_anchor_string)
self.assertIn('href="#footnote-1"', source_anchor_string)
self.assertIn('id="footnote-source-1"', source_anchor_string)

footnotes = soup.find("div", {"class": "footnotes"})
self.assertTrue(footnotes)

footnotes_string = str(footnotes)
self.assertIn('id="footnote-1"', footnotes_string)
self.assertIn('href="#footnote-source-1"', footnotes_string)
self.assertIn("[1] This is a French translated footnote", footnotes_string)
6 changes: 5 additions & 1 deletion tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.urls import include, path
from wagtail import urls as wagtail_urls
Expand All @@ -11,6 +12,9 @@
path("django-admin/", admin.site.urls),
path("admin/", include(wagtailadmin_urls)),
path("documents/", include(wagtaildocs_urls)),
]

urlpatterns += i18n_patterns(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be good to add a note, or even section in the README about usage with translation

path("footnotes/", include(footnotes_urls)),
path("", include(wagtail_urls)),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 4.2.9 on 2024-01-11 12:53

import django.db.models.deletion

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("wagtailcore", "0078_referenceindex"),
("wagtail_footnotes", "0002_alter_footnote_unique_together"),
]

operations = [
migrations.AlterUniqueTogether(
name="footnote",
unique_together={("page", "uuid")},
),
migrations.AddField(
model_name="footnote",
name="locale",
field=models.ForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="wagtailcore.locale",
),
),
migrations.AddField(
model_name="footnote",
name="translation_key",
field=models.UUIDField(editable=False, null=True),
),
migrations.AlterUniqueTogether(
name="footnote",
unique_together={("translation_key", "locale"), ("page", "uuid")},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 4.2.9 on 2024-01-11 12:56

from django.db import migrations
from wagtail.models import BootstrapTranslatableModel


class Migration(migrations.Migration):
dependencies = [
("wagtail_footnotes", "0003_add_bootstrap_translatable_mixin"),
]

operations = [
BootstrapTranslatableModel("wagtail_footnotes.Footnote"),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 4.2.9 on 2024-01-11 12:58

import uuid

import django.db.models.deletion

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("wagtailcore", "0078_referenceindex"),
("wagtail_footnotes", "0004_boostrap_translatable_mixin_data_migration"),
]

operations = [
migrations.AlterField(
model_name="footnote",
name="locale",
field=models.ForeignKey(
editable=False,
on_delete=django.db.models.deletion.PROTECT,
related_name="+",
to="wagtailcore.locale",
),
),
migrations.AlterField(
model_name="footnote",
name="translation_key",
field=models.UUIDField(default=uuid.uuid4, editable=False),
),
]
7 changes: 4 additions & 3 deletions wagtail_footnotes/models.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import FieldPanel
from wagtail.fields import RichTextField
from wagtail.models import TranslatableMixin

from wagtail_footnotes.fields import CustomUUIDField
from wagtail_footnotes.widgets import ReadonlyUUIDInput


class Footnote(models.Model):
class Footnote(TranslatableMixin, models.Model):
"""
Footnote has a UUID field which is set using JavaScript on object creation
so that it is available immediately for hardcoding a reference to the
Expand All @@ -29,8 +30,8 @@ class Footnote(models.Model):

panels = [FieldPanel("text"), FieldPanel("uuid", widget=ReadonlyUUIDInput)]

class Meta:
unique_together = ("page", "uuid")
class Meta(TranslatableMixin.Meta):
unique_together = [("page", "uuid"), ("translation_key", "locale")]

def __str__(self):
return str(self.uuid)
Loading