Use pycountry in a vocabulary

Only for documentation:

Example: a list with sorted label of localized countries

# init.py
import gettext
import pycountry

german = gettext.translation(
    "iso3166",
    pycountry.LOCALES_DIR,
    languages=["de"])
german.install()
german_countries = german.gettext
# vocabulary.py
from your.addon import german_countries
from zope.interface import provider
from zope.schema.interfaces import IVocabularyFactory
from zope.schema.vocabulary import SimpleTerm
from zope.schema.vocabulary import SimpleVocabulary
import pycountry

def countries_factory(context=None):
    countries = [country for country in pycountry.countries]
    countries.sort(key=lambda country: german_countries(country.name))
    terms = [
        SimpleTerm(
            value=country.name,
            token=country.numeric,
            title=german_countries(country.name),
        )
        for country in countries
    ]
    return SimpleVocabulary(terms)


@provider(IVocabularyFactory)
def countries(context):
    return countries_factory(context)

<!-- vocabulary.zcml -->
<configure xmlns="http://namespaces.zope.org/zope"
  xmlns:five="http://namespaces.zope.org/five"
  xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
  i18n_domain="your.addon">

  <utility component=".vocabularies.countries"
    name="your.addon.vocabulary.countries" />

</configure>
# interfaces.py
class IMyCT(model.Schema):
    """Marker interface and Dexterity Python Schema for CT"""

    country = schema.Choice(
        title="Land",
        required=False,
        source="your.addon.vocabulary.countries",
    )

Thanks for the reminder to degrok my country vocabulary :wink:

class CountryVocabulary(object):
    grok.implements(IVocabularyFactory)
    def __call__(self, context):
        terms = [
            SimpleTerm(value=country.alpha3, token=country.alpha3, title=country.name)
            for country in pycountry.countries]
        return SimpleVocabulary(terms)

and slightly different implementation

def get_country_name(country_code, language):
    """ The _(country_name) was somehow returned untranslated (using the 'message' domain)
    so we define country_trans locally
    https://docs.python.org/2/library/gettext.html
    https://pypi.python.org/pypi/pycountry
    https://docs.plone.org/develop/plone/i18n/internationalisation.html
    """
    country_name = pycountry.countries.get(alpha3=country_code).name

    # country_name is in english and there is no iso3166.mo in the en locales folder
    if language != 'en':
        country_trans = gettext.translation('iso3166', pycountry.LOCALES_DIR, languages=[language])
        return country_trans.gettext(country_name)
    else:
        return country_name

There is also https://pypi.org/project/gocept.country/ which wraps pycountry into sources.

2 Likes

Plone Foundation Code of Conduct