jazzband / django-formtools

A set of high-level abstractions for Django forms
https://django-formtools.readthedocs.io
BSD 3-Clause "New" or "Revised" License
801 stars 135 forks source link

URL translation in NamedUrlWizardView (i18n) #90

Open lingxiaoyang opened 7 years ago

lingxiaoyang commented 7 years ago

Hello there,

I was developing a bilingual app and was trying to find a way of using translated step names in URL. I didn't find a solution either by browsing the code/doc or by asking #django IRC. I ended up in inheriting the view class as below:

class NamedUrlSessionWizardView_i18nURL(NamedUrlSessionWizardView):

    @classonlymethod
    def as_view(cls, *args, **kwargs):
        cls.i18n_url_names = kwargs.pop('i18n_url_names')
        return super(NamedUrlSessionWizardView_i18nURL, cls).as_view(
            *args, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        """
        Replace kwargs['step'] as non-i18n step name.
        """
        if 'step' in kwargs:
            i18n_step = kwargs.pop('step')
            try:
                matched_tup = next(
                    tup for tup in self.i18n_url_names
                    if force_text(tup[1]) == i18n_step
                )
                non_i18n_step = matched_tup[0]
            except StopIteration:
                # not found
                non_i18n_step = i18n_step
            finally:
                kwargs['step'] = non_i18n_step
        return super(NamedUrlSessionWizardView_i18nURL, self).dispatch(
            request, *args, **kwargs)

    def get_step_url(self, step):
        """
        Replace non-i18n step name as i18n step name.
        """
        non_i18n_step = step
        try:
            matched_tup = next(
                tup for tup in self.i18n_url_names
                if force_text(tup[0]) == non_i18n_step
            )
            i18n_step = matched_tup[1]
        except StopIteration:
            # not found
            i18n_step = non_i18n_step
        finally:
            return super(NamedUrlSessionWizardView_i18nURL, self).get_step_url(
                i18n_step)

while in urls.py, I explicitly assigned i18n url names:

create_member_forms = (
    ('basic_information', ClientBasicInformation),
    ('address_information', ClientAddressInformation),
    ('referent_information', ClientReferentInformation),
    ('payment_information', ClientPaymentInformation),
    ('dietary_restriction', ClientRestrictionsInformation),
    ('emergency_contact', ClientEmergencyContactInformation)
)
i18n_url_names = (
    ('basic_information', _('basic_information')),
    ('address_information', _('address_information')),
    ('referent_information', _('referent_information')),
    ('payment_information', _('payment_information')),
    ('dietary_restriction', _('dietary_restriction')),
    ('emergency_contact', _('emergency_contact'))
)
member_wizard = ClientWizard.as_view(create_member_forms,
                                     i18n_url_names=i18n_url_names,
                                     url_name='member:member_step')

urlpatterns = [
    url(_(r'^create/$'), member_wizard, name='member_step'),
    url(_(r'^create/(?P<step>.+)/$'), member_wizard,
        name='member_step'),
    ....

Is it a good solution? If so, could you please consider adding this feature?

i-salameh95 commented 7 months ago

I have tried your solution, but I ended up with this error 'Reverse for 'account_step' not found. 'account_step' is not a valid view function or pattern name', because the view start using the translated name instead of english one...

I mean the name of the step is needed in processing.. so thats why translation is big issue here.

i-salameh95 commented 7 months ago

I have found solution to my problem!

its so simple, you can use the template ( html) to translate the steps like this :

 <div class="account-area">
                            <div class="multiStep-wrapper multiStep-top-border multiStep-bottom-border">
                                <div class="multiStep-wrapper-flex">
                                    <div class="multiStep-wrapper-left">
                                        <ul class="step-list-wrapper list-style-none">
                                            {% for t in wizard.steps.all %}
                                                <li class="single-step-list-step
                                                    {% if wizard.steps.current == t %}current-items
                                                     {% elif forloop.counter0 < wizard.steps.index %}completed {% endif %}">
                                                    <span class="single-multiStep-request-list-item-number"> {{ forloop.counter }} </span>
                                                    <div class="single-step-list-contents">
<!-- hereeeee is the updated thing: so simple ---> 
                                                        <h5 class="title">{% trans t %}</h5>
                                                    </div>
                                                </li>
                                            {% endfor %}
                                        </ul>
                                    </div>
                                    <div class="multiStep-wrapper-contents {% if wizard.steps.current %}active {% endif %}">
                                        {% include 'form_errors.html' %}
                                        <form method="post" enctype="multipart/form-data"
                                              class="multiStep-form style-one simplePresentCart-two"
                                              style="height: calc(100%);" id="main_register_form">
                                            <div class="row">
                                                {% csrf_token %}
                                                {{ wizard.management_form|crispy }}
                                                {% if wizard.form.forms %}
                                                    {{ wizard.form.management_form|crispy }}
                                                    {% for form in wizard.form.forms %}
                                                        {% crispy form %}
                                                    {% endfor %}
                                                {% else %}
                                                    {% crispy wizard.form %}
                                                {% endif %}
                                                <div class="multiStep-footer">
                                                    <div class="multiStep-footer-flex">
                                                        <div class="multiStep-footer-left">
                                                            {% if wizard.steps.prev %}
                                                                <button formnovalidate
                                                                        name="wizard_goto_step"
                                                                        type="submit"
                                                                        class="primry-btn-2 lg-btn"
                                                                        value="{{ wizard.steps.prev }}">{% trans "Prev Step" %}</button>

                                                            {% endif %}
                                                        </div>
                                                        {% if wizard.steps.current == wizard.steps.last %}
                                                            <div class="multiStep-footer-right">
                                                                <input type="submit" class="primry-btn-2 lg-btn"
                                                                       value="{% trans "Register" %}"/>
                                                            </div>
                                                        {% else %}
                                                            <div class="multiStep-footer-right">
                                                                <input type="submit" class="primry-btn-2 lg-btn"
                                                                       value="{% trans "Next" %}"/>
                                                            </div>
                                                        {% endif %}
                                                    </div>
                                                </div>
                                            </div>
                                        </form>
                                    </div>
                                </div>
                            </div>
                        </div>