Exemple #1
0
def get_localized_property(context, field=None, language=None):
    '''
    When accessing to the name of the field itself, the value
    in the current language will be returned. Unless it's set,
    the value in the default language will be returned.
    '''
    if language:
        return getattr(context, get_real_fieldname(field, language))
    
    if hasattr(settings, 'FALLBACK_LANGUAGES'):
        attrs = [translation.get_language()]
        attrs += get_fallback_languages()
    else:
        attrs = [
            translation.get_language(),
            translation.get_language()[:2],
            settings.LANGUAGE_CODE, 
        ]
    
    # print(context.__dict__)
    # print(getattr(context, 'title_ru', None))
    def predicate(x):
        value = getattr(context, get_real_fieldname(field, x), None)
        return value if valid_for_gettext(value) else None

    return first_match(predicate, attrs)
Exemple #2
0
    def _get_localized_field_checks(self):
        """
        Get the checks we must perform for the localized fields.
        """
        localized_fields_checks = []
        for localized_field in self.instance.localized_fields:
            if self.cleaned_data.get(localized_field) is None:
                continue
            f = getattr(self.instance.__class__, localized_field, None)
            if f and f.unique:
                if f.unique:
                    local_name = get_real_fieldname(localized_field,
                                                    self.language)
                    localized_fields_checks.append(
                        (localized_field, local_name))

        return localized_fields_checks
Exemple #3
0
    def __init__(self,
                 data=None,
                 files=None,
                 auto_id='id_%s',
                 prefix=None,
                 initial=None,
                 error_class=ErrorList,
                 label_suffix=':',
                 empty_permitted=False,
                 instance=None,
                 js=None):

        # store language so it can be used later
        self.language = get_language()

        locale_data = initial or SortedDict()

        # Set up the initial data for the form
        for localized_field in self.localized_fields:
            # determine localized name of the field, because it is called like
            # that in the initial dict
            local_name = get_real_fieldname(localized_field, self.language)

            if initial:  # get value from initial if it is defined
                initial_value = initial.get(local_name)
                if not initial_value and instance:  # try model if defined
                    initial_value = getattr(instance, localized_field)
                locale_data[localized_field] = initial_value
            elif instance is not None:
                locale_data[localized_field] = getattr(instance,
                                                       localized_field)

        super(LocalisedForm,
              self).__init__(data, files, auto_id, prefix, locale_data,
                             error_class, label_suffix, empty_permitted,
                             instance)
Exemple #4
0
def localize_fields(cls, localized_fields):
    """
    For each field name in localized_fields,
    for each language in settings.LANGUAGES,
    add fields to cls,
    and remove the original field, instead
    replace it with a DefaultFieldDescriptor,
    which always returns the field in the current language.
    """

    # never do this twice
    if hasattr(cls, 'localized_fields'):
        return cls

    # MSGID_LANGUAGE is the language that is used for the gettext message id's.
    # If it is not available, because the site isn't using subsites, the
    # LANGUAGE_CODE is good too. MSGID_LANGUAGE gives the opportunity to
    # specify a language not available in the site but which is still used for
    # the message id's.
    msgid_language = getattr(settings, 'MSGID_LANGUAGE',
                             settings.LANGUAGE_CODE)

    # set the localized fields property
    cls.localized_fields = localized_fields

    for field in localized_fields:
        original_attr = get_field_from_model_by_name(cls, field)

        for cnt, language_code in enumerate(get_all_language_codes()):
            i18n_attr = copy.copy(original_attr)
            # add support for south introspection.
            i18n_attr._south_introspects = True
            i18n_attr.original_fieldname = field
            i18n_attr.include_in_xml = False
            lang_attr_name = get_real_fieldname(field, language_code)
            i18n_attr.name = lang_attr_name
            i18n_attr.creation_counter = i18n_attr.creation_counter + .01 * cnt
            # null must be allowed for the message id language because this
            # language might not be available at all in the backend
            if not i18n_attr.null and i18n_attr.default is NOT_PROVIDED:
                i18n_attr.null = True

            if language_code != msgid_language:
                # no validation for the fields that are language specific
                if not i18n_attr.blank:
                    i18n_attr.blank = True

            if i18n_attr.verbose_name:
                i18n_attr.verbose_name = format_lazy('{0}{1}',
                                                     i18n_attr.verbose_name,
                                                     u' (%s)' % language_code)
            cls.add_to_class(lang_attr_name, i18n_attr)

        # delete original field
        del cls._meta.local_fields[cls._meta.local_fields.index(original_attr)]

        # copy some values and functions from the original_attr
        # so the field can emulate the original_attr as good as possible
        kwargs = {
            'serialize':
            getattr(original_attr, 'serialize', True),
            'extra_attrs':
            getattr(original_attr, 'extra_attrs', None),
            'max_length':
            getattr(original_attr, 'max_length', None),
            'min_length':
            getattr(original_attr, 'min_length', None),
            'form_field':
            original_attr.formfield(**FORMFIELD_FOR_DBFIELD_DEFAULTS.get(
                original_attr.__class__, {})),
            'get_internal_type':
            original_attr.get_internal_type,
            'unique':
            getattr(original_attr, 'unique', False),
            'to_python':
            original_attr.to_python,
        }

        # copy __serialize__ if it was defined on the original attr
        if hasattr(original_attr, '__serialize__'):
            kwargs['__serialize__'] = original_attr.__serialize__

        # add the DefaultFieldDescriptor where the original_attr was.
        cls.add_to_class(field, DefaultFieldDescriptor(field, **kwargs))

        # update fields cache
        try:
            cls._meta._fill_fields_cache()
        except AttributeError:
            # Django 1.8 removed _fill_fields_cache
            cls._meta._expire_cache()
            cls._meta._get_fields(reverse=False)

    # return the finished product
    return cls
Exemple #5
0
    def __get__(self, obj, typ=None):
        """
        Read the localised version of the field this descriptor emulates.
        First try to see if the localised field is really set.
        If not, then use ugettext_lazy to find a tranlation in the current language
        for this field.
        """
        # self must be returned in a getattr context.
        if obj is None:
            return self

        current_language = translation.get_language()
        real_field_name = get_real_fieldname(self.name, current_language)

        vo = GettextVO()

        # first check if the database contains the localized data
        vo.stored_value = getattr(obj, real_field_name)
        if getattr(settings, 'I18N_NOFALLBACK', False):
            return vo.stored_value
        # the database does not have our localized data.
        # check if we have a translation, first get the msgid, as a unicode string.
        vo.msgid = get_localized_property(
            obj, self.name,
            getattr(settings, 'MSGID_LANGUAGE', settings.LANGUAGE_CODE))

        # check the translation in the current language
        # but avoid empty string and None
        if valid_for_gettext(vo.msgid):
            vo.msg = self.to_python(translation.ugettext(force_text(vo.msgid)))
        elif valid_for_gettext(vo.stored_value):
            # we can not use the msgid for gettext but we did find a valid
            # translation in the database. Fine we stop here and return that
            # value. No need for a standin, because we don't have a catalog value.
            return vo.stored_value
        else:
            # we can not use the msgid for gettext lookups, so there is no
            # point in trying. Check for fallback languages in database.
            vo.fallback = get_localized_property(obj, self.name)

            if not valid_for_gettext(vo.fallback):
                # Also if we are sure we don't have any old
                # translations in the catalog or something for the fallback
                # languages in the database, we do not need to return a standin either
                return vo.msgid

        # we got here so we've got a valid messageid. Now collect data from the catalog(s)

        # if there isn't anything new in the catalog belonging to the current language:
        if vo.msg == vo.msgid:
            # maybe we have a translation in any of the fallback languages.
            if hasattr(settings, 'FALLBACK_LANGUAGES'):
                # first check if the database has the localized data in
                # any of the fallback languages.
                vo.fallback = get_localized_property(obj, self.name)

                # if the fallback is the same as the msgid, go and look in the catalog
                if vo.fallback == vo.msgid:
                    # there might be a translation in any
                    # of the fallback languages.
                    for fallback in get_fallback_languages():
                        catalog = translation_catalogs(fallback)
                        msg = catalog.ugettext(force_text(vo.msgid))
                        if self.to_python(msg) != vo.msgid:
                            vo.fallback = self.to_python(msg)
                            break
                elif vo.fallback:
                    # if a valid fallback is found, then, since the msg is equal
                    # to the msgid, the fallback is the winner.
                    vo.msg = vo.fallback

        # if we came here we collected data from the catalog and we should return
        # a standin. A standin is the return value, with some extra properties.
        # see GettextVO for the extra properties added.
        if valid_for_gettext(vo.stored_value):
            vo.standin_value_is_from_database = True
            # database always wins
            return standin_for(vo.stored_value, **vo.__dict__)
        elif valid_for_gettext(vo.msg):
            # runner up is the translation in the native language
            return standin_for(vo.msg, **vo.__dict__)
        elif valid_for_gettext(vo.fallback):
            # and last is the translation in a fallback language
            return standin_for(vo.fallback, **vo.__dict__)

        assert (valid_for_gettext(vo.msgid))

        # there is a very very small probability that the translation of
        # a valid msgid evaluates to empty string or None (after to_python).
        # If that happened, we got here. I choose to return None, because i like
        # to be like that
        return None
Exemple #6
0
 def predicate(x):
     field_name = get_real_fieldname(field, x)
     if hasattr(context, field_name):
         return field_name
     return None
Exemple #7
0
 def predicate(x):
     value = getattr(context, get_real_fieldname(field, x), None)
     return value if valid_for_gettext(value) else None
    def __call__(self, cls):
        """run the filter on the class to be decorated"""
        if hasattr(cls, 'model'):
            self.model = cls.model
        elif not self.model:
            raise AttributeError(L10n.error_no_model % (cls.__name__))

        # gather names of fields added by I18n
        added_fields = []
        for field in self.model.localized_fields:
            for language in get_all_language_codes():
                added_fields.append(get_real_fieldname(field, language))

        # hide added fields from form and admin
        cls.exclude = added_fields
        cls.form = forms.make_localised_form(self.model,
                                             cls.form,
                                             exclude=added_fields)

        # determine name of the permission to edit untranslated fields
        permisson_name = "%s.can_edit_untranslated_fields_of_%s" % (
            self.model._meta.app_label, self.model.__name__.lower())

        # make sure that fields become read_only when no permission is given.
        def get_readonly_fields(self, request, obj=None):
            if not request.user.has_perm(permisson_name):
                fields = self.fields or map(lambda x: x.name,
                                            self.model._meta.fields)
                # remove primary key because we don't show that, not even uneditable
                fields.pop(self.model._meta.pk_index())
                prohibited_fields = compute_prohibited(
                    fields, self.exclude, self.model.localized_fields)

                return set(self.readonly_fields).union(prohibited_fields)

            return self.readonly_fields

        cls.get_readonly_fields = get_readonly_fields

        # override some views to hide fields which are not localized
        if hasattr(cls, 'change_view'):
            # BaseModelAdmin.__init__ will mess up our lazy lists if the following is
            # not allready defined
            if 'action_checkbox' not in cls.list_display and cls.actions is not None:
                cls.list_display = ['action_checkbox'] + list(cls.list_display)

            if not cls.list_display_links:
                for name in cls.list_display:
                    if name != 'action_checkbox':
                        cls.list_display_links = [name]
                        break

            # Make certain properties lazy and internationalized
            cls.list_display_links = lazy_localized_list(
                cls.list_display_links, self.model.localized_fields)
            cls.list_display = lazy_localized_list(cls.list_display,
                                                   self.model.localized_fields)
            cls.list_editable = lazy_localized_list(
                cls.list_editable, self.model.localized_fields)
            cls.search_fields = lazy_localized_list(
                cls.search_fields, self.model.localized_fields)

        else:

            def get_formset(self, request, obj=None, **kwargs):
                if self.declared_fieldsets:
                    fields = flatten_fieldsets(self.declared_fieldsets)
                else:
                    fields = None
                if self.exclude is None:
                    exclude = []
                else:
                    exclude = list(self.exclude)
                exclude.extend(self.get_readonly_fields(request, obj))
                exclude = exclude or None

                if not hasattr(self, 'ct_fk_field'):
                    return super(cls, self).get_formset(request, obj, **kwargs)
                else:
                    # TODO:
                    # this code can be deleted if django fixes GenericInlineModelAdmin it's
                    # get_formset signature so it looks like InlineModelAdmin
                    defaults = {
                        "ct_field": self.ct_field,
                        "fk_field": self.ct_fk_field,
                        "form": self.form,
                        "formfield_callback": self.formfield_for_dbfield,
                        "formset": self.formset,
                        "extra": self.extra,
                        "can_delete": self.can_delete,
                        "can_order": False,
                        "fields": fields,
                        "max_num": self.max_num,
                        "exclude": exclude
                    }

                    defaults.update(kwargs)
                    # the BaseGenericInlineFormSet does not work too well
                    # with modified models, so use LocalizableGenericInlineFormSet.
                    if self.formset is BaseGenericInlineFormSet \
                        or self.formset.__class__ is BaseGenericInlineFormSet:
                        defaults['formset'] = LocalizableGenericInlineFormSet

                    return generic_inlineformset_factory(
                        self.model, **defaults)

            cls.get_formset = get_formset

        return cls