a `V@sdZddlmZddlZddlZddlmZmZddlm Z ddl m Z m Z ddZ d d ZGd d d eZGd ddeZGdddeZd ddZedZddZd!ddZd"ddZddZdS)#z babel.messages.pofile ~~~~~~~~~~~~~~~~~~~~~ Reading and writing of files in the ``gettext`` PO (portable object) format. :copyright: (c) 2013-2021 by the Babel Team. :license: BSD, see LICENSE for more details. )print_functionN)CatalogMessage)wraptext) text_typecmpcCs"dd}td||ddS)zReverse `escape` the given string. >>> print(unescape('"Say:\\n \\"hello, world!\\"\\n"')) Say: "hello, world!" :param string: the string to unescape cSs2|d}|dkrdS|dkr"dS|dkr.dS|S)Nn t r )group)matchmr9/usr/lib/python3.9/site-packages/babel/messages/pofile.pyreplace_escapes s z!unescape..replace_escapesz \\([\\trn"])r)recompilesub)stringrrrrunescapes  rcCsFd|vr:|}|dr&|dd}tt|}d|St|SdS)aReverse the normalization done by the `normalize` function. >>> print(denormalize(r'''"" ... "Say:\n" ... " \"hello, world!\"\n"''')) Say: "hello, world!" >>> print(denormalize(r'''"" ... "Say:\n" ... " \"Lorem ipsum dolor sit " ... "amet, consectetur adipisicing" ... " elit, \"\n"''')) Say: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " :param string: the string to denormalize r ""rN) splitlines startswithmaprjoin)rZ escaped_lineslinesrrr denormalize-s    r"cs eZdZdZfddZZS) PoFileErrorzDException thrown by PoParser when an invalid po file is encountered.cs0tt|dj||d||_||_||_dS)Nz{message} on {lineno})messagelineno)superr#__init__formatcatalogliner%)selfr$r)r*r% __class__rrr'NszPoFileError.__init__)__name__ __module__ __qualname____doc__r' __classcell__rrr,rr#Lsr#c@speZdZddZddZddZddZeZd d Zd d Z d dZ ddZ ddZ ddZ ddZddZdS)_NormalizedStringcGsg|_|D]}||q dSN)_strsappend)r+argsargrrrr'Wsz_NormalizedString.__init__cCs|j|dSr4)r5r6strip)r+srrrr6\sz_NormalizedString.appendcCsdtt|jS)Nr)r rrr5r+rrrr"_sz_NormalizedString.denormalizecCs t|jSr4)boolr5r;rrr __nonzero__bsz_NormalizedString.__nonzero__cCstj|jSr4)oslinesepr r5r;rrr__repr__gsz_NormalizedString.__repr__cCs|sdStt|t|S)Nr)rrr+otherrrr__cmp__jsz_NormalizedString.__cmp__cCs||dkSNrrCrArrr__gt__psz_NormalizedString.__gt__cCs||dkSrDrErArrr__lt__ssz_NormalizedString.__lt__cCs||dkSrDrErArrr__ge__vsz_NormalizedString.__ge__cCs||dkSrDrErArrr__le__ysz_NormalizedString.__le__cCs||dkSrDrErArrr__eq__|sz_NormalizedString.__eq__cCs||dkSrDrErArrr__ne__sz_NormalizedString.__ne__N)r.r/r0r'r6r"r=__bool__r@rCrFrGrHrIrJrKrrrrr3Usr3c@sneZdZdZgdZdddZddZdd Zd d Zdd d Z dddZ ddZ ddZ ddZ ddZdS) PoFileParserzSupport class to read messages from a ``gettext`` PO (portable object) file and add them to a `Catalog` See `read_po` for simple cases. )msgidmsgstrmsgctxt msgid_pluralFcCs*||_||_d|_d|_||_|dSrD)r)ignore_obsoletecounteroffset abort_invalid_reset_message_state)r+r)rRrUrrrr's zPoFileParser.__init__cCsFg|_g|_g|_g|_g|_g|_d|_d|_d|_d|_ d|_ dS)NF) messages translations locationsflags user_comments auto_commentscontextobsoletein_msgid in_msgstr in_msgctxtr;rrrrVsz!PoFileParser._reset_message_statec Cs<|jt|jdkr.tdd|jD}n|jd}t|ttfrddt|j j D}|jD]2\}}||j j kr| d|j dqf|||<qft|}n|jdd}|j r|j }nd}t||t|jt|j|j|j|j d|d }|jr|js"||j j|<n ||j |<|jd7_|dS) z Add a message to the catalog based on the current parser state and clear the state ready to process the next message. rcSsg|] }|qSr)r").0rrrr z-PoFileParser._add_message..rcSsg|]}dqS)rr)rb_rrrrcrdrz5msg has more translations than num_plurals of catalogN)r%r])rXsortlenrWtupler" isinstancelistranger) num_plurals_invalid_pofilerTr]rrYsetrZr\r[r^rRrSrV)r+rNridx translationrPr$rrr _add_messages4     zPoFileParser._add_messagecCs|jr|dSr4)rWrqr;rrr_finish_current_messagesz$PoFileParser._finish_current_messagecCs*|dr|||n||||dS)N")r!_process_string_continuation_line_process_keyword_line)r+r%r*r^rrr_process_message_lines z"PoFileParser._process_message_linec CsF|jD]Z}z4||r<|t|dvr<|t|d}WqtWqty^|||dYq0q|||ddS|dvr|||_|dkr||_|dvrd|_d|_ |j t |n|d kr(d|_ d|_ |d r|d dd d \}}|j t|t |gn|j d t |gn|dkrBd|_t ||_dS)N) [z$Keyword must be followed by a stringz0Start of line didn't match any expected keyword.)rNrPrN)rNrQFTrOrxr]rrP) _keywordsrrg IndexErrorrmrrr^rTrar_rWr6r3r`splitrXintr])r+r%r*r^keywordr8romsgrrrrus8      z"PoFileParser._process_keyword_linecCsV|jr|jd}n6|jr(|jdd}n |jr6|j}n|||ddS||dS)Nrrz>> from datetime import datetime >>> from babel._compat import StringIO >>> buf = StringIO(''' ... #: main.py:1 ... #, fuzzy, python-format ... msgid "foo %(name)s" ... msgstr "quux %(name)s" ... ... # A user comment ... #. An auto comment ... #: main.py:3 ... msgid "bar" ... msgid_plural "baz" ... msgstr[0] "bar" ... msgstr[1] "baaz" ... ''') >>> catalog = read_po(buf) >>> catalog.revision_date = datetime(2007, 4, 1) >>> for message in catalog: ... if message.id: ... print((message.id, message.string)) ... print(' ', (message.locations, sorted(list(message.flags)))) ... print(' ', (message.user_comments, message.auto_comments)) (u'foo %(name)s', u'quux %(name)s') ([(u'main.py', 1)], [u'fuzzy', u'python-format']) ([], []) ((u'bar', u'baz'), (u'bar', u'baaz')) ([(u'main.py', 3)], []) ([u'A user comment'], [u'An auto comment']) .. versionadded:: 1.0 Added support for explicit charset argument. :param fileobj: the file-like object to read the PO file from :param locale: the locale identifier or `Locale` object, or `None` if the catalog is not bound to a locale (which basically means it's a template) :param domain: the message domain :param ignore_obsolete: whether to ignore obsolete messages in the input :param charset: the character set of the catalog. :param abort_invalid: abort read if po file is invalid )localedomainr)rU)rrMr)rrrrRrrUr)parserrrrread_poLs/ rzL(\s+|[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))cCs0d|dddddddd d d S) zEscape the given string so that it can be included in double-quoted strings in ``PO`` files. >>> escape('''Say: ... "hello, world!" ... ''') '"Say:\\n \\"hello, world!\\"\\n"' :param string: the string to escape z"%s"\z\\r z\trz\rr z\nrsz\")replace)rrrrescapes rrLc s0|r|dkrt}g}|dD]}tt|||krt|}||rg}d}|rtt|dd|} || |kr|||| 7}qX|s||qqX|d|qLq"||q"n |d}t|dkrt|S|r|ds|d=|dd7<ddfd d |DS) aConvert a string into a format that is appropriate for .po files. >>> print(normalize('''Say: ... "hello, world!" ... ''', width=None)) "" "Say:\n" " \"hello, world!\"\n" >>> print(normalize('''Say: ... "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " ... ''', width=32)) "" "Say:\n" " \"Lorem ipsum dolor sit " "amet, consectetur adipisicing" " elit, \"\n" :param string: the string to normalize :param prefix: a string that should be prepended to every line :param width: the maximum line width; use `None`, 0, or a negative number to completely disable line wrapping rTrrrrr z"" csg|]}t|qSr)r)rbr*prefixrrrcrdznormalize..) rgrrWORD_SEPr|reverser6popr ) rrwidthZ prefixlenr!r*chunksbufsizelrrr normalizes6      rTc  sbd#fdd fddd$fdd } d%fdd } d } |rLd } n|rTd } t| d D]} | js|rrq`j}rdkrg}|D]}|t|dd7}qd|}|d| jD] }| |q| jD]}| |ddq|sg}zt| j ddd}Wnt y$| j }Yn0|D]L\}}|r\| r\| d| t jd|fn| d| t jdq*| d|dd| jrdddgt| j| jr|r| d| jdddt| jd kr| d!| jd dd| | dq`|s^tj| d D]0} | jD]}| |q6| | d"ddq,d S)&aWrite a ``gettext`` PO (portable object) template file for a given message catalog to the provided file-like object. >>> catalog = Catalog() >>> catalog.add(u'foo %(name)s', locations=[('main.py', 1)], ... flags=('fuzzy',)) >>> catalog.add((u'bar', u'baz'), locations=[('main.py', 3)]) >>> from babel._compat import BytesIO >>> buf = BytesIO() >>> write_po(buf, catalog, omit_header=True) >>> print(buf.getvalue().decode("utf8")) #: main.py:1 #, fuzzy, python-format msgid "foo %(name)s" msgstr "" #: main.py:3 msgid "bar" msgid_plural "baz" msgstr[0] "" msgstr[1] "" :param fileobj: the file-like object to write to :param catalog: the `Catalog` instance :param width: the maximum line width for the generated output; use `None`, 0, or a negative number to completely disable line wrapping :param no_location: do not emit a location comment for every message :param omit_header: do not include the ``msgid ""`` entry at the top of the output :param sort_output: whether to sort the messages in the output by msgid :param sort_by_file: whether to sort the messages in the output by their locations :param ignore_obsolete: whether to ignore obsolete messages and not include them in the output; by default they are included as comments :param include_previous: include the old msgid as a comment when updating the catalog :param include_lineno: include line number in the location comment rcst||dS)N)rr)r)keyr)rrr _normalizeszwrite_po.._normalizecs&t|tr|jd}|dS)Nbackslashreplace)rirencoderwrite)text)r)rrr_writes zwrite_po.._writecs>rdkr}nd}t||D]}d||fq dS)Nrrz#%s %s )rr9)rr_widthr*)rrrr_write_comment s  z write_po.._write_commentc s t|jttfr|jr.d||j|fd||jd|fd||jd|ftjD]B}z|j|}Wntyd}Yn0d||||fqpnR|jrԈd||j|fd||j|fd||jpd|fdS) Nz %smsgctxt %s z %smsgid %s rz%smsgid_plural %s rrz%smsgstr[%d] %s z %smsgstr %s ) riidrjrhr]rkrlrr{)r$rror)rrr)rr_write_messages2     z write_po.._write_messageNr$r)sort_byrz# )rsubsequent_indentr rrcSs"|dt|dtr|dpdfS)Nrrr)rir})xrrrSrdzwrite_po..rz%s:%d/z%srwrz#%s z, zmsgid %s|rzmsgid_plural %sz#~ )r)r)r)_sort_messagesrZheader_commentrrr r[r\sortedrY TypeErrorr6rr>seprZZ previous_idrgr^values)rr)rZ no_locationZ omit_headerZ sort_outputZ sort_by_filerRZinclude_previousZinclude_linenorrrr$Zcomment_headerr!r*rZlocsrYfilenamer%r)rrr)rrrwrite_pos|.                  rcCs6t|}|dkr|n|dkr2|jddd|S)z Sort the given message iterable by the given criteria. Always returns a list. :param messages: An iterable of Messages. :param sort_by: Sort by which criteria? Options are `message` and `location`. :return: list[Message] r$rcSs|jSr4)rY)rrrrrrdz _sort_messages..r)rjrf)rWrrrrrvs  r)NNFNF)rr)rFFFFFFT)r1Z __future__rr>rZbabel.messages.catalogrrZ babel.utilrZ babel._compatrrrr" Exceptionr#objectr3rMrrrrrrrrrrrs,   /I 5  =