From 659d99b7afda730180e80ea4d68a7d505b21ba86 Mon Sep 17 00:00:00 2001 From: Joseph Kocherhans Date: Thu, 24 May 2007 04:00:19 +0000 Subject: [PATCH] newforms-admin: Merged to [5325] git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5326 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 2 +- django/conf/locale/ca/LC_MESSAGES/django.mo | Bin 31909 -> 48325 bytes django/conf/locale/ca/LC_MESSAGES/django.po | 3479 ++++++++++------- django/conf/locale/ca/LC_MESSAGES/djangojs.mo | Bin 1520 -> 1556 bytes django/conf/locale/ca/LC_MESSAGES/djangojs.po | 17 +- django/conf/locale/te/LC_MESSAGES/django.mo | Bin 35037 -> 35163 bytes django/conf/locale/te/LC_MESSAGES/django.po | 51 +- .../contrib/admin/templatetags/admin_list.py | 10 +- django/contrib/sessions/middleware.py | 3 + django/contrib/sessions/tests.py | 19 + django/core/management.py | 8 +- django/core/serializers/json.py | 19 +- django/core/validators.py | 28 +- django/db/backends/ado_mssql/creation.py | 3 +- django/db/backends/mysql/base.py | 4 +- django/db/backends/mysql/creation.py | 3 +- django/db/backends/mysql/introspection.py | 2 +- django/db/backends/mysql_old/base.py | 1 + django/db/backends/mysql_old/creation.py | 3 +- django/db/backends/mysql_old/introspection.py | 2 +- django/db/backends/oracle/creation.py | 3 +- django/db/backends/oracle/introspection.py | 2 +- django/db/backends/postgresql/base.py | 1 + django/db/backends/postgresql/creation.py | 3 +- .../db/backends/postgresql/introspection.py | 3 +- .../postgresql_psycopg2/introspection.py | 3 +- django/db/backends/sqlite3/base.py | 7 + django/db/backends/sqlite3/creation.py | 3 +- django/db/backends/util.py | 15 + django/db/models/fields/__init__.py | 73 +- django/newforms/fields.py | 68 +- django/oldforms/__init__.py | 30 +- django/utils/_decimal.py | 3079 +++++++++++++++ docs/forms.txt | 7 +- docs/model-api.txt | 57 +- docs/newforms.txt | 8 +- docs/tutorial02.txt | 2 +- tests/modeltests/invalid_models/models.py | 10 +- tests/regressiontests/forms/tests.py | 131 + .../serializers_regress/models.py | 16 +- .../serializers_regress/tests.py | 71 +- 41 files changed, 5671 insertions(+), 1575 deletions(-) create mode 100644 django/contrib/sessions/tests.py create mode 100644 django/utils/_decimal.py 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 00cc135c2d7dd7151c917b59e71252c4c359d700..ca03991f2b10e7772f2a8f44277ca3489d0ffae7 100644 GIT binary patch literal 48325 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�^{49qhLjeV{%-3OSyva0W$7}P7t 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 cE~S|{VQrNqN{BFw;ZX9mpAyLL%S;R8Ry_i^UCh zQ3pW*Z8uu6ZEYRfp|$Nzr=4i4fGxI7)K+?I=XdU{(9nP8`+svk&i&P?s#|sH)T!l# z*S>G^$oDD954$wmrFgccDAgMV+bPwjiBd;giL3%C>I1;@Y#piI#RFew)Pm4vvm z=|H7=!p=}!o(GwBH4REfE1@)84aMN3Dc=G6pdW&ShdK^h!ZUCQJPXA$3v!fVtSSh} zQaT6!%iJ9$LmK!Bc847XDMcKq0dNSM0r{ua@)FDUL22kHEQEi8Ghu43?+F{WLa&6Q zVFb2@kHI$ZFcgD+nv4HyNc@%zanI5 zeiv8*yTb)=smVV8WhxIt8R&bk9ZY^eLOMJNWAH1O4FiLf8VR?-5%5(gBmW-C`u!1N zgKEnBh${=BG&}>!Naw@$@CqoNTM1<#A(#d?K%Aabdq{L3<5eiGdKbz_{u9a+d}Q>` zp&0rXC=LG&N(?k70I*r*L5ZbhP&(LQ^7lb$=QmIc{Ltv1!Va?jza-I-f*+tnfBT`n zK?7kr`Y0%d&4+E_GQ%n;hQy$_awEJH-U64xw_!2N#;V0|1r$#mgBJV)+$`(=EQw8U z<8Xh2XokP}R^>n$*%k0Qcr)ArJ2Ed7a1WG@{|+a^R-^oZ%z@&mE1`I{5=zGjD4w_l zc7}UkQZgPVAs-wz6<&Zc1+PNcLf?Smi9bT|z~7*Fr0HmXqiPTNr~2{AfR{swmGw~S zT?eK8?NIiWT~G`^G#dYRA@LF!V(B|@4y=cgKbY;RE1U>r?iN95a5)qYRKaoZEhrtd zD)JAZPEhLSL&+Zw#qe=Z1~v`KRLw5J|2;`;BjYOgC={2s9P4i=BO!67HW?m)5?r5{ z^1nl7MNP#DqFJqnatb~UX-d5g=fafn{)RRmN_hfqhPNk4EF_VRIjkrZfrNp&4<3f^ zK{0U41i#}4VR!WBp~T2Lkm*;=ij~TOW1%cR8!mxCI32zQ7sHNh4$@B*l>H#NheTfz z&%=T6_mDnS%0z!EilMlC2b5U36N(ETgfhpE8XkZ$RY#z>{1-3-9*0fg87S?13#D9@ zdaER`FNf0bR_JvI#ie&bvG_G8LGul)f=wo|G~r5E2_J;=U2B$t7}^zzfxV5M z0h^-dLRq$hVXmzI@g(|?;X;|4Es#Fc!*Dq~37KX!Ws2|GO>hAEb|_QxG!)MrhZ1Dp z88(~h2Vo~D9e0Pa?0OkJUr7I|kc3442q>0Mf-T`}qc1f2l~6oT3B8R6N{1VvxPCj7 z?;n8D;lDwtcM!^VN1@bz6H0%-fys^}J~0JfL-|lm^GDhYic32hrkngsIDq^DC=D-! z(os2V4y$1cSOZ(Z^-zLxv*B$}+P!BQ{+EJ%WQZY$pcwFq;cJF(Lb3d9C>{O*%6FeZ zY3Q$}{O?9@Io)rkvtcT1NxgI^Bku=ge;G0z|BLHN$&iK@LkYqF6hmCt8iowlK+ok+ z%D2N-@HQw0-DS!jf#Q)vP?qbUh;YWslPMX9whCdp%p5cGc)vyngja44<3jZ^gB9{2vg% zhq48ao$K#-LD&oZJ}6u9i!cb^g|d7n%=0_E0!k2;!;a8}Vn7{i3$KUGWc}Z2D(r%? zV?6>TNZx>A`R|~7@JGlVqfS8?dBJ?Yd>HJ9J_d@5mqGcy0``DWD68dWD4sfOcoepi z_5V5v8Og7q%;D!!0M%u_#qFWEz6V?l2f%cAhbcb{<@=w)PVhZw!B3$W-eQ4oa2F_g zcPO68gh}aW5D6LSC@8KP4?DxzP%OU6l)F$y9)U8qaVR5QZ}e?Y+S>(XYkm+)^uG^f zYR;JQj%EIt)4L4+%ho&AWGsc!a4nQM-U#Kxy)YZT0JGr7P(09zqf*f(gtCQaEb_PND`6)3mPPn~Fp2$SNW~AJbkuRNea1wkJN^E=qYvB)2+D+D6?iZ|sJ;>M&#h`sq3^)L# z;$bKTK4;30K^B7g1)K$YEc5F-P^Mrdl-RfliU;n15>p4^K6ngXBJ02E3jes>0=XNg zx1e;~fwNcG70ML!fPA1PLovjG(qPCi37eqbX7rt~H~Ky(bNwn5gHAvh;J46Q|38or zS2elP@31YD4m!cEum_X|@?b8U2PJ0K!7lI#I0(KBrJc{AbkG3Bb7x^+C>Kmw#+N{8 zHy@@ZNtBSV;9}Sg)<=q#-{70! z1L)7fTj5eZ8UjCua{pAlD*W}j6iUV0-~l)+;QwXgBPesY$l(?QgHU4S2oytq2_@>^ zhw1QhD4u9m>93;pP&}Foo5EpG%12c)|IJAhlhF%Kfzm)Zlm^#9>0p~FzY9)8{|USV zeh(!^(yROi`$B0i8}@_wP^M@u6obp5w7(Wc;I68q@6zTg{EFRSO9}=-v2Z9%z_D;D zJY@2}hkekSxPCngiXk~rMpyvFkfE>`j)s}A4z7ZG;YipzxzhikQ49;o*anBgm!MeO z09j$G9bZY%Rlxyp50s#K2~LB5goJ|{5cGGr%}_l0B-{z#g3`{_A;04nVJUj@42h#8 zMuxE-Ho$JM5B}*1hr+RNHoO$x1oPn=unqhc(u7K>_8W{q;z}KXgJ9ZfpCwSHBnV}I zw?GU?s#i%YA){r~Z}4i^0(}(}mqwwua*ff~!_MfNpiI?HC>`Ac#r3~1`ENln@Yir5 z{18ga43GKkm%*0wua=Pziz*E7fSu6ahImzd2*k4u%a-49r~P zdtx{g1E#l_-bO-3vKxy20F;XRVORJPl##p(Q{l%@ zJn$Wq4pM5(DuPn4JCv<98_GyW!fx;~C{tGj<-59C{4X7@CqqWG1xf>VKr!GkI1s*Q z%0Dsr-$03tR(1Z!+r#$gU7-x5ACz|U3`d&sVkiTc1I2(q9sZZZ3X>6p(s4DE5nKo5 zgWI75+haz53d)FHhw}XghW`b{Gk=2Z;1{q5{2t0`>T->5SU!~RMkP&RER+r>7|wy> z`sJp4HIz9^Kv^9dp^WfGC@#Gn_JS7!=RE3uWp)fCOz))sv8D{R~P& zKR_9Imv#Q!4uH~N9uyA^gAywfp%}6NieWC4DXKB$*Fx!Vhbg}U%Iavr>sz=U4oG4C zkE48y%tsoZTnaZx?em@}`k?djzM@PFmgiF+-c3l7tWeJzCR^<$QHrcU4kC55BgWn? z>whQ;@uBWQzCq+kn2yqYRox6%ke2~}f)pcnAR+R)B225g8u=|E&o0D8icC9_HwU@q zJpDWLd}IpdQI>$^@F+ZO3frT%MS3ESkSBwb zX9n^#as-JWKb4|?kmyfEdBz%E0aKCA$SZ!4YD0PxBFkw6Y<#{Z@o&gplkx9Reoy9^ zIt!o#vOItC;r%uUk-kApn@Oi`Zv_$zfBnO#@$P=bao?*ySh(!AD z5qY+lvNZSvdM)y(6qx67IEJ#%;k)9R7)rS*98da6M4ogw$)tN5u7s5)Ez5Eh={LOE zTrx-xM5d$v(d3n&pCEl1TxRlaHT-Xwj8Gt_(ms3jG~KHkf?$OjwAF zM5@qTM4rut`$_*DS!eVOq~+;|o()UkAE1+h|Hn%S&t~KbBq$1x{D-7G!{8d^dD6x3 zT9f|+`VizrL>^h|@?3?ircQHH?>abHg#3l@m+&K`TI$LCPbX70m`TWO$VT)p;7HgM zk*6mz6Ujyon!K14@Z5l&2X8{&Mkb;!LhdD(BBlY7-rEbq#r`snYPD~ zP8Om#rcyuBhe%H_y5JZxmwX#JDH%L7Nz2pH@J86lq+jrhyuXrcA^jQhED}d9HFfey zmmzP;{x45^?|tE`9qMz)9+SNhS~PH<(HpVeB$^w3XgCI5iBuTFXUy?_aOHpr;vrnSVU1KPXqF# zm*UlCT(wmZv}3XH87q#2hTW8++l_cse=%a0zJJu(v>H3;2CRjb&9dB>6?Il8+^7>M z(9Y=07EZv*iWTUGMvv%H9d(1MB0Gsnc5q9jn@ouh3hk?Hd}f z>(o?wt8neGn(9PDf{2KkSi>tD@UqswMz>UWWsW-ru{IZOLFYv?!@TN8-}iqxD4 z|Ex;a2?kBv!gT9YF;1a3&tS5~&vJ1Z~I z%d7h6uPbNiEmfJRG4WfO8*;o?kt&Ojp?{=C{}o4@#;|(2Tco$T1#Mjh9SK`8H|`vH z$IVSSGIiDV6rC2Dpq-GVw}sx;SB3kvjK<ioP-9_Va@{YnJf&W5NOW7?U6|}9O8+L3f zYoIVE##~#cVpbw-+3~1bo`_pD^(?YTfi-YeW8I2K$O)g&#SeGWR!!QNi+^uhPTXCc zaN@S#f^AwjExn!B!uPf9`ehA_l&^Fu;&g9^$}OAEn3k-8UW|%gb<;JI+ga64l*|N^ zoTFFQrVlT{%=5ar=x~CrRcQw?f)Pcm&?)^=ZO^uz#pi5D)mB|2ph_w#+y*_X zE~hyLuz~EV>z_Omc0-+yG0B052pMnaZd zZii!eV)z*=!de!8SJzj$m>tNr1H>6iJ8ILV%&d$i9I3Hcn_(Of6?3X01-f8uOFeCE z<_K?lI<2xNMk1?1c63$HiN~@PTF|Mt1}<`<0Xu+;BGwclBF7Zy<7@l3h`B*0T&H`j z8<28Fm#)jz$#s`DXPVux{`oqKX!&eiw(h#VSeLCYYErsF-@1N4%1Qm3^`$9ib0ESk4VObb?NmDhWF5&~>WRiQ+IhcPe_DM*ggnn54vShtNJ`E&JsVRx85>Z|K*mfd9v)(bUDK6IR)u}mlIVp?CMCE=4K>< zZiIEk<`#)EVz!|FbpbejDrPa?g% zF7D_Fo4QMR#y`vJYu!*|F-v8(9X-Lztee(7pS)~yI z*d{9L{C&u~DwNvX!0eE`!ZofP*DvUC3r-Rjd@p4IH^x-C<@`s-oJcf5WX*}x6v)d7 zvE#{Q!n;u>{<$Y|I;WX0lW_?CjkM3z@u}*tw#2cMh09FwF7RhQA>M zta)=6lwFV&=PK8@_VHb?(k?$4$4=jk<97AX%Xg(W_vS(G-PMns@VC2$bv;?@#?QB_ zNWHK7?zRS>kywaDm{2vxU#?7Y+zFnnjD#hm{QblW5y{mDcaM=Bm6iJC?t%f-B~ULs zS}$^hPT8}c@MzdGzC+`byOzwqF1|zJ#=Rr69gCVfpSQ!<`rzF?M{`>^Ez^(OmDlNC zYv@1S^>9Dg24V#ke)V_xtbs7$Gdl&1H`^NNYFkT%? zyve?w6U&y{k8EGM;C_p(dfxqY`tZd>sjZXsN(>mort`JplRY{Nsvynpu4_;Fk-xMG~U>cvmVt;lz{n9AV22xcxW z+VA8ZvRDe#g6tLS#8puTmwRD%Qs4LRY%UC6dhhX%^wp{R=Cy8Y%hnIe-07F=cbvs&eAl-kf8Zm7mY`!J0nF zbF_dzp#4S_6-s=}%7RK#Efy|8e^y z>zOA<4VmPwj3oF?$>qj-6{(V%gsUWUV;1{xJ?k2IRMB_1C{#0f+vn}N)eq^hK z6;Xb$)$6;T>dREV_|(Gojk}f{R0Vp+p~;gc<4CR&Zp^l(mCc^zFELrzM0YeAiGoz1ROrIP=_#l6ti!{3Z#taEJUw-|Cw>0%@Ysvr za{3&Zk;`?kOxBG2p64FIKb#oj1n}1eXN5(?7Rd1gVkQ!)NtFfi<_xvKG3bxusb_ssl(-mS;mDY9+(A)!L@ zQ%QCNezy5JTur%tR{dlbo6|pjGMxADXZv@**wu|ojIMb$ZxmN3xiwU~%DbxiM~l~! zGQTBc%VM0iuM^AO9`^aOg*xN8VfG0-$ax=*E4h!#%ZVv7Hn&z_S8sI%;R<`Sa(xoYu3Y4Uti3f1}A&6B_k^3K; zqxaJ;{!cfZKYizXr})DO;$14`=bIe5{x;-g@wcyp, 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 412c2eb87672d4c069c06d801794b83d2e9b4c60..d12c8f8d7a249fe03b5a5f21c15656b4c6a5ecdb 100644 GIT binary patch delta 333 zcmeysJ%wk&nfh!-1_m2u1_m|;28IK2G=`RM-AoFX1G|(!B79b5$*TKr5&%g_0OauyO z0O|Qa+8Rio0MbA+8CZb2fJ_EXAPwU4vu&l(n-nE=&I-pAa;8XDrJYcbi2Mb9|Q(^tV<52!b%v?wu0 z*Db#&x7bQSA+G?;FSa$ZR)8>VHS!b`Z50ePttYp#=rbOgyoDu->G1N&{H(V)4liJE J%*ZTa004OgJb?fJ delta 296 zcmbQj^MQN9nR+iq1_m2u1_m|;28MPP2;B#y`GEYXK$;UsF9FhAKzbdJ768&aq4X&r z%>(4$2GT-6`UQ~Y0MfsKv^S7uVTI@q0Ma1yqkuHfDuzTL4N{lN%An7{3uNR01vG$k z4Uo15(o2Cf&n-*N42~I@MGOE;yfL=` 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 318ce767795b33a86c1bac135b59c04e8d80d614..8823b2015c9710579c42a49d17103c241ed01438 100644 GIT binary patch delta 8012 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 delta 7928 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 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))