Ticket #70: forms-nevow-i18n.diff

File forms-nevow-i18n.diff, 19.2 kB (added by Mathieu, 3 years ago)

forms i18n patch but with nevow's i18n instead of gettext's

  • forms/converters.py

    old new  
    66from nevow.compy import Adapter 
    77from forms import iforms, validation 
    88from zope.interface import implements 
     9from forms.i18n import i_ 
    910 
    10  
    1111class NullConverter(Adapter): 
    1212    implements( iforms.IStringConvertible ) 
    1313     
     
    3939        try: 
    4040            value = self.cast(value) 
    4141        except ValueError: 
    42             raise validation.FieldValidationError("Not a valid number"
     42            raise validation.FieldValidationError(i_("Not a valid number")
    4343        return value 
    4444         
    4545         
     
    6767        if not value: 
    6868            return None 
    6969        if value not in ('True', 'False'): 
    70             raise validation.FieldValidationError('%r should be either True or False'
     70            raise validation.FieldValidationError(i_("%r should be either True or False")%value
    7171        return value == 'True' 
    7272     
    7373     
     
    9090        try: 
    9191            y, m, d = [int(p) for p in value.split('-')] 
    9292        except ValueError: 
    93             raise validation.FieldValidationError('Invalid date'
     93            raise validation.FieldValidationError(i_("Invalid date")
    9494        try: 
    9595            value = date(y, m, d) 
    9696        except ValueError, e: 
    97             raise validation.FieldValidationError('Invalid date: '+str(e)) 
     97            raise validation.FieldValidationError(i_("Invalid date")+': '+str(e)) 
    9898        return value 
    9999 
    100100 
     
    131131                h, m, s = parts 
    132132            h, m, s, ms = int(h), int(m), int(s), int(ms) 
    133133        except: 
    134             raise validation.FieldValidationError('Invalid time'
     134            raise validation.FieldValidationError(i_("Invalid time")
    135135         
    136136        try: 
    137137            value = time(h, m, s, ms) 
    138138        except ValueError, e: 
    139             raise validation.FieldValidationError('Invalid time: '+str(e)) 
     139            raise validation.FieldValidationError(i_("Invalid time")+': '+str(e)) 
    140140             
    141141        return value 
    142142         
     
    155155        try: 
    156156            value = date(*value) 
    157157        except (TypeError, ValueError), e: 
    158             raise validation.FieldValidationError('Invalid date: '+str(e)) 
     158            raise validation.FieldValidationError(i_("Invalid date")+': '+str(e)) 
    159159        return value 
    160160         
  • forms/validation.py

    old new  
    11import re 
    22from zope.interface import implements 
    33from forms import iforms 
     4#for internationalisation 
     5from forms.i18n import i_ 
    46 
    5  
    67class FormsError(Exception): 
    78    """ 
    89    Base class for all Forms errors. A single string, message, is accepted and 
     
    5455     
    5556    def validate(self, field, value): 
    5657        if value is None: 
    57             raise FieldRequiredError, 'Required' 
     58            raise FieldRequiredError, i_("Required") 
    5859 
    5960     
    6061class LengthValidator(object): 
     
    6970         
    7071    def validationErrorMessage(self, field): 
    7172        if self.min is not None and self.max is None: 
    72             return 'Must be longer than %r characters'%(self.min,) 
     73            return i_("Must be longer than %r characters")%self.min 
    7374        if self.min is None and self.max is not None: 
    74             return 'Must be shorter than %r characters'%(self.max,) 
    75         return 'Must be between %r and %r characters'%(self.min, self.max) 
     75            return i_("Must be shorter than %r characters")%self.max 
     76        return i_("Must be between %r and %r characters")%(self.min, self.max) 
    7677     
    7778    def validate(self, field, value): 
    7879        if value is None: 
     
    9697         
    9798    def validationErrorMessage(self, field): 
    9899        if self.min is not None and self.max is None: 
    99             return 'Must be greater than %r'%(self.min,) 
     100            return i_("Must be greater than %r")%(self.min,) 
    100101        if self.min is None and self.max is not None: 
    101             return 'Must be less than %r'%(self.max,) 
    102         return 'Must be between %r and %r'%(self.min, self.max) 
     102            return i_("Must be less than %r")%(self.max,) 
     103        return i_("Must be between %r and %r")%(self.min, self.max) 
    103104     
    104105    def validate(self, field, value): 
    105106        if value is None: 
     
    128129        if not hasattr(self.regex, 'match'): 
    129130            self.regex = re.compile(self.regex) 
    130131        if self.regex.match(value) is None: 
    131             raise FieldValidationError, 'Invalid format' 
     132            raise FieldValidationError, i_("Invalid format") 
    132133             
    133134     
    134135__all__ = [ 
  • forms/iforms.py

    old new  
    11from nevow.compy import Interface 
    22 
    3  
    43class IType(Interface): 
    54    def validate(self, value): 
    65        pass 
     
    98class IStructure(Interface): 
    109    pass 
    1110 
     11#to remember the previous i18n config in the context 
     12class ISavedI18NConfig(Interface): 
     13    pass 
    1214 
    1315class IWidget(Interface): 
    1416 
  • forms/form.py

    old new  
    99from forms import iforms, util, validation 
    1010from resourcemanager import ResourceManager 
    1111from zope.interface import implements 
     12#for internationalisation 
     13from forms.i18n import i_, APP_NAME, I18N_DIR 
     14from nevow.i18n import I18NConfig 
    1215 
    13  
    1416SEPARATOR = '!!' 
    1517FORMS_KEY = '__nevow_form__' 
    1618WIDGET_RESOURCE_KEY = 'widget_resource' 
    1719 
    1820 
    1921def renderForm(name): 
    20  
     22     
    2123    def _(ctx, data): 
    2224 
    2325        def _processForm( form, ctx, name ): 
    2426            # Remember the form 
    2527            ctx.remember(form, iforms.IForm) 
     28             
    2629 
    27             # Create a keyed tag that will render the form when flattened. 
     30            # get the current i18n config from the context 
     31            conf = ctx.locate(inevow.II18NConfig) 
     32             
     33            # Create a keyed tag that will render the form when flattened. 
    2834            tag = T.invisible(key=name)[inevow.IRenderer(form)] 
    2935 
    3036            # Create a new context, referencing the above tag, so that we don't 
     
    3238            # rendering. 
    3339            ctx = context.WovenContext(parent=ctx, tag=tag) 
    3440 
     41            # put our config in the context as the saved one 
     42            # forms will use his own config and the saved one, depending one which message to translate 
     43            ctx.remember(conf, iforms.ISavedI18NConfig)  
     44 
     45             
    3546            # Find errors for *this* form and remember things on the context 
    3647            errors = iforms.IFormErrors(ctx, None) 
    3748            if errors is not None and errors.formName == name: 
     
    8091 
    8192    def addField(self, name, type, widgetFactory=None, label=None, description=None, cssClass=None): 
    8293        if not util.validIdentifier(name): 
    83             raise ValueError('%r is an invalid field name'%name) 
     94            raise ValueError(i_("%r is an invalid field name")%name) 
    8495        type.name = name 
    8596        if label is None: 
    8697            label = util.titleFromName(name) 
     
    94105        if self.actions is None: 
    95106            self.actions = [] 
    96107        if name in [action.name for action in self.actions]: 
    97             raise ValueError('Action with name %r already exists.' % name) 
     108            raise ValueError(i_("Action with name %r already exists.")%name) 
    98109        self.actions.append( Action(callback, name, validate, label) ) 
    99110 
    100111    def getField(self,fieldName): 
     
    127138 
    128139        # Get the request args 
    129140        requestArgs = inevow.IRequest(ctx).args 
     141         
    130142 
    131143        # Decode the request arg names 
    132144        charset = getPOSTCharset(ctx) 
     
    154166                    break 
    155167 
    156168        if callback is None: 
    157             raise Exception('The form has no callback and no action was found.'
     169            raise Exception(i_("The form has no callback and no action was found.")
    158170 
    159171        # Store an errors object in the context 
    160172        errors = FormErrors(self.name) 
     
    232244            return d 
    233245        return appserver.NotFound 
    234246 
    235     def renderHTTP(self, ctx): 
     247    def renderHTTP(self, ctx):    
    236248        raise NotImplemented() 
    237249 
    238250    def _fileFromWidget(self, form, ctx, segments): 
     
    363375    the same instance that is located when a form is rendered after validation 
    364376    failure. 
    365377    """ 
     378                                 
    366379    # Get hold of the request 
    367380    request = inevow.IRequest(ctx) 
    368381    # Find or create the known forms instance 
     
    379392 
    380393    def cacheForm( form, name ): 
    381394        if form is None: 
    382             raise Exception('Form %r not found'%name) 
     395            raise Exception(i_("Form %r not found")%name) 
    383396        form.name = name 
    384397        # Make it a known 
    385398        knownForms[name] = form 
     
    436449        tag.fillSlots('name', self.original.name) 
    437450        tag.fillSlots('id', util.keytocssid(ctx.key)) 
    438451        tag.fillSlots('action', url.here) 
     452        #we put the forms' i18n config in the context in _renderErrors 
    439453        tag.fillSlots('errors', self._renderErrors) 
     454        #restore the previous i18n config 
     455        conf = ctx.locate(iforms.ISavedI18NConfig) 
     456        ctx.remember(conf, inevow.II18NConfig) 
     457 
    440458        tag.fillSlots('items', self._renderItems) 
    441459        tag.fillSlots('hiddenitems', self._renderHiddenItems) 
    442460        tag.fillSlots('actions', self._renderActions) 
    443461        return tag 
    444462 
    445463    def _renderErrors(self, ctx, data): 
     464 
    446465        errors = iforms.IFormErrors(ctx, None) 
    447466        if errors is not None: 
    448467            errors = errors.getFormErrors() 
    449468        if not errors: 
    450469            return '' 
     470         
     471        # put forms' i18n config in the context to translate the error messages 
     472        ctx.remember(I18NConfig(domain=APP_NAME, localeDir=I18N_DIR), inevow.II18NConfig) 
    451473 
    452474        errorList = T.ul() 
    453475        for error in errors: 
     
    457479            if isinstance(error, validation.FieldError): 
    458480                name, type, label, description, cssClass = self.original.getField(error.fieldName) 
    459481                errorList[ T.li[ T.strong[ label, ' : ' ], error.message ] ] 
    460         return T.div(class_='errors')[ T.p['Please correct the following errors:'], errorList ] 
     482        return T.div(class_='errors')[ T.p[i_("Please correct the following errors:")], errorList ] 
    461483 
    462484    def _renderItems(self, ctx, data): 
    463485        if self.original.items is None: 
     
    482504    def _renderItem(self, ctx, data): 
    483505 
    484506        def _(ctx, data): 
    485  
     507                 
     508            # put forms' i18n config in the context to translate the messages  
     509            ctx.remember(I18NConfig(domain=APP_NAME, localeDir=I18N_DIR), inevow.II18NConfig) 
     510             
    486511            name, type, label, description, cssClass = data 
    487512            form = self.original 
    488513            formErrors = iforms.IFormErrors(ctx, None) 
     
    523548            else: 
    524549                render = widget.render 
    525550            ctx.tag.fillSlots('inputs', render(ctx, name, formData, formErrors)) 
     551             
    526552            ctx.tag.fillSlots('message', message) 
     553 
     554            # put back the saved i18n config in the context so that the form's descriptions 
     555            # are translated 
     556            conf = ctx.locate(iforms.ISavedI18NConfig) 
     557            ctx.remember(conf, inevow.II18NConfig) 
     558 
    527559            ctx.tag.fillSlots('description', T.div(class_='description')[description or '']) 
     560            
    528561 
     562           # XXX / FIXME : 
     563           # there is still a problem here: 
     564           # at render time, forms has to translate both descriptions and error messages (those under the description, 
     565           # used as reminders) 
     566           # unfortunately, it would have to use both i18n configs (his own config and the saved one  
     567           # from the external web app) 
     568           # but it can't due to the actual code  
     569           # we prefer having the descriptions translated than the error messages (as they are already translated  
     570           # on top of the form) 
     571           # 
     572           # So the code structure should be changed (as in the _renderErrors method) to be able to use both i18n config  
     573           # at different times to translate all messages 
     574 
    529575            return ctx.tag 
    530576 
    531577        return _ 
     
    553599        if self.original.actions is None: 
    554600            yield '' 
    555601            return 
    556  
     602     
     603        #be sure to use the saved i18n config 
     604        conf = iforms.ISavedI18NConfig(ctx) 
     605        ctx.remember(conf, inevow.II18NConfig) 
     606                         
    557607        for action in self.original.actions: 
    558608            yield T.invisible(data=action, render=self._renderAction) 
    559609 
  • forms/types.py

    old new  
    55from forms import iforms, validation 
    66from zope.interface import implements 
    77 
    8  
    98class Type(object): 
    109 
    1110    implements( iforms.IType ) 
  • forms/util.py

    old new  
    33from nevow import inevow 
    44from forms import iforms 
    55 
    6  
    76_IDENTIFIER_REGEX = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') 
    87 
    98 
  • forms/i18n.py

    old new  
     1# -*- coding: iso-8859-1 -*- 
     2""" 
     3i18n for forms 
     4""" 
     5 
     6from nevow.i18n import Translator 
     7import os 
     8import pkg_resources 
     9 
     10#the directory where is located forms' message dir (in absolute path) 
     11I18N_DIR = os.path.abspath(pkg_resources.resource_filename('forms', 'messages')) 
     12APP_NAME = 'forms' 
     13 
     14i_ = Translator(gettextFunction='gettext') 
  • forms/resourcemanager.py

    old new  
    55from shutil import copyfileobj 
    66from exceptions import IOError, OSError 
    77 
    8  
    98class ResourceManagerException( Exception ): 
    109    def __init__( self, *args, **kwds ): 
    1110        super( ResourceManagerException, self ).__init__( *args, **kwds ) 
  • forms/widget.py

    old new  
    55 
    66import itertools 
    77from nevow import inevow, loaders, tags as T, util, url, static, rend 
    8 from nevow.i18n import _ 
    98from forms import converters, iforms, validation 
    109from forms.util import keytocssid 
    1110from forms.form import widgetResourceURL, widgetResourceURLFromContext 
    1211from zope.interface import implements 
    1312from twisted.internet import defer 
     13from forms.i18n import i_ 
    1414 
    15  
    1615# Marker object for args that are not supplied 
    1716_UNSET = object() 
    1817 
    19  
    2018class TextInput(object): 
    2119    """ 
    2220    A text input field. 
     
    180178        if len(pwds) == 0: 
    181179            pwd = '' 
    182180        elif len(pwds) == 1: 
    183             raise validation.FieldValidationError('Please enter the password twice for confirmation.'
     181            raise validation.FieldValidationError(i_("Please enter the password twice for confirmation.")
    184182        else: 
    185183            if pwds[0] != pwds[1]: 
    186                 raise validation.FieldValidationError('Passwords do not match.'
     184                raise validation.FieldValidationError(i_("Passwords do not match.")
    187185        return self.original.validate(pwds[0]) 
    188186 
    189187 
     
    472470                tag(class_='readonly', readonly='readonly') 
    473471 
    474472        if self.dayFirst: 
    475             return dayTag, ' / ', monthTag, ' / ', yearTag, ' ', _('(day/month/year)') 
     473            return dayTag, ' / ', monthTag, ' / ', yearTag, ' ', i_('(day/month/year)') 
    476474        else: 
    477             return monthTag, ' / ', dayTag, ' / ', yearTag, ' ', _('(month/day/year)') 
     475            return monthTag, ' / ', dayTag, ' / ', yearTag, ' ', i_('(month/day/year)') 
    478476 
    479477    def render(self, ctx, key, args, errors): 
    480478        converter = iforms.IDateTupleConvertible(self.original) 
     
    504502        if not ymd: 
    505503            ymd = None 
    506504        elif len(ymd) != 3: 
    507             raise validation.FieldValidationError("Invalid date"
     505            raise validation.FieldValidationError(i_("Invalid date")
    508506        # So, we have what looks like a good attempt to enter a date. 
    509507        if ymd is not None: 
    510508            # If a 2-char year is allowed then prepend the century. 
     
    520518            # By now, we should have a year of at least 4 characters. 
    521519            if len(ymd[0]) < 4: 
    522520                if self.twoCharCutoffYear is not None: 
    523                     msg = "Please enter a 2 or 4 digit year" 
     521                    msg = i_("Please enter a 2 or 4 digit year") 
    524522                else: 
    525                     msg = "Please enter a 4 digit year" 
     523                    msg = i_("Please enter a 4 digit year") 
    526524                raise validation.FieldValidationError(msg) 
    527525            # Map to integers 
    528526            try: 
    529527                ymd = [int(p) for p in ymd] 
    530528            except ValueError, e: 
    531                 raise validation.FieldValidationError("Invalid date"
     529                raise validation.FieldValidationError(i_("Invalid date")
    532530        ymd = iforms.IDateTupleConvertible(self.original).toType(ymd) 
    533531        return self.original.validate(ymd) 
    534532 
     
    592590        if not value: 
    593591            value = None 
    594592        elif len(value) != 2: 
    595             raise validation.FieldValidationError("Invalid date"
     593            raise validation.FieldValidationError(i_("Invalid date")
    596594        if value is not None: 
    597595            try: 
    598596                value = [int(p) for p in value] 
    599597            except ValueError, e: 
    600                 raise validation.FieldValidationError("Invalid date"
     598                raise validation.FieldValidationError(i_("Invalid date")
    601599            if value[1] < 0 or value[1] > 99: 
    602                 raise validation.FieldValidationError("Invalid year. Please enter a two-digit year."
     600                raise validation.FieldValidationError(i_("Invalid year. Please enter a two-digit year.")
    603601            if value[0] > self.cutoffYear: 
    604602                value[0] = 1900 + value[0] 
    605603            else: 
     
    725723            else: 
    726724                yield T.p[value] 
    727725        else: 
    728             yield T.p[T.strong['nothing uploaded']] 
     726            yield T.p[T.strong[i_("nothing uploaded")]] 
    729727 
    730728        yield T.input(name=namer('value'),value=value,type='hidden') 
    731729        tag=T.input(name=key, id=keytocssid(ctx.key),type='file') 
     
    856854            yield T.p[T.img(src=tmpURL)] 
    857855        else: 
    858856            # No uploaded file, no original 
    859             yield T.p[T.strong['Nothing uploaded']] 
     857            yield T.p[T.strong[i_("Nothing uploaded")]] 
    860858 
    861859        yield T.input(name=key, id=keytocssid(ctx.key),type='file') 
    862860 
     
    889887            yield T.p[T.img(src=tmpURL)] 
    890888        else: 
    891889            # No uploaded file, no original 
    892             yield T.p[T.strong['Nothing uploaded']] 
     890            yield T.p[T.strong[i_("Nothing uploaded")]] 
    893891 
    894892        if originalKey: 
    895893            # key of the original that can be used to get a file later 
  • setup.py

    old new  
    1010    author_email='matt@pollenation.net', 
    1111    packages=find_packages(), 
    1212    package_data={ 
    13         'forms': ['forms.css', 'html/*', 'js/*'], 
     13        'forms': ['forms.css', 'html/*', 'js/*', 'messages/fr/*.po','messages/fr/*.sh','messages/fr/LC_MESSAGES/*'], 
     14        #'forms': ['forms.css', 'html/*', 'js/*'], 
    1415        }, 
    1516    zip_safe = True, 
    1617    )