B
    }Ž<[gP  ã               @   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 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-2018 by the Babel Team.
    :license: BSD, see LICENSE for more details.
é    )Úprint_functionN)ÚCatalogÚMessage)Úwraptext)Ú	text_typec             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   úJlib/python3.7/site-packages/Babel-2.6.0-py3.7.egg/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   sF   d| kr:|   ¡ }|  d¡r&|dd… }tt|ƒ}d |¡S t| ƒS d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	   z""r   NÚ )Ú
splitlinesÚ
startswithÚmapr   Újoin)r   Z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%   O   s    zPoFileError.__init__)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r%   Ú__classcell__r   r   )r*   r   r!   M   s   r!   c               @   s,   e Zd Zdd„ Zdd„ Zdd„ Zdd„ Zd	S )
Ú_NormalizedStringc             G   s"   g | _ x|D ]}|  |¡ qW d S )N)Ú_strsÚappend)r)   ÚargsÚargr   r   r   r%   X   s    
z_NormalizedString.__init__c             C   s   | j  | ¡ ¡ d S )N)r1   r2   Ústrip)r)   Úsr   r   r   r2   ]   s    z_NormalizedString.appendc             C   s   d  tt| jƒ¡S )Nr   )r   r   r   r1   )r)   r   r   r   r    `   s    z_NormalizedString.denormalizec             C   s
   t | jƒS )N)Úboolr1   )r)   r   r   r   Ú__nonzero__c   s    z_NormalizedString.__nonzero__N)r+   r,   r-   r%   r2   r    r8   r   r   r   r   r0   V   s   r0   c               @   sr   e Zd ZdZddddg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 )Nr   )r'   Úignore_obsoleteÚcounterÚoffsetÚabort_invalidÚ_reset_message_state)r)   r'   r>   rA   r   r   r   r%   u   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_msgctxt)r)   r   r   r   rB   }   s    z!PoFileParser._reset_message_statec          
   C   s@  | j  ¡  t| jƒdkr.tdd„ | jD ƒƒ}n| jd  ¡ }t|ttfƒr¨dd„ t| j	j
ƒD ƒ}x<| j D ]2\}}|| j	j
krŽ|  d| jd¡ qh| ¡ ||< qhW t|ƒ}n| j d d  ¡ }| jrÌ| 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   )rN   Ú_r   r   r   rO   •   s    r   z5msg has more translations than num_plurals of catalogN)r#   rI   )rD   ÚsortÚlenrC   Útupler    Ú
isinstanceÚlistÚranger'   Únum_pluralsÚ_invalid_pofiler@   rI   r   rE   ÚsetrF   rH   rG   rJ   r>   r?   rB   )r)   r:   r   ÚidxZtranslationr<   r"   r   r   r   Ú_add_messageŠ   s2    


zPoFileParser._add_messagec             C   s   | j r|  ¡  d S )N)rC   r[   )r)   r   r   r   Ú_finish_current_message­   s    z$PoFileParser._finish_current_messagec             C   s*   |  d¡r|  ||¡ n|  |||¡ d S )Nú")r   Ú!_process_string_continuation_lineÚ_process_keyword_line)r)   r#   r(   rJ   r   r   r   Ú_process_message_line±   s    
z"PoFileParser._process_message_linec             C   s  xL| j D ]0}| |¡r|t|ƒ dkr|t|ƒd … }P qW |  ||d¡ d S |dkr^|  ¡  || _|dkrr|| _|dkr˜d| _d| _| j	 
t|ƒ¡ n€|dkrþd| _d| _| d	¡rè|d
d …  dd
¡\}}| j 
t|ƒt|ƒg¡ n| j 
dt|ƒg¡ n|dkrd| _t|ƒ| _d S )N)ú ú[z0Start of line didn't match any expected keyword.)r:   r<   r:   )r:   r=   FTr;   rb   r   ú]r   r<   )Ú	_keywordsr   rR   rX   r\   rJ   r@   rM   rK   rC   r2   r0   rL   ÚsplitrD   ÚintrI   )r)   r#   r(   rJ   Úkeywordr4   rZ   Úmsgr   r   r   r_   ·   s2    

z"PoFileParser._process_keyword_linec             C   sV   | j r| jd }n6| jr(| jd d }n | jr6| j}n|  ||d¡ d S | |¡ d S )Nr   r   z<Got line starting with " but not in msgid, msgstr or msgctxt)rK   rC   rL   rD   rM   rI   rX   r2   )r)   r(   r#   r6   r   r   r   r^   Ý   s    z.PoFileParser._process_string_continuation_linec          	   C   s>  |   ¡  |dd …  d¡r¦xˆ|dd …  ¡  ¡ D ]p}| d¡}|dkryt||d d … ƒ}W n tk
rt   w0Y nX | j |d |… |f¡ q0| j |d f¡ q0W n”|dd …  d¡rêx€|dd …  ¡  d¡D ]}| j	 | 
¡ ¡ qÐW nP|dd …  d¡r"|dd …  
¡ }|r:| j |¡ n| j |dd …  
¡ ¡ d S )Nr   ú:é   r   ú,Ú.)r\   r   Úlstripre   Úrfindrf   Ú
ValueErrorrE   r2   rF   r5   rH   rG   )r)   r(   ÚlocationÚposr#   ÚflagÚcommentr   r   r   Ú_process_commenté   s&    
zPoFileParser._process_commentc             C   sÞ   xŒt |ƒD ]€\}}| ¡ }t|tƒs2| | jj¡}|s8q
| d¡r~|dd…  d¡rr| j||dd…  	¡ dd qŠ|  
|¡ q
|  ||¡ q
W |  ¡  | jsÚ| js®| js®| jrÚ| j tdƒ¡ | j d	tdƒg¡ |  ¡  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ú~rj   T)rJ   z""r   )Ú	enumerater5   rT   r   Údecoder'   Úcharsetr   r`   rm   rt   r\   r?   rF   rG   rH   rC   r2   r0   rD   r[   )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   )rA   r!   r'   Úprintr&   )r)   r(   r#   rh   r   r   r   rX   !  s    
zPoFileParser._invalid_pofileN)FF)F)F)r+   r,   r-   r.   rd   r%   rB   r[   r\   r`   r_   r^   rt   r{   rX   r   r   r   r   r9   g   s   
#

&r9   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Údomainry   )rA   )r   r9   r{   )rz   r}   r~   r>   ry   rA   r'   Úparserr   r   r   Úread_po(  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\nr]   z\")Úreplace)r   r   r   r   Úescaped  s
    rƒ   r   éL   c       
         s<  |rÜ|dkrÜt ˆ ƒ}g }xÌ|  d¡D ]²}t t|ƒƒ| |krÌt |¡}| ¡  x†|rÈg }d}xX|r´t t|d ƒƒd | }	||	 |k rž| | ¡ ¡ ||	7 }q^|s°| | ¡ ¡ P q^W | d |¡¡ qPW q$| |¡ q$W n
|  d¡}t |ƒdkrút| ƒ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   Trj   r   r   r   r	   z""
c                s   g | ]}ˆ t |ƒ ‘qS r   )rƒ   )rN   r(   )Úprefixr   r   rO   °  s    znormalize.<locals>.<listcomp>)	rR   r   rƒ   ÚWORD_SEPre   Úreverser2   Úpopr   )
r   r…   ÚwidthZ	prefixlenr   r(   ZchunksZbufÚsizeÚlr   )r…   r   Ú	normalizev  s6    


rŒ   Tc
          	      sT  d ‡fdd„	‰ ‡‡fdd„‰d!‡‡fdd„	}
d"‡ ‡‡fdd	„	}d
}|rLd}n|rTd}x¦t ˆ|dD ]”}|jsÆ|rvqdˆj}ˆrºˆdkrºg }x"| ¡ D ]}|t|ˆdd7 }q–W d |¡}ˆ|d ƒ x|jD ]}|
|ƒ qÎW x|jD ]}|
|dd qæW |srg }xZt|j	ƒD ]L\}}|rB|	rB| 
d| tjd¡|f ¡ n| 
d| tjd¡ ¡ qW |
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ƒ qdW |sPxJt ˆj ¡ |dD ]4}x|jD ]}|
|ƒ q$W ||dd ˆdƒ qW 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á  s    zwrite_po.<locals>._normalizec                s&   t | tƒr|  ˆ jd¡} ˆ | ¡ d S )NÚbackslashreplace)rT   r   Úencodery   Úwrite)Útext)r'   rz   r   r   Ú_writeä  s    
zwrite_po.<locals>._writec                sB   ˆrˆdkrˆ}nd}x&t | |ƒD ]}ˆ d|| ¡ f ƒ q"W d S )Nr   r„   z#%s %s
)r   r5   )rs   r…   Z_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 ƒ x¨tˆjƒD ]D}y| j| }W n tk
rœ   d}Y nX ˆd||ˆ ||ƒf ƒ qrW nT| jrÚˆ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
)	rT   ÚidrU   rS   rI   rV   rW   r   Ú
IndexError)r"   r…   rZ   r   )rŽ   r“   r'   r   r   Ú_write_messageó  s(    
z write_po.<locals>._write_messageNr"   rp   )Úsort_byr   z# )r‰   Zsubsequent_indentr	   rl   )r…   z%s:%dú/z%sra   ri   z#%s
z, zmsgid %sú|r   zmsgid_plural %sz#~ )r   )r   )r   )Ú_sort_messagesr•   Zheader_commentr   r   r   rG   rH   ÚsortedrE   r2   r‚   ÚosÚseprF   Zprevious_idrR   rJ   Úvalues)rz   r'   r‰   Zno_locationZomit_headerZsort_outputZsort_by_filer>   Zinclude_previousZinclude_linenor”   r—   r˜   r"   Zcomment_headerr   r(   rs   ZlocsÚfilenamer#   r   )rŽ   r“   r'   rz   r‰   r   Úwrite_po³  sf    .

r¡   c             C   s6   t | ƒ} |dkr|  ¡  n|dkr2| 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"   rp   c             S   s   | j S )N)rE   )r   r   r   r   Ú<lambda>U  s    z _sort_messages.<locals>.<lambda>)r   )rU   rQ   )rC   r˜   r   r   r   r›   G  s    

r›   )NNFNF)r   r„   )r„   FFFFFFT)r.   Z
__future__r   r   r   Zbabel.messages.catalogr   r   Z
babel.utilr   Zbabel._compatr   r   r    Ú	Exceptionr!   Úobjectr0   r9   r€   r   r†   rƒ   rŒ   r¡   r›   r   r   r   r   Ú<module>   s*   	 B
5

=  
 