2017-01-20 03:27:23 +03:00
#!/usr/bin/env python
#
# This script is based on https://github.com/django/django/blob/master/django/core/management/commands/compilemessages.py
# It has been modified to run without Django, because the virtual environment
# is not available when `make languages` is invoked.
#
import codecs
import datetime
import locale
import os
import sys
from decimal import Decimal
from subprocess import PIPE , Popen
def is_writable ( path ) :
# Known side effect: updating file access/modified time to current time if
# it is writable.
try :
with open ( path , ' a ' ) :
os . utime ( path , None )
except ( IOError , OSError ) :
return False
return True
def has_bom ( fn ) :
with open ( fn , ' rb ' ) as f :
sample = f . read ( 4 )
return sample . startswith ( ( codecs . BOM_UTF8 , codecs . BOM_UTF16_LE , codecs . BOM_UTF16_BE ) )
def popen_wrapper ( args , os_err_exc_type = StandardError , stdout_encoding = ' utf-8 ' ) :
"""
Friendly wrapper around Popen .
Returns stdout output , stderr output and OS status code .
"""
try :
p = Popen ( args , shell = False , stdout = PIPE , stderr = PIPE , close_fds = os . name != ' nt ' )
except OSError as e :
strerror = force_text ( e . strerror , DEFAULT_LOCALE_ENCODING , strings_only = True )
raise Exception ( os_err_exc_type , os_err_exc_type ( ' Error executing %s : %s ' %
2017-01-23 22:50:35 +03:00
( args [ 0 ] , strerror ) ) , sys . exc_info ( ) [ 2 ] )
2017-01-20 03:27:23 +03:00
output , errors = p . communicate ( )
return (
force_text ( output , stdout_encoding , strings_only = True , errors = ' strict ' ) ,
force_text ( errors , DEFAULT_LOCALE_ENCODING , strings_only = True , errors = ' replace ' ) ,
p . returncode
)
def get_system_encoding ( ) :
"""
The encoding of the default system locale but falls back to the given
fallback encoding if the encoding is unsupported by python or could
not be determined . See tickets #10335 and #5846
"""
try :
encoding = locale . getdefaultlocale ( ) [ 1 ] or ' ascii '
codecs . lookup ( encoding )
except Exception :
encoding = ' ascii '
return encoding
_PROTECTED_TYPES = (
type ( None ) , int , float , Decimal , datetime . datetime , datetime . date , datetime . time ,
)
def is_protected_type ( obj ) :
""" Determine if the object instance is of a protected type.
Objects of protected types are preserved as - is when passed to
force_text ( strings_only = True ) .
"""
return isinstance ( obj , _PROTECTED_TYPES )
DEFAULT_LOCALE_ENCODING = get_system_encoding ( )
def force_text ( s , encoding = ' utf-8 ' , strings_only = False , errors = ' strict ' ) :
"""
Similar to smart_text , except that lazy instances are resolved to
strings , rather than kept as lazy objects .
If strings_only is True , don ' t convert (some) non-string-like objects.
"""
# Handle the common case first for performance reasons.
if issubclass ( type ( s ) , str ) :
return s
if strings_only and is_protected_type ( s ) :
return s
try :
if not issubclass ( type ( s ) , str ) :
if isinstance ( s , bytes ) :
s = str ( s , encoding , errors )
else :
s = str ( s )
else :
# Note: We use .decode() here, instead of str(s, encoding,
# errors), so that if s is a SafeBytes, it ends up being a
# SafeText at the end.
s = s . decode ( encoding , errors )
except UnicodeDecodeError as e :
if not isinstance ( s , Exception ) :
raise Exception ( s , * e . args )
else :
# If we get to here, the caller has passed in an Exception
# subclass populated with non-ASCII bytestring data without a
# working unicode method. Try to handle this without raising a
# further exception by individually forcing the exception args
# to unicode.
s = ' ' . join ( force_text ( arg , encoding , strings_only , errors )
for arg in s )
return s
if __name__ == " __main__ " :
basedirs = [ os . path . join ( ' conf ' , ' locale ' ) , ' locale ' ]
# Walk entire tree, looking for locale directories
for dirpath , dirnames , filenames in os . walk ( ' . ' , topdown = True ) :
for dirname in dirnames :
if dirname == ' locale ' :
basedirs . append ( os . path . join ( dirpath , dirname ) )
basedirs = set ( map ( os . path . abspath , filter ( os . path . isdir , basedirs ) ) )
for basedir in basedirs :
dirs = [ basedir ]
locations = [ ]
for ldir in dirs :
for dirpath , dirnames , filenames in os . walk ( ldir ) :
locations . extend ( ( dirpath , f ) for f in filenames if f . endswith ( ' .po ' ) )
if locations :
program = ' msgfmt '
program_options = [ ' --check-format ' ]
for i , ( dirpath , f ) in enumerate ( locations ) :
print ' processing file %s in %s \n ' % ( f , dirpath )
po_path = os . path . join ( dirpath , f )
if has_bom ( po_path ) :
raise Exception ( " The %s file has a BOM (Byte Order Mark). "
2017-01-23 22:50:35 +03:00
" Django only supports .po files encoded in "
" UTF-8 and without any BOM. " % po_path )
2017-01-20 03:27:23 +03:00
base_path = os . path . splitext ( po_path ) [ 0 ]
# Check writability on first location
if i == 0 and not is_writable ( ( base_path + ' .mo ' ) ) :
raise Exception ( " The po files under %s are in a seemingly not writable location. "
2017-01-23 22:50:35 +03:00
" mo files will not be updated/created. " % dirpath )
2017-01-20 03:27:23 +03:00
args = [ program ] + program_options + [
' -o ' , ( base_path + ' .mo ' ) , ( base_path + ' .po ' )
]
output , errors , status = popen_wrapper ( args )
if status :
if errors :
msg = " Execution of %s failed: %s " % ( program , errors )
else :
msg = " Execution of %s failed " % program
raise Exception ( msg )