o
    �ú
^hU  ã                   @   sÎ   d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	 ddl
mZmZ dd„ Zd	d
„ ZG dd„ deƒZG dd„ deƒZG dd„ deƒZd dd„Ze d¡Zdd„ Zd!dd„Z			d"dd„Zdd„ ZdS )#zî
    babel.messages.pofile
    ~~~~~~~~~~~~~~~~~~~~~

    Reading and writing of files in the ``gettext`` PO (portable object)
    format.

    :copyright: (c) 2013-2019 by the Babel Team.
    :license: BSD, see LICENSE for more details.
é    )Úprint_functionN)ÚCatalogÚMessage)Úwraptext)Ú	text_typeÚcmpc                 C   s"   dd„ }t  d¡ || dd… ¡S )z¿Reverse `escape` the given string.

    >>> print(unescape('"Say:\\n  \\"hello, world!\\"\\n"'))
    Say:
      "hello, world!"
    <BLANKLINE>

    :param string: the string to unescape
    c                 S   s2   |   d¡}|dkrdS |dkrdS |dkrdS |S )Né   ÚnÚ
Útú	Úrú)Úgroup)ÚmatchÚm© r   ú7/usr/lib/python3/dist-packages/babel/messages/pofile.pyÚreplace_escapes    s   
z!unescape.<locals>.replace_escapesz\\([\\trn"])r   éÿÿÿÿ)ÚreÚcompileÚsub)Ústringr   r   r   r   Úunescape   s   

r   c                 C   sB   d| v r|   ¡ }|  d¡r|dd… }tt|ƒ}d |¡S t| ƒS )aì  Reverse the normalization done by the `normalize` function.

    >>> print(denormalize(r'''""
    ... "Say:\n"
    ... "  \"hello, world!\"\n"'''))
    Say:
      "hello, world!"
    <BLANKLINE>

    >>> print(denormalize(r'''""
    ... "Say:\n"
    ... "  \"Lorem ipsum dolor sit "
    ... "amet, consectetur adipisicing"
    ... " elit, \"\n"'''))
    Say:
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
    <BLANKLINE>

    :param string: the string to denormalize
    r
   ú""r   NÚ )Ú
splitlinesÚ
startswithÚmapr   Újoin)r   Úescaped_linesÚlinesr   r   r   Údenormalize-   s   


r#   c                       s    e Zd ZdZ‡ fdd„Z‡  ZS )ÚPoFileErrorzDException thrown by PoParser when an invalid po file is encountered.c                    s0   t t| ƒ dj||d�¡ || _|| _|| _d S )Nz{message} on {lineno})ÚmessageÚlineno)Úsuperr$   Ú__init__ÚformatÚcatalogÚliner&   )Úselfr%   r*   r+   r&   ©Ú	__class__r   r   r(   N   s   
zPoFileError.__init__)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r(   Ú__classcell__r   r   r-   r   r$   L   s    r$   c                   @   sp   e Zd Zdd„ Zdd„ Zdd„ Zdd„ ZeZd	d
„ Zdd„ Z	dd„ Z
dd„ Zdd„ Zdd„ Zdd„ Zdd„ ZdS )Ú_NormalizedStringc                 G   s   g | _ |D ]}|  |¡ qd S ©N)Ú_strsÚappend)r,   ÚargsÚargr   r   r   r(   W   s   ÿz_NormalizedString.__init__c                 C   s   | j  | ¡ ¡ d S r5   )r6   r7   Ústrip)r,   Úsr   r   r   r7   \   s   z_NormalizedString.appendc                 C   s   d  tt| jƒ¡S )Nr   )r    r   r   r6   ©r,   r   r   r   r#   _   s   z_NormalizedString.denormalizec                 C   s
   t | jƒS r5   )Úboolr6   r<   r   r   r   Ú__nonzero__b   s   
z_NormalizedString.__nonzero__c                 C   s   t j | j¡S r5   )ÚosÚlinesepr    r6   r<   r   r   r   Ú__repr__g   ó   z_NormalizedString.__repr__c                 C   s   |sdS t t| ƒt|ƒƒS )Nr   )r   r   ©r,   Úotherr   r   r   Ú__cmp__j   s   z_NormalizedString.__cmp__c                 C   s   |   |¡dkS ©Nr   ©rE   rC   r   r   r   Ú__gt__p   rB   z_NormalizedString.__gt__c                 C   s   |   |¡dk S rF   rG   rC   r   r   r   Ú__lt__s   rB   z_NormalizedString.__lt__c                 C   s   |   |¡dkS rF   rG   rC   r   r   r   Ú__ge__v   rB   z_NormalizedString.__ge__c                 C   s   |   |¡dkS rF   rG   rC   r   r   r   Ú__le__y   rB   z_NormalizedString.__le__c                 C   s   |   |¡dkS rF   rG   rC   r   r   r   Ú__eq__|   rB   z_NormalizedString.__eq__c                 C   s   |   |¡dkS rF   rG   rC   r   r   r   Ú__ne__   rB   z_NormalizedString.__ne__N)r/   r0   r1   r(   r7   r#   r>   Ú__bool__rA   rE   rH   rI   rJ   rK   rL   rM   r   r   r   r   r4   U   s    r4   c                   @   sn   e Zd ZdZg d¢Zddd„Zdd„ Zdd	„ Zd
d„ Zddd„Z	ddd„Z
dd„ Zdd„ Zdd„ Zdd„ ZdS )ÚPoFileParserz–Support class to  read messages from a ``gettext`` PO (portable object) file
    and add them to a `Catalog`

    See `read_po` for simple cases.
    )ÚmsgidÚmsgstrÚmsgctxtÚmsgid_pluralFc                 C   s*   || _ || _d| _d| _|| _|  ¡  d S rF   )r*   Úignore_obsoleteÚcounterÚoffsetÚabort_invalidÚ_reset_message_state)r,   r*   rT   rW   r   r   r   r(   ’   s   zPoFileParser.__init__c                 C   sF   g | _ g | _g | _g | _g | _g | _d | _d| _d| _d| _	d| _
d S )NF)ÚmessagesÚtranslationsÚ	locationsÚflagsÚuser_commentsÚauto_commentsÚcontextÚobsoleteÚin_msgidÚ	in_msgstrÚ
in_msgctxtr<   r   r   r   rX   š   s   
z!PoFileParser._reset_message_statec              
   C   s8  | j  ¡  t| jƒdkrtdd„ | jD ƒƒ}n| jd  ¡ }t|ttfƒrRdd„ t| j	j
ƒD ƒ}| j D ]\}}|| j	j
krF|  d| jd¡ q3| ¡ ||< q3t|ƒ}n	| j d d  ¡ }| jrd| j ¡ }nd}t||t| jƒt| jƒ| j| j| jd |d	�}| jrŠ| js‰|| j	j|< n|| j	|< |  jd7  _|  ¡  dS )
z�
        Add a message to the catalog based on the current parser state and
        clear the state ready to process the next message.
        r   c                 S   s   g | ]}|  ¡ ‘qS r   )r#   )Ú.0r   r   r   r   Ú
<listcomp>®   s    z-PoFileParser._add_message.<locals>.<listcomp>r   c                 S   s   g | ]}d ‘qS ©r   r   )rd   Ú_r   r   r   re   ²   s    r   z5msg has more translations than num_plurals of catalogN)r&   r_   )rZ   ÚsortÚlenrY   Útupler#   Ú
isinstanceÚlistÚranger*   Únum_pluralsÚ_invalid_pofilerV   r_   r   r[   Úsetr\   r^   r]   r`   rT   rU   rX   )r,   rP   r   ÚidxÚtranslationrR   r%   r   r   r   Ú_add_message§   s6   

þ€
zPoFileParser._add_messagec                 C   s   | j r	|  ¡  d S d S r5   )rY   rs   r<   r   r   r   Ú_finish_current_messageÊ   s   ÿz$PoFileParser._finish_current_messagec                 C   s,   |  d¡r|  ||¡ d S |  |||¡ d S )Nú")r   Ú!_process_string_continuation_lineÚ_process_keyword_line)r,   r&   r+   r`   r   r   r   Ú_process_message_lineÎ   s   
z"PoFileParser._process_message_linec              	   C   sH  | j D ],}z| |¡r|t|ƒ dv r|t|ƒd … }W  nW q ty/   |  ||d¡ Y qw |  ||d¡ d S |dv rA|  ¡  || _|dkrK|| _|dv r_d| _d| _	| j
 t|ƒ¡ d S |d	kr”d| _	d| _| d
¡rˆ|dd …  dd¡\}}| j t|ƒt|ƒg¡ d S | j dt|ƒg¡ d S |dkr¢d| _t|ƒ| _d S d S )N)ú ú[z$Keyword must be followed by a stringz0Start of line didn't match any expected keyword.)rP   rR   rP   )rP   rS   FTrQ   rz   r   ú]r   rR   )Ú	_keywordsr   ri   Ú
IndexErrorro   rt   r`   rV   rc   ra   rY   r7   r4   rb   ÚsplitrZ   Úintr_   )r,   r&   r+   r`   Úkeywordr9   rq   Úmsgr   r   r   rw   Ô   s>   
€ÿ
þz"PoFileParser._process_keyword_linec                 C   sV   | j r	| jd }n| jr| jd d }n| jr| j}n	|  ||d¡ d S | |¡ d S )Nr   r   z<Got line starting with " but not in msgid, msgstr or msgctxt)ra   rY   rb   rZ   rc   r_   ro   r7   )r,   r+   r&   r;   r   r   r   rv   ý   s   z.PoFileParser._process_string_continuation_linec              	   C   s8  |   ¡  |dd …  d¡rP|dd …  ¡  ¡ D ]6}| d¡}|dkrEzt||d d … ƒ}W n	 ty7   Y qw | j |d |… |f¡ q| j |d f¡ qd S |dd …  d¡rq|dd …  ¡  d¡D ]
}| j	 | 
¡ ¡ qdd S |dd …  d¡rŽ|dd …  
¡ }|rŒ| j |¡ d S d S | j |dd …  
¡ ¡ d S )Nr   ú:é   r   ú,Ú.)rt   r   Úlstripr~   Úrfindr   Ú
ValueErrorr[   r7   r\   r:   r^   r]   )r,   r+   ÚlocationÚposr&   ÚflagÚcommentr   r   r   Ú_process_comment	  s.   
ÿ÷
ÿÿzPoFileParser._process_commentc                 C   sâ   t |ƒD ]@\}}| ¡ }t|tƒs| | jj¡}|sq| d¡r>|dd…  d¡r8| j||dd…  	¡ dd� q|  
|¡ q|  ||¡ q|  ¡  | jsm| jsU| jsU| jro| j tdƒ¡ | j d	tdƒg¡ |  ¡  dS dS dS )
z˜
        Reads from the file-like object `fileobj` and adds any po file
        units found in it to the `Catalog` supplied to the constructor.
        ú#r   Nú~rƒ   T)r`   r   r   )Ú	enumerater:   rk   r   Údecoder*   Úcharsetr   rx   r†   r�   rt   rU   r\   r]   r^   rY   r7   r4   rZ   rs   )r,   Úfileobjr&   r+   r   r   r   Úparse$  s"   

ýzPoFileParser.parsec                 C   s8   | j rt|| j||ƒ‚td|ƒ td |d |¡ƒ d S )NzWARNING:z!WARNING: Problem on line {0}: {1}r   )rW   r$   r*   Úprintr)   )r,   r+   r&   r�   r   r   r   ro   A  s   
zPoFileParser._invalid_pofileN)FF)F)r/   r0   r1   r2   r|   r(   rX   rs   rt   rx   rw   rv   r�   r”   ro   r   r   r   r   rO   „   s    
#

)rO   Fc                 C   s*   t |||d�}t|||d�}| | ¡ |S )a�  Read messages from a ``gettext`` PO (portable object) file from the given
    file-like object and return a `Catalog`.

    >>> 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
    )ÚlocaleÚdomainr’   )rW   )r   rO   r”   )r“   r–   r—   rT   r’   rW   r*   Úparserr   r   r   Úread_poH  s   /
r™   zL(\s+|[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))c                 C   s0   d|   dd¡  dd¡  dd¡  dd	¡  d
d¡ S )zõEscape 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\tr   z\rr
   z\nru   z\")Úreplace)r   r   r   r   Úescape„  s   ürœ   r   éL   c           
         s0  |rj|dkrjt ˆ ƒ}g }|  d¡D ]W}t t|ƒƒ| |krct |¡}| ¡  |rbg }d}|rXt t|d ƒƒd | }	||	 |k rL| | ¡ ¡ ||	7 }n
|sU| | ¡ ¡ n|s.| d |¡¡ |s(q| |¡ qn|  d¡}t |ƒdkryt| ƒS |rŠ|d sŠ|d= |d  d7  < dd ‡ fd	d
„|D ƒ¡ S )a©  Convert 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
    r   Trƒ   r   r   r   r
   z""
c                    s   g | ]}ˆ t |ƒ ‘qS r   )rœ   )rd   r+   ©Úprefixr   r   re   Ð  s    znormalize.<locals>.<listcomp>)	ri   r   rœ   ÚWORD_SEPr~   Úreverser7   Úpopr    )
r   rŸ   ÚwidthÚ	prefixlenr"   r+   ÚchunksÚbufÚsizeÚlr   rž   r   Ú	normalize–  s>   

öò€ì
r©   Tc
              	      sR  d#‡fdd„	‰ ‡‡fdd„‰d#‡‡fdd„	}
d#‡ ‡‡fdd	„	}d
}|r&d}n|r*d}t ˆ|d�D ]Ï}|js^|r8q0ˆj}ˆrXˆdkrXg }| ¡ D ]}|t|ˆdd�7 }qGd |¡}ˆ|d ƒ |jD ]}|
|ƒ qa|jD ]}|
|dd� qk|s¿g }zt|j	dd„ d�}W n t
y�   |j	}Y nw |D ]#\}}|r©|	r©| d| tjd¡|f ¡ q’| d| tjd¡ ¡ q’|
d |¡dd� |jrÑˆdd dgt|jƒ ¡ ƒ |jr÷|r÷|
dˆ |jd ƒ dd� t|jƒd kr÷|
d!ˆ |jd  ƒ dd� ||ƒ ˆdƒ q0|�s%t ˆj ¡ |d�D ]}|jD ]}|
|ƒ �q||d"d� ˆdƒ �qd
S d
S )$aØ  Write 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',))
    <Message...>
    >>> catalog.add((u'bar', u'baz'), locations=[('main.py', 3)])
    <Message...>
    >>> 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 ""
    <BLANKLINE>
    #: main.py:3
    msgid "bar"
    msgid_plural "baz"
    msgstr[0] ""
    msgstr[1] ""
    <BLANKLINE>
    <BLANKLINE>

    :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
    r   c                    s   t | |ˆ d�S )N)rŸ   r£   )r©   )ÚkeyrŸ   )r£   r   r   Ú
_normalize  rB   zwrite_po.<locals>._normalizec                    s&   t | tƒr|  ˆ jd¡} ˆ | ¡ d S )NÚbackslashreplace)rk   r   Úencoder’   Úwrite)Útext)r*   r“   r   r   Ú_write  s   
zwrite_po.<locals>._writec                    s>   ˆr	ˆdkr	ˆ}nd}t | |ƒD ]}ˆ d|| ¡ f ƒ qd S )Nr   r�   z#%s %s
)r   r:   )rŒ   rŸ   Ú_widthr+   )r°   r£   r   r   Ú_write_comment	  s   ÿz write_po.<locals>._write_commentc              	      s  t | jttfƒr\| jrˆd|ˆ | j|ƒf ƒ ˆd|ˆ | jd |ƒf ƒ ˆd|ˆ | jd |ƒf ƒ tˆjƒD ]!}z| j| }W n tyL   d}Y nw ˆd||ˆ ||ƒf ƒ q8d S | jrkˆd|ˆ | j|ƒf ƒ ˆd|ˆ | j|ƒf ƒ ˆd|ˆ | jpd|ƒf ƒ d S )	Nz%smsgctxt %s
z%smsgid %s
r   z%smsgid_plural %s
r   r   z%smsgstr[%d] %s
z%smsgstr %s
)	rk   Úidrl   rj   r_   rm   rn   r   r}   )r%   rŸ   rq   r   )r«   r°   r*   r   r   Ú_write_message  s6   
ÿÿÿ
ÿû	
ÿÿz write_po.<locals>._write_messageNr%   r‰   )Úsort_byr   z# )r£   Úsubsequent_indentr
   r…   rž   c                 S   s"   | d t | d tƒr| d pdfS )Nr   r   r   )rk   r   )Úxr   r   r   Ú<lambda>O  s   " zwrite_po.<locals>.<lambda>©rª   z%s:%dú/z%sry   r‚   z#%s
z, zmsgid %sú|r   zmsgid_plural %sz#~ rf   )Ú_sort_messagesr³   Úheader_commentr   r   r    r]   r^   Úsortedr[   Ú	TypeErrorr7   r›   r?   Úsepr\   Úprevious_idri   r`   Úvalues)r“   r*   r£   Úno_locationÚomit_headerÚsort_outputÚsort_by_filerT   Úinclude_previousÚinclude_linenor²   r´   rµ   r%   Úcomment_headerr"   r+   rŒ   Úlocsr[   Úfilenamer&   r   )r«   r°   r*   r“   r£   r   Úwrite_poÓ  s‚   .

ÿ




ÿ
ÿ
ÿÿþ

þ
ørÌ   c                 C   s8   t | ƒ} |dkr|  ¡  | S |dkr| jdd„ d� | 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%   r‰   c                 S   s   | j S r5   )r[   )r   r   r   r   r¸   €  s    z _sort_messages.<locals>.<lambda>r¹   )rl   rh   )rY   rµ   r   r   r   r¼   r  s   
þr¼   )NNFNF)r   r�   )r�   FFFFFFT)r2   Ú
__future__r   r?   r   Úbabel.messages.catalogr   r   Ú
babel.utilr   Úbabel._compatr   r   r   r#   Ú	Exceptionr$   Úobjectr4   rO   r™   r   r    rœ   r©   rÌ   r¼   r   r   r   r   Ú<module>   s.   	/ 
E
5
=
þ  