Custom Translations
This commit is contained in:
parent
97a1431c32
commit
b30919eada
@ -73,6 +73,7 @@ Core:
|
|||||||
- Added config for disabling header and footer in the projector [#3357].
|
- Added config for disabling header and footer in the projector [#3357].
|
||||||
- Updated CKEditor to 4.7 [#3375].
|
- Updated CKEditor to 4.7 [#3375].
|
||||||
- Reduced ckeditor toolbar for inline editing [#3368].
|
- Reduced ckeditor toolbar for inline editing [#3368].
|
||||||
|
- Added custom translations in config [#3383].
|
||||||
|
|
||||||
Mediafiles:
|
Mediafiles:
|
||||||
- Fixed reloading of PDF on page change [#3274].
|
- Fixed reloading of PDF on page change [#3274].
|
||||||
|
@ -29,6 +29,7 @@ INPUT_TYPE_MAPPING = {
|
|||||||
'datetimepicker': int,
|
'datetimepicker': int,
|
||||||
'majorityMethod': str,
|
'majorityMethod': str,
|
||||||
'logo': dict,
|
'logo': dict,
|
||||||
|
'translations': list,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -134,6 +135,22 @@ class ConfigHandler:
|
|||||||
if not isinstance(value[required_entry], str):
|
if not isinstance(value[required_entry], str):
|
||||||
raise ConfigError(_('{} has to be a string.'.format(required_entry)))
|
raise ConfigError(_('{} has to be a string.'.format(required_entry)))
|
||||||
|
|
||||||
|
if config_variable.input_type == 'translations':
|
||||||
|
if not isinstance(value, list):
|
||||||
|
raise ConfigError(_('Translations has to be a list.'))
|
||||||
|
for entry in value:
|
||||||
|
if not isinstance(entry, dict):
|
||||||
|
raise ConfigError(_('Every value has to be a dict, not {}.'.format(type(entry))))
|
||||||
|
whitelist = (
|
||||||
|
'original',
|
||||||
|
'translation',
|
||||||
|
)
|
||||||
|
for required_entry in whitelist:
|
||||||
|
if required_entry not in entry:
|
||||||
|
raise ConfigError(_('{} has to be given.'.format(required_entry)))
|
||||||
|
if not isinstance(entry[required_entry], str):
|
||||||
|
raise ConfigError(_('{} has to be a string.'.format(required_entry)))
|
||||||
|
|
||||||
# Save the new value to the database.
|
# Save the new value to the database.
|
||||||
db_value = ConfigStore.objects.get(key=key)
|
db_value = ConfigStore.objects.get(key=key)
|
||||||
db_value.value = value
|
db_value.value = value
|
||||||
|
@ -322,3 +322,12 @@ def get_config_variables():
|
|||||||
weight=312,
|
weight=312,
|
||||||
group='Logo',
|
group='Logo',
|
||||||
hidden=True)
|
hidden=True)
|
||||||
|
|
||||||
|
# Custom translations
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='translations',
|
||||||
|
label='Custom translations',
|
||||||
|
default_value=[],
|
||||||
|
input_type='translations',
|
||||||
|
weight=1000,
|
||||||
|
group='Custom translations')
|
||||||
|
@ -1095,11 +1095,24 @@ img {
|
|||||||
|
|
||||||
/** Config **/
|
/** Config **/
|
||||||
.input-comments > div {
|
.input-comments > div {
|
||||||
margin-bottom: 5px
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.config-checkbox {
|
.config-checkbox {
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
}
|
}
|
||||||
|
.config-translations > div {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
padding-right: 15px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.config-translations .inputs input {
|
||||||
|
width: 47%;
|
||||||
|
}
|
||||||
|
.config-translations .inputs .arrow {
|
||||||
|
width: 6%;
|
||||||
|
float: left;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
/** Pojector sidebar **/
|
/** Pojector sidebar **/
|
||||||
.col2 .projectorSelector {
|
.col2 .projectorSelector {
|
||||||
|
@ -254,6 +254,52 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// Hook into gettextCatalog to include custom translations by wrapping
|
||||||
|
// the getString method. The translations are stored in the config.
|
||||||
|
.decorator('gettextCatalog', [
|
||||||
|
'$delegate',
|
||||||
|
'$rootScope',
|
||||||
|
function ($delegate, $rootScope) {
|
||||||
|
var oldGetString = $delegate.getString;
|
||||||
|
var customTranslations = {};
|
||||||
|
|
||||||
|
$delegate.getString = function () {
|
||||||
|
var translated = oldGetString.apply($delegate, arguments);
|
||||||
|
if (customTranslations[translated]) {
|
||||||
|
translated = customTranslations[translated];
|
||||||
|
}
|
||||||
|
return translated;
|
||||||
|
};
|
||||||
|
$delegate.setCustomTranslations = function (translations) {
|
||||||
|
customTranslations = translations;
|
||||||
|
$rootScope.$broadcast('gettextLanguageChanged');
|
||||||
|
};
|
||||||
|
|
||||||
|
return $delegate;
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.run([
|
||||||
|
'$rootScope',
|
||||||
|
'Config',
|
||||||
|
'gettextCatalog',
|
||||||
|
function ($rootScope, Config, gettextCatalog) {
|
||||||
|
$rootScope.$watch(function () {
|
||||||
|
return Config.lastModified('translations');
|
||||||
|
}, function () {
|
||||||
|
var translations = Config.get('translations');
|
||||||
|
if (translations) {
|
||||||
|
var customTranslations = {};
|
||||||
|
_.forEach(translations.value, function (entry) {
|
||||||
|
customTranslations[entry.original] = entry.translation;
|
||||||
|
});
|
||||||
|
// Update all translate directives
|
||||||
|
gettextCatalog.setCustomTranslations(customTranslations);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
// set browser language as default language for OpenSlides
|
// set browser language as default language for OpenSlides
|
||||||
.run([
|
.run([
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
|
@ -825,6 +825,7 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
colorpicker: 'colorpicker',
|
colorpicker: 'colorpicker',
|
||||||
datetimepicker: 'datetimepicker',
|
datetimepicker: 'datetimepicker',
|
||||||
majorityMethod: 'choice',
|
majorityMethod: 'choice',
|
||||||
|
translations: 'translations',
|
||||||
}[type];
|
}[type];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1134,6 +1135,19 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
$scope.save(configOption, parent.value);
|
$scope.save(configOption, parent.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For custom translations input
|
||||||
|
$scope.addTranslation = function (configOption, parent) {
|
||||||
|
parent.value.push({
|
||||||
|
original: gettextCatalog.getString('New'),
|
||||||
|
translation: gettextCatalog.getString('New'),
|
||||||
|
});
|
||||||
|
$scope.save(configOption, parent.value);
|
||||||
|
};
|
||||||
|
$scope.removeTranslation = function (configOption, parent, index) {
|
||||||
|
parent.value.splice(index, 1);
|
||||||
|
$scope.save(configOption, parent.value);
|
||||||
|
};
|
||||||
|
|
||||||
// For majority method
|
// For majority method
|
||||||
angular.forEach(
|
angular.forEach(
|
||||||
_.filter($scope.configGroups, function (configGroup) {
|
_.filter($scope.configGroups, function (configGroup) {
|
||||||
@ -1912,6 +1926,7 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
gettext('PDF footer logo');
|
gettext('PDF footer logo');
|
||||||
gettext('Web interface header logo');
|
gettext('Web interface header logo');
|
||||||
gettext('PDF ballot paper logo');
|
gettext('PDF ballot paper logo');
|
||||||
|
gettext('Custom translations');
|
||||||
|
|
||||||
// Mark the string 'Default projector' here, because it does not appear in the templates.
|
// Mark the string 'Default projector' here, because it does not appear in the templates.
|
||||||
gettext('Default projector');
|
gettext('Default projector');
|
||||||
|
@ -95,6 +95,41 @@
|
|||||||
ng-options="option.value as option.display_name | translate for option in choices">
|
ng-options="option.value as option.display_name | translate for option in choices">
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- custom trnaslations -->
|
||||||
|
<div class="config-translations" ng-if="type === 'translations'">
|
||||||
|
<div ng-repeat="entry in $parent.value" class="input-group">
|
||||||
|
<div class="inputs">
|
||||||
|
<input ng-model="entry.original"
|
||||||
|
ng-model-options="{debounce: 1000}"
|
||||||
|
ng-change="save(configOption, $parent.value)"
|
||||||
|
class="form-control"
|
||||||
|
id="{{ key }}_original"
|
||||||
|
type="text">
|
||||||
|
<span class="arrow form-control"><i class="fa fa-arrow-right"></i></span>
|
||||||
|
<input ng-model="entry.translation"
|
||||||
|
ng-model-options="{debounce: 1000}"
|
||||||
|
ng-change="save(configOption, $parent.value)"
|
||||||
|
class="form-control"
|
||||||
|
id="{{ key }}_translated"
|
||||||
|
type="text">
|
||||||
|
</div>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default"
|
||||||
|
ng-click="removeTranslation(configOption, $parent, $index)">
|
||||||
|
<i class="fa fa-minus"></i>
|
||||||
|
<translate>Remove</translate>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="button" ng-click="addTranslation(configOption, $parent)"
|
||||||
|
class="btn btn-default btn-sm">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
<translate>Add new custom translation</translate>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span id="success-{{ key }}" class="input-group-addon" ng-if="configOption.success !== undefined">
|
<span id="success-{{ key }}" class="input-group-addon" ng-if="configOption.success !== undefined">
|
||||||
<i class="fa fa-lg fa-check-circle text-success"
|
<i class="fa fa-lg fa-check-circle text-success"
|
||||||
ng-if="configOption.success === true"></i>
|
ng-if="configOption.success === true"></i>
|
||||||
|
Loading…
Reference in New Issue
Block a user