Commit b6d77a9a authored by Baptiste Jonglez's avatar Baptiste Jonglez
Browse files

WIP i18n

parent 7ba267c8
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class BillingConfig(AppConfig):
name = "coin.billing"
verbose_name = "Billing"
verbose_name = _("Billing")
......@@ -9,6 +9,7 @@ from decimal import Decimal
from django.db import models
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
from django.dispatch import receiver
from coin.offers.models import OfferSubscription
......@@ -36,14 +37,15 @@ def invoice_pdf_filename(instance, filename):
class Invoice(models.Model):
INVOICES_STATUS_CHOICES = (
('open', 'A payer'),
('closed', 'Reglée'),
('trouble', 'Litige')
('open', _('To pay')),
('closed', _('Paid')),
('trouble', _('Trouble'))
)
validated = models.BooleanField(default=False, verbose_name='validée',
help_text='Once validated, a PDF is generated'
' and the invoice cannot be modified')
validated = models.BooleanField(default=False, verbose_name=_('validated'),
help_text=_('Once validated, a PDF '
'is generated and the invoice '
'cannot be modified.'))
number = models.CharField(max_length=25,
default=next_invoice_number,
unique=True,
......@@ -133,7 +135,8 @@ class Invoice(models.Model):
return '#%s %0.2f€ %s' % (self.number, self.amount(), self.date_due)
class Meta:
verbose_name = 'facture'
verbose_name = _('invoice')
verbose_name_plural = _('invoices')
class InvoiceDetail(models.Model):
......
......@@ -2,6 +2,7 @@
from __future__ import unicode_literals
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from coin.resources.models import IPSubnet
......@@ -26,7 +27,7 @@ class ParentConfigurationAdmin(PolymorphicParentModelAdmin):
def offer_subscription_member(self, config):
return config.offersubscription.member
offer_subscription_member.short_description = 'Membre'
offer_subscription_member.short_description = _("Member")
def get_child_models(self):
"""
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class ConfigurationConfig(AppConfig):
name = "coin.configuration"
verbose_name = "Configurations"
verbose_name = _("Configurations")
......@@ -2,12 +2,13 @@
from __future__ import unicode_literals
from django.db import models
from polymorphic import PolymorphicModel
from coin.offers.models import OfferSubscription
from django.db.models.signals import post_save, post_delete
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist
from django.dispatch import receiver
from polymorphic import PolymorphicModel
from coin.offers.models import OfferSubscription
from coin.resources.models import IPSubnet
"""
......@@ -25,9 +26,9 @@ class Configuration(PolymorphicModel):
offersubscription = models.OneToOneField(OfferSubscription,
related_name='configuration',
verbose_name='abonnement')
verbose_name=_('subscription'))
comment = models.CharField(blank=True, max_length=512,
verbose_name="commentaire")
verbose_name=_("comment"))
@staticmethod
def get_configurations_choices_list():
......@@ -40,11 +41,11 @@ class Configuration(PolymorphicModel):
def model_name(self):
return self.__class__.__name__
model_name.short_description = 'Nom du modèle'
model_name.short_description = _('Model name')
def configuration_type_name(self):
return self._meta.verbose_name
configuration_type_name.short_description = 'Type'
configuration_type_name.short_description = _('Type')
def get_absolute_url(self):
"""
......@@ -67,7 +68,8 @@ class Configuration(PolymorphicModel):
return self.model_name().lower()
class Meta:
verbose_name = 'configuration'
verbose_name = _('configuration')
verbose_name_plural = _('configurations')
@receiver(post_save, sender=IPSubnet)
......
......@@ -2,9 +2,16 @@
from __future__ import unicode_literals
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from coin.isp_database.models import ISPInfo, RegisteredOffice, OtherWebsite, ChatRoom, CoveredArea, BankInfo
extra_desc = _("These fields are not part of the db.ffdn.org specification, "
"but are used in a few places on the website.")
bankinfo_desc = _("Bank information are not part of the db.ffdn.org "
"specification, but are used in a few places on the website "
"(for billing, for instance).")
class SingleInstanceAdminMixin(object):
"""Hides the "Add" button when there is already an instance"""
......@@ -25,11 +32,10 @@ class RegisteredOfficeInline(admin.StackedInline):
('region', 'country_name'))}),
('Extras', {
'fields': ('siret',),
'description': 'Ces champs ne font pas partie de la spécification db.ffdn.org mais sont utilisés sur le site'})
'description': extra_desc})
)
class OtherWebsiteInline(admin.StackedInline):
model = OtherWebsite
extra = 0
......@@ -51,10 +57,7 @@ class BankInfoInline(admin.StackedInline):
fieldsets = (('', {
'fields': ('iban', 'bic', 'bank_name', 'check_order'),
'description': (
'Les coordonnées bancaires ne font pas partie de la'+
' spécification db.ffdn.org mais sont utilisées par le'+
' site (facturation notamment).')
'description': bankinfo_desc
}),)
......@@ -73,8 +76,7 @@ class ISPInfoAdmin(SingleInstanceAdminMixin, admin.ModelAdmin):
'website')}),
('Extras', {
'fields': ('administrative_email', 'support_email', 'lists_url'),
'description':
'Ces champs ne font pas partie de la spécification db.ffdn.org mais sont utilisés sur le site'
'description': extra_desc
}),
)
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class ISPDatabaseConfig(AppConfig):
name = "coin.isp_database"
verbose_name = "ISP database"
verbose_name = _("ISP database")
......@@ -4,7 +4,7 @@ from __future__ import unicode_literals
from django.db import models
from django.core.validators import MaxValueValidator
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from localflavor.generic.models import IBANField, BICField
from localflavor.fr.models import FRSIRETField
......@@ -15,18 +15,20 @@ from coin import utils
# API version, see http://db.ffdn.org/format
API_VERSION = 0.1
TECHNOLOGIES = (('ftth', 'FTTH'),
('dsl', '*DSL'),
('wifi', 'WiFi'))
TECHNOLOGIES = (('ftth', _('FTTH')),
('dsl', _('xDSL')),
('wifi', _('Wi-Fi')))
class SingleInstanceMixin(object):
"""Makes sure that no more than one instance of a given model is created."""
def clean(self):
from django.utils.translation import ugettext as _
model = self.__class__
if (model.objects.count() > 0 and self.id != model.objects.get().id):
raise ValidationError("Can only create 1 instance of %s" % model.__name__)
error = _("Can only create 1 instance of {model}.")
raise ValidationError(error.format(model=model.__name__))
super(SingleInstanceMixin, self).clean()
......@@ -36,39 +38,43 @@ class ISPInfo(SingleInstanceMixin, models.Model):
The naming convention is different from Python/django so that it
matches exactly the format (which uses CamelCase...)
"""
name = models.CharField(max_length=512,
help_text="The ISP's name")
name = models.CharField(max_length=512, verbose_name=_("name"),
help_text=_("The ISP's name"))
# Length required by the spec
shortname = models.CharField(max_length=15, blank=True,
help_text="Shorter name")
description = models.TextField(blank=True,
help_text="Short text describing the project")
logoURL = models.URLField(blank=True,
verbose_name="logo URL",
help_text="HTTP(S) URL of the ISP's logo")
website = models.URLField(blank=True,
help_text='URL to the official website')
email = models.EmailField(max_length=254,
help_text="Contact email address")
verbose_name=_("short name"),
help_text=_("Shorter name"))
description = models.TextField(blank=True, verbose_name=_("description"),
help_text=_("Short text describing the project"))
logoURL = models.URLField(blank=True, verbose_name=_("logo URL"),
help_text=_("HTTP(S) URL of the ISP's logo"))
website = models.URLField(blank=True, verbose_name=_("website"),
help_text=_('URL to the official website'))
email = models.EmailField(max_length=254, verbose_name=_("email"),
help_text=_("Contact email address"))
mainMailingList = models.EmailField(max_length=254, blank=True,
verbose_name="main mailing list",
help_text="Main public mailing-list")
verbose_name=_("main mailing list"),
help_text=_("Main public mailing-list"))
creationDate = models.DateField(blank=True, null=True,
verbose_name="creation date",
help_text="Date of creation for legal structure")
verbose_name=_("creation date"),
help_text=_("Date of creation for legal structure"))
ffdnMemberSince = models.DateField(blank=True, null=True,
verbose_name="FFDN member since",
help_text="Date at wich the ISP joined the Federation")
verbose_name=_("FFDN member since"),
help_text=_("Date at wich the ISP joined the Federation"))
# TODO: choice field
progressStatus = models.PositiveSmallIntegerField(
validators=[MaxValueValidator(7)],
blank=True, null=True, verbose_name='progress status',
help_text="Progression status of the ISP")
blank=True, null=True, verbose_name=_('progress status'),
help_text=_("Progression status of the ISP"))
# TODO: better model for coordinates
latitude = models.FloatField(blank=True, null=True,
help_text="Coordinates of the registered office (latitude)")
verbose_name=_("latitude"),
help_text=_("Coordinates of the registered "
"office (latitude)"))
longitude = models.FloatField(blank=True, null=True,
help_text="Coordinates of the registered office (longitude)")
verbose_name=_("longitude"),
help_text=_("Coordinates of the registered "
"office (longitude)"))
# Uncomment this if you want to manage these counters by hand.
#member_count = models.PositiveIntegerField(help_text="Number of members")
......@@ -77,16 +83,17 @@ class ISPInfo(SingleInstanceMixin, models.Model):
# field outside of db-ffdn format:
administrative_email = models.EmailField(
max_length=254, blank=True, verbose_name="contact administratif",
help_text='Adresse email pour les contacts administratifs (ex: bureau)')
max_length=254, blank=True, verbose_name=_("administrative contact email"),
help_text=_("Email address to use for administrative inquiries "
"(e.g. membership, personal information)"))
support_email = models.EmailField(
max_length=254, blank=True, verbose_name="contact de support",
help_text="Adresse email pour les demandes de support technique")
max_length=254, blank=True, verbose_name=_("support contact email"),
help_text=_("Email address to use for technical support"))
lists_url = models.URLField(
verbose_name="serveur de listes", blank=True,
help_text="URL du serveur de listes de discussions/diffusion")
verbose_name=_("mailing list server"), blank=True,
help_text=_("URL of the mailing list server"))
@property
def memberCount(self):
......@@ -105,8 +112,9 @@ class ISPInfo(SingleInstanceMixin, models.Model):
@property
def main_chat_verbose(self):
from django.utils.translation import ugettext as _
m = utils.re_chat_url.match(self.chatroom_set.first().url)
return '{channel} sur {server}'.format(**(m.groupdict()))
return _('{channel} on {server}').format(**(m.groupdict()))
def get_absolute_url(self):
return '/isp.json'
......@@ -149,23 +157,36 @@ class ISPInfo(SingleInstanceMixin, models.Model):
def __unicode__(self):
return self.name
class Meta:
verbose_name = _('ISP details')
verbose_name_plural = _('ISP details')
class OtherWebsite(models.Model):
name = models.CharField(max_length=512)
url = models.URLField(verbose_name="URL")
isp = models.ForeignKey(ISPInfo)
name = models.CharField(max_length=512, verbose_name=_("name"))
url = models.URLField(verbose_name=_("URL"))
isp = models.ForeignKey(ISPInfo, verbose_name=_("ISP"))
class Meta:
verbose_name = _('other website')
verbose_name_plural = _('other websites')
class RegisteredOffice(models.Model):
""" http://json-schema.org/address """
post_office_box = models.CharField(max_length=512, blank=True)
extended_address = models.CharField(max_length=512, blank=True)
street_address = models.CharField(max_length=512, blank=True)
locality = models.CharField(max_length=512)
region = models.CharField(max_length=512)
postal_code = models.CharField(max_length=512, blank=True)
country_name = models.CharField(max_length=512)
isp = models.OneToOneField(ISPInfo)
post_office_box = models.CharField(max_length=512, blank=True,
verbose_name=_("post office box"))
extended_address = models.CharField(max_length=512, blank=True,
verbose_name=_("extended address"))
street_address = models.CharField(max_length=512, blank=True,
verbose_name=_("street address"))
locality = models.CharField(max_length=512, verbose_name=_("locality"))
region = models.CharField(max_length=512, verbose_name=_("region"))
postal_code = models.CharField(max_length=512, blank=True,
verbose_name=_("postal code"))
country_name = models.CharField(max_length=512,
verbose_name=_("country name"))
isp = models.OneToOneField(ISPInfo, verbose_name=_("ISP"))
# not in db.ffdn.org spec
siret = FRSIRETField('SIRET')
......@@ -179,41 +200,54 @@ class RegisteredOffice(models.Model):
d[key] = getattr(self, field)
return d
class Meta:
verbose_name = _('registered office')
verbose_name_plural = _('registered offices')
class ChatRoom(models.Model):
url = models.CharField(verbose_name="URL", max_length=256)
isp = models.ForeignKey(ISPInfo)
url = models.CharField(verbose_name=_("URL"), max_length=256)
isp = models.ForeignKey(ISPInfo, verbose_name=_("ISP"))
class Meta:
verbose_name = _('chat room')
verbose_name_plural = _('chat rooms')
class CoveredArea(models.Model):
name = models.CharField(max_length=512)
name = models.CharField(max_length=512, verbose_name=_("name"))
# TODO: we must allow multiple values
technologies = models.CharField(choices=TECHNOLOGIES, max_length=16)
technologies = models.CharField(choices=TECHNOLOGIES, max_length=16,
verbose_name=_("technologies"))
# TODO: find a geojson library
#area =
isp = models.ForeignKey(ISPInfo)
isp = models.ForeignKey(ISPInfo, verbose_name=_("ISP"))
def to_dict(self):
return {"name": self.name,
"technologies": [self.technologies]}
class Meta:
verbose_name = _('covered area')
verbose_name_plural = _('covered areas')
class BankInfo(models.Model):
"""Information about bank account and the bank itself
This is out of the scope of db.ffdn.org spec.
"""
isp = models.OneToOneField(ISPInfo)
iban = IBANField('IBAN')
bic = BICField('BIC', blank=True, null=True)
bank_name = models.CharField('établissement bancaire',
max_length=100, blank=True, null=True)
check_order = models.CharField('ordre',
isp = models.OneToOneField(ISPInfo, verbose_name=_("ISP"))
iban = IBANField(verbose_name=_('IBAN'))
bic = BICField(verbose_name=_('BIC'), blank=True, null=True)
bank_name = models.CharField(verbose_name=_("bank name"), max_length=100,
blank=True, null=True)
check_order = models.CharField(verbose_name=_("check recipient"),
max_length=100, blank=False, null=False,
help_text='Ordre devant figurer sur un \
chèque bancaire à destination de\
l\'association')
help_text=_("Recipient that must appear on "
"a bank check destinated to "
"the ISP"))
class Meta:
verbose_name = 'coordonnées bancaires'
verbose_name_plural = verbose_name
verbose_name = _('bank information')
verbose_name_plural = _('banks information')
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MembersConfig(AppConfig):
name = "coin.members"
verbose_name = "Members"
verbose_name = _("Members")
......@@ -7,6 +7,8 @@ import datetime
from django.db import models
from django.db.models import Q
from django.db.models.signals import pre_save
from django.utils.translation import ugettext_lazy as _
from django.utils.text import capfirst
from django.dispatch import receiver
from django.contrib.auth.models import AbstractUser
from django.conf import settings
......@@ -25,51 +27,51 @@ class Member(CoinLdapSyncMixin, AbstractUser):
REQUIRED_FIELDS = ['first_name', 'last_name', 'email', ]
MEMBER_TYPE_CHOICES = (
('natural_person', 'Personne physique'),
('legal_entity', 'Personne morale'),
('natural_person', _("Individual")),
('legal_entity', _("Organisation")),
)
MEMBER_STATUS_CHOICES = (
('member', 'Adhérent'),
('not_member', 'Non adhérent'),
('pending', "Demande d'adhésion"),
('member', _("Member")),
('not_member', _("Not a member")),
('pending', _("Pending membership")),
)
status = models.CharField(max_length=50, choices=MEMBER_STATUS_CHOICES,
default='member', verbose_name='statut')
default='member', verbose_name=_('status'))
type = models.CharField(max_length=20, choices=MEMBER_TYPE_CHOICES,
default='natural_person', verbose_name='type')
default='natural_person', verbose_name=_("type"))
nickname = models.CharField(max_length=64, blank=True,
verbose_name="nom d'usage",
help_text='Pseudonyme, …')
verbose_name=_("nickname"),
help_text=_("Pseudonym, …"))
organization_name = models.CharField(max_length=200, blank=True,
verbose_name="nom de l'organisme",
help_text='Pour une personne morale')
verbose_name=_("organisation name"),
help_text=_("For organisations"))
home_phone_number = models.CharField(max_length=25, blank=True,
verbose_name='téléphone fixe')
verbose_name=_("home phone number"))
mobile_phone_number = models.CharField(max_length=25, blank=True,
verbose_name='téléphone mobile')
verbose_name=_("mobile phone number"))
# TODO: use a django module that provides an address model? (would
# support more countries and address types)
address = models.TextField(
verbose_name='adresse postale', blank=True, null=True)
verbose_name=_("physical address"), blank=True, null=True)
validator = RegexValidator(regex=r'^\d{5}$',
message=_("Invalid postal code."))
postal_code = models.CharField(max_length=5, blank=True, null=True,
validators=[RegexValidator(regex=r'^\d{5}$',
message='Code postal non valide.')],
verbose_name='code postal')
validators=[validator],
verbose_name=_("postal code"))
city = models.CharField(max_length=200, blank=True, null=True,
verbose_name='commune')
verbose_name=_("city"))
country = models.CharField(max_length=200, blank=True, null=True,
default='France',
verbose_name='pays')
verbose_name=_("country"))
resign_date = models.DateField(null=True, blank=True,
verbose_name="date de départ de "
"l'association",
help_text="En cas de départ prématuré")
comments = models.TextField(blank=True, verbose_name='commentaires',
help_text="Commentaires libres (informations"
" spécifiques concernant l'adhésion,"
" raison du départ, etc)")
verbose_name=_("resign date of membership"),
help_text=_("In case of premature leaving"))
comments = models.TextField(blank=True, verbose_name=_("comments"),
help_text=_("Free-form comments (specific"
" membership information, reason"
" for leaving, etc)"))
# Following fields are managed by the parent class AbstractUser :
# username, first_name, last_name, email
......@@ -82,22 +84,26 @@ class Member(CoinLdapSyncMixin, AbstractUser):
_password_ldap = None
def clean(self):
from django.utils.translation import ugettext as _
if self.type == 'legal_entity':
if not self.organization_name:
raise ValidationError("Le nom de l'organisme est obligatoire "
"pour une personne morale")
raise ValidationError(_("An organisation name is mandatory for"
" organisations."))
elif self.type == 'natural_person':
if not (self.first_name and self.last_name):
raise ValidationError("Le nom et prénom sont obligatoires "
"pour une personne physique")
raise ValidationError(_("First name and last name are mandatory"
" for individuals."))
def __unicode__(self):
from django.utils.translation import ugettext as _
if self.type == 'legal_entity':
return self.organization_name
elif self.nickname:
return self.nickname
else:
return self.first_name + ' ' + self.last_name
return _("{first_name} {last_name}").format(
first_name=self.first_name,
last_name=self.last_name)
def get_full_name(self):
return str(self)
......@@ -110,7 +116,7 @@ class Member(CoinLdapSyncMixin, AbstractUser):
x = self.membership_fees.order_by('-end_date')
if x:
return self.membership_fees.order_by('-end_date')[0].end_date
end_date_of_membership.short_description = "Date de fin d'adhésion"
end_date_of_membership.short_description = _("End date of membership")
def is_paid_up(self):
"""
......@@ -167,7 +173,7 @@ class Member(CoinLdapSyncMixin, AbstractUser):
"""
Update LDAP data when a member is saved
"""
from django.utils.translation import ugettext as _
# Do not perform LDAP query if no usefull fields to update are specified
# in update_fields
# Ex : at login, last_login field is updated by django auth module.
......@@ -175,8 +181,8 @@ class Member(CoinLdapSyncMixin, AbstractUser):
return
# Fail if no username specified
assert self.username, ('Can\'t sync with LDAP because missing username '
'value for the Member : %s' % self)
assert self.username, _("Can't sync with LDAP because missing username "
"value for Member <{name}>").format(name=str(self))
# If try to sync a superuser in creation mode