coderedcorp / coderedcms

Wagtail + CodeRed Extensions enabling rapid development of marketing-focused websites.
https://www.coderedcorp.com/cms
Other
685 stars 133 forks source link

[solved]Multi-language support for snippets #566

Closed redwired closed 4 months ago

redwired commented 1 year ago

Hi,

I'm trying to use WagtailCRX for a multi-languages website (french, japanese and english).

I started implementing i18n with wagtail-localize and it seems to be working as intended for pages content.

However, I cant make it work with CRX snippets.

I'm trying to follow the wagtail official documentation:

https://docs.wagtail.org/en/stable/advanced_topics/i18n.html#translatable-snippets

At first, I tried to customize footer and navbar snippets in my website/models.py by unregistering the default ones and redefine my own classes by adding the TranslatableMixin behavior and inherit the default snippets classes.

For example, to redefine Footer, I tried some dirty hack like this one :

# removing previously registered coderedcms snippets to replace them with
# a custom child class implementing TranslatableMixin
DEFERRED_REGISTRATIONS.remove((coderedcms.models.snippet_models.Footer, None))

@register_snippet
class Footer(TranslatableMixin, coderedcms.models.snippet_models.Footer):
    class Meta(TranslatableMixin.Meta):
        verbose_name = _("Footer")

After I made all the needed migrations with BootstrapTranslatableMixin as explained in the wagtail's documentation, I managed to get translatable snippets on the admin.

However, if I select my Footer on the CRX Settings menu, its will stick to one selected language and doesn't translate at all.

I understood that in the database I have the 2 models (base and inherited), but only my redefined model has the language support.

It looks like CRX takes the base class (coderedcms.models.snippet_models.Footer) to render the snippet.

I tried to redefine a footer without inherit your base class, but then I had no footer choice at all.

I'm wondering if what I want to do is even possible without modifying the WagtailCRX library ? Or perhaps I just make it wrong (im a seasoned developer, but still a beginner with python/django/wagtail) ?

If not possible, do you plan to implement multiple language support at some point ?

I sincerely think multiple-language support would be a real added value for WagtailCRX. But on the other hand I also understand it can be a difficult upgrade, as it could need some deep rework and consequent (and risky) DB migration for your clients.

On my side, I can try to make it works by modifying coderedcms itself. But I dont want to work on something you already planned. And I don't like the idea to fork (and maintain) another version of your repo either.

So, what do you think/suggest I do to get it works as I intended ?

Thanks in advance.

vsalvino commented 1 year ago

Thanks for providing all the details. It is probably not possible to translate our built-in Navbar and Footer models, because they are concrete in coderedcms, and therefore cannot be altered. This was a design mistake that was made early on. The long-term plan is to eventually make all models abstract, so that the client can define them in local code and make customizations as necessary. See issue #56

However, in the short-term, my recommendation is to use a custom Navbar and Footer. Simply ignore the built-in ones from coderedcms.

First, define a custom snippet as normal. You can copy the implementation of the built-in ones here: https://github.com/coderedcorp/coderedcms/blob/dev/coderedcms/models/snippet_models.py

Second, override the HTML templates (specifically navbar.html and footer.html from here https://github.com/coderedcorp/coderedcms/tree/dev/coderedcms/templates/coderedcms/snippets) to render your own models instead. You may need to create a templatetag to query them.

redwired commented 1 year ago

Thanks for you advice. I was also thinking about rewriting a whole new snippet, but wanted to be sure about it.

I can close this issue now.

redwired commented 1 year ago

I'm reopening this comment as I eventually succeeded to make this work !

After trying several ways, I came back to my first intuition to inherit from the base, non translatable, snippet.

In the website/models.py, I took advantage of the deferred registration to unregister the coderedcms predefined snippets models.

Then I defined and registered new custom models inheriting from the former ones.

Then, I used a trick and monkey patched the coderedcms original model classes with my new one.

I made this for Navbar and Footer as they are those two which are mostly annoying in my case.

Here's the code I added to models.py


import coderedcms.models.snippet_models

from wagtail.models import TranslatableMixin
from wagtail.snippets.models import DEFERRED_REGISTRATIONS, register_snippet

from django.utils.translation import gettext_lazy as _

# hack to remove previously registered coderedcms snippets as they are being replaced
# with custom snippets implementing TranslatableMixin (in models/snippet_models.py)
DEFERRED_REGISTRATIONS.remove((coderedcms.models.snippet_models.Navbar, None))
DEFERRED_REGISTRATIONS.remove((coderedcms.models.snippet_models.Footer, None))

@register_snippet
class Navbar(TranslatableMixin, coderedcms.models.snippet_models.Navbar):
    class Meta(TranslatableMixin.Meta):
        verbose_name = _("Navigation Bar")

@register_snippet
class Footer(TranslatableMixin, coderedcms.models.snippet_models.Footer):
    class Meta(TranslatableMixin.Meta):
        verbose_name = _("Footer")

# dirty "Monkey Patching" to change default CRX snippets behavior
# to inherit of TranslatableMixin
coderedcms.models.snippet_models.Navbar = Navbar
coderedcms.models.snippet_models.Footer = Footer

Also dont forget to build migrations with After I made all the needed migrations with BootstrapTranslatableMixin as explained in the wagtail's documentation: https://docs.wagtail.org/en/stable/advanced_topics/i18n.html#translatable-snippets )

Finally, I had to slightly modify my customized navbar.html and footer.html templates to use the "localized" property provided by TranslatableMixin.

https://docs.wagtail.org/en/stable/reference/pages/model_reference.html#wagtail.models.TranslatableMixin.localized

(see https://docs.coderedcorp.com/wagtail-crx/how_to/headers_and_footers.html for customization tutorial)

here is my footer.html :


{% load wagtailcore_tags coderedcms_tags i18n %}

{% if settings.coderedcms.LayoutSettings.site_footer %}
<footer>
  {% get_footers as footers %}
  {% for footer in footers %}
  <div {% if footer.custom_id %}id="{{footer.custom_id}}"{% endif %} {% if footer.custom_css_class %}class="{{footer.custom_css_class}}"{% endif %}>
    {% for item in footer.localized.content %}
    {% include_block item with settings=settings %}
    {% endfor %}
  </div>
  {% endfor %}
</footer>
{% endif %}

I guess there must be better ways to solve the problem. For instance, I'm not sure the "localized" property uses wagtail cache. But it works and most important: it still based on the coderedcms concrete classes definitions via inheritance, and doesn't requires any built-in source code modifications.

vsalvino commented 1 year ago

That is quite a clever hack/workaround. Glad you got it working! Our goal is to eventually make all models abstract (#56) so that this won't be an issue, but that is still a long way out.

Everything and anything will work with wagtail-cache. It works by looking at the final rendered page, then storing that in the cache based on HTTP headers and URL. So the English version and French version would be cached separately for example, as long as they use separate URLs or separate HTTP lang headers.

vsalvino commented 4 months ago

Closing this due to inactivity. Feel free to re-open if you are still experiencing the issue or have any new information. Thanks!