
    Ki7                         S r SSKrSSKrSSKrSSKrSSKrSSKJr  SSKJ	r	  SSK
Jr  SSKJr  SSKJr  \R                   " S5      r " S	 S
5      rS\S\S\4S jrg)z)Localization system using Mozilla Fluent.    N)Pathformat_list)FluentBundle)compile_messages)FtlResourcezplaypalace.localizationc                      \ rS rSr% Sr0 r\\\4   \	S'   Sr
\S-  \	S'   Sr\S-  \	S'   Sr\\	S'   S	r\\	S
'   SrSrSrSr\\   S-  \	S'   \S\SS4S j5       r\S\4S j5       r\SS.S\\-  S\\   S-  SS4S jj5       r\S2S j5       r\S\S\4S j5       r\S\S\S\\\   \4   4S j5       r\S\S\S\S-  4S j5       r\SS.S\S \\   S\S!\S\4
S" jj5       r\S\S-  4S# j5       r \S\S\S \\   SS4S$ j5       r!S%r"\S\S&\S\4S' j5       r#\S\S(\\   S\4S) j5       r$\S\S(\\   S\4S* j5       r%\S\\   4S+ j5       r&\ S3S,S-.S.\S/\S\\\4   4S0 jjj5       r'S1r(g)4Localization   z
Localization system using Mozilla Fluent via fluent-compiler.

Loads .ftl files from the locales directory and provides message
rendering with variable substitution.
_bundlesN_locales_dir
_cache_dirT_cache_enabledF_warmup_active1PLAYPALACE_DISABLE_LOCALE_CACHEPLAYPALACE_LOCALE_CACHE_DIR_enabled_localesactivereturnc                     Xl         g Nr   )clsr   s     ;c:\Users\dbart\PlayPalace11\server\messages\localization.pyset_warmup_activeLocalization.set_warmup_active%   s    #    c                     U R                   $ r   r   r   s    r   is_warmup_activeLocalization.is_warmup_active)   s    !!!r   )enabled_localeslocales_dirr#   c                   [        U5      U l        0 U l        [        R                  R                  U R                  S5      R                  5       R                  5       nUS;  U l	        SU l
        Ub  1 UkSiU l        gSU l        g)zInitialize the localization system with a locales directory.

Args:
    locales_dir: Path to the locales directory.
    enabled_locales: If set, only load these locales (plus ``en`` as
        fallback).  ``None`` loads all locales.
 >   r   onyestrueNen)r   r   r   osenvironget_CACHE_DISABLE_ENVstriplowerr   r   r   )r   r$   r#   disable_caches       r   initLocalization.init-   sy      ,

s'='=rBHHJPPR*2LL&#;_#;d#;C #'C r   c                 @   U R                   c#  [        S[        R                  S9  [	        S5      eU R                   R                  5       (       a  U R                   R                  5       (       d1  [        SU R                    S3[        R                  S9  [	        S5      eSnU R                   R                  5        Ha  nUR                  5       (       d  M  U R                  b  UR                  U R                  ;  a  MC  Sn U R                  UR                  5        Mc     U(       d1  [        SU R                    S3[        R                  S9  [	        S5      eg! [         a9  n[        S	UR                   S
U 3[        R                  S9  [	        S5      UeSnAff = f)z'Pre-load all locale bundles at startup.N0ERROR: Localization directory is not configured.file   zERROR: Localization directory 'z ' is missing or not a directory.FTz/ERROR: Failed to load localization bundle for 'z': z&' does not contain any locale bundles.)r   printsysstderr
SystemExitexistsis_diriterdirr   name_get_bundleRuntimeError)r   found_locale
locale_direxcs       r   preload_bundlesLocalization.preload_bundles@   sk    #D3::VQ-&&((0@0@0G0G0I0I1#2B2B1CCcdZZ Q-**224J$$&&##/JOO3K_K_4_L-
0 5 1#2B2B1CCijZZ Q-    -EjooEVVYZ]Y^_ !m,-s   E
F$4FFlocalec                    XR                   ;   a  U R                   U   $ U R                  c#  [        S[        R                  S9  [        S5      eU R                  U-  nUnUR                  5       (       dM  U R                  S-  nSnUR                  5       (       d'  [        SU S3[        R                  S9  [        S5      eU R                  X#5      u  pEU R                  X55      nUc  U R                  X4U5      nX`R                   U'   U$ )z$Get or create a bundle for a locale.r5   r6   r8   r*   z(ERROR: No localization files found for 'z' or fallback 'en'.)
r   r   r9   r:   r;   r<   r=   _load_locale_payloads_load_bundle_from_cache_compile_bundle)r   rH   rD   actual_localepayloadsfingerprintbundles          r   rA   Localization._get_bundlee   s     \\!<<''#D3::VQ-%%.
  ""))D0J M$$&&>vhFYZ !m# # 9 9* T,,]H>((+NF%Vr   rD   rM   c           	         [        UR                  S5      5      nU(       d'  [        SU S3[        R                  S9  [        S5      e[        R                  " 5       nUR                  U R                  R                  S5      5        UR                  UR                  S5      5        / nU H  nUR                  SS9nUR                  U5        UR                  S5      nUR                  UR                  R                  S5      5        UR                  [        U5      R                  SS	S
S95        UR                  [        R                  " U5      R!                  5       5        M     XTR#                  5       4$ )z4Read locale files and compute a content fingerprint.z*.ftlz+ERROR: No .ftl localization files found in .r6   r8   utf-8encoding   bigF)signed)sortedglobr9   r:   r;   r<   hashlibsha256update_CACHE_VERSIONencode	read_textappendr@   lento_bytesdigest	hexdigest)	r   rD   rM   	ftl_filesre   rN   ftl_filetextencodeds	            r   rJ   "Localization._load_locale_payloads   s,    :??734	=j\KZZ Q-!c((//89m**734 !H%%w%7DOOD!kk'*GMM(--..w78MM#g,//5/GHMM'..188:; " ))+++r   rO   c                    U R                  5       nUc  gX1-  U S3-  nUR                  5       (       d  g [        R                  " UR	                  SS95      nUR                  S5      U R                  :w  a  [        S5      eUR                  S5      U:w  a  [        S5      eUR                  S	5      U:w  a  [        S
5      eUS   n[        U[        5      (       d  [        S5      e U R                  XUSS9$ ! [         a:    [        R                  SUSS9   UR                  5         O! [         a     Of = f gf = f)z$Load a cached bundle when available.N.jsonrT   rU   versionzCache version mismatchrO   zCache fingerprint mismatchrH   zCache locale mismatchrN   zCache payloads missingz(Discarding corrupt locale cache for '%s'T)exc_infoFwrite_cache)_resolve_cache_dirr=   jsonloadsra   r-   r_   
ValueError
isinstancelist	ExceptionLOGdebugunlinkFileNotFoundErrorrL   )r   rM   rO   
cache_root
cache_pathpayloadrN   s          r   rK   $Localization._load_bundle_from_cache   sM    ++-
/[M2GG
  ""	jj!5!5w!5!GHG{{9%););; !9::{{=)[8 !=>>{{8$5 !899z*Hh-- !9:: . ""=KUZ"[[  	II@-Z^I_!!#$ 	s6   B2C: :D>D+*D>+
D85D>7D88D>=D>rp   rN   rq   c                &   U Vs/ s H  n[         R                  " U5      PM     nn[        X5      n[        R	                  [
        5      nXl        UR                  Ul        UR                  Ul
        U(       a  U R                  XU5        U$ s  snf )z-Compile locale files and persist cache entry.)r   from_stringr   object__new__r   rH   message_functions_compiled_messageserrors_compilation_errors_write_cache_entry)	r   rM   rN   rO   rq   ri   	resourcescompiledrP   s	            r   rL   Localization._compile_bundle   sz     @HHxt[,,T2x	H#M=-%$,$>$>!%-__"""=xH Is    Bc                 j   U R                   (       d  gU R                  b  U R                  $ [        R                  R	                  U R
                  5      nU(       a  [        U5      nO+U R                  b  U R                  R                  S-  S-  nOgUR                  SSS9  X l        U R                  $ )z(Resolve (or create) the cache directory.Nz.cachelocalesTparentsexist_ok)
r   r   r+   r,   r-   _CACHE_DIR_ENVr   r   parentmkdir)r   basepaths      r   rr   Localization._resolve_cache_dir   s     !!>>%>>!zz~~c001:D)##**X5	AD

4$
/~~r   c                    U R                  5       nUc  gXA-  nUR                  SSS9  U R                  UUUS.nXR S3-  nXR S3-  nUR                  [        R
                  " U5      SS9  [        R                  " Xx5        UR                  S	5       H  n	X:X  a  M
   U	R                  5         M     g! [         a     M-  f = f)
z,Persist compiled bundle artifacts for reuse.NTr   )rn   rO   rH   rN   z.tmprm   rT   rU   z*.json)rr   r   r_   
write_textrs   dumpsr+   replacer[   r{   OSError)
r   rM   rO   rN   r}   	entry_dirr   tmp_path
final_pathcacheds
             r   r   Localization._write_cache_entry   s     ++-
.	t4))&# 	
 d33=!66
DJJw/'B


8(nnX.F#	 /
  s   B33
C Cu   ⁨⁩
message_idc                 .    U R                  U5      nUR                  X#5      u  pVU(       a  [        R                  SX!U5        U R                   H  nUR                  US5      nM     U$ ! [         a    [        R                  SX!5        SU S3s $ f = f)z
Get a localized message.

Args:
    locale: The locale code (e.g., 'en', 'es').
    message_id: The message ID from the .ftl file.
    **kwargs: Variables to substitute into the message.

Returns:
    The formatted message string.
z*Fluent formatting errors for '%s' [%s]: %sr&   z-Failed to format message '%s' for locale '%s'[])rA   formatry   warning_BIDI_CHARSr   rx   	exception)r   rH   r   kwargsrP   resultr   chars           r   r-   Localization.get   s    	%__V,F#]]:>NF@
 b1 (M 	%MM? zl!$$	%s   A(A+ +&BBitemsc                     [        USUS9$ )z
Format a list with 'and' conjunction using Babel.

Args:
    locale: The locale code.
    items: List of items to format.

Returns:
    Formatted list string (e.g., "A, B, and C").
standardstylerH   r   r   rH   r   s      r   format_list_andLocalization.format_list_and   s     5
6BBr   c                     [        USUS9$ )z
Format a list with 'or' conjunction using Babel.

Args:
    locale: The locale code.
    items: List of items to format.

Returns:
    Formatted list string (e.g., "A, B, or C").
orr   r   r   s      r   format_list_orLocalization.format_list_or.  s     5V<<r   c                    ^  T R                   c  [        S5      e[        U 4S jT R                   R                  5        5       5      $ )a  Return sorted language codes from the locales directory.

Only returns locales that are enabled in the server configuration.
Unlike :meth:`get_available_languages`, this only scans the
filesystem and never triggers bundle compilation, so it is safe
to call during warmup.
=Localization not initialized. Call Localization.init() first.c              3      >#    U  HQ  nUR                  5       (       d  M  TR                  b  UR                  TR                  ;   d  MC  UR                  v   MS     g 7fr   )r>   r   r@   ).0rD   r   s     r   	<genexpr>:Localization.get_available_locale_codes.<locals>.<genexpr>G  sN      
8
  "  %%-CDXDX1X JOO8s   A%A	A)r   rB   rZ   r?   r    s   `r   get_available_locale_codes'Localization.get_available_locale_codes<  sF     #^__ 
!..668
 
 	
r   r*   )fallbackdisplay_languager   c                >   U R                   c  [        S5      e0 nU R                   R                  5        Vs/ s HO  nUR                  5       (       d  M  U R                  b  UR
                  U R                  ;   d  MC  UR
                  PMQ     nn[        U5       Hz  nSU 3nU(       a  U R                  X5      nOU R                  Xg5      nXSU S34;   a  X!:w  a  U R                  X'5      nXSU S34;   a  US:w  a  U R                  SU5      nXU'   M|     U$ s  snf )u  
Get a dictionary of available languages.

Args:
    display_language: The locale to use for displaying language names.
                      If empty, each language name is shown in its own
                      language (e.g., "English" for en, "中文" for zh).
    fallback: The locale to use if a language name is not found
                     in the display language. Defaults to "en".

Returns:
    Dictionary mapping language codes to language names.
r   z	language-r   r   r*   )r   rB   r?   r>   r   r@   rZ   r-   )	r   r   r   r   rD   r   locale_coder   r@   s	            r   get_available_languages$Localization.get_available_languagesN  s0   " #^__
 "..668
8
  "  %%-CDXDX1X JOO8 	 
 "'?K$[M2Jww/< ww{7 a
|1$5668;Wwwx4 a
|1$5668t;KwwtZ0"&;# +& 5
s   D%D=D )r   N)r&   ))__name__
__module____qualname____firstlineno____doc__r   dictstrr   __annotations__r   r   r   r   boolr   r_   r.   r   r   setclassmethodr   r!   rw   r2   rF   rA   tuplerJ   rK   rL   rr   r   r   r-   r   r   r   r   __static_attributes__r   r   r   r
   r
      s    )+Hd3$%* $L$+$"Jt"ND ND N:2N(,c#ho,$t $ $ $ " " " RV (tcz (tCy4?O ([_ ( ($ "  " H    : ,t ,C ,ERVWZR[]`R`La , ,2 \C \c \l]aNa \ \>  ! s) 	  
 & 4$;  " s  PTUXPY ^b  4 !K% %# %C % %> CS Cc Cs C C =C =S	 =c = = 
49 
 
" %'0<@0"0690	c3h0 0r   r
   rH   r   r   c                 0    [         R                  " X40 UD6$ )z0Convenience function to get a localized message.)r
   r-   )rH   r   r   s      r   get_messager     s    F9&99r   )r   r\   rs   loggingr+   r:   pathlibr   babel.listsr   fluent_compiler.bundler   fluent_compiler.compilerr   fluent_compiler.resourcer   	getLoggerry   r
   r   r   r   r   r   <module>r      s`    /    	 
  # / 5 0 12l l^: : :3 :r   