378 lines
12 KiB
Python
378 lines
12 KiB
Python
##############################################################################
|
|
#
|
|
# Copyright (c) 2004-2008 Tiny SPRL (http://tiny.be) All Rights Reserved.
|
|
#
|
|
# $Id$
|
|
#
|
|
# WARNING: This program as such is intended to be used by professional
|
|
# programmers who take the whole responsability of assessing all potential
|
|
# consequences resulting from its eventual inadequacies and bugs
|
|
# End users who are looking for a ready-to-use solution with commercial
|
|
# garantees and support are strongly adviced to contract a Free Software
|
|
# Service Company
|
|
#
|
|
# This program is Free Software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# 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
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
###############################################################################
|
|
|
|
import os
|
|
from os.path import join
|
|
import fnmatch
|
|
import csv, xml.dom, re
|
|
import osv, tools, pooler
|
|
import ir
|
|
import netsvc
|
|
from tools.misc import UpdateableStr
|
|
|
|
|
|
class TINY(csv.excel):
|
|
lineterminator = '\n'
|
|
|
|
csv.register_dialect("TINY", TINY)
|
|
|
|
#
|
|
# TODO: a caching method
|
|
#
|
|
def translate(cr, uid, name, source_type, lang, source=None):
|
|
if source and name:
|
|
cr.execute('select value from ir_translation where lang=%s and type=%s and name=%s and src=%s', (lang, source_type, str(name), source))
|
|
elif name:
|
|
cr.execute('select value from ir_translation where lang=%s and type=%s and name=%s', (lang, source_type, str(name)))
|
|
elif source:
|
|
cr.execute('select value from ir_translation where lang=%s and type=%s and src=%s', (lang, source_type, source))
|
|
res_trans = cr.fetchone()
|
|
res = res_trans and res_trans[0] or False
|
|
return res
|
|
|
|
def translate_code(cr, uid, source, context):
|
|
lang = context.get('lang', False)
|
|
if lang:
|
|
return translate(cr, uid, None, 'code', lang, source)
|
|
else:
|
|
return source
|
|
_ = lambda source: translate_code(cr, uid, source, context)
|
|
|
|
# Methods to export the translation file
|
|
|
|
def trans_parse_xsl(de):
|
|
res = []
|
|
for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]:
|
|
if n.hasAttribute("t"):
|
|
for m in [j for j in n.childNodes if (j.nodeType == j.TEXT_NODE)]:
|
|
l = m.data.strip().replace('\n',' ')
|
|
if len(l):
|
|
res.append(l.encode("utf8"))
|
|
res.extend(trans_parse_xsl(n))
|
|
return res
|
|
|
|
def trans_parse_rml(de):
|
|
res = []
|
|
for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]:
|
|
for m in [j for j in n.childNodes if (j.nodeType == j.TEXT_NODE)]:
|
|
string_list = [s.replace('\n', ' ').strip() for s in re.split('\[\[.+?\]\]', m.data)]
|
|
for s in string_list:
|
|
if s:
|
|
res.append(s.encode("utf8"))
|
|
res.extend(trans_parse_rml(n))
|
|
return res
|
|
|
|
def trans_parse_view(de):
|
|
res = []
|
|
if de.hasAttribute("string"):
|
|
s = de.getAttribute('string')
|
|
if s:
|
|
res.append(s.encode("utf8"))
|
|
if de.hasAttribute("sum"):
|
|
s = de.getAttribute('sum')
|
|
if s:
|
|
res.append(s.encode("utf8"))
|
|
for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]:
|
|
res.extend(trans_parse_view(n))
|
|
return res
|
|
|
|
# tests whether an object is in a list of modules
|
|
def in_modules(object_name, modules):
|
|
if 'all' in modules:
|
|
return True
|
|
|
|
module_dict = {
|
|
'ir': 'base',
|
|
'res': 'base',
|
|
'workflow': 'base',
|
|
}
|
|
module = object_name.split('.')[0]
|
|
module = module_dict.get(module, module)
|
|
return module in modules
|
|
|
|
def trans_generate(lang, modules, dbname=None):
|
|
logger = netsvc.Logger()
|
|
if not dbname:
|
|
dbname=tools.config['db_name']
|
|
pool = pooler.get_pool(dbname)
|
|
trans_obj = pool.get('ir.translation')
|
|
model_data_obj = pool.get('ir.model.data')
|
|
cr = pooler.get_db(dbname).cursor()
|
|
uid = 1
|
|
l = pool.obj_pool.items()
|
|
l.sort()
|
|
out = [["type","name","res_id","src","value"]]
|
|
|
|
to_translate = []
|
|
|
|
if 'all' in modules:
|
|
cr.execute('select name,model,res_id,module from ir_model_data')
|
|
else:
|
|
cr.execute('select name,model,res_id,module from ir_model_data where module in ('+','.join(['%s']*len(modules))+')', modules)
|
|
|
|
for (xml_name,model,res_id,module) in cr.fetchall():
|
|
xml_name = module+'.'+xml_name
|
|
obj = pool.get(model).browse(cr, uid, res_id)
|
|
if model=='ir.ui.view':
|
|
d = xml.dom.minidom.parseString(obj.arch)
|
|
for t in trans_parse_view(d.documentElement):
|
|
to_translate.append(["view", obj.model, 0, t])
|
|
elif model=='ir.actions.wizard':
|
|
service_name = 'wizard.'+obj.wiz_name
|
|
obj2 = netsvc._service[service_name]
|
|
for state_name, state_def in obj2.states.iteritems():
|
|
if 'result' in state_def:
|
|
result = state_def['result']
|
|
if result['type'] != 'form':
|
|
continue
|
|
name = obj.wiz_name + ',' + state_name
|
|
|
|
# export fields
|
|
for field_name, field_def in result['fields'].iteritems():
|
|
if 'string' in field_def:
|
|
source = field_def['string']
|
|
res_name = name + ',' + field_name
|
|
to_translate.append(["wizard_field", res_name,0,source])
|
|
|
|
# export arch
|
|
arch = result['arch']
|
|
if not isinstance(arch, UpdateableStr):
|
|
d = xml.dom.minidom.parseString(arch)
|
|
for t in trans_parse_view(d.documentElement):
|
|
to_translate.append(["wizard_view", name, 0, t])
|
|
|
|
# export button labels
|
|
for but_args in result['state']:
|
|
button_name = but_args[0]
|
|
button_label = but_args[1]
|
|
res_name = name + ',' + button_name
|
|
to_translate.append(["wizard_button", res_name, 0, button_label])
|
|
|
|
elif model=='ir.model.fields':
|
|
field_name = obj.name
|
|
field_def = pool.get(obj.model)._columns[field_name]
|
|
|
|
name = obj.model + "," + field_name
|
|
to_translate.append(["field", name, 0, field_def.string.encode('utf8')])
|
|
|
|
if field_def.help:
|
|
to_translate.append(["help", name, 0, field_def.help.encode('utf8')])
|
|
|
|
if field_def.translate:
|
|
ids = pool.get(obj.model).search(cr, uid, [])
|
|
obj_values = pool.get(obj.model).read(cr, uid, ids, [field_name])
|
|
for obj_value in obj_values:
|
|
res_id = obj_value['id']
|
|
if obj.name in ('ir.model', 'ir.ui.menu'):
|
|
res_id = 0
|
|
model_data_ids = model_data_obj.search(cr, uid, [
|
|
('model', '=', model),
|
|
('res_id', '=', res_id),
|
|
])
|
|
if not model_data_ids:
|
|
to_translate.append(['model', name, 0, obj_value[field_name]])
|
|
|
|
if hasattr(field_def, 'selection') and isinstance(field_def.selection, (list, tuple)):
|
|
for key, val in field_def.selection:
|
|
to_translate.append(["selection", name, 0, val.encode('utf8')])
|
|
|
|
elif model=='ir.actions.report.xml':
|
|
name = obj.report_name
|
|
fname = ""
|
|
if obj.report_rml:
|
|
fname = obj.report_rml
|
|
parse_func = trans_parse_rml
|
|
report_type = "rml"
|
|
elif obj.report_xsl:
|
|
fname = obj.report_xsl
|
|
parse_func = trans_parse_xsl
|
|
report_type = "xsl"
|
|
try:
|
|
xmlstr = tools.file_open(fname).read()
|
|
d = xml.dom.minidom.parseString(xmlstr)
|
|
for t in parse_func(d.documentElement):
|
|
to_translate.append([report_type, name, 0, t])
|
|
except IOError:
|
|
if fname:
|
|
logger.notifyChannel("init", netsvc.LOG_WARNING, "couldn't export translation for report %s %s %s" % (name, report_type, fname))
|
|
|
|
|
|
for field_name,field_def in pool.get(model)._columns.items():
|
|
if field_def.translate:
|
|
name = model + "," + field_name
|
|
to_translate.append(['model', name, xml_name, getattr(obj,field_name)])
|
|
|
|
# TODO: By module
|
|
# code
|
|
for root, dirs, files in os.walk(tools.config['root_path']):
|
|
for fname in fnmatch.filter(files, '*.py'):
|
|
frelativepath = join(root, fname)
|
|
code_string = tools.file_open(frelativepath, subdir='').read()
|
|
iter = re.finditer(
|
|
'[^a-zA-Z0-9_]_\([\s]*["\'](.*?)["\'][\s]*\)',
|
|
code_string)
|
|
sources = []
|
|
for i in iter:
|
|
to_translate.append(['code', frelativepath, 0, i.group(1).encode('utf8')])
|
|
|
|
# translate strings marked as to be translated
|
|
for a in to_translate:
|
|
(type, name, id, source) = a
|
|
trans = trans_obj._get_source(cr, uid, name, type, lang, source)
|
|
out.append([type, name, id, source, trans or ''])
|
|
|
|
cr.close()
|
|
return out
|
|
|
|
def trans_load(db_name, filename, lang, strict=False):
|
|
logger = netsvc.Logger()
|
|
data=''
|
|
try:
|
|
data=file(filename,'r').read().split('\n')
|
|
except IOError:
|
|
logger.notifyChannel("init", netsvc.LOG_ERROR, "couldn't read file")
|
|
return trans_load_data(db_name, data, lang, strict=False)
|
|
|
|
def trans_load_data(db_name, data, lang, strict=False, lang_name=None):
|
|
logger = netsvc.Logger()
|
|
logger.notifyChannel("init", netsvc.LOG_INFO,
|
|
'loading translation file for language %s' % (lang))
|
|
pool = pooler.get_pool(db_name)
|
|
lang_obj = pool.get('res.lang')
|
|
trans_obj = pool.get('ir.translation')
|
|
model_data_obj = pool.get('ir.model.data')
|
|
try:
|
|
uid = 1
|
|
cr = pooler.get_db(db_name).cursor()
|
|
|
|
ids = lang_obj.search(cr, uid, [('code','=',lang)])
|
|
if not ids:
|
|
if not lang_name:
|
|
lang_name=lang
|
|
languages=tools.get_languages()
|
|
if lang in languages:
|
|
lang_name=languages[lang]
|
|
ids = lang_obj.create(cr, uid, {
|
|
'code': lang,
|
|
'name': lang_name,
|
|
'translatable': 1,
|
|
})
|
|
else:
|
|
lang_obj.write(cr, uid, ids, {'translatable':1})
|
|
lang_ids = lang_obj.search(cr, uid, [])
|
|
langs = lang_obj.read(cr, uid, lang_ids)
|
|
ls = map(lambda x: (x['code'],x['name']), langs)
|
|
|
|
reader = csv.reader(data)
|
|
# read the first line of the file (it contains columns titles)
|
|
for row in reader:
|
|
f = row
|
|
break
|
|
|
|
# read the rest of the file
|
|
line = 1
|
|
for row in reader:
|
|
line += 1
|
|
#try:
|
|
# skip empty rows and rows where the translation field is empty
|
|
if (not row) or (not row[4]):
|
|
continue
|
|
|
|
# dictionary which holds values for this line of the csv file
|
|
# {'lang': ..., 'type': ..., 'name': ..., 'res_id': ...,
|
|
# 'src': ..., 'value': ...}
|
|
dic = {'lang': lang}
|
|
for i in range(len(f)):
|
|
# if trans_obj._columns[f[i]]._type == 'integer':
|
|
# row[i] = row[i] and int(row[i]) or False
|
|
dic[f[i]] = row[i]
|
|
|
|
try:
|
|
dic['res_id'] = int(dic['res_id'])
|
|
except:
|
|
model_data_ids = model_data_obj.search(cr, uid, [
|
|
('model', '=', dic['name'].split(',')[0]),
|
|
('module', '=', dic['res_id'].split('.', 1)[0]),
|
|
('name', '=', dic['res_id'].split('.', 1)[1]),
|
|
])
|
|
if model_data_ids:
|
|
dic['res_id'] = model_data_obj.browse(cr, uid,
|
|
model_data_ids[0]).res_id
|
|
else:
|
|
dic['res_id'] = False
|
|
|
|
if dic['type'] == 'model' and not strict:
|
|
(model, field) = dic['name'].split(',')
|
|
|
|
# get the ids of the resources of this model which share
|
|
# the same source
|
|
obj = pool.get(model)
|
|
if obj:
|
|
ids = obj.search(cr, uid, [(field, '=', dic['src'])])
|
|
|
|
# if the resource id (res_id) is in that list, use it,
|
|
# otherwise use the whole list
|
|
ids = (dic['res_id'] in ids) and [dic['res_id']] or ids
|
|
for id in ids:
|
|
dic['res_id'] = id
|
|
ids = trans_obj.search(cr, uid, [
|
|
('lang', '=', lang),
|
|
('type', '=', dic['type']),
|
|
('name', '=', dic['name']),
|
|
('src', '=', dic['src']),
|
|
('res_id', '=', dic['res_id'])
|
|
])
|
|
if ids:
|
|
trans_obj.write(cr, uid, ids, {'value': dic['value']})
|
|
else:
|
|
trans_obj.create(cr, uid, dic)
|
|
else:
|
|
ids = trans_obj.search(cr, uid, [
|
|
('lang', '=', lang),
|
|
('type', '=', dic['type']),
|
|
('name', '=', dic['name']),
|
|
('src', '=', dic['src'])
|
|
])
|
|
if ids:
|
|
trans_obj.write(cr, uid, ids, {'value': dic['value']})
|
|
else:
|
|
trans_obj.create(cr, uid, dic)
|
|
cr.commit()
|
|
#except Exception, e:
|
|
# logger.notifyChannel('init', netsvc.LOG_ERROR,
|
|
# 'Import error: %s on line %d: %s!' % (str(e), line, row))
|
|
# cr.rollback()
|
|
# cr.close()
|
|
# cr = pooler.get_db(db_name).cursor()
|
|
cr.close()
|
|
logger.notifyChannel("init", netsvc.LOG_INFO,
|
|
"translation file loaded succesfully")
|
|
except IOError:
|
|
logger.notifyChannel("init", netsvc.LOG_ERROR, "couldn't read file")
|
|
|