2009-10-20 10:52:23 +00:00
# -*- coding: utf-8 -*-
2007-11-26 15:46:03 +00:00
##############################################################################
2009-11-20 11:33:49 +00:00
#
2009-10-14 12:32:15 +00:00
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 12:32:15 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2007-11-26 15:46:03 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-10-14 12:32:15 +00:00
# GNU Affero General Public License for more details.
2007-11-26 15:46:03 +00:00
#
2009-10-14 12:32:15 +00:00
# You should have received a copy of the GNU Affero General Public License
2009-11-20 11:33:49 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2007-11-26 15:46:03 +00:00
#
##############################################################################
2011-01-12 16:06:08 +00:00
import locale
2012-12-10 15:27:23 +00:00
from locale import localeconv
2011-01-12 16:06:08 +00:00
import logging
2011-05-16 10:07:12 +00:00
import re
2011-01-12 16:06:08 +00:00
2012-12-10 15:27:23 +00:00
from openerp import tools
from openerp . osv import fields , osv
from openerp . tools . safe_eval import safe_eval as eval
from openerp . tools . translate import _
2010-12-30 09:17:34 +00:00
2012-01-24 13:17:05 +00:00
_logger = logging . getLogger ( __name__ )
2007-11-26 15:46:03 +00:00
class lang ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = " res.lang "
2008-12-10 13:35:06 +00:00
_description = " Languages "
2009-05-11 19:45:28 +00:00
2011-01-12 16:06:08 +00:00
_disallowed_datetime_patterns = tools . DATETIME_FORMATS_MAP . keys ( )
_disallowed_datetime_patterns . remove ( ' % y ' ) # this one is in fact allowed, just not good practice
2010-12-30 09:17:34 +00:00
def install_lang ( self , cr , uid , * * args ) :
2011-05-17 12:00:27 +00:00
"""
This method is called from openerp / addons / base / base_data . xml to load
some language and set it as the default for every partners . The
language is set via tools . config by the RPC ' create ' method on the
' db ' object . This is a fragile solution and something else should be
found .
"""
2011-01-07 12:32:07 +00:00
lang = tools . config . get ( ' lang ' )
if not lang :
return False
lang_ids = self . search ( cr , uid , [ ( ' code ' , ' = ' , lang ) ] )
2011-01-03 12:19:49 +00:00
if not lang_ids :
2011-06-03 08:57:58 +00:00
self . load_lang ( cr , uid , lang )
ir_values_obj = self . pool . get ( ' ir.values ' )
2011-05-17 12:00:27 +00:00
default_value = ir_values_obj . get ( cr , uid , ' default ' , False , [ ' res.partner ' ] )
2011-01-04 05:45:12 +00:00
if not default_value :
2011-05-16 15:05:34 +00:00
ir_values_obj . set ( cr , uid , ' default ' , False , ' lang ' , [ ' res.partner ' ] , lang )
2010-12-30 09:17:34 +00:00
return True
def load_lang ( self , cr , uid , lang , lang_name = None ) :
# create the language with locale information
fail = True
iso_lang = tools . get_iso_codes ( lang )
for ln in tools . get_locales ( lang ) :
try :
locale . setlocale ( locale . LC_ALL , str ( ln ) )
fail = False
break
except locale . Error :
continue
if fail :
lc = locale . getdefaultlocale ( ) [ 0 ]
msg = ' Unable to get information for locale %s . Information from the default locale ( %s ) have been used. '
2012-01-24 13:17:05 +00:00
_logger . warning ( msg , lang , lc )
2010-12-30 09:17:34 +00:00
if not lang_name :
2012-02-23 10:30:45 +00:00
lang_name = tools . ALL_LANGUAGES . get ( lang , lang )
2010-12-30 09:17:34 +00:00
def fix_xa0 ( s ) :
2011-01-10 15:19:12 +00:00
""" Fix badly-encoded non-breaking space Unicode character from locale.localeconv(),
coercing to utf - 8 , as some platform seem to output localeconv ( ) in their system
encoding , e . g . Windows - 1252 """
2010-12-30 09:17:34 +00:00
if s == ' \xa0 ' :
return ' \xc2 \xa0 '
return s
2011-01-12 16:06:08 +00:00
def fix_datetime_format ( format ) :
""" Python ' s strftime supports only the format directives
that are available on the platform ' s libc, so in order to
be 100 % cross - platform we map to the directives required by
the C standard ( 1989 version ) , always available on platforms
with a C standard implementation . """
for pattern , replacement in tools . DATETIME_FORMATS_MAP . iteritems ( ) :
format = format . replace ( pattern , replacement )
2011-01-10 15:19:12 +00:00
return str ( format )
2010-12-30 09:17:34 +00:00
lang_info = {
' code ' : lang ,
' iso_code ' : iso_lang ,
' name ' : lang_name ,
' translatable ' : 1 ,
2011-01-12 16:06:08 +00:00
' date_format ' : fix_datetime_format ( locale . nl_langinfo ( locale . D_FMT ) ) ,
' time_format ' : fix_datetime_format ( locale . nl_langinfo ( locale . T_FMT ) ) ,
2010-12-30 09:17:34 +00:00
' decimal_point ' : fix_xa0 ( str ( locale . localeconv ( ) [ ' decimal_point ' ] ) ) ,
' thousands_sep ' : fix_xa0 ( str ( locale . localeconv ( ) [ ' thousands_sep ' ] ) ) ,
}
2011-01-03 12:19:49 +00:00
lang_id = False
2010-12-30 09:17:34 +00:00
try :
2011-01-03 12:19:49 +00:00
lang_id = self . create ( cr , uid , lang_info )
2010-12-30 09:17:34 +00:00
finally :
tools . resetlocale ( )
2011-01-03 12:19:49 +00:00
return lang_id
2010-12-30 09:17:34 +00:00
2011-01-12 16:06:08 +00:00
def _check_format ( self , cr , uid , ids , context = None ) :
for lang in self . browse ( cr , uid , ids , context = context ) :
for pattern in self . _disallowed_datetime_patterns :
if ( lang . time_format and pattern in lang . time_format ) \
or ( lang . date_format and pattern in lang . date_format ) :
return False
return True
2015-01-16 16:24:47 +00:00
def _check_grouping ( self , cr , uid , ids , context = None ) :
for lang in self . browse ( cr , uid , ids , context = context ) :
try :
if not all ( isinstance ( x , int ) for x in eval ( lang . grouping ) ) :
return False
except Exception :
return False
return True
2011-11-07 15:19:49 +00:00
def _get_default_date_format ( self , cursor , user , context = None ) :
2009-04-06 15:59:16 +00:00
return ' % m/ %d / % Y '
2009-05-11 19:45:28 +00:00
2011-11-07 15:19:49 +00:00
def _get_default_time_format ( self , cursor , user , context = None ) :
2008-12-10 13:35:06 +00:00
return ' % H: % M: % S '
2009-05-11 19:45:28 +00:00
2008-07-22 14:24:36 +00:00
_columns = {
' name ' : fields . char ( ' Name ' , size = 64 , required = True ) ,
2010-12-03 16:09:07 +00:00
' code ' : fields . char ( ' Locale Code ' , size = 16 , required = True , help = ' This field is used to set/get locales for user ' ) ,
' iso_code ' : fields . char ( ' ISO code ' , size = 16 , required = False , help = ' This ISO code is the name of po files to use for translations ' ) ,
2008-07-22 14:24:36 +00:00
' translatable ' : fields . boolean ( ' Translatable ' ) ,
' active ' : fields . boolean ( ' Active ' ) ,
2008-12-10 13:35:06 +00:00
' direction ' : fields . selection ( [ ( ' ltr ' , ' Left-to-Right ' ) , ( ' rtl ' , ' Right-to-Left ' ) ] , ' Direction ' , required = True ) ,
' date_format ' : fields . char ( ' Date Format ' , size = 64 , required = True ) ,
' time_format ' : fields . char ( ' Time Format ' , size = 64 , required = True ) ,
2009-11-20 11:33:49 +00:00
' grouping ' : fields . char ( ' Separator Format ' , size = 64 , required = True , help = " The Separator Format should be like [,n] where 0 < n :starting from Unit digit.-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500;[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. Provided ' , ' as the thousand separator in each case. " ) ,
2008-12-10 13:35:06 +00:00
' decimal_point ' : fields . char ( ' Decimal Separator ' , size = 64 , required = True ) ,
' thousands_sep ' : fields . char ( ' Thousands Separator ' , size = 64 ) ,
2008-07-22 14:24:36 +00:00
}
_defaults = {
2012-11-02 09:47:05 +00:00
' active ' : 1 ,
' translatable ' : 0 ,
' direction ' : ' ltr ' ,
2008-12-10 13:35:06 +00:00
' date_format ' : _get_default_date_format ,
' time_format ' : _get_default_time_format ,
2012-11-02 09:47:05 +00:00
' grouping ' : ' [] ' ,
' decimal_point ' : ' . ' ,
' thousands_sep ' : ' , ' ,
2008-07-22 14:24:36 +00:00
}
2009-01-29 23:20:17 +00:00
_sql_constraints = [
( ' name_uniq ' , ' unique (name) ' , ' The name of the language must be unique ! ' ) ,
( ' code_uniq ' , ' unique (code) ' , ' The code of the language must be unique ! ' ) ,
]
2010-05-14 13:04:25 +00:00
2011-01-12 16:06:08 +00:00
_constraints = [
2015-01-16 16:24:47 +00:00
( _check_format , ' Invalid date/time format directive specified. Please refer to the list of allowed directives, displayed when you edit a language. ' , [ ' time_format ' , ' date_format ' ] ) ,
( _check_grouping , " The Separator Format should be like [,n] where 0 < n :starting from Unit digit.-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500;[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. Provided ' , ' as the thousand separator in each case. " , [ ' grouping ' ] )
2011-01-12 16:06:08 +00:00
]
2011-06-08 03:03:30 +00:00
@tools.ormcache ( skiparg = 3 )
2013-12-03 21:19:03 +00:00
def _lang_data_get ( self , cr , uid , lang , monetary = False ) :
if type ( lang ) in ( str , unicode ) :
lang = self . search ( cr , uid , [ ( ' code ' , ' = ' , lang ) ] ) or \
self . search ( cr , uid , [ ( ' code ' , ' = ' , ' en_US ' ) ] )
lang = lang [ 0 ]
2008-12-10 13:35:06 +00:00
conv = localeconv ( )
2013-12-03 21:19:03 +00:00
lang_obj = self . browse ( cr , uid , lang )
2008-12-10 13:35:06 +00:00
thousands_sep = lang_obj . thousands_sep or conv [ monetary and ' mon_thousands_sep ' or ' thousands_sep ' ]
2010-03-08 06:41:15 +00:00
decimal_point = lang_obj . decimal_point
grouping = lang_obj . grouping
2012-12-14 12:38:03 +00:00
return grouping , thousands_sep , decimal_point
2010-03-08 06:41:15 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2015-10-26 10:41:49 +00:00
if ' code ' in vals :
2015-10-26 16:27:01 +00:00
for rec in self . browse ( cr , uid , ids , context ) :
if rec . code != vals [ ' code ' ] :
2015-10-26 10:41:49 +00:00
raise osv . except_osv ( _ ( ' User Error ' ) , _ ( " Language code cannot be modified. " ) )
2010-03-08 06:41:15 +00:00
for lang_id in ids :
2011-08-31 18:22:16 +00:00
self . _lang_data_get . clear_cache ( self )
2010-03-08 06:41:15 +00:00
return super ( lang , self ) . write ( cr , uid , ids , vals , context )
2010-10-11 13:01:41 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
languages = self . read ( cr , uid , ids , [ ' code ' , ' active ' ] , context = context )
for language in languages :
2010-12-09 15:04:42 +00:00
ctx_lang = context . get ( ' lang ' )
2010-10-11 13:01:41 +00:00
if language [ ' code ' ] == ' en_US ' :
2013-04-29 07:29:38 +00:00
raise osv . except_osv ( _ ( ' User Error ' ) , _ ( " Base Language ' en_US ' can not be deleted! " ) )
2010-12-09 15:04:42 +00:00
if ctx_lang and ( language [ ' code ' ] == ctx_lang ) :
2013-04-29 07:29:38 +00:00
raise osv . except_osv ( _ ( ' User Error ' ) , _ ( " You cannot delete the language which is User ' s Preferred Language! " ) )
2010-10-11 13:01:41 +00:00
if language [ ' active ' ] :
2013-04-29 07:29:38 +00:00
raise osv . except_osv ( _ ( ' User Error ' ) , _ ( " You cannot delete the language which is Active! \n Please de-activate the language first. " ) )
2010-10-11 13:01:41 +00:00
trans_obj = self . pool . get ( ' ir.translation ' )
trans_ids = trans_obj . search ( cr , uid , [ ( ' lang ' , ' = ' , language [ ' code ' ] ) ] , context = context )
trans_obj . unlink ( cr , uid , trans_ids , context = context )
return super ( lang , self ) . unlink ( cr , uid , ids , context = context )
2013-12-03 21:19:03 +00:00
#
# IDS: can be a list of IDS or a list of XML_IDS
#
2011-09-13 14:46:00 +00:00
def format ( self , cr , uid , ids , percent , value , grouping = False , monetary = False , context = None ) :
2008-12-10 13:35:06 +00:00
""" Format() will return the language-specific output for float values """
if percent [ 0 ] != ' % ' :
raise ValueError ( " format() must be given exactly one %c har format specifier " )
formatted = percent % value
2013-10-21 11:14:57 +00:00
2008-12-10 13:35:06 +00:00
# floats and decimal ints need special action!
2013-10-21 11:14:57 +00:00
if grouping :
lang_grouping , thousands_sep , decimal_point = \
self . _lang_data_get ( cr , uid , ids [ 0 ] , monetary )
eval_lang_grouping = eval ( lang_grouping )
if percent [ - 1 ] in ' eEfFgG ' :
parts = formatted . split ( ' . ' )
parts [ 0 ] , _ = intersperse ( parts [ 0 ] , eval_lang_grouping , thousands_sep )
formatted = decimal_point . join ( parts )
elif percent [ - 1 ] in ' diu ' :
2011-05-16 09:09:08 +00:00
formatted = intersperse ( formatted , eval_lang_grouping , thousands_sep ) [ 0 ]
2008-12-10 13:35:06 +00:00
return formatted
# import re, operator
# _percent_re = re.compile(r'%(?:\((?P<key>.*?)\))?'
# r'(?P<modifiers>[-#0-9 +*.hlL]*?)[eEfFgGdiouxXcrs%]')
2007-11-26 15:46:03 +00:00
lang ( )
2011-05-13 09:31:21 +00:00
def split ( l , counts ) :
"""
>> > split ( " hello world " , [ ] )
[ ' hello world ' ]
>> > split ( " hello world " , [ 1 ] )
[ ' h ' , ' ello world ' ]
>> > split ( " hello world " , [ 2 ] )
[ ' he ' , ' llo world ' ]
>> > split ( " hello world " , [ 2 , 3 ] )
[ ' he ' , ' llo ' , ' world ' ]
>> > split ( " hello world " , [ 2 , 3 , 0 ] )
[ ' he ' , ' llo ' , ' wo ' , ' rld ' ]
>> > split ( " hello world " , [ 2 , - 1 , 3 ] )
[ ' he ' , ' llo world ' ]
"""
res = [ ]
saved_count = len ( l ) # count to use when encoutering a zero
for count in counts :
2011-11-29 14:18:09 +00:00
if not l :
break
2011-05-13 09:31:21 +00:00
if count == - 1 :
break
if count == 0 :
while l :
res . append ( l [ : saved_count ] )
l = l [ saved_count : ]
break
res . append ( l [ : count ] )
l = l [ count : ]
saved_count = count
if l :
res . append ( l )
return res
2011-05-16 10:07:12 +00:00
intersperse_pat = re . compile ( ' ([^0-9]*)([^ ]*)(.*) ' )
2011-05-13 09:31:21 +00:00
def intersperse ( string , counts , separator = ' ' ) :
"""
2011-05-16 09:03:12 +00:00
See the asserts below for examples .
2011-05-13 09:31:21 +00:00
"""
2011-05-16 10:07:12 +00:00
left , rest , right = intersperse_pat . match ( string ) . groups ( )
2011-05-13 09:31:21 +00:00
def reverse ( s ) : return s [ : : - 1 ]
splits = split ( reverse ( rest ) , counts )
res = separator . join ( map ( reverse , reverse ( splits ) ) )
2011-05-16 10:07:12 +00:00
return left + res + right , len ( splits ) > 0 and len ( splits ) - 1 or 0
2011-05-13 09:31:21 +00:00
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: