Add language configuration wizard

issue9172
review267031002
This commit is contained in:
Sergi Almacellas Abellana committed 2020-04-13 20:47:17 +02:00
1 parent 0081dc431d
commit 3378e283fd
9 files changed
+149 -22

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Add language configuration wizard
* Allow copying attachments and notes to created records
* Add link button on form
* Support explicit delete and remove for saving and on_change xxx2Many
+2
View File
@@ -71,6 +71,7 @@ def register():
note.NoteRead,
cron.Cron,
lang.Lang,
lang.LangConfigStart,
export.Export,
export.ExportLine,
rule.RuleGroup,
@@ -105,6 +106,7 @@ def register():
module.ModuleConfigWizard,
module.ModuleActivateUpgrade,
module.ModuleConfig,
lang.LangConfig,
module='ir', type_='wizard')
Pool.register(
model.ModelGraph,
+77 -5
View File
@@ -8,18 +8,17 @@ from ast import literal_eval
from sql import Table
from ..model import ModelView, ModelSQL, DeactivableMixin, fields, Check
from ..modules import create_graph, load_translations
from ..cache import Cache
from ..transaction import Transaction
from ..pool import Pool
from ..pyson import Eval
from ..exceptions import UserError
from ..i18n import gettext
from ..wizard import Wizard, StateView, Button, StateTransition
Transaction.cache_keys.add('translate_name')
__all__ = [
'Lang',
]
class GroupingError(UserError):
pass
@@ -43,7 +42,7 @@ class Lang(DeactivableMixin, ModelSQL, ModelView):
name = fields.Char('Name', required=True, translate=True)
code = fields.Char('Code', required=True,
help="RFC 4646 tag: http://tools.ietf.org/html/rfc4646")
translatable = fields.Boolean('Translatable')
translatable = fields.Boolean('Translatable', readonly=True)
parent = fields.Char("Parent Code", help="Code of the exceptional parent")
direction = fields.Selection([
('ltr', 'Left-to-right'),
@@ -87,6 +86,12 @@ class Lang(DeactivableMixin, ModelSQL, ModelView):
Check(table, table.decimal_point != table.thousands_sep),
'decimal_point and thousands_sep must be different!'),
]
cls._buttons.update({
'load_translations': {},
'unload_translations': {
'invisible': ~Eval('translatable', False),
},
})
@classmethod
def search_rec_name(cls, name, clause):
@@ -186,6 +191,37 @@ class Lang(DeactivableMixin, ModelSQL, ModelView):
def default_n_sep_by_space(cls):
return False
@classmethod
@ModelView.button
def load_translations(cls, languages):
pool = Pool()
Module = pool.get('ir.module')
codes = set()
cls.write(languages, {'translatable': True})
for language in languages:
code = language.code
while code:
codes.add(code)
code = get_parent_language(code)
modules = Module.search([
('state', '=', 'activated'),
])
modules = [m.name for m in modules]
for node in create_graph(modules):
load_translations(pool, node, codes)
@classmethod
@ModelView.button
def unload_translations(cls, languages):
pool = Pool()
Translation = pool.get('ir.translation')
cls.write(languages, {'translatable': False})
languages = [l.code for l in languages]
Translation.delete(Translation.search([
('lang', 'in', languages),
('module', '!=', None),
]))
@classmethod
def validate(cls, languages):
super(Lang, cls).validate(languages)
@@ -503,6 +539,42 @@ class Lang(DeactivableMixin, ModelSQL, ModelView):
return value.strftime(format)
class LangConfigStart(ModelView):
'Language Configuration Start'
__name__ = 'ir.lang.config.start'
languages = fields.Many2Many('ir.lang', None, None, "Languages")
@classmethod
def default_languages(cls):
pool = Pool()
Lang = pool.get('ir.lang')
return [x.id for x in Lang.search([('translatable', '=', True)])]
class LangConfig(Wizard):
'Configure languages'
__name__ = 'ir.lang.config'
start = StateView('ir.lang.config.start',
'ir.lang_config_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Load', 'load', 'tryton-ok', default=True),
])
load = StateTransition()
def transition_load(self):
pool = Pool()
Lang = pool.get('ir.lang')
Lang.load_translations(list(self.start.languages))
untranslated_languages = Lang.search([
('id', 'not in', [l.id for l in self.start.languages]),
('translatable', '=', True),
])
Lang.unload_translations(untranslated_languages)
return 'end'
def get_parent_language(code):
if code not in _parents:
# Use SQL because it is used by load_module_graph
+29
View File
@@ -495,5 +495,34 @@ this repository contains the full copyright notices and license terms. -->
</record>
<menuitem name="Languages" parent="ir.menu_localization"
action="act_lang_form" id="menu_lang_form"/>
<record model="ir.model.button" id="lang_load_translations_button">
<field name="name">load_translations</field>
<field name="string">Load translations</field>
<field name="model" search="[('model', '=', 'ir.lang')]"/>
<field name="confirm">Are you sure you want to load languages' translations?</field>
</record>
<record model="ir.model.button" id="lang_unload_translations_button">
<field name="name">unload_translations</field>
<field name="string">Unload translations</field>
<field name="confirm">Are you sure you want to remove languages' translations?</field>
<field name="model" search="[('model', '=', 'ir.lang')]"/>
</record>
<record model="ir.ui.view" id="lang_config_start_view_form">
<field name="model">ir.lang.config.start</field>
<field name="type">form</field>
<field name="name">lang_config_start_form</field>
</record>
<record model="ir.action.wizard" id="act_lang_config">
<field name="name">Configure Languages</field>
<field name="wiz_name">ir.lang.config</field>
<field name="window" eval="True"/>
</record>
<record model="ir.module.config_wizard.item" id="config_wizard_item_lang">
<field name="action" ref="act_lang_config"/>
</record>
</data>
</tryton>
@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form col="2">
<image name="tryton-info" xexpand="0" xfill="0"/>
<label string="You can now load aditional translations to the system."
id="load" yalign="0.0" xalign="0.0" xexpand="1"/>
<field name="languages" colspan="2" widget="multiselection"/>
</form>
+4
View File
@@ -55,4 +55,8 @@ this repository contains the full copyright notices and license terms. -->
<field name="p_sep_by_space"/>
<label name="n_sep_by_space"/>
<field name="n_sep_by_space"/>
<group id="buttons" colspan="6" col="-1">
<button name="load_translations" icon="tryton-launch"/>
<button name="unload_translations" icon="tryton-launch"/>
</group>
</form>
+2 -1
View File
@@ -1,9 +1,10 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree editable="1">
<tree>
<field name="name" expand="1"/>
<field name="code"/>
<field name="direction"/>
<field name="translatable"/>
<button name="load_translations"/>
</tree>
+21 -16
View File
@@ -166,6 +166,26 @@ def is_module_to_install(module, update):
return False
def load_translations(pool, node, languages):
module = node.name
localedir = '%s/%s' % (node.info['directory'], 'locale')
lang2filenames = defaultdict(list)
for filename in itertools.chain(
iglob('%s/*.po' % localedir),
iglob('%s/override/*.po' % localedir)):
filename = filename.replace('/', os.sep)
lang = os.path.splitext(os.path.basename(filename))[0]
if lang not in languages:
continue
lang2filenames[lang].append(filename)
base_path_position = len(node.info['directory']) + 1
for language, files in lang2filenames.items():
filenames = [f[base_path_position:] for f in files]
logger.info('%s:loading %s', module, ','.join(filenames))
Translation = pool.get('ir.translation')
Translation.translation_import(language, module, files)
def load_module_graph(graph, pool, update=None, lang=None):
from trytond.ir.lang import get_parent_language
@@ -233,22 +253,7 @@ def load_module_graph(graph, pool, update=None, lang=None):
modules_todo.append((module, list(tryton_parser.to_delete)))
localedir = '%s/%s' % (node.info['directory'], 'locale')
lang2filenames = defaultdict(list)
for filename in itertools.chain(
iglob('%s/*.po' % localedir),
iglob('%s/override/*.po' % localedir)):
filename = filename.replace('/', os.sep)
lang2 = os.path.splitext(os.path.basename(filename))[0]
if lang2 not in lang:
continue
lang2filenames[lang2].append(filename)
base_path_position = len(node.info['directory']) + 1
for language, files in lang2filenames.items():
filenames = [f[base_path_position:] for f in files]
logger.info('%s:loading %s', module, ','.join(filenames))
Translation = pool.get('ir.translation')
Translation.translation_import(language, module, files)
load_translations(pool, node, lang)
if package_state == 'to remove':
continue
+4
View File
@@ -560,6 +560,10 @@ this repository contains the full copyright notices and license terms. -->
<field name="action" ref="ir.act_translation_export"/>
<field name="group" ref="group_admin"/>
</record>
<record model="ir.action-res.group" id="act_lang_config">
<field name="action" ref="ir.act_lang_config"/>
<field name="group" ref="group_admin"/>
</record>
<record model="ir.ui.menu-res.group" id="menu_administration_group_admin">
<field name="menu" ref="ir.menu_administration"/>