diff --git a/AUTHORS b/AUTHORS index 66b19c2659..cd54dbad08 100644 --- a/AUTHORS +++ b/AUTHORS @@ -41,7 +41,6 @@ And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- people who have submitted patches, reported bugs, added translations, helped answer newbie questions, and generally made Django that much better: - adurdin@gmail.com alang@bright-green.com Marty Alchin Daniel Alves Barbosa de Oliveira Vaz @@ -90,6 +89,7 @@ answer newbie questions, and generally made Django that much better: dne@mayonnaise.net Maximillian Dornseif Jeremy Dunck + Andrew Durdin Andy Dustman Clint Ecker enlight diff --git a/django/conf/locale/ca/LC_MESSAGES/django.mo b/django/conf/locale/ca/LC_MESSAGES/django.mo index 00cc135c2d..ca03991f2b 100644 Binary files a/django/conf/locale/ca/LC_MESSAGES/django.mo and b/django/conf/locale/ca/LC_MESSAGES/django.mo differ diff --git a/django/conf/locale/ca/LC_MESSAGES/django.po b/django/conf/locale/ca/LC_MESSAGES/django.po index 212ecda34a..ccef4f8e3b 100644 --- a/django/conf/locale/ca/LC_MESSAGES/django.po +++ b/django/conf/locale/ca/LC_MESSAGES/django.po @@ -2,277 +2,43 @@ # This file is distributed under the same license as the PACKAGE package. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER. # -# Ricardo Javier Cárdenes Medina , 2005. -# Ricardo Javier Cardenes Medina , 2005. -# Marc Fargas , 2007. msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-02-15 11:05+1100\n" -"PO-Revision-Date: 2007-01-19 10:23+0100\n" +"POT-Creation-Date: 2007-05-20 18:25+0200\n" +"PO-Revision-Date: 2007-05-20 18:24+0200\n" "Last-Translator: Marc Fargas \n" "Language-Team: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: KBabel 1.11.4\n" +"X-Generator: VIM 7.0\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: db/models/manipulators.py:305 -#, python-format -msgid "%(object)s with this %(type)s already exists for the given %(field)s." -msgstr "Ja existeix %(object)s amb aquest %(fieldname)s." +#: utils/dateformat.py:40 +msgid "p.m." +msgstr "p.m." -#: db/models/manipulators.py:306 contrib/admin/views/main.py:335 -#: contrib/admin/views/main.py:337 contrib/admin/views/main.py:339 -msgid "and" -msgstr "i" +#: utils/dateformat.py:41 +msgid "a.m." +msgstr "a.m." -#: db/models/fields/related.py:53 -#, python-format -msgid "Please enter a valid %s." -msgstr "Si us plau, introdueixi un %s vàlid." +#: utils/dateformat.py:46 +msgid "PM" +msgstr "PM" -#: db/models/fields/related.py:642 -msgid "Separate multiple IDs with commas." -msgstr "Separi múltiples IDs amb comes." +#: utils/dateformat.py:47 +msgid "AM" +msgstr "AM" -#: db/models/fields/related.py:644 -msgid "" -"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." -msgstr "Premi \"Control\" o \"Command\" en un Mac per escollir més d'un." +#: utils/dateformat.py:95 +msgid "midnight" +msgstr "mitja nit" -#: db/models/fields/related.py:691 -#, python-format -msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." -msgid_plural "" -"Please enter valid %(self)s IDs. The values %(value)r are invalid." -msgstr[0] "" -"Si us plau, introdueixi IDs de %(self)s vàlids. El valor %(value)r és " -"invàlid." -msgstr[1] "" -"Si us plau, introdueixi IDs de %(self)s vàlids. Els valors %(value)r són " -"invàlids." - -#: db/models/fields/__init__.py:42 -#, python-format -msgid "%(optname)s with this %(fieldname)s already exists." -msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s." - -#: db/models/fields/__init__.py:116 db/models/fields/__init__.py:273 -#: db/models/fields/__init__.py:605 db/models/fields/__init__.py:616 -#: oldforms/__init__.py:352 newforms/fields.py:78 newforms/fields.py:373 -#: newforms/fields.py:449 newforms/fields.py:460 -msgid "This field is required." -msgstr "Aquest camp és obligatori." - -#: db/models/fields/__init__.py:366 -msgid "This value must be an integer." -msgstr "Aquest valor ha de ser un enter." - -#: db/models/fields/__init__.py:401 -msgid "This value must be either True or False." -msgstr "Aquest valor ha de ser True (Veritat) o False (Fals)" - -#: db/models/fields/__init__.py:422 -msgid "This field cannot be null." -msgstr "Aquest camp no pot ser null (estar buit)." - -#: db/models/fields/__init__.py:454 core/validators.py:147 -msgid "Enter a valid date in YYYY-MM-DD format." -msgstr "Introdueixi una data vàlida en el forma AAAA-MM-DD." - -#: db/models/fields/__init__.py:521 core/validators.py:156 -msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." -msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM." - -#: db/models/fields/__init__.py:625 -msgid "Enter a valid filename." -msgstr "Introdueixi un nom de fitxer vàlid." - -#: conf/global_settings.py:39 -msgid "Arabic" -msgstr "" - -#: conf/global_settings.py:40 -msgid "Bengali" -msgstr "Bengalí" - -#: conf/global_settings.py:41 -#, fuzzy -msgid "Catalan" -msgstr "Italià" - -#: conf/global_settings.py:42 -msgid "Czech" -msgstr "Chec" - -#: conf/global_settings.py:43 -msgid "Welsh" -msgstr "Galès" - -#: conf/global_settings.py:44 -msgid "Danish" -msgstr "Danès" - -#: conf/global_settings.py:45 -msgid "German" -msgstr "Alemany" - -#: conf/global_settings.py:46 -msgid "Greek" -msgstr "Grec" - -#: conf/global_settings.py:47 -msgid "English" -msgstr "Anglès" - -#: conf/global_settings.py:48 -msgid "Spanish" -msgstr "Espanyol" - -#: conf/global_settings.py:49 -msgid "Argentinean Spanish" -msgstr "" - -#: conf/global_settings.py:50 -#, fuzzy -msgid "Finnish" -msgstr "Danès" - -#: conf/global_settings.py:51 -msgid "French" -msgstr "Francés" - -#: conf/global_settings.py:52 -msgid "Galician" -msgstr "Galleg" - -#: conf/global_settings.py:53 -msgid "Hungarian" -msgstr "Húngar" - -#: conf/global_settings.py:54 -msgid "Hebrew" -msgstr "Hebreu" - -#: conf/global_settings.py:55 -msgid "Icelandic" -msgstr "Islandés" - -#: conf/global_settings.py:56 -msgid "Italian" -msgstr "Italià" - -#: conf/global_settings.py:57 -msgid "Japanese" -msgstr "Japonés" - -#: conf/global_settings.py:58 -msgid "Latvian" -msgstr "" - -#: conf/global_settings.py:59 -msgid "Macedonian" -msgstr "" - -#: conf/global_settings.py:60 -msgid "Dutch" -msgstr "Holandés" - -#: conf/global_settings.py:61 -msgid "Norwegian" -msgstr "Norueg" - -#: conf/global_settings.py:62 -#, fuzzy -msgid "Polish" -msgstr "Anglès" - -#: conf/global_settings.py:63 -msgid "Brazilian" -msgstr "Brasileny" - -#: conf/global_settings.py:64 -msgid "Romanian" -msgstr "Rumanés" - -#: conf/global_settings.py:65 -msgid "Russian" -msgstr "Rús" - -#: conf/global_settings.py:66 -msgid "Slovak" -msgstr "Eslovac" - -#: conf/global_settings.py:67 -msgid "Slovenian" -msgstr "Esloveni" - -#: conf/global_settings.py:68 -msgid "Serbian" -msgstr "Serbi" - -#: conf/global_settings.py:69 -msgid "Swedish" -msgstr "Suec" - -#: conf/global_settings.py:70 -msgid "Tamil" -msgstr "" - -#: conf/global_settings.py:71 -msgid "Turkish" -msgstr "" - -#: conf/global_settings.py:72 -msgid "Ukrainian" -msgstr "Ucranià" - -#: conf/global_settings.py:73 -msgid "Simplified Chinese" -msgstr "Xinés simplificat" - -#: conf/global_settings.py:74 -msgid "Traditional Chinese" -msgstr "Xinés tradicional" - -#: utils/timesince.py:12 -msgid "year" -msgid_plural "years" -msgstr[0] "any" -msgstr[1] "anys" - -#: utils/timesince.py:13 -msgid "month" -msgid_plural "months" -msgstr[0] "mes" -msgstr[1] "mesos" - -#: utils/timesince.py:14 -msgid "week" -msgid_plural "weeks" -msgstr[0] "setmana" -msgstr[1] "setmanes" - -#: utils/timesince.py:15 -msgid "day" -msgid_plural "days" -msgstr[0] "dia" -msgstr[1] "dies" - -#: utils/timesince.py:16 -msgid "hour" -msgid_plural "hours" -msgstr[0] "hora" -msgstr[1] "hores" - -#: utils/timesince.py:17 -msgid "minute" -msgid_plural "minutes" -msgstr[0] "minut" -msgstr[1] "minuts" +#: utils/dateformat.py:97 +msgid "noon" +msgstr "mig dia" #: utils/dates.py:6 msgid "Monday" @@ -312,7 +78,7 @@ msgstr "Febrer" #: utils/dates.py:14 utils/dates.py:27 msgid "March" -msgstr "Març" +msgstr "Març" #: utils/dates.py:14 utils/dates.py:27 msgid "April" @@ -426,383 +192,110 @@ msgstr "Nov." msgid "Dec." msgstr "Des." -#: utils/translation/trans_real.py:362 +#: utils/timesince.py:12 +msgid "year" +msgid_plural "years" +msgstr[0] "any" +msgstr[1] "anys" + +#: utils/timesince.py:13 +msgid "month" +msgid_plural "months" +msgstr[0] "mes" +msgstr[1] "mesos" + +#: utils/timesince.py:14 +msgid "week" +msgid_plural "weeks" +msgstr[0] "setmana" +msgstr[1] "setmanes" + +#: utils/timesince.py:15 +msgid "day" +msgid_plural "days" +msgstr[0] "dia" +msgstr[1] "dies" + +#: utils/timesince.py:16 +msgid "hour" +msgid_plural "hours" +msgstr[0] "hora" +msgstr[1] "hores" + +#: utils/timesince.py:17 +msgid "minute" +msgid_plural "minutes" +msgstr[0] "minut" +msgstr[1] "minuts" + +#: utils/timesince.py:40 +#, python-format +msgid "%d milliseconds" +msgstr "%d milisegons" + +#: utils/timesince.py:41 +#, python-format +msgid "%(number)d %(type)s" +msgstr "%(number)d %(type)s" + +#: utils/timesince.py:47 +#, python-format +msgid ", %(number)d %(type)s" +msgstr ", %(number)d %(type)s" + +#: utils/translation/trans_real.py:358 msgid "DATE_FORMAT" msgstr "F j, Y" -#: utils/translation/trans_real.py:363 +#: utils/translation/trans_real.py:359 msgid "DATETIME_FORMAT" msgstr "F j, Y, H:i" -#: utils/translation/trans_real.py:364 +#: utils/translation/trans_real.py:360 msgid "TIME_FORMAT" msgstr "H:i" -#: utils/translation/trans_real.py:380 -#, fuzzy +#: utils/translation/trans_real.py:376 msgid "YEAR_MONTH_FORMAT" -msgstr "F j, Y" +msgstr "j de/d' F del Y" -#: utils/translation/trans_real.py:381 -#, fuzzy +#: utils/translation/trans_real.py:377 msgid "MONTH_DAY_FORMAT" -msgstr "F j, Y" +msgstr "j de/d' F del Y" -#: oldforms/__init__.py:387 +#: template/defaultfilters.py:491 +msgid "yes,no,maybe" +msgstr "si,no,potser" + +#: template/defaultfilters.py:520 #, python-format -msgid "Ensure your text is less than %s character." -msgid_plural "Ensure your text is less than %s characters." -msgstr[0] "Aseguris de que el seu texte té menys de %s caracter." -msgstr[1] "Aseguris de que el seu texte té menys de %s caracters." +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d byte" +msgstr[1] "%(size)d bytes" -#: oldforms/__init__.py:392 -msgid "Line breaks are not allowed here." -msgstr "No es permeten salts de linea." - -#: oldforms/__init__.py:493 oldforms/__init__.py:566 oldforms/__init__.py:605 +#: template/defaultfilters.py:522 #, python-format -msgid "Select a valid choice; '%(data)s' is not in %(choices)s." -msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s." - -#: oldforms/__init__.py:572 contrib/admin/filterspecs.py:150 -#: newforms/widgets.py:162 -msgid "Unknown" -msgstr "Desconegut" - -#: oldforms/__init__.py:572 contrib/admin/filterspecs.py:143 -#: newforms/widgets.py:162 -msgid "Yes" -msgstr "Si" - -#: oldforms/__init__.py:572 contrib/admin/filterspecs.py:143 -#: newforms/widgets.py:162 -msgid "No" -msgstr "No" - -#: oldforms/__init__.py:667 core/validators.py:173 core/validators.py:442 -msgid "No file was submitted. Check the encoding type on the form." -msgstr "" - -#: oldforms/__init__.py:669 -msgid "The submitted file is empty." -msgstr "El fitxer enviat està buit." - -#: oldforms/__init__.py:725 -msgid "Enter a whole number between -32,768 and 32,767." -msgstr "Introdueixi un número enter entre -32,768 i 32,767." - -#: oldforms/__init__.py:735 -msgid "Enter a positive number." -msgstr "Introdueixi un número positiu." - -#: oldforms/__init__.py:745 -msgid "Enter a whole number between 0 and 32,767." -msgstr "Introdueixi un número entre 0 i 32,767." - -#: contrib/sessions/models.py:51 -msgid "session key" -msgstr "clau de la sessió" - -#: contrib/sessions/models.py:52 -msgid "session data" -msgstr "dades de la sessió" - -#: contrib/sessions/models.py:53 -msgid "expire date" -msgstr "data de caducitat" - -#: contrib/sessions/models.py:57 -msgid "session" -msgstr "sessió" - -#: contrib/sessions/models.py:58 -msgid "sessions" -msgstr "sessions" - -#: contrib/auth/forms.py:17 contrib/auth/forms.py:138 -msgid "The two password fields didn't match." -msgstr "" - -#: contrib/auth/forms.py:25 -#, fuzzy -msgid "A user with that username already exists." -msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s." - -#: contrib/auth/forms.py:53 -msgid "" -"Your Web browser doesn't appear to have cookies enabled. Cookies are " -"required for logging in." -msgstr "" -"El seu navegador no sembla tenir les 'cookies' (galetes) activades. Aquestes " -"són necessàries per iniciar la sessió." - -#: contrib/auth/forms.py:60 contrib/admin/views/decorators.py:10 -msgid "" -"Please enter a correct username and password. Note that both fields are case-" -"sensitive." -msgstr "" -"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en " -"compte que tots dos camps son sensibles a majúscules i minúscules." - -#: contrib/auth/forms.py:62 -msgid "This account is inactive." -msgstr "" - -#: contrib/auth/forms.py:85 -msgid "" -"That e-mail address doesn't have an associated user account. Are you sure " -"you've registered?" -msgstr "" - -#: contrib/auth/forms.py:117 -msgid "The two 'new password' fields didn't match." -msgstr "" - -#: contrib/auth/forms.py:124 -msgid "Your old password was entered incorrectly. Please enter it again." -msgstr "" - -#: contrib/auth/views.py:39 -#, fuzzy -msgid "Logged out" -msgstr "Finalitzar sessió" - -#: contrib/auth/models.py:38 contrib/auth/models.py:57 -msgid "name" -msgstr "nom" - -#: contrib/auth/models.py:40 -msgid "codename" -msgstr "nom en clau" - -#: contrib/auth/models.py:42 -msgid "permission" -msgstr "permís" - -#: contrib/auth/models.py:43 contrib/auth/models.py:58 -msgid "permissions" -msgstr "permissos" - -#: contrib/auth/models.py:60 -msgid "group" -msgstr "grup" - -#: contrib/auth/models.py:61 contrib/auth/models.py:100 -msgid "groups" -msgstr "grups" - -#: contrib/auth/models.py:90 -msgid "username" -msgstr "nom d'usuari" - -#: contrib/auth/models.py:90 -msgid "" -"Required. 30 characters or fewer. Alphanumeric characters only (letters, " -"digits and underscores)." -msgstr "" - -#: contrib/auth/models.py:91 -msgid "first name" -msgstr "nom propi" - -#: contrib/auth/models.py:92 -msgid "last name" -msgstr "cognoms" - -#: contrib/auth/models.py:93 -msgid "e-mail address" -msgstr "adreça de correu electrònic" - -#: contrib/auth/models.py:94 -msgid "password" -msgstr "contrasenya" - -#: contrib/auth/models.py:94 -msgid "" -"Use '[algo]$[salt]$[hexdigest]' or use the change " -"password form." -msgstr "" - -#: contrib/auth/models.py:95 -msgid "staff status" -msgstr "és membre del personal" - -#: contrib/auth/models.py:95 -msgid "Designates whether the user can log into this admin site." -msgstr "Indica si l'usuari pot entrar en el lloc administratiu." - -#: contrib/auth/models.py:96 -msgid "active" -msgstr "actiu" - -#: contrib/auth/models.py:96 -#, fuzzy -msgid "" -"Designates whether this user can log into the Django admin. Unselect this " -"instead of deleting accounts." -msgstr "Indica si l'usuari pot entrar en el lloc administratiu." - -#: contrib/auth/models.py:97 -msgid "superuser status" -msgstr "estat de superusuari" - -#: contrib/auth/models.py:97 -msgid "" -"Designates that this user has all permissions without explicitly assigning " -"them." -msgstr "" - -#: contrib/auth/models.py:98 -msgid "last login" -msgstr "últim inici de sessió" - -#: contrib/auth/models.py:99 -msgid "date joined" -msgstr "data de creació" - -#: contrib/auth/models.py:101 -msgid "" -"In addition to the permissions manually assigned, this user will also get " -"all permissions granted to each group he/she is in." -msgstr "" -"Junt amb els permissos asignats manualment, aquest usuari tindrà, també, els " -"permissos dels grups dels que sigui membre." - -#: contrib/auth/models.py:102 -msgid "user permissions" -msgstr "permissos de l'usuari" - -#: contrib/auth/models.py:105 -msgid "user" -msgstr "usuari" - -#: contrib/auth/models.py:106 -msgid "users" -msgstr "usuaris" - -#: contrib/auth/models.py:111 -msgid "Personal info" -msgstr "Informaciò personal" - -#: contrib/auth/models.py:112 -msgid "Permissions" -msgstr "permissos" - -#: contrib/auth/models.py:113 -msgid "Important dates" -msgstr "Dates importants" - -#: contrib/auth/models.py:114 -msgid "Groups" -msgstr "Grups" - -#: contrib/auth/models.py:258 -msgid "message" -msgstr "missatge" - -#: contrib/contenttypes/models.py:26 -msgid "python model class name" -msgstr "nom de la classe del model en python" - -#: contrib/contenttypes/models.py:29 -msgid "content type" -msgstr "tipus de contingut" - -#: contrib/contenttypes/models.py:30 -msgid "content types" -msgstr "tipus de continguts" - -#: contrib/redirects/models.py:7 -msgid "redirect from" -msgstr "redirigir desde" - -#: contrib/redirects/models.py:8 -msgid "" -"This should be an absolute path, excluding the domain name. Example: '/" -"events/search/'." -msgstr "" -"Aquesta ruta hauria de ser el camí absolut, excluint el nom del domini. " -"Exemple '/events/search/'." - -#: contrib/redirects/models.py:9 -msgid "redirect to" -msgstr "redirigir a" - -#: contrib/redirects/models.py:10 -msgid "" -"This can be either an absolute path (as above) or a full URL starting with " -"'http://'." -msgstr "" -"Això pot ser bé una ruta absoluta (com abans) o una URL completa que comenci " -"per http:// ." - -#: contrib/redirects/models.py:13 -msgid "redirect" -msgstr "redirecció" - -#: contrib/redirects/models.py:14 -msgid "redirects" -msgstr "redireccions" - -#: contrib/flatpages/models.py:7 contrib/admin/views/doc.py:315 -msgid "URL" -msgstr "URL" - -#: contrib/flatpages/models.py:8 -msgid "" -"Example: '/about/contact/'. Make sure to have leading and trailing slashes." -msgstr "" -"Exemple: '/about/contact/'. Asseguri's de posar les barres al principi i al " -"final." - -#: contrib/flatpages/models.py:9 -msgid "title" -msgstr "tìtol" - -#: contrib/flatpages/models.py:10 -msgid "content" -msgstr "contingut" - -#: contrib/flatpages/models.py:11 -msgid "enable comments" -msgstr "habilitar comentaris" - -#: contrib/flatpages/models.py:12 -msgid "template name" -msgstr "nom de la plantilla" - -#: contrib/flatpages/models.py:13 -#, fuzzy -msgid "" -"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " -"will use 'flatpages/default.html'." -msgstr "" -"Exemple: 'flatpages/contact_page'. Si no el proporciona, el sistema " -"utilitzarà 'flatpages/default'." - -#: contrib/flatpages/models.py:14 -msgid "registration required" -msgstr "ha de estar registrat" - -#: contrib/flatpages/models.py:14 -msgid "If this is checked, only logged-in users will be able to view the page." -msgstr "Si està marcat, només els usuaris registrats podran veure la pàgina." - -#: contrib/flatpages/models.py:18 -msgid "flat page" -msgstr "pàgina estàtica" - -#: contrib/flatpages/models.py:19 -msgid "flat pages" -msgstr "pàgines estàtiques" +msgid "%.1f KB" +msgstr "%.1f KB" + +#: template/defaultfilters.py:524 +#, python-format +msgid "%.1f MB" +msgstr "%.1f MB" + +#: template/defaultfilters.py:525 +#, python-format +msgid "%.1f GB" +msgstr "%.1f GB" #: contrib/comments/models.py:67 contrib/comments/models.py:166 msgid "object ID" -msgstr "ID de l'objete" +msgstr "ID de l'objecte" #: contrib/comments/models.py:68 msgid "headline" -msgstr "encapçalament" +msgstr "encapçalament" #: contrib/comments/models.py:69 contrib/comments/models.py:90 #: contrib/comments/models.py:167 @@ -811,39 +304,39 @@ msgstr "comentari" #: contrib/comments/models.py:70 msgid "rating #1" -msgstr "calificació 1" +msgstr "qualificació #1" #: contrib/comments/models.py:71 msgid "rating #2" -msgstr "calificació 2" +msgstr "qualificació #2" #: contrib/comments/models.py:72 msgid "rating #3" -msgstr "calificació 3" +msgstr "qualificació #3" #: contrib/comments/models.py:73 msgid "rating #4" -msgstr "calificació 4" +msgstr "qualificació #4" #: contrib/comments/models.py:74 msgid "rating #5" -msgstr "calificació 5" +msgstr "qualificació #5" #: contrib/comments/models.py:75 msgid "rating #6" -msgstr "calificació 6" +msgstr "qualificació #6" #: contrib/comments/models.py:76 msgid "rating #7" -msgstr "calificació 7" +msgstr "qualificació #7" #: contrib/comments/models.py:77 msgid "rating #8" -msgstr "calificació 8" +msgstr "qualificació #8" #: contrib/comments/models.py:82 msgid "is valid rating" -msgstr "es calificació vàlida" +msgstr "és qualificació vàlida" #: contrib/comments/models.py:83 contrib/comments/models.py:169 msgid "date/time submitted" @@ -851,22 +344,22 @@ msgstr "data/hora d'enviament" #: contrib/comments/models.py:84 contrib/comments/models.py:170 msgid "is public" -msgstr "és públic" +msgstr "és públic" #: contrib/comments/models.py:85 contrib/admin/views/doc.py:304 msgid "IP address" -msgstr "Adreça IP" +msgstr "Adreça IP" #: contrib/comments/models.py:86 msgid "is removed" -msgstr "està eliminat" +msgstr "està eliminat" #: contrib/comments/models.py:86 msgid "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." msgstr "" -"Marqui aquesta caixa si el comentari és inapropiat. En lloc seu es mostrarà " +"Marqui aquesta caixa si el comentari no és apropiat. En lloc seu es mostrarà " "\"Aquest comentari ha estat eliminat\" " #: contrib/comments/models.py:91 @@ -875,7 +368,7 @@ msgstr "comentaris" #: contrib/comments/models.py:131 contrib/comments/models.py:207 msgid "Content object" -msgstr "Objete Contingut" +msgstr "Objecte Contingut" #: contrib/comments/models.py:159 #, python-format @@ -898,7 +391,7 @@ msgstr "nom de la persona" #: contrib/comments/models.py:171 msgid "ip address" -msgstr "adreça ip" +msgstr "adreça ip" #: contrib/comments/models.py:173 msgid "approved by staff" @@ -914,15 +407,15 @@ msgstr "comentaris lliures" #: contrib/comments/models.py:233 msgid "score" -msgstr "puntuació" +msgstr "puntuació" #: contrib/comments/models.py:234 msgid "score date" -msgstr "data de la puntuació" +msgstr "data de la puntuació" #: contrib/comments/models.py:237 msgid "karma score" -msgstr "puntuació de karma" +msgstr "puntuació de karma" #: contrib/comments/models.py:238 msgid "karma scores" @@ -931,7 +424,7 @@ msgstr "punts de karma" #: contrib/comments/models.py:242 #, python-format msgid "%(score)d rating by %(user)s" -msgstr "%(score)d punt per %(user)s" +msgstr "%(score)d punt/s per %(user)s" #: contrib/comments/models.py:258 #, python-format @@ -963,11 +456,11 @@ msgstr "Marca de %r" #: contrib/comments/models.py:278 msgid "deletion date" -msgstr "data d'eliminació" +msgstr "data d'eliminació" #: contrib/comments/models.py:280 msgid "moderator deletion" -msgstr "eliminació del moderador" +msgstr "eliminació del moderador" #: contrib/comments/models.py:281 msgid "moderator deletions" @@ -976,93 +469,12 @@ msgstr "eliminacions del moderador" #: contrib/comments/models.py:285 #, python-format msgid "Moderator deletion by %r" -msgstr "eliminació del moderador per %r" - -#: contrib/comments/templates/comments/form.html:6 -#: contrib/comments/templates/comments/form.html:8 -#: contrib/admin/templates/admin/login.html:17 -msgid "Username:" -msgstr "Usuari:" - -#: contrib/comments/templates/comments/form.html:6 -#: contrib/admin/templates/admin_doc/bookmarklets.html:4 -#: contrib/admin/templates/admin_doc/missing_docutils.html:4 -#: contrib/admin/templates/admin_doc/view_detail.html:4 -#: contrib/admin/templates/admin_doc/template_filter_index.html:5 -#: contrib/admin/templates/admin_doc/view_index.html:5 -#: contrib/admin/templates/admin_doc/template_tag_index.html:5 -#: contrib/admin/templates/admin_doc/model_detail.html:3 -#: contrib/admin/templates/admin_doc/model_index.html:5 -#: contrib/admin/templates/admin_doc/index.html:4 -#: contrib/admin/templates/admin_doc/template_detail.html:4 -#: contrib/admin/templates/admin/object_history.html:3 -#: contrib/admin/templates/admin/delete_confirmation.html:3 -#: contrib/admin/templates/admin/change_list.html:5 -#: contrib/admin/templates/admin/change_form.html:10 -#: contrib/admin/templates/admin/base.html:25 -#: contrib/admin/templates/admin/auth/user/change_password.html:9 -#: contrib/admin/templates/registration/password_change_form.html:3 -#: contrib/admin/templates/registration/password_change_done.html:3 -msgid "Log out" -msgstr "Finalitzar sessió" - -#: contrib/comments/templates/comments/form.html:8 -#: contrib/admin/templates/admin/login.html:20 -msgid "Password:" -msgstr "Contrasenya:" - -#: contrib/comments/templates/comments/form.html:8 -msgid "Forgotten your password?" -msgstr "Contrasenya oblidada?" - -#: contrib/comments/templates/comments/form.html:12 -msgid "Ratings" -msgstr "Calificacions" - -#: contrib/comments/templates/comments/form.html:12 -#: contrib/comments/templates/comments/form.html:23 -msgid "Required" -msgstr "Requerit" - -#: contrib/comments/templates/comments/form.html:12 -#: contrib/comments/templates/comments/form.html:23 -msgid "Optional" -msgstr "Opcional" - -#: contrib/comments/templates/comments/form.html:23 -msgid "Post a photo" -msgstr "Enviar una fotografia" - -#: contrib/comments/templates/comments/form.html:28 -#: contrib/comments/templates/comments/freeform.html:5 -msgid "Comment:" -msgstr "Comentari:" - -#: contrib/comments/templates/comments/form.html:35 -#: contrib/comments/templates/comments/freeform.html:10 -msgid "Preview comment" -msgstr "Previsualitzar comentari" - -#: contrib/comments/templates/comments/freeform.html:4 -msgid "Your name:" -msgstr "El seu nom:" - -#: contrib/comments/views/karma.py:19 -msgid "Anonymous users cannot vote" -msgstr "Els usuaris anònims no poden votar" - -#: contrib/comments/views/karma.py:23 -msgid "Invalid comment ID" -msgstr "ID del comentari invàlid" - -#: contrib/comments/views/karma.py:25 -msgid "No voting for yourself" -msgstr "No pots votar-te a tu mateix" +msgstr "eliminació del moderador per %r" #: contrib/comments/views/comments.py:27 msgid "" "This rating is required because you've entered at least one other rating." -msgstr "Es precisa aquesta puntuació perquè has introduit almenys un altre." +msgstr "Es precisa aquesta puntuació perquè has introduït almenys un altre." #: contrib/comments/views/comments.py:111 #, python-format @@ -1094,25 +506,25 @@ msgid "" "\n" "%(text)s" msgstr "" -"Aquest comentari va ser publicat per un usuari incomplert\n" +"Aquest comentari va ser publicat per un usuari incomplet\n" "\n" "%(text)s" #: contrib/comments/views/comments.py:188 #: contrib/comments/views/comments.py:280 msgid "Only POSTs are allowed" -msgstr "Només s'admed POST" +msgstr "Només s'admed POST" #: contrib/comments/views/comments.py:192 #: contrib/comments/views/comments.py:284 msgid "One or more of the required fields wasn't submitted" -msgstr "Un o més dels caps requerits no ha estat sotmés" +msgstr "Un o més dels caps requerits no ha estat sotmès" #: contrib/comments/views/comments.py:196 #: contrib/comments/views/comments.py:286 msgid "Somebody tampered with the comment form (security violation)" msgstr "" -"Algú està jugant amb el formulari de comentaris (violació de seguretat)" +"Algú està jugant amb el formulari de comentaris (violació de seguretat)" #: contrib/comments/views/comments.py:206 #: contrib/comments/views/comments.py:292 @@ -1120,30 +532,155 @@ msgid "" "The comment form had an invalid 'target' parameter -- the object ID was " "invalid" msgstr "" -"El formulari de comentaris tenia un paràmetre 'target' invàlid -- el ID del " -"objecte era invàlid" +"El formulari de comentaris tenia un paràmetre 'target' invàlid -- el ID del " +"objecte era invàlid" #: contrib/comments/views/comments.py:257 #: contrib/comments/views/comments.py:321 msgid "The comment form didn't provide either 'preview' or 'post'" msgstr "" -"El formulari del comentari no ha proveit ni 'previsualitzar' ni 'enviar'" +"El formulari del comentari no ha proveït ni 'previsualitzar' ni 'enviar'" -#: contrib/sites/models.py:10 -msgid "domain name" -msgstr "nom del domini" +#: contrib/comments/views/karma.py:19 +msgid "Anonymous users cannot vote" +msgstr "Els usuaris anònims no poden votar" -#: contrib/sites/models.py:11 -msgid "display name" -msgstr "nom per mostrar" +#: contrib/comments/views/karma.py:23 +msgid "Invalid comment ID" +msgstr "ID del comentari invàlid" -#: contrib/sites/models.py:15 -msgid "site" -msgstr "lloc" +#: contrib/comments/views/karma.py:25 +msgid "No voting for yourself" +msgstr "No pots votar-te a tu mateix" -#: contrib/sites/models.py:16 -msgid "sites" -msgstr "llocs" +#: contrib/comments/templates/comments/form.html:6 +#: contrib/comments/templates/comments/form.html:8 +#: contrib/admin/templates/admin/login.html:17 +msgid "Username:" +msgstr "Usuari:" + +#: contrib/comments/templates/comments/form.html:6 +#: contrib/admin/templates/admin/change_form.html:10 +#: contrib/admin/templates/admin/change_list.html:5 +#: contrib/admin/templates/admin/delete_confirmation.html:3 +#: contrib/admin/templates/admin/object_history.html:3 +#: contrib/admin/templates/admin/base.html:25 +#: contrib/admin/templates/admin/auth/user/change_password.html:9 +#: contrib/admin/templates/admin_doc/missing_docutils.html:4 +#: contrib/admin/templates/admin_doc/template_detail.html:4 +#: contrib/admin/templates/admin_doc/view_detail.html:4 +#: contrib/admin/templates/admin_doc/view_index.html:5 +#: contrib/admin/templates/admin_doc/index.html:4 +#: contrib/admin/templates/admin_doc/template_tag_index.html:5 +#: contrib/admin/templates/admin_doc/bookmarklets.html:4 +#: contrib/admin/templates/admin_doc/model_index.html:5 +#: contrib/admin/templates/admin_doc/template_filter_index.html:5 +#: contrib/admin/templates/admin_doc/model_detail.html:3 +#: contrib/admin/templates/registration/password_change_form.html:3 +#: contrib/admin/templates/registration/password_change_done.html:3 +msgid "Log out" +msgstr "Finalitzar sessió" + +#: contrib/comments/templates/comments/form.html:8 +#: contrib/admin/templates/admin/login.html:20 +msgid "Password:" +msgstr "Contrasenya:" + +#: contrib/comments/templates/comments/form.html:8 +msgid "Forgotten your password?" +msgstr "Contrasenya oblidada?" + +#: contrib/comments/templates/comments/form.html:12 +msgid "Ratings" +msgstr "Qualificacions" + +#: contrib/comments/templates/comments/form.html:12 +#: contrib/comments/templates/comments/form.html:23 +msgid "Required" +msgstr "Requerit" + +#: contrib/comments/templates/comments/form.html:12 +#: contrib/comments/templates/comments/form.html:23 +msgid "Optional" +msgstr "Opcional" + +#: contrib/comments/templates/comments/form.html:23 +msgid "Post a photo" +msgstr "Enviar una fotografia" + +#: contrib/comments/templates/comments/form.html:28 +#: contrib/comments/templates/comments/freeform.html:5 +msgid "Comment:" +msgstr "Comentari:" + +#: contrib/comments/templates/comments/form.html:35 +#: contrib/comments/templates/comments/freeform.html:10 +msgid "Preview comment" +msgstr "Previsualitzar comentari" + +#: contrib/comments/templates/comments/freeform.html:4 +msgid "Your name:" +msgstr "El seu nom:" + +#: contrib/redirects/models.py:7 +msgid "redirect from" +msgstr "redreçar des de" + +#: contrib/redirects/models.py:8 +msgid "" +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." +msgstr "" +"Aquesta ruta hauria de ser el camí absolut, excloent-ne el nom del domini. " +"Exemple '/events/search/'." + +#: contrib/redirects/models.py:9 +msgid "redirect to" +msgstr "redreçar a" + +#: contrib/redirects/models.py:10 +msgid "" +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." +msgstr "" +"Això pot ser bé una ruta absoluta (com abans) o una URL completa que comenci " +"per http:// ." + +#: contrib/redirects/models.py:13 +msgid "redirect" +msgstr "redreçament" + +#: contrib/redirects/models.py:14 +msgid "redirects" +msgstr "redreçaments" + +#: contrib/admin/models.py:16 +msgid "action time" +msgstr "moment de l'acció" + +#: contrib/admin/models.py:19 +msgid "object id" +msgstr "id del objecte" + +#: contrib/admin/models.py:20 +msgid "object repr" +msgstr "'repr' de l'objecte" + +#: contrib/admin/models.py:21 +msgid "action flag" +msgstr "marca de l'acció" + +#: contrib/admin/models.py:22 +msgid "change message" +msgstr "missatge del canvi" + +#: contrib/admin/models.py:25 +msgid "log entry" +msgstr "entrada del registre" + +#: contrib/admin/models.py:26 +msgid "log entries" +msgstr "entrades del registre" #: contrib/admin/filterspecs.py:40 #, python-format @@ -1161,7 +698,7 @@ msgstr "Tots" #: contrib/admin/filterspecs.py:109 msgid "Any date" -msgstr "Cualsevol data" +msgstr "Qualsevol data" #: contrib/admin/filterspecs.py:110 msgid "Today" @@ -1169,7 +706,7 @@ msgstr "Avui" #: contrib/admin/filterspecs.py:113 msgid "Past 7 days" -msgstr "Últims 7 dies" +msgstr "Últims 7 dies" #: contrib/admin/filterspecs.py:115 msgid "This month" @@ -1179,159 +716,407 @@ msgstr "Aquest mes" msgid "This year" msgstr "Aquest any" -#: contrib/admin/models.py:16 -msgid "action time" -msgstr "moment de l'acció" +#: contrib/admin/filterspecs.py:143 newforms/widgets.py:182 +#: oldforms/__init__.py:577 +msgid "Yes" +msgstr "Si" -#: contrib/admin/models.py:19 -msgid "object id" -msgstr "id del objecte" +#: contrib/admin/filterspecs.py:143 newforms/widgets.py:182 +#: oldforms/__init__.py:577 +msgid "No" +msgstr "No" -#: contrib/admin/models.py:20 -msgid "object repr" -msgstr "'repr' de l'objecte" +#: contrib/admin/filterspecs.py:150 newforms/widgets.py:182 +#: oldforms/__init__.py:577 +msgid "Unknown" +msgstr "Desconegut" -#: contrib/admin/models.py:21 -msgid "action flag" -msgstr "marca de l'acció" - -#: contrib/admin/models.py:22 -msgid "change message" -msgstr "missatge del canvi" - -#: contrib/admin/models.py:25 -msgid "log entry" -msgstr "entrada del registre" - -#: contrib/admin/models.py:26 -msgid "log entries" -msgstr "entrades del registre" - -#: contrib/admin/templatetags/admin_list.py:238 +#: contrib/admin/templatetags/admin_list.py:247 msgid "All dates" msgstr "Totes les dates" -#: contrib/admin/templates/admin_doc/bookmarklets.html:3 -#: contrib/admin/templates/admin/500.html:4 -#: contrib/admin/templates/admin/invalid_setup.html:4 -#: contrib/admin/templates/admin/object_history.html:5 -#: contrib/admin/templates/admin/delete_confirmation.html:6 -#: contrib/admin/templates/admin/change_list.html:6 -#: contrib/admin/templates/admin/change_form.html:13 -#: contrib/admin/templates/admin/base.html:30 -#: contrib/admin/templates/admin/auth/user/change_password.html:12 -#: contrib/admin/templates/registration/logged_out.html:4 -#: contrib/admin/templates/registration/password_reset_done.html:4 -#: contrib/admin/templates/registration/password_change_form.html:4 -#: contrib/admin/templates/registration/password_change_done.html:4 -#: contrib/admin/templates/registration/password_reset_form.html:4 -msgid "Home" -msgstr "Inici" +#: contrib/admin/views/doc.py:46 contrib/admin/views/doc.py:48 +#: contrib/admin/views/doc.py:50 +msgid "tag:" +msgstr "etiqueta:" + +#: contrib/admin/views/doc.py:77 contrib/admin/views/doc.py:79 +#: contrib/admin/views/doc.py:81 +msgid "filter:" +msgstr "filtre:" + +#: contrib/admin/views/doc.py:135 contrib/admin/views/doc.py:137 +#: contrib/admin/views/doc.py:139 +msgid "view:" +msgstr "vista:" + +#: contrib/admin/views/doc.py:164 +#, python-format +msgid "App %r not found" +msgstr "La aplicació %r no s'ha pogut trobar" + +#: contrib/admin/views/doc.py:171 +#, python-format +msgid "Model %(name)r not found in app %(label)r" +msgstr "El model %(name)r no s'ha trobat en la aplicació %(label)r" + +#: contrib/admin/views/doc.py:183 +#, python-format +msgid "the related `%(label)s.%(type)s` object" +msgstr "el objecte relacionat `%(label)s.%(type)s`" + +#: contrib/admin/views/doc.py:183 contrib/admin/views/doc.py:205 +#: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224 +msgid "model:" +msgstr "model:" + +#: contrib/admin/views/doc.py:214 +#, python-format +msgid "related `%(label)s.%(name)s` objects" +msgstr "objectes relacionats `%(label)s.%(name)s`" + +#: contrib/admin/views/doc.py:219 +#, python-format +msgid "all %s" +msgstr "tots %s" + +#: contrib/admin/views/doc.py:224 +#, python-format +msgid "number of %s" +msgstr "nombre de %s" + +#: contrib/admin/views/doc.py:229 +#, python-format +msgid "Fields on %s objects" +msgstr "Camps en objectes %s" + +#: contrib/admin/views/doc.py:291 contrib/admin/views/doc.py:301 +#: contrib/admin/views/doc.py:303 contrib/admin/views/doc.py:309 +#: contrib/admin/views/doc.py:310 contrib/admin/views/doc.py:312 +msgid "Integer" +msgstr "Enter" + +#: contrib/admin/views/doc.py:292 +msgid "Boolean (Either True or False)" +msgstr "Booleà (Verdader o Fals)" + +#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:311 +#, python-format +msgid "String (up to %(maxlength)s)" +msgstr "Cadena (fins a %(maxlength)s)" + +#: contrib/admin/views/doc.py:294 +msgid "Comma-separated integers" +msgstr "Enters separats per comes" + +#: contrib/admin/views/doc.py:295 +msgid "Date (without time)" +msgstr "Data (sense hora)" + +#: contrib/admin/views/doc.py:296 +msgid "Date (with time)" +msgstr "Data (amb hora)" + +#: contrib/admin/views/doc.py:297 +msgid "E-mail address" +msgstr "Adreça de correu electrònic" + +#: contrib/admin/views/doc.py:298 contrib/admin/views/doc.py:299 +#: contrib/admin/views/doc.py:302 +msgid "File path" +msgstr "Ruta del fitxer" + +#: contrib/admin/views/doc.py:300 +msgid "Decimal number" +msgstr "Número decimal" + +#: contrib/admin/views/doc.py:306 +msgid "Boolean (Either True, False or None)" +msgstr "Booleà (Verdader, Fals o 'None' (cap))" + +#: contrib/admin/views/doc.py:307 +msgid "Relation to parent model" +msgstr "Relació amb el model pare" + +#: contrib/admin/views/doc.py:308 +msgid "Phone number" +msgstr "Número de telèfon" + +#: contrib/admin/views/doc.py:313 +msgid "Text" +msgstr "Texte" + +#: contrib/admin/views/doc.py:314 +msgid "Time" +msgstr "Hora" + +#: contrib/admin/views/doc.py:315 contrib/flatpages/models.py:7 +msgid "URL" +msgstr "URL" + +#: contrib/admin/views/doc.py:316 +msgid "U.S. state (two uppercase letters)" +msgstr "Estat dels E.U.A. (dos lletres majúscules)" + +#: contrib/admin/views/doc.py:317 +msgid "XML text" +msgstr "Texte XML" + +#: contrib/admin/views/doc.py:343 +#, python-format +msgid "%s does not appear to be a urlpattern object" +msgstr "%s no sembla ser un objecte 'urlpattern'" + +#: contrib/admin/views/decorators.py:10 contrib/auth/forms.py:60 +msgid "" +"Please enter a correct username and password. Note that both fields are case-" +"sensitive." +msgstr "" +"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en " +"compte que tots dos camps son sensibles a majúscules i minúscules." + +#: contrib/admin/views/decorators.py:24 +#: contrib/admin/templates/admin/login.html:25 +msgid "Log in" +msgstr "Iniciar sessió" + +#: contrib/admin/views/decorators.py:62 +msgid "" +"Please log in again, because your session has expired. Don't worry: Your " +"submission has been saved." +msgstr "" +"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es " +"preocupi, el seu enviament està emmagatzemat." + +#: contrib/admin/views/decorators.py:69 +msgid "" +"Looks like your browser isn't configured to accept cookies. Please enable " +"cookies, reload this page, and try again." +msgstr "" +"Sembla ser que el seu navegador no està configurat per acceptar " +"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta " +"pàgina i provi-ho de nou. " + +#: contrib/admin/views/decorators.py:83 +msgid "Usernames cannot contain the '@' character." +msgstr "Els noms d'usuari no poden contenir el caracter '@'." + +#: contrib/admin/views/decorators.py:85 +#, python-format +msgid "Your e-mail address is not your username. Try '%s' instead." +msgstr "" +"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas." + +#: contrib/admin/views/auth.py:19 contrib/admin/views/main.py:257 +#, python-format +msgid "The %(name)s \"%(obj)s\" was added successfully." +msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit." + +#: contrib/admin/views/auth.py:24 contrib/admin/views/main.py:261 +#: contrib/admin/views/main.py:347 +msgid "You may edit it again below." +msgstr "Pot editar-lo de nou abaix." + +#: contrib/admin/views/auth.py:30 +msgid "Add user" +msgstr "Agregar usuari" + +#: contrib/admin/views/auth.py:57 +msgid "Password changed successfully." +msgstr "Canvi de clau exitós" + +#: contrib/admin/views/auth.py:64 +#, python-format +msgid "Change password: %s" +msgstr "Canviar clau: %s" + +#: contrib/admin/views/main.py:223 +msgid "Site administration" +msgstr "Lloc administratiu" + +#: contrib/admin/views/main.py:271 contrib/admin/views/main.py:356 +#, python-format +msgid "You may add another %s below." +msgstr "Pot agregar un altre %s abaix." + +#: contrib/admin/views/main.py:289 +#, python-format +msgid "Add %s" +msgstr "Agregar %s" + +#: contrib/admin/views/main.py:335 +#, python-format +msgid "Added %s." +msgstr "Agregat %s." + +#: contrib/admin/views/main.py:335 contrib/admin/views/main.py:337 +#: contrib/admin/views/main.py:339 db/models/manipulators.py:308 +msgid "and" +msgstr "i" + +#: contrib/admin/views/main.py:337 +#, python-format +msgid "Changed %s." +msgstr "Modificat %s." + +#: contrib/admin/views/main.py:339 +#, python-format +msgid "Deleted %s." +msgstr "Eliminat %s." + +#: contrib/admin/views/main.py:342 +msgid "No fields changed." +msgstr "Cap camp canviat." + +#: contrib/admin/views/main.py:345 +#, python-format +msgid "The %(name)s \"%(obj)s\" was changed successfully." +msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s." + +#: contrib/admin/views/main.py:353 +#, python-format +msgid "" +"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." +msgstr "" +"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix." + +#: contrib/admin/views/main.py:391 +#, python-format +msgid "Change %s" +msgstr "Modificar %s" + +#: contrib/admin/views/main.py:476 +#, python-format +msgid "One or more %(fieldname)s in %(name)s: %(obj)s" +msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s" + +#: contrib/admin/views/main.py:481 +#, python-format +msgid "One or more %(fieldname)s in %(name)s:" +msgstr "Un o més %(fieldname)s en %(name)s:" + +#: contrib/admin/views/main.py:514 +#, python-format +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit." + +#: contrib/admin/views/main.py:517 +msgid "Are you sure?" +msgstr "Està segur?" + +#: contrib/admin/views/main.py:539 +#, python-format +msgid "Change history: %s" +msgstr "Modificar històric: %s" + +#: contrib/admin/views/main.py:573 +#, python-format +msgid "Select %s" +msgstr "Seleccioni %s" + +#: contrib/admin/views/main.py:573 +#, python-format +msgid "Select %s to change" +msgstr "Seleccioni %s per modificar" + +#: contrib/admin/views/main.py:768 +msgid "Database error" +msgstr "Error de/en la base de dades" + +#: contrib/admin/templates/admin/filter.html:2 +#, python-format +msgid " By %(filter_title)s " +msgstr "Per %(filter_title)s " + +#: contrib/admin/templates/admin/filters.html:4 +msgid "Filter" +msgstr "Filtre" -#: contrib/admin/templates/admin_doc/bookmarklets.html:3 -#: contrib/admin/templates/admin/object_history.html:3 -#: contrib/admin/templates/admin/delete_confirmation.html:3 -#: contrib/admin/templates/admin/change_list.html:5 #: contrib/admin/templates/admin/change_form.html:10 +#: contrib/admin/templates/admin/change_list.html:5 +#: contrib/admin/templates/admin/delete_confirmation.html:3 +#: contrib/admin/templates/admin/object_history.html:3 #: contrib/admin/templates/admin/base.html:25 #: contrib/admin/templates/admin/auth/user/change_password.html:9 +#: contrib/admin/templates/admin_doc/bookmarklets.html:3 #: contrib/admin/templates/registration/password_change_form.html:3 #: contrib/admin/templates/registration/password_change_done.html:3 msgid "Documentation" -msgstr "Documentació" +msgstr "Documentació" -#: contrib/admin/templates/admin_doc/bookmarklets.html:3 -msgid "Bookmarklets" -msgstr "'Bookmarklets'" - -#: contrib/admin/templates/admin_doc/bookmarklets.html:4 -#: contrib/admin/templates/admin_doc/missing_docutils.html:4 -#: contrib/admin/templates/admin_doc/view_detail.html:4 -#: contrib/admin/templates/admin_doc/template_filter_index.html:5 -#: contrib/admin/templates/admin_doc/view_index.html:5 -#: contrib/admin/templates/admin_doc/template_tag_index.html:5 -#: contrib/admin/templates/admin_doc/model_detail.html:3 -#: contrib/admin/templates/admin_doc/model_index.html:5 -#: contrib/admin/templates/admin_doc/index.html:4 -#: contrib/admin/templates/admin_doc/template_detail.html:4 -#: contrib/admin/templates/admin/object_history.html:3 -#: contrib/admin/templates/admin/delete_confirmation.html:3 -#: contrib/admin/templates/admin/change_list.html:5 #: contrib/admin/templates/admin/change_form.html:10 +#: contrib/admin/templates/admin/change_list.html:5 +#: contrib/admin/templates/admin/delete_confirmation.html:3 +#: contrib/admin/templates/admin/object_history.html:3 #: contrib/admin/templates/admin/base.html:25 #: contrib/admin/templates/admin/auth/user/change_password.html:9 #: contrib/admin/templates/admin/auth/user/change_password.html:15 #: contrib/admin/templates/admin/auth/user/change_password.html:46 +#: contrib/admin/templates/admin_doc/missing_docutils.html:4 +#: contrib/admin/templates/admin_doc/template_detail.html:4 +#: contrib/admin/templates/admin_doc/view_detail.html:4 +#: contrib/admin/templates/admin_doc/view_index.html:5 +#: contrib/admin/templates/admin_doc/index.html:4 +#: contrib/admin/templates/admin_doc/template_tag_index.html:5 +#: contrib/admin/templates/admin_doc/bookmarklets.html:4 +#: contrib/admin/templates/admin_doc/model_index.html:5 +#: contrib/admin/templates/admin_doc/template_filter_index.html:5 +#: contrib/admin/templates/admin_doc/model_detail.html:3 #: contrib/admin/templates/registration/password_change_form.html:3 #: contrib/admin/templates/registration/password_change_done.html:3 msgid "Change password" msgstr "Canviar clau" -#: contrib/admin/templates/admin_doc/bookmarklets.html:5 -msgid "Documentation bookmarklets" -msgstr "'Bookmarklets' de documentació" +#: contrib/admin/templates/admin/change_form.html:13 +#: contrib/admin/templates/admin/change_list.html:6 +#: contrib/admin/templates/admin/500.html:4 +#: contrib/admin/templates/admin/delete_confirmation.html:6 +#: contrib/admin/templates/admin/object_history.html:5 +#: contrib/admin/templates/admin/base.html:30 +#: contrib/admin/templates/admin/invalid_setup.html:4 +#: contrib/admin/templates/admin/auth/user/change_password.html:12 +#: contrib/admin/templates/admin_doc/bookmarklets.html:3 +#: contrib/admin/templates/registration/password_reset_form.html:4 +#: contrib/admin/templates/registration/password_reset_done.html:4 +#: contrib/admin/templates/registration/password_change_form.html:4 +#: contrib/admin/templates/registration/password_change_done.html:4 +#: contrib/admin/templates/registration/logged_out.html:4 +msgid "Home" +msgstr "Inici" -#: contrib/admin/templates/admin_doc/bookmarklets.html:9 -msgid "" -"\n" -"

To install bookmarklets, drag the link to your bookmarks\n" -"toolbar, or right-click the link and add it to your bookmarks. Now you can\n" -"select the bookmarklet from any page in the site. Note that some of these\n" -"bookmarklets require you to be viewing the site from a computer designated\n" -"as \"internal\" (talk to your system administrator if you aren't sure if\n" -"your computer is \"internal\").

\n" -msgstr "" -"\n" -"

Per a instalar 'bookmarklets', arrosegui l'enllaç a la " -"seva barra de\n" -"marcadors, o faci click amb el botò dret en l'enllaç i afegeixi'l als " -"marcadors.\n" -"Ara pot escollir el 'bookmarklet' desde cualsevol pàgina del lloc.\n" -"Observi que alguns d'aquests 'bookmarklets' precisen que estigui veient\n" -"el lloc desde un ordinador senyalat com a \"intern\" (parli\n" -"amb el seu administrador de sistemes si no està segur de la condició del " -"seu).

\n" +#: contrib/admin/templates/admin/change_form.html:15 +#: contrib/admin/templates/admin/index.html:28 +msgid "Add" +msgstr "Afegir" -#: contrib/admin/templates/admin_doc/bookmarklets.html:19 -msgid "Documentation for this page" -msgstr "Documentació d'aquesta pàgina" +#: contrib/admin/templates/admin/change_form.html:21 +#: contrib/admin/templates/admin/object_history.html:5 +msgid "History" +msgstr "Històric" -#: contrib/admin/templates/admin_doc/bookmarklets.html:20 -msgid "" -"Jumps you from any page to the documentation for the view that generates " -"that page." -msgstr "" -"El porta desde cualsevol pàgina de la documentació a la vista que la genera." +#: contrib/admin/templates/admin/change_form.html:22 +msgid "View on site" +msgstr "Veure en el lloc" -#: contrib/admin/templates/admin_doc/bookmarklets.html:22 -msgid "Show object ID" -msgstr "Mostra el ID de l'objecte" +#: contrib/admin/templates/admin/change_form.html:32 +#: contrib/admin/templates/admin/auth/user/change_password.html:24 +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Si us plau, corregeixi l'error mostrat abaix." +msgstr[1] "Si us plau, corregeixi els errors mostrats abaix." -#: contrib/admin/templates/admin_doc/bookmarklets.html:23 -msgid "" -"Shows the content-type and unique ID for pages that represent a single " -"object." -msgstr "" -"Mostra el 'content-type' (tipus de contingut) i el ID inequívoc de les " -"pàgines que representen un únic objecte." +#: contrib/admin/templates/admin/change_form.html:50 +msgid "Ordering" +msgstr "Ordre" -#: contrib/admin/templates/admin_doc/bookmarklets.html:25 -msgid "Edit this object (current window)" -msgstr "Editar aquest objecte (finestra actual)" +#: contrib/admin/templates/admin/change_form.html:53 +msgid "Order:" +msgstr "Ordre:" -#: contrib/admin/templates/admin_doc/bookmarklets.html:26 -msgid "Jumps to the admin page for pages that represent a single object." -msgstr "" -"El porta a la pàgina d'administració de pàgines que representen un únic " -"objecte." - -#: contrib/admin/templates/admin_doc/bookmarklets.html:28 -msgid "Edit this object (new window)" -msgstr "Editar aquest objecte (nova finestra)" - -#: contrib/admin/templates/admin_doc/bookmarklets.html:29 -msgid "As above, but opens the admin page in a new window." -msgstr "Com abans, però obre la pàgina d'administració en una nova finestra." +#: contrib/admin/templates/admin/change_list.html:12 +#, python-format +msgid "Add %(name)s" +msgstr "Afegir %(name)s" #: contrib/admin/templates/admin/submit_line.html:3 #: contrib/admin/templates/admin/delete_confirmation.html:9 @@ -1354,6 +1139,19 @@ msgstr "Desar i continuar editant" msgid "Save" msgstr "Desar" +#: contrib/admin/templates/admin/404.html:4 +#: contrib/admin/templates/admin/404.html:8 +msgid "Page not found" +msgstr "No s'ha pogut trobar la pàgina" + +#: contrib/admin/templates/admin/404.html:10 +msgid "We're sorry, but the requested page could not be found." +msgstr "Ho sentim, però no s'ha pogut trobar la pàgina solicitada" + +#: contrib/admin/templates/admin/pagination.html:10 +msgid "Show all" +msgstr "Mostrar tots" + #: contrib/admin/templates/admin/500.html:4 msgid "Server error" msgstr "Error del servidor" @@ -1372,23 +1170,15 @@ msgid "" "mail and should be fixed shortly. Thanks for your patience." msgstr "" "Hi ha hagut un error. S'ha informat als administradors del lloc per correu " -"electrònic y hauria d'arreglar-se en breu. Gràcies per la seva paciència." +"electrònic y hauria d'arreglar-se en breu. Gràcies per la seva paciència." -#: contrib/admin/templates/admin/filter.html:2 -#, fuzzy, python-format -msgid " By %(filter_title)s " -msgstr "Per %(title)s " +#: contrib/admin/templates/admin/base_site.html:4 +msgid "Django site admin" +msgstr "Lloc administratiu de Django" -#: contrib/admin/templates/admin/filters.html:4 -msgid "Filter" -msgstr "" - -#: contrib/admin/templates/admin/invalid_setup.html:8 -msgid "" -"Something's wrong with your database installation. Make sure the appropriate " -"database tables have been created, and make sure the database is readable by " -"the appropriate user." -msgstr "" +#: contrib/admin/templates/admin/base_site.html:7 +msgid "Django administration" +msgstr "Adminsitració de Django" #: contrib/admin/templates/admin/search_form.html:8 msgid "Go" @@ -1398,127 +1188,23 @@ msgstr "Cercar" #, python-format msgid "1 result" msgid_plural "%(counter)s results" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "1 resultat" +msgstr[1] "%(counter)s resultats" #: contrib/admin/templates/admin/search_form.html:10 #, python-format msgid "%(full_result_count)s total" -msgstr "" - -#: contrib/admin/templates/admin/object_history.html:5 -#: contrib/admin/templates/admin/change_form.html:21 -msgid "History" -msgstr "Històric" - -#: contrib/admin/templates/admin/object_history.html:18 -msgid "Date/time" -msgstr "Data/hora" - -#: contrib/admin/templates/admin/object_history.html:19 -msgid "User" -msgstr "Usuari" - -#: contrib/admin/templates/admin/object_history.html:20 -msgid "Action" -msgstr "Acció" - -#: contrib/admin/templates/admin/object_history.html:26 -msgid "DATE_WITH_TIME_FULL" -msgstr "F j, Y, H:i " - -#: contrib/admin/templates/admin/object_history.html:36 -msgid "" -"This object doesn't have a change history. It probably wasn't added via this " -"admin site." -msgstr "" -"Aquest objecte no te historial de canvis. Probablement no va ser afegit " -"utilitzant aquest lloc administratiu." - -#: contrib/admin/templates/admin/delete_confirmation.html:14 -#, fuzzy, python-format -msgid "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects:" -msgstr "" -"Eliminar el/la %(object_name)s '%(object)s' provocaria l'eliminació " -"d'objectes relacionats, però el seu compte no te permisos per a esborrar els " -"tipus d'objecte següents:" - -#: contrib/admin/templates/admin/delete_confirmation.html:21 -#, fuzzy, python-format -msgid "" -"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " -"All of the following related items will be deleted:" -msgstr "" -"Està segur voler esborrar els/les %(object_name)s \"%(object)s\"? " -"S'esborraran els següents elements relacionats:" - -#: contrib/admin/templates/admin/delete_confirmation.html:26 -msgid "Yes, I'm sure" -msgstr "Si, estic segur" - -#: contrib/admin/templates/admin/pagination.html:10 -msgid "Show all" -msgstr "" - -#: contrib/admin/templates/admin/change_list.html:12 -#, python-format -msgid "Add %(name)s" -msgstr "Afegir %(name)s" - -#: contrib/admin/templates/admin/change_form.html:15 -#: contrib/admin/templates/admin/index.html:28 -msgid "Add" -msgstr "Afegir" - -#: contrib/admin/templates/admin/change_form.html:22 -msgid "View on site" -msgstr "Veure en el lloc" - -#: contrib/admin/templates/admin/change_form.html:32 -#: contrib/admin/templates/admin/auth/user/change_password.html:24 -msgid "Please correct the error below." -msgid_plural "Please correct the errors below." -msgstr[0] "Si us plau, corregeixi l'error mostrat abaix." -msgstr[1] "Si us plau, corregeixi els errors mostrats abaix." - -#: contrib/admin/templates/admin/change_form.html:50 -msgid "Ordering" -msgstr "Ordre" - -#: contrib/admin/templates/admin/change_form.html:53 -msgid "Order:" -msgstr "Ordre:" - -#: contrib/admin/templates/admin/base.html:25 -msgid "Welcome," -msgstr "Benvingut," - -#: contrib/admin/templates/admin/404.html:4 -#: contrib/admin/templates/admin/404.html:8 -msgid "Page not found" -msgstr "No s'ha pogut trobar la pàgina" - -#: contrib/admin/templates/admin/404.html:10 -msgid "We're sorry, but the requested page could not be found." -msgstr "Ho sentim, però no s'ha pogut trobar la pàgina solicitada" - -#: contrib/admin/templates/admin/login.html:25 -#: contrib/admin/views/decorators.py:24 -msgid "Log in" -msgstr "Iniciar sessió" +msgstr "%(full_result_count)s en total" #: contrib/admin/templates/admin/index.html:17 #, python-format msgid "Models available in the %(name)s application." -msgstr "Models disponibles en la aplicació %(name)s." +msgstr "Models disponibles en la aplicació %(name)s." #: contrib/admin/templates/admin/index.html:18 -#, fuzzy, python-format +#, python-format msgid "%(name)s" -msgstr "Afegir %(name)s" +msgstr "%(name)s" #: contrib/admin/templates/admin/index.html:34 msgid "Change" @@ -1526,7 +1212,7 @@ msgstr "Modificar" #: contrib/admin/templates/admin/index.html:44 msgid "You don't have permission to edit anything." -msgstr "No té permís per editar res." +msgstr "No té permís per editar res." #: contrib/admin/templates/admin/index.html:52 msgid "Recent Actions" @@ -1540,54 +1226,169 @@ msgstr "Les meves accions" msgid "None available" msgstr "Cap disponible" -#: contrib/admin/templates/admin/base_site.html:4 -msgid "Django site admin" -msgstr "Lloc administratiu de Django" +#: contrib/admin/templates/admin/delete_confirmation.html:14 +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Eliminar el/la %(object_name)s '%(escaped_object)s' provocaria l'eliminació " +"d'objectes relacionats, però el seu compte no te permisos per a esborrar els " +"tipus d'objecte següents:" -#: contrib/admin/templates/admin/base_site.html:7 -msgid "Django administration" -msgstr "Adminsitració de Django" +#: contrib/admin/templates/admin/delete_confirmation.html:21 +#, python-format +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Està segur de voler esborrar els/les %(object_name)s \"%(escaped_object)s\"? " +"S'esborraran els següents elements relacionats:" + +#: contrib/admin/templates/admin/delete_confirmation.html:26 +msgid "Yes, I'm sure" +msgstr "Si, estic segur" + +#: contrib/admin/templates/admin/object_history.html:18 +msgid "Date/time" +msgstr "Data/hora" + +#: contrib/admin/templates/admin/object_history.html:19 +msgid "User" +msgstr "Usuari" + +#: contrib/admin/templates/admin/object_history.html:20 +msgid "Action" +msgstr "Acció" + +#: contrib/admin/templates/admin/object_history.html:26 +msgid "DATE_WITH_TIME_FULL" +msgstr "F j, Y, H:i " + +#: contrib/admin/templates/admin/object_history.html:36 +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Aquest objecte no te historial de canvis. Probablement no va ser afegit " +"utilitzant aquest lloc administratiu." + +#: contrib/admin/templates/admin/base.html:25 +msgid "Welcome," +msgstr "Benvingut," + +#: contrib/admin/templates/admin/invalid_setup.html:8 +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Alguna cosa està malament en la instal·lació de la teva base de dades. " +"Assegurat de que s'han creat les taules, i de que la base de dades és " +"llegible per l'usuari apropiat." + +#: contrib/admin/templates/admin/auth/user/change_password.html:28 +#, python-format +msgid "Enter a new password for the user %(username)s." +msgstr "Introdueixi una contrasenya per l'usuari %(username)s" + +#: contrib/admin/templates/admin/auth/user/change_password.html:34 +#: contrib/admin/templates/admin/auth/user/add_form.html:18 +msgid "Password" +msgstr "Contrasenya" + +#: contrib/admin/templates/admin/auth/user/change_password.html:39 +#: contrib/admin/templates/admin/auth/user/add_form.html:23 +msgid "Password (again)" +msgstr "Contrasenya (de nou)" + +#: contrib/admin/templates/admin/auth/user/change_password.html:40 +#: contrib/admin/templates/admin/auth/user/add_form.html:24 +msgid "Enter the same password as above, for verification." +msgstr "Introdueixi la mateixa contrasenya que a sobre, per a verificació." #: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" "First, enter a username and password. Then, you'll be able to edit more user " "options." msgstr "" +"Primer, entri un usuari i una contrasenya. Després podrà editar més opcions " +"del usuari." #: contrib/admin/templates/admin/auth/user/add_form.html:12 -#, fuzzy msgid "Username" -msgstr "Usuari:" +msgstr "Usuari" -#: contrib/admin/templates/admin/auth/user/add_form.html:18 -#: contrib/admin/templates/admin/auth/user/change_password.html:34 -#, fuzzy -msgid "Password" -msgstr "Contrasenya:" +#: contrib/admin/templates/admin_doc/bookmarklets.html:3 +msgid "Bookmarklets" +msgstr "'Bookmarklets'" -#: contrib/admin/templates/admin/auth/user/add_form.html:23 -#: contrib/admin/templates/admin/auth/user/change_password.html:39 -#, fuzzy -msgid "Password (again)" -msgstr "Canvi de clau" +#: contrib/admin/templates/admin_doc/bookmarklets.html:5 +msgid "Documentation bookmarklets" +msgstr "'Bookmarklets' de documentació" -#: contrib/admin/templates/admin/auth/user/add_form.html:24 -#: contrib/admin/templates/admin/auth/user/change_password.html:40 -msgid "Enter the same password as above, for verification." +#: contrib/admin/templates/admin_doc/bookmarklets.html:9 +msgid "" +"\n" +"

To install bookmarklets, drag the link to your bookmarks\n" +"toolbar, or right-click the link and add it to your bookmarks. Now you can\n" +"select the bookmarklet from any page in the site. Note that some of these\n" +"bookmarklets require you to be viewing the site from a computer designated\n" +"as \"internal\" (talk to your system administrator if you aren't sure if\n" +"your computer is \"internal\").

\n" msgstr "" +"\n" +"

Per a instalar 'bookmarklets', arrosegui l'enllaç a la " +"seva barra de\n" +"marcadors, o faci click amb el botò dret en l'enllaç i afegeixi'l als " +"marcadors.\n" +"Ara pot escollir el 'bookmarklet' des de qualsevol pàgina del lloc.\n" +"Observi que alguns d'aquests 'bookmarklets' precisen que estigui veient\n" +"el lloc des de un ordinador senyalat com a \"intern\" (parli\n" +"amb el seu administrador de sistemes si no està segur de la condició del " +"seu).

\n" -#: contrib/admin/templates/admin/auth/user/change_password.html:28 -#, python-format -msgid "Enter a new password for the user %(username)s." +#: contrib/admin/templates/admin_doc/bookmarklets.html:19 +msgid "Documentation for this page" +msgstr "Documentació d'aquesta pàgina" + +#: contrib/admin/templates/admin_doc/bookmarklets.html:20 +msgid "" +"Jumps you from any page to the documentation for the view that generates " +"that page." msgstr "" +"El porta des de qualsevol pàgina de la documentació a la vista que la genera." -#: contrib/admin/templates/widget/file.html:2 -msgid "Currently:" -msgstr "Actualment:" +#: contrib/admin/templates/admin_doc/bookmarklets.html:22 +msgid "Show object ID" +msgstr "Mostra el ID de l'objecte" -#: contrib/admin/templates/widget/file.html:3 -msgid "Change:" -msgstr "Modificar:" +#: contrib/admin/templates/admin_doc/bookmarklets.html:23 +msgid "" +"Shows the content-type and unique ID for pages that represent a single " +"object." +msgstr "" +"Mostra el 'content-type' (tipus de contingut) i el ID inequívoc de les " +"pàgines que representen un únic objecte." + +#: contrib/admin/templates/admin_doc/bookmarklets.html:25 +msgid "Edit this object (current window)" +msgstr "Editar aquest objecte (finestra actual)" + +#: contrib/admin/templates/admin_doc/bookmarklets.html:26 +msgid "Jumps to the admin page for pages that represent a single object." +msgstr "" +"El porta a la pàgina d'administració de pàgines que representen un únic " +"objecte." + +#: contrib/admin/templates/admin_doc/bookmarklets.html:28 +msgid "Edit this object (new window)" +msgstr "Editar aquest objecte (nova finestra)" + +#: contrib/admin/templates/admin_doc/bookmarklets.html:29 +msgid "As above, but opens the admin page in a new window." +msgstr "Com abans, però obre la pàgina d'administració en una nova finestra." #: contrib/admin/templates/widget/date_time.html:3 msgid "Date:" @@ -1597,18 +1398,18 @@ msgstr "Data:" msgid "Time:" msgstr "Hora:" -#: contrib/admin/templates/registration/logged_out.html:8 -msgid "Thanks for spending some quality time with the Web site today." -msgstr "Gràcies per emprar algun temps de cualitat amb el lloc web avui." +#: contrib/admin/templates/widget/file.html:2 +msgid "Currently:" +msgstr "Actualment:" -#: contrib/admin/templates/registration/logged_out.html:10 -msgid "Log in again" -msgstr "Iniciar sessió de nou" +#: contrib/admin/templates/widget/file.html:3 +msgid "Change:" +msgstr "Modificar:" #: contrib/admin/templates/registration/password_reset_email.html:2 msgid "You're receiving this e-mail because you requested a password reset" msgstr "" -"Està rebent aquest missatge degut a que va solicitar un restabliment de " +"Està rebent aquest missatge degut a que va solicitar un restabliment de " "contrasenya." #: contrib/admin/templates/registration/password_reset_email.html:3 @@ -1619,11 +1420,11 @@ msgstr "del seu compte d'usuari a %(site_name)s." #: contrib/admin/templates/registration/password_reset_email.html:5 #, python-format msgid "Your new password is: %(new_password)s" -msgstr "La seva nova contrasenya és: %(new_password)s" +msgstr "La seva nova contrasenya és: %(new_password)s" #: contrib/admin/templates/registration/password_reset_email.html:7 msgid "Feel free to change this password by going to this page:" -msgstr "Sentis lliure de canviar-la en aquesta pàgina:" +msgstr "Sentis lliure de canviar-la en aquesta pàgina:" #: contrib/admin/templates/registration/password_reset_email.html:11 msgid "Your username, in case you've forgotten:" @@ -1631,31 +1432,47 @@ msgstr "El seu nom d'usuari, en cas d'haver-lo oblidat:" #: contrib/admin/templates/registration/password_reset_email.html:13 msgid "Thanks for using our site!" -msgstr "Gràcies per fer us del nostre lloc!" +msgstr "Gràcies per fer us del nostre lloc!" #: contrib/admin/templates/registration/password_reset_email.html:15 #, python-format msgid "The %(site_name)s team" msgstr "L'equip de %(site_name)s" -#: contrib/admin/templates/registration/password_reset_done.html:4 #: contrib/admin/templates/registration/password_reset_form.html:4 #: contrib/admin/templates/registration/password_reset_form.html:6 #: contrib/admin/templates/registration/password_reset_form.html:10 +#: contrib/admin/templates/registration/password_reset_done.html:4 msgid "Password reset" msgstr "Restablir contrasenya" +#: contrib/admin/templates/registration/password_reset_form.html:12 +msgid "" +"Forgotten your password? Enter your e-mail address below, and we'll reset " +"your password and e-mail the new one to you." +msgstr "" +"Ha oblidat la seva contrasenya? Introdueixi la seva adreça de correu " +"electrònic i crearem una nova que li enviarem per correu." + +#: contrib/admin/templates/registration/password_reset_form.html:16 +msgid "E-mail address:" +msgstr "Adreça de correu electrònic:" + +#: contrib/admin/templates/registration/password_reset_form.html:16 +msgid "Reset my password" +msgstr "Restablir la meva contrasenya" + #: contrib/admin/templates/registration/password_reset_done.html:6 #: contrib/admin/templates/registration/password_reset_done.html:10 msgid "Password reset successful" -msgstr "Contrasenya restaber-ta amb èxit" +msgstr "Contrasenya restaber-ta amb èxit" #: contrib/admin/templates/registration/password_reset_done.html:12 msgid "" "We've e-mailed a new password to the e-mail address you submitted. You " "should be receiving it shortly." msgstr "" -"Li hem enviat una contrasenya nova a l'adreça de correu electrònic que ens " +"Li hem enviat una contrasenya nova a l'adreça de correu electrònic que ens " "ha indicat. L'hauria de rebre en breu." #: contrib/admin/templates/registration/password_change_form.html:4 @@ -1693,426 +1510,1228 @@ msgstr "Canviar la meva clau:" #: contrib/admin/templates/registration/password_change_done.html:6 #: contrib/admin/templates/registration/password_change_done.html:10 msgid "Password change successful" -msgstr "Canvi de clau exitò" +msgstr "Canvi de clau exitò" #: contrib/admin/templates/registration/password_change_done.html:12 msgid "Your password was changed." msgstr "La seva clau ha estat canviada." -#: contrib/admin/templates/registration/password_reset_form.html:12 +#: contrib/admin/templates/registration/logged_out.html:8 +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Gràcies per emprar algun temps de cualitat amb el lloc web avui." + +#: contrib/admin/templates/registration/logged_out.html:10 +msgid "Log in again" +msgstr "Iniciar sessió de nou" + +#: contrib/localflavor/fi/forms.py:14 contrib/localflavor/de/forms.py:16 +#: contrib/localflavor/fr/forms.py:17 +msgid "Enter a zip code in the format XXXXX." +msgstr "Introdueixi un codi zip en el format XXXXX." + +#: contrib/localflavor/fi/forms.py:40 contrib/localflavor/fi/forms.py:45 +msgid "Enter a valid Finnish social security number." +msgstr "Introdueixi un número vàlid de la seguretat social finlandesa." + +#: contrib/localflavor/jp/forms.py:21 +msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX." +msgstr "Introdueixi un codi postal en el format XXXXXXX o XX-XXXX." + +#: contrib/localflavor/jp/jp_prefectures.py:4 +msgid "Hokkaido" +msgstr "Hokkaido" + +#: contrib/localflavor/jp/jp_prefectures.py:5 +msgid "Aomori" +msgstr "Aomori" + +#: contrib/localflavor/jp/jp_prefectures.py:6 +msgid "Iwate" +msgstr "Iwate" + +#: contrib/localflavor/jp/jp_prefectures.py:7 +msgid "Miyagi" +msgstr "Miyagi" + +#: contrib/localflavor/jp/jp_prefectures.py:8 +msgid "Akita" +msgstr "Akita" + +#: contrib/localflavor/jp/jp_prefectures.py:9 +msgid "Yamagata" +msgstr "Yamagata" + +#: contrib/localflavor/jp/jp_prefectures.py:10 +msgid "Fukushima" +msgstr "Fukushima" + +#: contrib/localflavor/jp/jp_prefectures.py:11 +msgid "Ibaraki" +msgstr "Ibaraki" + +#: contrib/localflavor/jp/jp_prefectures.py:12 +msgid "Tochigi" +msgstr "Tochigi" + +#: contrib/localflavor/jp/jp_prefectures.py:13 +msgid "Gunma" +msgstr "Gunma" + +#: contrib/localflavor/jp/jp_prefectures.py:14 +msgid "Saitama" +msgstr "Saitama" + +#: contrib/localflavor/jp/jp_prefectures.py:15 +msgid "Chiba" +msgstr "Chiba" + +#: contrib/localflavor/jp/jp_prefectures.py:16 +msgid "Tokyo" +msgstr "Tokyo" + +#: contrib/localflavor/jp/jp_prefectures.py:17 +msgid "Kanagawa" +msgstr "Kanagawa" + +#: contrib/localflavor/jp/jp_prefectures.py:18 +msgid "Yamanashi" +msgstr "Yamanashi" + +#: contrib/localflavor/jp/jp_prefectures.py:19 +msgid "Nagano" +msgstr "Nagano" + +#: contrib/localflavor/jp/jp_prefectures.py:20 +msgid "Niigata" +msgstr "Niigata" + +#: contrib/localflavor/jp/jp_prefectures.py:21 +msgid "Toyama" +msgstr "Toyama" + +#: contrib/localflavor/jp/jp_prefectures.py:22 +msgid "Ishikawa" +msgstr "Ishikawa" + +#: contrib/localflavor/jp/jp_prefectures.py:23 +msgid "Fukui" +msgstr "Fukui" + +#: contrib/localflavor/jp/jp_prefectures.py:24 +msgid "Gifu" +msgstr "Gifu" + +#: contrib/localflavor/jp/jp_prefectures.py:25 +msgid "Shizuoka" +msgstr "Shizuoka" + +#: contrib/localflavor/jp/jp_prefectures.py:26 +msgid "Aichi" +msgstr "Aichi" + +#: contrib/localflavor/jp/jp_prefectures.py:27 +msgid "Mie" +msgstr "Mie" + +#: contrib/localflavor/jp/jp_prefectures.py:28 +msgid "Shiga" +msgstr "Shiga" + +#: contrib/localflavor/jp/jp_prefectures.py:29 +msgid "Kyoto" +msgstr "Kyoto" + +#: contrib/localflavor/jp/jp_prefectures.py:30 +msgid "Osaka" +msgstr "Osaka" + +#: contrib/localflavor/jp/jp_prefectures.py:31 +msgid "Hyogo" +msgstr "Hyogo" + +#: contrib/localflavor/jp/jp_prefectures.py:32 +msgid "Nara" +msgstr "Nara" + +#: contrib/localflavor/jp/jp_prefectures.py:33 +msgid "Wakayama" +msgstr "Wakayama" + +#: contrib/localflavor/jp/jp_prefectures.py:34 +msgid "Tottori" +msgstr "Tottori" + +#: contrib/localflavor/jp/jp_prefectures.py:35 +msgid "Shimane" +msgstr "Shimane" + +#: contrib/localflavor/jp/jp_prefectures.py:36 +msgid "Okayama" +msgstr "Okayama" + +#: contrib/localflavor/jp/jp_prefectures.py:37 +msgid "Hiroshima" +msgstr "Hiroshima" + +#: contrib/localflavor/jp/jp_prefectures.py:38 +msgid "Yamaguchi" +msgstr "Yamaguchi" + +#: contrib/localflavor/jp/jp_prefectures.py:39 +msgid "Tokushima" +msgstr "Tokushima" + +#: contrib/localflavor/jp/jp_prefectures.py:40 +msgid "Kagawa" +msgstr "Kagawa" + +#: contrib/localflavor/jp/jp_prefectures.py:41 +msgid "Ehime" +msgstr "Ehime" + +#: contrib/localflavor/jp/jp_prefectures.py:42 +msgid "Kochi" +msgstr "Kochi" + +#: contrib/localflavor/jp/jp_prefectures.py:43 +msgid "Fukuoka" +msgstr "Fukuoka" + +#: contrib/localflavor/jp/jp_prefectures.py:44 +msgid "Saga" +msgstr "Saga" + +#: contrib/localflavor/jp/jp_prefectures.py:45 +msgid "Nagasaki" +msgstr "Nagasaki" + +#: contrib/localflavor/jp/jp_prefectures.py:46 +msgid "Kumamoto" +msgstr "Kumamoto" + +#: contrib/localflavor/jp/jp_prefectures.py:47 +msgid "Oita" +msgstr "Oita" + +#: contrib/localflavor/jp/jp_prefectures.py:48 +msgid "Miyazaki" +msgstr "Miyazaki" + +#: contrib/localflavor/jp/jp_prefectures.py:49 +msgid "Kagoshima" +msgstr "Kagoshima" + +#: contrib/localflavor/jp/jp_prefectures.py:50 +msgid "Okinawa" +msgstr "Okinawa" + +#: contrib/localflavor/it/forms.py:16 +msgid "Enter a valid zip code." +msgstr "Introdueixi un codi zip vàlid." + +#: contrib/localflavor/it/forms.py:41 +msgid "Enter a valid Social Security number." +msgstr "Introdueixi un número valid de la Seguretat Social." + +#: contrib/localflavor/it/forms.py:68 +msgid "Enter a valid VAT number." +msgstr "Introdueixi un número de IVA (VAT) vàlid." + +#: contrib/localflavor/de/forms.py:60 msgid "" -"Forgotten your password? Enter your e-mail address below, and we'll reset " -"your password and e-mail the new one to you." +"Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X " +"format." msgstr "" -"Ha oblidat la seva contrasenya? Introdueixi la seva adreça de correu " -"electrònic i crearem una nova que li enviarem per correu." +"Introdueixi un número de tarjeta d'identificació alemany vàlid en el format " +"XXXXXXXXXXX-XXXXXXX-XXXXXXX-X." -#: contrib/admin/templates/registration/password_reset_form.html:16 -msgid "E-mail address:" -msgstr "Adreça de correu electrònic:" +#: contrib/localflavor/de/de_states.py:5 +msgid "Baden-Wuerttemberg" +msgstr "Baden-Wuerttemberg" -#: contrib/admin/templates/registration/password_reset_form.html:16 -msgid "Reset my password" -msgstr "Restablir la meva contrasenya" +#: contrib/localflavor/de/de_states.py:6 +msgid "Bavaria" +msgstr "Bavaria" -#: contrib/admin/views/main.py:223 -msgid "Site administration" -msgstr "Lloc administratiu" +#: contrib/localflavor/de/de_states.py:7 +msgid "Berlin" +msgstr "Berlin" -#: contrib/admin/views/main.py:257 contrib/admin/views/auth.py:19 -#, python-format -msgid "The %(name)s \"%(obj)s\" was added successfully." -msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit." +#: contrib/localflavor/de/de_states.py:8 +msgid "Brandenburg" +msgstr "Brandenburg" -#: contrib/admin/views/main.py:261 contrib/admin/views/main.py:347 -#: contrib/admin/views/auth.py:24 -msgid "You may edit it again below." -msgstr "Pot editar-lo de nou abaix." +#: contrib/localflavor/de/de_states.py:9 +msgid "Bremen" +msgstr "Bremen" -#: contrib/admin/views/main.py:271 contrib/admin/views/main.py:356 -#, python-format -msgid "You may add another %s below." -msgstr "Pot agregar un altre %s abaix." +#: contrib/localflavor/de/de_states.py:10 +msgid "Hamburg" +msgstr "Hamburg" -#: contrib/admin/views/main.py:289 -#, python-format -msgid "Add %s" -msgstr "Agregar %s" +#: contrib/localflavor/de/de_states.py:11 +msgid "Hessen" +msgstr "Hessen" -#: contrib/admin/views/main.py:335 -#, python-format -msgid "Added %s." -msgstr "Agregat %s." +#: contrib/localflavor/de/de_states.py:12 +msgid "Mecklenburg-Western Pomerania" +msgstr "Mecklenburg-Western Pomerania" -#: contrib/admin/views/main.py:337 -#, python-format -msgid "Changed %s." -msgstr "Modificat %s." +#: contrib/localflavor/de/de_states.py:13 +msgid "Lower Saxony" +msgstr "Lower Saxony" -#: contrib/admin/views/main.py:339 -#, python-format -msgid "Deleted %s." -msgstr "Eliminat %s." +#: contrib/localflavor/de/de_states.py:14 +msgid "North Rhine-Westphalia" +msgstr "North Rhine-Westphalia" -#: contrib/admin/views/main.py:342 -msgid "No fields changed." -msgstr "Cap camp canviat." +#: contrib/localflavor/de/de_states.py:15 +msgid "Rhineland-Palatinate" +msgstr "Rhineland-Palatinate" -#: contrib/admin/views/main.py:345 -#, python-format -msgid "The %(name)s \"%(obj)s\" was changed successfully." -msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s." +#: contrib/localflavor/de/de_states.py:16 +msgid "Saarland" +msgstr "Saarland" -#: contrib/admin/views/main.py:353 -#, python-format +#: contrib/localflavor/de/de_states.py:17 +msgid "Saxony" +msgstr "Saxony" + +#: contrib/localflavor/de/de_states.py:18 +msgid "Saxony-Anhalt" +msgstr "Saxony-Anhalt" + +#: contrib/localflavor/de/de_states.py:19 +msgid "Schleswig-Holstein" +msgstr "Schleswig-Holstein" + +#: contrib/localflavor/de/de_states.py:20 +msgid "Thuringia" +msgstr "Thuringia" + +#: contrib/localflavor/us/forms.py:18 +msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX." +msgstr "Introdueixi un codi zip en el format XXXXX o XXXXX-XXXX." + +#: contrib/localflavor/us/forms.py:51 +msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format." +msgstr "" +"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format " +"XXX-XX-XXXX." + +#: contrib/localflavor/ch/forms.py:18 contrib/localflavor/no/forms.py:15 +msgid "Enter a zip code in the format XXXX." +msgstr "Introdueixi un codi zip en el format XXXX." + +#: contrib/localflavor/ch/forms.py:90 msgid "" -"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." +"Enter a valid Swiss identity or passport card number in X1234567<0 or " +"1234567890 format." msgstr "" -"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix." +"Introdueixi un número de identificació o de passaport Suïssos en els formats " +"1234567890 o X1234567<0." -#: contrib/admin/views/main.py:391 -#, python-format -msgid "Change %s" -msgstr "Modificar %s" +#: contrib/localflavor/ch/ch_states.py:5 +msgid "Aargau" +msgstr "Argau" -#: contrib/admin/views/main.py:473 -#, python-format -msgid "One or more %(fieldname)s in %(name)s: %(obj)s" -msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s" +#: contrib/localflavor/ch/ch_states.py:6 +msgid "Appenzell Innerrhoden" +msgstr "Appenzell Innerrhoden" -#: contrib/admin/views/main.py:478 -#, python-format -msgid "One or more %(fieldname)s in %(name)s:" -msgstr "Un o més %(fieldname)s en %(name)s:" +#: contrib/localflavor/ch/ch_states.py:7 +msgid "Appenzell Ausserrhoden" +msgstr "Appenzell Ausserrhoden" -#: contrib/admin/views/main.py:511 -#, python-format -msgid "The %(name)s \"%(obj)s\" was deleted successfully." -msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit." +#: contrib/localflavor/ch/ch_states.py:8 +msgid "Basel-Stadt" +msgstr "Basel-Stadt" -#: contrib/admin/views/main.py:514 -msgid "Are you sure?" -msgstr "Està segur?" +#: contrib/localflavor/ch/ch_states.py:9 +msgid "Basel-Land" +msgstr "Basel-Land" -#: contrib/admin/views/main.py:536 -#, python-format -msgid "Change history: %s" -msgstr "Modificar històric: %s" +#: contrib/localflavor/ch/ch_states.py:10 +msgid "Berne" +msgstr "Berne" -#: contrib/admin/views/main.py:570 -#, python-format -msgid "Select %s" -msgstr "Seleccioni %s" +#: contrib/localflavor/ch/ch_states.py:11 +msgid "Fribourg" +msgstr "Fribourg" -#: contrib/admin/views/main.py:570 -#, python-format -msgid "Select %s to change" -msgstr "Seleccioni %s per modificar" +#: contrib/localflavor/ch/ch_states.py:12 +msgid "Geneva" +msgstr "Geneva" -#: contrib/admin/views/main.py:758 -msgid "Database error" -msgstr "" +#: contrib/localflavor/ch/ch_states.py:13 +msgid "Glarus" +msgstr "Glarus" -#: contrib/admin/views/decorators.py:62 +#: contrib/localflavor/ch/ch_states.py:14 +msgid "Graubuenden" +msgstr "Graubuenden" + +#: contrib/localflavor/ch/ch_states.py:15 +msgid "Jura" +msgstr "Jura" + +#: contrib/localflavor/ch/ch_states.py:16 +msgid "Lucerne" +msgstr "Lucerne" + +#: contrib/localflavor/ch/ch_states.py:17 +msgid "Neuchatel" +msgstr "Neuchatel" + +#: contrib/localflavor/ch/ch_states.py:18 +msgid "Nidwalden" +msgstr "Nidwalden" + +#: contrib/localflavor/ch/ch_states.py:19 +msgid "Obwalden" +msgstr "Obwalden" + +#: contrib/localflavor/ch/ch_states.py:20 +msgid "Schaffhausen" +msgstr "Schaffhausen" + +#: contrib/localflavor/ch/ch_states.py:21 +msgid "Schwyz" +msgstr "Schwyz" + +#: contrib/localflavor/ch/ch_states.py:22 +msgid "Solothurn" +msgstr "Solothurn" + +#: contrib/localflavor/ch/ch_states.py:23 +msgid "St. Gallen" +msgstr "St. Gallen" + +#: contrib/localflavor/ch/ch_states.py:24 +msgid "Thurgau" +msgstr "Thurgau" + +#: contrib/localflavor/ch/ch_states.py:25 +msgid "Ticino" +msgstr "Ticino" + +#: contrib/localflavor/ch/ch_states.py:26 +msgid "Uri" +msgstr "Uri" + +#: contrib/localflavor/ch/ch_states.py:27 +msgid "Valais" +msgstr "Valais" + +#: contrib/localflavor/ch/ch_states.py:28 +msgid "Vaud" +msgstr "Vaud" + +#: contrib/localflavor/ch/ch_states.py:29 +msgid "Zug" +msgstr "Zug" + +#: contrib/localflavor/ch/ch_states.py:30 +msgid "Zurich" +msgstr "Zurich" + +#: contrib/localflavor/au/forms.py:18 +msgid "Enter a 4 digit post code." +msgstr "Introdueixi un codi postal de 4 dígits." + +#: contrib/localflavor/is_/forms.py:16 msgid "" -"Please log in again, because your session has expired. Don't worry: Your " -"submission has been saved." +"Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." msgstr "" -"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es " -"preocupi, el seu enviament està emmagatzemat." +"Introdueixi un número de identificació d'Islàndia. El format és XXXXXX-XXXX." -#: contrib/admin/views/decorators.py:69 +#: contrib/localflavor/is_/forms.py:30 +msgid "The Icelandic identification number is not valid." +msgstr "El número de identificació d'Islàndia no és vàlid." + +#: contrib/localflavor/br/forms.py:18 +msgid "Enter a zip code in the format XXXXX-XXX." +msgstr "Introdueixi un codi zip en el format XXXXX-XXX." + +#: contrib/localflavor/br/forms.py:30 +msgid "Phone numbers must be in XX-XXXX-XXXX format." +msgstr "El número de telèfon ha de ser en el format XX-XXXX-XXXX." + +#: contrib/localflavor/br/forms.py:72 +msgid "This field requires only numbers." +msgstr "Aquest camps requereix només números." + +#: contrib/localflavor/br/forms.py:74 +msgid "This field requires at most 11 digits or 14 characters." +msgstr "Aquest camp requereix com a màxim 11 dígits o 14 caracters." + +#: contrib/localflavor/br/forms.py:84 +msgid "Invalid CPF number." +msgstr "Número CPF invàlid." + +#: contrib/localflavor/br/forms.py:106 +msgid "This field requires at least 14 digits" +msgstr "Aquest camp requereix almenys 14 dígits." + +#: contrib/localflavor/br/forms.py:116 +msgid "Invalid CNPJ number." +msgstr "Número CNPJ invàlid." + +#: contrib/localflavor/cl/forms.py:21 +msgid "Enter valid a Chilean RUT. The format is XX.XXX.XXX-X." +msgstr "Introdueixi un RUT Xilè vàlid. El format és XX.XXX.XXX-X" + +#: contrib/localflavor/cl/forms.py:26 +msgid "Enter valid a Chilean RUT" +msgstr "Introdueixi un RUT Xilè vàlid." + +#: contrib/localflavor/no/forms.py:36 +msgid "Enter a valid Norwegian social security number." +msgstr "Introdueixi un número de la seguretat social Noruega vàlid." + +#: contrib/localflavor/uk/forms.py:18 +msgid "Enter a postcode. A space is required between the two postcode parts." +msgstr "" +"Introdueixi un codi postal. És necessari un espai entre les dues parts del " +"codi postal." + +#: contrib/sessions/models.py:68 +msgid "session key" +msgstr "clau de la sessió" + +#: contrib/sessions/models.py:69 +msgid "session data" +msgstr "dades de la sessió" + +#: contrib/sessions/models.py:70 +msgid "expire date" +msgstr "data de caducitat" + +#: contrib/sessions/models.py:74 +msgid "session" +msgstr "sessió" + +#: contrib/sessions/models.py:75 +msgid "sessions" +msgstr "sessions" + +#: contrib/flatpages/models.py:8 msgid "" -"Looks like your browser isn't configured to accept cookies. Please enable " -"cookies, reload this page, and try again." +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." msgstr "" -"Sembla ser que el seu navegador no està configurat per acceptar " -"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta " -"pàgina i provi-ho de nou. " +"Exemple: '/about/contact/'. Asseguri's de posar les barres al principi i al " +"final." -#: contrib/admin/views/decorators.py:83 -msgid "Usernames cannot contain the '@' character." -msgstr "Els noms d'usuari no poden contenir el caracter '@'." +#: contrib/flatpages/models.py:9 +msgid "title" +msgstr "tìtol" -#: contrib/admin/views/decorators.py:85 +#: contrib/flatpages/models.py:10 +msgid "content" +msgstr "contingut" + +#: contrib/flatpages/models.py:11 +msgid "enable comments" +msgstr "habilitar comentaris" + +#: contrib/flatpages/models.py:12 +msgid "template name" +msgstr "nom de la plantilla" + +#: contrib/flatpages/models.py:13 +msgid "" +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." +msgstr "" +"Exemple: 'flatpages/contact_page.html'. Si no el proporciona, el sistema " +"utilitzarà 'flatpages/defaula.htmlt'." + +#: contrib/flatpages/models.py:14 +msgid "registration required" +msgstr "s'ha de estar registrat" + +#: contrib/flatpages/models.py:14 +msgid "If this is checked, only logged-in users will be able to view the page." +msgstr "Si està marcat, només els usuaris registrats podran veure la pàgina." + +#: contrib/flatpages/models.py:18 +msgid "flat page" +msgstr "pàgina estàtica" + +#: contrib/flatpages/models.py:19 +msgid "flat pages" +msgstr "pàgines estàtiques" + +#: contrib/humanize/templatetags/humanize.py:17 +msgid "th" +msgstr "" + +#: contrib/humanize/templatetags/humanize.py:17 +msgid "st" +msgstr "r" + +#: contrib/humanize/templatetags/humanize.py:17 +msgid "nd" +msgstr "n" + +#: contrib/humanize/templatetags/humanize.py:17 +msgid "rd" +msgstr "r" + +#: contrib/humanize/templatetags/humanize.py:47 #, python-format -msgid "Your e-mail address is not your username. Try '%s' instead." -msgstr "" -"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas." +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milió" +msgstr[1] "%(value).1f milions" -#: contrib/admin/views/doc.py:46 contrib/admin/views/doc.py:48 -#: contrib/admin/views/doc.py:50 -msgid "tag:" -msgstr "" - -#: contrib/admin/views/doc.py:77 contrib/admin/views/doc.py:79 -#: contrib/admin/views/doc.py:81 -msgid "filter:" -msgstr "" - -#: contrib/admin/views/doc.py:135 contrib/admin/views/doc.py:137 -#: contrib/admin/views/doc.py:139 -msgid "view:" -msgstr "" - -#: contrib/admin/views/doc.py:164 -#, fuzzy, python-format -msgid "App %r not found" -msgstr "No s'ha pogut trobar la pàgina" - -#: contrib/admin/views/doc.py:171 +#: contrib/humanize/templatetags/humanize.py:50 #, python-format -msgid "Model %r not found in app %r" -msgstr "" +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f bilió" +msgstr[1] "%(value).1f bilions" -#: contrib/admin/views/doc.py:183 +#: contrib/humanize/templatetags/humanize.py:53 #, python-format -msgid "the related `%s.%s` object" +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f trilió" +msgstr[1] "%(value).1f trilions" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "one" +msgstr "un" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "two" +msgstr "dos" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "three" +msgstr "tres" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "four" +msgstr "cuatre" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "five" +msgstr "cinc" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "six" +msgstr "sis" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "seven" +msgstr "set" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "eight" +msgstr "vuit" + +#: contrib/humanize/templatetags/humanize.py:68 +msgid "nine" +msgstr "nou" + +#: contrib/contenttypes/models.py:36 +msgid "python model class name" +msgstr "nom de la classe del model en python" + +#: contrib/contenttypes/models.py:39 +msgid "content type" +msgstr "tipus de contingut" + +#: contrib/contenttypes/models.py:40 +msgid "content types" +msgstr "tipus de continguts" + +#: contrib/sites/models.py:10 +msgid "domain name" +msgstr "nom del domini" + +#: contrib/sites/models.py:11 +msgid "display name" +msgstr "nom per mostrar" + +#: contrib/sites/models.py:15 +msgid "site" +msgstr "lloc" + +#: contrib/sites/models.py:16 +msgid "sites" +msgstr "llocs" + +#: contrib/auth/forms.py:17 contrib/auth/forms.py:138 +msgid "The two password fields didn't match." +msgstr "Els dos camps de contrasenya no coincideixen." + +#: contrib/auth/forms.py:25 +msgid "A user with that username already exists." +msgstr "Ja existeix un usuari amb aquest nom." + +#: contrib/auth/forms.py:53 +msgid "" +"Your Web browser doesn't appear to have cookies enabled. Cookies are " +"required for logging in." +msgstr "" +"El seu navegador no sembla tenir les 'cookies' (galetes) activades. Aquestes " +"són necessàries per iniciar la sessió." + +#: contrib/auth/forms.py:62 +msgid "This account is inactive." +msgstr "Aquest compte està inactiu" + +#: contrib/auth/forms.py:85 +msgid "" +"That e-mail address doesn't have an associated user account. Are you sure " +"you've registered?" msgstr "" -#: contrib/admin/views/doc.py:183 contrib/admin/views/doc.py:205 -#: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224 -msgid "model:" -msgstr "" +#: contrib/auth/forms.py:117 +msgid "The two 'new password' fields didn't match." +msgstr "Els dos camps de nova contrasenya no coincideixen." -#: contrib/admin/views/doc.py:214 +#: contrib/auth/forms.py:124 +msgid "Your old password was entered incorrectly. Please enter it again." +msgstr "" +"La seva antiga contrasenya no és correcte. Si el plau, introdueixi-la de nou." + +#: contrib/auth/views.py:40 +msgid "Logged out" +msgstr "Sessió finalitzada" + +#: contrib/auth/models.py:44 contrib/auth/models.py:64 +msgid "name" +msgstr "nom" + +#: contrib/auth/models.py:46 +msgid "codename" +msgstr "nom en clau" + +#: contrib/auth/models.py:49 +msgid "permission" +msgstr "permís" + +#: contrib/auth/models.py:50 contrib/auth/models.py:65 +msgid "permissions" +msgstr "permissos" + +#: contrib/auth/models.py:68 +msgid "group" +msgstr "grup" + +#: contrib/auth/models.py:69 contrib/auth/models.py:109 +msgid "groups" +msgstr "grups" + +#: contrib/auth/models.py:99 +msgid "username" +msgstr "nom d'usuari" + +#: contrib/auth/models.py:99 +msgid "" +"Required. 30 characters or fewer. Alphanumeric characters only (letters, " +"digits and underscores)." +msgstr "" +"Requerit. 30 o menys caracters. Només caracters alfanumèrics (lletres, " +"dígits i guions baixos)." + +#: contrib/auth/models.py:100 +msgid "first name" +msgstr "nom propi" + +#: contrib/auth/models.py:101 +msgid "last name" +msgstr "cognoms" + +#: contrib/auth/models.py:102 +msgid "e-mail address" +msgstr "adreça de correu electrònic" + +#: contrib/auth/models.py:103 +msgid "password" +msgstr "contrasenya" + +#: contrib/auth/models.py:103 +msgid "" +"Use '[algo]$[salt]$[hexdigest]' or use the change " +"password form." +msgstr "" +"Utilitzi '[algo]$[salt]$[hexdigest]' o el formulari de " +"canvi de contrasenya." + +#: contrib/auth/models.py:104 +msgid "staff status" +msgstr "és membre del personal" + +#: contrib/auth/models.py:104 +msgid "Designates whether the user can log into this admin site." +msgstr "Indica si l'usuari pot entrar en el lloc administratiu." + +#: contrib/auth/models.py:105 +msgid "active" +msgstr "actiu" + +#: contrib/auth/models.py:105 +msgid "" +"Designates whether this user can log into the Django admin. Unselect this " +"instead of deleting accounts." +msgstr "" +"Designa si aquest usuari pot iniciar sessió a la interfície administrativa " +"Djano. Deselecciona-ho enlloc de esborrar comptes d'usuari." + +#: contrib/auth/models.py:106 +msgid "superuser status" +msgstr "estat de superusuari" + +#: contrib/auth/models.py:106 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" +"Designa que aquest usuari té tots els permisos sense assignar-los " +"explícitament." + +#: contrib/auth/models.py:107 +msgid "last login" +msgstr "últim inici de sessió" + +#: contrib/auth/models.py:108 +msgid "date joined" +msgstr "data de creació" + +#: contrib/auth/models.py:110 +msgid "" +"In addition to the permissions manually assigned, this user will also get " +"all permissions granted to each group he/she is in." +msgstr "" +"Junt amb els permissos asignats manualment, aquest usuari tindrà, també, els " +"permissos dels grups dels que sigui membre." + +#: contrib/auth/models.py:111 +msgid "user permissions" +msgstr "permissos de l'usuari" + +#: contrib/auth/models.py:115 +msgid "user" +msgstr "usuari" + +#: contrib/auth/models.py:116 +msgid "users" +msgstr "usuaris" + +#: contrib/auth/models.py:122 +msgid "Personal info" +msgstr "Informaciò personal" + +#: contrib/auth/models.py:123 +msgid "Permissions" +msgstr "permissos" + +#: contrib/auth/models.py:124 +msgid "Important dates" +msgstr "Dates importants" + +#: contrib/auth/models.py:125 +msgid "Groups" +msgstr "Grups" + +#: contrib/auth/models.py:269 +msgid "message" +msgstr "missatge" + +#: newforms/models.py:173 newforms/fields.py:366 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles." + +#: newforms/models.py:186 newforms/fields.py:82 newforms/fields.py:378 +#: newforms/fields.py:454 newforms/fields.py:465 +#: db/models/fields/__init__.py:117 db/models/fields/__init__.py:274 +#: db/models/fields/__init__.py:612 db/models/fields/__init__.py:623 +#: oldforms/__init__.py:357 +msgid "This field is required." +msgstr "Aquest camp és obligatori." + +#: newforms/models.py:190 newforms/fields.py:382 newforms/fields.py:458 +msgid "Enter a list of values." +msgstr "Introdueixi una llista de valors." + +#: newforms/models.py:196 newforms/fields.py:391 #, python-format -msgid "related `%s.%s` objects" -msgstr "" +msgid "Select a valid choice. %s is not one of the available choices." +msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides." -#: contrib/admin/views/doc.py:219 +#: newforms/fields.py:105 newforms/fields.py:258 #, python-format -msgid "all %s" -msgstr "" - -#: contrib/admin/views/doc.py:224 -#, python-format -msgid "number of %s" -msgstr "" - -#: contrib/admin/views/doc.py:229 -#, python-format -msgid "Fields on %s objects" -msgstr "" - -#: contrib/admin/views/doc.py:291 contrib/admin/views/doc.py:301 -#: contrib/admin/views/doc.py:303 contrib/admin/views/doc.py:309 -#: contrib/admin/views/doc.py:310 contrib/admin/views/doc.py:312 -msgid "Integer" -msgstr "Enter" - -#: contrib/admin/views/doc.py:292 -msgid "Boolean (Either True or False)" -msgstr "Booleà (Verdader o Fals)" - -#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:311 -#, python-format -msgid "String (up to %(maxlength)s)" -msgstr "Cadena (fins a %(maxlength)s)" - -#: contrib/admin/views/doc.py:294 -msgid "Comma-separated integers" -msgstr "Enters separats per comes" - -#: contrib/admin/views/doc.py:295 -msgid "Date (without time)" -msgstr "Data (sense hora)" - -#: contrib/admin/views/doc.py:296 -msgid "Date (with time)" -msgstr "Data (amb hora)" - -#: contrib/admin/views/doc.py:297 -msgid "E-mail address" -msgstr "Adreça de correu electrònic" - -#: contrib/admin/views/doc.py:298 contrib/admin/views/doc.py:299 -#: contrib/admin/views/doc.py:302 -msgid "File path" -msgstr "Ruta del fitxer" - -#: contrib/admin/views/doc.py:300 -msgid "Decimal number" -msgstr "Número decimal" - -#: contrib/admin/views/doc.py:306 -msgid "Boolean (Either True, False or None)" -msgstr "Booleà (Verdader, Fals o 'None' (cap))" - -#: contrib/admin/views/doc.py:307 -msgid "Relation to parent model" -msgstr "Relació amb el model pare" - -#: contrib/admin/views/doc.py:308 -msgid "Phone number" -msgstr "Número de telèfon" - -#: contrib/admin/views/doc.py:313 -msgid "Text" -msgstr "Texte" - -#: contrib/admin/views/doc.py:314 -msgid "Time" -msgstr "Hora" - -#: contrib/admin/views/doc.py:316 -msgid "U.S. state (two uppercase letters)" -msgstr "Estat dels E.U.A. (dos lletres majúscules)" - -#: contrib/admin/views/doc.py:317 -msgid "XML text" -msgstr "Texte XML" - -#: contrib/admin/views/doc.py:343 -#, python-format -msgid "%s does not appear to be a urlpattern object" -msgstr "" - -#: contrib/admin/views/auth.py:30 -#, fuzzy -msgid "Add user" -msgstr "Agregar %s" - -#: contrib/admin/views/auth.py:57 -#, fuzzy -msgid "Password changed successfully." -msgstr "Canvi de clau exitò" - -#: contrib/admin/views/auth.py:64 -#, fuzzy, python-format -msgid "Change password: %s" -msgstr "Canviar clau" - -#: newforms/fields.py:101 newforms/fields.py:254 -#, fuzzy, python-format msgid "Ensure this value has at most %d characters." -msgstr "Aseguris de que el seu texte té menys de %s caracter." +msgstr "Asseguris de que el seu texte té com a màxim %d caracters." -#: newforms/fields.py:103 newforms/fields.py:256 -#, fuzzy, python-format +#: newforms/fields.py:107 newforms/fields.py:260 +#, python-format msgid "Ensure this value has at least %d characters." -msgstr "Aseguris de que el seu texte té menys de %s caracter." +msgstr "Asseguris de que el seu texte té almenys %d caracters." -#: newforms/fields.py:126 core/validators.py:120 +#: newforms/fields.py:130 core/validators.py:120 msgid "Enter a whole number." -msgstr "Introdueixi un número senser." +msgstr "Introdueixi un número sencer." -#: newforms/fields.py:128 -#, fuzzy, python-format +#: newforms/fields.py:132 +#, python-format msgid "Ensure this value is less than or equal to %s." -msgstr "Aquest valor ha de ser una potència de %s." +msgstr "Aquest valor ha de ser menor o igual a %s." -#: newforms/fields.py:130 +#: newforms/fields.py:134 #, python-format msgid "Ensure this value is greater than or equal to %s." -msgstr "" +msgstr "Asseguris de que aquest valor sigui superior o igual a %s." -#: newforms/fields.py:163 -#, fuzzy +#: newforms/fields.py:167 msgid "Enter a valid date." -msgstr "Introdueixi un nom de fitxer vàlid." +msgstr "Introdueixi una data vàlida." -#: newforms/fields.py:190 -#, fuzzy +#: newforms/fields.py:194 msgid "Enter a valid time." -msgstr "Introdueixi un nom de fitxer vàlid." +msgstr "Introdueixi una hora vàlida." -#: newforms/fields.py:226 -#, fuzzy +#: newforms/fields.py:230 msgid "Enter a valid date/time." -msgstr "Introdueixi un nom de fitxer vàlid." +msgstr "Introdueixi una data/hora vàlides." -#: newforms/fields.py:240 -#, fuzzy +#: newforms/fields.py:244 msgid "Enter a valid value." -msgstr "Introdueixi un nom de fitxer vàlid." +msgstr "Introdueixi un valor vàlid." -#: newforms/fields.py:269 core/validators.py:161 +#: newforms/fields.py:273 core/validators.py:162 msgid "Enter a valid e-mail address." -msgstr "Introdueixi una adreça de correu vàlida." +msgstr "Introdueixi una adreça de correu vàlida." -#: newforms/fields.py:287 newforms/fields.py:309 -#, fuzzy +#: newforms/fields.py:291 newforms/fields.py:313 msgid "Enter a valid URL." -msgstr "Introdueixi un nom de fitxer vàlid." +msgstr "Introdueixi una URL vàlida." -#: newforms/fields.py:311 -#, fuzzy +#: newforms/fields.py:315 msgid "This URL appears to be a broken link." -msgstr "La URL %sés un enllaç trencat." +msgstr "Aquesta URL sembla ser un enllaç trencat." -#: newforms/fields.py:359 -#, fuzzy -msgid "Select a valid choice. That choice is not one of the available choices." -msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s." +#: db/models/manipulators.py:307 +#, python-format +msgid "%(object)s with this %(type)s already exists for the given %(field)s." +msgstr "Ja existeix un %(object)s del tipus %(type)s amb aquest %(field)s." -#: newforms/fields.py:377 newforms/fields.py:453 -#, fuzzy -msgid "Enter a list of values." -msgstr "Introdueixi un nom de fitxer vàlid." +#: db/models/fields/related.py:53 +#, python-format +msgid "Please enter a valid %s." +msgstr "Si us plau, introdueixi un %s vàlid." -#: newforms/fields.py:386 -#, fuzzy, python-format -msgid "Select a valid choice. %s is not one of the available choices." -msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s." +#: db/models/fields/related.py:642 +msgid "Separate multiple IDs with commas." +msgstr "Separi múltiples IDs amb comes." -#: template/defaultfilters.py:436 -msgid "yes,no,maybe" -msgstr "si,no,potser" +#: db/models/fields/related.py:644 +msgid "" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "Premi \"Control\" o \"Command\" en un Mac per escollir més d'un." + +#: db/models/fields/related.py:691 +#, python-format +msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." +msgid_plural "" +"Please enter valid %(self)s IDs. The values %(value)r are invalid." +msgstr[0] "" +"Si us plau, introdueixi IDs de %(self)s vàlids. El valor %(value)r és " +"invàlid." +msgstr[1] "" +"Si us plau, introdueixi IDs de %(self)s vàlids. Els valors %(value)r són " +"invàlids." + +#: db/models/fields/__init__.py:42 +#, python-format +msgid "%(optname)s with this %(fieldname)s already exists." +msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s." + +#: db/models/fields/__init__.py:369 +msgid "This value must be an integer." +msgstr "Aquest valor ha de ser un enter." + +#: db/models/fields/__init__.py:404 +msgid "This value must be either True or False." +msgstr "Aquest valor ha de ser True (Veritat) o False (Fals)" + +#: db/models/fields/__init__.py:425 +msgid "This field cannot be null." +msgstr "Aquest camp no pot ser null (estar buit)." + +#: db/models/fields/__init__.py:459 core/validators.py:148 +msgid "Enter a valid date in YYYY-MM-DD format." +msgstr "Introdueixi una data vàlida en el forma AAAA-MM-DD." + +#: db/models/fields/__init__.py:528 core/validators.py:157 +msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." +msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM." + +#: db/models/fields/__init__.py:632 +msgid "Enter a valid filename." +msgstr "Introdueixi un nom de fitxer vàlid." + +#: db/models/fields/__init__.py:753 +msgid "This value must be either None, True or False." +msgstr "Aquest valor ha de ser None (Cap), True (Veritat) o False (Fals)" #: views/generic/create_update.py:43 -#, fuzzy, python-format +#, python-format msgid "The %(verbose_name)s was created successfully." -msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s." +msgstr "El/La %(verbose_name)s s'ha creat amb èxit." #: views/generic/create_update.py:117 -#, fuzzy, python-format +#, python-format msgid "The %(verbose_name)s was updated successfully." -msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit." +msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit." #: views/generic/create_update.py:184 -#, fuzzy, python-format +#, python-format msgid "The %(verbose_name)s was deleted." -msgstr "L'equip de %(site_name)s" +msgstr "El %(verbose_name)s s'ha eliminat." + +#: oldforms/__init__.py:392 +#, python-format +msgid "Ensure your text is less than %s character." +msgid_plural "Ensure your text is less than %s characters." +msgstr[0] "Asseguris de que el seu texte té menys de %s caracter." +msgstr[1] "Asseguris de que el seu texte té menys de %s caracters." + +#: oldforms/__init__.py:397 +msgid "Line breaks are not allowed here." +msgstr "No es permeten salts de línia." + +#: oldforms/__init__.py:498 oldforms/__init__.py:571 oldforms/__init__.py:610 +#, python-format +msgid "Select a valid choice; '%(data)s' is not in %(choices)s." +msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s." + +#: oldforms/__init__.py:672 core/validators.py:174 core/validators.py:445 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari." + +#: oldforms/__init__.py:674 +msgid "The submitted file is empty." +msgstr "El fitxer enviat està buit." + +#: oldforms/__init__.py:730 +msgid "Enter a whole number between -32,768 and 32,767." +msgstr "Introdueixi un número enter entre -32,768 i 32,767." + +#: oldforms/__init__.py:740 +msgid "Enter a positive number." +msgstr "Introdueixi un número positiu." + +#: oldforms/__init__.py:750 +msgid "Enter a whole number between 0 and 32,767." +msgstr "Introdueixi un número entre 0 i 32,767." + +#: conf/global_settings.py:39 +msgid "Arabic" +msgstr "Arabic" + +#: conf/global_settings.py:40 +msgid "Bengali" +msgstr "Bengalí" + +#: conf/global_settings.py:41 +msgid "Bulgarian" +msgstr "Bulgar" + +#: conf/global_settings.py:42 +msgid "Catalan" +msgstr "Català" + +#: conf/global_settings.py:43 +msgid "Czech" +msgstr "Txec" + +#: conf/global_settings.py:44 +msgid "Welsh" +msgstr "Galès" + +#: conf/global_settings.py:45 +msgid "Danish" +msgstr "Danès" + +#: conf/global_settings.py:46 +msgid "German" +msgstr "Alemany" + +#: conf/global_settings.py:47 +msgid "Greek" +msgstr "Grec" + +#: conf/global_settings.py:48 +msgid "English" +msgstr "Anglès" + +#: conf/global_settings.py:49 +msgid "Spanish" +msgstr "Espanyol" + +#: conf/global_settings.py:50 +msgid "Argentinean Spanish" +msgstr "Castellà Argentí" + +#: conf/global_settings.py:51 +msgid "Finnish" +msgstr "Finlandès" + +#: conf/global_settings.py:52 +msgid "French" +msgstr "Francès" + +#: conf/global_settings.py:53 +msgid "Galician" +msgstr "Galleg" + +#: conf/global_settings.py:54 +msgid "Hungarian" +msgstr "Húngar" + +#: conf/global_settings.py:55 +msgid "Hebrew" +msgstr "Hebreu" + +#: conf/global_settings.py:56 +msgid "Icelandic" +msgstr "Islandès" + +#: conf/global_settings.py:57 +msgid "Italian" +msgstr "Italià" + +#: conf/global_settings.py:58 +msgid "Japanese" +msgstr "Japonès" + +#: conf/global_settings.py:59 +msgid "Korean" +msgstr "Coreà" + +#: conf/global_settings.py:60 +msgid "Kannada" +msgstr "" + +#: conf/global_settings.py:61 +msgid "Latvian" +msgstr "" + +#: conf/global_settings.py:62 +msgid "Macedonian" +msgstr "Macedoni" + +#: conf/global_settings.py:63 +msgid "Dutch" +msgstr "Holandès" + +#: conf/global_settings.py:64 +msgid "Norwegian" +msgstr "Norueg" + +#: conf/global_settings.py:65 +msgid "Polish" +msgstr "Polac" + +#: conf/global_settings.py:66 +msgid "Portugese" +msgstr "Portuguès" + +#: conf/global_settings.py:67 +msgid "Brazilian" +msgstr "Brasileny" + +#: conf/global_settings.py:68 +msgid "Romanian" +msgstr "Rumanès" + +#: conf/global_settings.py:69 +msgid "Russian" +msgstr "Rús" + +#: conf/global_settings.py:70 +msgid "Slovak" +msgstr "Eslovac" + +#: conf/global_settings.py:71 +msgid "Slovenian" +msgstr "Esloveni" + +#: conf/global_settings.py:72 +msgid "Serbian" +msgstr "Serbi" + +#: conf/global_settings.py:73 +msgid "Swedish" +msgstr "Suec" + +#: conf/global_settings.py:74 +msgid "Tamil" +msgstr "" + +#: conf/global_settings.py:75 +msgid "Telugu" +msgstr "" + +#: conf/global_settings.py:76 +msgid "Turkish" +msgstr "Turc" + +#: conf/global_settings.py:77 +msgid "Ukrainian" +msgstr "Ucranià" + +#: conf/global_settings.py:78 +msgid "Simplified Chinese" +msgstr "Xinés simplificat" + +#: conf/global_settings.py:79 +msgid "Traditional Chinese" +msgstr "Xinés tradicional" #: core/validators.py:64 msgid "This value must contain only letters, numbers and underscores." -msgstr "Aquest valor ha de contenir només números, guions, i guions baixos." +msgstr "Aquest valor ha de contenir només números, guions, i guions baixos." #: core/validators.py:68 msgid "" "This value must contain only letters, numbers, underscores, dashes or " "slashes." msgstr "" -"Aquest valor ha de contenir només lletres, números, guions, guions baixos, i " +"Aquest valor ha de contenir només lletres, números, guions, guions baixos, i " "barres (/)." #: core/validators.py:72 -#, fuzzy msgid "This value must contain only letters, numbers, underscores or hyphens." msgstr "" -"Aquest valor ha de contenir només lletres, números, guions, guions baixos, i " -"barres (/)." +"Aquest valor ha de contenir només lletres, números, guions o guions baixos" #: core/validators.py:76 msgid "Uppercase letters are not allowed here." -msgstr "No es permeten majúscules aquí." +msgstr "No es permeten majúscules aquí." #: core/validators.py:80 msgid "Lowercase letters are not allowed here." -msgstr "No es permeten minúscules aquí." +msgstr "No es permeten minúscules aquí." #: core/validators.py:87 msgid "Enter only digits separated by commas." -msgstr "Introdueixi només dígits separats per comes." +msgstr "Introdueixi només dígits separats per comes." #: core/validators.py:99 msgid "Enter valid e-mail addresses separated by commas." -msgstr "Introdueixi adreces de correu electrònic vàlides separades per comes." +msgstr "Introdueixi adreces de correu electrònic vàlides separades per comes." #: core/validators.py:103 msgid "Please enter a valid IP address." -msgstr "Per favor introdueixi una adreça IP vàlida." +msgstr "Per favor introdueixi una adreça IP vàlida." #: core/validators.py:107 msgid "Empty values are not allowed here." @@ -2120,264 +2739,260 @@ msgstr "No s'admeten valor buits." #: core/validators.py:111 msgid "Non-numeric characters aren't allowed here." -msgstr "No s'admeten caracters no numèrics." +msgstr "No s'admeten caracters no numèrics." #: core/validators.py:115 msgid "This value can't be comprised solely of digits." -msgstr "Aquest valor no pot contenir només dígits." +msgstr "Aquest valor no pot contenir només dígits." #: core/validators.py:124 msgid "Only alphabetical characters are allowed here." -msgstr "Només s'admeted caracters alfabètics aquí." +msgstr "Només s'admeted caracters alfabètics aquí." #: core/validators.py:139 msgid "Year must be 1900 or later." -msgstr "" +msgstr "L'any ha de ser posterior al 1900" #: core/validators.py:143 -#, fuzzy, python-format -msgid "Invalid date: %s." -msgstr "URL invalida: %s" +#, python-format +msgid "Invalid date: %s" +msgstr "Data invàlida: %s" -#: core/validators.py:152 +#: core/validators.py:153 msgid "Enter a valid time in HH:MM format." -msgstr "Introdueixi una hora vàlida en el format HH:MM." +msgstr "Introdueixi una hora vàlida en el format HH:MM." -#: core/validators.py:177 +#: core/validators.py:178 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." msgstr "" -"Envii una imatge vàilda. El fitxer que ha enviat no era una imatge o estaba " +"Envii una imatge vàilda. El fitxer que ha enviat no era una imatge o estaba " "corrupte." -#: core/validators.py:184 +#: core/validators.py:185 #, python-format msgid "The URL %s does not point to a valid image." -msgstr "La URL %s no apunta una imatge vàlida." +msgstr "La URL %s no apunta una imatge vàlida." -#: core/validators.py:188 +#: core/validators.py:189 #, python-format msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid." msgstr "" -"El números de telèfon han de guardar-se en el format XXX-XXX-XXXX. \"%s\" no " -"és vàlid." +"El números de telèfon han de guardar-se en el format XXX-XXX-XXXX. \"%s\" no " +"és vàlid." -#: core/validators.py:196 +#: core/validators.py:197 #, python-format msgid "The URL %s does not point to a valid QuickTime video." -msgstr "La URL %s no apunta a un video QuickTime vàlid." +msgstr "La URL %s no apunta a un video QuickTime vàlid." -#: core/validators.py:200 +#: core/validators.py:201 msgid "A valid URL is required." -msgstr "Es precisa d'una URL vàlida." +msgstr "Es precisa d'una URL vàlida." -#: core/validators.py:214 +#: core/validators.py:215 #, python-format msgid "" "Valid HTML is required. Specific errors are:\n" "%s" msgstr "" -"Es precisa HTML vàlid. Els errors específics sòn:\n" +"Es precisa HTML vàlid. Els errors específics sòn:\n" "%s" -#: core/validators.py:221 +#: core/validators.py:222 #, python-format msgid "Badly formed XML: %s" msgstr "XML incorrectament formatejat: %s" -#: core/validators.py:238 +#: core/validators.py:239 #, python-format msgid "Invalid URL: %s" msgstr "URL invalida: %s" -#: core/validators.py:243 core/validators.py:245 +#: core/validators.py:244 core/validators.py:246 #, python-format msgid "The URL %s is a broken link." -msgstr "La URL %sés un enllaç trencat." +msgstr "La URL %sés un enllaç trencat." -#: core/validators.py:251 +#: core/validators.py:252 msgid "Enter a valid U.S. state abbreviation." -msgstr "Introdueixi una abreviatura vàlida d'estat d'els E.U.A.." +msgstr "Introdueixi una abreviatura vàlida d'estat d'els E.U.A.." -#: core/validators.py:265 +#: core/validators.py:266 #, python-format msgid "Watch your mouth! The word %s is not allowed here." msgid_plural "Watch your mouth! The words %s are not allowed here." -msgstr[0] "Vigili la seva boca! Aquí no admetem la paraula: %s." -msgstr[1] "Vigili la seva boca! Aquí no admetem les paraules: %s." +msgstr[0] "Vigili la seva boca! Aquí no admetem la paraula: %s." +msgstr[1] "Vigili la seva boca! Aquí no admetem les paraules: %s." -#: core/validators.py:272 +#: core/validators.py:273 #, python-format msgid "This field must match the '%s' field." msgstr "Aquest camp ha de concordar amb el camp '%s'." -#: core/validators.py:291 +#: core/validators.py:292 msgid "Please enter something for at least one field." msgstr "Si us plau, introdueixi alguna cosa alemnys en un camp." -#: core/validators.py:300 core/validators.py:311 +#: core/validators.py:301 core/validators.py:312 msgid "Please enter both fields or leave them both empty." msgstr "Si us plau, ompli els dos camps o deixi'ls tots dos en blanc." -#: core/validators.py:318 +#: core/validators.py:320 #, python-format msgid "This field must be given if %(field)s is %(value)s" -msgstr "S'ha de proporcionar aquest camps si %(field)s és %(value)s" +msgstr "S'ha de proporcionar aquest camps si %(field)s és %(value)s" -#: core/validators.py:330 +#: core/validators.py:333 #, python-format msgid "This field must be given if %(field)s is not %(value)s" -msgstr "S'ha de proporcionar aquest camps si %(field)s no és %(value)s" +msgstr "S'ha de proporcionar aquest camps si %(field)s no és %(value)s" -#: core/validators.py:349 +#: core/validators.py:352 msgid "Duplicate values are not allowed." msgstr "No s'admeten valors duplicats." -#: core/validators.py:364 -#, fuzzy, python-format -msgid "This value must be between %s and %s." -msgstr "Aquest valor ha de ser una potència de %s." +#: core/validators.py:367 +#, python-format +msgid "This value must be between %(lower)s and %(upper)s." +msgstr "Aquest valor ha de estar comprés entre %(lower)s i %(upper)s." -#: core/validators.py:366 -#, fuzzy, python-format +#: core/validators.py:369 +#, python-format msgid "This value must be at least %s." -msgstr "Aquest valor ha de ser una potència de %s." +msgstr "Aquest valor ha de ser com a mínim %s." -#: core/validators.py:368 -#, fuzzy, python-format +#: core/validators.py:371 +#, python-format msgid "This value must be no more than %s." -msgstr "Aquest valor ha de ser una potència de %s." +msgstr "Aquest valor ha de ser com a màxim %s." -#: core/validators.py:404 +#: core/validators.py:407 #, python-format msgid "This value must be a power of %s." -msgstr "Aquest valor ha de ser una potència de %s." +msgstr "Aquest valor ha de ser una potència de %s." -#: core/validators.py:415 +#: core/validators.py:418 msgid "Please enter a valid decimal number." -msgstr "Si us plau, introdueixi un número decimal vàlid." +msgstr "Si us plau, introdueixi un número decimal vàlid." -#: core/validators.py:419 +#: core/validators.py:422 #, python-format msgid "Please enter a valid decimal number with at most %s total digit." msgid_plural "" "Please enter a valid decimal number with at most %s total digits." msgstr[0] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digit." +"Si us plau, introdueixi un número decimal vàlid amb no més de %s digit." msgstr[1] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digits." +"Si us plau, introdueixi un número decimal vàlid amb no més de %s digits." -#: core/validators.py:422 -#, fuzzy, python-format +#: core/validators.py:425 +#, python-format msgid "" "Please enter a valid decimal number with a whole part of at most %s digit." msgid_plural "" "Please enter a valid decimal number with a whole part of at most %s digits." msgstr[0] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digit." +"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a " +"màxim %s dígit." msgstr[1] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digits." +"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a " +"màxim %s dígits." -#: core/validators.py:425 +#: core/validators.py:428 #, python-format msgid "Please enter a valid decimal number with at most %s decimal place." msgid_plural "" "Please enter a valid decimal number with at most %s decimal places." msgstr[0] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digit " -"decimal." +"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígit en la " +"part decimal." msgstr[1] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digits " -"decimals." +"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígits en " +"la part decimal." -#: core/validators.py:435 +#: core/validators.py:438 #, python-format msgid "Make sure your uploaded file is at least %s bytes big." -msgstr "Asseguris de que el fitxer que ha enviat té, com a mínim, %s bytes." +msgstr "Asseguris de que el fitxer que ha enviat té, com a mínim, %s bytes." -#: core/validators.py:436 +#: core/validators.py:439 #, python-format msgid "Make sure your uploaded file is at most %s bytes big." -msgstr "Asseguris de que el fitxer que ha enviat té, com a màxim %s bytes." +msgstr "Asseguris de que el fitxer que ha enviat té, com a màxim %s bytes." -#: core/validators.py:453 +#: core/validators.py:456 msgid "The format for this field is wrong." -msgstr "El format per aquest camp és incorrecte." +msgstr "El format per aquest camp és incorrecte." -#: core/validators.py:468 +#: core/validators.py:471 msgid "This field is invalid." -msgstr "El camp no és vàlid." +msgstr "El camp no és vàlid." -#: core/validators.py:504 +#: core/validators.py:507 #, python-format msgid "Could not retrieve anything from %s." msgstr "No s'ha pogut obtenir res de %s." -#: core/validators.py:507 +#: core/validators.py:510 #, python-format msgid "" "The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'." msgstr "" "La URL %(url)s ha va tornar la capcelera Content-Type '%(contenttype)s', que " -"no és vàlida." +"no és vàlida." -#: core/validators.py:540 +#: core/validators.py:543 #, python-format msgid "" "Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with " "\"%(start)s\".)" msgstr "" -"Si us plau, tanqui l'etiqueta %(tag)s desde la linea %(line)s. (La linea " -"comença amb \"%(start)s\".)" +"Si us plau, tanqui l'etiqueta %(tag)s des de la línia %(line)s. (La línia " +"comença amb \"%(start)s\".)" -#: core/validators.py:544 +#: core/validators.py:547 #, python-format msgid "" "Some text starting on line %(line)s is not allowed in that context. (Line " "starts with \"%(start)s\".)" msgstr "" -"Part del text que comença en la linea %(line)s no està permés en aquest " -"contexte. (La linea comença per \"%(start)s\".)" +"Part del text que comença en la línia %(line)s no està permès en aquest " +"context. (La línia comença per \"%(start)s\".)" -#: core/validators.py:549 +#: core/validators.py:552 #, python-format msgid "" "\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%" "(start)s\".)" msgstr "" -"El \"%(attr)s\" de la linea %(line)s no és un atribut vàlido. (La linea " -"comença per \"%(start)s\".)" +"El \"%(attr)s\" de la línia %(line)s no és un atribut vàlid. (La línia " +"comença per \"%(start)s\".)" -#: core/validators.py:554 +#: core/validators.py:557 #, python-format msgid "" "\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%" "(start)s\".)" msgstr "" -"La \"<%(tag)s>\" de la linea %(line)s no és una etiqueta vàlida. (La línea " -"comença per \"%(start)s\".)" +"La \"<%(tag)s>\" de la línia %(line)s no és una etiqueta vàlida. (La línia " +"comença per \"%(start)s\".)" -#: core/validators.py:558 +#: core/validators.py:561 #, python-format msgid "" "A tag on line %(line)s is missing one or more required attributes. (Line " "starts with \"%(start)s\".)" msgstr "" -"A una etiqueta de la linea %(line)s li falta un o més atributs requerits.(La " -"linea comença per \"%(start)s\".)" +"Una etiqueta de la línia %(line)s li falta un o més atributs requerits.(La " +"línia comença per \"%(start)s\".)" -#: core/validators.py:563 +#: core/validators.py:566 #, python-format msgid "" "The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line " "starts with \"%(start)s\".)" msgstr "" -"L'atribut \"%(attr)s\" de la linena %(line)s té un valor que no és vàlid. " -"(La linea comença per \"%(start)s\".)" - -#~ msgid "Have you forgotten your password?" -#~ msgstr "Ha oblidat la seva clau?" - -#~ msgid "Use '[algo]$[salt]$[hexdigest]'" -#~ msgstr "Utilitzi '[algo]$[salt]$[hexdigest]'" +"L'atribut \"%(attr)s\" de la línia %(line)s té un valor que no és vàlid. (La " +"línia comença per \"%(start)s\".)" diff --git a/django/conf/locale/ca/LC_MESSAGES/djangojs.mo b/django/conf/locale/ca/LC_MESSAGES/djangojs.mo index 412c2eb876..d12c8f8d7a 100644 Binary files a/django/conf/locale/ca/LC_MESSAGES/djangojs.mo and b/django/conf/locale/ca/LC_MESSAGES/djangojs.mo differ diff --git a/django/conf/locale/ca/LC_MESSAGES/djangojs.po b/django/conf/locale/ca/LC_MESSAGES/djangojs.po index 8903957b86..ed7231988b 100644 --- a/django/conf/locale/ca/LC_MESSAGES/djangojs.po +++ b/django/conf/locale/ca/LC_MESSAGES/djangojs.po @@ -3,20 +3,19 @@ # Copyright (C) # This file is distributed under the same license as the PACKAGE package. # -# Jorge Gajon , 2005. -# Marc Fargas , 2007. msgid "" msgstr "" "Project-Id-Version: djangojs\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-02-15 11:05+1100\n" -"PO-Revision-Date: 2007-01-19 10:30+0100\n" +"POT-Creation-Date: 2007-05-20 18:25+0200\n" +"PO-Revision-Date: 2007-05-20 18:24+0200\n" "Last-Translator: Marc Fargas \n" "Language-Team: \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: KBabel 1.11.4\n" +"X-Generator: VIM 7.0\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: contrib/admin/media/js/SelectFilter2.js:33 #, perl-format @@ -54,7 +53,7 @@ msgid "" "January February March April May June July August September October November " "December" msgstr "" -"Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre Desembre" +"Febrer Març Abril Maig Juny Juliol Agost Setembre Octubre Novembre Desembre" #: contrib/admin/media/js/dateparse.js:33 msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" @@ -92,7 +91,7 @@ msgstr "Migdia" #: contrib/admin/media/js/admin/DateTimeShortcuts.js:88 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:183 msgid "Cancel" -msgstr "Cancel·lar" +msgstr "Cancel·lar" #: contrib/admin/media/js/admin/DateTimeShortcuts.js:128 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:177 @@ -109,7 +108,7 @@ msgstr "Ahir" #: contrib/admin/media/js/admin/DateTimeShortcuts.js:179 msgid "Tomorrow" -msgstr "Demà" +msgstr "Demà" #: contrib/admin/media/js/admin/CollapsedFieldsets.js:34 #: contrib/admin/media/js/admin/CollapsedFieldsets.js:72 diff --git a/django/conf/locale/te/LC_MESSAGES/django.mo b/django/conf/locale/te/LC_MESSAGES/django.mo index 318ce76779..8823b2015c 100644 Binary files a/django/conf/locale/te/LC_MESSAGES/django.mo and b/django/conf/locale/te/LC_MESSAGES/django.mo differ diff --git a/django/conf/locale/te/LC_MESSAGES/django.po b/django/conf/locale/te/LC_MESSAGES/django.po index b2e450c0a7..248baf2249 100644 --- a/django/conf/locale/te/LC_MESSAGES/django.po +++ b/django/conf/locale/te/LC_MESSAGES/django.po @@ -2,19 +2,19 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # -# pavithran , 2007. msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2006-09-25 15:43+0200\n" -"PO-Revision-Date: 2007-02-28 18:35+0530\n" +"PO-Revision-Date: 2007-05-19 12:44+0530\n" "Last-Translator: pavithran \n" "Language-Team: Telugu \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: KBabel 1.11.4\n" +"Plural-Forms: nplurals=2; nplurals=n>1;" #: contrib/comments/models.py:67 contrib/comments/models.py:166 msgid "object ID" @@ -144,7 +144,7 @@ msgstr "à°•à°°à±à°® à°¸à±à°•ొరà±à°²à±" #: contrib/comments/models.py:242 #, python-format msgid "%(score)d rating by %(user)s" -msgstr "%(user) రేటింగà±" +msgstr "%(score)d à°•à°¿ %(user)s రేటింగà±" #: contrib/comments/models.py:258 #, python-format @@ -153,9 +153,9 @@ msgid "" "\n" "%(text)s" msgstr "" -"%(user)s చేత చేయబడà±à°¡ à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à±à°²à±" +"%(user)s చేత చేయబడà±à°¡ à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à±à°²à±:\n" "\n" -"%(text)à°²à±" +"%(text)s" #: contrib/comments/models.py:265 msgid "flag date" @@ -220,12 +220,12 @@ msgid_plural "" "\n" "%(text)s" msgstr[0] "" -"à°ˆ à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à± చేసిన యూజరౠ%(count)లౠకనà±à°¨ తకà±à°•à±à°µ సమరà±à°ªà°¿à°‚చాడౠ" +"à°ˆ à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à± చేసిన యూజరౠ%(count)s లౠకనà±à°¨ తకà±à°•à±à°µ సమరà±à°ªà°¿à°‚చాడౠ" "à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à±:\n" "\n" "%(text)s" msgstr[1] "" -"à°ˆ à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à± చేసిన యూజరౠ%(count)లౠకనà±à°¨ తకà±à°•à±à°µ సమరà±à°ªà°¿à°‚చాడà±" +"à°ˆ à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à± చేసిన యూజరౠ%(count)s లౠకనà±à°¨ తకà±à°•à±à°µ సమరà±à°ªà°¿à°‚చాడà±" "à°µà±à°¯à°¾à°–à±à°¯à°¾à°¨à°®à±à°²à±:\n" "\n" "%(text)s" @@ -343,7 +343,8 @@ msgstr "మీ పేరà±" msgid "" "

By %s:

\n" "
    \n" -msgstr "

    %s తో:

    \n" +msgstr "" +"

    %s తో:

    \n" "
      \n" #: contrib/admin/filterspecs.py:70 contrib/admin/filterspecs.py:88 @@ -513,17 +514,17 @@ msgstr "%s ని మారà±à°šà°‚ది" #: contrib/admin/views/main.py:473 #, python-format msgid "One or more %(fieldname)s in %(name)s: %(obj)s" -msgstr "à°’à°•à°Ÿà°¿ కాని ,à°…à°‚à°¤ à°•à°¨à±à°¨à°Žà°•à±à°•à±à°µ %(name)లౠలో %(fieldname)లౠ: %(obj)లౠ" +msgstr "à°’à°•à°Ÿà°¿ కాని ,à°…à°‚à°¤ à°•à°¨à±à°¨à°Žà°•à±à°•à±à°µ %(name)s లో %(fieldname)s : %(obj)s " #: contrib/admin/views/main.py:478 #, python-format msgid "One or more %(fieldname)s in %(name)s:" -msgstr "à°’à°•à°Ÿà°¿ కాని ,à°…à°‚à°¤ à°•à°¨à±à°¨à°Žà°•à±à°•à±à°µ %(name)లౠలో %(fieldname)à°²à±" +msgstr "à°’à°•à°Ÿà°¿ కాని ,à°…à°‚à°¤ à°•à°¨à±à°¨à°Žà°•à±à°•à±à°µ %(name)s లో %(fieldname)s" #: contrib/admin/views/main.py:511 #, python-format msgid "The %(name)s \"%(obj)s\" was deleted successfully." -msgstr "%(name)లౠ\"%(obj)s\"జయపà±à°°à°¦à°‚à°—à°¾ తీసివేయబడà±à°¡à°¡à°¿" +msgstr "%(name)s \"%(obj)s\"జయపà±à°°à°¦à°‚à°—à°¾ తీసివేయబడà±à°¡à°¡à°¿" #: contrib/admin/views/main.py:514 msgid "Are you sure?" @@ -532,7 +533,7 @@ msgstr "మీరౠకచà±à°šà°¿à°¤à°‚à°—à°¾ ఉనà±à°¨à°¾à°°à°¾?" #: contrib/admin/views/main.py:536 #, python-format msgid "Change history: %s" -msgstr "మారà±à°šà°¬à°¡à°¿à°¨ à°ªà±à°°à°¾à°£à°®à±" +msgstr "మారà±à°šà°¬à°¡à°¿à°¨ à°ªà±à°°à°¾à°£à°®à±: %s" #: contrib/admin/views/main.py:570 #, python-format @@ -796,12 +797,12 @@ msgstr "à°•à±à°·à°®à°¿à°‚à°šà°‚à°¡à°¿ మీరౠకోరిన పేజి #: contrib/admin/templates/admin/index.html:17 #, python-format msgid "Models available in the %(name)s application." -msgstr "మొడలౠలౠ%(name)లో దొరికే à°…à°ªà±à°ªà±à°²à°¿à°•ేషనà±" +msgstr "మొడలౠలౠ%(name)s లో దొరికే à°…à°ªà±à°ªà±à°²à°¿à°•ేషనà±" #: contrib/admin/templates/admin/index.html:18 #, python-format msgid "%(name)s" -msgstr "%(name)à°²à±" +msgstr "%(name)s" #: contrib/admin/templates/admin/index.html:28 #: contrib/admin/templates/admin/change_form.html:15 @@ -831,7 +832,7 @@ msgstr "à°à°®à°¿ దొరకలేదà±" #: contrib/admin/templates/admin/change_list.html:11 #, python-format msgid "Add %(name)s" -msgstr "%(name)లౠజత చేయà±" +msgstr "%(name)s జత చేయà±" #: contrib/admin/templates/admin/login.html:22 msgid "Have you forgotten your password?" @@ -1794,7 +1795,7 @@ msgstr "సంవతà±à°¸à°°à°®à± 1900 లేక దాని తరà±à°µà°¾ #: core/validators.py:142 #, python-format msgid "Invalid date: %s." -msgstr "సరికాని తారీఖà±" +msgstr "సరికాని తారీఖౠ: %s." #: core/validators.py:146 db/models/fields/__init__.py:415 msgid "Enter a valid date in YYYY-MM-DD format." @@ -1846,8 +1847,10 @@ msgstr "సరైన URL కావాలి" msgid "" "Valid HTML is required. Specific errors are:\n" "%s" -msgstr "సరైన HTML ఇవà±à°µà°‚à°¡à°¿ .à°ªà±à°°à°¤à±à°¯à±‡à°•మైన తపà±à°ªà±à°²à± :\n" +msgstr "" +"సరైన HTML ఇవà±à°µà°‚à°¡à°¿ .à°ªà±à°°à°¤à±à°¯à±‡à°•మైన తపà±à°ªà±à°²à± :\n" "%s" + #: core/validators.py:220 #, python-format msgid "Badly formed XML: %s" @@ -1856,7 +1859,7 @@ msgstr "" #: core/validators.py:230 #, python-format msgid "Invalid URL: %s" -msgstr "" +msgstr "సరికాని URL: %s" #: core/validators.py:234 core/validators.py:236 #, python-format @@ -2004,27 +2007,27 @@ msgstr "" #: views/generic/create_update.py:43 #, python-format msgid "The %(verbose_name)s was created successfully." -msgstr "%(verbose_name)లౠజయపà±à°°à°¦à°‚à°—à°¾ తయారయింది" +msgstr "%(verbose_name)s జయపà±à°°à°¦à°‚à°—à°¾ తయారయింది" #: views/generic/create_update.py:117 #, python-format msgid "The %(verbose_name)s was updated successfully." -msgstr "%(verbose_name)లౠజయపà±à°°à°¦à°‚à°—à°¾ @@" +msgstr "%(verbose_name)s జయపà±à°°à°¦à°‚à°—à°¾ @@" #: views/generic/create_update.py:184 #, python-format msgid "The %(verbose_name)s was deleted." -msgstr "%(verbose_name)లౠతీసివేయబడినది" +msgstr "%(verbose_name)s తీసివేయబడినది" #: db/models/manipulators.py:302 #, python-format msgid "%(object)s with this %(type)s already exists for the given %(field)s." -msgstr "%(field)à°² లో %(object)తో %(type) ఉనà±à°¨à°¾à°¯à°¿" +msgstr "%(field)s లో %(object)s తో %(type)s ఉనà±à°¨à°¾à°¯à°¿" #: db/models/fields/__init__.py:40 #, python-format msgid "%(optname)s with this %(fieldname)s already exists." -msgstr "%(optname)లౠతో %(fieldname) à°®à±à°‚దే ఉనà±à°¨à°¾à°¯à°¿ ." +msgstr "%(optname)s తో %(fieldname)s à°®à±à°‚దే ఉనà±à°¨à°¾à°¯à°¿ ." #: db/models/fields/__init__.py:114 db/models/fields/__init__.py:265 #: db/models/fields/__init__.py:551 db/models/fields/__init__.py:562 @@ -2082,7 +2085,7 @@ msgstr "లైనౠబà±à°°à±‡à°•à±à°¸à± à°•à°¿ ఇకà±à°•à°¡ ఆన #: forms/__init__.py:487 forms/__init__.py:560 forms/__init__.py:599 #, python-format msgid "Select a valid choice; '%(data)s' is not in %(choices)s." -msgstr "సరైనది à°Žà°‚à°šà±à°•ోండి; %(choices) à°² లో '%(data)s' లేవౠ" +msgstr "సరైనది à°Žà°‚à°šà±à°•ోండి; %(choices)s లో '%(data)s' లేవౠ" #: forms/__init__.py:663 msgid "The submitted file is empty." diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index 8301f7ba5a..95be1b6231 100644 --- a/django/contrib/admin/templatetags/admin_list.py +++ b/django/contrib/admin/templatetags/admin_list.py @@ -72,6 +72,7 @@ def result_headers(cl): for i, field_name in enumerate(cl.list_display): try: f = lookup_opts.get_field(field_name) + admin_order_field = None except models.FieldDoesNotExist: # For non-field list_display values, check for the function # attribute "short_description". If that doesn't exist, fall @@ -86,7 +87,8 @@ def result_headers(cl): header = field_name.replace('_', ' ') # It is a non-field, but perhaps one that is sortable - if not getattr(getattr(cl.model, field_name), "admin_order_field", None): + admin_order_field = getattr(getattr(cl.model, field_name), "admin_order_field", None) + if not admin_order_field: yield {"text": header} continue @@ -101,7 +103,7 @@ def result_headers(cl): th_classes = [] new_order_type = 'asc' - if field_name == cl.order_field: + if field_name == cl.order_field or admin_order_field == cl.order_field: th_classes.append('sorted %sending' % cl.order_type.lower()) new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()] @@ -166,8 +168,8 @@ def items_for_result(cl, result): # Booleans are special: We use images. elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField): result_repr = _boolean_icon(field_val) - # FloatFields are special: Zero-pad the decimals. - elif isinstance(f, models.FloatField): + # DecimalFields are special: Zero-pad the decimals. + elif isinstance(f, models.DecimalField): if field_val is not None: result_repr = ('%%.%sf' % f.decimal_places) % field_val else: diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py index 1498f3c8ba..434997d616 100644 --- a/django/contrib/sessions/middleware.py +++ b/django/contrib/sessions/middleware.py @@ -36,6 +36,9 @@ class SessionWrapper(object): def get(self, key, default=None): return self._session.get(key, default) + def pop(self, key, *args): + return self._session.pop(key, *args) + def set_test_cookie(self): self[TEST_COOKIE_NAME] = TEST_COOKIE_VALUE diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py new file mode 100644 index 0000000000..5a28effa86 --- /dev/null +++ b/django/contrib/sessions/tests.py @@ -0,0 +1,19 @@ +r""" +>>> s = SessionWrapper(None) + +Inject data into the session cache. +>>> s._session_cache = {} +>>> s._session_cache['some key'] = 'exists' + +>>> s.pop('some key') +'exists' + +>>> s.pop('some key', 'does not exist') +'does not exist' +""" + +from django.contrib.sessions.middleware import SessionWrapper + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/django/core/management.py b/django/core/management.py index 789ce21f66..26a06792b2 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -870,7 +870,7 @@ def inspectdb(): if field_type == 'CharField' and row[3]: extra_params['maxlength'] = row[3] - if field_type == 'FloatField': + if field_type == 'DecimalField': extra_params['max_digits'] = row[4] extra_params['decimal_places'] = row[5] @@ -945,11 +945,11 @@ def get_validation_errors(outfile, app=None): e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name) if isinstance(f, models.CharField) and f.maxlength in (None, 0): e.add(opts, '"%s": CharFields require a "maxlength" attribute.' % f.name) - if isinstance(f, models.FloatField): + if isinstance(f, models.DecimalField): if f.decimal_places is None: - e.add(opts, '"%s": FloatFields require a "decimal_places" attribute.' % f.name) + e.add(opts, '"%s": DecimalFields require a "decimal_places" attribute.' % f.name) if f.max_digits is None: - e.add(opts, '"%s": FloatFields require a "max_digits" attribute.' % f.name) + e.add(opts, '"%s": DecimalFields require a "max_digits" attribute.' % f.name) if isinstance(f, models.FileField) and not f.upload_to: e.add(opts, '"%s": FileFields require an "upload_to" attribute.' % f.name) if isinstance(f, models.ImageField): diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index 7c029e7029..55804b8316 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -4,19 +4,24 @@ Serialize data to/from JSON import datetime from django.utils import simplejson +from django.utils.simplejson import decoder from django.core.serializers.python import Serializer as PythonSerializer from django.core.serializers.python import Deserializer as PythonDeserializer try: from cStringIO import StringIO except ImportError: from StringIO import StringIO +try: + import decimal +except ImportError: + from django.utils import _decimal as decimal # Python 2.3 fallback class Serializer(PythonSerializer): """ Convert a queryset to JSON. """ def end_serialization(self): - simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder, **self.options) + simplejson.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options) def getvalue(self): if callable(getattr(self.stream, 'getvalue', None)): @@ -33,9 +38,9 @@ def Deserializer(stream_or_string, **options): for obj in PythonDeserializer(simplejson.load(stream)): yield obj -class DateTimeAwareJSONEncoder(simplejson.JSONEncoder): +class DjangoJSONEncoder(simplejson.JSONEncoder): """ - JSONEncoder subclass that knows how to encode date/time types + JSONEncoder subclass that knows how to encode date/time and decimal types. """ DATE_FORMAT = "%Y-%m-%d" @@ -48,5 +53,11 @@ class DateTimeAwareJSONEncoder(simplejson.JSONEncoder): return o.strftime(self.DATE_FORMAT) elif isinstance(o, datetime.time): return o.strftime(self.TIME_FORMAT) + elif isinstance(o, decimal.Decimal): + return str(o) else: - return super(DateTimeAwareJSONEncoder, self).default(o) + return super(DjangoJSONEncoder, self).default(o) + +# Older, deprecated class name (for backwards compatibility purposes). +DateTimeAwareJSONEncoder = DjangoJSONEncoder + diff --git a/django/core/validators.py b/django/core/validators.py index 26165c4af1..293f0e1a8c 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -25,6 +25,7 @@ email_re = re.compile( r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE) # domain +decimal_re = re.compile(r'^-?(?P\d+)(\.(?P\d+))?$') integer_re = re.compile(r'^-?\d+$') ip4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$') phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNORECASE) @@ -406,28 +407,35 @@ class IsAPowerOf(object): if val != int(val): raise ValidationError, gettext("This value must be a power of %s.") % self.power_of -class IsValidFloat(object): +class IsValidDecimal(object): def __init__(self, max_digits, decimal_places): self.max_digits, self.decimal_places = max_digits, decimal_places def __call__(self, field_data, all_data): - data = str(field_data) - try: - float(data) - except ValueError: + match = decimal_re.search(str(field_data)) + if not match: raise ValidationError, gettext("Please enter a valid decimal number.") - # Negative floats require more space to input. - max_allowed_length = data.startswith('-') and (self.max_digits + 2) or (self.max_digits + 1) - if len(data) > max_allowed_length: + + digits = len(match.group('digits') or '') + decimals = len(match.group('decimals') or '') + + if digits + decimals > self.max_digits: raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.", "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits - if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places - 1)) or ('.' in data and len(data) > (max_allowed_length - (self.decimal_places - len(data.split('.')[1])))): + if digits > (self.max_digits - self.decimal_places): raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.", "Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places) - if '.' in data and len(data.split('.')[1]) > self.decimal_places: + if decimals > self.decimal_places: raise ValidationError, ngettext("Please enter a valid decimal number with at most %s decimal place.", "Please enter a valid decimal number with at most %s decimal places.", self.decimal_places) % self.decimal_places +def isValidFloat(field_data, all_data): + data = str(field_data) + try: + float(data) + except ValueError: + raise ValidationError, gettext("Please enter a valid floating point number.") + class HasAllowableSize(object): """ Checks that the file-upload field data is a certain size. min_size and diff --git a/django/db/backends/ado_mssql/creation.py b/django/db/backends/ado_mssql/creation.py index 5158ba02f9..a1098ea43e 100644 --- a/django/db/backends/ado_mssql/creation.py +++ b/django/db/backends/ado_mssql/creation.py @@ -5,9 +5,10 @@ DATA_TYPES = { 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 'DateField': 'smalldatetime', 'DateTimeField': 'smalldatetime', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'FileField': 'varchar(100)', 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'FloatField': 'double precision', 'ImageField': 'varchar(100)', 'IntegerField': 'int', 'IPAddressField': 'char(15)', diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 41f4638980..d4cb1fa964 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -15,7 +15,7 @@ except ImportError, e: # lexicographic ordering in this check because then (1, 2, 1, 'gamma') # inadvertently passes the version test. version = Database.version_info -if (version < (1,2,1) or (version[:3] == (1, 2, 1) and +if (version < (1,2,1) or (version[:3] == (1, 2, 1) and (len(version) < 5 or version[3] != 'final' or version[4] < 2))): raise ImportError, "MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__ @@ -36,6 +36,8 @@ IntegrityError = Database.IntegrityError django_conversions = conversions.copy() django_conversions.update({ FIELD_TYPE.TIME: util.typecast_time, + FIELD_TYPE.DECIMAL: util.typecast_decimal, + FIELD_TYPE.NEWDECIMAL: util.typecast_decimal, }) # This should match the numerical portion of the version numbers (we can treat diff --git a/django/db/backends/mysql/creation.py b/django/db/backends/mysql/creation.py index 22ed901653..1b23fbff6e 100644 --- a/django/db/backends/mysql/creation.py +++ b/django/db/backends/mysql/creation.py @@ -9,9 +9,10 @@ DATA_TYPES = { 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 'DateField': 'date', 'DateTimeField': 'datetime', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'FileField': 'varchar(100)', 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'FloatField': 'double precision', 'ImageField': 'varchar(100)', 'IntegerField': 'integer', 'IPAddressField': 'char(15)', diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index 558fe49787..39733311c5 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -76,7 +76,7 @@ def get_indexes(cursor, table_name): DATA_TYPES_REVERSE = { FIELD_TYPE.BLOB: 'TextField', FIELD_TYPE.CHAR: 'CharField', - FIELD_TYPE.DECIMAL: 'FloatField', + FIELD_TYPE.DECIMAL: 'DecimalField', FIELD_TYPE.DATE: 'DateField', FIELD_TYPE.DATETIME: 'DateTimeField', FIELD_TYPE.DOUBLE: 'FloatField', diff --git a/django/db/backends/mysql_old/base.py b/django/db/backends/mysql_old/base.py index d56b8513f9..ac3b75efde 100644 --- a/django/db/backends/mysql_old/base.py +++ b/django/db/backends/mysql_old/base.py @@ -24,6 +24,7 @@ django_conversions.update({ FIELD_TYPE.DATETIME: util.typecast_timestamp, FIELD_TYPE.DATE: util.typecast_date, FIELD_TYPE.TIME: util.typecast_time, + FIELD_TYPE.DECIMAL: util.typecast_decimal, }) # This should match the numerical portion of the version numbers (we can treat diff --git a/django/db/backends/mysql_old/creation.py b/django/db/backends/mysql_old/creation.py index 22ed901653..1b23fbff6e 100644 --- a/django/db/backends/mysql_old/creation.py +++ b/django/db/backends/mysql_old/creation.py @@ -9,9 +9,10 @@ DATA_TYPES = { 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 'DateField': 'date', 'DateTimeField': 'datetime', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'FileField': 'varchar(100)', 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'FloatField': 'double precision', 'ImageField': 'varchar(100)', 'IntegerField': 'integer', 'IPAddressField': 'char(15)', diff --git a/django/db/backends/mysql_old/introspection.py b/django/db/backends/mysql_old/introspection.py index 5ea626a5a9..cb5b8320d9 100644 --- a/django/db/backends/mysql_old/introspection.py +++ b/django/db/backends/mysql_old/introspection.py @@ -76,7 +76,7 @@ def get_indexes(cursor, table_name): DATA_TYPES_REVERSE = { FIELD_TYPE.BLOB: 'TextField', FIELD_TYPE.CHAR: 'CharField', - FIELD_TYPE.DECIMAL: 'FloatField', + FIELD_TYPE.DECIMAL: 'DecimalField', FIELD_TYPE.DATE: 'DateField', FIELD_TYPE.DATETIME: 'DateTimeField', FIELD_TYPE.DOUBLE: 'FloatField', diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py index da65df172e..14a864ac28 100644 --- a/django/db/backends/oracle/creation.py +++ b/django/db/backends/oracle/creation.py @@ -5,9 +5,10 @@ DATA_TYPES = { 'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)', 'DateField': 'date', 'DateTimeField': 'date', + 'DecimalField': 'number(%(max_digits)s, %(decimal_places)s)', 'FileField': 'varchar2(100)', 'FilePathField': 'varchar2(100)', - 'FloatField': 'number(%(max_digits)s, %(decimal_places)s)', + 'FloatField': 'double precision', 'ImageField': 'varchar2(100)', 'IntegerField': 'integer', 'IPAddressField': 'char(15)', diff --git a/django/db/backends/oracle/introspection.py b/django/db/backends/oracle/introspection.py index ecc8f372a8..7634206178 100644 --- a/django/db/backends/oracle/introspection.py +++ b/django/db/backends/oracle/introspection.py @@ -46,5 +46,5 @@ DATA_TYPES_REVERSE = { 1114: 'DateTimeField', 1184: 'DateTimeField', 1266: 'TimeField', - 1700: 'FloatField', + 1700: 'DecimalField', } diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 1ae16feb25..a5b38a883d 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -249,6 +249,7 @@ except AttributeError: Database.register_type(Database.new_type((1083,1266), "TIME", util.typecast_time)) Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", util.typecast_timestamp)) Database.register_type(Database.new_type((16,), "BOOLEAN", util.typecast_boolean)) +Database.register_type(Database.new_type((1700,), "NUMERIC", util.typecast_decimal)) OPERATOR_MAPPING = { 'exact': '= %s', diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py index 6c130f368e..4646b68ab8 100644 --- a/django/db/backends/postgresql/creation.py +++ b/django/db/backends/postgresql/creation.py @@ -9,9 +9,10 @@ DATA_TYPES = { 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 'DateField': 'date', 'DateTimeField': 'timestamp with time zone', + 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'FileField': 'varchar(100)', 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'FloatField': 'double precision', 'ImageField': 'varchar(100)', 'IntegerField': 'integer', 'IPAddressField': 'inet', diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py index 6e1d60c4ff..2605490afd 100644 --- a/django/db/backends/postgresql/introspection.py +++ b/django/db/backends/postgresql/introspection.py @@ -72,6 +72,7 @@ DATA_TYPES_REVERSE = { 21: 'SmallIntegerField', 23: 'IntegerField', 25: 'TextField', + 701: 'FloatField', 869: 'IPAddressField', 1043: 'CharField', 1082: 'DateField', @@ -79,5 +80,5 @@ DATA_TYPES_REVERSE = { 1114: 'DateTimeField', 1184: 'DateTimeField', 1266: 'TimeField', - 1700: 'FloatField', + 1700: 'DecimalField', } diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py index a546da8c45..aa45fe7db7 100644 --- a/django/db/backends/postgresql_psycopg2/introspection.py +++ b/django/db/backends/postgresql_psycopg2/introspection.py @@ -72,6 +72,7 @@ DATA_TYPES_REVERSE = { 21: 'SmallIntegerField', 23: 'IntegerField', 25: 'TextField', + 701: 'FloatField', 869: 'IPAddressField', 1043: 'CharField', 1082: 'DateField', @@ -79,5 +80,5 @@ DATA_TYPES_REVERSE = { 1114: 'DateTimeField', 1184: 'DateTimeField', 1266: 'TimeField', - 1700: 'FloatField', + 1700: 'DecimalField', } diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index d8e1336a9a..5cd67a32f5 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -17,6 +17,11 @@ except ImportError, e: module = 'sqlite3' raise ImproperlyConfigured, "Error loading %s module: %s" % (module, e) +try: + import decimal +except ImportError: + from django.utils import _decimal as decimal # for Python 2.3 + DatabaseError = Database.DatabaseError IntegrityError = Database.IntegrityError @@ -26,6 +31,8 @@ Database.register_converter("date", util.typecast_date) Database.register_converter("datetime", util.typecast_timestamp) Database.register_converter("timestamp", util.typecast_timestamp) Database.register_converter("TIMESTAMP", util.typecast_timestamp) +Database.register_converter("decimal", util.typecast_decimal) +Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) def utf8rowFactory(cursor, row): def utf8(s): diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py index 77f570b2e8..e63046ab7d 100644 --- a/django/db/backends/sqlite3/creation.py +++ b/django/db/backends/sqlite3/creation.py @@ -8,9 +8,10 @@ DATA_TYPES = { 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 'DateField': 'date', 'DateTimeField': 'datetime', + 'DecimalField': 'decimal', 'FileField': 'varchar(100)', 'FilePathField': 'varchar(100)', - 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', + 'FloatField': 'real', 'ImageField': 'varchar(100)', 'IntegerField': 'integer', 'IPAddressField': 'char(15)', diff --git a/django/db/backends/util.py b/django/db/backends/util.py index d14a337ca2..18f120b600 100644 --- a/django/db/backends/util.py +++ b/django/db/backends/util.py @@ -1,6 +1,11 @@ import datetime from time import time +try: + import decimal +except ImportError: + from django.utils import _decimal as decimal # for Python 2.3 + class CursorDebugWrapper(object): def __init__(self, cursor, db): self.cursor = cursor @@ -85,6 +90,11 @@ def typecast_boolean(s): if not s: return False return str(s)[0].lower() == 't' +def typecast_decimal(s): + if s is None: + return None + return decimal.Decimal(s) + ############################################### # Converters from Python to database (string) # ############################################### @@ -92,6 +102,11 @@ def typecast_boolean(s): def rev_typecast_boolean(obj, d): return obj and '1' or '0' +def rev_typecast_decimal(d): + if d is None: + return None + return str(d) + ################################################################################## # Helper functions for dictfetch* for databases that don't natively support them # ################################################################################## diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index d1c6fa1a74..9ba3ac96c6 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -10,6 +10,10 @@ from django.utils.itercompat import tee from django.utils.text import capfirst from django.utils.translation import gettext, gettext_lazy import datetime, os, time +try: + import decimal +except ImportError: + from django.utils import _decimal as decimal # for Python 2.3 class NOT_PROVIDED: pass @@ -570,6 +574,65 @@ class DateTimeField(DateField): defaults.update(kwargs) return super(DateTimeField, self).formfield(**defaults) +class DecimalField(Field): + empty_strings_allowed = False + def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs): + self.max_digits, self.decimal_places = max_digits, decimal_places + Field.__init__(self, verbose_name, name, **kwargs) + + def to_python(self, value): + if value is None: + return value + try: + return decimal.Decimal(value) + except decimal.InvalidOperation: + raise validators.ValidationError, gettext("This value must be a decimal number.") + + def _format(self, value): + if isinstance(value, basestring): + return value + else: + return self.format_number(value) + + def format_number(self, value): + """ + Formats a number into a string with the requisite number of digits and + decimal places. + """ + num_chars = self.max_digits + # Allow for a decimal point + if self.decimal_places > 0: + num_chars += 1 + # Allow for a minus sign + if value < 0: + num_chars += 1 + + return "%.*f" % (self.decimal_places, value) + + def get_db_prep_save(self, value): + if value is not None: + value = self._format(value) + return super(DecimalField, self).get_db_prep_save(value) + + def get_db_prep_lookup(self, lookup_type, value): + if lookup_type == 'range': + value = [self._format(v) for v in value] + else: + value = self._format(value) + return super(DecimalField, self).get_db_prep_lookup(lookup_type, value) + + def get_manipulator_field_objs(self): + return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)] + + def formfield(self, **kwargs): + defaults = { + 'max_digits': self.max_digits, + 'decimal_places': self.decimal_places, + 'form_class': forms.DecimalField, + } + defaults.update(kwargs) + return super(DecimalField, self).formfield(**defaults) + class EmailField(CharField): def __init__(self, *args, **kwargs): kwargs['maxlength'] = 75 @@ -680,12 +743,14 @@ class FilePathField(Field): class FloatField(Field): empty_strings_allowed = False - def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs): - self.max_digits, self.decimal_places = max_digits, decimal_places - Field.__init__(self, verbose_name, name, **kwargs) def get_manipulator_field_objs(self): - return [curry(oldforms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)] + return [oldforms.FloatField] + + def formfield(self, **kwargs): + defaults = {'form_class': forms.FloatField} + defaults.update(kwargs) + return super(FloatField, self).formfield(**defaults) class ImageField(FileField): def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): diff --git a/django/newforms/fields.py b/django/newforms/fields.py index 30c675c14a..b73dd181e6 100644 --- a/django/newforms/fields.py +++ b/django/newforms/fields.py @@ -19,7 +19,7 @@ __all__ = ( 'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField', 'RegexField', 'EmailField', 'URLField', 'BooleanField', 'ChoiceField', 'NullBooleanField', 'MultipleChoiceField', - 'ComboField', 'MultiValueField', + 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', 'SplitDateTimeField', ) @@ -31,6 +31,11 @@ try: except NameError: from sets import Set as set # Python 2.3 fallback +try: + from decimal import Decimal +except ImportError: + from django.utils._decimal import Decimal # Python 2.3 fallback + class Field(object): widget = TextInput # Default widget to use when rendering this type of Field. hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". @@ -134,6 +139,67 @@ class IntegerField(Field): raise ValidationError(gettext(u'Ensure this value is greater than or equal to %s.') % self.min_value) return value +class FloatField(Field): + def __init__(self, max_value=None, min_value=None, *args, **kwargs): + self.max_value, self.min_value = max_value, min_value + Field.__init__(self, *args, **kwargs) + + def clean(self, value): + """ + Validates that float() can be called on the input. Returns a float. + Returns None for empty values. + """ + super(FloatField, self).clean(value) + if not self.required and value in EMPTY_VALUES: + return None + try: + value = float(value) + except (ValueError, TypeError): + raise ValidationError(gettext('Enter a number.')) + if self.max_value is not None and value > self.max_value: + raise ValidationError(gettext('Ensure this value is less than or equal to %s.') % self.max_value) + if self.min_value is not None and value < self.min_value: + raise ValidationError(gettext('Ensure this value is greater than or equal to %s.') % self.min_value) + return value + +decimal_re = re.compile(r'^-?(?P\d+)(\.(?P\d+))?$') + +class DecimalField(Field): + def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs): + self.max_value, self.min_value = max_value, min_value + self.max_digits, self.decimal_places = max_digits, decimal_places + Field.__init__(self, *args, **kwargs) + + def clean(self, value): + """ + Validates that the input is a decimal number. Returns a Decimal + instance. Returns None for empty values. Ensures that there are no more + than max_digits in the number, and no more than decimal_places digits + after the decimal point. + """ + super(DecimalField, self).clean(value) + if not self.required and value in EMPTY_VALUES: + return None + value = value.strip() + match = decimal_re.search(value) + if not match: + raise ValidationError(gettext('Enter a number.')) + else: + value = Decimal(value) + digits = len(match.group('digits') or '') + decimals = len(match.group('decimals') or '') + if self.max_value is not None and value > self.max_value: + raise ValidationError(gettext('Ensure this value is less than or equal to %s.') % self.max_value) + if self.min_value is not None and value < self.min_value: + raise ValidationError(gettext('Ensure this value is greater than or equal to %s.') % self.min_value) + if self.max_digits is not None and (digits + decimals) > self.max_digits: + raise ValidationError(gettext('Ensure that there are no more than %s digits in total.') % self.max_digits) + if self.decimal_places is not None and decimals > self.decimal_places: + raise ValidationError(gettext('Ensure that there are no more than %s decimal places.') % self.decimal_places) + if self.max_digits is not None and self.decimal_places is not None and digits > (self.max_digits - self.decimal_places): + raise ValidationError(gettext('Ensure that there are no more than %s digits before the decimal point.') % (self.max_digits - self.decimal_places)) + return value + DEFAULT_DATE_INPUT_FORMATS = ( '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006' diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py index 0a4676ee8f..eed73ab156 100644 --- a/django/oldforms/__init__.py +++ b/django/oldforms/__init__.py @@ -750,14 +750,27 @@ class PositiveSmallIntegerField(IntegerField): raise validators.CriticalValidationError, gettext("Enter a whole number between 0 and 32,767.") class FloatField(TextField): + def __init__(self, field_name, is_required=False, validator_list=None): + if validator_list is None: validator_list = [] + validator_list = [validators.isValidFloat] + validator_list + TextField.__init__(self, field_name, is_required=is_required, validator_list=validator_list) + + def html2python(data): + if data == '' or data is None: + return None + return float(data) + html2python = staticmethod(html2python) + +class DecimalField(TextField): def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None): if validator_list is None: validator_list = [] self.max_digits, self.decimal_places = max_digits, decimal_places - validator_list = [self.isValidFloat] + validator_list - TextField.__init__(self, field_name, max_digits+2, max_digits+2, is_required, validator_list) + validator_list = [self.isValidDecimal] + validator_list + # Initialise the TextField, making sure it's large enough to fit the number with a - sign and a decimal point. + super(DecimalField, self).__init__(field_name, max_digits+2, max_digits+2, is_required, validator_list) - def isValidFloat(self, field_data, all_data): - v = validators.IsValidFloat(self.max_digits, self.decimal_places) + def isValidDecimal(self, field_data, all_data): + v = validators.IsValidDecimal(self.max_digits, self.decimal_places) try: v(field_data, all_data) except validators.ValidationError, e: @@ -766,7 +779,14 @@ class FloatField(TextField): def html2python(data): if data == '' or data is None: return None - return float(data) + try: + import decimal + except ImportError: + from django.utils import decimal + try: + return decimal.Decimal(data) + except decimal.InvalidOperation, e: + raise ValueError, e html2python = staticmethod(html2python) #################### diff --git a/django/utils/_decimal.py b/django/utils/_decimal.py new file mode 100644 index 0000000000..677d26bb32 --- /dev/null +++ b/django/utils/_decimal.py @@ -0,0 +1,3079 @@ +# Copyright (c) 2004 Python Software Foundation. +# All rights reserved. + +# Written by Eric Price +# and Facundo Batista +# and Raymond Hettinger +# and Aahz +# and Tim Peters + +# This module is currently Py2.3 compatible and should be kept that way +# unless a major compelling advantage arises. IOW, 2.3 compatibility is +# strongly preferred, but not guaranteed. + +# Also, this module should be kept in sync with the latest updates of +# the IBM specification as it evolves. Those updates will be treated +# as bug fixes (deviation from the spec is a compatibility, usability +# bug) and will be backported. At this point the spec is stabilizing +# and the updates are becoming fewer, smaller, and less significant. + +""" +This is a Py2.3 implementation of decimal floating point arithmetic based on +the General Decimal Arithmetic Specification: + + www2.hursley.ibm.com/decimal/decarith.html + +and IEEE standard 854-1987: + + www.cs.berkeley.edu/~ejr/projects/754/private/drafts/854-1987/dir.html + +Decimal floating point has finite precision with arbitrarily large bounds. + +The purpose of the module is to support arithmetic using familiar +"schoolhouse" rules and to avoid the some of tricky representation +issues associated with binary floating point. The package is especially +useful for financial applications or for contexts where users have +expectations that are at odds with binary floating point (for instance, +in binary floating point, 1.00 % 0.1 gives 0.09999999999999995 instead +of the expected Decimal("0.00") returned by decimal floating point). + +Here are some examples of using the decimal module: + +>>> from decimal import * +>>> setcontext(ExtendedContext) +>>> Decimal(0) +Decimal("0") +>>> Decimal("1") +Decimal("1") +>>> Decimal("-.0123") +Decimal("-0.0123") +>>> Decimal(123456) +Decimal("123456") +>>> Decimal("123.45e12345678901234567890") +Decimal("1.2345E+12345678901234567892") +>>> Decimal("1.33") + Decimal("1.27") +Decimal("2.60") +>>> Decimal("12.34") + Decimal("3.87") - Decimal("18.41") +Decimal("-2.20") +>>> dig = Decimal(1) +>>> print dig / Decimal(3) +0.333333333 +>>> getcontext().prec = 18 +>>> print dig / Decimal(3) +0.333333333333333333 +>>> print dig.sqrt() +1 +>>> print Decimal(3).sqrt() +1.73205080756887729 +>>> print Decimal(3) ** 123 +4.85192780976896427E+58 +>>> inf = Decimal(1) / Decimal(0) +>>> print inf +Infinity +>>> neginf = Decimal(-1) / Decimal(0) +>>> print neginf +-Infinity +>>> print neginf + inf +NaN +>>> print neginf * inf +-Infinity +>>> print dig / 0 +Infinity +>>> getcontext().traps[DivisionByZero] = 1 +>>> print dig / 0 +Traceback (most recent call last): + ... + ... + ... +DivisionByZero: x / 0 +>>> c = Context() +>>> c.traps[InvalidOperation] = 0 +>>> print c.flags[InvalidOperation] +0 +>>> c.divide(Decimal(0), Decimal(0)) +Decimal("NaN") +>>> c.traps[InvalidOperation] = 1 +>>> print c.flags[InvalidOperation] +1 +>>> c.flags[InvalidOperation] = 0 +>>> print c.flags[InvalidOperation] +0 +>>> print c.divide(Decimal(0), Decimal(0)) +Traceback (most recent call last): + ... + ... + ... +InvalidOperation: 0 / 0 +>>> print c.flags[InvalidOperation] +1 +>>> c.flags[InvalidOperation] = 0 +>>> c.traps[InvalidOperation] = 0 +>>> print c.divide(Decimal(0), Decimal(0)) +NaN +>>> print c.flags[InvalidOperation] +1 +>>> +""" + +__all__ = [ + # Two major classes + 'Decimal', 'Context', + + # Contexts + 'DefaultContext', 'BasicContext', 'ExtendedContext', + + # Exceptions + 'DecimalException', 'Clamped', 'InvalidOperation', 'DivisionByZero', + 'Inexact', 'Rounded', 'Subnormal', 'Overflow', 'Underflow', + + # Constants for use in setting up contexts + 'ROUND_DOWN', 'ROUND_HALF_UP', 'ROUND_HALF_EVEN', 'ROUND_CEILING', + 'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', + + # Functions for manipulating contexts + 'setcontext', 'getcontext' +] + +import copy as _copy + +#Rounding +ROUND_DOWN = 'ROUND_DOWN' +ROUND_HALF_UP = 'ROUND_HALF_UP' +ROUND_HALF_EVEN = 'ROUND_HALF_EVEN' +ROUND_CEILING = 'ROUND_CEILING' +ROUND_FLOOR = 'ROUND_FLOOR' +ROUND_UP = 'ROUND_UP' +ROUND_HALF_DOWN = 'ROUND_HALF_DOWN' + +#Rounding decision (not part of the public API) +NEVER_ROUND = 'NEVER_ROUND' # Round in division (non-divmod), sqrt ONLY +ALWAYS_ROUND = 'ALWAYS_ROUND' # Every operation rounds at end. + +#Errors + +class DecimalException(ArithmeticError): + """Base exception class. + + Used exceptions derive from this. + If an exception derives from another exception besides this (such as + Underflow (Inexact, Rounded, Subnormal) that indicates that it is only + called if the others are present. This isn't actually used for + anything, though. + + handle -- Called when context._raise_error is called and the + trap_enabler is set. First argument is self, second is the + context. More arguments can be given, those being after + the explanation in _raise_error (For example, + context._raise_error(NewError, '(-x)!', self._sign) would + call NewError().handle(context, self._sign).) + + To define a new exception, it should be sufficient to have it derive + from DecimalException. + """ + def handle(self, context, *args): + pass + + +class Clamped(DecimalException): + """Exponent of a 0 changed to fit bounds. + + This occurs and signals clamped if the exponent of a result has been + altered in order to fit the constraints of a specific concrete + representation. This may occur when the exponent of a zero result would + be outside the bounds of a representation, or when a large normal + number would have an encoded exponent that cannot be represented. In + this latter case, the exponent is reduced to fit and the corresponding + number of zero digits are appended to the coefficient ("fold-down"). + """ + + +class InvalidOperation(DecimalException): + """An invalid operation was performed. + + Various bad things cause this: + + Something creates a signaling NaN + -INF + INF + 0 * (+-)INF + (+-)INF / (+-)INF + x % 0 + (+-)INF % x + x._rescale( non-integer ) + sqrt(-x) , x > 0 + 0 ** 0 + x ** (non-integer) + x ** (+-)INF + An operand is invalid + """ + def handle(self, context, *args): + if args: + if args[0] == 1: #sNaN, must drop 's' but keep diagnostics + return Decimal( (args[1]._sign, args[1]._int, 'n') ) + return NaN + +class ConversionSyntax(InvalidOperation): + """Trying to convert badly formed string. + + This occurs and signals invalid-operation if an string is being + converted to a number and it does not conform to the numeric string + syntax. The result is [0,qNaN]. + """ + + def handle(self, context, *args): + return (0, (0,), 'n') #Passed to something which uses a tuple. + +class DivisionByZero(DecimalException, ZeroDivisionError): + """Division by 0. + + This occurs and signals division-by-zero if division of a finite number + by zero was attempted (during a divide-integer or divide operation, or a + power operation with negative right-hand operand), and the dividend was + not zero. + + The result of the operation is [sign,inf], where sign is the exclusive + or of the signs of the operands for divide, or is 1 for an odd power of + -0, for power. + """ + + def handle(self, context, sign, double = None, *args): + if double is not None: + return (Infsign[sign],)*2 + return Infsign[sign] + +class DivisionImpossible(InvalidOperation): + """Cannot perform the division adequately. + + This occurs and signals invalid-operation if the integer result of a + divide-integer or remainder operation had too many digits (would be + longer than precision). The result is [0,qNaN]. + """ + + def handle(self, context, *args): + return (NaN, NaN) + +class DivisionUndefined(InvalidOperation, ZeroDivisionError): + """Undefined result of division. + + This occurs and signals invalid-operation if division by zero was + attempted (during a divide-integer, divide, or remainder operation), and + the dividend is also zero. The result is [0,qNaN]. + """ + + def handle(self, context, tup=None, *args): + if tup is not None: + return (NaN, NaN) #for 0 %0, 0 // 0 + return NaN + +class Inexact(DecimalException): + """Had to round, losing information. + + This occurs and signals inexact whenever the result of an operation is + not exact (that is, it needed to be rounded and any discarded digits + were non-zero), or if an overflow or underflow condition occurs. The + result in all cases is unchanged. + + The inexact signal may be tested (or trapped) to determine if a given + operation (or sequence of operations) was inexact. + """ + pass + +class InvalidContext(InvalidOperation): + """Invalid context. Unknown rounding, for example. + + This occurs and signals invalid-operation if an invalid context was + detected during an operation. This can occur if contexts are not checked + on creation and either the precision exceeds the capability of the + underlying concrete representation or an unknown or unsupported rounding + was specified. These aspects of the context need only be checked when + the values are required to be used. The result is [0,qNaN]. + """ + + def handle(self, context, *args): + return NaN + +class Rounded(DecimalException): + """Number got rounded (not necessarily changed during rounding). + + This occurs and signals rounded whenever the result of an operation is + rounded (that is, some zero or non-zero digits were discarded from the + coefficient), or if an overflow or underflow condition occurs. The + result in all cases is unchanged. + + The rounded signal may be tested (or trapped) to determine if a given + operation (or sequence of operations) caused a loss of precision. + """ + pass + +class Subnormal(DecimalException): + """Exponent < Emin before rounding. + + This occurs and signals subnormal whenever the result of a conversion or + operation is subnormal (that is, its adjusted exponent is less than + Emin, before any rounding). The result in all cases is unchanged. + + The subnormal signal may be tested (or trapped) to determine if a given + or operation (or sequence of operations) yielded a subnormal result. + """ + pass + +class Overflow(Inexact, Rounded): + """Numerical overflow. + + This occurs and signals overflow if the adjusted exponent of a result + (from a conversion or from an operation that is not an attempt to divide + by zero), after rounding, would be greater than the largest value that + can be handled by the implementation (the value Emax). + + The result depends on the rounding mode: + + For round-half-up and round-half-even (and for round-half-down and + round-up, if implemented), the result of the operation is [sign,inf], + where sign is the sign of the intermediate result. For round-down, the + result is the largest finite number that can be represented in the + current precision, with the sign of the intermediate result. For + round-ceiling, the result is the same as for round-down if the sign of + the intermediate result is 1, or is [0,inf] otherwise. For round-floor, + the result is the same as for round-down if the sign of the intermediate + result is 0, or is [1,inf] otherwise. In all cases, Inexact and Rounded + will also be raised. + """ + + def handle(self, context, sign, *args): + if context.rounding in (ROUND_HALF_UP, ROUND_HALF_EVEN, + ROUND_HALF_DOWN, ROUND_UP): + return Infsign[sign] + if sign == 0: + if context.rounding == ROUND_CEILING: + return Infsign[sign] + return Decimal((sign, (9,)*context.prec, + context.Emax-context.prec+1)) + if sign == 1: + if context.rounding == ROUND_FLOOR: + return Infsign[sign] + return Decimal( (sign, (9,)*context.prec, + context.Emax-context.prec+1)) + + +class Underflow(Inexact, Rounded, Subnormal): + """Numerical underflow with result rounded to 0. + + This occurs and signals underflow if a result is inexact and the + adjusted exponent of the result would be smaller (more negative) than + the smallest value that can be handled by the implementation (the value + Emin). That is, the result is both inexact and subnormal. + + The result after an underflow will be a subnormal number rounded, if + necessary, so that its exponent is not less than Etiny. This may result + in 0 with the sign of the intermediate result and an exponent of Etiny. + + In all cases, Inexact, Rounded, and Subnormal will also be raised. + """ + +# List of public traps and flags +_signals = [Clamped, DivisionByZero, Inexact, Overflow, Rounded, + Underflow, InvalidOperation, Subnormal] + +# Map conditions (per the spec) to signals +_condition_map = {ConversionSyntax:InvalidOperation, + DivisionImpossible:InvalidOperation, + DivisionUndefined:InvalidOperation, + InvalidContext:InvalidOperation} + +##### Context Functions ####################################### + +# The getcontext() and setcontext() function manage access to a thread-local +# current context. Py2.4 offers direct support for thread locals. If that +# is not available, use threading.currentThread() which is slower but will +# work for older Pythons. If threads are not part of the build, create a +# mock threading object with threading.local() returning the module namespace. + +try: + import threading +except ImportError: + # Python was compiled without threads; create a mock object instead + import sys + class MockThreading: + def local(self, sys=sys): + return sys.modules[__name__] + threading = MockThreading() + del sys, MockThreading + +try: + threading.local + +except AttributeError: + + #To fix reloading, force it to create a new context + #Old contexts have different exceptions in their dicts, making problems. + if hasattr(threading.currentThread(), '__decimal_context__'): + del threading.currentThread().__decimal_context__ + + def setcontext(context): + """Set this thread's context to context.""" + if context in (DefaultContext, BasicContext, ExtendedContext): + context = context.copy() + context.clear_flags() + threading.currentThread().__decimal_context__ = context + + def getcontext(): + """Returns this thread's context. + + If this thread does not yet have a context, returns + a new context and sets this thread's context. + New contexts are copies of DefaultContext. + """ + try: + return threading.currentThread().__decimal_context__ + except AttributeError: + context = Context() + threading.currentThread().__decimal_context__ = context + return context + +else: + + local = threading.local() + if hasattr(local, '__decimal_context__'): + del local.__decimal_context__ + + def getcontext(_local=local): + """Returns this thread's context. + + If this thread does not yet have a context, returns + a new context and sets this thread's context. + New contexts are copies of DefaultContext. + """ + try: + return _local.__decimal_context__ + except AttributeError: + context = Context() + _local.__decimal_context__ = context + return context + + def setcontext(context, _local=local): + """Set this thread's context to context.""" + if context in (DefaultContext, BasicContext, ExtendedContext): + context = context.copy() + context.clear_flags() + _local.__decimal_context__ = context + + del threading, local # Don't contaminate the namespace + + +##### Decimal class ########################################### + +class Decimal(object): + """Floating point class for decimal arithmetic.""" + + __slots__ = ('_exp','_int','_sign', '_is_special') + # Generally, the value of the Decimal instance is given by + # (-1)**_sign * _int * 10**_exp + # Special values are signified by _is_special == True + + # We're immutable, so use __new__ not __init__ + def __new__(cls, value="0", context=None): + """Create a decimal point instance. + + >>> Decimal('3.14') # string input + Decimal("3.14") + >>> Decimal((0, (3, 1, 4), -2)) # tuple input (sign, digit_tuple, exponent) + Decimal("3.14") + >>> Decimal(314) # int or long + Decimal("314") + >>> Decimal(Decimal(314)) # another decimal instance + Decimal("314") + """ + + self = object.__new__(cls) + self._is_special = False + + # From an internal working value + if isinstance(value, _WorkRep): + self._sign = value.sign + self._int = tuple(map(int, str(value.int))) + self._exp = int(value.exp) + return self + + # From another decimal + if isinstance(value, Decimal): + self._exp = value._exp + self._sign = value._sign + self._int = value._int + self._is_special = value._is_special + return self + + # From an integer + if isinstance(value, (int,long)): + if value >= 0: + self._sign = 0 + else: + self._sign = 1 + self._exp = 0 + self._int = tuple(map(int, str(abs(value)))) + return self + + # tuple/list conversion (possibly from as_tuple()) + if isinstance(value, (list,tuple)): + if len(value) != 3: + raise ValueError, 'Invalid arguments' + if value[0] not in (0,1): + raise ValueError, 'Invalid sign' + for digit in value[1]: + if not isinstance(digit, (int,long)) or digit < 0: + raise ValueError, "The second value in the tuple must be composed of non negative integer elements." + + self._sign = value[0] + self._int = tuple(value[1]) + if value[2] in ('F','n','N'): + self._exp = value[2] + self._is_special = True + else: + self._exp = int(value[2]) + return self + + if isinstance(value, float): + raise TypeError("Cannot convert float to Decimal. " + + "First convert the float to a string") + + # Other argument types may require the context during interpretation + if context is None: + context = getcontext() + + # From a string + # REs insist on real strings, so we can too. + if isinstance(value, basestring): + if _isinfinity(value): + self._exp = 'F' + self._int = (0,) + self._is_special = True + if _isinfinity(value) == 1: + self._sign = 0 + else: + self._sign = 1 + return self + if _isnan(value): + sig, sign, diag = _isnan(value) + self._is_special = True + if len(diag) > context.prec: #Diagnostic info too long + self._sign, self._int, self._exp = \ + context._raise_error(ConversionSyntax) + return self + if sig == 1: + self._exp = 'n' #qNaN + else: #sig == 2 + self._exp = 'N' #sNaN + self._sign = sign + self._int = tuple(map(int, diag)) #Diagnostic info + return self + try: + self._sign, self._int, self._exp = _string2exact(value) + except ValueError: + self._is_special = True + self._sign, self._int, self._exp = context._raise_error(ConversionSyntax) + return self + + raise TypeError("Cannot convert %r to Decimal" % value) + + def _isnan(self): + """Returns whether the number is not actually one. + + 0 if a number + 1 if NaN + 2 if sNaN + """ + if self._is_special: + exp = self._exp + if exp == 'n': + return 1 + elif exp == 'N': + return 2 + return 0 + + def _isinfinity(self): + """Returns whether the number is infinite + + 0 if finite or not a number + 1 if +INF + -1 if -INF + """ + if self._exp == 'F': + if self._sign: + return -1 + return 1 + return 0 + + def _check_nans(self, other = None, context=None): + """Returns whether the number is not actually one. + + if self, other are sNaN, signal + if self, other are NaN return nan + return 0 + + Done before operations. + """ + + self_is_nan = self._isnan() + if other is None: + other_is_nan = False + else: + other_is_nan = other._isnan() + + if self_is_nan or other_is_nan: + if context is None: + context = getcontext() + + if self_is_nan == 2: + return context._raise_error(InvalidOperation, 'sNaN', + 1, self) + if other_is_nan == 2: + return context._raise_error(InvalidOperation, 'sNaN', + 1, other) + if self_is_nan: + return self + + return other + return 0 + + def __nonzero__(self): + """Is the number non-zero? + + 0 if self == 0 + 1 if self != 0 + """ + if self._is_special: + return 1 + return sum(self._int) != 0 + + def __cmp__(self, other, context=None): + other = _convert_other(other) + if other is NotImplemented: + return other + + if self._is_special or other._is_special: + ans = self._check_nans(other, context) + if ans: + return 1 # Comparison involving NaN's always reports self > other + + # INF = INF + return cmp(self._isinfinity(), other._isinfinity()) + + if not self and not other: + return 0 #If both 0, sign comparison isn't certain. + + #If different signs, neg one is less + if other._sign < self._sign: + return -1 + if self._sign < other._sign: + return 1 + + self_adjusted = self.adjusted() + other_adjusted = other.adjusted() + if self_adjusted == other_adjusted and \ + self._int + (0,)*(self._exp - other._exp) == \ + other._int + (0,)*(other._exp - self._exp): + return 0 #equal, except in precision. ([0]*(-x) = []) + elif self_adjusted > other_adjusted and self._int[0] != 0: + return (-1)**self._sign + elif self_adjusted < other_adjusted and other._int[0] != 0: + return -((-1)**self._sign) + + # Need to round, so make sure we have a valid context + if context is None: + context = getcontext() + + context = context._shallow_copy() + rounding = context._set_rounding(ROUND_UP) #round away from 0 + + flags = context._ignore_all_flags() + res = self.__sub__(other, context=context) + + context._regard_flags(*flags) + + context.rounding = rounding + + if not res: + return 0 + elif res._sign: + return -1 + return 1 + + def __eq__(self, other): + if not isinstance(other, (Decimal, int, long)): + return NotImplemented + return self.__cmp__(other) == 0 + + def __ne__(self, other): + if not isinstance(other, (Decimal, int, long)): + return NotImplemented + return self.__cmp__(other) != 0 + + def compare(self, other, context=None): + """Compares one to another. + + -1 => a < b + 0 => a = b + 1 => a > b + NaN => one is NaN + Like __cmp__, but returns Decimal instances. + """ + other = _convert_other(other) + if other is NotImplemented: + return other + + #compare(NaN, NaN) = NaN + if (self._is_special or other and other._is_special): + ans = self._check_nans(other, context) + if ans: + return ans + + return Decimal(self.__cmp__(other, context)) + + def __hash__(self): + """x.__hash__() <==> hash(x)""" + # Decimal integers must hash the same as the ints + # Non-integer decimals are normalized and hashed as strings + # Normalization assures that hast(100E-1) == hash(10) + if self._is_special: + if self._isnan(): + raise TypeError('Cannot hash a NaN value.') + return hash(str(self)) + i = int(self) + if self == Decimal(i): + return hash(i) + assert self.__nonzero__() # '-0' handled by integer case + return hash(str(self.normalize())) + + def as_tuple(self): + """Represents the number as a triple tuple. + + To show the internals exactly as they are. + """ + return (self._sign, self._int, self._exp) + + def __repr__(self): + """Represents the number as an instance of Decimal.""" + # Invariant: eval(repr(d)) == d + return 'Decimal("%s")' % str(self) + + def __str__(self, eng = 0, context=None): + """Return string representation of the number in scientific notation. + + Captures all of the information in the underlying representation. + """ + + if self._is_special: + if self._isnan(): + minus = '-'*self._sign + if self._int == (0,): + info = '' + else: + info = ''.join(map(str, self._int)) + if self._isnan() == 2: + return minus + 'sNaN' + info + return minus + 'NaN' + info + if self._isinfinity(): + minus = '-'*self._sign + return minus + 'Infinity' + + if context is None: + context = getcontext() + + tmp = map(str, self._int) + numdigits = len(self._int) + leftdigits = self._exp + numdigits + if eng and not self: #self = 0eX wants 0[.0[0]]eY, not [[0]0]0eY + if self._exp < 0 and self._exp >= -6: #short, no need for e/E + s = '-'*self._sign + '0.' + '0'*(abs(self._exp)) + return s + #exp is closest mult. of 3 >= self._exp + exp = ((self._exp - 1)// 3 + 1) * 3 + if exp != self._exp: + s = '0.'+'0'*(exp - self._exp) + else: + s = '0' + if exp != 0: + if context.capitals: + s += 'E' + else: + s += 'e' + if exp > 0: + s += '+' #0.0e+3, not 0.0e3 + s += str(exp) + s = '-'*self._sign + s + return s + if eng: + dotplace = (leftdigits-1)%3+1 + adjexp = leftdigits -1 - (leftdigits-1)%3 + else: + adjexp = leftdigits-1 + dotplace = 1 + if self._exp == 0: + pass + elif self._exp < 0 and adjexp >= 0: + tmp.insert(leftdigits, '.') + elif self._exp < 0 and adjexp >= -6: + tmp[0:0] = ['0'] * int(-leftdigits) + tmp.insert(0, '0.') + else: + if numdigits > dotplace: + tmp.insert(dotplace, '.') + elif numdigits < dotplace: + tmp.extend(['0']*(dotplace-numdigits)) + if adjexp: + if not context.capitals: + tmp.append('e') + else: + tmp.append('E') + if adjexp > 0: + tmp.append('+') + tmp.append(str(adjexp)) + if eng: + while tmp[0:1] == ['0']: + tmp[0:1] = [] + if len(tmp) == 0 or tmp[0] == '.' or tmp[0].lower() == 'e': + tmp[0:0] = ['0'] + if self._sign: + tmp.insert(0, '-') + + return ''.join(tmp) + + def to_eng_string(self, context=None): + """Convert to engineering-type string. + + Engineering notation has an exponent which is a multiple of 3, so there + are up to 3 digits left of the decimal place. + + Same rules for when in exponential and when as a value as in __str__. + """ + return self.__str__(eng=1, context=context) + + def __neg__(self, context=None): + """Returns a copy with the sign switched. + + Rounds, if it has reason. + """ + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + + if not self: + # -Decimal('0') is Decimal('0'), not Decimal('-0') + sign = 0 + elif self._sign: + sign = 0 + else: + sign = 1 + + if context is None: + context = getcontext() + if context._rounding_decision == ALWAYS_ROUND: + return Decimal((sign, self._int, self._exp))._fix(context) + return Decimal( (sign, self._int, self._exp)) + + def __pos__(self, context=None): + """Returns a copy, unless it is a sNaN. + + Rounds the number (if more then precision digits) + """ + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + + sign = self._sign + if not self: + # + (-0) = 0 + sign = 0 + + if context is None: + context = getcontext() + + if context._rounding_decision == ALWAYS_ROUND: + ans = self._fix(context) + else: + ans = Decimal(self) + ans._sign = sign + return ans + + def __abs__(self, round=1, context=None): + """Returns the absolute value of self. + + If the second argument is 0, do not round. + """ + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + + if not round: + if context is None: + context = getcontext() + context = context._shallow_copy() + context._set_rounding_decision(NEVER_ROUND) + + if self._sign: + ans = self.__neg__(context=context) + else: + ans = self.__pos__(context=context) + + return ans + + def __add__(self, other, context=None): + """Returns self + other. + + -INF + INF (or the reverse) cause InvalidOperation errors. + """ + other = _convert_other(other) + if other is NotImplemented: + return other + + if context is None: + context = getcontext() + + if self._is_special or other._is_special: + ans = self._check_nans(other, context) + if ans: + return ans + + if self._isinfinity(): + #If both INF, same sign => same as both, opposite => error. + if self._sign != other._sign and other._isinfinity(): + return context._raise_error(InvalidOperation, '-INF + INF') + return Decimal(self) + if other._isinfinity(): + return Decimal(other) #Can't both be infinity here + + shouldround = context._rounding_decision == ALWAYS_ROUND + + exp = min(self._exp, other._exp) + negativezero = 0 + if context.rounding == ROUND_FLOOR and self._sign != other._sign: + #If the answer is 0, the sign should be negative, in this case. + negativezero = 1 + + if not self and not other: + sign = min(self._sign, other._sign) + if negativezero: + sign = 1 + return Decimal( (sign, (0,), exp)) + if not self: + exp = max(exp, other._exp - context.prec-1) + ans = other._rescale(exp, watchexp=0, context=context) + if shouldround: + ans = ans._fix(context) + return ans + if not other: + exp = max(exp, self._exp - context.prec-1) + ans = self._rescale(exp, watchexp=0, context=context) + if shouldround: + ans = ans._fix(context) + return ans + + op1 = _WorkRep(self) + op2 = _WorkRep(other) + op1, op2 = _normalize(op1, op2, shouldround, context.prec) + + result = _WorkRep() + if op1.sign != op2.sign: + # Equal and opposite + if op1.int == op2.int: + if exp < context.Etiny(): + exp = context.Etiny() + context._raise_error(Clamped) + return Decimal((negativezero, (0,), exp)) + if op1.int < op2.int: + op1, op2 = op2, op1 + #OK, now abs(op1) > abs(op2) + if op1.sign == 1: + result.sign = 1 + op1.sign, op2.sign = op2.sign, op1.sign + else: + result.sign = 0 + #So we know the sign, and op1 > 0. + elif op1.sign == 1: + result.sign = 1 + op1.sign, op2.sign = (0, 0) + else: + result.sign = 0 + #Now, op1 > abs(op2) > 0 + + if op2.sign == 0: + result.int = op1.int + op2.int + else: + result.int = op1.int - op2.int + + result.exp = op1.exp + ans = Decimal(result) + if shouldround: + ans = ans._fix(context) + return ans + + __radd__ = __add__ + + def __sub__(self, other, context=None): + """Return self + (-other)""" + other = _convert_other(other) + if other is NotImplemented: + return other + + if self._is_special or other._is_special: + ans = self._check_nans(other, context=context) + if ans: + return ans + + # -Decimal(0) = Decimal(0), which we don't want since + # (-0 - 0 = -0 + (-0) = -0, but -0 + 0 = 0.) + # so we change the sign directly to a copy + tmp = Decimal(other) + tmp._sign = 1-tmp._sign + + return self.__add__(tmp, context=context) + + def __rsub__(self, other, context=None): + """Return other + (-self)""" + other = _convert_other(other) + if other is NotImplemented: + return other + + tmp = Decimal(self) + tmp._sign = 1 - tmp._sign + return other.__add__(tmp, context=context) + + def _increment(self, round=1, context=None): + """Special case of add, adding 1eExponent + + Since it is common, (rounding, for example) this adds + (sign)*one E self._exp to the number more efficiently than add. + + For example: + Decimal('5.624e10')._increment() == Decimal('5.625e10') + """ + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + + return Decimal(self) # Must be infinite, and incrementing makes no difference + + L = list(self._int) + L[-1] += 1 + spot = len(L)-1 + while L[spot] == 10: + L[spot] = 0 + if spot == 0: + L[0:0] = [1] + break + L[spot-1] += 1 + spot -= 1 + ans = Decimal((self._sign, L, self._exp)) + + if context is None: + context = getcontext() + if round and context._rounding_decision == ALWAYS_ROUND: + ans = ans._fix(context) + return ans + + def __mul__(self, other, context=None): + """Return self * other. + + (+-) INF * 0 (or its reverse) raise InvalidOperation. + """ + other = _convert_other(other) + if other is NotImplemented: + return other + + if context is None: + context = getcontext() + + resultsign = self._sign ^ other._sign + + if self._is_special or other._is_special: + ans = self._check_nans(other, context) + if ans: + return ans + + if self._isinfinity(): + if not other: + return context._raise_error(InvalidOperation, '(+-)INF * 0') + return Infsign[resultsign] + + if other._isinfinity(): + if not self: + return context._raise_error(InvalidOperation, '0 * (+-)INF') + return Infsign[resultsign] + + resultexp = self._exp + other._exp + shouldround = context._rounding_decision == ALWAYS_ROUND + + # Special case for multiplying by zero + if not self or not other: + ans = Decimal((resultsign, (0,), resultexp)) + if shouldround: + #Fixing in case the exponent is out of bounds + ans = ans._fix(context) + return ans + + # Special case for multiplying by power of 10 + if self._int == (1,): + ans = Decimal((resultsign, other._int, resultexp)) + if shouldround: + ans = ans._fix(context) + return ans + if other._int == (1,): + ans = Decimal((resultsign, self._int, resultexp)) + if shouldround: + ans = ans._fix(context) + return ans + + op1 = _WorkRep(self) + op2 = _WorkRep(other) + + ans = Decimal( (resultsign, map(int, str(op1.int * op2.int)), resultexp)) + if shouldround: + ans = ans._fix(context) + + return ans + __rmul__ = __mul__ + + def __div__(self, other, context=None): + """Return self / other.""" + return self._divide(other, context=context) + __truediv__ = __div__ + + def _divide(self, other, divmod = 0, context=None): + """Return a / b, to context.prec precision. + + divmod: + 0 => true division + 1 => (a //b, a%b) + 2 => a //b + 3 => a%b + + Actually, if divmod is 2 or 3 a tuple is returned, but errors for + computing the other value are not raised. + """ + other = _convert_other(other) + if other is NotImplemented: + if divmod in (0, 1): + return NotImplemented + return (NotImplemented, NotImplemented) + + if context is None: + context = getcontext() + + sign = self._sign ^ other._sign + + if self._is_special or other._is_special: + ans = self._check_nans(other, context) + if ans: + if divmod: + return (ans, ans) + return ans + + if self._isinfinity() and other._isinfinity(): + if divmod: + return (context._raise_error(InvalidOperation, + '(+-)INF // (+-)INF'), + context._raise_error(InvalidOperation, + '(+-)INF % (+-)INF')) + return context._raise_error(InvalidOperation, '(+-)INF/(+-)INF') + + if self._isinfinity(): + if divmod == 1: + return (Infsign[sign], + context._raise_error(InvalidOperation, 'INF % x')) + elif divmod == 2: + return (Infsign[sign], NaN) + elif divmod == 3: + return (Infsign[sign], + context._raise_error(InvalidOperation, 'INF % x')) + return Infsign[sign] + + if other._isinfinity(): + if divmod: + return (Decimal((sign, (0,), 0)), Decimal(self)) + context._raise_error(Clamped, 'Division by infinity') + return Decimal((sign, (0,), context.Etiny())) + + # Special cases for zeroes + if not self and not other: + if divmod: + return context._raise_error(DivisionUndefined, '0 / 0', 1) + return context._raise_error(DivisionUndefined, '0 / 0') + + if not self: + if divmod: + otherside = Decimal(self) + otherside._exp = min(self._exp, other._exp) + return (Decimal((sign, (0,), 0)), otherside) + exp = self._exp - other._exp + if exp < context.Etiny(): + exp = context.Etiny() + context._raise_error(Clamped, '0e-x / y') + if exp > context.Emax: + exp = context.Emax + context._raise_error(Clamped, '0e+x / y') + return Decimal( (sign, (0,), exp) ) + + if not other: + if divmod: + return context._raise_error(DivisionByZero, 'divmod(x,0)', + sign, 1) + return context._raise_error(DivisionByZero, 'x / 0', sign) + + #OK, so neither = 0, INF or NaN + + shouldround = context._rounding_decision == ALWAYS_ROUND + + #If we're dividing into ints, and self < other, stop. + #self.__abs__(0) does not round. + if divmod and (self.__abs__(0, context) < other.__abs__(0, context)): + + if divmod == 1 or divmod == 3: + exp = min(self._exp, other._exp) + ans2 = self._rescale(exp, context=context, watchexp=0) + if shouldround: + ans2 = ans2._fix(context) + return (Decimal( (sign, (0,), 0) ), + ans2) + + elif divmod == 2: + #Don't round the mod part, if we don't need it. + return (Decimal( (sign, (0,), 0) ), Decimal(self)) + + op1 = _WorkRep(self) + op2 = _WorkRep(other) + op1, op2, adjust = _adjust_coefficients(op1, op2) + res = _WorkRep( (sign, 0, (op1.exp - op2.exp)) ) + if divmod and res.exp > context.prec + 1: + return context._raise_error(DivisionImpossible) + + prec_limit = 10 ** context.prec + while 1: + while op2.int <= op1.int: + res.int += 1 + op1.int -= op2.int + if res.exp == 0 and divmod: + if res.int >= prec_limit and shouldround: + return context._raise_error(DivisionImpossible) + otherside = Decimal(op1) + frozen = context._ignore_all_flags() + + exp = min(self._exp, other._exp) + otherside = otherside._rescale(exp, context=context, watchexp=0) + context._regard_flags(*frozen) + if shouldround: + otherside = otherside._fix(context) + return (Decimal(res), otherside) + + if op1.int == 0 and adjust >= 0 and not divmod: + break + if res.int >= prec_limit and shouldround: + if divmod: + return context._raise_error(DivisionImpossible) + shouldround=1 + # Really, the answer is a bit higher, so adding a one to + # the end will make sure the rounding is right. + if op1.int != 0: + res.int *= 10 + res.int += 1 + res.exp -= 1 + + break + res.int *= 10 + res.exp -= 1 + adjust += 1 + op1.int *= 10 + op1.exp -= 1 + + if res.exp == 0 and divmod and op2.int > op1.int: + #Solves an error in precision. Same as a previous block. + + if res.int >= prec_limit and shouldround: + return context._raise_error(DivisionImpossible) + otherside = Decimal(op1) + frozen = context._ignore_all_flags() + + exp = min(self._exp, other._exp) + otherside = otherside._rescale(exp, context=context) + + context._regard_flags(*frozen) + + return (Decimal(res), otherside) + + ans = Decimal(res) + if shouldround: + ans = ans._fix(context) + return ans + + def __rdiv__(self, other, context=None): + """Swaps self/other and returns __div__.""" + other = _convert_other(other) + if other is NotImplemented: + return other + return other.__div__(self, context=context) + __rtruediv__ = __rdiv__ + + def __divmod__(self, other, context=None): + """ + (self // other, self % other) + """ + return self._divide(other, 1, context) + + def __rdivmod__(self, other, context=None): + """Swaps self/other and returns __divmod__.""" + other = _convert_other(other) + if other is NotImplemented: + return other + return other.__divmod__(self, context=context) + + def __mod__(self, other, context=None): + """ + self % other + """ + other = _convert_other(other) + if other is NotImplemented: + return other + + if self._is_special or other._is_special: + ans = self._check_nans(other, context) + if ans: + return ans + + if self and not other: + return context._raise_error(InvalidOperation, 'x % 0') + + return self._divide(other, 3, context)[1] + + def __rmod__(self, other, context=None): + """Swaps self/other and returns __mod__.""" + other = _convert_other(other) + if other is NotImplemented: + return other + return other.__mod__(self, context=context) + + def remainder_near(self, other, context=None): + """ + Remainder nearest to 0- abs(remainder-near) <= other/2 + """ + other = _convert_other(other) + if other is NotImplemented: + return other + + if self._is_special or other._is_special: + ans = self._check_nans(other, context) + if ans: + return ans + if self and not other: + return context._raise_error(InvalidOperation, 'x % 0') + + if context is None: + context = getcontext() + # If DivisionImpossible causes an error, do not leave Rounded/Inexact + # ignored in the calling function. + context = context._shallow_copy() + flags = context._ignore_flags(Rounded, Inexact) + #keep DivisionImpossible flags + (side, r) = self.__divmod__(other, context=context) + + if r._isnan(): + context._regard_flags(*flags) + return r + + context = context._shallow_copy() + rounding = context._set_rounding_decision(NEVER_ROUND) + + if other._sign: + comparison = other.__div__(Decimal(-2), context=context) + else: + comparison = other.__div__(Decimal(2), context=context) + + context._set_rounding_decision(rounding) + context._regard_flags(*flags) + + s1, s2 = r._sign, comparison._sign + r._sign, comparison._sign = 0, 0 + + if r < comparison: + r._sign, comparison._sign = s1, s2 + #Get flags now + self.__divmod__(other, context=context) + return r._fix(context) + r._sign, comparison._sign = s1, s2 + + rounding = context._set_rounding_decision(NEVER_ROUND) + + (side, r) = self.__divmod__(other, context=context) + context._set_rounding_decision(rounding) + if r._isnan(): + return r + + decrease = not side._iseven() + rounding = context._set_rounding_decision(NEVER_ROUND) + side = side.__abs__(context=context) + context._set_rounding_decision(rounding) + + s1, s2 = r._sign, comparison._sign + r._sign, comparison._sign = 0, 0 + if r > comparison or decrease and r == comparison: + r._sign, comparison._sign = s1, s2 + context.prec += 1 + if len(side.__add__(Decimal(1), context=context)._int) >= context.prec: + context.prec -= 1 + return context._raise_error(DivisionImpossible)[1] + context.prec -= 1 + if self._sign == other._sign: + r = r.__sub__(other, context=context) + else: + r = r.__add__(other, context=context) + else: + r._sign, comparison._sign = s1, s2 + + return r._fix(context) + + def __floordiv__(self, other, context=None): + """self // other""" + return self._divide(other, 2, context)[0] + + def __rfloordiv__(self, other, context=None): + """Swaps self/other and returns __floordiv__.""" + other = _convert_other(other) + if other is NotImplemented: + return other + return other.__floordiv__(self, context=context) + + def __float__(self): + """Float representation.""" + return float(str(self)) + + def __int__(self): + """Converts self to an int, truncating if necessary.""" + if self._is_special: + if self._isnan(): + context = getcontext() + return context._raise_error(InvalidContext) + elif self._isinfinity(): + raise OverflowError, "Cannot convert infinity to long" + if self._exp >= 0: + s = ''.join(map(str, self._int)) + '0'*self._exp + else: + s = ''.join(map(str, self._int))[:self._exp] + if s == '': + s = '0' + sign = '-'*self._sign + return int(sign + s) + + def __long__(self): + """Converts to a long. + + Equivalent to long(int(self)) + """ + return long(self.__int__()) + + def _fix(self, context): + """Round if it is necessary to keep self within prec precision. + + Rounds and fixes the exponent. Does not raise on a sNaN. + + Arguments: + self - Decimal instance + context - context used. + """ + if self._is_special: + return self + if context is None: + context = getcontext() + prec = context.prec + ans = self._fixexponents(context) + if len(ans._int) > prec: + ans = ans._round(prec, context=context) + ans = ans._fixexponents(context) + return ans + + def _fixexponents(self, context): + """Fix the exponents and return a copy with the exponent in bounds. + Only call if known to not be a special value. + """ + folddown = context._clamp + Emin = context.Emin + ans = self + ans_adjusted = ans.adjusted() + if ans_adjusted < Emin: + Etiny = context.Etiny() + if ans._exp < Etiny: + if not ans: + ans = Decimal(self) + ans._exp = Etiny + context._raise_error(Clamped) + return ans + ans = ans._rescale(Etiny, context=context) + #It isn't zero, and exp < Emin => subnormal + context._raise_error(Subnormal) + if context.flags[Inexact]: + context._raise_error(Underflow) + else: + if ans: + #Only raise subnormal if non-zero. + context._raise_error(Subnormal) + else: + Etop = context.Etop() + if folddown and ans._exp > Etop: + context._raise_error(Clamped) + ans = ans._rescale(Etop, context=context) + else: + Emax = context.Emax + if ans_adjusted > Emax: + if not ans: + ans = Decimal(self) + ans._exp = Emax + context._raise_error(Clamped) + return ans + context._raise_error(Inexact) + context._raise_error(Rounded) + return context._raise_error(Overflow, 'above Emax', ans._sign) + return ans + + def _round(self, prec=None, rounding=None, context=None): + """Returns a rounded version of self. + + You can specify the precision or rounding method. Otherwise, the + context determines it. + """ + + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + + if self._isinfinity(): + return Decimal(self) + + if context is None: + context = getcontext() + + if rounding is None: + rounding = context.rounding + if prec is None: + prec = context.prec + + if not self: + if prec <= 0: + dig = (0,) + exp = len(self._int) - prec + self._exp + else: + dig = (0,) * prec + exp = len(self._int) + self._exp - prec + ans = Decimal((self._sign, dig, exp)) + context._raise_error(Rounded) + return ans + + if prec == 0: + temp = Decimal(self) + temp._int = (0,)+temp._int + prec = 1 + elif prec < 0: + exp = self._exp + len(self._int) - prec - 1 + temp = Decimal( (self._sign, (0, 1), exp)) + prec = 1 + else: + temp = Decimal(self) + + numdigits = len(temp._int) + if prec == numdigits: + return temp + + # See if we need to extend precision + expdiff = prec - numdigits + if expdiff > 0: + tmp = list(temp._int) + tmp.extend([0] * expdiff) + ans = Decimal( (temp._sign, tmp, temp._exp - expdiff)) + return ans + + #OK, but maybe all the lost digits are 0. + lostdigits = self._int[expdiff:] + if lostdigits == (0,) * len(lostdigits): + ans = Decimal( (temp._sign, temp._int[:prec], temp._exp - expdiff)) + #Rounded, but not Inexact + context._raise_error(Rounded) + return ans + + # Okay, let's round and lose data + + this_function = getattr(temp, self._pick_rounding_function[rounding]) + #Now we've got the rounding function + + if prec != context.prec: + context = context._shallow_copy() + context.prec = prec + ans = this_function(prec, expdiff, context) + context._raise_error(Rounded) + context._raise_error(Inexact, 'Changed in rounding') + + return ans + + _pick_rounding_function = {} + + def _round_down(self, prec, expdiff, context): + """Also known as round-towards-0, truncate.""" + return Decimal( (self._sign, self._int[:prec], self._exp - expdiff) ) + + def _round_half_up(self, prec, expdiff, context, tmp = None): + """Rounds 5 up (away from 0)""" + + if tmp is None: + tmp = Decimal( (self._sign,self._int[:prec], self._exp - expdiff)) + if self._int[prec] >= 5: + tmp = tmp._increment(round=0, context=context) + if len(tmp._int) > prec: + return Decimal( (tmp._sign, tmp._int[:-1], tmp._exp + 1)) + return tmp + + def _round_half_even(self, prec, expdiff, context): + """Round 5 to even, rest to nearest.""" + + tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff)) + half = (self._int[prec] == 5) + if half: + for digit in self._int[prec+1:]: + if digit != 0: + half = 0 + break + if half: + if self._int[prec-1] & 1 == 0: + return tmp + return self._round_half_up(prec, expdiff, context, tmp) + + def _round_half_down(self, prec, expdiff, context): + """Round 5 down""" + + tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff)) + half = (self._int[prec] == 5) + if half: + for digit in self._int[prec+1:]: + if digit != 0: + half = 0 + break + if half: + return tmp + return self._round_half_up(prec, expdiff, context, tmp) + + def _round_up(self, prec, expdiff, context): + """Rounds away from 0.""" + tmp = Decimal( (self._sign, self._int[:prec], self._exp - expdiff) ) + for digit in self._int[prec:]: + if digit != 0: + tmp = tmp._increment(round=1, context=context) + if len(tmp._int) > prec: + return Decimal( (tmp._sign, tmp._int[:-1], tmp._exp + 1)) + else: + return tmp + return tmp + + def _round_ceiling(self, prec, expdiff, context): + """Rounds up (not away from 0 if negative.)""" + if self._sign: + return self._round_down(prec, expdiff, context) + else: + return self._round_up(prec, expdiff, context) + + def _round_floor(self, prec, expdiff, context): + """Rounds down (not towards 0 if negative)""" + if not self._sign: + return self._round_down(prec, expdiff, context) + else: + return self._round_up(prec, expdiff, context) + + def __pow__(self, n, modulo = None, context=None): + """Return self ** n (mod modulo) + + If modulo is None (default), don't take it mod modulo. + """ + n = _convert_other(n) + if n is NotImplemented: + return n + + if context is None: + context = getcontext() + + if self._is_special or n._is_special or n.adjusted() > 8: + #Because the spot << doesn't work with really big exponents + if n._isinfinity() or n.adjusted() > 8: + return context._raise_error(InvalidOperation, 'x ** INF') + + ans = self._check_nans(n, context) + if ans: + return ans + + if not n._isinteger(): + return context._raise_error(InvalidOperation, 'x ** (non-integer)') + + if not self and not n: + return context._raise_error(InvalidOperation, '0 ** 0') + + if not n: + return Decimal(1) + + if self == Decimal(1): + return Decimal(1) + + sign = self._sign and not n._iseven() + n = int(n) + + if self._isinfinity(): + if modulo: + return context._raise_error(InvalidOperation, 'INF % x') + if n > 0: + return Infsign[sign] + return Decimal( (sign, (0,), 0) ) + + #with ludicrously large exponent, just raise an overflow and return inf. + if not modulo and n > 0 and (self._exp + len(self._int) - 1) * n > context.Emax \ + and self: + + tmp = Decimal('inf') + tmp._sign = sign + context._raise_error(Rounded) + context._raise_error(Inexact) + context._raise_error(Overflow, 'Big power', sign) + return tmp + + elength = len(str(abs(n))) + firstprec = context.prec + + if not modulo and firstprec + elength + 1 > DefaultContext.Emax: + return context._raise_error(Overflow, 'Too much precision.', sign) + + mul = Decimal(self) + val = Decimal(1) + context = context._shallow_copy() + context.prec = firstprec + elength + 1 + if n < 0: + #n is a long now, not Decimal instance + n = -n + mul = Decimal(1).__div__(mul, context=context) + + spot = 1 + while spot <= n: + spot <<= 1 + + spot >>= 1 + #Spot is the highest power of 2 less than n + while spot: + val = val.__mul__(val, context=context) + if val._isinfinity(): + val = Infsign[sign] + break + if spot & n: + val = val.__mul__(mul, context=context) + if modulo is not None: + val = val.__mod__(modulo, context=context) + spot >>= 1 + context.prec = firstprec + + if context._rounding_decision == ALWAYS_ROUND: + return val._fix(context) + return val + + def __rpow__(self, other, context=None): + """Swaps self/other and returns __pow__.""" + other = _convert_other(other) + if other is NotImplemented: + return other + return other.__pow__(self, context=context) + + def normalize(self, context=None): + """Normalize- strip trailing 0s, change anything equal to 0 to 0e0""" + + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + + dup = self._fix(context) + if dup._isinfinity(): + return dup + + if not dup: + return Decimal( (dup._sign, (0,), 0) ) + end = len(dup._int) + exp = dup._exp + while dup._int[end-1] == 0: + exp += 1 + end -= 1 + return Decimal( (dup._sign, dup._int[:end], exp) ) + + + def quantize(self, exp, rounding=None, context=None, watchexp=1): + """Quantize self so its exponent is the same as that of exp. + + Similar to self._rescale(exp._exp) but with error checking. + """ + if self._is_special or exp._is_special: + ans = self._check_nans(exp, context) + if ans: + return ans + + if exp._isinfinity() or self._isinfinity(): + if exp._isinfinity() and self._isinfinity(): + return self #if both are inf, it is OK + if context is None: + context = getcontext() + return context._raise_error(InvalidOperation, + 'quantize with one INF') + return self._rescale(exp._exp, rounding, context, watchexp) + + def same_quantum(self, other): + """Test whether self and other have the same exponent. + + same as self._exp == other._exp, except NaN == sNaN + """ + if self._is_special or other._is_special: + if self._isnan() or other._isnan(): + return self._isnan() and other._isnan() and True + if self._isinfinity() or other._isinfinity(): + return self._isinfinity() and other._isinfinity() and True + return self._exp == other._exp + + def _rescale(self, exp, rounding=None, context=None, watchexp=1): + """Rescales so that the exponent is exp. + + exp = exp to scale to (an integer) + rounding = rounding version + watchexp: if set (default) an error is returned if exp is greater + than Emax or less than Etiny. + """ + if context is None: + context = getcontext() + + if self._is_special: + if self._isinfinity(): + return context._raise_error(InvalidOperation, 'rescale with an INF') + + ans = self._check_nans(context=context) + if ans: + return ans + + if watchexp and (context.Emax < exp or context.Etiny() > exp): + return context._raise_error(InvalidOperation, 'rescale(a, INF)') + + if not self: + ans = Decimal(self) + ans._int = (0,) + ans._exp = exp + return ans + + diff = self._exp - exp + digits = len(self._int) + diff + + if watchexp and digits > context.prec: + return context._raise_error(InvalidOperation, 'Rescale > prec') + + tmp = Decimal(self) + tmp._int = (0,) + tmp._int + digits += 1 + + if digits < 0: + tmp._exp = -digits + tmp._exp + tmp._int = (0,1) + digits = 1 + tmp = tmp._round(digits, rounding, context=context) + + if tmp._int[0] == 0 and len(tmp._int) > 1: + tmp._int = tmp._int[1:] + tmp._exp = exp + + tmp_adjusted = tmp.adjusted() + if tmp and tmp_adjusted < context.Emin: + context._raise_error(Subnormal) + elif tmp and tmp_adjusted > context.Emax: + return context._raise_error(InvalidOperation, 'rescale(a, INF)') + return tmp + + def to_integral(self, rounding=None, context=None): + """Rounds to the nearest integer, without raising inexact, rounded.""" + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + if self._exp >= 0: + return self + if context is None: + context = getcontext() + flags = context._ignore_flags(Rounded, Inexact) + ans = self._rescale(0, rounding, context=context) + context._regard_flags(flags) + return ans + + def sqrt(self, context=None): + """Return the square root of self. + + Uses a converging algorithm (Xn+1 = 0.5*(Xn + self / Xn)) + Should quadratically approach the right answer. + """ + if self._is_special: + ans = self._check_nans(context=context) + if ans: + return ans + + if self._isinfinity() and self._sign == 0: + return Decimal(self) + + if not self: + #exponent = self._exp / 2, using round_down. + #if self._exp < 0: + # exp = (self._exp+1) // 2 + #else: + exp = (self._exp) // 2 + if self._sign == 1: + #sqrt(-0) = -0 + return Decimal( (1, (0,), exp)) + else: + return Decimal( (0, (0,), exp)) + + if context is None: + context = getcontext() + + if self._sign == 1: + return context._raise_error(InvalidOperation, 'sqrt(-x), x > 0') + + tmp = Decimal(self) + + expadd = tmp._exp // 2 + if tmp._exp & 1: + tmp._int += (0,) + tmp._exp = 0 + else: + tmp._exp = 0 + + context = context._shallow_copy() + flags = context._ignore_all_flags() + firstprec = context.prec + context.prec = 3 + if tmp.adjusted() & 1 == 0: + ans = Decimal( (0, (8,1,9), tmp.adjusted() - 2) ) + ans = ans.__add__(tmp.__mul__(Decimal((0, (2,5,9), -2)), + context=context), context=context) + ans._exp -= 1 + tmp.adjusted() // 2 + else: + ans = Decimal( (0, (2,5,9), tmp._exp + len(tmp._int)- 3) ) + ans = ans.__add__(tmp.__mul__(Decimal((0, (8,1,9), -3)), + context=context), context=context) + ans._exp -= 1 + tmp.adjusted() // 2 + + #ans is now a linear approximation. + + Emax, Emin = context.Emax, context.Emin + context.Emax, context.Emin = DefaultContext.Emax, DefaultContext.Emin + + half = Decimal('0.5') + + maxp = firstprec + 2 + rounding = context._set_rounding(ROUND_HALF_EVEN) + while 1: + context.prec = min(2*context.prec - 2, maxp) + ans = half.__mul__(ans.__add__(tmp.__div__(ans, context=context), + context=context), context=context) + if context.prec == maxp: + break + + #round to the answer's precision-- the only error can be 1 ulp. + context.prec = firstprec + prevexp = ans.adjusted() + ans = ans._round(context=context) + + #Now, check if the other last digits are better. + context.prec = firstprec + 1 + # In case we rounded up another digit and we should actually go lower. + if prevexp != ans.adjusted(): + ans._int += (0,) + ans._exp -= 1 + + + lower = ans.__sub__(Decimal((0, (5,), ans._exp-1)), context=context) + context._set_rounding(ROUND_UP) + if lower.__mul__(lower, context=context) > (tmp): + ans = ans.__sub__(Decimal((0, (1,), ans._exp)), context=context) + + else: + upper = ans.__add__(Decimal((0, (5,), ans._exp-1)),context=context) + context._set_rounding(ROUND_DOWN) + if upper.__mul__(upper, context=context) < tmp: + ans = ans.__add__(Decimal((0, (1,), ans._exp)),context=context) + + ans._exp += expadd + + context.prec = firstprec + context.rounding = rounding + ans = ans._fix(context) + + rounding = context._set_rounding_decision(NEVER_ROUND) + if not ans.__mul__(ans, context=context) == self: + # Only rounded/inexact if here. + context._regard_flags(flags) + context._raise_error(Rounded) + context._raise_error(Inexact) + else: + #Exact answer, so let's set the exponent right. + #if self._exp < 0: + # exp = (self._exp +1)// 2 + #else: + exp = self._exp // 2 + context.prec += ans._exp - exp + ans = ans._rescale(exp, context=context) + context.prec = firstprec + context._regard_flags(flags) + context.Emax, context.Emin = Emax, Emin + + return ans._fix(context) + + def max(self, other, context=None): + """Returns the larger value. + + like max(self, other) except if one is not a number, returns + NaN (and signals if one is sNaN). Also rounds. + """ + other = _convert_other(other) + if other is NotImplemented: + return other + + if self._is_special or other._is_special: + # if one operand is a quiet NaN and the other is number, then the + # number is always returned + sn = self._isnan() + on = other._isnan() + if sn or on: + if on == 1 and sn != 2: + return self + if sn == 1 and on != 2: + return other + return self._check_nans(other, context) + + ans = self + c = self.__cmp__(other) + if c == 0: + # if both operands are finite and equal in numerical value + # then an ordering is applied: + # + # if the signs differ then max returns the operand with the + # positive sign and min returns the operand with the negative sign + # + # if the signs are the same then the exponent is used to select + # the result. + if self._sign != other._sign: + if self._sign: + ans = other + elif self._exp < other._exp and not self._sign: + ans = other + elif self._exp > other._exp and self._sign: + ans = other + elif c == -1: + ans = other + + if context is None: + context = getcontext() + if context._rounding_decision == ALWAYS_ROUND: + return ans._fix(context) + return ans + + def min(self, other, context=None): + """Returns the smaller value. + + like min(self, other) except if one is not a number, returns + NaN (and signals if one is sNaN). Also rounds. + """ + other = _convert_other(other) + if other is NotImplemented: + return other + + if self._is_special or other._is_special: + # if one operand is a quiet NaN and the other is number, then the + # number is always returned + sn = self._isnan() + on = other._isnan() + if sn or on: + if on == 1 and sn != 2: + return self + if sn == 1 and on != 2: + return other + return self._check_nans(other, context) + + ans = self + c = self.__cmp__(other) + if c == 0: + # if both operands are finite and equal in numerical value + # then an ordering is applied: + # + # if the signs differ then max returns the operand with the + # positive sign and min returns the operand with the negative sign + # + # if the signs are the same then the exponent is used to select + # the result. + if self._sign != other._sign: + if other._sign: + ans = other + elif self._exp > other._exp and not self._sign: + ans = other + elif self._exp < other._exp and self._sign: + ans = other + elif c == 1: + ans = other + + if context is None: + context = getcontext() + if context._rounding_decision == ALWAYS_ROUND: + return ans._fix(context) + return ans + + def _isinteger(self): + """Returns whether self is an integer""" + if self._exp >= 0: + return True + rest = self._int[self._exp:] + return rest == (0,)*len(rest) + + def _iseven(self): + """Returns 1 if self is even. Assumes self is an integer.""" + if self._exp > 0: + return 1 + return self._int[-1+self._exp] & 1 == 0 + + def adjusted(self): + """Return the adjusted exponent of self""" + try: + return self._exp + len(self._int) - 1 + #If NaN or Infinity, self._exp is string + except TypeError: + return 0 + + # support for pickling, copy, and deepcopy + def __reduce__(self): + return (self.__class__, (str(self),)) + + def __copy__(self): + if type(self) == Decimal: + return self # I'm immutable; therefore I am my own clone + return self.__class__(str(self)) + + def __deepcopy__(self, memo): + if type(self) == Decimal: + return self # My components are also immutable + return self.__class__(str(self)) + +##### Context class ########################################### + + +# get rounding method function: +rounding_functions = [name for name in Decimal.__dict__.keys() if name.startswith('_round_')] +for name in rounding_functions: + #name is like _round_half_even, goes to the global ROUND_HALF_EVEN value. + globalname = name[1:].upper() + val = globals()[globalname] + Decimal._pick_rounding_function[val] = name + +del name, val, globalname, rounding_functions + +class Context(object): + """Contains the context for a Decimal instance. + + Contains: + prec - precision (for use in rounding, division, square roots..) + rounding - rounding type. (how you round) + _rounding_decision - ALWAYS_ROUND, NEVER_ROUND -- do you round? + traps - If traps[exception] = 1, then the exception is + raised when it is caused. Otherwise, a value is + substituted in. + flags - When an exception is caused, flags[exception] is incremented. + (Whether or not the trap_enabler is set) + Should be reset by user of Decimal instance. + Emin - Minimum exponent + Emax - Maximum exponent + capitals - If 1, 1*10^1 is printed as 1E+1. + If 0, printed as 1e1 + _clamp - If 1, change exponents if too high (Default 0) + """ + + def __init__(self, prec=None, rounding=None, + traps=None, flags=None, + _rounding_decision=None, + Emin=None, Emax=None, + capitals=None, _clamp=0, + _ignored_flags=None): + if flags is None: + flags = [] + if _ignored_flags is None: + _ignored_flags = [] + if not isinstance(flags, dict): + flags = dict([(s,s in flags) for s in _signals]) + del s + if traps is not None and not isinstance(traps, dict): + traps = dict([(s,s in traps) for s in _signals]) + del s + for name, val in locals().items(): + if val is None: + setattr(self, name, _copy.copy(getattr(DefaultContext, name))) + else: + setattr(self, name, val) + del self.self + + def __repr__(self): + """Show the current context.""" + s = [] + s.append('Context(prec=%(prec)d, rounding=%(rounding)s, Emin=%(Emin)d, Emax=%(Emax)d, capitals=%(capitals)d' % vars(self)) + s.append('flags=[' + ', '.join([f.__name__ for f, v in self.flags.items() if v]) + ']') + s.append('traps=[' + ', '.join([t.__name__ for t, v in self.traps.items() if v]) + ']') + return ', '.join(s) + ')' + + def clear_flags(self): + """Reset all flags to zero""" + for flag in self.flags: + self.flags[flag] = 0 + + def _shallow_copy(self): + """Returns a shallow copy from self.""" + nc = Context(self.prec, self.rounding, self.traps, self.flags, + self._rounding_decision, self.Emin, self.Emax, + self.capitals, self._clamp, self._ignored_flags) + return nc + + def copy(self): + """Returns a deep copy from self.""" + nc = Context(self.prec, self.rounding, self.traps.copy(), self.flags.copy(), + self._rounding_decision, self.Emin, self.Emax, + self.capitals, self._clamp, self._ignored_flags) + return nc + __copy__ = copy + + def _raise_error(self, condition, explanation = None, *args): + """Handles an error + + If the flag is in _ignored_flags, returns the default response. + Otherwise, it increments the flag, then, if the corresponding + trap_enabler is set, it reaises the exception. Otherwise, it returns + the default value after incrementing the flag. + """ + error = _condition_map.get(condition, condition) + if error in self._ignored_flags: + #Don't touch the flag + return error().handle(self, *args) + + self.flags[error] += 1 + if not self.traps[error]: + #The errors define how to handle themselves. + return condition().handle(self, *args) + + # Errors should only be risked on copies of the context + #self._ignored_flags = [] + raise error, explanation + + def _ignore_all_flags(self): + """Ignore all flags, if they are raised""" + return self._ignore_flags(*_signals) + + def _ignore_flags(self, *flags): + """Ignore the flags, if they are raised""" + # Do not mutate-- This way, copies of a context leave the original + # alone. + self._ignored_flags = (self._ignored_flags + list(flags)) + return list(flags) + + def _regard_flags(self, *flags): + """Stop ignoring the flags, if they are raised""" + if flags and isinstance(flags[0], (tuple,list)): + flags = flags[0] + for flag in flags: + self._ignored_flags.remove(flag) + + def __hash__(self): + """A Context cannot be hashed.""" + # We inherit object.__hash__, so we must deny this explicitly + raise TypeError, "Cannot hash a Context." + + def Etiny(self): + """Returns Etiny (= Emin - prec + 1)""" + return int(self.Emin - self.prec + 1) + + def Etop(self): + """Returns maximum exponent (= Emax - prec + 1)""" + return int(self.Emax - self.prec + 1) + + def _set_rounding_decision(self, type): + """Sets the rounding decision. + + Sets the rounding decision, and returns the current (previous) + rounding decision. Often used like: + + context = context._shallow_copy() + # That so you don't change the calling context + # if an error occurs in the middle (say DivisionImpossible is raised). + + rounding = context._set_rounding_decision(NEVER_ROUND) + instance = instance / Decimal(2) + context._set_rounding_decision(rounding) + + This will make it not round for that operation. + """ + + rounding = self._rounding_decision + self._rounding_decision = type + return rounding + + def _set_rounding(self, type): + """Sets the rounding type. + + Sets the rounding type, and returns the current (previous) + rounding type. Often used like: + + context = context.copy() + # so you don't change the calling context + # if an error occurs in the middle. + rounding = context._set_rounding(ROUND_UP) + val = self.__sub__(other, context=context) + context._set_rounding(rounding) + + This will make it round up for that operation. + """ + rounding = self.rounding + self.rounding= type + return rounding + + def create_decimal(self, num='0'): + """Creates a new Decimal instance but using self as context.""" + d = Decimal(num, context=self) + return d._fix(self) + + #Methods + def abs(self, a): + """Returns the absolute value of the operand. + + If the operand is negative, the result is the same as using the minus + operation on the operand. Otherwise, the result is the same as using + the plus operation on the operand. + + >>> ExtendedContext.abs(Decimal('2.1')) + Decimal("2.1") + >>> ExtendedContext.abs(Decimal('-100')) + Decimal("100") + >>> ExtendedContext.abs(Decimal('101.5')) + Decimal("101.5") + >>> ExtendedContext.abs(Decimal('-101.5')) + Decimal("101.5") + """ + return a.__abs__(context=self) + + def add(self, a, b): + """Return the sum of the two operands. + + >>> ExtendedContext.add(Decimal('12'), Decimal('7.00')) + Decimal("19.00") + >>> ExtendedContext.add(Decimal('1E+2'), Decimal('1.01E+4')) + Decimal("1.02E+4") + """ + return a.__add__(b, context=self) + + def _apply(self, a): + return str(a._fix(self)) + + def compare(self, a, b): + """Compares values numerically. + + If the signs of the operands differ, a value representing each operand + ('-1' if the operand is less than zero, '0' if the operand is zero or + negative zero, or '1' if the operand is greater than zero) is used in + place of that operand for the comparison instead of the actual + operand. + + The comparison is then effected by subtracting the second operand from + the first and then returning a value according to the result of the + subtraction: '-1' if the result is less than zero, '0' if the result is + zero or negative zero, or '1' if the result is greater than zero. + + >>> ExtendedContext.compare(Decimal('2.1'), Decimal('3')) + Decimal("-1") + >>> ExtendedContext.compare(Decimal('2.1'), Decimal('2.1')) + Decimal("0") + >>> ExtendedContext.compare(Decimal('2.1'), Decimal('2.10')) + Decimal("0") + >>> ExtendedContext.compare(Decimal('3'), Decimal('2.1')) + Decimal("1") + >>> ExtendedContext.compare(Decimal('2.1'), Decimal('-3')) + Decimal("1") + >>> ExtendedContext.compare(Decimal('-3'), Decimal('2.1')) + Decimal("-1") + """ + return a.compare(b, context=self) + + def divide(self, a, b): + """Decimal division in a specified context. + + >>> ExtendedContext.divide(Decimal('1'), Decimal('3')) + Decimal("0.333333333") + >>> ExtendedContext.divide(Decimal('2'), Decimal('3')) + Decimal("0.666666667") + >>> ExtendedContext.divide(Decimal('5'), Decimal('2')) + Decimal("2.5") + >>> ExtendedContext.divide(Decimal('1'), Decimal('10')) + Decimal("0.1") + >>> ExtendedContext.divide(Decimal('12'), Decimal('12')) + Decimal("1") + >>> ExtendedContext.divide(Decimal('8.00'), Decimal('2')) + Decimal("4.00") + >>> ExtendedContext.divide(Decimal('2.400'), Decimal('2.0')) + Decimal("1.20") + >>> ExtendedContext.divide(Decimal('1000'), Decimal('100')) + Decimal("10") + >>> ExtendedContext.divide(Decimal('1000'), Decimal('1')) + Decimal("1000") + >>> ExtendedContext.divide(Decimal('2.40E+6'), Decimal('2')) + Decimal("1.20E+6") + """ + return a.__div__(b, context=self) + + def divide_int(self, a, b): + """Divides two numbers and returns the integer part of the result. + + >>> ExtendedContext.divide_int(Decimal('2'), Decimal('3')) + Decimal("0") + >>> ExtendedContext.divide_int(Decimal('10'), Decimal('3')) + Decimal("3") + >>> ExtendedContext.divide_int(Decimal('1'), Decimal('0.3')) + Decimal("3") + """ + return a.__floordiv__(b, context=self) + + def divmod(self, a, b): + return a.__divmod__(b, context=self) + + def max(self, a,b): + """max compares two values numerically and returns the maximum. + + If either operand is a NaN then the general rules apply. + Otherwise, the operands are compared as as though by the compare + operation. If they are numerically equal then the left-hand operand + is chosen as the result. Otherwise the maximum (closer to positive + infinity) of the two operands is chosen as the result. + + >>> ExtendedContext.max(Decimal('3'), Decimal('2')) + Decimal("3") + >>> ExtendedContext.max(Decimal('-10'), Decimal('3')) + Decimal("3") + >>> ExtendedContext.max(Decimal('1.0'), Decimal('1')) + Decimal("1") + >>> ExtendedContext.max(Decimal('7'), Decimal('NaN')) + Decimal("7") + """ + return a.max(b, context=self) + + def min(self, a,b): + """min compares two values numerically and returns the minimum. + + If either operand is a NaN then the general rules apply. + Otherwise, the operands are compared as as though by the compare + operation. If they are numerically equal then the left-hand operand + is chosen as the result. Otherwise the minimum (closer to negative + infinity) of the two operands is chosen as the result. + + >>> ExtendedContext.min(Decimal('3'), Decimal('2')) + Decimal("2") + >>> ExtendedContext.min(Decimal('-10'), Decimal('3')) + Decimal("-10") + >>> ExtendedContext.min(Decimal('1.0'), Decimal('1')) + Decimal("1.0") + >>> ExtendedContext.min(Decimal('7'), Decimal('NaN')) + Decimal("7") + """ + return a.min(b, context=self) + + def minus(self, a): + """Minus corresponds to unary prefix minus in Python. + + The operation is evaluated using the same rules as subtract; the + operation minus(a) is calculated as subtract('0', a) where the '0' + has the same exponent as the operand. + + >>> ExtendedContext.minus(Decimal('1.3')) + Decimal("-1.3") + >>> ExtendedContext.minus(Decimal('-1.3')) + Decimal("1.3") + """ + return a.__neg__(context=self) + + def multiply(self, a, b): + """multiply multiplies two operands. + + If either operand is a special value then the general rules apply. + Otherwise, the operands are multiplied together ('long multiplication'), + resulting in a number which may be as long as the sum of the lengths + of the two operands. + + >>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3')) + Decimal("3.60") + >>> ExtendedContext.multiply(Decimal('7'), Decimal('3')) + Decimal("21") + >>> ExtendedContext.multiply(Decimal('0.9'), Decimal('0.8')) + Decimal("0.72") + >>> ExtendedContext.multiply(Decimal('0.9'), Decimal('-0')) + Decimal("-0.0") + >>> ExtendedContext.multiply(Decimal('654321'), Decimal('654321')) + Decimal("4.28135971E+11") + """ + return a.__mul__(b, context=self) + + def normalize(self, a): + """normalize reduces an operand to its simplest form. + + Essentially a plus operation with all trailing zeros removed from the + result. + + >>> ExtendedContext.normalize(Decimal('2.1')) + Decimal("2.1") + >>> ExtendedContext.normalize(Decimal('-2.0')) + Decimal("-2") + >>> ExtendedContext.normalize(Decimal('1.200')) + Decimal("1.2") + >>> ExtendedContext.normalize(Decimal('-120')) + Decimal("-1.2E+2") + >>> ExtendedContext.normalize(Decimal('120.00')) + Decimal("1.2E+2") + >>> ExtendedContext.normalize(Decimal('0.00')) + Decimal("0") + """ + return a.normalize(context=self) + + def plus(self, a): + """Plus corresponds to unary prefix plus in Python. + + The operation is evaluated using the same rules as add; the + operation plus(a) is calculated as add('0', a) where the '0' + has the same exponent as the operand. + + >>> ExtendedContext.plus(Decimal('1.3')) + Decimal("1.3") + >>> ExtendedContext.plus(Decimal('-1.3')) + Decimal("-1.3") + """ + return a.__pos__(context=self) + + def power(self, a, b, modulo=None): + """Raises a to the power of b, to modulo if given. + + The right-hand operand must be a whole number whose integer part (after + any exponent has been applied) has no more than 9 digits and whose + fractional part (if any) is all zeros before any rounding. The operand + may be positive, negative, or zero; if negative, the absolute value of + the power is used, and the left-hand operand is inverted (divided into + 1) before use. + + If the increased precision needed for the intermediate calculations + exceeds the capabilities of the implementation then an Invalid operation + condition is raised. + + If, when raising to a negative power, an underflow occurs during the + division into 1, the operation is not halted at that point but + continues. + + >>> ExtendedContext.power(Decimal('2'), Decimal('3')) + Decimal("8") + >>> ExtendedContext.power(Decimal('2'), Decimal('-3')) + Decimal("0.125") + >>> ExtendedContext.power(Decimal('1.7'), Decimal('8')) + Decimal("69.7575744") + >>> ExtendedContext.power(Decimal('Infinity'), Decimal('-2')) + Decimal("0") + >>> ExtendedContext.power(Decimal('Infinity'), Decimal('-1')) + Decimal("0") + >>> ExtendedContext.power(Decimal('Infinity'), Decimal('0')) + Decimal("1") + >>> ExtendedContext.power(Decimal('Infinity'), Decimal('1')) + Decimal("Infinity") + >>> ExtendedContext.power(Decimal('Infinity'), Decimal('2')) + Decimal("Infinity") + >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('-2')) + Decimal("0") + >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('-1')) + Decimal("-0") + >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('0')) + Decimal("1") + >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('1')) + Decimal("-Infinity") + >>> ExtendedContext.power(Decimal('-Infinity'), Decimal('2')) + Decimal("Infinity") + >>> ExtendedContext.power(Decimal('0'), Decimal('0')) + Decimal("NaN") + """ + return a.__pow__(b, modulo, context=self) + + def quantize(self, a, b): + """Returns a value equal to 'a' (rounded) and having the exponent of 'b'. + + The coefficient of the result is derived from that of the left-hand + operand. It may be rounded using the current rounding setting (if the + exponent is being increased), multiplied by a positive power of ten (if + the exponent is being decreased), or is unchanged (if the exponent is + already equal to that of the right-hand operand). + + Unlike other operations, if the length of the coefficient after the + quantize operation would be greater than precision then an Invalid + operation condition is raised. This guarantees that, unless there is an + error condition, the exponent of the result of a quantize is always + equal to that of the right-hand operand. + + Also unlike other operations, quantize will never raise Underflow, even + if the result is subnormal and inexact. + + >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.001')) + Decimal("2.170") + >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.01')) + Decimal("2.17") + >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('0.1')) + Decimal("2.2") + >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('1e+0')) + Decimal("2") + >>> ExtendedContext.quantize(Decimal('2.17'), Decimal('1e+1')) + Decimal("0E+1") + >>> ExtendedContext.quantize(Decimal('-Inf'), Decimal('Infinity')) + Decimal("-Infinity") + >>> ExtendedContext.quantize(Decimal('2'), Decimal('Infinity')) + Decimal("NaN") + >>> ExtendedContext.quantize(Decimal('-0.1'), Decimal('1')) + Decimal("-0") + >>> ExtendedContext.quantize(Decimal('-0'), Decimal('1e+5')) + Decimal("-0E+5") + >>> ExtendedContext.quantize(Decimal('+35236450.6'), Decimal('1e-2')) + Decimal("NaN") + >>> ExtendedContext.quantize(Decimal('-35236450.6'), Decimal('1e-2')) + Decimal("NaN") + >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e-1')) + Decimal("217.0") + >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e-0')) + Decimal("217") + >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e+1')) + Decimal("2.2E+2") + >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e+2')) + Decimal("2E+2") + """ + return a.quantize(b, context=self) + + def remainder(self, a, b): + """Returns the remainder from integer division. + + The result is the residue of the dividend after the operation of + calculating integer division as described for divide-integer, rounded to + precision digits if necessary. The sign of the result, if non-zero, is + the same as that of the original dividend. + + This operation will fail under the same conditions as integer division + (that is, if integer division on the same two operands would fail, the + remainder cannot be calculated). + + >>> ExtendedContext.remainder(Decimal('2.1'), Decimal('3')) + Decimal("2.1") + >>> ExtendedContext.remainder(Decimal('10'), Decimal('3')) + Decimal("1") + >>> ExtendedContext.remainder(Decimal('-10'), Decimal('3')) + Decimal("-1") + >>> ExtendedContext.remainder(Decimal('10.2'), Decimal('1')) + Decimal("0.2") + >>> ExtendedContext.remainder(Decimal('10'), Decimal('0.3')) + Decimal("0.1") + >>> ExtendedContext.remainder(Decimal('3.6'), Decimal('1.3')) + Decimal("1.0") + """ + return a.__mod__(b, context=self) + + def remainder_near(self, a, b): + """Returns to be "a - b * n", where n is the integer nearest the exact + value of "x / b" (if two integers are equally near then the even one + is chosen). If the result is equal to 0 then its sign will be the + sign of a. + + This operation will fail under the same conditions as integer division + (that is, if integer division on the same two operands would fail, the + remainder cannot be calculated). + + >>> ExtendedContext.remainder_near(Decimal('2.1'), Decimal('3')) + Decimal("-0.9") + >>> ExtendedContext.remainder_near(Decimal('10'), Decimal('6')) + Decimal("-2") + >>> ExtendedContext.remainder_near(Decimal('10'), Decimal('3')) + Decimal("1") + >>> ExtendedContext.remainder_near(Decimal('-10'), Decimal('3')) + Decimal("-1") + >>> ExtendedContext.remainder_near(Decimal('10.2'), Decimal('1')) + Decimal("0.2") + >>> ExtendedContext.remainder_near(Decimal('10'), Decimal('0.3')) + Decimal("0.1") + >>> ExtendedContext.remainder_near(Decimal('3.6'), Decimal('1.3')) + Decimal("-0.3") + """ + return a.remainder_near(b, context=self) + + def same_quantum(self, a, b): + """Returns True if the two operands have the same exponent. + + The result is never affected by either the sign or the coefficient of + either operand. + + >>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('0.001')) + False + >>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('0.01')) + True + >>> ExtendedContext.same_quantum(Decimal('2.17'), Decimal('1')) + False + >>> ExtendedContext.same_quantum(Decimal('Inf'), Decimal('-Inf')) + True + """ + return a.same_quantum(b) + + def sqrt(self, a): + """Returns the square root of a non-negative number to context precision. + + If the result must be inexact, it is rounded using the round-half-even + algorithm. + + >>> ExtendedContext.sqrt(Decimal('0')) + Decimal("0") + >>> ExtendedContext.sqrt(Decimal('-0')) + Decimal("-0") + >>> ExtendedContext.sqrt(Decimal('0.39')) + Decimal("0.624499800") + >>> ExtendedContext.sqrt(Decimal('100')) + Decimal("10") + >>> ExtendedContext.sqrt(Decimal('1')) + Decimal("1") + >>> ExtendedContext.sqrt(Decimal('1.0')) + Decimal("1.0") + >>> ExtendedContext.sqrt(Decimal('1.00')) + Decimal("1.0") + >>> ExtendedContext.sqrt(Decimal('7')) + Decimal("2.64575131") + >>> ExtendedContext.sqrt(Decimal('10')) + Decimal("3.16227766") + >>> ExtendedContext.prec + 9 + """ + return a.sqrt(context=self) + + def subtract(self, a, b): + """Return the difference between the two operands. + + >>> ExtendedContext.subtract(Decimal('1.3'), Decimal('1.07')) + Decimal("0.23") + >>> ExtendedContext.subtract(Decimal('1.3'), Decimal('1.30')) + Decimal("0.00") + >>> ExtendedContext.subtract(Decimal('1.3'), Decimal('2.07')) + Decimal("-0.77") + """ + return a.__sub__(b, context=self) + + def to_eng_string(self, a): + """Converts a number to a string, using scientific notation. + + The operation is not affected by the context. + """ + return a.to_eng_string(context=self) + + def to_sci_string(self, a): + """Converts a number to a string, using scientific notation. + + The operation is not affected by the context. + """ + return a.__str__(context=self) + + def to_integral(self, a): + """Rounds to an integer. + + When the operand has a negative exponent, the result is the same + as using the quantize() operation using the given operand as the + left-hand-operand, 1E+0 as the right-hand-operand, and the precision + of the operand as the precision setting, except that no flags will + be set. The rounding mode is taken from the context. + + >>> ExtendedContext.to_integral(Decimal('2.1')) + Decimal("2") + >>> ExtendedContext.to_integral(Decimal('100')) + Decimal("100") + >>> ExtendedContext.to_integral(Decimal('100.0')) + Decimal("100") + >>> ExtendedContext.to_integral(Decimal('101.5')) + Decimal("102") + >>> ExtendedContext.to_integral(Decimal('-101.5')) + Decimal("-102") + >>> ExtendedContext.to_integral(Decimal('10E+5')) + Decimal("1.0E+6") + >>> ExtendedContext.to_integral(Decimal('7.89E+77')) + Decimal("7.89E+77") + >>> ExtendedContext.to_integral(Decimal('-Inf')) + Decimal("-Infinity") + """ + return a.to_integral(context=self) + +class _WorkRep(object): + __slots__ = ('sign','int','exp') + # sign: 0 or 1 + # int: int or long + # exp: None, int, or string + + def __init__(self, value=None): + if value is None: + self.sign = None + self.int = 0 + self.exp = None + elif isinstance(value, Decimal): + self.sign = value._sign + cum = 0 + for digit in value._int: + cum = cum * 10 + digit + self.int = cum + self.exp = value._exp + else: + # assert isinstance(value, tuple) + self.sign = value[0] + self.int = value[1] + self.exp = value[2] + + def __repr__(self): + return "(%r, %r, %r)" % (self.sign, self.int, self.exp) + + __str__ = __repr__ + + + +def _normalize(op1, op2, shouldround = 0, prec = 0): + """Normalizes op1, op2 to have the same exp and length of coefficient. + + Done during addition. + """ + # Yes, the exponent is a long, but the difference between exponents + # must be an int-- otherwise you'd get a big memory problem. + numdigits = int(op1.exp - op2.exp) + if numdigits < 0: + numdigits = -numdigits + tmp = op2 + other = op1 + else: + tmp = op1 + other = op2 + + + if shouldround and numdigits > prec + 1: + # Big difference in exponents - check the adjusted exponents + tmp_len = len(str(tmp.int)) + other_len = len(str(other.int)) + if numdigits > (other_len + prec + 1 - tmp_len): + # If the difference in adjusted exps is > prec+1, we know + # other is insignificant, so might as well put a 1 after the precision. + # (since this is only for addition.) Also stops use of massive longs. + + extend = prec + 2 - tmp_len + if extend <= 0: + extend = 1 + tmp.int *= 10 ** extend + tmp.exp -= extend + other.int = 1 + other.exp = tmp.exp + return op1, op2 + + tmp.int *= 10 ** numdigits + tmp.exp -= numdigits + return op1, op2 + +def _adjust_coefficients(op1, op2): + """Adjust op1, op2 so that op2.int * 10 > op1.int >= op2.int. + + Returns the adjusted op1, op2 as well as the change in op1.exp-op2.exp. + + Used on _WorkRep instances during division. + """ + adjust = 0 + #If op1 is smaller, make it larger + while op2.int > op1.int: + op1.int *= 10 + op1.exp -= 1 + adjust += 1 + + #If op2 is too small, make it larger + while op1.int >= (10 * op2.int): + op2.int *= 10 + op2.exp -= 1 + adjust -= 1 + + return op1, op2, adjust + +##### Helper Functions ######################################## + +def _convert_other(other): + """Convert other to Decimal. + + Verifies that it's ok to use in an implicit construction. + """ + if isinstance(other, Decimal): + return other + if isinstance(other, (int, long)): + return Decimal(other) + return NotImplemented + +_infinity_map = { + 'inf' : 1, + 'infinity' : 1, + '+inf' : 1, + '+infinity' : 1, + '-inf' : -1, + '-infinity' : -1 +} + +def _isinfinity(num): + """Determines whether a string or float is infinity. + + +1 for negative infinity; 0 for finite ; +1 for positive infinity + """ + num = str(num).lower() + return _infinity_map.get(num, 0) + +def _isnan(num): + """Determines whether a string or float is NaN + + (1, sign, diagnostic info as string) => NaN + (2, sign, diagnostic info as string) => sNaN + 0 => not a NaN + """ + num = str(num).lower() + if not num: + return 0 + + #get the sign, get rid of trailing [+-] + sign = 0 + if num[0] == '+': + num = num[1:] + elif num[0] == '-': #elif avoids '+-nan' + num = num[1:] + sign = 1 + + if num.startswith('nan'): + if len(num) > 3 and not num[3:].isdigit(): #diagnostic info + return 0 + return (1, sign, num[3:].lstrip('0')) + if num.startswith('snan'): + if len(num) > 4 and not num[4:].isdigit(): + return 0 + return (2, sign, num[4:].lstrip('0')) + return 0 + + +##### Setup Specific Contexts ################################ + +# The default context prototype used by Context() +# Is mutable, so that new contexts can have different default values + +DefaultContext = Context( + prec=28, rounding=ROUND_HALF_EVEN, + traps=[DivisionByZero, Overflow, InvalidOperation], + flags=[], + _rounding_decision=ALWAYS_ROUND, + Emax=999999999, + Emin=-999999999, + capitals=1 +) + +# Pre-made alternate contexts offered by the specification +# Don't change these; the user should be able to select these +# contexts and be able to reproduce results from other implementations +# of the spec. + +BasicContext = Context( + prec=9, rounding=ROUND_HALF_UP, + traps=[DivisionByZero, Overflow, InvalidOperation, Clamped, Underflow], + flags=[], +) + +ExtendedContext = Context( + prec=9, rounding=ROUND_HALF_EVEN, + traps=[], + flags=[], +) + + +##### Useful Constants (internal use only) #################### + +#Reusable defaults +Inf = Decimal('Inf') +negInf = Decimal('-Inf') + +#Infsign[sign] is infinity w/ that sign +Infsign = (Inf, negInf) + +NaN = Decimal('NaN') + + +##### crud for parsing strings ################################# +import re + +# There's an optional sign at the start, and an optional exponent +# at the end. The exponent has an optional sign and at least one +# digit. In between, must have either at least one digit followed +# by an optional fraction, or a decimal point followed by at least +# one digit. Yuck. + +_parser = re.compile(r""" +# \s* + (?P[-+])? + ( + (?P\d+) (\. (?P\d*))? + | + \. (?P\d+) + ) + ([eE](?P[-+]? \d+))? +# \s* + $ +""", re.VERBOSE).match #Uncomment the \s* to allow leading or trailing spaces. + +del re + +# return sign, n, p s.t. float string value == -1**sign * n * 10**p exactly + +def _string2exact(s): + m = _parser(s) + if m is None: + raise ValueError("invalid literal for Decimal: %r" % s) + + if m.group('sign') == "-": + sign = 1 + else: + sign = 0 + + exp = m.group('exp') + if exp is None: + exp = 0 + else: + exp = int(exp) + + intpart = m.group('int') + if intpart is None: + intpart = "" + fracpart = m.group('onlyfrac') + else: + fracpart = m.group('frac') + if fracpart is None: + fracpart = "" + + exp -= len(fracpart) + + mantissa = intpart + fracpart + tmp = map(int, mantissa) + backup = tmp + while tmp and tmp[0] == 0: + del tmp[0] + + # It's a zero + if not tmp: + if backup: + return (sign, tuple(backup), exp) + return (sign, (0,), exp) + mantissa = tuple(tmp) + + return (sign, mantissa, exp) + + +if __name__ == '__main__': + import doctest, sys + doctest.testmod(sys.modules[__name__]) diff --git a/docs/forms.txt b/docs/forms.txt index 329e84a1b1..f6cb55a3f6 100644 --- a/docs/forms.txt +++ b/docs/forms.txt @@ -567,6 +567,7 @@ check for the given property: * isValidANSIDate * isValidANSITime * isValidEmail + * isValidFloat * isValidImage * isValidImageURL * isValidPhone @@ -664,10 +665,10 @@ fails. If no message is passed in, a default message is used. Takes an integer argument and when called as a validator, checks that the field being validated is a power of the integer. -``IsValidFloat`` +``IsValidDecimal`` Takes a maximum number of digits and number of decimal places (in that - order) and validates whether the field is a float with less than the - maximum number of digits and decimal place. + order) and validates whether the field is a decimal with no more than the + maximum number of digits and decimal places. ``MatchesRegularExpression`` Takes a regular expression (a string) as a parameter and validates the diff --git a/docs/model-api.txt b/docs/model-api.txt index 1125d2810f..ae1c37fc8f 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -184,6 +184,35 @@ A date and time field. Takes the same extra options as ``DateField``. The admin represents this as two ```` fields, with JavaScript shortcuts. +``DecimalField`` +~~~~~~~~~~~~~~~~ + +**New in Django development version** + +A fixed-precision decimal number, represented in Python by a ``Decimal`` instance. +Has two **required** arguments: + + ====================== =================================================== + Argument Description + ====================== =================================================== + ``max_digits`` The maximum number of digits allowed in the number. + + ``decimal_places`` The number of decimal places to store with the + number. + ====================== =================================================== + +For example, to store numbers up to 999 with a resolution of 2 decimal places, +you'd use:: + + models.DecimalField(..., max_digits=5, decimal_places=2) + +And to store numbers up to approximately one billion with a resolution of 10 +decimal places:: + + models.DecimalField(..., max_digits=19, decimal_places=10) + +The admin represents this as an ```` (a single-line input). + ``EmailField`` ~~~~~~~~~~~~~~ @@ -290,29 +319,17 @@ because the ``match`` applies to the base filename (``foo.gif`` and ``FloatField`` ~~~~~~~~~~~~~~ -A floating-point number. Has two **required** arguments: +**Changed in Django development version** - ====================== =================================================== - Argument Description - ====================== =================================================== - ``max_digits`` The maximum number of digits allowed in the number. - - ``decimal_places`` The number of decimal places to store with the - number. - ====================== =================================================== - -For example, to store numbers up to 999 with a resolution of 2 decimal places, -you'd use:: - - models.FloatField(..., max_digits=5, decimal_places=2) - -And to store numbers up to approximately one billion with a resolution of 10 -decimal places:: - - models.FloatField(..., max_digits=19, decimal_places=10) +A floating-point number represented in Python by a ``float`` instance. The admin represents this as an ```` (a single-line input). +**NOTE:** The semantics of ``FloatField`` have changed in the Django +development version. See the `Django 0.96 documentation`_ for the old behavior. + +.. _Django 0.96 documentation: http://www.djangoproject.com/documentation/0.96/model-api/#floatfield + ``ImageField`` ~~~~~~~~~~~~~~ @@ -743,7 +760,7 @@ relationship should work. All are optional: ``limit_choices_to`` A dictionary of lookup arguments and values (see the `Database API reference`_) that limit the available admin choices for this object. Use this - with functions from the Python ``datetime`` module + with functions from the Python ``datetime`` module to limit choices of objects by date. For example:: limit_choices_to = {'pub_date__lte': datetime.now} diff --git a/docs/newforms.txt b/docs/newforms.txt index 5516f60683..7ec4e9560c 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -1253,10 +1253,11 @@ the full list of conversions: ``CommaSeparatedIntegerField`` ``CharField`` ``DateField`` ``DateField`` ``DateTimeField`` ``DateTimeField`` + ``DecimalField`` ``DecimalField`` ``EmailField`` ``EmailField`` ``FileField`` ``CharField`` ``FilePathField`` ``CharField`` - ``FloatField`` ``CharField`` + ``FloatField`` ``FloatField`` ``ForeignKey`` ``ModelChoiceField`` (see below) ``ImageField`` ``CharField`` ``IntegerField`` ``IntegerField`` @@ -1281,6 +1282,11 @@ the full list of conversions: ``XMLField`` ``CharField`` with ``widget=Textarea`` =============================== ======================================== + +.. note:: + The ``FloatField`` form field and ``DecimalField`` model and form fields + are new in the development version. + As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field types are special cases: diff --git a/docs/tutorial02.txt b/docs/tutorial02.txt index 6e4b0ea35e..99f586b4a1 100644 --- a/docs/tutorial02.txt +++ b/docs/tutorial02.txt @@ -320,7 +320,7 @@ method a ``short_description`` attribute:: Let's add another improvement to the Poll change list page: Filters. Add the -following line to ``Poll.admin``:: +following line to ``Poll.Admin``:: list_filter = ['pub_date'] diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py index af54ec3d35..90f2f54632 100644 --- a/tests/modeltests/invalid_models/models.py +++ b/tests/modeltests/invalid_models/models.py @@ -8,7 +8,7 @@ from django.db import models class FieldErrors(models.Model): charfield = models.CharField() - floatfield = models.FloatField() + decimalfield = models.DecimalField() filefield = models.FileField() prepopulate = models.CharField(maxlength=10, prepopulate_from='bad') choices = models.CharField(maxlength=10, choices='bad') @@ -87,10 +87,10 @@ class SelfClashM2M(models.Model): src_safe = models.CharField(maxlength=10) selfclashm2m = models.CharField(maxlength=10) - # Non-symmetrical M2M fields _do_ have related accessors, so + # Non-symmetrical M2M fields _do_ have related accessors, so # there is potential for clashes. selfclashm2m_set = models.ManyToManyField("SelfClashM2M", symmetrical=False) - + m2m_1 = models.ManyToManyField("SelfClashM2M", related_name='id', symmetrical=False) m2m_2 = models.ManyToManyField("SelfClashM2M", related_name='src_safe', symmetrical=False) @@ -108,8 +108,8 @@ class Car(models.Model): model = models.ForeignKey(Model) model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "maxlength" attribute. -invalid_models.fielderrors: "floatfield": FloatFields require a "decimal_places" attribute. -invalid_models.fielderrors: "floatfield": FloatFields require a "max_digits" attribute. +invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute. +invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute. invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute. invalid_models.fielderrors: "prepopulate": prepopulate_from should be a list or tuple. invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list). diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 027371d606..8d219dc0a1 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -8,6 +8,10 @@ form_tests = r""" >>> import datetime >>> import time >>> import re +>>> try: +... from decimal import Decimal +... except ImportError: +... from django.utils._decimal import Decimal ########### # Widgets # @@ -1047,6 +1051,133 @@ Traceback (most recent call last): ... ValidationError: [u'Ensure this value is less than or equal to 20.'] +# FloatField ################################################################## + +>>> f = FloatField() +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('1') +1.0 +>>> isinstance(f.clean('1'), float) +True +>>> f.clean('23') +23.0 +>>> f.clean('3.14') +3.1400000000000001 +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('1.0 ') +1.0 +>>> f.clean(' 1.0') +1.0 +>>> f.clean(' 1.0 ') +1.0 +>>> f.clean('1.0a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] + +>>> f = FloatField(required=False) +>>> f.clean('') + +>>> f.clean(None) + +>>> f.clean('1') +1.0 + +FloatField accepts min_value and max_value just like IntegerField: +>>> f = FloatField(max_value=1.5, min_value=0.5) + +>>> f.clean('1.6') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 1.5.'] +>>> f.clean('0.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] +>>> f.clean('1.5') +1.5 +>>> f.clean('0.5') +0.5 + +# DecimalField ################################################################ + +>>> f = DecimalField(max_digits=4, decimal_places=2) +>>> f.clean('') +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean(None) +Traceback (most recent call last): +... +ValidationError: [u'This field is required.'] +>>> f.clean('1') +Decimal("1") +>>> isinstance(f.clean('1'), Decimal) +True +>>> f.clean('23') +Decimal("23") +>>> f.clean('3.14') +Decimal("3.14") +>>> f.clean('a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('1.0 ') +Decimal("1.0") +>>> f.clean(' 1.0') +Decimal("1.0") +>>> f.clean(' 1.0 ') +Decimal("1.0") +>>> f.clean('1.0a') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] +>>> f.clean('123.45') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 4 digits in total.'] +>>> f.clean('1.234') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 2 decimal places.'] +>>> f.clean('123.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure that there are no more than 2 digits before the decimal point.'] +>>> f = DecimalField(max_digits=4, decimal_places=2, required=False) +>>> f.clean('') + +>>> f.clean(None) + +>>> f.clean('1') +Decimal("1") + +DecimalField accepts min_value and max_value just like IntegerField: +>>> f = DecimalField(max_digits=4, decimal_places=2, max_value=Decimal('1.5'), min_value=Decimal('0.5')) + +>>> f.clean('1.6') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is less than or equal to 1.5.'] +>>> f.clean('0.4') +Traceback (most recent call last): +... +ValidationError: [u'Ensure this value is greater than or equal to 0.5.'] +>>> f.clean('1.5') +Decimal("1.5") +>>> f.clean('0.5') +Decimal("0.5") + # DateField ################################################################### >>> import datetime diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py index fea5c94cab..999b79ccaf 100644 --- a/tests/regressiontests/serializers_regress/models.py +++ b/tests/regressiontests/serializers_regress/models.py @@ -1,7 +1,7 @@ """ A test spanning all the capabilities of all the serializers. -This class sets up a model for each model field type +This class sets up a model for each model field type (except for image types, because of the PIL dependency). """ @@ -9,12 +9,12 @@ from django.db import models from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType -# The following classes are for testing basic data +# The following classes are for testing basic data # marshalling, including NULL values. class BooleanData(models.Model): data = models.BooleanField(null=True) - + class CharData(models.Model): data = models.CharField(maxlength=30, null=True) @@ -24,6 +24,9 @@ class DateData(models.Model): class DateTimeData(models.Model): data = models.DateTimeField(null=True) +class DecimalData(models.Model): + data = models.DecimalField(null=True, decimal_places=3, max_digits=5) + class EmailData(models.Model): data = models.EmailField(null=True) @@ -34,7 +37,7 @@ class FilePathData(models.Model): data = models.FilePathField(null=True) class FloatData(models.Model): - data = models.FloatField(null=True, decimal_places=3, max_digits=5) + data = models.FloatField(null=True) class IntegerData(models.Model): data = models.IntegerField(null=True) @@ -145,6 +148,9 @@ class CharPKData(models.Model): # class DateTimePKData(models.Model): # data = models.DateTimeField(primary_key=True) +class DecimalPKData(models.Model): + data = models.DecimalField(primary_key=True, decimal_places=3, max_digits=5) + class EmailPKData(models.Model): data = models.EmailField(primary_key=True) @@ -155,7 +161,7 @@ class FilePathPKData(models.Model): data = models.FilePathField(primary_key=True) class FloatPKData(models.Model): - data = models.FloatField(primary_key=True, decimal_places=3, max_digits=5) + data = models.FloatField(primary_key=True) class IntegerPKData(models.Model): data = models.IntegerField(primary_key=True) diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py index 317739dac4..5df4c6818a 100644 --- a/tests/regressiontests/serializers_regress/tests.py +++ b/tests/regressiontests/serializers_regress/tests.py @@ -2,7 +2,7 @@ A test spanning all the capabilities of all the serializers. This class defines sample data and a dynamically generated -test case that is capable of testing the capabilities of +test case that is capable of testing the capabilities of the serializers. This includes all valid data values, plus forward, backwards and self references. """ @@ -16,13 +16,17 @@ from django.db import transaction from django.core import management from models import * +try: + import decimal +except ImportError: + from django.utils import _decimal as decimal # A set of functions that can be used to recreate # test data objects of various kinds def data_create(pk, klass, data): instance = klass(id=pk) instance.data = data - instance.save() + instance.save() return instance def generic_create(pk, klass, data): @@ -32,13 +36,13 @@ def generic_create(pk, klass, data): for tag in data[1:]: instance.tags.create(data=tag) return instance - + def fk_create(pk, klass, data): instance = klass(id=pk) setattr(instance, 'data_id', data) instance.save() return instance - + def m2m_create(pk, klass, data): instance = klass(id=pk) instance.save() @@ -61,14 +65,14 @@ def pk_create(pk, klass, data): # test data objects of various kinds def data_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) - testcase.assertEqual(data, instance.data, + testcase.assertEqual(data, instance.data, "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % (pk,data, type(data), instance.data, type(instance.data))) def generic_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data[0], instance.data) testcase.assertEqual(data[1:], [t.data for t in instance.tags.all()]) - + def fk_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data, instance.data_id) @@ -84,7 +88,7 @@ def o2o_compare(testcase, pk, klass, data): def pk_compare(testcase, pk, klass, data): instance = klass.objects.get(data=data) testcase.assertEqual(data, instance.data) - + # Define some data types. Each data type is # actually a pair of functions; one to create # and one to compare objects of that type @@ -96,7 +100,7 @@ o2o_obj = (o2o_create, o2o_compare) pk_obj = (pk_create, pk_compare) test_data = [ - # Format: (data type, PK value, Model Class, data) + # Format: (data type, PK value, Model Class, data) (data_obj, 1, BooleanData, True), (data_obj, 2, BooleanData, False), (data_obj, 10, CharData, "Test Char Data"), @@ -115,10 +119,14 @@ test_data = [ (data_obj, 51, FileData, None), (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"), (data_obj, 61, FilePathData, None), - (data_obj, 70, FloatData, 12.345), - (data_obj, 71, FloatData, -12.345), - (data_obj, 72, FloatData, 0.0), - (data_obj, 73, FloatData, None), + (data_obj, 70, DecimalData, decimal.Decimal('12.345')), + (data_obj, 71, DecimalData, decimal.Decimal('-12.345')), + (data_obj, 72, DecimalData, decimal.Decimal('0.0')), + (data_obj, 73, DecimalData, None), + (data_obj, 74, FloatData, 12.345), + (data_obj, 75, FloatData, -12.345), + (data_obj, 76, FloatData, 0.0), + (data_obj, 77, FloatData, None), (data_obj, 80, IntegerData, 123456789), (data_obj, 81, IntegerData, -123456789), (data_obj, 82, IntegerData, 0), @@ -137,10 +145,10 @@ test_data = [ (data_obj, 131, PositiveSmallIntegerData, None), (data_obj, 140, SlugData, "this-is-a-slug"), (data_obj, 141, SlugData, None), - (data_obj, 150, SmallData, 12), - (data_obj, 151, SmallData, -12), - (data_obj, 152, SmallData, 0), - (data_obj, 153, SmallData, None), + (data_obj, 150, SmallData, 12), + (data_obj, 151, SmallData, -12), + (data_obj, 152, SmallData, 0), + (data_obj, 153, SmallData, None), (data_obj, 160, TextData, """This is a long piece of text. It contains line breaks. Several of them. @@ -188,7 +196,7 @@ The end."""), (fk_obj, 450, FKDataToField, "UAnchor 1"), (fk_obj, 451, FKDataToField, "UAnchor 2"), (fk_obj, 452, FKDataToField, None), - + (data_obj, 500, Anchor, "Anchor 3"), (data_obj, 501, Anchor, "Anchor 4"), (data_obj, 502, UniqueAnchor, "UAnchor 2"), @@ -201,9 +209,12 @@ The end."""), (pk_obj, 640, EmailPKData, "hovercraft@example.com"), (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'), (pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"), - (pk_obj, 670, FloatPKData, 12.345), - (pk_obj, 671, FloatPKData, -12.345), - (pk_obj, 672, FloatPKData, 0.0), + (pk_obj, 670, DecimalPKData, decimal.Decimal('12.345')), + (pk_obj, 671, DecimalPKData, decimal.Decimal('-12.345')), + (pk_obj, 672, DecimalPKData, decimal.Decimal('0.0')), + (pk_obj, 673, FloatPKData, 12.345), + (pk_obj, 674, FloatPKData, -12.345), + (pk_obj, 675, FloatPKData, 0.0), (pk_obj, 680, IntegerPKData, 123456789), (pk_obj, 681, IntegerPKData, -123456789), (pk_obj, 682, IntegerPKData, 0), @@ -215,9 +226,9 @@ The end."""), (pk_obj, 720, PositiveIntegerPKData, 123456789), (pk_obj, 730, PositiveSmallIntegerPKData, 12), (pk_obj, 740, SlugPKData, "this-is-a-slug"), - (pk_obj, 750, SmallPKData, 12), - (pk_obj, 751, SmallPKData, -12), - (pk_obj, 752, SmallPKData, 0), + (pk_obj, 750, SmallPKData, 12), + (pk_obj, 751, SmallPKData, -12), + (pk_obj, 752, SmallPKData, 0), # (pk_obj, 760, TextPKData, """This is a long piece of text. # It contains line breaks. # Several of them. @@ -226,7 +237,7 @@ The end."""), (pk_obj, 780, USStatePKData, "MA"), # (pk_obj, 790, XMLPKData, ""), ] - + # Dynamically create serializer tests to ensure that all # registered serializers are automatically tested. class SerializerTests(unittest.TestCase): @@ -234,7 +245,7 @@ class SerializerTests(unittest.TestCase): def serializerTest(format, self): # Clear the database first - management.flush(verbosity=0, interactive=False) + management.flush(verbosity=0, interactive=False) # Create all the objects defined in the test data objects = [] @@ -245,14 +256,14 @@ def serializerTest(format, self): transaction.commit() transaction.leave_transaction_management() - # Add the generic tagged objects to the object list + # Add the generic tagged objects to the object list objects.extend(Tag.objects.all()) - + # Serialize the test database serialized_data = serializers.serialize(format, objects, indent=2) # Flush the database and recreate from the serialized data - management.flush(verbosity=0, interactive=False) + management.flush(verbosity=0, interactive=False) transaction.enter_transaction_management() transaction.managed(True) for obj in serializers.deserialize(format, serialized_data): @@ -260,10 +271,10 @@ def serializerTest(format, self): transaction.commit() transaction.leave_transaction_management() - # Assert that the deserialized data is the same + # Assert that the deserialized data is the same # as the original source for (func, pk, klass, datum) in test_data: func[1](self, pk, klass, datum) - + for format in serializers.get_serializer_formats(): setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format))