diff --git a/src/api/serializers.py b/src/api/serializers.py
index f9caf721ff..a5fb400585 100755
--- a/src/api/serializers.py
+++ b/src/api/serializers.py
@@ -2,8 +2,10 @@
from rest_framework import serializers, validators
+from django.conf import settings
from django.db import transaction
from django.shortcuts import reverse
+from modeltranslation.utils import build_localized_fieldname
from core import models as core_models, logic as core_logic
from journal import models as journal_models
@@ -13,6 +15,55 @@
from events import logic as event_logic
+def translatable_repository(instance):
+ """Return the repository governing the active languages for a translatable
+ preprint object (Preprint, PreprintVersion or VersionQueue)."""
+ if hasattr(instance, "repository"):
+ return instance.repository
+ return instance.preprint.repository
+
+
+def apply_translations(instance, field_name, translations):
+ """Set per-language values for a modeltranslation-managed field from a
+ ``{language_code: value}`` mapping. Does not save the instance."""
+ if not translations:
+ return
+ for code, value in translations.items():
+ setattr(instance, build_localized_fieldname(field_name, code), value)
+
+
+class TranslationsField(serializers.DictField):
+ """A readable and writable ``{language_code: value}`` map for a
+ modeltranslation-managed field. On read it exposes one entry per language
+ the preprint's repository accepts; on write it accepts a partial mapping."""
+
+ def __init__(self, translated_field, **kwargs):
+ self.translated_field = translated_field
+ kwargs.setdefault("required", False)
+ kwargs.setdefault(
+ "child",
+ serializers.CharField(allow_blank=True, trim_whitespace=False),
+ )
+ super().__init__(**kwargs)
+
+ def get_attribute(self, instance):
+ # to_representation builds the map from the instance itself.
+ return instance
+
+ def to_representation(self, instance):
+ repository = translatable_repository(instance)
+ languages = repository.languages or [settings.LANGUAGE_CODE]
+ return {
+ code: getattr(
+ instance,
+ build_localized_fieldname(self.translated_field, code),
+ "",
+ )
+ or ""
+ for code in languages
+ }
+
+
class LicenceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = submission_models.Licence
@@ -202,9 +253,14 @@ class Meta:
"date_time",
"title",
"abstract",
+ "title_translations",
+ "abstract_translations",
"public_download_url",
)
+ title_translations = TranslationsField("title", read_only=True)
+ abstract_translations = TranslationsField("abstract", read_only=True)
+
class PreprintSupplementaryFileSerializer(serializers.ModelSerializer):
class Meta:
@@ -372,6 +428,8 @@ class Meta:
"pk",
"title",
"abstract",
+ "title_translations",
+ "abstract_translations",
"stage",
"license",
"keywords",
@@ -389,6 +447,8 @@ class Meta:
)
depth = 2
+ title_translations = TranslationsField("title", read_only=True)
+ abstract_translations = TranslationsField("abstract", read_only=True)
authors = PreprintAccountSerializer(
many=True,
)
@@ -436,6 +496,15 @@ def create(self, validated_data):
comments_editor=validated_data.get("comments_editor"),
)
+ apply_translations(preprint, "title", validated_data.get("title_translations"))
+ apply_translations(
+ preprint, "abstract", validated_data.get("abstract_translations")
+ )
+ if validated_data.get("title_translations") or validated_data.get(
+ "abstract_translations"
+ ):
+ preprint.save()
+
for i, author_data in enumerate(validated_data.get("authors", [])):
author_email = author_data.pop("email").lower()
author, created = core_models.Account.objects.get_or_create(
@@ -530,6 +599,10 @@ def update(self, instance, validated_data):
instance.doi = validated_data.get("doi")
instance.preprint_doi = validated_data.get("preprint_doi")
instance.comments_editor = validated_data.get("comments_editor")
+ apply_translations(instance, "title", validated_data.get("title_translations"))
+ apply_translations(
+ instance, "abstract", validated_data.get("abstract_translations")
+ )
instance.save()
authors = []
@@ -633,6 +706,8 @@ class Meta:
"authors",
"title",
"abstract",
+ "title_translations",
+ "abstract_translations",
"stage",
"license",
"keywords",
@@ -649,6 +724,8 @@ class Meta:
"comments_editor",
)
+ title_translations = TranslationsField("title")
+ abstract_translations = TranslationsField("abstract")
authors = PreprintAccountSerializer(
many=True,
)
@@ -689,10 +766,15 @@ class Meta:
"update_type",
"title",
"abstract",
+ "title_translations",
+ "abstract_translations",
"published_doi",
"file",
)
+ title_translations = TranslationsField("title")
+ abstract_translations = TranslationsField("abstract")
+
def validate(self, data):
request = self.context.get("request", None)
preprint = data.get("preprint")
@@ -707,6 +789,16 @@ def validate(self, data):
return data
+ def create(self, validated_data):
+ title_translations = validated_data.pop("title_translations", None)
+ abstract_translations = validated_data.pop("abstract_translations", None)
+ version_queue = super().create(validated_data)
+ apply_translations(version_queue, "title", title_translations)
+ apply_translations(version_queue, "abstract", abstract_translations)
+ if title_translations or abstract_translations:
+ version_queue.save()
+ return version_queue
+
class VersionQueueSerializer(serializers.ModelSerializer):
class Meta:
@@ -720,9 +812,14 @@ class Meta:
"published_doi",
"title",
"abstract",
+ "title_translations",
+ "abstract_translations",
"file",
)
+ title_translations = TranslationsField("title", read_only=True)
+ abstract_translations = TranslationsField("abstract", read_only=True)
+
class RegisterAccountSerializer(serializers.ModelSerializer):
class Meta:
diff --git a/src/api/tests/test_preprints.py b/src/api/tests/test_preprints.py
index a9771f2547..b4db8a751d 100644
--- a/src/api/tests/test_preprints.py
+++ b/src/api/tests/test_preprints.py
@@ -16,6 +16,7 @@
from rest_framework.test import APIClient
+from api import serializers as api_serializers
from identifiers import forms as identifier_forms
from identifiers import models as identifier_models
from repository import models as repository_models
@@ -522,3 +523,122 @@ def test_owner_sees_only_own_files(self):
}
self.assertNotIn(self.preprint_other.pk, preprint_ids)
self.api_client.force_authenticate(user=None)
+
+
+MULTILINGUAL_API_DOMAIN = "preprint-api-multilingual.domain.com"
+MONOLINGUAL_API_DOMAIN = "preprint-api-monolingual.domain.com"
+
+
+class TestPreprintMultilingualAPI(TestCase):
+ """
+ Regression tests for multilingual title/abstract support in the preprint
+ API serializers (#3375). The repository uses django-modeltranslation, so
+ the serializers expose and accept a {language_code: value} map alongside
+ the primary title/abstract.
+ """
+
+ @classmethod
+ def setUpTestData(cls):
+ cls.press = helpers.create_press()
+ cls.author = helpers.create_user("preprint.api.multilingual@test.com")
+
+ cls.repo, cls.subject = helpers.create_repository(
+ cls.press, [], [], domain=MULTILINGUAL_API_DOMAIN
+ )
+ cls.repo.languages = ["en", "es"]
+ cls.repo.default_language = "en"
+ cls.repo.save()
+
+ cls.mono_repo, cls.mono_subject = helpers.create_repository(
+ cls.press, [], [], domain=MONOLINGUAL_API_DOMAIN
+ )
+ cls.mono_repo.languages = ["en"]
+ cls.mono_repo.save()
+
+ cls.preprint = helpers.create_preprint(
+ cls.repo, cls.author, cls.subject, title="English Title"
+ )
+ cls.preprint.title_en = "English Title"
+ cls.preprint.title_es = "Título en español"
+ cls.preprint.abstract_en = "English abstract"
+ cls.preprint.abstract_es = "Resumen en español"
+ cls.preprint.save()
+
+ cls.mono_preprint = helpers.create_preprint(
+ cls.mono_repo, cls.author, cls.mono_subject, title="Mono Title"
+ )
+
+ def test_read_serializer_exposes_translations_for_active_languages(self):
+ """The read serializer returns one entry per repository language."""
+ data = api_serializers.PreprintSerializer(self.preprint).data
+ self.assertEqual(
+ data["title_translations"],
+ {"en": "English Title", "es": "Título en español"},
+ )
+ self.assertEqual(
+ data["abstract_translations"],
+ {"en": "English abstract", "es": "Resumen en español"},
+ )
+
+ def test_read_serializer_single_language_repository(self):
+ """A single-language repository exposes a single translation entry."""
+ data = api_serializers.PreprintSerializer(self.mono_preprint).data
+ self.assertEqual(
+ data["title_translations"],
+ {"en": "Mono Title"},
+ )
+
+ def test_update_serializer_applies_translations(self):
+ """Updating via the create/update serializer writes per-language fields."""
+ validated_data = {
+ "title": "Updated English",
+ "abstract": "Updated English abstract",
+ "title_translations": {
+ "en": "Updated English",
+ "es": "Actualizado en español",
+ },
+ "abstract_translations": {"es": "Resumen actualizado"},
+ "owner": self.author,
+ "repository": self.repo,
+ "stage": repository_models.STAGE_PREPRINT_REVIEW,
+ "license": None,
+ "date_submitted": self.preprint.date_submitted,
+ "date_accepted": None,
+ "date_published": None,
+ "doi": None,
+ "preprint_doi": None,
+ "comments_editor": "",
+ "authors": [],
+ "keywords": [],
+ "subject": [],
+ "repositoryfieldanswer_set": [],
+ "preprintsupplementaryfile_set": [],
+ }
+ serializer = api_serializers.PreprintCreateSerializer()
+ result = serializer.update(self.preprint, validated_data)
+ result.refresh_from_db()
+
+ self.assertEqual(result.title_en, "Updated English")
+ self.assertEqual(result.title_es, "Actualizado en español")
+ self.assertEqual(result.abstract_es, "Resumen actualizado")
+
+ def test_version_queue_create_applies_translations(self):
+ """Creating a version queue entry writes per-language fields."""
+ serializer = api_serializers.VersionQueueCreateSerializer()
+ version_queue = serializer.create(
+ {
+ "preprint": self.preprint,
+ "update_type": "correction",
+ "title": "Version English",
+ "abstract": "Version English abstract",
+ "title_translations": {
+ "en": "Version English",
+ "es": "Versión en español",
+ },
+ "abstract_translations": {"es": "Resumen de la versión"},
+ }
+ )
+ version_queue.refresh_from_db()
+
+ self.assertEqual(version_queue.title_es, "Versión en español")
+ self.assertEqual(version_queue.abstract_es, "Resumen de la versión")
diff --git a/src/journal/middleware.py b/src/journal/middleware.py
index 2b284ffd17..b0b5c0ebf4 100644
--- a/src/journal/middleware.py
+++ b/src/journal/middleware.py
@@ -47,3 +47,20 @@ def process_request(request):
request.available_languages = set(available_languages)
request.default_language = default_language
request.current_language = translation.get_language()
+
+ elif getattr(request, "repository", None) and settings.USE_I18N:
+ current_language = translation.get_language()
+ available_languages = list(request.repository.languages or [])
+ default_language = (
+ request.repository.default_language or settings.LANGUAGE_CODE
+ )
+
+ if current_language not in available_languages:
+ translation.activate(default_language)
+
+ if not available_languages:
+ available_languages = [lang[0] for lang in settings.LANGUAGES]
+
+ request.available_languages = set(available_languages)
+ request.default_language = default_language
+ request.current_language = translation.get_language()
diff --git a/src/repository/forms.py b/src/repository/forms.py
index b22cb849e9..d712bea193 100755
--- a/src/repository/forms.py
+++ b/src/repository/forms.py
@@ -4,6 +4,7 @@
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.text import slugify
from django.contrib import messages
+from modeltranslation.utils import build_localized_fieldname
from tinymce.widgets import TinyMCE
@@ -90,6 +91,7 @@ class Meta:
fields = (
"title",
"abstract",
+ "language",
"license",
"comments_editor",
"subject",
@@ -108,6 +110,53 @@ def __init__(self, *args, **kwargs):
self.submission_type_slug = kwargs.pop("submission_type_slug", None)
super(PreprintInfo, self).__init__(*args, **kwargs)
+ repository = self.request.repository
+ active_languages = repository.languages or [settings.LANGUAGE_CODE]
+ lang_dict = dict(settings.LANGUAGES)
+ primary_language = repository.default_language or settings.LANGUAGE_CODE
+ is_multilingual = len(active_languages) > 1
+
+ self.language_field_names = []
+ if is_multilingual:
+ self.fields.pop("title", None)
+ self.fields.pop("abstract", None)
+ for code in active_languages:
+ lang_name = lang_dict.get(code, code)
+ title_field = build_localized_fieldname("title", code)
+ abstract_field = build_localized_fieldname("abstract", code)
+ self.fields[title_field] = forms.CharField(
+ max_length=300,
+ required=(code == primary_language),
+ label=_("Title ({})").format(lang_name),
+ widget=forms.TextInput(attrs={"placeholder": _("Title")}),
+ )
+ self.fields[abstract_field] = forms.CharField(
+ required=False,
+ label=_("Abstract ({})").format(lang_name),
+ widget=forms.Textarea(
+ attrs={"placeholder": _("Enter your article's abstract here")}
+ ),
+ )
+ if self.instance and self.instance.pk:
+ self.initial[title_field] = getattr(self.instance, title_field, "")
+ self.initial[abstract_field] = getattr(
+ self.instance, abstract_field, ""
+ )
+ self.language_field_names.extend([title_field, abstract_field])
+
+ if is_multilingual:
+ self.fields["language"] = forms.ChoiceField(
+ choices=[
+ (code, lang_dict.get(code, code)) for code in active_languages
+ ],
+ required=True,
+ label=_("Language"),
+ help_text=_("The primary language of this preprint."),
+ )
+ else:
+ self.initial["language"] = active_languages[0]
+ self.fields["language"].widget = forms.HiddenInput()
+
if (
not self.submission_type_slug
and self.instance
@@ -145,7 +194,7 @@ def __init__(self, *args, **kwargs):
)
elif element.input_type == "textarea":
self.fields[element.name] = forms.CharField(
- widget=forms.Textarea,
+ widget=forms.Textarea(),
required=element.required,
)
elif element.input_type == "date":
@@ -209,6 +258,10 @@ def save(self, commit=True):
preprint.repository = self.request.repository
+ for field_name in self.language_field_names:
+ if field_name in self.cleaned_data:
+ setattr(preprint, field_name, self.cleaned_data[field_name])
+
if self.request:
additional_fields = models.RepositoryField.objects.filter(
repository=self.request.repository,
@@ -485,14 +538,48 @@ class Meta:
def __init__(self, *args, **kwargs):
self.preprint = kwargs.pop("preprint")
super(VersionForm, self).__init__(*args, **kwargs)
- self.fields["title"].initial = self.preprint.title
- self.fields["abstract"].initial = self.preprint.abstract
+
+ repository = self.preprint.repository
+ active_languages = repository.languages or [settings.LANGUAGE_CODE]
+ lang_dict = dict(settings.LANGUAGES)
+
+ self.language_field_names = []
+ if len(active_languages) > 1:
+ self.fields.pop("title", None)
+ self.fields.pop("abstract", None)
+ for code in active_languages:
+ lang_name = lang_dict.get(code, code)
+ title_field = build_localized_fieldname("title", code)
+ abstract_field = build_localized_fieldname("abstract", code)
+ self.fields[title_field] = forms.CharField(
+ max_length=300,
+ required=False,
+ label=_("Title ({})").format(lang_name),
+ )
+ self.fields[abstract_field] = forms.CharField(
+ required=False,
+ label=_("Abstract ({})").format(lang_name),
+ widget=forms.Textarea(),
+ )
+ self.initial[title_field] = getattr(self.preprint, title_field, "")
+ self.initial[abstract_field] = getattr(
+ self.preprint, abstract_field, ""
+ )
+ self.language_field_names.extend([title_field, abstract_field])
+ else:
+ self.fields["title"].initial = self.preprint.title
+ self.fields["abstract"].initial = self.preprint.abstract
+
self.fields["published_doi"].initial = self.preprint.doi
def save(self, commit=True):
version = super(VersionForm, self).save(commit=False)
version.preprint = self.preprint
+ for field_name in self.language_field_names:
+ if field_name in self.cleaned_data:
+ setattr(version, field_name, self.cleaned_data[field_name])
+
if commit:
version.save()
diff --git a/src/repository/migrations/0057_add_multilingual_support.py b/src/repository/migrations/0057_add_multilingual_support.py
new file mode 100644
index 0000000000..e3882063e1
--- /dev/null
+++ b/src/repository/migrations/0057_add_multilingual_support.py
@@ -0,0 +1,303 @@
+# Generated by Django 4.2.29 on 2026-05-27 12:25
+
+import core.model_utils
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("repository", "0056_preprintfile_text"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="historicalrepository",
+ name="default_language",
+ field=models.CharField(
+ blank=True,
+ default="en",
+ help_text="The primary language of this repository.",
+ max_length=20,
+ ),
+ ),
+ migrations.AddField(
+ model_name="historicalrepository",
+ name="languages",
+ field=models.JSONField(
+ blank=True,
+ default=list,
+ help_text="List of language codes accepted by this repository, sourced from settings.LANGUAGES.",
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="abstract_cy",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="abstract_de",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="abstract_en",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="abstract_en_us",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="abstract_es",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="abstract_fr",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="abstract_nl",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="title_cy",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="title_de",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="title_en",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="title_en_us",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="title_es",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="title_fr",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprint",
+ name="title_nl",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="abstract_cy",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="abstract_de",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="abstract_en",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="abstract_en_us",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="abstract_es",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="abstract_fr",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="abstract_nl",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="title_cy",
+ field=models.CharField(
+ blank=True, help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="title_de",
+ field=models.CharField(
+ blank=True, help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="title_en",
+ field=models.CharField(
+ blank=True, help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="title_en_us",
+ field=models.CharField(
+ blank=True, help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="title_es",
+ field=models.CharField(
+ blank=True, help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="title_fr",
+ field=models.CharField(
+ blank=True, help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="preprintversion",
+ name="title_nl",
+ field=models.CharField(
+ blank=True, help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="repository",
+ name="default_language",
+ field=models.CharField(
+ blank=True,
+ default="en",
+ help_text="The primary language of this repository.",
+ max_length=20,
+ ),
+ ),
+ migrations.AddField(
+ model_name="repository",
+ name="languages",
+ field=models.JSONField(
+ blank=True,
+ default=list,
+ help_text="List of language codes accepted by this repository, sourced from settings.LANGUAGES.",
+ ),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="abstract_cy",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="abstract_de",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="abstract_en",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="abstract_en_us",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="abstract_es",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="abstract_fr",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="abstract_nl",
+ field=core.model_utils.JanewayBleachField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="title_cy",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="title_de",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="title_en",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="title_en_us",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="title_es",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="title_fr",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ migrations.AddField(
+ model_name="versionqueue",
+ name="title_nl",
+ field=models.CharField(
+ help_text="Your article title", max_length=300, null=True
+ ),
+ ),
+ ]
diff --git a/src/repository/migrations/0058_add_preprint_language_field.py b/src/repository/migrations/0058_add_preprint_language_field.py
new file mode 100644
index 0000000000..03938c76b5
--- /dev/null
+++ b/src/repository/migrations/0058_add_preprint_language_field.py
@@ -0,0 +1,22 @@
+# Generated by Django 4.2.29 on 2026-05-27 13:34
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("repository", "0057_add_multilingual_support"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="preprint",
+ name="language",
+ field=models.CharField(
+ blank=True,
+ default="en",
+ help_text="The primary language of this preprint.",
+ max_length=20,
+ ),
+ ),
+ ]
diff --git a/src/repository/models.py b/src/repository/models.py
index c10859e3da..1b297f3344 100755
--- a/src/repository/models.py
+++ b/src/repository/models.py
@@ -33,6 +33,7 @@
from django.utils.html import format_html
from django.core.validators import RegexValidator
+from modeltranslation.utils import build_localized_fieldname
from openpyxl import load_workbook
from simple_history.models import HistoricalRecords
import swapper
@@ -363,6 +364,20 @@ class Repository(model_utils.AbstractSiteModel):
default=False,
help_text="Enable to use Crossref test.",
)
+ languages = models.JSONField(
+ default=list,
+ blank=True,
+ help_text=_(
+ "List of language codes accepted by this repository, "
+ "sourced from settings.LANGUAGES."
+ ),
+ )
+ default_language = models.CharField(
+ max_length=20,
+ blank=True,
+ default="en",
+ help_text=_("The primary language of this repository."),
+ )
class Meta:
verbose_name_plural = "repositories"
@@ -846,6 +861,12 @@ class Preprint(models.Model):
null=True,
on_delete=models.SET_NULL,
)
+ language = models.CharField(
+ max_length=20,
+ blank=True,
+ default="en",
+ help_text=_("The primary language of this preprint."),
+ )
comments_editor = models.TextField(
blank=True,
null=True,
@@ -901,6 +922,29 @@ def __str__(self):
self.title,
)
+ @property
+ def language_name(self):
+ lang_dict = dict(settings.LANGUAGES)
+ return lang_dict.get(self.language, self.language or "")
+
+ def translated_metadata(self):
+ active_languages = self.repository.languages if self.repository else []
+ if len(active_languages) <= 1:
+ return []
+ lang_dict = dict(settings.LANGUAGES)
+ result = []
+ for code in active_languages:
+ title_field = build_localized_fieldname("title", code)
+ abstract_field = build_localized_fieldname("abstract", code)
+ result.append(
+ {
+ "language": lang_dict.get(code, code),
+ "title": getattr(self, title_field, "") or "",
+ "abstract": getattr(self, abstract_field, "") or "",
+ }
+ )
+ return result
+
def old_versions(self):
return PreprintVersion.objects.filter(
preprint=self,
@@ -1881,12 +1925,32 @@ def approve(self):
self.approved = True
current_version = None
+ active_languages = self.preprint.repository.languages or list(
+ dict(settings.LANGUAGES).keys()
+ )
+ translated_fields = [
+ (
+ build_localized_fieldname("title", code),
+ build_localized_fieldname("abstract", code),
+ )
+ for code in active_languages
+ ]
+
# Update the current version to have the Preprint's current title
# and abstract.
if self.preprint.current_version is not None:
current_version = self.preprint.current_version
- current_version.title = self.preprint.title
- current_version.abstract = self.preprint.abstract
+ for title_field, abstract_field in translated_fields:
+ setattr(
+ current_version,
+ title_field,
+ getattr(self.preprint, title_field, ""),
+ )
+ setattr(
+ current_version,
+ abstract_field,
+ getattr(self.preprint, abstract_field, ""),
+ )
current_version.published_doi = self.preprint.doi
this_file = self.preprint.current_version.file
# no version yet
@@ -1905,11 +1969,13 @@ def approve(self):
)
# Overwrite the preprint's metadata now we have a historical record.
- # Check that title and abstract have value, if not there is no change.
- if self.title:
- self.preprint.title = self.title
- if self.abstract:
- self.preprint.abstract = self.abstract
+ for title_field, abstract_field in translated_fields:
+ title_val = getattr(self, title_field, "")
+ abstract_val = getattr(self, abstract_field, "")
+ if title_val:
+ setattr(self.preprint, title_field, title_val)
+ if abstract_val:
+ setattr(self.preprint, abstract_field, abstract_val)
if self.published_doi:
self.preprint.doi = self.published_doi
@@ -1937,6 +2003,28 @@ def status(self):
else:
return _("Declined")
+ def translated_metadata(self):
+ active_languages = (
+ self.preprint.repository.languages
+ if self.preprint and self.preprint.repository
+ else []
+ )
+ if len(active_languages) <= 1:
+ return []
+ lang_dict = dict(settings.LANGUAGES)
+ result = []
+ for code in active_languages:
+ title_field = build_localized_fieldname("title", code)
+ abstract_field = build_localized_fieldname("abstract", code)
+ result.append(
+ {
+ "language": lang_dict.get(code, code),
+ "title": getattr(self, title_field, "") or "",
+ "abstract": getattr(self, abstract_field, "") or "",
+ }
+ )
+ return result
+
@property
def safe_title(self):
if self.title:
diff --git a/src/repository/tests/test_languages.py b/src/repository/tests/test_languages.py
new file mode 100644
index 0000000000..42381b146d
--- /dev/null
+++ b/src/repository/tests/test_languages.py
@@ -0,0 +1,347 @@
+from django.conf import settings
+from django.test import TestCase, override_settings
+from django.shortcuts import reverse
+from django.urls.base import clear_script_prefix
+
+from utils.testing import helpers
+from repository import models as rm
+
+
+class TestRepositoryLanguages(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.press = helpers.create_press()
+ cls.press.save()
+ cls.repo_manager = helpers.create_user("lang_manager@janeway.systems")
+ cls.repo_manager.is_active = True
+ cls.repo_manager.save()
+ cls.server_name = "lang-repo.test.com"
+ cls.repository, cls.subject = helpers.create_repository(
+ cls.press,
+ [cls.repo_manager],
+ [],
+ domain=cls.server_name,
+ )
+
+ def setUp(self):
+ clear_script_prefix()
+ self.repository.languages = ["en"]
+ self.repository.default_language = "en"
+ self.repository.save()
+
+ @override_settings(URL_CONFIG="domain")
+ def test_default_language_is_english(self):
+ self.assertEqual(self.repository.default_language, "en")
+ self.assertEqual(self.repository.languages, ["en"])
+
+ @override_settings(URL_CONFIG="domain")
+ def test_enable_language(self):
+ self.client.force_login(self.repo_manager)
+ self.client.post(
+ reverse("repository_languages"),
+ {"enable": "es"},
+ SERVER_NAME=self.server_name,
+ )
+ self.repository.refresh_from_db()
+ self.assertIn("es", self.repository.languages)
+ self.assertIn("en", self.repository.languages)
+
+ @override_settings(URL_CONFIG="domain")
+ def test_disable_language(self):
+ self.repository.languages = ["en", "es"]
+ self.repository.save()
+ self.client.force_login(self.repo_manager)
+ self.client.post(
+ reverse("repository_languages"),
+ {"disable": "es"},
+ SERVER_NAME=self.server_name,
+ )
+ self.repository.refresh_from_db()
+ self.assertNotIn("es", self.repository.languages)
+ self.assertIn("en", self.repository.languages)
+
+ @override_settings(URL_CONFIG="domain")
+ def test_set_default_language(self):
+ self.repository.languages = ["en", "es"]
+ self.repository.save()
+ self.client.force_login(self.repo_manager)
+ self.client.post(
+ reverse("repository_languages"),
+ {"default": "es"},
+ SERVER_NAME=self.server_name,
+ )
+ self.repository.refresh_from_db()
+ self.assertEqual(self.repository.default_language, "es")
+
+ @override_settings(URL_CONFIG="domain")
+ def test_set_default_to_inactive_language_rejected(self):
+ self.client.force_login(self.repo_manager)
+ self.client.post(
+ reverse("repository_languages"),
+ {"default": "de"},
+ SERVER_NAME=self.server_name,
+ )
+ self.repository.refresh_from_db()
+ self.assertEqual(self.repository.default_language, "en")
+
+ @override_settings(URL_CONFIG="domain")
+ def test_disable_all_languages_falls_back_to_default(self):
+ self.client.force_login(self.repo_manager)
+ self.client.post(
+ reverse("repository_languages"),
+ {"disable": "en"},
+ SERVER_NAME=self.server_name,
+ )
+ self.repository.refresh_from_db()
+ self.assertIn(settings.LANGUAGE_CODE, self.repository.languages)
+
+ @override_settings(URL_CONFIG="domain")
+ def test_default_language_reset_when_disabled(self):
+ self.repository.languages = ["en", "es"]
+ self.repository.default_language = "es"
+ self.repository.save()
+ self.client.force_login(self.repo_manager)
+ self.client.post(
+ reverse("repository_languages"),
+ {"disable": "es"},
+ SERVER_NAME=self.server_name,
+ )
+ self.repository.refresh_from_db()
+ self.assertEqual(self.repository.default_language, "en")
+
+
+class TestPreprintTranslation(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.press = helpers.create_press()
+ cls.press.save()
+ cls.repo_manager = helpers.create_user("lang_trans_mgr@janeway.systems")
+ cls.repo_manager.is_active = True
+ cls.repo_manager.save()
+ cls.author = helpers.create_user("lang_author@janeway.systems")
+ cls.author.is_active = True
+ cls.author.save()
+ cls.server_name = "lang-trans.test.com"
+ cls.repository, cls.subject = helpers.create_repository(
+ cls.press,
+ [cls.repo_manager],
+ [],
+ domain=cls.server_name,
+ )
+ cls.preprint = helpers.create_preprint(
+ cls.repository,
+ cls.author,
+ cls.subject,
+ )
+
+ def setUp(self):
+ clear_script_prefix()
+
+ def test_preprint_translation_saves(self):
+ self.preprint.title_en = "English Title"
+ self.preprint.title_es = "Título en Español"
+ self.preprint.abstract_en = "English abstract"
+ self.preprint.abstract_es = "Resumen en español"
+ self.preprint.save()
+ self.preprint.refresh_from_db()
+ self.assertEqual(self.preprint.title_en, "English Title")
+ self.assertEqual(self.preprint.title_es, "Título en Español")
+ self.assertEqual(self.preprint.abstract_en, "English abstract")
+ self.assertEqual(self.preprint.abstract_es, "Resumen en español")
+
+ def test_translated_metadata_empty_for_single_language(self):
+ self.repository.languages = ["en"]
+ self.repository.save()
+ self.preprint.repository = self.repository
+ self.assertEqual(self.preprint.translated_metadata(), [])
+
+ def test_translated_metadata_returns_entry_per_active_language(self):
+ self.repository.languages = ["en", "es"]
+ self.repository.save()
+ self.preprint.repository = self.repository
+ self.preprint.title_en = "English Title"
+ self.preprint.title_es = "Título en Español"
+ self.preprint.abstract_en = "English abstract"
+ self.preprint.abstract_es = "Resumen en español"
+ metadata = self.preprint.translated_metadata()
+ self.assertEqual(len(metadata), 2)
+ by_title = {entry["title"]: entry for entry in metadata}
+ self.assertIn("English Title", by_title)
+ self.assertIn("Título en Español", by_title)
+ self.assertEqual(
+ by_title["Título en Español"]["abstract"],
+ "Resumen en español",
+ )
+
+ def test_form_shows_per_language_fields_when_multiple(self):
+ from repository.forms import PreprintInfo
+
+ self.repository.languages = ["en", "es"]
+ self.repository.save()
+ request = helpers.Request()
+ request.press = self.press
+ request.repository = self.repository
+ form = PreprintInfo(
+ instance=self.preprint,
+ request=request,
+ submission_type_slug=None,
+ )
+ self.assertIn("title_en", form.fields)
+ self.assertIn("title_es", form.fields)
+ self.assertIn("abstract_en", form.fields)
+ self.assertIn("abstract_es", form.fields)
+ self.assertNotIn("title", form.fields)
+ self.assertNotIn("abstract", form.fields)
+
+ def test_form_shows_base_fields_when_single_language(self):
+ from repository.forms import PreprintInfo
+
+ self.repository.languages = ["en"]
+ self.repository.save()
+ request = helpers.Request()
+ request.press = self.press
+ request.repository = self.repository
+ form = PreprintInfo(
+ instance=self.preprint,
+ request=request,
+ submission_type_slug=None,
+ )
+ self.assertIn("title", form.fields)
+ self.assertIn("abstract", form.fields)
+ self.assertNotIn("title_en", form.fields)
+ self.assertNotIn("title_es", form.fields)
+
+ def test_form_primary_language_title_required(self):
+ from repository.forms import PreprintInfo
+
+ self.repository.languages = ["en", "es"]
+ self.repository.save()
+ request = helpers.Request()
+ request.press = self.press
+ request.repository = self.repository
+ form = PreprintInfo(
+ instance=self.preprint,
+ request=request,
+ submission_type_slug=None,
+ )
+ self.assertTrue(form.fields["title_en"].required)
+ self.assertFalse(form.fields["title_es"].required)
+
+ def test_form_required_title_follows_repository_default_language(self):
+ from repository.forms import PreprintInfo
+
+ self.repository.languages = ["en", "es"]
+ self.repository.default_language = "es"
+ self.repository.save()
+ request = helpers.Request()
+ request.press = self.press
+ request.repository = self.repository
+ form = PreprintInfo(
+ instance=self.preprint,
+ request=request,
+ submission_type_slug=None,
+ )
+ self.assertTrue(form.fields["title_es"].required)
+ self.assertFalse(form.fields["title_en"].required)
+
+ def test_form_language_dropdown_with_multiple_languages(self):
+ from repository.forms import PreprintInfo
+
+ self.repository.languages = ["en", "es"]
+ self.repository.save()
+ request = helpers.Request()
+ request.press = self.press
+ request.repository = self.repository
+ form = PreprintInfo(
+ instance=self.preprint,
+ request=request,
+ submission_type_slug=None,
+ )
+ self.assertIn("language", form.fields)
+ choice_values = [c[0] for c in form.fields["language"].choices]
+ self.assertEqual(choice_values, ["en", "es"])
+
+ def test_form_language_hidden_with_single_language(self):
+ from repository.forms import PreprintInfo
+ from django.forms import HiddenInput
+
+ self.repository.languages = ["en"]
+ self.repository.save()
+ request = helpers.Request()
+ request.press = self.press
+ request.repository = self.repository
+ form = PreprintInfo(
+ instance=self.preprint,
+ request=request,
+ submission_type_slug=None,
+ )
+ self.assertIsInstance(form.fields["language"].widget, HiddenInput)
+ self.assertEqual(form.initial["language"], "en")
+
+ def test_version_form_shows_per_language_fields_when_multiple(self):
+ from repository.forms import VersionForm
+
+ self.repository.languages = ["en", "es"]
+ self.repository.save()
+ self.preprint.repository = self.repository
+ form = VersionForm(preprint=self.preprint)
+ self.assertIn("title_en", form.fields)
+ self.assertIn("title_es", form.fields)
+ self.assertIn("abstract_en", form.fields)
+ self.assertIn("abstract_es", form.fields)
+ self.assertNotIn("title", form.fields)
+ self.assertNotIn("abstract", form.fields)
+
+ def test_version_form_shows_base_fields_when_single_language(self):
+ from repository.forms import VersionForm
+
+ self.repository.languages = ["en"]
+ self.repository.save()
+ self.preprint.repository = self.repository
+ form = VersionForm(preprint=self.preprint)
+ self.assertIn("title", form.fields)
+ self.assertIn("abstract", form.fields)
+ self.assertNotIn("title_en", form.fields)
+ self.assertNotIn("title_es", form.fields)
+
+
+class TestRepositoryLanguageMiddleware(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.press = helpers.create_press()
+ cls.press.save()
+ cls.repo_manager = helpers.create_user("lang_mw_mgr@janeway.systems")
+ cls.repo_manager.is_active = True
+ cls.repo_manager.save()
+ cls.server_name = "lang-mw.test.com"
+ cls.repository, cls.subject = helpers.create_repository(
+ cls.press,
+ [cls.repo_manager],
+ [],
+ domain=cls.server_name,
+ )
+
+ def setUp(self):
+ clear_script_prefix()
+ self.repository.languages = ["en", "es"]
+ self.repository.default_language = "en"
+ self.repository.save()
+
+ @override_settings(URL_CONFIG="domain")
+ def test_middleware_sets_available_languages(self):
+ self.client.force_login(self.repo_manager)
+ response = self.client.get(
+ reverse("repository_languages"),
+ SERVER_NAME=self.server_name,
+ )
+ self.assertIn("en", response.wsgi_request.available_languages)
+ self.assertIn("es", response.wsgi_request.available_languages)
+
+ @override_settings(URL_CONFIG="domain")
+ def test_middleware_sets_default_language(self):
+ self.client.force_login(self.repo_manager)
+ response = self.client.get(
+ reverse("repository_languages"),
+ SERVER_NAME=self.server_name,
+ )
+ self.assertEqual(response.wsgi_request.default_language, "en")
diff --git a/src/repository/translation.py b/src/repository/translation.py
new file mode 100644
index 0000000000..f77f12b0f5
--- /dev/null
+++ b/src/repository/translation.py
@@ -0,0 +1,18 @@
+from modeltranslation.translator import register, TranslationOptions
+
+from repository import models
+
+
+@register(models.Preprint)
+class PreprintTranslationOptions(TranslationOptions):
+ fields = ("title", "abstract")
+
+
+@register(models.PreprintVersion)
+class PreprintVersionTranslationOptions(TranslationOptions):
+ fields = ("title", "abstract")
+
+
+@register(models.VersionQueue)
+class VersionQueueTranslationOptions(TranslationOptions):
+ fields = ("title", "abstract")
diff --git a/src/repository/urls.py b/src/repository/urls.py
index 40b1f1ae1b..a98084d641 100755
--- a/src/repository/urls.py
+++ b/src/repository/urls.py
@@ -236,6 +236,11 @@
re_path(
r"^manager/licenses/$", views.repository_licenses, name="repository_licenses"
),
+ re_path(
+ r"^manager/languages/$",
+ views.repository_languages,
+ name="repository_languages",
+ ),
re_path(
r"^manager/subjects/$", views.repository_subjects, name="repository_subjects"
),
diff --git a/src/repository/views.py b/src/repository/views.py
index 8edd100b1c..dee0fc24cd 100644
--- a/src/repository/views.py
+++ b/src/repository/views.py
@@ -6,8 +6,9 @@
from datetime import datetime
from dateutil import tz
+from django.conf import settings
from django.shortcuts import render, redirect, get_object_or_404
-from django.utils import timezone
+from django.utils import timezone, translation
from django.db.models import Q, Count
from django.db.models.query import RawQuerySet
from django.urls import reverse
@@ -1707,6 +1708,12 @@ def repository_wizard(request, short_name=None, step="1"):
)
)
+ active_language_names = []
+ if repository and step == "3":
+ lang_dict = dict(settings.LANGUAGES)
+ active_codes = repository.languages or [settings.LANGUAGE_CODE]
+ active_language_names = [lang_dict.get(code, code) for code in active_codes]
+
template = "admin/repository/wizard.html"
context = {
"repository": repository,
@@ -1715,6 +1722,7 @@ def repository_wizard(request, short_name=None, step="1"):
"help_template": "admin/elements/repository/{step}_help.html".format(
step=step,
),
+ "repository_active_language_names": active_language_names,
}
return render(request, template, context)
@@ -2529,6 +2537,87 @@ def repository_licenses(request):
)
+@is_repository_manager
+def repository_languages(request):
+ repository = request.repository
+ active_languages = repository.languages or [settings.LANGUAGE_CODE]
+
+ if request.POST:
+ if "default" in request.POST:
+ new_default = request.POST.get("default")
+ if new_default in active_languages:
+ repository.default_language = new_default
+ messages.add_message(
+ request,
+ messages.SUCCESS,
+ "Default language now set to {}.".format(new_default),
+ )
+ else:
+ messages.add_message(
+ request,
+ messages.WARNING,
+ "{} is not an active language for this repository.".format(
+ new_default
+ ),
+ )
+ if "enable" in request.POST:
+ lang_to_enable = request.POST.get("enable")
+ if lang_to_enable not in dict(settings.LANGUAGES):
+ messages.add_message(
+ request,
+ messages.WARNING,
+ "{} is not a valid language.".format(lang_to_enable),
+ )
+ else:
+ if lang_to_enable not in active_languages:
+ active_languages.append(lang_to_enable)
+ messages.add_message(
+ request,
+ messages.SUCCESS,
+ "{} enabled.".format(lang_to_enable),
+ )
+ if "disable" in request.POST:
+ lang_to_disable = request.POST.get("disable")
+ if lang_to_disable in active_languages:
+ active_languages.remove(lang_to_disable)
+ messages.add_message(
+ request,
+ messages.SUCCESS,
+ "{} disabled.".format(lang_to_disable),
+ )
+ else:
+ messages.add_message(
+ request,
+ messages.WARNING,
+ "{} is not an active language for this repository.".format(
+ lang_to_disable
+ ),
+ )
+
+ if not active_languages:
+ active_languages.append(settings.LANGUAGE_CODE)
+
+ repository.languages = active_languages
+ if repository.default_language not in active_languages:
+ repository.default_language = active_languages[0]
+ repository.save()
+ return redirect(reverse("repository_languages"))
+
+ all_languages = settings.LANGUAGES
+ lang_dict = dict(all_languages)
+ active_language_details = [
+ (code, lang_dict.get(code, code)) for code in active_languages
+ ]
+
+ template = "admin/repository/languages.html"
+ context = {
+ "active_languages": active_languages,
+ "active_language_details": active_language_details,
+ "language_choices": all_languages,
+ }
+ return render(request, template, context)
+
+
@is_repository_manager
def send_user_email(request, user_id, preprint_id):
user = get_object_or_404(core_models.Account, pk=user_id)
diff --git a/src/templates/admin/elements/repository/1_help.html b/src/templates/admin/elements/repository/1_help.html
index a9ea5f8c7d..ae35a1d586 100644
--- a/src/templates/admin/elements/repository/1_help.html
+++ b/src/templates/admin/elements/repository/1_help.html
@@ -17,4 +17,4 @@
The publisher name is used across the site and as the Publisher for Crossref deposit and Dublin Core metadata output. It should be the name of your organisation for example: Birkbeck, University of London
-
\ No newline at end of file
+
diff --git a/src/templates/admin/elements/repository/metadata_form.html b/src/templates/admin/elements/repository/metadata_form.html
index f92cd73deb..cd58618f65 100644
--- a/src/templates/admin/elements/repository/metadata_form.html
+++ b/src/templates/admin/elements/repository/metadata_form.html
@@ -3,14 +3,31 @@
{% load field %}
{% include "elements/forms/errors.html" with form=form %}
-
- {{ form.title|foundation }}
-
+{% if form.language_field_names %}
+
+
+
{% trans "This repository accepts submissions in multiple languages. Please provide a title and abstract in the primary language. Translations are optional." %}
+
+
+ {% for field_name in form.language_field_names %}
+ {% get_form_field form field_name as lang_field %}
+
+ {{ lang_field|foundation }}
+
+ {% endfor %}
+{% else %}
+
+ {{ form.title|foundation }}
+
+
+ {{ form.abstract|foundation }}
+
+{% endif %}
{{ form.submission_type|foundation }}
- {{ form.abstract|foundation }}
+ {{ form.language|foundation }}
{{ form.license|foundation }}
diff --git a/src/templates/admin/elements/repository/version_detail.html b/src/templates/admin/elements/repository/version_detail.html
index 6f2fdcd3ba..086d84eff6 100644
--- a/src/templates/admin/elements/repository/version_detail.html
+++ b/src/templates/admin/elements/repository/version_detail.html
@@ -16,10 +16,21 @@
{{ preprint.title|safe }} Version Detail
Current Metadata
-
Title
-
{{ version.preprint.title|safe }}
-
Abstract
-
{{ version.preprint.abstract|safe|linebreaksbr }}
+ {% with version.preprint.translated_metadata as current_meta %}
+ {% if current_meta %}
+ {% for meta in current_meta %}
+
Title ({{ meta.language }})
+
{{ meta.title|default:"—" }}
+
Abstract ({{ meta.language }})
+
{{ meta.abstract|default:"—"|safe|linebreaksbr }}
+ {% endfor %}
+ {% else %}
+
Title
+
{{ version.preprint.title|safe }}
+
Abstract
+
{{ version.preprint.abstract|safe|linebreaksbr }}
+ {% endif %}
+ {% endwith %}
Published DOI
{{ version.preprint.doi }}
File
@@ -33,10 +44,21 @@
Current Metadata
New Metadata
-
Title{% if version.title and version.title != version.preprint.title %} (changed){% endif %}
-
{% if version.title == version.preprint.title %}Unchanged{% else %} {{ version.title }}{% endif %}
-
Abstract{% if version.abstract and version.abstract != version.preprint.abstract %} (changed){% endif %}
-
{% if version.abstract == version.preprint.abstract %}Unchanged{% else %}{{ version.abstract|safe|linebreaksbr }}{% endif %}
+ {% with version.translated_metadata as new_meta %}
+ {% if new_meta %}
+ {% for meta in new_meta %}
+
Title ({{ meta.language }})
+
{{ meta.title|default:"Unchanged" }}
+
Abstract ({{ meta.language }})
+
{{ meta.abstract|default:"Unchanged"|safe|linebreaksbr }}
+ {% endfor %}
+ {% else %}
+
Title{% if version.title and version.title != version.preprint.title %} (changed){% endif %}
+
{% if version.title == version.preprint.title %}Unchanged{% else %} {{ version.title }}{% endif %}
+
Abstract{% if version.abstract and version.abstract != version.preprint.abstract %} (changed){% endif %}
+
{% if version.abstract == version.preprint.abstract %}Unchanged{% else %}{{ version.abstract|safe|linebreaksbr }}{% endif %}
+ {% endif %}
+ {% endwith %}
Published DOI{% if version.published_doi and version.published_doi != version.preprint.doi %} (changed){% endif %}
{% if version.published_doi == version.preprint.doi %}Unchanged{% else %}{{ version.published_doi }}{% endif %}
diff --git a/src/templates/admin/press/nav.html b/src/templates/admin/press/nav.html
index ff848295a5..8d43c35ba3 100644
--- a/src/templates/admin/press/nav.html
+++ b/src/templates/admin/press/nav.html
@@ -19,11 +19,12 @@
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
- {% get_language_info_list for LANGUAGES as languages %}
- {% for language in languages %}
-
- {{ language.name_local }} ({{ language.code }})
+ {% for lang_code, lang_name in LANGUAGES %}
+ {% if not request.available_languages or lang_code in request.available_languages %}
+
+ {{ lang_name }} ({{ lang_code }})
+ {% endif %}
{% endfor %}
@@ -41,6 +42,7 @@
{% if request.user.is_staff %}
Subjects
Licences
+
Languages
Additional Fields
Recommendations
Settings
diff --git a/src/templates/admin/repository/article.html b/src/templates/admin/repository/article.html
index a686861fc3..62fcb1c50b 100644
--- a/src/templates/admin/repository/article.html
+++ b/src/templates/admin/repository/article.html
@@ -26,24 +26,26 @@
Metadata