o V\(~@s0dZddlZddlZddlmZddlmZddlmZmZddl m Z ddl m Z ddl m Z dd lmZdd lmZmZdd lmZdd lmZdd lmZmZmZddlmZmZmZm Z m!Z!m"Z"gdZ#e$dej%Z&ddZ'Gddde(Z)Gddde*Z+dZ,erddZ-ne Z-Gddde(Z.dS)z babel.messages.catalog ~~~~~~~~~~~~~~~~~~~~~~ Data structures for message catalogs. :copyright: (c) 2013-2019 by the Babel Team. :license: BSD, see LICENSE for more details. N) parse_header) OrderedDict)datetimetime)get_close_matches)message_from_string)copy) __version__)LocaleUnknownLocaleError)format_datetime) get_plural)distinctLOCALTZFixedOffsetTimezone) string_types number_typesPY2cmp text_type force_text)MessageCatalogTranslationErrorz \% (?:\(([\w]*)\))? ( [-#0\ +]?(?:\*|[\d]+)? (?:\.(?:\*|[\d]+))? [hlL]? ) ([diouxXeEfFgGcrs%]) cCstd|}t|dd}t|}t|}|d}|dur^|d|dd}}|dd|dd}} t|d} t|} t| } | d } | | 7} | | 9} t | }|j |d }|S) Nz+^(?P.*?)(?P[+-]\d{4})?$rz%Y-%m-%d %H:%Mtzoffsetr1<tzinfo) rematchrstrptimegroupmktimer fromtimestampintrreplace)valuer"tttsdtr plus_minus_sresthours_offset_s mins_offset_s plus_minus hours_offset mins_offsetnet_mins_offsetr58/usr/lib/python3/dist-packages/babel/messages/catalog.py_parse_datetime_header,s"      r7c@seZdZdZ  d!ddZddZd d Zd d Zd dZddZ ddZ ddZ ddZ ddZ d"ddZeddZeddZedd ZdS)#rz0Representation of a single message in a catalog.r5Nc Cs||_|s |jr d}||_tt||_t||_|r%|jr%|j dn|j dtt||_ tt||_ t |trC|g|_nt||_||_| |_dS)a_Create the message object. :param id: the message ID, or a ``(singular, plural)`` tuple for pluralizable messages :param string: the translated message string, or a ``(singular, plural)`` tuple for pluralizable messages :param locations: a sequence of ``(filename, lineno)`` tuples :param flags: a set or sequence of flags :param auto_comments: a sequence of automatic comments for the message :param user_comments: a sequence of user comments for the message :param previous_id: the previous message ID, or a ``(singular, plural)`` tuple for pluralizable messages :param lineno: the line number on which the msgid line was found in the PO file, if any :param context: the message context )r8r8z python-formatN)id pluralizablestringlistr locationssetflags python_formatadddiscard auto_comments user_comments isinstancer previous_idlinenocontext) selfr9r;r=r?rCrDrFrGrHr5r5r6__init__Os         zMessage.__init__cCsdt|j|jt|jfS)Nz<%s %r (flags: %r)>)type__name__r9r<r?rIr5r5r6__repr__tszMessage.__repr__cCsdd}t||||S)z0Compare Messages, taking into account plural idscSs4t|tr|jr|jd|jpdfS|j|jpdfS)Nrr8)rErr:r9rH)objr5r5r6values_to_comparezsz*Message.__cmp__..values_to_compare)r)rIotherrPr5r5r6__cmp__xszMessage.__cmp__cCs||dkSNrrRrIrQr5r5r6__gt__zMessage.__gt__cCs||dkSrSrTrUr5r5r6__lt__rWzMessage.__lt__cCs||dkSrSrTrUr5r5r6__ge__rWzMessage.__ge__cCs||dkSrSrTrUr5r5r6__le__rWzMessage.__le__cCs||dkSrSrTrUr5r5r6__eq__rWzMessage.__eq__cCs||dkSrSrTrUr5r5r6__ne__rWzMessage.__ne__c Cs2ttt|j|j|j|j|j|j|j |j |j f SN) rmaprr9r;r=r?rCrDrFrGrHrMr5r5r6clones z Message.clonec CsZddlm}g}|D] }z|||Wq ty*}z ||WYd}~q d}~ww|S)aRun various validation checks on the message. Some validations are only performed if the catalog is provided. This method returns a sequence of `TranslationError` objects. :rtype: ``iterator`` :param catalog: A catalog instance that is passed to the checkers :see: `Catalog.check` for a way to perform checks for all messages in a catalog. r)checkersN)babel.messages.checkersr`rappend)rIcatalogr`errorscheckerer5r5r6checks z Message.checkcCs d|jvS)aWhether the translation is fuzzy. >>> Message('foo').fuzzy False >>> msg = Message('foo', 'foo', flags=['fuzzy']) >>> msg.fuzzy True >>> msg :type: `bool`fuzzyr?rMr5r5r6rhs z Message.fuzzycCst|jttfS)zWhether the message is plurizable. >>> Message('foo').pluralizable False >>> Message(('foo', 'bar')).pluralizable True :type: `bool`)rEr9r<tuplerMr5r5r6r: zMessage.pluralizablecCs,|j}t|ttfs |g}tdd|DS)zWhether the message contains Python-style parameters. >>> Message('foo %(name)s bar').python_format True >>> Message(('foo %(name)s', 'foo %(name)s')).python_format True :type: `bool`css|]}t|VqdSr]) PYTHON_FORMATsearch).0r9r5r5r6 sz(Message.python_format..)r9rEr<rjany)rIidsr5r5r6r@s zMessage.python_format)r8r5r5r5r5r5NNr])rL __module__ __qualname____doc__rJrNrRrVrXrYrZr[r\r_rgpropertyrhr:r@r5r5r5r6rLs* %    rc@seZdZdZdS)rz_Exception thrown by translation checkers when invalid message translations are encountered.N)rLrrrsrtr5r5r5r6rsrz# Translations template for PROJECT. # Copyright (C) YEAR ORGANIZATION # This file is distributed under the same license as the PROJECT project. # FIRST AUTHOR , YEAR. #cCsDt|d}i}|D]\}}|d}|d}|||<q |S)Nutf8)rencodeitemsdecode) header_stringheadersdecoded_headersnamer)r5r5r6 _parse_headers   r~c @s4eZdZdZddeddddddddddf ddZddZdd Zd d Ze eeZ e eZ d d Z ddZ e e e ddZddZddZe eeddZe ddZe ddZe ddZddZdd Zd!d"Zd#d$Zd%d&Zd'd(Zd)d*Z + d9d,d-Zd.d/Zd:d0d1Zd:d2d3Zd;d5d6Z d:d7d8Z!dS)zLANGUAGE zutf-8NrzYEAR-MO-DA HO:MI+ZONE)domainlocale_header_commentr _messagesprojectversioncopyright_holdermsgid_bugs_addresslast_translator language_teamcharsetrnowrrEr r( creation_date revision_daterhobsolete _num_plurals _plural_expr)rIrrheader_commentrrrrrrrrrrhr5r5r6rJs6           zCatalog.__init__cCs|dur d|_d|_dSt|trt||_||_dSt|tr>> catalog = Catalog(project='Foobar', version='1.0', ... copyright_holder='Foo Company') >>> print(catalog.header_comment) #doctest: +ELLIPSIS # Translations template for Foobar. # Copyright (C) ... Foo Company # This file is distributed under the same license as the Foobar project. # FIRST AUTHOR , .... # The header can also be set from a string. Any known upper-case variables will be replaced when the header is retrieved again: >>> catalog = Catalog(project='Foobar', version='1.0', ... copyright_holder='Foo Company') >>> catalog.header_comment = '''\ ... # The POT for my really cool PROJECT project. ... # Copyright (C) 1990-2003 ORGANIZATION ... # This file is distributed under the same license as the PROJECT ... # project. ... #''' >>> print(catalog.header_comment) # The POT for my really cool Foobar project. # Copyright (C) 1990-2003 Foo Company # This file is distributed under the same license as the Foobar # project. # :type: `unicode` )doccCsJg}|dd|j|jff|d|jf|dt|jdddft|jtt ft r<|dt|jdddfn|d|jf|d |j f|j rY|d t |j f|j rqd |jvrq|d |jd t |j fn|d |jf|jdur|d |jf|d|dd|jf|d|ddtf|S)NzProject-Id-Versionz%s %szReport-Msgid-Bugs-TozPOT-Creation-Datezyyyy-MM-dd HH:mmZen)rzPO-Revision-DatezLast-TranslatorLanguageLANGUAGEz Language-Teamz Plural-Forms)z MIME-Versionz1.0z Content-Typeztext/plain; charset=%s)zContent-Transfer-Encoding8bitz Generated-Byz Babel %s )rbrrrr rrErrtime_rrrstrrr(r plural_formsrr)rIr{r5r5r6_get_mime_headerssH   zCatalog._get_mime_headerscCsH|D]\}}t||jd}t||jd}|dkr/|d}d|dd|_|d|_q|dkr7||_q|dkr?||_q|dkrO| dd }| |q|d krW||_ q|d krmt |\}}d |vrl|d |_q|d krt d|\}}t |dd|_|dd|_q|dkrt||_q|dkrd|vrt||_qdS)N)encodingzproject-id-version zreport-msgid-bugs-tozlast-translatorlanguage-_z language-teamz content-typerz plural-formsz ;npluralsrplural(n != 1)zpot-creation-datezpo-revision-dater)rlowerrsplitjoinrrrrr(rrrr'getrrr7rr)rIr{r}r)partsmimetypeparamsrr5r5r6_set_mime_headerss@        zCatalog._set_mime_headersa The MIME headers of the catalog, used for the special ``msgid ""`` entry. The behavior of this property changes slightly depending on whether a locale is set or not, the latter indicating that the catalog is actually a template for actual translations. Here's an example of the output for such a catalog template: >>> from babel.dates import UTC >>> created = datetime(1990, 4, 1, 15, 30, tzinfo=UTC) >>> catalog = Catalog(project='Foobar', version='1.0', ... creation_date=created) >>> for name, value in catalog.mime_headers: ... print('%s: %s' % (name, value)) Project-Id-Version: Foobar 1.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 1990-04-01 15:30+0000 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel ... And here's an example of the output when the locale is set: >>> revised = datetime(1990, 8, 3, 12, 0, tzinfo=UTC) >>> catalog = Catalog(locale='de_DE', project='Foobar', version='1.0', ... creation_date=created, revision_date=revised, ... last_translator='John Doe ', ... language_team='de_DE ') >>> for name, value in catalog.mime_headers: ... print('%s: %s' % (name, value)) Project-Id-Version: Foobar 1.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 1990-04-01 15:30+0000 PO-Revision-Date: 1990-08-03 12:00+0000 Last-Translator: John Doe Language: de_DE Language-Team: de_DE Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel ... :type: `list` cC.|jdurd}|jrt|jd}||_|jS)zThe number of plurals used by the catalog or locale. >>> Catalog(locale='en').num_plurals 2 >>> Catalog(locale='ga').num_plurals 5 :type: `int`Nrr)rrr )rInumr5r5r6 num_pluralss zCatalog.num_pluralscCr)a`The plural expression used by the catalog or locale. >>> Catalog(locale='en').plural_expr '(n != 1)' >>> Catalog(locale='ga').plural_expr '(n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4)' >>> Catalog(locale='ding').plural_expr # unknown locale '(n != 1)' :type: `string_types`Nrr)rrr )rIexprr5r5r6 plural_exprs zCatalog.plural_exprcCsd|j|jfS)zReturn the plural forms declaration for the locale. >>> Catalog(locale='en').plural_forms 'nplurals=2; plural=(n != 1)' >>> Catalog(locale='pt_BR').plural_forms 'nplurals=2; plural=(n > 1)' :type: `str`znplurals=%s; plural=%s)rrrMr5r5r6rrkzCatalog.plural_formscCs|||jvS)z?Return whether the catalog has a message with the specified ID._key_forrrIr9r5r5r6 __contains__"szCatalog.__contains__cCs t|jS)zeThe number of messages in the catalog. This does not include the special ``msgid ""`` entry.)lenrrMr5r5r6__len__&s zCatalog.__len__ccspg}|jD] \}}|d||fqt}|jr|dhO}tdd||dV|jD]}|j|Vq-dS)zIterates through all the entries in the catalog, in the order they were added, yielding a `Message` object for every entry. :rtype: ``iterator``z%s: %srhr8 riN) mime_headersrbr>rhrrr)rIbufr}r)r?keyr5r5r6__iter__,s  zCatalog.__iter__cCs*d}|jr d|j}dt|j|j|fS)Nr8z %sz <%s %r%s>)rrKrLrrr5r5r6rN;s zCatalog.__repr__cCs||dS)z)Delete the message with the specified ID.N)deleterr5r5r6 __delitem__AszCatalog.__delitem__cCs ||S)zUReturn the message with the specified ID. :param id: the message ID )rrr5r5r6 __getitem__Es zCatalog.__getitem__cCs t|ts Jd|||j}|j|}|rS|jr&|js&|j|_|j|_t t |j |j |_ t t |j |j |_ t t |j |j |_ |j|jO_|}dS|dkrqt|j|_ddd|j D|_|j|_dSt|t tfrt|jt tfsJdt|j||j|<dS)aAdd or update the message with the specified ID. >>> catalog = Catalog() >>> catalog[u'foo'] = Message(u'foo') >>> catalog[u'foo'] If a message with that ID is already in the catalog, it is updated to include the locations and flags of the new message. >>> catalog = Catalog() >>> catalog[u'foo'] = Message(u'foo', locations=[('main.py', 1)]) >>> catalog[u'foo'].locations [('main.py', 1)] >>> catalog[u'foo'] = Message(u'foo', locations=[('utils.py', 5)]) >>> catalog[u'foo'].locations [('main.py', 1), ('utils.py', 5)] :param id: the message ID :param message: the `Message` object zexpected a Message objectr8rcSsg|]}d|qS)z# %s)rstrip)rncr5r5r6 usz'Catalog.__setitem__..zExpected sequence but got %sN)rErrrHrrr:r9r;r<rr=rCrDr?r~rxrrrrhrjrK)rIr9messagercurrentr5r5r6 __setitem__Ls:        zCatalog.__setitem__r5c Cs*t||t||||||| d } | ||<| S)atAdd or update the message with the specified ID. >>> catalog = Catalog() >>> catalog.add(u'foo') >>> catalog[u'foo'] This method simply constructs a `Message` object with the given arguments and invokes `__setitem__` with that object. :param id: the message ID, or a ``(singular, plural)`` tuple for pluralizable messages :param string: the translated message string, or a ``(singular, plural)`` tuple for pluralizable messages :param locations: a sequence of ``(filename, lineno)`` tuples :param flags: a set or sequence of flags :param auto_comments: a sequence of automatic comments :param user_comments: a sequence of user comments :param previous_id: the previous message ID, or a ``(singular, plural)`` tuple for pluralizable messages :param lineno: the line number on which the msgid line was found in the PO file, if any :param context: the message context )rGrH)rr<) rIr9r;r=r?rCrDrFrGrHrr5r5r6rA~s z Catalog.addccs0|jD]}|j|d}|r||fVqdS)aBRun various validation checks on the translations in the catalog. For every message which fails validation, this method yield a ``(message, errors)`` tuple, where ``message`` is the `Message` object and ``errors`` is a sequence of `TranslationError` objects. :rtype: ``iterator`` )rcN)rvaluesrg)rIrrdr5r5r6rgs  z Catalog.checkcCs|j|||S)zReturn the message with the specified ID and context. :param id: the message ID :param context: the message context, or ``None`` for no context )rrr)rIr9rHr5r5r6rsz Catalog.getcCs&|||}||jvr|j|=dSdS)zDelete the message with the specified ID and context. :param id: the message ID :param context: the message context, or ``None`` for no context NrrIr9rHrr5r5r6rs   zCatalog.deleteFcs6jt_g}|stfddD}tfdd}|D]R}|jr||j|j}|vrB||||q*|durwt|t rP|d} n|} t | | d} | rw| d} || } | durp| | f} ||| |q*||j<q*D]} |s| vr| j| <q|r|j_|j_dS) aUpdate the catalog based on the given template catalog. >>> from babel.messages import Catalog >>> template = Catalog() >>> template.add('green', locations=[('main.py', 99)]) >>> template.add('blue', locations=[('main.py', 100)]) >>> template.add(('salad', 'salads'), locations=[('util.py', 42)]) >>> catalog = Catalog(locale='de_DE') >>> catalog.add('blue', u'blau', locations=[('main.py', 98)]) >>> catalog.add('head', u'Kopf', locations=[('util.py', 33)]) >>> catalog.add(('salad', 'salads'), (u'Salat', u'Salate'), ... locations=[('util.py', 38)]) >>> catalog.update(template) >>> len(catalog) 3 >>> msg1 = catalog['green'] >>> msg1.string >>> msg1.locations [('main.py', 99)] >>> msg2 = catalog['blue'] >>> msg2.string u'blau' >>> msg2.locations [('main.py', 100)] >>> msg3 = catalog['salad'] >>> msg3.string (u'Salat', u'Salate') >>> msg3.locations [('util.py', 42)] Messages that are in the catalog but not in the template are removed from the main collection, but can still be accessed via the `obsolete` member: >>> 'head' in catalog False >>> list(catalog.obsolete.values()) [] :param template: the reference catalog, usually read from a POT file :param no_fuzzy_matching: whether to use fuzzy matching of message IDs cs.g|]}|r|jr||jfqSr5)r;rrH)rnmsgid)messagesrIr5r6rsz"Catalog.update..csD|}d}||kr)d}||}t|jtr"|jg|_n t|j|_n|d}|j |_ r=tt |j |_ t|jtt fryt|j tt fsad}t |j gdgt |jd|_ n(t |j jkrxd}t |j dt |j |_ nt|j tt frd}|j d|_ |j|jO_|r|jdhO_||j<dS)NFTr8rrrh)r_rArrEr9rrFr<popr;rrDrjrrr?)roldkeynewkeyrholdmsg fuzzy_matcheskeep_user_commentsr remainingrIr5r6_merges<      zCatalog.update.._mergeFrrN)rrrdictr>r9rrHrErjrrstripkeysrrr)rItemplateno_fuzzy_matchingupdate_header_commentrfuzzy_candidatesrrrmatchkeymatchesrnewctxtrr5rr6updatesL5#       zCatalog.updatecCs.|}t|ttfr |d}|dur||f}|S)zThe key for a message is just the singular ID even for pluralizable messages, but is a ``(msgid, msgctxt)`` tuple for context-specific messages. rN)rEr<rjrr5r5r6rIs zCatalog._key_for)Nr5r5r5r5r5NNr])FFT)"rLrrrsrtDEFAULT_HEADERrJrrrrurrrrrrrrrrrrrrrNrrrrArgrrrrr5r5r5r6rsR : !  3    2 !    r)/rtr!rcgir collectionsrrrdifflibremailrrbabelr r babel.corer r babel.datesr babel.messages.pluralsr babel.utilrrr babel._compatrrrrrr__all__compileVERBOSErlr7objectr Exceptionrrr~rr5r5r5r6s8