From ca63a51809ac887602fab06ecc50f59d580a1fb0 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sat, 26 Jul 2025 22:25:55 +0200 Subject: [PATCH 01/13] fix: Show internal links in the language of the plugin --- djangocms_link/cms_plugins.py | 11 +++++++++++ djangocms_link/fields.py | 37 +++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/djangocms_link/cms_plugins.py b/djangocms_link/cms_plugins.py index cd42e21d..c9a11c65 100644 --- a/djangocms_link/cms_plugins.py +++ b/djangocms_link/cms_plugins.py @@ -53,5 +53,16 @@ def render(self, context, instance, placeholder): ) return super().render(context, instance, placeholder) + def get_form(self, request, obj=None, **kwargs): + form = super().get_form(request, obj, **kwargs) + + if obj: + language = getattr(obj, "language", None) + else: + language = request.GET.get("plugin_language", None) + for widget in self.fields["link"].widget.widgets: + widget.language = language + return form + plugin_pool.register_plugin(LinkPlugin) diff --git a/djangocms_link/fields.py b/djangocms_link/fields.py index a2ed7ee8..b03035ce 100644 --- a/djangocms_link/fields.py +++ b/djangocms_link/fields.py @@ -31,8 +31,9 @@ class LinkAutoCompleteWidget(AutocompleteSelect): - def __init__(self, attrs: dict | None = None): + def __init__(self, attrs: dict | None = None, language: str | None = None): super().__init__(None, None, attrs) + self.language = language def get_internal_obj(self, values: list[str | None]) -> list[models.Model | None]: internal_obj = [] @@ -68,7 +69,10 @@ def optgroups(self, name: str, value: str, attr: str | None = None): return groups def get_url(self): - return admin_reverse("djangocms_link_link_urls") + reverse = admin_reverse("djangocms_link_link_urls") + if self.language: + return f"{reverse}?language={self.language}" + return reverse def build_attrs(self, base_attrs: dict, extra_attrs: dict | None = None) -> dict: """ @@ -233,7 +237,7 @@ class Media: js = ("djangocms_link/link-widget.js",) css = {"all": ("djangocms_link/link-widget.css",)} - def __init__(self, site_selector: bool | None = None): + def __init__(self, site_selector: bool | None = None, language: str | None = None): if site_selector is None: site_selector = LinkWidget.default_site_selector @@ -242,22 +246,24 @@ def __init__(self, site_selector: bool | None = None): for key, widget in _available_widgets.items() if key == "always" or _mapping[key] in link_types ] - if site_selector and "internal_link" in allowed_link_types: + if "internal_link" in allowed_link_types: index = next( i for i, widget in enumerate(widgets) if widget.attrs.get("widget") == "internal_link" ) - widgets.insert( - index, - SiteAutocompleteSelect( - attrs={ - "class": "js-link-site-widget", - "widget": "site", - "data-placeholder": _("Select site"), - }, - ), - ) # Site selector + widgets[index].language = language # Pass on language to the internal link widget + if site_selector: + widgets.insert( + index, + SiteAutocompleteSelect( + attrs={ + "class": "js-link-site-widget", + "widget": "site", + "data-placeholder": _("Select site"), + }, + ), + ) # Site selector # Remember which widget expets its content at which position self.data_pos = { @@ -287,7 +293,6 @@ def get_context(self, name: str, value: str | None, attrs: dict) -> dict: class LinkFormField(Field): - widget = LinkWidget external_link_validators = [ ExtendedURLValidator(allowed_link_types=allowed_link_types) ] @@ -302,6 +307,7 @@ def __init__(self, *args, **kwargs): kwargs.setdefault("initial", {}) kwargs.pop("encoder", None) # Passed from LinkField's JSONField parent class kwargs.pop("decoder", None) # but not needed + self.widget = LinkWidget(language=kwargs.pop("language", None)) super().__init__(*args, **kwargs) if isinstance(self.initial, dict): self.initial = self.prepare_value(self.initial) @@ -366,6 +372,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def formfield(self, **kwargs): + print("LinkField.formfield", kwargs) kwargs.setdefault("form_class", LinkFormField) return super().formfield(**kwargs) From 822171877dc929d53b767ee70c0de62f0deeb019 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sat, 26 Jul 2025 22:32:34 +0200 Subject: [PATCH 02/13] Adjust to get_form processing classes only --- djangocms_link/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_link/cms_plugins.py b/djangocms_link/cms_plugins.py index c9a11c65..dde200e8 100644 --- a/djangocms_link/cms_plugins.py +++ b/djangocms_link/cms_plugins.py @@ -60,7 +60,7 @@ def get_form(self, request, obj=None, **kwargs): language = getattr(obj, "language", None) else: language = request.GET.get("plugin_language", None) - for widget in self.fields["link"].widget.widgets: + for widget in form.base_fields["link"].widget.widgets: widget.language = language return form From c6aef27d4a9f8b858493ba9de5a6f7e404d20371 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sat, 26 Jul 2025 23:15:38 +0200 Subject: [PATCH 03/13] Show inital value in correct language --- djangocms_link/cms_plugins.py | 37 +++++++++++++++++++++++---------- djangocms_link/fields.py | 39 +++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/djangocms_link/cms_plugins.py b/djangocms_link/cms_plugins.py index dde200e8..bf698add 100644 --- a/djangocms_link/cms_plugins.py +++ b/djangocms_link/cms_plugins.py @@ -1,13 +1,39 @@ from django.contrib.sites.shortcuts import get_current_site +from django.db import models from django.utils.translation import gettext_lazy as _ from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool +from djangocms_link.fields import LinkFormField + from .helpers import get_link from .models import Link +def patch(original: callable) -> callable: + """ + Patch the get_form method of CMSPluginBase to ensure that the + 'language' attribute is set on the link widget's subwidgets in the form. + This is necessary for the link field to work correctly with multilingual sites and + shows the internal link target in the correct plugin language. + """ + def get_form(self, request, obj: models.Model | None = None, change: bool = False, **kwargs): + form = original(self, request, obj=obj, change=change, **kwargs) + + language = getattr(obj, "language", None) if obj else request.GET.get("plugin_language", None) + for field in form.base_fields.values(): + if isinstance(field, LinkFormField): + for widget in field.widget.widgets: + widget.language = language + return form + + return get_form + + +CMSPluginBase.get_form = patch(CMSPluginBase.get_form) + + class LinkPlugin(CMSPluginBase): model = Link name = _("Link") @@ -53,16 +79,5 @@ def render(self, context, instance, placeholder): ) return super().render(context, instance, placeholder) - def get_form(self, request, obj=None, **kwargs): - form = super().get_form(request, obj, **kwargs) - - if obj: - language = getattr(obj, "language", None) - else: - language = request.GET.get("plugin_language", None) - for widget in form.base_fields["link"].widget.widgets: - widget.language = language - return form - plugin_pool.register_plugin(LinkPlugin) diff --git a/djangocms_link/fields.py b/djangocms_link/fields.py index b03035ce..45c93e2f 100644 --- a/djangocms_link/fields.py +++ b/djangocms_link/fields.py @@ -10,8 +10,10 @@ from django.db import models from django.db.models import JSONField, ManyToOneRel from django.forms import Field, MultiWidget, Select, TextInput, URLInput +from django.utils.encoding import force_str from django.utils.translation import get_language from django.utils.translation import gettext_lazy as _ +from django.utils.translation import override from cms.utils.urlutils import admin_reverse @@ -47,25 +49,26 @@ def get_internal_obj(self, values: list[str | None]) -> list[models.Model | None return internal_obj def optgroups(self, name: str, value: str, attr: str | None = None): - default = (None, [], 0) - groups = [default] - has_selected = False - selected_choices = set(value) - if not self.is_required and not self.allow_multiple_selected: - default[1].append(self.create_option(name, "", "", False, 0)) - - for option_value, option_label in zip(value, self.get_internal_obj(value)): - selected = str(option_value) in value and ( - has_selected is False or self.allow_multiple_selected - ) - has_selected |= selected - index = len(default[1]) - subgroup = default[1] - subgroup.append( - self.create_option( - name, option_value, option_label, selected_choices, index + with override(self.language or get_language()): + default = (None, [], 0) + groups = [default] + has_selected = False + selected_choices = set(value) + if not self.is_required and not self.allow_multiple_selected: + default[1].append(self.create_option(name, "", "", False, 0)) + + for option_value, option_label in zip(value, self.get_internal_obj(value)): + selected = str(option_value) in value and ( + has_selected is False or self.allow_multiple_selected + ) + has_selected |= selected + index = len(default[1]) + subgroup = default[1] + subgroup.append( + self.create_option( + name, option_value, force_str(option_label), selected_choices, index + ) ) - ) return groups def get_url(self): From e8ccb296ab658f5b4c5013abad3eb02fce21e76f Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sat, 26 Jul 2025 23:20:11 +0200 Subject: [PATCH 04/13] Update readme --- README.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.rst b/README.rst index 38d426bb..4701f0a2 100644 --- a/README.rst +++ b/README.rst @@ -236,6 +236,13 @@ cached.):: obj = MyModel.objects.first() url = obj.link.url +A ``LinkField`` used inside a CMS plugins will automatically show internal link targets in +the language of the plugin (which might differ from the edit dialog's language). This +follows the principle the all content in the dialog is shown in the object's language. + +If you use ``LinkField`` in your own models, the default will be the current language. + + Link models ----------- From f7a0ff775b920e7db3f45eb5bd759658f6c11024 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sat, 26 Jul 2025 23:30:37 +0200 Subject: [PATCH 05/13] Fix tests --- README.rst | 2 +- djangocms_link/cms_plugins.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4701f0a2..e14d3ed6 100644 --- a/README.rst +++ b/README.rst @@ -236,7 +236,7 @@ cached.):: obj = MyModel.objects.first() url = obj.link.url -A ``LinkField`` used inside a CMS plugins will automatically show internal link targets in +A ``LinkField`` used inside a CMS plugin will automatically show internal link targets in the language of the plugin (which might differ from the edit dialog's language). This follows the principle the all content in the dialog is shown in the object's language. diff --git a/djangocms_link/cms_plugins.py b/djangocms_link/cms_plugins.py index bf698add..7d9babf2 100644 --- a/djangocms_link/cms_plugins.py +++ b/djangocms_link/cms_plugins.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from django.contrib.sites.shortcuts import get_current_site from django.db import models from django.utils.translation import gettext_lazy as _ From d98b91807f20b6d626816d31953ae50bf5a46d4f Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sat, 26 Jul 2025 23:50:04 +0200 Subject: [PATCH 06/13] Update translations --- .../locale/de/LC_MESSAGES/django.mo | Bin 2660 -> 2745 bytes .../locale/de/LC_MESSAGES/django.po | 84 +++++++++-------- .../locale/en/LC_MESSAGES/django.po | 86 +++++++++--------- 3 files changed, 89 insertions(+), 81 deletions(-) diff --git a/djangocms_link/locale/de/LC_MESSAGES/django.mo b/djangocms_link/locale/de/LC_MESSAGES/django.mo index a4939469382bbcc220402af304a4a250cbc33877..22e5bf4db659c02d4407c513bb3828dba5d922d0 100644 GIT binary patch delta 892 zcmXZaKTH!*9Ki8+1)-?K07aBP@(Ttd37iu{bpjKO36Vx*VLQsRJz8GPafpE+L?;Jb zDkd5oOmxvj(?Rji#ON+E=ztE!#Kgh4XpG-q=}X$rdw2JKf8O2K)JFU2=hpgl!|3JL z%`fE{Gl2UO{4t(mBfh{E+^G2;_p<&}yZ;SaS^vZXSXW03Y{L`^cH(etoxvt!R*hxS z%#9M(;|gk`>o|&cPy=sa8-B-=_z$mPnoU~#E9}9Kcnp8zMeNyQOczGD4{u`w-p3^Q z%_AmS@l!m4uQB2jwoxA(qy2+8froGwsodPaB;G>7yQqb{L`}4X9ry(`?jO_!I@zQr z=VLSZO_~YCnzN`KjA0sQQ4>B!?cg0w;x<}5K^6VDj(W6jYU>Ypf%RY1{o`cQ2J=V_ z<|-1xtYN?2xXDB-zlStltzHxA&~M^!xh2u%b(Dt^z31AiS9b&P2CDn?lDZdud@`9K=1?Yxf;k)8i0HAnFq@C1 zPs8`gf}4>-;9Tu$=){K3N-l6qR;Ge{Q3|{HLUPKYjb^O#%g>VCXLv4Wr>z^Lf9 delta 819 zcmYMyKWGzC9KiA4rDtt4Qf-@B8*QE!+OrsvC5wSzEjl;}{u^BEsjtn^yI8;PKmUj$tiRwe{=^Ip42X>3UYx|~{(276B3-$_ z#U379Mi-l?i8?rsw^0MX#Zml(kMKL@@h+PV;R~F>cX%FuU=C015ZR3Z4q_9BFvel> z%S|q{`+L}->rK=b^RzvVGdO`Yq++><8ejnphNy+yK~3}=_v1^{xF1jp|AzX-95!pi z8Fa}nlU!&Ac|3+S)P!rO9o)wRH*p&0s6s#GBI^1Up2erA`+lPy!6@0tDFtM!97Vlf zK`r+o)ozWBg_iHsV&*@7-{JIZmb59^7&qlT?N_ zl5#rN4WcD`#YROFM$3)WmGHVX=dRXmGcaqRHS4AMlJT}9dqv}EWY4s&*^cr0p}mqh v(~iSd6f}$tBO4j75Jbzi, YEAR. # # Translators: +# Angelo Dini , 2016 # danielastreuli , 2016 -# Angelo Dini , 2019 -# Fabian Braun , 2024 +# Fabian Braun , 2025 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-27 14:11+0100\n" +"POT-Creation-Date: 2025-07-26 23:36+0200\n" "PO-Revision-Date: 2016-09-15 09:08+0000\n" -"Last-Translator: Fabian Braun , 2024\n" +"Last-Translator: Fabian Braun , 2025\n" "Language-Team: German (https://app.transifex.com/divio/teams/58664/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -23,49 +23,53 @@ msgstr "" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: djangocms_link/apps.py:7 +#: admin.py:105 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" +"Page-Parameter ist nicht \"last\" und kann nicht in eine ganze Zahl " +"konvertiert werden." + +#: apps.py:10 msgid "django CMS Link" msgstr "django CMS Link" -#: djangocms_link/cms_plugins.py:15 djangocms_link/models.py:73 +#: cms_plugins.py:41 models.py:70 msgid "Link" msgstr "Link" -#: djangocms_link/cms_plugins.py:27 +#: cms_plugins.py:64 msgid "Advanced settings" msgstr "Erweiterte Einstellungen" -#: djangocms_link/fields.py:26 djangocms_link/forms.py:15 +#: fields.py:155 msgid "Internal link" msgstr "Interner Link" -#: djangocms_link/fields.py:27 +#: fields.py:156 msgid "External link/anchor" msgstr "Externer Link/Anker" -#: djangocms_link/fields.py:30 +#: fields.py:159 msgid "File link" -msgstr "Datei Link" +msgstr "Datei-Link" -#: djangocms_link/fields.py:169 +#: fields.py:191 msgid "No destination selected. Use the dropdown to select a destination." msgstr "Kein Ziel ausgewählt. Ziel im Dropdown-Menü auswählen." -#: djangocms_link/fields.py:175 +#: fields.py:198 msgid "https://example.com or #anchor" msgstr "https://beispiel.de oder #anker" -#: djangocms_link/fields.py:177 +#: fields.py:200 msgid "" -"Provide a link to an external URL, including the schema such as 'https://', " -"'tel:', or 'mailto:'. Optionally, add an #anchor (including the #) to scroll" -" to." +"Provide a link to an external URL, including the schema such as {}. " +"Optionally, add an #anchor (including the #) to scroll to." msgstr "" -"Link zu einer externen URL angeben, inklusive Schema wie z.B. \"https://\", " -"\"tel:\" oder \"mailto:\". Optional einen \"#anker\" hinzufügen (inklusive " -"#)." +"Link zu einer externen URL eingeben, inkl. Schema wie z.B. {}. Optional " +"#Anker hinzufügen (inkl. #)." -#: djangocms_link/fields.py:186 +#: fields.py:209 msgid "" "Select from available internal destinations. Optionally, add an anchor to " "scroll to." @@ -73,78 +77,78 @@ msgstr "" "Aus den verfügbaren internen Zielen auswählen. Optional einen Anker " "hinzufügen." -#: djangocms_link/fields.py:188 +#: fields.py:211 msgid "Select internal destination" msgstr "Internes Ziel auswählen" -#: djangocms_link/fields.py:194 +#: fields.py:217 msgid "#anchor" msgstr "#anker" -#: djangocms_link/fields.py:195 +#: fields.py:218 msgid "Provide an anchor to scroll to." msgstr "Anker angeben, der angesprungen wird." -#: djangocms_link/fields.py:206 +#: fields.py:228 msgid "Select a file as destination." msgstr "Datei als Ziel auswählen." -#: djangocms_link/fields.py:215 +#: fields.py:266 msgid "Select site" msgstr "Site auswählen" -#: djangocms_link/fields.py:249 +#: fields.py:309 msgid "Select a link type and provide a link." msgstr "Link-Typ auswählen und Ziel angeben." -#: djangocms_link/models.py:24 +#: models.py:25 msgid "Default" msgstr "Standard" -#: djangocms_link/models.py:41 +#: models.py:38 msgid "Open in new window" msgstr "In neuem Fenster öffnen" -#: djangocms_link/models.py:42 +#: models.py:39 msgid "Open in same window" msgstr "Im gleichen Fenster öffnen" -#: djangocms_link/models.py:43 +#: models.py:40 msgid "Delegate to parent" msgstr "Zum nächsten Fenster delegieren" -#: djangocms_link/models.py:44 +#: models.py:41 msgid "Delegate to top" msgstr "Zum obersten Fenster delegieren" -#: djangocms_link/models.py:61 +#: models.py:58 msgid "Template" msgstr "Template" -#: djangocms_link/models.py:67 +#: models.py:64 msgid "Display name" msgstr "Anzeigename" -#: djangocms_link/models.py:77 +#: models.py:74 msgid "Target" msgstr "Verweis" -#: djangocms_link/models.py:83 +#: models.py:80 msgid "Attributes" msgstr "Attribute" -#: djangocms_link/models.py:109 +#: models.py:106 msgid "" msgstr "" -#: djangocms_link/models.py:118 +#: models.py:115 msgid "Link is required." msgstr "Der Link ist ein Pflichtfeld." -#: djangocms_link/validators.py:50 +#: validators.py:61 msgid "Enter a valid anchor" msgstr "Gültigen Anker angeben" -#: djangocms_link/validators.py:84 +#: validators.py:104 msgid "Enter a valid phone number" msgstr "Gültige Telefonnummer angeben" diff --git a/djangocms_link/locale/en/LC_MESSAGES/django.po b/djangocms_link/locale/en/LC_MESSAGES/django.po index a4603d79..ef1a7547 100644 --- a/djangocms_link/locale/en/LC_MESSAGES/django.po +++ b/djangocms_link/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-27 14:11+0100\n" +"POT-Creation-Date: 2025-07-26 23:36+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,123 +17,127 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: djangocms_link/apps.py:7 + +#: admin.py:105 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "" + +#: apps.py:10 msgid "django CMS Link" msgstr "" -#: djangocms_link/cms_plugins.py:15 djangocms_link/models.py:73 +#: cms_plugins.py:41 models.py:70 msgid "Link" -msgstr "Link" +msgstr "" -#: djangocms_link/cms_plugins.py:27 +#: cms_plugins.py:64 msgid "Advanced settings" -msgstr "Advanced settings" +msgstr "" -#: djangocms_link/fields.py:26 djangocms_link/forms.py:15 +#: fields.py:155 msgid "Internal link" -msgstr "Internal link" +msgstr "" -#: djangocms_link/fields.py:27 +#: fields.py:156 msgid "External link/anchor" msgstr "" -#: djangocms_link/fields.py:30 +#: fields.py:159 msgid "File link" msgstr "" -#: djangocms_link/fields.py:169 +#: fields.py:191 msgid "No destination selected. Use the dropdown to select a destination." msgstr "" -#: djangocms_link/fields.py:175 +#: fields.py:198 msgid "https://example.com or #anchor" -msgstr "https://example.com or #anchor" +msgstr "" -#: djangocms_link/fields.py:177 +#: fields.py:200 msgid "" -"Provide a link to an external URL, including the schema such as 'https://', " -"'tel:', or 'mailto:'. Optionally, add an #anchor (including the #) to scroll " -"to." +"Provide a link to an external URL, including the schema such as {}. " +"Optionally, add an #anchor (including the #) to scroll to." msgstr "" -#: djangocms_link/fields.py:186 +#: fields.py:209 msgid "" "Select from available internal destinations. Optionally, add an anchor to " "scroll to." msgstr "" -#: djangocms_link/fields.py:188 +#: fields.py:211 msgid "Select internal destination" msgstr "" -#: djangocms_link/fields.py:194 +#: fields.py:217 msgid "#anchor" msgstr "" -#: djangocms_link/fields.py:195 +#: fields.py:218 msgid "Provide an anchor to scroll to." msgstr "" -#: djangocms_link/fields.py:206 +#: fields.py:228 msgid "Select a file as destination." msgstr "" -#: djangocms_link/fields.py:215 +#: fields.py:266 msgid "Select site" -msgstr "Select site" +msgstr "" -#: djangocms_link/fields.py:249 +#: fields.py:309 msgid "Select a link type and provide a link." msgstr "" -#: djangocms_link/models.py:24 +#: models.py:25 msgid "Default" msgstr "" -#: djangocms_link/models.py:41 +#: models.py:38 msgid "Open in new window" msgstr "" -#: djangocms_link/models.py:42 +#: models.py:39 msgid "Open in same window" msgstr "" -#: djangocms_link/models.py:43 +#: models.py:40 msgid "Delegate to parent" msgstr "" -#: djangocms_link/models.py:44 +#: models.py:41 msgid "Delegate to top" msgstr "" -#: djangocms_link/models.py:61 +#: models.py:58 msgid "Template" msgstr "" -#: djangocms_link/models.py:67 +#: models.py:64 msgid "Display name" msgstr "" -#: djangocms_link/models.py:77 +#: models.py:74 msgid "Target" msgstr "" -#: djangocms_link/models.py:83 +#: models.py:80 msgid "Attributes" msgstr "" -#: djangocms_link/models.py:109 +#: models.py:106 msgid "" msgstr "" -#: djangocms_link/models.py:118 +#: models.py:115 msgid "Link is required." -msgstr "Link is required." +msgstr "" -#: djangocms_link/validators.py:50 +#: validators.py:61 msgid "Enter a valid anchor" -msgstr "Enter a valid anchor" +msgstr "" -#: djangocms_link/validators.py:84 +#: validators.py:104 msgid "Enter a valid phone number" -msgstr "Enter a valid phone number" +msgstr "" From 00e7f1f330747f36fc2a7d390e926dd9727e3941 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sat, 26 Jul 2025 23:58:10 +0200 Subject: [PATCH 07/13] Add French translations --- .../locale/fr/LC_MESSAGES/django.mo | Bin 1216 -> 3006 bytes .../locale/fr/LC_MESSAGES/django.po | 103 ++++++++++-------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/djangocms_link/locale/fr/LC_MESSAGES/django.mo b/djangocms_link/locale/fr/LC_MESSAGES/django.mo index f8a972ba486a85f6c5d0bd8d66267062dd49c4f2..b5d999e9edd93da5df60c74f021fb1c2b68b3f82 100644 GIT binary patch literal 3006 zcmbW2Piz!b9LGn&f2*Pb{ulib0<@v~r$|ZJQm~ZPG`6&93kPp+XWnjKoq037_hxAo zlyD&G!FbTb7)|hC;=)C5A}86ScmEvpz{#iwPhL!n-`~t`>DC^IFP(no{r~-&@BXrV z>xUlN3wWNxv-2*`>jxjY8~@P01n&dC0v`jfclaGxK>z2?{IB44^#24O0k>?y8Q>0Z zCrIEk;83SO2R?wl1Rn-ta4UEL z2mS>vg1heVyk|iJJ_x=C-V1&V-Vc5Tvb|Tq=fQ8l&k=*4KwkG2oM(G3gKWn~AlrKl z#GiK^|Jc88JNzEx^L_`P1pfy2gFA2;?;inq{V9;+Dn;5E>IBjEE$!U6DA@DO+zd>6a{?gb~|^y6R%!X@t#2$Q_GLALXKkk7vY!tE?Z z94~HM4^QLaJUxns?c`W++lGhzW1H7Eju|(;i|^&e_aR+bp19BPdx~GsIOc2z$A;^J zZQ+J8@}9s0H?nvk3|YLqJwp3SvGID7^!=QT%33(l}$piN?JtHo6t@g64Vr-3aE2;?_jbNYe_nd zYSMTUD_E~ZNNirYvwl*AG8^!w__*%0H;pyfKla1Ov#ByND0!!13Z#V#!l_upolyD? z6Li|b?j=cqiIX5cr`d^o78b7CQu1aKsYz+l@*JI08jIJ4EZpjj&54HOyLIg7*2Pak zVco5ZpAMjJ$QL-lQB9H`>!viYfbZi`?MmL9iJK~rxHwBhJ|nx{pPru{z&bxn17spI z+WJc}5@b_YF$W@wV=I-AT%Z`GqWK zu&&Or$9+BFS)0vmUrn$?2bA>qRPs8@>L!jzG=&O94Tr(~JcsL!w3|AUr+;(HHan$z zp;b0hl`aX#laz}9H~}@4^+?!({7X5zMIgQZGgJVs(PEJ8Y~^n-bpAYLZ-f1G99^uX-8{Z; zAKv!TAV;iH$=4pIK`b~pQpnWI$W6p-zTIks2$SW)(|)^Uy=m@r6b*vbZb8wYUhP(h zwJEDqx+ZCz!FG!WZ5&0Yq_KKk;TtrB4Ids}Z^E z#9Zn!EtC*0XiWaaOIAAgkqtpBEa5xh+Y}@*8_df+Uja`b8ob%MT=s0q8%uq*WZgGK z7I&hAQHHwRl8`^ALS7ccg$TXkGj_V4ilEltx zhiH_%Nm#1!o0Ww=k3$wQ_8zj?jpzWaVtvy!P($Tz<)PKa0s5hR7o;$R4tc%*Jp-9K!+> zk(Y1`^)*bxTUdm5kb3lC77kzrzQGCj0h4e9>G$7olCdshamG@3QGzO*f)6;Hg*14W z2>&pG)aVD&;0(HzU?Fl2(gG_{y#;BJeTa)$xXnNpPQxBlX#OJ}sNoaD+#oRHU(grR z*HA+W8Z?fi#c62;`8Tk8&=W0#rE_#xm%={Lgr`};^Mq|S*s9^b#`h{4LUN4`*{0p$ zuC2?k+Qut?m{`cSbl2n^*5X!M>K*RCD`!>dxI(wJbuPulxmG_4>#7mdP1DkatR6a6 kV^OOu)t3A1R6gckrBD4`b>9C{o4GY{AUWG{jKMwp1)iKvo&W#< diff --git a/djangocms_link/locale/fr/LC_MESSAGES/django.po b/djangocms_link/locale/fr/LC_MESSAGES/django.po index 7bac24c0..23f41bed 100644 --- a/djangocms_link/locale/fr/LC_MESSAGES/django.po +++ b/djangocms_link/locale/fr/LC_MESSAGES/django.po @@ -6,15 +6,16 @@ # Translators: # a270031086f2a0d3514bc0cb507b48f6, 2019 # Florian Delizy , 2021 +# Fabian Braun , 2025 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-27 14:11+0100\n" +"POT-Creation-Date: 2025-07-26 23:36+0200\n" "PO-Revision-Date: 2016-09-15 09:08+0000\n" -"Last-Translator: Florian Delizy , 2021\n" +"Last-Translator: Fabian Braun , 2025\n" "Language-Team: French (https://app.transifex.com/divio/teams/58664/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -22,123 +23,133 @@ msgstr "" "Language: fr\n" "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" -#: djangocms_link/apps.py:7 +#: admin.py:105 +msgid "Page is not “last”, nor can it be converted to an int." +msgstr "La page n'est pas «last», et ne peut pas être convertie en entier." + +#: apps.py:10 msgid "django CMS Link" -msgstr "" +msgstr "django CMS Lien" -#: djangocms_link/cms_plugins.py:15 djangocms_link/models.py:73 +#: cms_plugins.py:41 models.py:70 msgid "Link" msgstr "Lien" -#: djangocms_link/cms_plugins.py:27 +#: cms_plugins.py:64 msgid "Advanced settings" msgstr "Réglages avancés" -#: djangocms_link/fields.py:26 djangocms_link/forms.py:15 +#: fields.py:155 msgid "Internal link" msgstr "Lien interne" -#: djangocms_link/fields.py:27 +#: fields.py:156 msgid "External link/anchor" -msgstr "" +msgstr "Lien externe/ancre" -#: djangocms_link/fields.py:30 +#: fields.py:159 msgid "File link" msgstr "Lien vers le fichier" -#: djangocms_link/fields.py:169 +#: fields.py:191 msgid "No destination selected. Use the dropdown to select a destination." msgstr "" +"Aucune destination sélectionnée. Utilisez le menu déroulant pour " +"sélectionner une destination." -#: djangocms_link/fields.py:175 +#: fields.py:198 msgid "https://example.com or #anchor" -msgstr "" +msgstr "https://example.com ou #ancre" -#: djangocms_link/fields.py:177 +#: fields.py:200 msgid "" -"Provide a link to an external URL, including the schema such as 'https://', " -"'tel:', or 'mailto:'. Optionally, add an #anchor (including the #) to scroll" -" to." +"Provide a link to an external URL, including the schema such as {}. " +"Optionally, add an #anchor (including the #) to scroll to." msgstr "" +"Fournissez un lien vers une URL externe, y compris le schéma tel que {}. " +"Vous pouvez également ajouter une #ancre (y compris le #) pour faire défiler" +" jusqu'à celle-ci." -#: djangocms_link/fields.py:186 +#: fields.py:209 msgid "" "Select from available internal destinations. Optionally, add an anchor to " "scroll to." msgstr "" +"Sélectionnez parmi les destinations internes disponibles. Vous pouvez " +"également ajouter une ancre pour faire défiler jusqu'à celle-ci." -#: djangocms_link/fields.py:188 +#: fields.py:211 msgid "Select internal destination" -msgstr "" +msgstr "Sélectionnez une destination interne" -#: djangocms_link/fields.py:194 +#: fields.py:217 msgid "#anchor" -msgstr "" +msgstr "#ancre" -#: djangocms_link/fields.py:195 +#: fields.py:218 msgid "Provide an anchor to scroll to." -msgstr "" +msgstr "Fournissez une ancre pour faire défiler jusqu'à celle-ci." -#: djangocms_link/fields.py:206 +#: fields.py:228 msgid "Select a file as destination." -msgstr "" +msgstr "Sélectionnez un fichier comme destination." -#: djangocms_link/fields.py:215 +#: fields.py:266 msgid "Select site" -msgstr "" +msgstr "Sélectionnez le site" -#: djangocms_link/fields.py:249 +#: fields.py:309 msgid "Select a link type and provide a link." -msgstr "" +msgstr "Sélectionnez un type de lien et fournissez un lien." -#: djangocms_link/models.py:24 +#: models.py:25 msgid "Default" msgstr "Défaut" -#: djangocms_link/models.py:41 +#: models.py:38 msgid "Open in new window" msgstr "Ouvrir dans une nouvelle fenêtre" -#: djangocms_link/models.py:42 +#: models.py:39 msgid "Open in same window" msgstr "Ouvrir dans la même fenêtre" -#: djangocms_link/models.py:43 +#: models.py:40 msgid "Delegate to parent" msgstr "Déléguer au parent" -#: djangocms_link/models.py:44 +#: models.py:41 msgid "Delegate to top" msgstr "Délégué au sommet" -#: djangocms_link/models.py:61 +#: models.py:58 msgid "Template" msgstr "Modèle" -#: djangocms_link/models.py:67 +#: models.py:64 msgid "Display name" msgstr "Afficher un nom" -#: djangocms_link/models.py:77 +#: models.py:74 msgid "Target" msgstr "Cible" -#: djangocms_link/models.py:83 +#: models.py:80 msgid "Attributes" msgstr "Les attributs" -#: djangocms_link/models.py:109 +#: models.py:106 msgid "" msgstr "" -#: djangocms_link/models.py:118 +#: models.py:115 msgid "Link is required." -msgstr "" +msgstr "Le lien est requis." -#: djangocms_link/validators.py:50 +#: validators.py:61 msgid "Enter a valid anchor" -msgstr "" +msgstr "Entrez une ancre valide" -#: djangocms_link/validators.py:84 +#: validators.py:104 msgid "Enter a valid phone number" -msgstr "" +msgstr "Entrez un numéro de téléphone valide" From 638237db8762b6f0470b681c29676a92959ac899 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sun, 27 Jul 2025 00:50:32 +0200 Subject: [PATCH 08/13] Show language-specific path in plugin's short description --- djangocms_link/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/djangocms_link/models.py b/djangocms_link/models.py index 61635211..cf2e6b56 100644 --- a/djangocms_link/models.py +++ b/djangocms_link/models.py @@ -9,6 +9,7 @@ from django.utils.encoding import force_str from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ +from django.utils.translation import override from cms.models import CMSPlugin @@ -100,7 +101,8 @@ def __str__(self): return self.name or str(self.pk) def get_short_description(self): - link = self.get_link() + with override(self.language): + link = self.get_link() if self.name and link: return f"{self.name} ({link})" return self.name or link or gettext("") From 023ad7f0c904f0ccc07962afdcc760c0c45d8cdd Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sun, 27 Jul 2025 00:51:45 +0200 Subject: [PATCH 09/13] Remove debug statement --- djangocms_link/fields.py | 1 - 1 file changed, 1 deletion(-) diff --git a/djangocms_link/fields.py b/djangocms_link/fields.py index 45c93e2f..b7d1af18 100644 --- a/djangocms_link/fields.py +++ b/djangocms_link/fields.py @@ -375,7 +375,6 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def formfield(self, **kwargs): - print("LinkField.formfield", kwargs) kwargs.setdefault("form_class", LinkFormField) return super().formfield(**kwargs) From 4368779ffc03f67373f553bb77c8d8f7acb72915 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sun, 27 Jul 2025 00:53:58 +0200 Subject: [PATCH 10/13] Allow widget customization --- djangocms_link/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_link/fields.py b/djangocms_link/fields.py index b7d1af18..13eb8a7c 100644 --- a/djangocms_link/fields.py +++ b/djangocms_link/fields.py @@ -308,9 +308,9 @@ class LinkFormField(Field): def __init__(self, *args, **kwargs): kwargs.setdefault("help_text", _("Select a link type and provide a link.")) kwargs.setdefault("initial", {}) + kwargs.setdefault("widget", LinkWidget(language=kwargs.pop("language", None))) kwargs.pop("encoder", None) # Passed from LinkField's JSONField parent class kwargs.pop("decoder", None) # but not needed - self.widget = LinkWidget(language=kwargs.pop("language", None)) super().__init__(*args, **kwargs) if isinstance(self.initial, dict): self.initial = self.prepare_value(self.initial) From fc5ede6155603a5e81854170bb7cb4a2874b04e0 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sun, 27 Jul 2025 00:54:35 +0200 Subject: [PATCH 11/13] Update djangocms_link/fields.py Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- djangocms_link/fields.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/djangocms_link/fields.py b/djangocms_link/fields.py index 13eb8a7c..744f33d5 100644 --- a/djangocms_link/fields.py +++ b/djangocms_link/fields.py @@ -73,9 +73,7 @@ def optgroups(self, name: str, value: str, attr: str | None = None): def get_url(self): reverse = admin_reverse("djangocms_link_link_urls") - if self.language: - return f"{reverse}?language={self.language}" - return reverse + return f"{reverse}?language={self.language}" if self.language else reverse def build_attrs(self, base_attrs: dict, extra_attrs: dict | None = None) -> dict: """ From 384f28ee0155a27b4bdd3bb8f17966af4a34c685 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Sun, 27 Jul 2025 20:11:06 +0200 Subject: [PATCH 12/13] Add nl locale --- .../locale/nl/LC_MESSAGES/django.mo | Bin 1154 -> 2840 bytes .../locale/nl/LC_MESSAGES/django.po | 103 ++++++++++-------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/djangocms_link/locale/nl/LC_MESSAGES/django.mo b/djangocms_link/locale/nl/LC_MESSAGES/django.mo index 8b272cd38b1512bec298300bc36c1d99115fb33a..eb171a55d8bd7539f69c2bdd0db88d4f9b47b3d5 100644 GIT binary patch literal 2840 zcma)-yKh@n9LF!Tyv(~Tgl1CI&lAHV_|10V!~PQZYeKrD#w@7(LWC=yq)KR(aj^M3uqp51Rm zcpky$A$;~-7ezzhZP()q&qv@*;K$(I;AaKD081EuU#$NO?!owX@J?{oF5Cg`1^0mj zJ^+pt;|1^*j2XNgwBT;=5-4_F22X;ogL3cJ;9l@oum=7Oz62h@A@TiF@BsJ?_&E3n zxC|b+A&MRZ9e6AF8h9i4CU`UWE-3!K4?YZD0WTv5--5F5Jc1X06nH*(8on_i2$DA1x zdZ`CfPLyZzmb%KZ**@iMH=5E}H&tt7REMLw@$5)ZU!^*x!uIybZrd6r)9WuBT^lA9OM0m&FCE%`_~1~8wdrh{;@1}QrYOpe^`($Bc-NBL!``;^ zY^>(KuXVA-#MIpuQza_4wVdsc>MNb91};PRc@4K*nO!rOy!_Yp>{_=B3})JMw5(jh zK3e7ux)I~}f-;GvQ*%oc`ljuDH>*{vd{JQsSDJ7VWgEO7sLutoO?f6RpJSIH&ox>& zaq77f({(ymUtF4*KT}%du64dVmnAwbpX?=BdD+&ev@l;@djAFFR#2IA~psmOS1vgLlOs-G=IDnpIBPbSw)C&or&$#+v7f zNskZX(!*o8!z4X)VR@NVr$$pfkM=k;h)s376)y&Ki!Vxq)mZnnt$J+Es$&!5<4;vh zS{*9Q&CJy|;~cGwlulWc$-qApw?-bFSw$YTIZDm8a*&A0v&*N;6Px?QOp9H)Zkkq_ zwnh^T?Kd2(kx5GnY0s&&eA>EBhKX(%W|N1HQvP%DkfBE>X>{mlNrY+zg79yhnr8G8 zgo+(rA8nAE6m6KWiRv2Pcux>EMoS(&sg#SOGx#Rki=jlZ=odq`^2vtnvsqEbZ&{dT zZ>_=IMyXCD@e`vO38%46(C6aS&RO`tEu4aibv9Hi*Q|kvorEO8p-f(xLdObG3+*Uh zA|+4?$2x;TOpDb9PI0FrB9L&dRT6lGrn%vIdIf%O${9L%#~BE9aR?#jvQ`M2=H4od zN%~ySwtT+Lex1`6ZXK*amJjl>}47o;SA-nH%#kRrMc>zvz!paJ>O$a!)Ed-Od*gyyr(qO|sz#t8I9OC|e l#hoj%1d!|J)=LZZl*J&RQjwxjs5@Rv$Y|g{{YCeC;I>Z delta 523 zcmY+=ze@u#6bJBVe`{^AE&iYdPjC>CqT(W!Qb9z)tsPvWrZt+olO`2Jq2lfog^v0U zxb#nOE;xx;a1&ho2b}!Avx7c(`CO8__mb;OTqoM^neZJ$q>%;WEpif>9KwJ&g5yvQ zynrK^-@q8`zzn>HRHF-%@Chd1GaQ3&FbqE-z5fNXjI|jXW-N+@9E`&}yu{`lq{a^+ z#-?ExQlTEC!hM*7A8-nO5B9U@LI;=~ScKGg9Rppg0`qV$SVw<@2Wor{(KiUx`WJL# zYD$GE=%%AcIyikBLH-S#J@`8)85}xAn`Q1qUE|G$FsvrrQ^k$2uaQ=9TS!-UPSyDJx=`9Fok?wYOKV-Jb8TEM oRq?=g%Ztv+((;;ZNBfq?uIxg5#y-YZQ=7g}ta{A%mG67(2h=o9A^-pY diff --git a/djangocms_link/locale/nl/LC_MESSAGES/django.po b/djangocms_link/locale/nl/LC_MESSAGES/django.po index 4b6116f6..f9df9b2f 100644 --- a/djangocms_link/locale/nl/LC_MESSAGES/django.po +++ b/djangocms_link/locale/nl/LC_MESSAGES/django.po @@ -7,15 +7,16 @@ # Angelo Dini , 2016 # Evelijn Saaltink , 2016 # Heimen Stoffels , 2019 +# Stefan van den Eertwegh , 2025 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-27 14:11+0100\n" +"POT-Creation-Date: 2025-07-26 23:36+0200\n" "PO-Revision-Date: 2016-09-15 09:08+0000\n" -"Last-Translator: Heimen Stoffels , 2019\n" +"Last-Translator: Stefan van den Eertwegh , 2025\n" "Language-Team: Dutch (https://app.transifex.com/divio/teams/58664/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -23,123 +24,133 @@ msgstr "" "Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: djangocms_link/apps.py:7 -msgid "django CMS Link" +#: admin.py:105 +msgid "Page is not “last”, nor can it be converted to an int." msgstr "" +"Pagina is niet de “laatste” en kan ook niet worden omgezet naar een int." + +#: apps.py:10 +msgid "django CMS Link" +msgstr "django CMS Link" -#: djangocms_link/cms_plugins.py:15 djangocms_link/models.py:73 +#: cms_plugins.py:41 models.py:70 msgid "Link" msgstr "Link" -#: djangocms_link/cms_plugins.py:27 +#: cms_plugins.py:64 msgid "Advanced settings" msgstr "Geavanceerde instellingen" -#: djangocms_link/fields.py:26 djangocms_link/forms.py:15 +#: fields.py:155 msgid "Internal link" msgstr "Interne link" -#: djangocms_link/fields.py:27 +#: fields.py:156 msgid "External link/anchor" -msgstr "" +msgstr "Externe link/anchor" -#: djangocms_link/fields.py:30 +#: fields.py:159 msgid "File link" msgstr "Bestandslink" -#: djangocms_link/fields.py:169 +#: fields.py:191 msgid "No destination selected. Use the dropdown to select a destination." msgstr "" +"Geen bestemming geselecteerd. Gebruik de dropdown om een bestemming te " +"selecteren." -#: djangocms_link/fields.py:175 +#: fields.py:198 msgid "https://example.com or #anchor" -msgstr "" +msgstr "https://example.com of #anchor" -#: djangocms_link/fields.py:177 +#: fields.py:200 msgid "" -"Provide a link to an external URL, including the schema such as 'https://', " -"'tel:', or 'mailto:'. Optionally, add an #anchor (including the #) to scroll" -" to." +"Provide a link to an external URL, including the schema such as {}. " +"Optionally, add an #anchor (including the #) to scroll to." msgstr "" +"Geef een link naar een externe URL, inclusief het schema, zoals {}. Voeg " +"eventueel een #anker (inclusief #) toe om naartoe te scrollen." -#: djangocms_link/fields.py:186 +#: fields.py:209 msgid "" "Select from available internal destinations. Optionally, add an anchor to " "scroll to." msgstr "" +"Selecteer uit beschikbare interne bestemmingen. Voeg optioneel een anker toe" +" om naartoe te scrollen." -#: djangocms_link/fields.py:188 +#: fields.py:211 msgid "Select internal destination" -msgstr "" +msgstr "Selecteer interne bestemming" -#: djangocms_link/fields.py:194 +#: fields.py:217 msgid "#anchor" -msgstr "" +msgstr "#anker" -#: djangocms_link/fields.py:195 +#: fields.py:218 msgid "Provide an anchor to scroll to." -msgstr "" +msgstr "Zorg voor een anker waar naartoe gescrolld kan worden." -#: djangocms_link/fields.py:206 +#: fields.py:228 msgid "Select a file as destination." -msgstr "" +msgstr "Selecteer een bestand als bestemming." -#: djangocms_link/fields.py:215 +#: fields.py:266 msgid "Select site" -msgstr "" +msgstr "Selecteer site" -#: djangocms_link/fields.py:249 +#: fields.py:309 msgid "Select a link type and provide a link." -msgstr "" +msgstr "Selecteer een link type en voorzie deze van een link." -#: djangocms_link/models.py:24 +#: models.py:25 msgid "Default" msgstr "Standaard" -#: djangocms_link/models.py:41 +#: models.py:38 msgid "Open in new window" msgstr "Openen in nieuw venster" -#: djangocms_link/models.py:42 +#: models.py:39 msgid "Open in same window" msgstr "Openen in hetzelfde venster" -#: djangocms_link/models.py:43 +#: models.py:40 msgid "Delegate to parent" msgstr "Naar ouder delegeren" -#: djangocms_link/models.py:44 +#: models.py:41 msgid "Delegate to top" msgstr "Naar bovenkant delegeren" -#: djangocms_link/models.py:61 +#: models.py:58 msgid "Template" msgstr "Sjabloon" -#: djangocms_link/models.py:67 +#: models.py:64 msgid "Display name" msgstr "Getoonde naam" -#: djangocms_link/models.py:77 +#: models.py:74 msgid "Target" msgstr "Doel" -#: djangocms_link/models.py:83 +#: models.py:80 msgid "Attributes" msgstr "Eigenschappen" -#: djangocms_link/models.py:109 +#: models.py:106 msgid "" msgstr "" -#: djangocms_link/models.py:118 +#: models.py:115 msgid "Link is required." -msgstr "" +msgstr "Link is verreist." -#: djangocms_link/validators.py:50 +#: validators.py:61 msgid "Enter a valid anchor" -msgstr "" +msgstr "Type een geldig anker" -#: djangocms_link/validators.py:84 +#: validators.py:104 msgid "Enter a valid phone number" -msgstr "" +msgstr "Type een geldig telefoonnummer" From 1ca5acd73859ebfafe0c777a7afcf054c4f67ac3 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Tue, 29 Jul 2025 17:29:40 +0200 Subject: [PATCH 13/13] Fix: Skip empty results --- djangocms_link/admin.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/djangocms_link/admin.py b/djangocms_link/admin.py index 572198e2..f38ea590 100644 --- a/djangocms_link/admin.py +++ b/djangocms_link/admin.py @@ -139,17 +139,17 @@ def get_reference(self, request: HttpRequest) -> JsonResponse: obj = get_manager(model).get(pk=pk) if model_str == "cms.page": obj.__link_text__ = obj.get_admin_content(language, fallback=True).title - return JsonResponse(self.serialize_result(obj)) + return JsonResponse(self.serialize_result(obj) or {}) elif model_str == "cms.page": obj = get_manager(model).get(pk=pk) obj.__link_text__ = obj.get_title(language, fallback=True) - return JsonResponse(self.serialize_result(obj)) + return JsonResponse(self.serialize_result(obj) or {}) if hasattr(model_admin, "get_link_queryset"): obj = model_admin.get_link_queryset(self.request, None).get(pk=pk) else: obj = model_admin.get_queryset(self.request).get(pk=pk) - return JsonResponse(self.serialize_result(obj)) + return JsonResponse(self.serialize_result(obj) or {}) except Exception as e: return JsonResponse({"error": str(e)}) @@ -166,7 +166,9 @@ def get_optgroups(self, context): "text": previous_model.capitalize(), "children": [], } - model["children"].append(self.serialize_result(obj)) + data = self.serialize_result(obj) + if data: # Only append if serialization was successful + model["children"].append(data) if model: results.append(model) return results @@ -181,12 +183,16 @@ def serialize_result(self, obj: Model) -> dict: obj.__link_text__ = obj.get_admin_content(self.language).title indentation = UNICODE_SPACE * (max(getattr(obj, "__depth__", 1), 1) - 1) - return { - "id": f"{obj._meta.app_label}.{obj._meta.model_name}:{obj.pk}", - "text": indentation + (getattr(obj, "__link_text__", str(obj)) or str(obj)), - "url": obj.get_absolute_url(), - "verbose_name": str(obj._meta.verbose_name).capitalize(), - } + url = obj.get_absolute_url() + text = getattr(obj, "__link_text__", str(obj)) or str(obj) + if url and text: + return { + "id": f"{obj._meta.app_label}.{obj._meta.model_name}:{obj.pk}", + "text": indentation + text, + "url": url, + "verbose_name": str(obj._meta.verbose_name).capitalize(), + } + return None def get_queryset(self) -> QuerySet: """Return queryset based on ModelAdmin.get_search_results()."""