
    1yi]c                       d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 ddl
mZ ddlmZmZ ddlmZ erdd	lmZmZ dd
lmZ  ej*                  d      Zd!dZd!dZd"dZ G d de      Z G d de      Z G d d      Z	 	 	 	 	 d#	 	 	 	 	 	 	 	 	 	 	 	 	 d$dZ ej*                  d      Z d!dZ!d%d&dZ"d'dZ#	 	 	 	 	 	 	 	 d(	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d)dZ$dddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d*dZ%	 	 	 	 	 	 d+d Z&y),z
babel.messages.pofile
~~~~~~~~~~~~~~~~~~~~~

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

:copyright: (c) 2013-2026 by the Babel Team.
:license: BSD, see LICENSE for more details.
    )annotationsN)Iterable)TYPE_CHECKINGLiteral)Locale)CatalogMessage)TextWrapper)IOAnyStr)SupportsWritez\\([\\trn"])c                L    d }d| vr| dd S t         j                  || dd       S )zReverse `escape` the given string.

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

    :param string: the string to unescape
    c                L    | j                  d      }|dk(  ry|dk(  ry|dk(  ry|S )N   n
t	r)group)matchms     W/var/www/html/content_weaver/venv/lib/python3.12/site-packages/babel/messages/pofile.pyreplace_escapesz!unescape.<locals>.replace_escapes+   s2    KKN8#X#X    \r   )_unescape_resub)stringr   s     r   unescaper"       s6    	 6a|OVAb\::r   c                    d| v rE| j                         }| j                  d      r|dd }dj                  t        t        |            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joinmapr"   )r!   escaped_liness     r   denormalizer+   ;   sT    * v~))+T")!"-Mwws8]344r   c                R   d| vr"d| vr| j                         j                         S g }d}d}| D ]U  }|dk(  r|rt        d      d}|dk(  r|st        d      d}-|dk(  r|r||z  }:|s=|j                  |       d}Q||z  }W |r|rt        d      |j                  |       |S )	zExtract locations from location comments.

    Locations are extracted while properly handling First Strong
    Isolate (U+2068) and Pop Directional Isolate (U+2069), used by
    gettext to enclose filenames with spaces and tabs in their names.
       ⁨   ⁩r%   Fzglocation comment contains more First Strong Isolate characters, than Pop Directional Isolate charactersTzglocation comment contains more Pop Directional Isolate characters, than First Strong Isolate characters )lstripsplit
ValueErrorappend)line	locationslocationin_filenamecs        r   _extract_locationsr9   Y   s     t 4{{}""$$IHK  '= J  K(] G   K#XA  *MH1 '4  J  X&r   c                  $     e Zd ZdZd fdZ xZS )PoFileErrorzDException thrown by PoParser when an invalid po file is encountered.c                X    t         |   | d|        || _        || _        || _        y )Nz on )super__init__catalogr4   lineno)selfmessager?   r4   r@   	__class__s        r   r>   zPoFileError.__init__   s0    G9D12	r   )
rB   strr?   r   r4   rD   r@   intreturnNone)__name__
__module____qualname____doc__r>   __classcell__rC   s   @r   r;   r;      s    N r   r;   c                  (     e Zd Zd fdZddZ xZS )_NormalizedStringc                T    t         |   t        t        j                  |             y N)r=   r>   r)   rD   strip)rA   argsrC   s     r   r>   z_NormalizedString.__init__   s    SYY-.r   c                F    | sydj                  t        t        |             S )Nr%   )r(   r)   r"   rA   s    r   r+   z_NormalizedString.denormalize   s    wws8T*++r   )rS   rD   rF   rG   )rF   rD   )rH   rI   rJ   r>   r+   rL   rM   s   @r   rO   rO      s    /,r   rO   c                  x    e Zd Z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y)PoFileParserzSupport class to  read messages from a ``gettext`` PO (portable object) file
    and add them to a `Catalog`

    See `read_po` for simple cases.
    c                j    || _         || _        d| _        d| _        || _        | j                          y )Nr   )r?   ignore_obsoletecounteroffsetabort_invalid_reset_message_state)rA   r?   rY   r\   s       r   r>   zPoFileParser.__init__   s5     .*!!#r   c                    g | _         g | _        g | _        g | _        g | _        g | _        d | _        d| _        d| _        d| _	        d| _
        y )NF)messagestranslationsr5   flagsuser_commentsauto_commentscontextobsoletein_msgid	in_msgstr
in_msgctxtrU   s    r   r]   z!PoFileParser._reset_message_state   sR    
r   c           
        t        | j                        dkD  rt        d | j                  D              }t        | j                  j
                        D cg c]  }d }}t        | j                        D ]O  \  }}|| j                  j
                  k\  r| j                  d| j                  d       =|j                         ||<   Q t        |      }n=| j                  d   j                         }| j                  d   d   j                         }| j                  r| j                  j                         nd}t        ||| j                  | j                  | j                  | j                   | j                  dz   |      }| j"                  r@| j$                  sC|| j                  j"                  | j                  j'                  ||      <   n|| j                  |<   | xj(                  dz  c_        | j+                          yc c}w )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              3  <   K   | ]  }|j                           y wrQ   )r+   ).0r   s     r   	<genexpr>z,PoFileParser._add_message.<locals>.<genexpr>   s     Aa!--/A   r%   z5msg has more translations than num_plurals of catalogr   N)r@   rd   )lenr_   tupleranger?   num_pluralssortedr`   _invalid_pofiler[   r+   rd   r	   r5   ra   rc   rb   re   rY   _key_forrZ   r]   )rA   msgid_r!   idxtranslationmsgctxtrB   s           r   _add_messagezPoFileParser._add_message   s   
 t}}!A4==AAE"'(@(@"ABQbBFB$*4+<+<$= 8 [$,,222((O
 )557s8 6]FMM!$002E&&q)!,88:F04$,,**,$NNJJ;;?	
 ==''OV%%dll&;&;E7&KL")DLL!!#? Cs   	Hc                   | j                   r~| j                  sa| j                  d| j                  d| j                   d   j	                          d       | j                  j                  dt               g       | j                          y y )Nr%   zmissing msgstr for msgid 'r   ')r_   r`   rs   r[   r+   r3   rO   rz   rU   s    r   _finish_current_messagez$PoFileParser._finish_current_message   sz    ==$$$$KK0q1A1M1M1O0PPQR
 !!((!->-@)AB r   c                f    |sy |d   dk(  r| j                  ||       y | j                  |||       y )Nr   ")!_process_string_continuation_line_process_keyword_line)rA   r@   r4   re   s       r   _process_message_linez"PoFileParser._process_message_line   s5    7c>224@&&vtX>r   c                P   |j                  d      \  }}}|dv r| j                          || _        |dk(  r|| _        |dv r3d| _        d| _        | j                  j                  t        |             y |dk(  rd| _        t        |      | _	        y |dk(  s|j                  d	      rmd| _        d| _        |j                  d
      \  }}}	|rt        |	d d       nd}
|dk7  rt        |      n	t               }| j                  j                  |
|g       y | j                  ||d       y )Nr/   )ru   ry   ru   )ru   msgid_pluralFTry   msgstrmsgstr[[r   r   r$   zUnknown or misformatted keyword)	partitionr}   re   r[   rh   rf   r_   r3   rO   rd   r'   rg   rE   r`   rs   )rA   r@   r4   re   keywordrv   argkwarghas_bracketidxargrw   ss               r   r   z"PoFileParser._process_keyword_line   s&   ..-C**((*  g DK//#DO DMMM  !23!78i"DO,S1DLh'"4"4Y"?!DM!DN)0):):3)?&E;&1#fSbk"qC*-+!#&;L;NA$$c1X.T6+LMr   c                   | j                   r| j                  d   }nL| j                  r| j                  d   d   }n-| j                  r| j
                  }n| j                  ||d       y |j                  |j                                y )Nr   r   z<Got line starting with " but not in msgid, msgstr or msgctxt)	rf   r_   rg   r`   rh   rd   rs   r3   rR   )rA   r4   r@   r   s       r   r   z.PoFileParser._process_string_continuation_line  sq    ==b!A^^!!"%a(A__A  O
 	r   c                   | j                          |d d }|dk(  rpt        |dd        D ]^  }|j                  d      \  }}}|r(	 | j                  j	                  |t        |      f       B| j                  j	                  |d f       ` y |dk(  rC| j                  j                  d |dd  j                         j                  d      D               y |dk(  r1|dd  j                         }|r| j                  j	                  |       y | j                  j	                  |dd  j                                y # t        $ r Y w xY w)	N   z#::z#,c              3  <   K   | ]  }|j                           y wrQ   )rR   )rk   flags     r   rl   z0PoFileParser._process_comment.<locals>.<genexpr>@  s     TtdjjlTrm   ,z#.r   )r}   r9   
rpartitionr5   r3   rE   r2   ra   extendr0   r1   rR   rc   rb   )rA   r4   prefixr6   acolonbcomments           r   _process_commentzPoFileParser._process_comment/  s4   $$&bqT>.tABx8 <&11#65!!--q#a&k: NN))8T*:;< T>JJTtABx7H7N7Ns7STTT>12hnn&G""))'2 	!!$qr(.."23% & ! !s   &D66	EEc           	        d}t        |      D ]  \  }}|j                         }|t        |t               }|s,|r%|j	                  | j
                  j                        }|d   dk(  rA|dd dk(  r&| j                  ||dd j                         d       	 | j                  |       | j                  ||        | j                          | j                  s~| j                  s| j                  s| j                   rY| j"                  j%                  t'                      | j(                  j%                  dt'               g       | j+                          yyy# t        $ r'}| j                  ||t        |             Y d}~vd}~ww xY w)aV  
        Reads from the file-like object (or iterable of string-likes) `fileobj`
        and adds any po file units found in it to the `Catalog`
        supplied to the constructor.

        All of the items in the iterable must be the same type; either `str`
        or `bytes` (decoded with the catalog charset), but not a mixture.
        Nr   #r   z#~T)re   )	enumeraterR   
isinstancerD   decoder?   charsetr   r0   r   r2   rs   r}   rZ   ra   rb   rc   r_   r3   rO   r`   rz   )rA   fileobjneeds_decoder@   r4   excs         r   parsezPoFileParser.parseM  sh    %g. 	9LFD::<D# $.dC#88{{4<<#7#78Aw#~8t#..vtABx7HSW.XE--d3 **648'	9* 	$$& ||t/A/ATEWEWMM  !2!45$$a):)<%=> FX| & E,,T63s8DDEs   E	F	"FF	c                    t        |t              sJ | j                  rt        || j                  ||      t        d|       t        d|dz    d|       y )NzWARNING:zWARNING: Problem on line r   z: )r   rD   r\   r;   r?   print)rA   r4   r@   msgs       r   rs   zPoFileParser._invalid_pofilev  sT    $$$$c4<<v>>j#)&1*Rx@Ar   N)FF)r?   r   rY   boolr\   r   rF   rG   )rF   rG   )F)r   IO[AnyStr] | Iterable[AnyStr]rF   rG   )rH   rI   rJ   rK   r>   r]   rz   r}   r   r   r   r   r   rs    r   r   rW   rW      sl     !&#	$$ $ 	$
 
$ &$P	 ?!NF"4<' RBr   rW   Fc                `    t        |||      }t        |||      }|j                  |        |S )a  Read messages from a ``gettext`` PO (portable object) file from the given
    file-like object (or an iterable of lines) 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))
    ('foo %(name)s', 'quux %(name)s')
      ([('main.py', 1)], ['fuzzy', 'python-format'])
      ([], [])
    (('bar', 'baz'), ('bar', 'baaz'))
      ([('main.py', 3)], [])
      (['A user comment'], ['An auto comment'])

    .. versionadded:: 1.0
       Added support for explicit charset argument.

    :param fileobj: the file-like object (or iterable of lines) 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   )r\   )r   rW   r   )r   r   r   rY   r   r\   r?   parsers           r   read_por   ~  s2    l VFGDG'?-PF
LLNr   zL(\s+|[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))c                    d| j                  dd      j                  dd      j                  dd      j                  dd	      j                  d
d      z  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"r   z\\r   z\tr   z\rr   z\nr   z\")replace)r!   s    r   escaper     sU     FNN4088uEMM gdE773./ /r   L   c           
        |r|dkD  rt        |      }g }| j                  d      D ]  }t        t        |            |z   |kD  rt        j	                  |      }|j                          |sEg }d}|rot        t        |d               dz
  |z   }	||	z   |k  r%|j                  |j                                ||	z  }n"|s|j                  |j                                n|ro|j                  dj                  |             |r|j                  |        n| j                  d      }t        |      dk  rt        |       S |r|d   s|d= |dxx   dz  cc<   ddj                  |D cg c]  }|t        |      z    c}      z   S c c}w )	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""
)	rn   r&   r   WORD_SEPr1   reverser3   popr(   )
r!   r   width	prefixlenlinesr4   chunksbufsizelengths
             r   	normalizer     sq   0 K	%%d+ 	#D6$< 9,u4!- CD !$VF2J%7!81!<y!H&=50JJvzz|4 FND#& !$

6::< 8! ! LL.   T")	#, !!$'
5zQf~ U2Y"Ib	T	DII5I4 5IJJJIs   +F	
c                r    d| vrd| vr| S | j                  d      sd| z   } | j                  d      s| dz  } | S )zEnclose filenames which include white spaces or tabs.

    Do the same as gettext and enclose filenames which contain white
    spaces or tabs with First Strong Isolate (U+2068) and Pop
    Directional Isolate (U+2069).
    r/   r   r-   r.   )r'   endswith)filenames    r   _enclose_filename_if_necessaryr     sM     (t83x(h&X&HOr   Tc
           
         d}
|rd}
n|rd}
t        |||	||||
|      D ]?  }t        |t              r|j                  |j                  d      }| j                  |       A y)a  Write a ``gettext`` PO (portable object) template file for a given
    message catalog to the provided file-like object.

    >>> catalog = Catalog()
    >>> catalog.add('foo %(name)s', locations=[('main.py', 1)],
    ...             flags=('fuzzy',))
    <Message...>
    >>> catalog.add(('bar', '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
    NrB   r6   rY   include_linenoinclude_previousno_locationomit_headersort_byr   backslashreplace)generate_por   rD   encoder   write)r   r?   r   r   r   sort_outputsort_by_filerY   r   r   r   r4   s               r   write_por   "  ss    p G	'%)	  dC ;;w0BCDdr   r   c             #     K   rdkD  rnd}t        |d      t        dd      }	dfd	}
d fd		}t         |
      D ]  }|j                  sY|r j                  }rAdkD  r<g }|j	                         D ]  }||	j                  |      z  } dj                  |      }| d |j                  D ]  } |
|      E d{     |j                  D ]  } |
|d      E d{     |sg }	 t        |j                  d       }|D ]R  \  }}|j                  t        j                  d      }t        |      }|r
|r| d|d}||vsB|j!                  |       T  |
dj                  |      d      E d{    |j"                  r-ddj                  dgt        |j"                               d |j$                  rv|rt |
dt'        |j$                  d          d      E d{    t)        |j$                        dkD  r/t'        |j$                  d         } |
d| d      E d{     ||      E d{    d  |sbt         j*                  j-                         |
      D ]9  }|j                  D ]  } |
|      E d{      ||d      E d{    d ; yy7 7 # t        $ r |j                  }Y w xY w7 ^7 7 7 7 K7 9w)zYield text strings representing a ``gettext`` PO (portable object) file.

    See `write_po()` for a more detailed description.
    r   r   F)r   break_long_wordsz# )r   subsequent_indentr   r%   c              3  l   K   j                  |       D ]  }d| d|j                          d  y w)Nr   r/   r   )wraprR   )r   r   r4   comment_wrappers      r   _format_commentz$generate_po.<locals>._format_comment  s<     #((1 	0DfXQtzz|nB//	0s   14c           
   3    K   t        | j                  t        t        f      r| j                  r | dt        | j                  |       d | dt        | j                  d   |       d | dt        | j                  d   |       d t        j                        D ],  }	 | j                  |   }| d	|d
dt        ||       d . y | j                  r | dt        | j                  |       d | dt        | j                  |       d | dt        | j                  xs d|       d y # t        $ r d}Y w xY ww)Nzmsgctxt )r   r   r   msgid r   msgid_plural r   r%   r   dz] zmsgstr )
r   idlistro   rd   r   rp   rq   r!   
IndexError)rB   r   rw   r!   r?   r   s       r   _format_messagez$generate_po.<locals>._format_message  st    gjj4-07??6Y^)_(``bccHF9WZZ]6QV#W"XXZ[[HM)GJJqM&X]*^)__abbW001 c $^^C0F  Awb6&X]1^0__abbc 7??6Y^)_(``bccHF9WZZe#T"UUWXXHGIgnn.B6Y^$_#``bcc "  F s+   B+E/E>BEEEEE)r   r   N.)r   c                H    | d   t        | d   t              xr | d   xs dfS )Nr   r   r   )r   rE   )xs    r   <lambda>zgenerate_po.<locals>.<lambda>  s(    1Q4AaD#)>)G1Q4)M2"N r   key/r   r   r/   r   z, r   )r   |r   r   z#~ )r%   )r
   _sort_messagesr   header_commentr&   r   r(   rb   rc   rr   r5   	TypeErrorr   ossepr   r3   ra   previous_idr   rn   re   values)r?   rY   r   r   r   r   r   r   comment_widthheader_wrapperr   r   rB   comment_headerr   r4   r   locsr5   r   r@   r6   norm_previous_idr   s   `      `               @r   r   r   o  s,    " #uqyEbM!NO uW\]N0d& "'7; 5zz$33N*557 7D^0066E7!%5!1#$B'',, 	0G&w///	0,, 	<G&ws;;;	< D."%%N	 %. * &#++BFFC89(Cn"*1VAJ7H4'KK)* 'sxx~cBBB==dii <fW]]&; <=>bAA#3&7#6#6q#9GHI   7&&'!+#,W-@-@-C5#Q *];K:L+MVYZZZ"7+++
k5n %##%
 	G #00 4*73334&wu===J	 U 0;  .#--	. C
 [+ 4=s   CK.	J?
"K.,K-
K.8KAK./K.K!A3K.7K$8AK.>K&?K.K(AK.K*K.2K,3K.K.KK.KK.$K.&K.(K.*K.,K.c                z    t        |       } |dk(  r| j                          | S |dk(  r| j                  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]
    rB   r6   c                    | j                   S rQ   )r5   )r   s    r   r   z _sort_messages.<locals>.<lambda>  s
    AKK r   r   )r   sort)r_   r   s     r   r   r     sC     H~H) O 
J	/0Or   )r!   rD   rF   rD   )r4   rD   rF   z	list[str])NNFNF)r   r   r   zLocale | str | Noner   
str | NonerY   r   r   r   r\   r   rF   r   )r%   r   )r!   rD   r   rD   r   rE   rF   rD   )r   rD   rF   rD   )r   FFFFFFT)r   zSupportsWrite[bytes]r?   r   r   rE   r   r   r   r   r   r   r   r   rY   r   r   r   r   r   rF   rG   )r?   r   rY   r   r   r   r   r   r   r   r   r   r   %Literal['message', 'location'] | Noner   rE   rF   zIterable[str])r_   zIterable[Message]r   r   rF   zlist[Message])'rK   
__future__r   r   recollections.abcr   typingr   r   
babel.corer   babel.messages.catalogr   r	   
babel.utilr
   r   r   	_typeshedr   compiler   r"   r+   r9   	Exceptionr;   r   rO   rW   r   r   r   r   r   r   r   r   r   r   r   <module>r     s+  	 # 	 	 $ )  3 "!' rzz/*;6 </d) , ,\B \BB #'!9*99 9 	9
 9 9 9x 2::/":Kz( !"J!JJ J 	J
 J J J J J J 
J` ""59ll l 	l
 l l l 3l l l^2 r   