3

X'ícÕUã@s¬dZddlZddlZddlmZmZddlmZmZdd„Z	dd„Z
Gd	d
„d
eƒZGdd„dƒZ
Gd
d„dƒZddd„ZejdƒZdd„Zddd„Zd dd„Zdd„ZdS)!zî
    babel.messages.pofile
    ~~~~~~~~~~~~~~~~~~~~~

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

    :copyright: (c) 2013-2022 by the Babel Team.
    :license: BSD, see LICENSE for more details.
éN)ÚCatalogÚMessage)ÚwraptextÚ_cmpcCs"dd„}tjdƒj||dd…ƒS)z¿Reverse `escape` the given string.

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

    :param string: the string to unescape
    cSs2|jdƒ}|dkrdS|dkr"dS|dkr.dS|S)NéÚnÚ
Útú	Úrú
)Úgroup)ÚmatchÚm©rú6/tmp/pip-build-gk9425m9/babel/babel/messages/pofile.pyÚreplace_escapess
z!unescape.<locals>.replace_escapesz\\([\\trn"])réÿÿÿÿ)ÚreÚcompileÚsub)ÚstringrrrrÚunescapes

rcCsFd|kr:|jƒ}|jdƒr&|dd…}tt|ƒ}dj|ƒSt|ƒSdS)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
    rz""rNÚ)Ú
splitlinesÚ
startswithÚmaprÚjoin)rZ
escaped_linesÚlinesrrrÚdenormalize*s


rcs eZdZdZ‡fdd„Z‡ZS)ÚPoFileErrorzDException thrown by PoParser when an invalid po file is encountered.cs,tƒj|›d|›ƒ||_||_||_dS)Nz on )ÚsuperÚ__init__ÚcatalogÚlineÚlineno)ÚselfÚmessager#r$r%)Ú	__class__rrr"KszPoFileError.__init__)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r"Ú
__classcell__rr)r(rr Isr c@sleZdZdd„Zdd„Zdd„Zdd„Zd	d
„Zdd„Zd
d„Z	dd„Z
dd„Zdd„Zdd„Z
dd„ZdS)Ú_NormalizedStringcGs"g|_x|D]}|j|ƒqWdS)N)Ú_strsÚappend)r&ÚargsÚargrrrr"Ts
z_NormalizedString.__init__cCs|jj|jƒƒdS)N)r/r0Ústrip)r&Úsrrrr0Ysz_NormalizedString.appendcCsdjtt|jƒƒS)Nr)rrrr/)r&rrrr\sz_NormalizedString.denormalizecCs
t|jƒS)N)Úboolr/)r&rrrÚ__bool___sz_NormalizedString.__bool__cCstjj|jƒS)N)ÚosÚlineseprr/)r&rrrÚ__repr__bsz_NormalizedString.__repr__cCs|sdStt|ƒt|ƒƒS)Nr)rÚstr)r&ÚotherrrrÚ__cmp__esz_NormalizedString.__cmp__cCs|j|ƒdkS)Nr)r<)r&r;rrrÚ__gt__ksz_NormalizedString.__gt__cCs|j|ƒdkS)Nr)r<)r&r;rrrÚ__lt__nsz_NormalizedString.__lt__cCs|j|ƒdkS)Nr)r<)r&r;rrrÚ__ge__qsz_NormalizedString.__ge__cCs|j|ƒdkS)Nr)r<)r&r;rrrÚ__le__tsz_NormalizedString.__le__cCs|j|ƒdkS)Nr)r<)r&r;rrrÚ__eq__wsz_NormalizedString.__eq__cCs|j|ƒdkS)Nr)r<)r&r;rrrÚ__ne__zsz_NormalizedString.__ne__N)r)r*r+r"r0rr6r9r<r=r>r?r@rArBrrrrr.Rsr.c@sreZdZdZddddgZddd„Zd	d
„Zdd„Zd
d„Zddd„Z	ddd„Z
dd„Zdd„Zdd„Z
dd„ZdS)Ú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_pluralFcCs*||_||_d|_d|_||_|jƒdS)Nr)r#Úignore_obsoleteÚcounterÚoffsetÚ
abort_invalidÚ_reset_message_state)r&r#rHrKrrrr"szPoFileParser.__init__cCsFg|_g|_g|_g|_g|_g|_d|_d|_d|_d|_	d|_
dS)NF)ÚmessagesÚtranslationsÚ	locationsÚflagsÚ
user_commentsÚ
auto_commentsÚcontextÚobsoleteÚin_msgidÚ	in_msgstrÚ
in_msgctxt)r&rrrrL•sz!PoFileParser._reset_message_statec
Cs@|jjƒt|jƒdkr.tdd„|jDƒƒ}n|jdjƒ}t|ttfƒr¨dd„t|j	j
ƒDƒ}x<|jD]2\}}||j	j
krŽ|jd|jdƒqh|jƒ||<qhWt|ƒ}n|jddjƒ}|j
rÌ|j
jƒ}nd	}t||t|jƒt|jƒ|j|j|jd|d
}|jr|js&||j	j|<n
||j	|<|jd7_|jƒ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.
        rcss|]}|jƒVqdS)N)r)Ú.0rrrrú	<genexpr>©sz,PoFileParser._add_message.<locals>.<genexpr>rcSsg|]}d‘qS)rr)rXÚ_rrrú
<listcomp>­sz-PoFileParser._add_message.<locals>.<listcomp>rz5msg has more translations than num_plurals of catalogN)r%rS)rNÚsortÚlenrMÚtuplerÚ
isinstanceÚlistÚranger#Únum_pluralsÚ_invalid_pofilerJrSrrOÚsetrPrRrQrTrHrIrL)r&rDrÚidxÚtranslationrFr'rrrÚ_add_message¢s2


zPoFileParser._add_messagecCs|jr|jƒdS)N)rMrg)r&rrrÚ_finish_current_messageÅsz$PoFileParser._finish_current_messagecCs*|jdƒr|j||ƒn|j|||ƒdS)Nú")rÚ!_process_string_continuation_lineÚ_process_keyword_line)r&r%r$rTrrrÚ_process_message_lineÉs
z"PoFileParser._process_message_linecCsHxt|jD]X}y0|j|ƒr:|t|ƒdkr:|t|ƒd…}PWqtk
r^|j||dƒYqXqW|j||dƒdS|dkr†|jƒ||_|dkrš||_|dkrÀd|_d	|_	|j
jt|ƒƒn„|d
kr*d|_	d	|_
|jdƒr|dd…jddƒ\}}|jjt|ƒt|ƒgƒn|jjd
t|ƒgƒn|dkrDd	|_t|ƒ|_dS)Nú ú[z$Keyword must be followed by a stringz0Start of line didn't match any expected keyword.rDrFrGFTrErú]r)rmrn)rDrF)rDrG)Ú	_keywordsrr]Ú
IndexErrorrcrhrTrJrWrUrMr0r.rVÚsplitrNÚintrS)r&r%r$rTÚkeywordr2reÚmsgrrrrkÏs8

z"PoFileParser._process_keyword_linecCsV|jr|jd}n6|jr(|jdd}n |jr6|j}n|j||dƒdS|j|ƒdS)Nrz<Got line starting with " but not in msgid, msgstr or msgctxtrr)rUrMrVrNrWrSrcr0)r&r$r%r4rrrrjøsz.PoFileParser._process_string_continuation_linec
Cs>|jƒ|dd…jdƒr¦xˆ|dd…jƒjƒD]p}|jdƒ}|dkryt||dd…ƒ}Wntk
rtw0YnX|jj|d|…|fƒq0|jj|dfƒq0Wn”|dd…jdƒrêx€|dd…jƒjdƒD]}|j	j|j
ƒƒqÐWnP|dd…jdƒr"|dd…j
ƒ}|r:|jj|ƒn|jj|dd…j
ƒƒdS)Nrú:érú,Ú.)
rhrÚlstriprrÚrfindrsÚ
ValueErrorrOr0rPr3rRrQ)r&r$ÚlocationÚposr%ÚflagÚcommentrrrÚ_process_comments&
zPoFileParser._process_commentcCsàxŒt|ƒD]€\}}|jƒ}t|tƒs2|j|jjƒ}|s8q
|jdƒr~|dd…jdƒrr|j||dd…j	ƒddqŠ|j
|ƒq
|j||ƒq
W|jƒ|jrÜ|j
s°|js°|jrÜ|jjtdƒƒ|jjd	tdƒgƒ|jƒdS)
        Reads from the file-like object `fileobj` and adds any po file
        units found in it to the `Catalog` supplied to the constructor.
        ú#rNú~rwT)rTz""r)Ú	enumerater3r_r:Údecoder#ÚcharsetrrlrzrrhrIrPrQrRrMr0r.rNrg)r&Úfileobjr%r$rrrÚparses 

zPoFileParser.parsecCsJt|tƒst‚|jr$t||j||ƒ‚td|ƒtdj|dt|ƒƒƒdS)NzWARNING:z!WARNING: Problem on line {0}: {1}r)	r_r:ÚAssertionErrorrKr r#ÚprintÚformatÚrepr)r&r$r%rurrrrc<s

zPoFileParser._invalid_pofileN)FF)F)F)r)r*r+r,rpr"rLrgrhrlrkrjrrˆrcrrrrrCs

#

)rCFcCs*t|||d}t|||d}|j|ƒ|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 io 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†)rK)rrCrˆ)r‡rrŽrHr†rKr#ÚparserrrrÚread_poGs/
rzL(\s+|[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))cCs0d|jddƒjddƒjddƒjdd	ƒjd
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\rrz\nriz\")Úreplace)rrrrÚescapeƒs
r“réLc
s>|rÜ|dkrÜtˆƒ}g}xÌ|jdƒD]²}tt|ƒƒ||krÌtj|ƒ}|jƒx†|rÈg}d}xX|r´tt|d
ƒƒd|}	||	|krž|j|jƒƒ||	7}q^|s°|j|jƒƒPq^W|jdj|ƒƒqPWq$|j|ƒq$Wn
|jdƒ}t|ƒdkrút|ƒS|r"|dr"|d=|d
d7<ddj‡fdd	„|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
    rTrwrrrz""
csg|]}ˆt|ƒ‘qSr)r“)rXr$)Úprefixrrr[Ïsznormalize.<locals>.<listcomp>rrrr)	r]rr“ÚWORD_SEPrrÚreverser0Úpopr)
rr•ÚwidthÚ	prefixlenrr$ÚchunksÚbufÚsizeÚlr)r•rÚ	normalize•s6


rŸTc
sŠd#‡fdd„	‰‡‡fdd„‰d$‡‡fdd„	}
d%‡‡‡fdd	„	}d
}|rLd}n|rTd}xÜtˆ|d
D]Ê}
|
jsÆ|rvqdˆj}ˆrºˆdkrºg}x"|jƒD]}|t|ˆdd7}q–Wdj|ƒ}ˆ|dƒx|
jD]}|
|ƒqÎWx|
jD]}|
|ddqæW|s¨g}yt|
j	dd„d}Wnt
k
r6|
j	}YnXx\|D]T\}}|rj|	rjd|jtj
dƒ|f}nd|jtj
dƒ}||kr>|j|ƒq>W|
dj|ƒdd|
jrΈddjdgt|
jƒƒƒ|
jr |r |
dˆ|
jdƒddt|
jƒd kr |
d!ˆ|
jd ƒdd||
ƒˆdƒqdW|s†xJtˆjjƒ|d
D]4}
x|
jD]}|
|ƒqZW||
d"dˆdƒqNWd
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 io 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
    rcst||ˆdS)N)r•r™)rŸ)Úkeyr•)r™rrÚ
_normalizeszwrite_po.<locals>._normalizecs&t|tƒr|jˆjdƒ}ˆj|ƒdS)NÚbackslashreplace)r_r:Úencoder†Úwrite)Útext)r#r‡rrÚ_writes
zwrite_po.<locals>._writecsBˆrˆdkrˆ}nd}x&t||ƒD]}ˆd||jƒfƒq"WdS)Nrr”z#%s %s
)rr3)r€r•Ú_widthr$)r¦r™rrÚ_write_comments
z write_po.<locals>._write_commentcst|jttfƒr¼|jr.ˆd|ˆ|j|ƒfƒˆd|ˆ|jd|ƒfƒˆd|ˆ|jd|ƒfƒx¨tˆjƒD]D}y|j|}Wntk
rœd}YnXˆd||ˆ||ƒfƒqrWnT|jrڈd|ˆ|j|ƒfƒˆd|ˆ|j|ƒfƒˆd|ˆ|jpd|ƒfƒdS)	Nz
%smsgctxt %s
z%smsgid %s
rz%smsgid_plural %s
rrz%smsgstr[%d] %s
z%smsgstr %s
)	r_Úidr`r^rSrarbrrq)r'r•rer)r¡r¦r#rrÚ_write_messages(
z write_po.<locals>._write_messageNr'r})Úsort_byrz# )r™Úsubsequent_indentrry)r•cSs"|dt|dtƒr|dpdfS)Nrrr)r_rs)ÚxrrrÚ<lambda>Nszwrite_po.<locals>.<lambda>)r z%s:%dú/z%srmrvz#%s
z, zmsgid %sú|rzmsgid_plural %sz#~ )r)r)r)Ú_sort_messagesr©Zheader_commentrrrrQrRÚsortedrOÚ	TypeErrorr’r7Úsepr0rPZprevious_idr]rTÚvalues)r‡r#r™Zno_locationZomit_headerZsort_outputZsort_by_filerHZinclude_previousZinclude_linenor¨rªr«r'Zcomment_headerrr$r€ZlocsrOÚfilenamer%r}r)r¡r¦r#r‡r™rÚwrite_poÒst.


r·cCs6t|ƒ}|dkr|jƒn|dkr2|jdd„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}cSs|jS)N)rO)rrrrr®sz _sort_messages.<locals>.<lambda>)r )r`r\)rMr«rrrr±ss

r±)NNFNF)rr”)r”FFFFFFT)r,r7rZbabel.messages.catalogrrZ
babel.utilrrrrÚ	Exceptionr r.rCrrr–r“rŸr·r±rrrrÚ<module>
s&	-I
5

=