Overriding zope.i18n locale settings

This is a note to my and @agitator's future self and anyone to whom it may concerns.

I have a year field and Plone renders it in German as as 2.021.
My customer dislikes the dot in there.

z3c.form uses a dataconverter to localize the output of number fields, such as int, float, decimal.
At the end, the dataconverter uses the locale formatters from zope.i18n.locales and those are having inheriting XML-definitions for de or de_AT.
The definitions are loaded there.

So how to change them?

Answer:

  • copy the xml-file from zope.i18n.locales.data to your projects root,
  • for my case change the numbers group value to empty:
    ...
    <numbers>
        <symbols>
          <decimal>.</decimal>
          <group></group>
    ...
    
  • in your projects __init__ add:
    from zope.i18n.locales import locales
    from zope.i18n.locales.xmlfactory import LocaleFactory
    
    import os
    
    locales._locales["de", "AT", None] = LocaleFactory(
        os.path.join(os.path.dirname(__file__), "de_AT_patched.xml")
    )()
    
    
5 Likes

In my case overriding zope.i18n/de_CH.xml at master · zopefoundation/zope.i18n · GitHub allowed me to have more than 3 decimal places. :partying_face:

2 Likes

Very useful….looking for a solutions for this for years :exploding_head:

Thanks for this solution!

Side note: if you just need to show a year: use a textline + maybe a int validator.
This will sidestep any formatting/localisation afaik

@jaroel just feels wrong if you need it as a number, a property on the widget to toggle formatting/localization would be much nicer :wink:

@agitator Just like an apple a day keeps the doctor away, I'm trying to prevent problems instead solving them :wink:

Of course I'm sure that one day your solution will be of value to many people, so thank you both for posting it!

2 Likes

This approach works for me...

widgets.py

# -*- coding: utf-8 -*-
from zope.component import adapter
from zope.interface import implementer
from zope.interface import Interface
from zope.schema.interfaces import IField
from z3c.form.interfaces import IFieldWidget

from z3c.form.browser.text import TextWidget
from z3c.form.widget import FieldWidget

class IIntNoFormatWidget(Interface):
    """ Marker interface for custom number field without comma formatting
    """
    pass

@implementer(IIntNoFormatWidget)
class IntNoFormatWidget(TextWidget):

    id = u'int-no-format-widget'

    def update(self):
        super(IntNoFormatWidget, self).update()

    def render(self):
        return super(IntNoFormatWidget, self).render()


@adapter(IField)
@implementer(IFieldWidget)
def IntNoFormatFieldWidget(field, request):
    return FieldWidget(field, IntNoFormatWidget(request))



converters.py

# -*- coding: utf-8 -*-
from zope.component import provideAdapter
from zope.component import adapts
from zope.schema.interfaces import IInt
from z3c.form.interfaces import ITextWidget
from z3c.form.converter import IntegerDataConverter
from z3c.form.converter import SequenceDataConverter
from .widgets import IIntNoFormatWidget


class NoFormatIntegerDataConverter(IntegerDataConverter):
    # adapts the Widget Definition above
    adapts(IInt, IIntNoFormatWidget)

    def toWidgetValue(self, value):
        if value is self.field.missing_value:
            return u''
        return unicode(value)

provideAdapter(NoFormatIntegerDataConverter)
2 Likes

This is what I first had in mind too. Works fine for Integer! Also the approach is great for other uses cases!
But the locale customization is handy for other parts of the locale as well, like decimals/floats or fixing wrongly localized months names in Austria.

1 Like