d2dd53eab6b0ae6ebdb02c1cdffaf4498a783769
[odoo/odoo.git] / bin / addons / base / res / res_lang.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import fields, osv
23 from locale import localeconv
24 import tools
25 from tools.safe_eval import safe_eval as eval
26 from tools.translate import _
27 import locale
28 import logging
29
30 class lang(osv.osv):
31     _name = "res.lang"
32     _description = "Languages"
33
34     def install_lang(self, cr, uid, **args):
35         lang_ids = self.search(cr, uid, [('code','=', tools.config.get('lang'))])
36         if not lang_ids:
37             lang_id = self.load_lang(cr, uid, tools.config.get('lang'))
38         return True
39
40     def load_lang(self, cr, uid, lang, lang_name=None):
41         # create the language with locale information
42         fail = True
43         logger = logging.getLogger('i18n')
44         iso_lang = tools.get_iso_codes(lang)
45         for ln in tools.get_locales(lang):
46             try:
47                 locale.setlocale(locale.LC_ALL, str(ln))
48                 fail = False
49                 break
50             except locale.Error:
51                 continue
52         if fail:
53             lc = locale.getdefaultlocale()[0]
54             msg = 'Unable to get information for locale %s. Information from the default locale (%s) have been used.'
55             logger.warning(msg, lang, lc)
56
57         if not lang_name:
58             lang_name = tools.get_languages().get(lang, lang)
59
60
61         def fix_xa0(s):
62             if s == '\xa0':
63                 return '\xc2\xa0'
64             return s
65
66         lang_info = {
67             'code': lang,
68             'iso_code': iso_lang,
69             'name': lang_name,
70             'translatable': 1,
71             'date_format' : str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')),
72             'time_format' : str(locale.nl_langinfo(locale.T_FMT)),
73             'decimal_point' : fix_xa0(str(locale.localeconv()['decimal_point'])),
74             'thousands_sep' : fix_xa0(str(locale.localeconv()['thousands_sep'])),
75         }
76         lang_id = False
77         try:
78             lang_id = self.create(cr, uid, lang_info)
79             self.pool.get('ir.values').set(cr, uid, 'default', False, 'lang', ['res.partner'], lang)
80         finally:
81             tools.resetlocale()
82         return lang_id
83
84     def _get_default_date_format(self,cursor,user,context={}):
85         return '%m/%d/%Y'
86
87     def _get_default_time_format(self,cursor,user,context={}):
88         return '%H:%M:%S'
89
90     _columns = {
91         'name': fields.char('Name', size=64, required=True),
92         'code': fields.char('Locale Code', size=16, required=True, help='This field is used to set/get locales for user'),
93         'iso_code': fields.char('ISO code', size=16, required=False, help='This ISO code is the name of po files to use for translations'),
94         'translatable': fields.boolean('Translatable'),
95         'active': fields.boolean('Active'),
96         'direction': fields.selection([('ltr', 'Left-to-Right'), ('rtl', 'Right-to-Left')], 'Direction',required=True),
97         'date_format':fields.char('Date Format',size=64,required=True),
98         'time_format':fields.char('Time Format',size=64,required=True),
99         '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."),
100         'decimal_point':fields.char('Decimal Separator', size=64,required=True),
101         'thousands_sep':fields.char('Thousands Separator',size=64),
102     }
103     _defaults = {
104         'active': lambda *a: 1,
105         'translatable': lambda *a: 0,
106         'direction': lambda *a: 'ltr',
107         'date_format':_get_default_date_format,
108         'time_format':_get_default_time_format,
109         'grouping':lambda *a: '[]',
110         'decimal_point':lambda *a: '.',
111         'thousands_sep':lambda *a: ',',
112     }
113     _sql_constraints = [
114         ('name_uniq', 'unique (name)', 'The name of the language must be unique !'),
115         ('code_uniq', 'unique (code)', 'The code of the language must be unique !'),
116     ]
117
118     @tools.cache(skiparg=3)
119     def _lang_data_get(self, cr, uid, lang_id, monetary=False):
120         conv = localeconv()
121         lang_obj = self.browse(cr, uid, lang_id)
122         thousands_sep = lang_obj.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep']
123         decimal_point = lang_obj.decimal_point
124         grouping = lang_obj.grouping
125         return (grouping, thousands_sep, decimal_point)
126
127     def write(self, cr, uid, ids, vals, context=None):
128         for lang_id in ids :
129             self._lang_data_get.clear_cache(cr.dbname,lang_id= lang_id)
130         return super(lang, self).write(cr, uid, ids, vals, context)
131
132     def unlink(self, cr, uid, ids, context=None):
133         if context is None:
134             context = {}
135         languages = self.read(cr, uid, ids, ['code','active'], context=context)
136         for language in languages:
137             ctx_lang = context.get('lang')
138             if language['code']=='en_US':
139                 raise osv.except_osv(_('User Error'), _("Base Language 'en_US' can not be deleted !"))
140             if ctx_lang and (language['code']==ctx_lang):
141                 raise osv.except_osv(_('User Error'), _("You cannot delete the language which is User's Preferred Language !"))
142             if language['active']:
143                 raise osv.except_osv(_('User Error'), _("You cannot delete the language which is Active !\nPlease de-activate the language first."))
144             trans_obj = self.pool.get('ir.translation')
145             trans_ids = trans_obj.search(cr, uid, [('lang','=',language['code'])], context=context)
146             trans_obj.unlink(cr, uid, trans_ids, context=context)
147         return super(lang, self).unlink(cr, uid, ids, context=context)
148
149     def _group(self, cr, uid, ids, s, monetary=False, grouping=False, thousands_sep=''):
150         grouping = eval(grouping)
151
152         if not grouping:
153             return (s, 0)
154
155         result = ""
156         seps = 0
157         spaces = ""
158
159         if s[-1] == ' ':
160             sp = s.find(' ')
161             spaces = s[sp:]
162             s = s[:sp]
163
164         while s and grouping:
165             # if grouping is -1, we are done
166             if grouping[0] == -1:
167                 break
168             # 0: re-use last group ad infinitum
169             elif grouping[0] != 0:
170                 #process last group
171                 group = grouping[0]
172                 grouping = grouping[1:]
173             if result:
174                 result = s[-group:] + thousands_sep + result
175                 seps += 1
176             else:
177                 result = s[-group:]
178             s = s[:-group]
179             if s and s[-1] not in "0123456789":
180                 # the leading string is only spaces and signs
181                 return s + result + spaces, seps
182         if not result:
183             return s + spaces, seps
184         if s:
185             result = s + thousands_sep + result
186             seps += 1
187         return result + spaces, seps
188
189     def format(self, cr, uid, ids, percent, value, grouping=False, monetary=False):
190         """ Format() will return the language-specific output for float values"""
191
192         if percent[0] != '%':
193             raise ValueError("format() must be given exactly one %char format specifier")
194
195         lang_grouping, thousands_sep, decimal_point = self._lang_data_get(cr, uid, ids[0], monetary)
196
197         formatted = percent % value
198         # floats and decimal ints need special action!
199         if percent[-1] in 'eEfFgG':
200             seps = 0
201             parts = formatted.split('.')
202
203             if grouping:
204                 parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep)
205
206             formatted = decimal_point.join(parts)
207             while seps:
208                 sp = formatted.find(' ')
209                 if sp == -1: break
210                 formatted = formatted[:sp] + formatted[sp+1:]
211                 seps -= 1
212         elif percent[-1] in 'diu':
213             if grouping:
214                 formatted = self._group(cr,uid,ids,formatted, monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep)[0]
215
216         return formatted
217
218 #    import re, operator
219 #    _percent_re = re.compile(r'%(?:\((?P<key>.*?)\))?'
220 #                             r'(?P<modifiers>[-#0-9 +*.hlL]*?)[eEfFgGdiouxXcrs%]')
221
222 lang()
223
224
225 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: