Inserted command line option to translate config strings during database setup. Fixed #460.
This commit is contained in:
parent
d49d087f4e
commit
ce8274fb53
@ -20,6 +20,7 @@ Other:
|
|||||||
- Changed api for plugins. Used entry points to detect them automaticly.
|
- Changed api for plugins. Used entry points to detect them automaticly.
|
||||||
- Renamed config api classes.
|
- Renamed config api classes.
|
||||||
- Renamed some classes of the poll api.
|
- Renamed some classes of the poll api.
|
||||||
|
- Inserted command line option to translate config strings during database setup.
|
||||||
- Inserted api for the personal info widget.
|
- Inserted api for the personal info widget.
|
||||||
- Changed api for main menu entries.
|
- Changed api for main menu entries.
|
||||||
- Enhanced http error pages.
|
- Enhanced http error pages.
|
||||||
|
@ -21,6 +21,7 @@ from openslides.utils.main import (
|
|||||||
get_port,
|
get_port,
|
||||||
setup_django_settings_module,
|
setup_django_settings_module,
|
||||||
start_browser,
|
start_browser,
|
||||||
|
translate_customizable_strings,
|
||||||
write_settings)
|
write_settings)
|
||||||
|
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ def parse_args():
|
|||||||
'start',
|
'start',
|
||||||
help='Setup settings and database, start tornado webserver, launch the '
|
help='Setup settings and database, start tornado webserver, launch the '
|
||||||
'default web browser and open the webinterface.')
|
'default web browser and open the webinterface.')
|
||||||
add_general_arguments(subcommand_start, ('settings', 'user_data_path', 'address', 'port'))
|
add_general_arguments(subcommand_start, ('settings', 'user_data_path', 'language', 'address', 'port'))
|
||||||
subcommand_start.add_argument(
|
subcommand_start.add_argument(
|
||||||
'--no-browser',
|
'--no-browser',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@ -113,7 +114,7 @@ def parse_args():
|
|||||||
subcommand_syncdb = subparsers.add_parser(
|
subcommand_syncdb = subparsers.add_parser(
|
||||||
'syncdb',
|
'syncdb',
|
||||||
help='Create or update database tables.')
|
help='Create or update database tables.')
|
||||||
add_general_arguments(subcommand_syncdb, ('settings', 'user_data_path'))
|
add_general_arguments(subcommand_syncdb, ('settings', 'user_data_path', 'language'))
|
||||||
subcommand_syncdb.set_defaults(callback=syncdb)
|
subcommand_syncdb.set_defaults(callback=syncdb)
|
||||||
|
|
||||||
# Subcommand createsuperuser
|
# Subcommand createsuperuser
|
||||||
@ -191,6 +192,11 @@ def add_general_arguments(subcommand, arguments):
|
|||||||
'when a new settings file is created. The given path is only '
|
'when a new settings file is created. The given path is only '
|
||||||
'written into the new settings file. Default according to the '
|
'written into the new settings file. Default according to the '
|
||||||
'OpenSlides is at the moment %s' % get_default_user_data_path(openslides_type)))
|
'OpenSlides is at the moment %s' % get_default_user_data_path(openslides_type)))
|
||||||
|
general_arguments['language'] = (
|
||||||
|
('-l', '--language'),
|
||||||
|
dict(help='Language code. All customizable strings will be translated '
|
||||||
|
'during database setup. See https://www.transifex.com/projects/p/openslides/ '
|
||||||
|
'for supported languages.'))
|
||||||
general_arguments['address'] = (
|
general_arguments['address'] = (
|
||||||
('-a', '--address',),
|
('-a', '--address',),
|
||||||
dict(default='0.0.0.0', help='IP address to listen on. Default is %(default)s.'))
|
dict(default='0.0.0.0', help='IP address to listen on. Default is %(default)s.'))
|
||||||
@ -250,6 +256,8 @@ def syncdb(settings, args):
|
|||||||
print('Clearing old search index...')
|
print('Clearing old search index...')
|
||||||
execute_from_command_line(["", "clear_index", "--noinput"])
|
execute_from_command_line(["", "clear_index", "--noinput"])
|
||||||
execute_from_command_line(["", "syncdb", "--noinput"])
|
execute_from_command_line(["", "syncdb", "--noinput"])
|
||||||
|
if args.language:
|
||||||
|
translate_customizable_strings(args.language)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ def setup_assignment_config(sender, **kwargs):
|
|||||||
assignment_pdf_title = ConfigVariable(
|
assignment_pdf_title = ConfigVariable(
|
||||||
name='assignment_pdf_title',
|
name='assignment_pdf_title',
|
||||||
default_value=_('Elections'),
|
default_value=_('Elections'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -53,8 +53,8 @@ class ConfigHandler(object):
|
|||||||
|
|
||||||
def setup_cache(self):
|
def setup_cache(self):
|
||||||
"""
|
"""
|
||||||
Loads all config variables from the database and by sending a
|
Loads all config variables from the database by sending a signal to
|
||||||
signal to get the default into the cache.
|
save the default to the cache.
|
||||||
"""
|
"""
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
for receiver, config_collection in config_signal.send(sender='setup_cache'):
|
for receiver, config_collection in config_signal.send(sender='setup_cache'):
|
||||||
@ -73,6 +73,15 @@ class ConfigHandler(object):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_all_translatable(self):
|
||||||
|
"""
|
||||||
|
Generator to get all config variables as strings when their values are
|
||||||
|
intended to be translated.
|
||||||
|
"""
|
||||||
|
for receiver, config_collection in config_signal.send(sender='get_all_translatable'):
|
||||||
|
for config_variable in config_collection.variables:
|
||||||
|
if config_variable.translatable:
|
||||||
|
yield config_variable.name
|
||||||
|
|
||||||
config = ConfigHandler()
|
config = ConfigHandler()
|
||||||
"""
|
"""
|
||||||
@ -162,10 +171,13 @@ class ConfigVariable(object):
|
|||||||
arguments 'name' and 'default_value' are required. The keyword
|
arguments 'name' and 'default_value' are required. The keyword
|
||||||
argument 'form_field' has to be set if the variable should appear
|
argument 'form_field' has to be set if the variable should appear
|
||||||
on the ConfigView. The argument 'on_change' can get a callback
|
on the ConfigView. The argument 'on_change' can get a callback
|
||||||
which is called every time, the variable is changed.
|
which is called every time, the variable is changed. If the argument
|
||||||
|
'translatable' is set, OpenSlides is able to translate the value during
|
||||||
|
setup of the database if the admin uses the respective command line option.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, default_value, form_field=None, on_change=None):
|
def __init__(self, name, default_value, form_field=None, on_change=None, translatable=False):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.default_value = default_value
|
self.default_value = default_value
|
||||||
self.form_field = form_field
|
self.form_field = form_field
|
||||||
self.on_change = on_change
|
self.on_change = on_change
|
||||||
|
self.translatable = translatable
|
||||||
|
@ -29,6 +29,7 @@ def setup_general_config(sender, **kwargs):
|
|||||||
event_description = ConfigVariable(
|
event_description = ConfigVariable(
|
||||||
name='event_description',
|
name='event_description',
|
||||||
default_value=_('Presentation and assembly system'),
|
default_value=_('Presentation and assembly system'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
label=ugettext_lazy('Short description of event'),
|
label=ugettext_lazy('Short description of event'),
|
||||||
@ -104,6 +105,7 @@ def setup_general_config(sender, **kwargs):
|
|||||||
welcome_title = ConfigVariable(
|
welcome_title = ConfigVariable(
|
||||||
name='welcome_title',
|
name='welcome_title',
|
||||||
default_value=_('Welcome to OpenSlides'),
|
default_value=_('Welcome to OpenSlides'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
label=ugettext_lazy('Title'),
|
label=ugettext_lazy('Title'),
|
||||||
@ -113,6 +115,7 @@ def setup_general_config(sender, **kwargs):
|
|||||||
welcome_text = ConfigVariable(
|
welcome_text = ConfigVariable(
|
||||||
name='welcome_text',
|
name='welcome_text',
|
||||||
default_value=_('[Place for your welcome text.]'),
|
default_value=_('[Place for your welcome text.]'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.Textarea(),
|
widget=forms.Textarea(),
|
||||||
label=ugettext_lazy('Welcome text'),
|
label=ugettext_lazy('Welcome text'),
|
||||||
|
@ -40,6 +40,7 @@ def setup_motion_config(sender, **kwargs):
|
|||||||
motion_preamble = ConfigVariable(
|
motion_preamble = ConfigVariable(
|
||||||
name='motion_preamble',
|
name='motion_preamble',
|
||||||
default_value=_('The assembly may decide,'),
|
default_value=_('The assembly may decide,'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -66,6 +67,7 @@ def setup_motion_config(sender, **kwargs):
|
|||||||
motion_pdf_title = ConfigVariable(
|
motion_pdf_title = ConfigVariable(
|
||||||
name='motion_pdf_title',
|
name='motion_pdf_title',
|
||||||
default_value=_('Motions'),
|
default_value=_('Motions'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.TextInput(),
|
widget=forms.TextInput(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -23,6 +23,7 @@ def setup_participant_config(sender, **kwargs):
|
|||||||
participant_pdf_welcometitle = ConfigVariable(
|
participant_pdf_welcometitle = ConfigVariable(
|
||||||
name='participant_pdf_welcometitle',
|
name='participant_pdf_welcometitle',
|
||||||
default_value=_('Welcome to OpenSlides!'),
|
default_value=_('Welcome to OpenSlides!'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.Textarea(),
|
widget=forms.Textarea(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -31,6 +32,7 @@ def setup_participant_config(sender, **kwargs):
|
|||||||
participant_pdf_welcometext = ConfigVariable(
|
participant_pdf_welcometext = ConfigVariable(
|
||||||
name='participant_pdf_welcometext',
|
name='participant_pdf_welcometext',
|
||||||
default_value=_('[Place for your welcome and help text.]'),
|
default_value=_('[Place for your welcome and help text.]'),
|
||||||
|
translatable=True,
|
||||||
form_field=forms.CharField(
|
form_field=forms.CharField(
|
||||||
widget=forms.Textarea(),
|
widget=forms.Textarea(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -12,6 +12,8 @@ import webbrowser
|
|||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.conf import ENVIRONMENT_VARIABLE
|
from django.conf import ENVIRONMENT_VARIABLE
|
||||||
|
from django.utils.translation import activate, check_for_language, get_language
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
UNIX_VERSION = 'Unix Version'
|
UNIX_VERSION = 'Unix Version'
|
||||||
WINDOWS_VERSION = 'Windows Version'
|
WINDOWS_VERSION = 'Windows Version'
|
||||||
@ -301,3 +303,16 @@ def get_database_path_from_settings():
|
|||||||
if default.get('ENGINE') != 'django.db.backends.sqlite3':
|
if default.get('ENGINE') != 'django.db.backends.sqlite3':
|
||||||
database_path = None
|
database_path = None
|
||||||
return database_path
|
return database_path
|
||||||
|
|
||||||
|
|
||||||
|
def translate_customizable_strings(language_code):
|
||||||
|
"""
|
||||||
|
Translates all translatable config values and saves them into database.
|
||||||
|
"""
|
||||||
|
if check_for_language(language_code):
|
||||||
|
from openslides.config.api import config
|
||||||
|
current_language = get_language()
|
||||||
|
activate(language_code)
|
||||||
|
for name in config.get_all_translatable():
|
||||||
|
config[name] = _(config[name])
|
||||||
|
activate(current_language)
|
||||||
|
@ -12,6 +12,7 @@ from openslides.__main__ import (
|
|||||||
runserver,
|
runserver,
|
||||||
start,
|
start,
|
||||||
syncdb)
|
syncdb)
|
||||||
|
from openslides.config.api import config
|
||||||
from openslides.utils.main import (
|
from openslides.utils.main import (
|
||||||
get_browser_url,
|
get_browser_url,
|
||||||
get_database_path_from_settings,
|
get_database_path_from_settings,
|
||||||
@ -22,6 +23,7 @@ from openslides.utils.main import (
|
|||||||
PortIsBlockedError,
|
PortIsBlockedError,
|
||||||
setup_django_settings_module,
|
setup_django_settings_module,
|
||||||
start_browser,
|
start_browser,
|
||||||
|
translate_customizable_strings,
|
||||||
UNIX_VERSION,
|
UNIX_VERSION,
|
||||||
WINDOWS_PORTABLE_VERSION)
|
WINDOWS_PORTABLE_VERSION)
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
@ -91,6 +93,11 @@ class TestFunctions(TestCase):
|
|||||||
def test_get_database_path_from_settings_memory(self):
|
def test_get_database_path_from_settings_memory(self):
|
||||||
self.assertEqual(get_database_path_from_settings(), ':memory:')
|
self.assertEqual(get_database_path_from_settings(), ':memory:')
|
||||||
|
|
||||||
|
def test_translate_customizable_strings(self):
|
||||||
|
self.assertEqual(config['event_description'], 'Presentation and assembly system')
|
||||||
|
translate_customizable_strings('de')
|
||||||
|
self.assertEqual(config['event_description'], u'Präsentations- und Versammlungssystem')
|
||||||
|
|
||||||
|
|
||||||
class TestOtherFunctions(TestCase):
|
class TestOtherFunctions(TestCase):
|
||||||
"""
|
"""
|
||||||
@ -127,6 +134,7 @@ class TestOtherFunctions(TestCase):
|
|||||||
def test_syncdb(self, mock_execute_from_command_line, mock_os, mock_exists):
|
def test_syncdb(self, mock_execute_from_command_line, mock_os, mock_exists):
|
||||||
mock_exists.return_value = True
|
mock_exists.return_value = True
|
||||||
mock_args = MagicMock()
|
mock_args = MagicMock()
|
||||||
|
mock_args.language = None
|
||||||
syncdb(settings=None, args=mock_args)
|
syncdb(settings=None, args=mock_args)
|
||||||
self.assertTrue(mock_execute_from_command_line.called)
|
self.assertTrue(mock_execute_from_command_line.called)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user