Fixes #2709, fixes #2666, fixes #2665, fixes #556, fixes #3275 - Refactoring: Implement new translation toolchain based on gettext.

- Translations are no longer fetched from the cloud.
- Instead, they are extracted from the codebase and stored in i18n/zammad.pot.
- Translations will be managed via a public Weblate instance soon.
- The translated .po files are fed to the database as before.
- It is now possible to change "translation" strings for en-us locally via the admin GUI.
- It is no longer possible to submit local changes.
This commit is contained in:
Martin Gruner 2021-11-15 16:58:19 +01:00
parent 6a6b19b4e6
commit 64a87b1c67
481 changed files with 534434 additions and 3442 deletions

View file

@ -0,0 +1,91 @@
module.exports = class DetectTranslatableString
# coffeelint: disable=detect_translatable_string
rule:
name: 'detect_translatable_string'
level: 'ignore'
message: 'The following string looks like it should be marked as translatable via __(...)'
description: '''
'''
constructor: ->
@callTokens = []
tokens: ['STRING', 'CALL_START', 'CALL_END']
lintToken: (token, tokenApi) ->
[type, tokenValue] = token
if type in ['CALL_START', 'CALL_END']
@trackCall token, tokenApi
return
return false if @isInIgnoredMethod()
return @lintString(token, tokenApi)
lintString: (token, tokenApi) ->
[type, tokenValue] = token
# Remove quotes.
string = tokenValue[1..-2]
# Ignore strings with less than two words.
return false if string.split(' ').length < 2
# Ignore strings that are being used as exception; unlike Ruby exceptions, these should not reach the user.
return false if tokenApi.peek(-3)[1] == 'throw'
return false if tokenApi.peek(-2)[1] == 'throw'
return false if tokenApi.peek(-1)[1] == 'throw'
# Ignore strings that are being used for comparison
return false if tokenApi.peek(-1)[1] == '=='
# String interpolation is handled via concatenation, ignore such strings.
return false if tokenApi.peek(1)[1] == '+'
return false if tokenApi.peek(2)[1] == '+'
BLOCKLIST = [
# Only look at strings starting with upper case letters
/^[^A-Z]/,
# # Ignore strings starting with three upper case letters like SELECT, POST etc.
# /^[A-Z]{3}/,
]
return false if BLOCKLIST.some (entry) ->
#console.log([string, entry, string.match(entry), token, tokenApi.peek(-1), tokenApi.peek(1)])
string.match(entry)
# console.log(tokenApi.peek(-3))
# console.log(tokenApi.peek(-2))
# console.log(tokenApi.peek(-1))
# console.log(token)
return { context: "Found: #{token[1]}" }
ignoredMethods: {
'__': true,
'log': true,
'T': true,
'controllerBind': true,
'error': true, # App.Log.error
'set': true, # App.Config.set
'translateInline': true,
'translateContent': true,
'translatePlain': true,
}
isInIgnoredMethod: ->
#console.log(@callTokens)
for t in @callTokens
return true if t.isIgnoredMethod
return false
trackCall: (token, tokenApi) ->
if token[0] is 'CALL_START'
p = tokenApi.peek(-1)
token.isIgnoredMethod = p and @ignoredMethods[p[1]]
@callTokens.push(token)
else
@callTokens.pop()
return null

4
.gitignore vendored
View file

@ -37,8 +37,8 @@
/db/*.sqlite3
/db/schema.rb
# translation cache files
/config/locales*.yml
# legacy translation cache files
/config/locales-*.yml
/config/translations/*.yml
# NPM / Yarn

View file

@ -22,7 +22,26 @@ shellcheck:
script:
- shellcheck -S warning $(find . -name "*.sh" -o -name "functions" | grep -v "/vendor/")
zeitwerk_check:
gettext lint:
<<: *template_pre
before_script:
- echo "Disable default before_script."
script:
- for FILE in i18n/*.pot i18n/*.po; do echo "Checking $FILE"; msgfmt -o /dev/null -c $FILE; done
gettext catalog consistency:
<<: *template_pre
extends:
- .tags_docker
- .services_postgresql
script:
- bundle install -j $(nproc) --path vendor
- bundle exec ruby .gitlab/configure_environment.rb
- source .gitlab/environment.env
- bundle exec rake zammad:db:init
- bundle exec rails generate translation_catalog --check
zeitwerk:check:
<<: *template_pre
extends:
- .tags_docker
@ -48,7 +67,7 @@ brakeman:
coffeelint:
<<: *template_pre
script:
- coffeelint app/
- coffeelint --rules ./.coffeelint/rules/* app/
stylelint:
<<: *template_pre

View file

@ -9,10 +9,18 @@ PreCommit:
enabled: false
RuboCop:
enabled: true
on_warn: fail # Treat all warnings as failures
on_warn: fail
CoffeeLint:
# .coffeelint/rules/* not supported in YAML, specify all rules separately.
flags: ['--reporter=csv', '--rules', './.coffeelint/rules/detect_translatable_string.coffee']
enabled: true
on_warn: fail # Treat all warnings as failures
on_warn: fail
exclude: public/assets/chat/**/*
CustomScript:
enabled: true
description: 'Check if translation catalog is up-to-date'
required_executable: 'rails'
flags: ['generate', 'translation_catalog', '--check']
Stylelint:
enabled: true
@ -43,4 +51,3 @@ PreRebase:
PrepareCommitMsg:
ALL:
enabled: false

View file

@ -0,0 +1,90 @@
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
module RuboCop
module Cop
module Zammad
class DetectTranslatableString < Base
extend AutoCorrector
MSG = 'This string looks like it should be marked as translatable via __(...).'.freeze
def on_str(node)
# Constants like __FILE__ are handled as strings, but don't respond to begin.
return if !node.loc.respond_to?(:begin) || !node.loc.begin
return if part_of_ignored_node?(node)
return if !offense?(node)
add_offense(node) do |corrector|
corrector.replace(node, "__(#{node.source})")
end
end
def on_regexp(node)
ignore_node(node)
end
METHOD_NAME_BLOCKLIST = %i[
__ translate
include? eql? parse
debug info warn error fatal unknown log log_error
].freeze
def on_send(node)
ignore_node(node) if METHOD_NAME_BLOCKLIST.include? node.method_name
end
private
PARENT_SOURCE_BLOCKLIST = [
# Ignore logged strings
'Rails.logger'
].freeze
NODE_START_BLOCKLIST = [
# Only look at strings starting with upper case letters
%r{[^A-Z]},
# Ignore strings starting with three upper case letters like SELECT, POST etc.
%r{[A-Z]{3}},
].freeze
NODE_CONTAIN_BLOCKLIST = [
# Ignore strings with interpolation.
'#{',
# Ignore Email addresses
'@'
].freeze
def offense?(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# Ignore Hash Keys
return false if node.parent.type.eql?(:pair) && node.parent.children.first.equal?(node)
# Ignore equality checks like ... == 'My String'
return false if node.left_sibling.eql?(:==)
# Remove quotes
node_source = node.source[1..-2]
# Only match strings with at least two words
return false if node_source.split.count < 2
NODE_START_BLOCKLIST.each do |entry|
return false if node_source.start_with? entry
end
NODE_CONTAIN_BLOCKLIST.each do |entry|
return false if node_source.include? entry
end
parent_source = node.parent.source
PARENT_SOURCE_BLOCKLIST.each do |entry|
return false if parent_source.include? entry
end
true
end
end
end
end
end

View file

@ -317,6 +317,20 @@ Zammad/ExistsResetColumnInformation:
- 'db/migrate/201*_*.rb'
- 'db/migrate/2020*_*.rb'
Zammad/DetectTranslatableString:
Enabled: true
Include:
- "app/**/*.rb"
- "db/**/*.rb"
- "lib/**/*.rb"
Exclude:
- "db/migrate/**/*.rb"
- "db/addon/**/*.rb"
- "lib/generators/**/*.rb"
- "lib/sequencer/**/*.rb"
- "lib/import/**/*.rb"
- "lib/tasks/**/*.rb"
Zammad/ExistsDbStrategy:
Include:
- "spec/**/*.rb"

View file

@ -1,6 +1,7 @@
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
require_relative 'cop/zammad/exists_condition'
require_relative 'cop/zammad/detect_translatable_string'
require_relative 'cop/zammad/exists_date_time_precision'
require_relative 'cop/zammad/exists_db_strategy'
require_relative 'cop/zammad/exists_reset_column_information'

View file

@ -149,6 +149,9 @@ gem 'viewpoint', require: false
# integrations - S/MIME
gem 'openssl'
# Translation sync
gem 'PoParser', require: false
# Gems used only for develop/test and not required
# in production environments by default.
group :development, :test do

View file

@ -19,6 +19,8 @@ GIT
GEM
remote: https://rubygems.org/
specs:
PoParser (3.2.5)
simple_po_parser (~> 1.1.2)
aasm (5.2.0)
concurrent-ruby (~> 1.0)
actioncable (6.0.4.1)
@ -555,6 +557,7 @@ GEM
shoulda-matchers (5.0.0)
activesupport (>= 5.2.0)
simple_oauth (0.3.1)
simple_po_parser (1.1.5)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
@ -655,6 +658,7 @@ PLATFORMS
ruby
DEPENDENCIES
PoParser
aasm
activerecord-import
activerecord-nulldb-adapter

View file

@ -114,7 +114,7 @@ class App.Controller extends Spine.Controller
if window.clipboardData # IE
window.clipboardData.setData('Text', text)
else
window.prompt('Copy to clipboard: Ctrl+C, Enter', text)
window.prompt(__('Copy to clipboard: Ctrl+C, Enter'), text)
# disable all delay's and interval's
disconnectClient: ->

View file

@ -59,7 +59,7 @@ class App.ControllerModal extends App.Controller
false
content: ->
'You need to implement a one @content()!'
__('You need to implement a one @content()!')
update: =>
if @message
@ -106,7 +106,7 @@ class App.ControllerModal extends App.Controller
if @buttonSubmit is true
@buttonSubmit = 'Submit'
if @buttonCancel is true
@buttonCancel = 'Cancel & Go Back'
@buttonCancel = __('Cancel & Go Back')
@update()

View file

@ -1,5 +1,5 @@
class App.ControllerForm extends App.Controller
fullFormSubmitLabel: 'Submit'
fullFormSubmitLabel: __('Submit')
fullFormSubmitAdditionalClasses: ''
fullFormButtonsContainerClass: ''
fullFormAdditionalButtons: [] # [{className: 'js-class', text: 'Label'}]
@ -173,7 +173,7 @@ class App.ControllerForm extends App.Controller
# input text field with max. 100 size
attribute_config = {
name: 'subject'
display: 'Subject'
display: __('Subject')
tag: 'input'
type: 'text'
limit: 100
@ -185,7 +185,7 @@ class App.ControllerForm extends App.Controller
# colection as relation with auto completion
attribute_config = {
name: 'customer_id'
display: 'Customer'
display: __('Customer')
tag: 'autocompletion'
# auto completion params, endpoints, ui,...
type: 'text'
@ -193,7 +193,7 @@ class App.ControllerForm extends App.Controller
null: false
relation: 'User'
autocapitalize: false
help: 'Select the customer of the ticket or create one.'
help: __('Select the customer of the ticket or create one.')
helpLink: '<a href="" class="customer_new">&raquo;</a>'
callback: @userInfo
class: 'span7'
@ -202,7 +202,7 @@ class App.ControllerForm extends App.Controller
# colection as relation
attribute_config = {
name: 'priority_id'
display: 'Priority'
display: __('Priority')
tag: 'select'
multiple: false
null: false
@ -216,7 +216,7 @@ class App.ControllerForm extends App.Controller
# colection as options
attribute_config = {
name: 'priority_id'
display: 'Priority'
display: __('Priority')
tag: 'select'
multiple: false
null: false

View file

@ -1,8 +1,8 @@
class App.ControllerGenericDescription extends App.ControllerModal
buttonClose: true
buttonCancel: false
buttonSubmit: 'Close'
head: 'Description'
buttonSubmit: __('Close')
head: __('Description')
content: =>
marked(App.i18n.translateContent(@description))

View file

@ -1,9 +1,9 @@
class App.ControllerGenericDestroyConfirm extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: 'delete'
buttonSubmit: __('delete')
buttonClass: 'btn--danger'
head: 'Confirm'
head: __('Confirm')
small: true
content: ->
@ -23,9 +23,9 @@ class App.ControllerGenericDestroyConfirm extends App.ControllerModal
class App.ControllerConfirm extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: 'yes'
buttonSubmit: __('yes')
buttonClass: 'btn--danger'
head: 'Confirm'
head: __('Confirm')
small: true
content: ->

View file

@ -51,5 +51,5 @@ class App.ControllerGenericEdit extends App.ControllerModal
App[ ui.genericObject ].fetch(id: @id)
ui.log 'errors'
ui.formEnable(e)
ui.controller.showAlert(details.error_human || details.error || 'Unable to update object!')
ui.controller.showAlert(details.error_human || details.error || __('Unable to update object!'))
)

View file

@ -1,9 +1,9 @@
class App.ControllerErrorModal extends App.ControllerModal
buttonClose: true
buttonCancel: false
buttonSubmit: 'Close'
buttonSubmit: __('Close')
#buttonClass: 'btn--danger'
head: 'Error'
head: __('Error')
#small: true
#shown: true
showTrySupport: true

View file

@ -5,7 +5,7 @@ class App.GenericHistory extends App.ControllerModal
buttonClose: true
buttonCancel: false
buttonSubmit: false
head: 'History'
head: __('History')
shown: false
constructor: ->

View file

@ -51,5 +51,5 @@ class App.ControllerGenericNew extends App.ControllerModal
fail: (settings, details) ->
ui.log 'errors', details
ui.formEnable(e)
ui.controller.showAlert(details.error_human || details.error || 'Unable to create object!')
ui.controller.showAlert(details.error_human || details.error || __('Unable to create object!'))
)

View file

@ -1,5 +1,5 @@
class App.ControllerReorderModal extends App.ControllerModal
head: 'Drag to reorder'
head: __('Drag to reorder')
content: ->
view = $(App.view('reorder_modal')())
@ -10,7 +10,7 @@ class App.ControllerReorderModal extends App.ControllerModal
true
overview: ['title']
attribute_list: [
{ name: 'title', display: 'Name' }
{ name: 'title', display: __('Name') }
]
objects: @items
)
@ -49,4 +49,3 @@ class App.ControllerReorderModal extends App.ControllerModal
onSubmit: ->
super
@save()

View file

@ -28,7 +28,7 @@
# add new header item
attribute =
name: 'some name'
display: 'Some Name'
display: __('Some Name')
headers.push attribute
console.log('new header is', headers)
headers
@ -78,12 +78,12 @@
el: element
overview: ['time', 'area', 'level', 'browser', 'location', 'data']
attribute_list: [
{ name: 'time', display: 'Time', tag: 'datetime' },
{ name: 'area', display: 'Area', type: 'text' },
{ name: 'level', display: 'Level', type: 'text' },
{ name: 'browser', display: 'Browser', type: 'text' },
{ name: 'location', display: 'Location', type: 'text' },
{ name: 'data', display: 'Data', type: 'text' },
{ name: 'time', display: __('Time'), tag: 'datetime' },
{ name: 'area', display: __('Area'), type: 'text' },
{ name: 'level', display: __('Level'), type: 'text' },
{ name: 'browser', display: __('Browser'), type: 'text' },
{ name: 'location', display: __('Location'), type: 'text' },
{ name: 'data', display: __('Data'), type: 'text' },
]
objects: data
)
@ -641,7 +641,7 @@ class App.ControllerTable extends App.Controller
if @clone
@actions.push
name: 'clone'
display: 'Clone'
display: __('Clone')
icon: 'clipboard'
class: 'create js-clone'
callback: (id) =>
@ -660,7 +660,7 @@ class App.ControllerTable extends App.Controller
if @destroy
@actions.push
name: 'delete'
display: 'Delete'
display: __('Delete')
icon: 'trash'
class: 'danger js-delete'
callback: (id) =>
@ -672,7 +672,7 @@ class App.ControllerTable extends App.Controller
if @actions.length
@headers.push
name: 'action'
display: 'Action'
display: __('Action')
width: '50px'
displayWidth: 50
align: 'right'

View file

@ -11,7 +11,7 @@ class App.ChannelEmailFilter extends App.Controller
template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' )
description = 'With filters you can e. g. dispatch new tickets into certain groups or set a certain priority for tickets of a VIP customer.'
description = __('With filters you can e. g. dispatch new tickets into certain groups or set a certain priority for tickets of a VIP customer.')
new App.ControllerTable(
el: template.find('.overview')
@ -28,7 +28,7 @@ class App.ChannelEmailFilter extends App.Controller
e.preventDefault()
new App.ControllerGenericNew(
pageData:
object: 'Postmaster Filter'
object: __('Postmaster Filter')
genericObject: 'PostmasterFilter'
container: @el.closest('.content')
callback: @load
@ -40,7 +40,7 @@ class App.ChannelEmailFilter extends App.Controller
new App.ControllerGenericEdit(
id: id,
pageData:
object: 'Postmaster Filter'
object: __('Postmaster Filter')
genericObject: 'PostmasterFilter'
container: @el.closest('.content')
callback: @load

View file

@ -11,11 +11,11 @@ class App.ChannelEmailSignature extends App.Controller
template = $( '<div><div class="overview"></div><a data-type="new" class="btn btn--success">' + App.i18n.translateContent('New') + '</a></div>' )
description = '''
description = __('''
You can define different signatures for each group. So you can have different email signatures for different departments.
Once you have created a signature here, you need also to edit the groups where you want to use it.
'''
''')
new App.ControllerTable(
el: template.find('.overview')
@ -46,7 +46,7 @@ class ChannelEmailSignatureEdit extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Signature'
head: __('Signature')
content: =>
if @object
@ -90,5 +90,5 @@ class ChannelEmailSignatureEdit extends App.ControllerModal
fail: (settings, details) =>
@log 'errors', details
@formEnable(e)
@form.showAlert(details.error_human || details.error || 'Unable to create object!')
@form.showAlert(details.error_human || details.error || __('Unable to create object!'))
)

View file

@ -1,6 +1,6 @@
class ChannelChat extends App.ControllerSubContent
requiredPermission: 'admin.channel_chat'
header: 'Chat'
header: __('Chat')
events:
'change .js-params': 'updateParams'
'input .js-params': 'updateParams'
@ -32,74 +32,74 @@ class ChannelChat extends App.ControllerSubContent
name: 'chatId'
default: '1'
type: 'Number'
description: 'Identifier of the chat-topic.'
description: __('Identifier of the chat-topic.')
}
{
name: 'show'
default: true
type: 'Boolean'
description: 'Show the chat when ready.'
description: __('Show the chat when ready.')
}
{
name: 'target'
default: "$('body')"
type: 'jQuery Object'
description: 'Where to append the chat to.'
description: __('Where to append the chat to.')
}
{
name: 'host'
default: '(Empty)'
type: 'String'
description: "If left empty, the host gets auto-detected - in this case %s. The auto-detection reads out the host from the <script> tag. If you don't include it via a <script> tag you need to specify the host."
description: __("If left empty, the host gets auto-detected - in this case %s. The auto-detection reads out the host from the <script> tag. If you don't include it via a <script> tag you need to specify the host.")
descriptionSubstitute: window.location.origin
}
{
name: 'debug'
default: false
type: 'Boolean'
description: 'Enables console logging.'
description: __('Enables console logging.')
}
{
name: 'title'
default: "'<strong>Chat</strong> with us!'"
type: 'String'
description: 'Welcome Title shown on the closed chat. Can contain HTML.'
description: __('Welcome Title shown on the closed chat. Can contain HTML.')
}
{
name: 'fontSize'
default: 'undefined'
type: 'String'
description: 'CSS font-size with a unit like 12px, 1.5em. If left to undefined it inherits the font-size of the website.'
description: __('CSS font-size with a unit like 12px, 1.5em. If left to undefined it inherits the font-size of the website.')
}
{
name: 'flat'
default: 'false'
type: 'Boolean'
description: 'Removes the shadows for a flat look.'
description: __('Removes the shadows for a flat look.')
}
{
name: 'buttonClass'
default: "'open-zammad-chat'"
type: 'String'
description: 'Add this class to a button on your page that should open the chat.'
description: __('Add this class to a button on your page that should open the chat.')
}
{
name: 'inactiveClass'
default: "'is-inactive'"
type: 'String'
description: 'This class gets added to the button on initialization and gets removed once the chat connection got established.'
description: __('This class gets added to the button on initialization and gets removed once the chat connection got established.')
}
{
name: 'cssAutoload'
default: 'true'
type: 'Boolean'
description: 'Automatically loads the chat.css file. If you want to use your own css, just set it to false.'
description: __('Automatically loads the chat.css file. If you want to use your own css, just set it to false.')
}
{
name: 'cssUrl'
default: 'undefined'
type: 'String'
description: 'Location of an external chat.css file.'
description: __('Location of an external chat.css file.')
}
]
@ -353,7 +353,7 @@ class ChannelChat extends App.ControllerSubContent
@code.each (i, block) ->
hljs.highlightBlock block
App.Config.set('Chat', { prio: 4000, name: 'Chat', parent: '#channels', target: '#channels/chat', controller: ChannelChat, permission: ['admin.channel_chat'] }, 'NavBarAdmin')
App.Config.set('Chat', { prio: 4000, name: __('Chat'), parent: '#channels', target: '#channels/chat', controller: ChannelChat, permission: ['admin.channel_chat'] }, 'NavBarAdmin')
class Topics extends App.Controller
events:
@ -373,9 +373,9 @@ class Topics extends App.Controller
new: (e) =>
new App.ControllerGenericNew(
pageData:
title: 'Chats'
object: 'Chat'
objects: 'Chats'
title: __('Chats')
object: __('Chat')
objects: __('Chats')
genericObject: 'Chat'
callback: @render
container: @el.closest('.content')
@ -389,7 +389,7 @@ class Topics extends App.Controller
id: id
genericObject: 'Chat'
pageData:
object: 'Chat'
object: __('Chat')
container: @el.closest('.content')
callback: @render
)

View file

@ -1,29 +1,29 @@
class ChannelEmail extends App.ControllerTabs
requiredPermission: 'admin.channel_email'
header: 'Email'
header: __('Email')
constructor: ->
super
@title 'Email', true
@title __('Email'), true
@tabs = [
{
name: 'Accounts',
name: __('Accounts'),
target: 'c-account',
controller: ChannelEmailAccountOverview,
},
{
name: 'Filter',
name: __('Filter'),
target: 'c-filter',
controller: App.ChannelEmailFilter,
},
{
name: 'Signatures',
name: __('Signatures'),
target: 'c-signature',
controller: App.ChannelEmailSignature,
},
{
name: 'Settings',
name: __('Settings'),
target: 'c-setting',
controller: App.SettingsArea,
params: { area: 'Email::Base' },
@ -142,7 +142,7 @@ class ChannelEmailAccountOverview extends App.Controller
e.preventDefault()
id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm(
message: 'Sure?'
message: __('Sure?')
callback: =>
@ajax(
id: 'email_delete'
@ -197,7 +197,7 @@ class ChannelEmailAccountOverview extends App.Controller
channel_id = $(e.target).closest('.action').data('id')
new App.ControllerGenericNew(
pageData:
object: 'Email Address'
object: __('Email Address')
genericObject: 'EmailAddress'
container: @el.closest('.content')
item:
@ -210,7 +210,7 @@ class ChannelEmailAccountOverview extends App.Controller
id = $(e.target).closest('li').data('id')
new App.ControllerGenericEdit(
pageData:
object: 'Email Address'
object: __('Email Address')
genericObject: 'EmailAddress'
container: @el.closest('.content')
id: id
@ -254,11 +254,11 @@ class ChannelEmailEdit extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Channel'
head: __('Channel')
content: =>
configureAttributesBase = [
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
{ name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
]
@form = new App.ControllerForm(
model:
@ -298,7 +298,7 @@ class ChannelEmailEdit extends App.ControllerModal
error: (xhr) =>
data = JSON.parse(xhr.responseText)
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
)
class ChannelEmailAccountWizard extends App.ControllerWizardModal
@ -371,10 +371,10 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
# base
configureAttributesBase = [
{ name: 'realname', display: 'Organization & Department Name', tag: 'input', type: 'text', limit: 160, null: false, placeholder: 'Organization Support', autocomplete: 'off' },
{ name: 'email', display: 'Email', tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' },
{ name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true },
{ name: 'realname', display: __('Organization & Department Name'), tag: 'input', type: 'text', limit: 160, null: false, placeholder: __('Organization Support'), autocomplete: 'off' },
{ name: 'email', display: __('Email'), tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' },
{ name: 'password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true },
]
@formMeta = new App.ControllerForm(
el: @$('.base-settings'),
@ -386,7 +386,7 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
# outbound
configureAttributesOutbound = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound },
{ name: 'adapter', display: __('Send Mails via'), tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound },
]
new App.ControllerForm(
el: @$('.base-outbound-type')
@ -400,22 +400,22 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
# inbound
configureAttributesInbound = [
{ name: 'adapter', display: 'Type', tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound },
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'off' },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::ssl', display: 'SSL/STARTTLS', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' },
{ name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' },
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, item_class: 'formGroup--halfSize' },
{ name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false, item_class: 'formGroup--halfSize' },
{ name: 'adapter', display: __('Type'), tag: 'select', multiple: false, null: false, options: @channelDriver.email.inbound },
{ name: 'options::host', display: __('Host'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false },
{ name: 'options::user', display: __('User'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autocomplete: 'off' },
{ name: 'options::password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: false, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::ssl', display: __('SSL/STARTTLS'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, default: true, translate: true, item_class: 'formGroup--halfSize' },
{ name: 'options::port', display: __('Port'), tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false, default: '993', item_class: 'formGroup--halfSize' },
{ name: 'options::folder', display: __('Folder'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, item_class: 'formGroup--halfSize' },
{ name: 'options::keep_on_server', display: __('Keep messages on server'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false, item_class: 'formGroup--halfSize' },
]
if !@channel
#Email Inbound form opened from new email wizard, show full settings
configureAttributesInbound = [
{ name: 'options::realname', display: 'Organization & Department Name', tag: 'input', type: 'text', limit: 160, null: false, placeholder: 'Organization Support', autocomplete: 'off' },
{ name: 'options::email', display: 'Email', tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' },
{ name: 'options::group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true },
{ name: 'options::realname', display: __('Organization & Department Name'), tag: 'input', type: 'text', limit: 160, null: false, placeholder: __('Organization Support'), autocomplete: 'off' },
{ name: 'options::email', display: __('Email'), tag: 'input', type: 'email', limit: 120, null: false, placeholder: 'support@example.com', autocapitalize: false, autocomplete: 'off' },
{ name: 'options::group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true },
].concat(configureAttributesInbound)
@ -468,10 +468,10 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
adapter = @$('.js-outbound [name=adapter]').val()
if adapter is 'smtp'
configureAttributesOutbound = [
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off', },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false },
{ name: 'options::host', display: __('Host'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: __('User'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off', },
{ name: 'options::password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::port', display: __('Port'), tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false },
]
@form = new App.ControllerForm(
el: @$('.base-outbound-settings')
@ -525,10 +525,10 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
else if data.result is 'duplicate'
@showSlide('js-intro')
@showAlert('js-intro', 'Account already exists!')
@showAlert('js-intro', __('Account already exists!'))
else
@showSlide('js-inbound')
@showAlert('js-inbound', 'Unable to detect your server settings. Manual configuration needed.')
@showAlert('js-inbound', __('Unable to detect your server settings. Manual configuration needed.'))
@$('.js-inbound [name="options::user"]').val(@account['meta']['email'])
@$('.js-inbound [name="options::password"]').val(@account['meta']['password'])
@$('.js-inbound [name="options::email"]').val(@account['meta']['email'])
@ -749,7 +749,7 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
@delay(
=>
@showSlide('js-intro')
@showAlert('js-intro', 'Unable to verify sending and receiving. Please check your settings.')
@showAlert('js-intro', __('Unable to verify sending and receiving. Please check your settings.'))
2300
)
@ -759,7 +759,7 @@ class ChannelEmailAccountWizard extends App.ControllerWizardModal
@verify(@account, count + 1)
error: =>
@showSlide('js-intro')
@showAlert('js-intro', 'Unable to verify sending and receiving. Please check your settings.')
@showAlert('js-intro', __('Unable to verify sending and receiving. Please check your settings.'))
)
hide: (e) =>
@ -830,7 +830,7 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
# outbound
configureAttributesOutbound = [
{ name: 'adapter', display: 'Send Mails via', tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound },
{ name: 'adapter', display: __('Send Mails via'), tag: 'select', multiple: false, null: false, options: @channelDriver.email.outbound },
]
new App.ControllerForm(
el: @$('.base-outbound-type')
@ -849,10 +849,10 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
adapter = @$('.js-outbound [name=adapter]').val()
if adapter is 'smtp'
configureAttributesOutbound = [
{ name: 'options::host', display: 'Host', tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: 'User', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off' },
{ name: 'options::password', display: 'Password', tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::port', display: 'Port', tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false },
{ name: 'options::host', display: __('Host'), tag: 'input', type: 'text', limit: 120, null: false, autocapitalize: false, autofocus: true },
{ name: 'options::user', display: __('User'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, autocomplete: 'off' },
{ name: 'options::password', display: __('Password'), tag: 'input', type: 'password', limit: 120, null: true, autocapitalize: false, autocomplete: 'new-password', single: true },
{ name: 'options::port', display: __('Port'), tag: 'input', type: 'text', limit: 6, null: true, autocapitalize: false },
]
@form = new App.ControllerForm(
el: @$('.base-outbound-settings')
@ -900,4 +900,4 @@ class ChannelEmailNotificationWizard extends App.ControllerWizardModal
@enable(e)
)
App.Config.set('Email', { prio: 3000, name: 'Email', parent: '#channels', target: '#channels/email', controller: ChannelEmail, permission: ['admin.channel_email'] }, 'NavBarAdmin')
App.Config.set('Email', { prio: 3000, name: __('Email'), parent: '#channels', target: '#channels/email', controller: ChannelEmail, permission: ['admin.channel_email'] }, 'NavBarAdmin')

View file

@ -1,6 +1,6 @@
class ChannelFacebook extends App.ControllerSubContent
requiredPermission: 'admin.channel_facebook'
header: 'Facebook'
header: __('Facebook')
events:
'click .js-new': 'new'
'click .js-edit': 'edit'
@ -95,7 +95,7 @@ class ChannelFacebook extends App.ControllerSubContent
e.preventDefault()
id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm(
message: 'Sure?'
message: __('Sure?')
callback: =>
@ajax(
id: 'facebook_delete'
@ -136,7 +136,7 @@ class ChannelFacebook extends App.ControllerSubContent
)
class AppConfig extends App.ControllerModal
head: 'Connect Facebook App'
head: __('Connect Facebook App')
shown: true
button: 'Connect'
buttonCancel: true
@ -178,15 +178,15 @@ class AppConfig extends App.ControllerModal
@isChanged = true
@close()
fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.')
@el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
)
return
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
)
class AccountEdit extends App.ControllerModal
head: 'Facebook Account'
head: __('Facebook Account')
shown: true
buttonCancel: true
@ -242,7 +242,7 @@ class AccountEdit extends App.ControllerModal
error: (xhr) =>
data = JSON.parse(xhr.responseText)
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
)
App.Config.set('Facebook', { prio: 5100, name: 'Facebook', parent: '#channels', target: '#channels/facebook', controller: ChannelFacebook, permission: ['admin.channel_facebook'] }, 'NavBarAdmin')
App.Config.set('Facebook', { prio: 5100, name: __('Facebook'), parent: '#channels', target: '#channels/facebook', controller: ChannelFacebook, permission: ['admin.channel_facebook'] }, 'NavBarAdmin')

View file

@ -1,7 +1,7 @@
# coffeelint: disable=no_unnecessary_double_quotes
class ChannelForm extends App.ControllerSubContent
requiredPermission: 'admin.channel_formular'
header: 'Form'
header: __('Form')
events:
'change form.js-paramsDesigner': 'updateParamsDesigner'
'keyup form.js-paramsDesigner': 'updateParamsDesigner'
@ -88,4 +88,4 @@ class ChannelForm extends App.ControllerSubContent
value = @paramsSetting.find('[name=group_id]').val()
App.Setting.set('form_ticket_create_group_id', value)
App.Config.set('Form', { prio: 2000, name: 'Form', parent: '#channels', target: '#channels/form', controller: ChannelForm, permission: ['admin.channel_formular'] }, 'NavBarAdmin')
App.Config.set('Form', { prio: 2000, name: __('Form'), parent: '#channels', target: '#channels/form', controller: ChannelForm, permission: ['admin.channel_formular'] }, 'NavBarAdmin')

View file

@ -1,29 +1,29 @@
class App.ChannelGoogle extends App.ControllerTabs
requiredPermission: 'admin.channel_google'
header: 'Google'
header: __('Google')
constructor: ->
super
@title 'Google', true
@title __('Google'), true
@tabs = [
{
name: 'Accounts',
name: __('Accounts'),
target: 'c-account',
controller: ChannelAccountOverview,
},
{
name: 'Filter',
name: __('Filter'),
target: 'c-filter',
controller: App.ChannelEmailFilter,
},
{
name: 'Signatures',
name: __('Signatures'),
target: 'c-signature',
controller: App.ChannelEmailSignature,
},
{
name: 'Settings',
name: __('Settings'),
target: 'c-setting',
controller: App.SettingsArea,
params: { area: 'Email::Base' },
@ -145,7 +145,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
e.preventDefault()
id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm(
message: 'Sure?'
message: __('Sure?')
callback: =>
@ajax(
id: 'google_delete'
@ -215,11 +215,11 @@ class ChannelAccountOverview extends App.ControllerSubContent
@load()
@notify
type: 'success'
msg: 'Rollback of channel migration succeeded!'
msg: __('Rollback of channel migration succeeded!')
error: (data) =>
@notify
type: 'error'
msg: 'Failed to rollback migration of the channel!'
msg: __('Failed to rollback migration of the channel!')
)
groupChange: (e) =>
@ -237,7 +237,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
channel_id = $(e.target).closest('.action').data('id')
new App.ControllerGenericNew(
pageData:
object: 'Email Address'
object: __('Email Address')
genericObject: 'EmailAddress'
container: @el.closest('.content')
item:
@ -250,7 +250,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
id = $(e.target).closest('li').data('id')
new App.ControllerGenericEdit(
pageData:
object: 'Email Address'
object: __('Email Address')
genericObject: 'EmailAddress'
container: @el.closest('.content')
id: id
@ -271,12 +271,12 @@ class ChannelInboundEdit extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Channel'
head: __('Channel')
content: =>
configureAttributesBase = [
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, placeholder: 'optional' },
{ name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false },
{ name: 'options::folder', display: __('Folder'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false, placeholder: __('optional') },
{ name: 'options::keep_on_server', display: __('Keep messages on server'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false },
]
@form = new App.ControllerForm(
model:
@ -323,7 +323,7 @@ class ChannelInboundEdit extends App.ControllerModal
details = xhr.responseJSON || {}
@notify
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save changes.')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save changes.'))
timeout: 6000
)
@ -331,11 +331,11 @@ class ChannelGroupEdit extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Channel'
head: __('Channel')
content: =>
configureAttributesBase = [
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
{ name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
]
@form = new App.ControllerForm(
model:
@ -375,11 +375,11 @@ class ChannelGroupEdit extends App.ControllerModal
error: (xhr) =>
data = JSON.parse(xhr.responseText)
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
)
class AppConfig extends App.ControllerModal
head: 'Connect Google App'
head: __('Connect Google App')
shown: true
button: 'Connect'
buttonCancel: true
@ -421,11 +421,11 @@ class AppConfig extends App.ControllerModal
@isChanged = true
@close()
fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.')
@el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
)
return
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
)
App.Config.set('google', { prio: 5000, name: 'Google', parent: '#channels', target: '#channels/google', controller: App.ChannelGoogle, permission: ['admin.channel_google'] }, 'NavBarAdmin')
App.Config.set('google', { prio: 5000, name: __('Google'), parent: '#channels', target: '#channels/google', controller: App.ChannelGoogle, permission: ['admin.channel_google'] }, 'NavBarAdmin')

View file

@ -1,29 +1,29 @@
class App.ChannelMicrosoft365 extends App.ControllerTabs
requiredPermission: 'admin.channel_microsoft365'
header: 'Microsoft 365'
header: __('Microsoft 365')
constructor: ->
super
@title 'Microsoft 365', true
@title __('Microsoft 365'), true
@tabs = [
{
name: 'Accounts',
name: __('Accounts'),
target: 'c-account',
controller: ChannelAccountOverview,
},
{
name: 'Filter',
name: __('Filter'),
target: 'c-filter',
controller: App.ChannelEmailFilter,
},
{
name: 'Signatures',
name: __('Signatures'),
target: 'c-signature',
controller: App.ChannelEmailSignature,
},
{
name: 'Settings',
name: __('Settings'),
target: 'c-setting',
controller: App.SettingsArea,
params: { area: 'Email::Base' },
@ -136,7 +136,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
e.preventDefault()
id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm(
message: 'Sure?'
message: __('Sure?')
callback: =>
@ajax(
id: 'microsoft365_delete'
@ -206,11 +206,11 @@ class ChannelAccountOverview extends App.ControllerSubContent
@load()
@notify
type: 'success'
msg: 'Rollback of channel migration succeeded!'
msg: __('Rollback of channel migration succeeded!')
error: (data) =>
@notify
type: 'error'
msg: 'Failed to rollback migration of the channel!'
msg: __('Failed to rollback migration of the channel!')
)
groupChange: (e) =>
@ -228,7 +228,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
channel_id = $(e.target).closest('.action').data('id')
new App.ControllerGenericNew(
pageData:
object: 'Email Address'
object: __('Email Address')
genericObject: 'EmailAddress'
container: @el.closest('.content')
item:
@ -241,7 +241,7 @@ class ChannelAccountOverview extends App.ControllerSubContent
id = $(e.target).closest('li').data('id')
new App.ControllerGenericEdit(
pageData:
object: 'Email Address'
object: __('Email Address')
genericObject: 'EmailAddress'
container: @el.closest('.content')
id: id
@ -262,12 +262,12 @@ class ChannelInboundEdit extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Channel'
head: __('Channel')
content: =>
configureAttributesBase = [
{ name: 'options::folder', display: 'Folder', tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::keep_on_server', display: 'Keep messages on server', tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false },
{ name: 'options::folder', display: __('Folder'), tag: 'input', type: 'text', limit: 120, null: true, autocapitalize: false },
{ name: 'options::keep_on_server', display: __('Keep messages on server'), tag: 'boolean', null: true, options: { true: 'yes', false: 'no' }, translate: true, default: false },
]
@form = new App.ControllerForm(
model:
@ -314,7 +314,7 @@ class ChannelInboundEdit extends App.ControllerModal
details = xhr.responseJSON || {}
@notify
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save changes.')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save changes.'))
timeout: 6000
)
@ -322,11 +322,11 @@ class ChannelGroupEdit extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true
head: 'Channel'
head: __('Channel')
content: =>
configureAttributesBase = [
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
{ name: 'group_id', display: __('Destination Group'), tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
]
@form = new App.ControllerForm(
model:
@ -366,11 +366,11 @@ class ChannelGroupEdit extends App.ControllerModal
error: (xhr) =>
data = JSON.parse(xhr.responseText)
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
)
class AppConfig extends App.ControllerModal
head: 'Connect Microsoft 365 App'
head: __('Connect Microsoft 365 App')
shown: true
button: 'Connect'
buttonCancel: true
@ -412,11 +412,11 @@ class AppConfig extends App.ControllerModal
@isChanged = true
@close()
fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.')
@el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
)
return
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
)
App.Config.set('microsoft365', { prio: 5000, name: 'Microsoft 365', parent: '#channels', target: '#channels/microsoft365', controller: App.ChannelMicrosoft365, permission: ['admin.channel_microsoft365'] }, 'NavBarAdmin')
App.Config.set('microsoft365', { prio: 5000, name: __('Microsoft 365'), parent: '#channels', target: '#channels/microsoft365', controller: App.ChannelMicrosoft365, permission: ['admin.channel_microsoft365'] }, 'NavBarAdmin')

View file

@ -1,13 +1,13 @@
class ChannelSms extends App.ControllerTabs
requiredPermission: 'admin.channel_sms'
header: 'SMS'
header: __('SMS')
constructor: ->
super
@title 'SMS', true
@title __('SMS'), true
@tabs = [
{
name: 'Accounts',
name: __('Accounts'),
target: 'c-account',
controller: ChannelSmsAccountOverview,
},
@ -136,11 +136,11 @@ class ChannelSmsAccountOverview extends App.Controller
)
class ChannelSmsAccount extends App.ControllerModal
head: 'SMS Account'
head: __('SMS Account')
buttonCancel: true
centerButtons: [
{
text: 'Test'
text: __('Test')
className: 'js-test'
}
]
@ -164,7 +164,7 @@ class ChannelSmsAccount extends App.ControllerModal
el: el.find('.js-channelAdapterSelector')
model:
configure_attributes: [
{ name: 'options::adapter', display: 'Provider', tag: 'select', null: false, options: options, nulloption: true }
{ name: 'options::adapter', display: __('Provider'), tag: 'select', null: false, options: options, nulloption: true }
]
className: ''
params: @channel
@ -198,7 +198,7 @@ class ChannelSmsAccount extends App.ControllerModal
el: el.find('.js-channelWebhook')
model:
configure_attributes: [
{ name: 'options::webhook', display: 'Webhook', tag: 'input', type: 'text', limit: 200, null: false, default: webhook, disabled: true },
{ name: 'options::webhook', display: __('Webhook'), tag: 'input', type: 'text', limit: 200, null: false, default: webhook, disabled: true },
]
className: ''
params: @channel
@ -262,7 +262,7 @@ class ChannelSmsAccount extends App.ControllerModal
fail: (settings, details) ->
ui.log 'errors', details
ui.formEnable(e)
ui.showAlert(details.error_human || details.error || 'Unable to update object!')
ui.showAlert(details.error_human || details.error || __('Unable to update object!'))
)
onTest: (e) ->
@ -273,11 +273,11 @@ class ChannelSmsAccount extends App.ControllerModal
)
class ChannelSmsNotification extends App.ControllerModal
head: 'SMS Notification'
head: __('SMS Notification')
buttonCancel: true
centerButtons: [
{
text: 'Test'
text: __('Test')
className: 'js-test'
}
]
@ -303,7 +303,7 @@ class ChannelSmsNotification extends App.ControllerModal
el: el.find('.js-channelAdapterSelector')
model:
configure_attributes: [
{ name: 'options::adapter', display: 'Provider', tag: 'select', null: false, options: options, nulloption: true }
{ name: 'options::adapter', display: __('Provider'), tag: 'select', null: false, options: options, nulloption: true }
]
className: ''
params: @channel
@ -376,7 +376,7 @@ class ChannelSmsNotification extends App.ControllerModal
fail: (settings, details) ->
ui.log 'errors', details
ui.formEnable(e)
ui.showAlert(details.error_human || details.error || 'Unable to update object!')
ui.showAlert(details.error_human || details.error || __('Unable to update object!'))
)
onTest: (e) ->
@ -387,15 +387,15 @@ class ChannelSmsNotification extends App.ControllerModal
)
class TestModal extends App.ControllerModal
head: 'Test SMS provider'
head: __('Test SMS provider')
buttonCancel: true
content: ->
form = new App.ControllerForm(
model:
configure_attributes: [
{ name: 'recipient', display: 'Recipient', tag: 'input', null: false }
{ name: 'message', display: 'Message', tag: 'input', null: false, default: 'Test message from Zammad' }
{ name: 'recipient', display: __('Recipient'), tag: 'input', null: false }
{ name: 'message', display: __('Message'), tag: 'input', null: false, default: __('Test message from Zammad') }
]
className: ''
)
@ -435,8 +435,8 @@ class TestModal extends App.ControllerModal
data = JSON.parse(xhr.responseText)
@formEnable(@el)
@el.find('.js-danger')
.text(@T(data.error || 'Unable to perform test'))
.text(@T(data.error || __('Unable to perform test')))
.removeClass('hide')
)
App.Config.set('SMS', { prio: 3100, name: 'SMS', parent: '#channels', target: '#channels/sms', controller: ChannelSms, permission: ['admin.channel_sms'] }, 'NavBarAdmin')
App.Config.set('SMS', { prio: 3100, name: __('SMS'), parent: '#channels', target: '#channels/sms', controller: ChannelSms, permission: ['admin.channel_sms'] }, 'NavBarAdmin')

View file

@ -63,7 +63,7 @@ class ChannelTelegram extends App.ControllerSubContent
e.preventDefault()
id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm(
message: 'Sure?'
message: __('Sure?')
callback: =>
@ajax(
id: 'telegram_delete'
@ -104,7 +104,7 @@ class ChannelTelegram extends App.ControllerSubContent
)
class BotAdd extends App.ControllerModal
head: 'Add Telegram Bot'
head: __('Add Telegram Bot')
shown: true
button: 'Add'
buttonCancel: true
@ -149,12 +149,12 @@ class BotAdd extends App.ControllerModal
error: (xhr) =>
data = JSON.parse(xhr.responseText)
@formEnable(e)
error_message = App.i18n.translateContent(data.error || 'Unable to save Bot.')
error_message = App.i18n.translateContent(data.error || __('Unable to save Bot.'))
@el.find('.alert').removeClass('hidden').text(error_message)
)
class BotEdit extends App.ControllerModal
head: 'Telegram Account'
head: __('Telegram Account')
shown: true
buttonCancel: true
@ -197,8 +197,8 @@ class BotEdit extends App.ControllerModal
error: (xhr) =>
data = JSON.parse(xhr.responseText)
@formEnable(e)
error_message = App.i18n.translateContent(data.error || 'Unable to save changes.')
error_message = App.i18n.translateContent(data.error || __('Unable to save changes.'))
@el.find('.alert').removeClass('hidden').text(error_message)
)
App.Config.set('Telegram', { prio: 5100, name: 'Telegram', parent: '#channels', target: '#channels/telegram', controller: ChannelTelegram, permission: ['admin.channel_telegram'] }, 'NavBarAdmin')
App.Config.set('Telegram', { prio: 5100, name: __('Telegram'), parent: '#channels', target: '#channels/telegram', controller: ChannelTelegram, permission: ['admin.channel_telegram'] }, 'NavBarAdmin')

View file

@ -102,7 +102,7 @@ class ChannelTwitter extends App.ControllerSubContent
e.preventDefault()
id = $(e.target).closest('.action').data('id')
new App.ControllerConfirm(
message: 'Sure?'
message: __('Sure?')
callback: =>
@ajax(
id: 'twitter_delete'
@ -143,7 +143,7 @@ class ChannelTwitter extends App.ControllerSubContent
)
class AppConfig extends App.ControllerModal
head: 'Connect Twitter App'
head: __('Connect Twitter App')
shown: true
button: 'Connect'
buttonCancel: true
@ -185,15 +185,15 @@ class AppConfig extends App.ControllerModal
@isChanged = true
@close()
fail: =>
@el.find('.alert').removeClass('hidden').text('Unable to create entry.')
@el.find('.alert').removeClass('hidden').text(__('Unable to create entry.'))
)
return
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to verify App.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to verify App.'))
)
class AccountEdit extends App.ControllerModal
head: 'Twitter Account'
head: __('Twitter Account')
shown: true
buttonCancel: true
@ -296,7 +296,7 @@ class AccountEdit extends App.ControllerModal
error: (xhr) =>
data = JSON.parse(xhr.responseText)
@formEnable(e)
@el.find('.alert').removeClass('hidden').text(data.error || 'Unable to save changes.')
@el.find('.alert').removeClass('hidden').text(data.error || __('Unable to save changes.'))
)
App.Config.set('Twitter', { prio: 5000, name: 'Twitter', parent: '#channels', target: '#channels/twitter', controller: ChannelTwitter, permission: ['admin.channel_twitter'] }, 'NavBarAdmin')
App.Config.set('Twitter', { prio: 5000, name: __('Twitter'), parent: '#channels', target: '#channels/twitter', controller: ChannelTwitter, permission: ['admin.channel_twitter'] }, 'NavBarAdmin')

View file

@ -1,14 +1,14 @@
class ChannelWeb extends App.ControllerTabs
requiredPermission: 'admin.channel_web'
header: 'Web'
header: __('Web')
constructor: ->
super
@title 'Web', true
@title __('Web'), true
@tabs = [
{
name: 'Settings',
name: __('Settings'),
target: 'w-setting',
controller: App.SettingsArea, params: { area: 'CustomerWeb::Base' },
},
@ -16,4 +16,4 @@ class ChannelWeb extends App.ControllerTabs
@render()
App.Config.set('Web', { prio: 1000, name: 'Web', parent: '#channels', target: '#channels/web', controller: ChannelWeb, permission: ['admin.channel_web'] }, 'NavBarAdmin')
App.Config.set('Web', { prio: 1000, name: __('Web'), parent: '#channels', target: '#channels/web', controller: ChannelWeb, permission: ['admin.channel_web'] }, 'NavBarAdmin')

View file

@ -38,7 +38,7 @@ class App.DashboardFirstSteps extends App.Controller
e.preventDefault()
new App.InviteUser(
#container: @el.closest('.content')
head: 'Invite Colleagues'
head: __('Invite Colleagues')
screen: 'invite_agent'
)
@ -46,7 +46,7 @@ class App.DashboardFirstSteps extends App.Controller
e.preventDefault()
new App.InviteUser(
#container: @el.closest('.content')
head: 'Invite Customer'
head: __('Invite Customer')
screen: 'invite_customer'
signup: true
)
@ -82,7 +82,7 @@ class App.DashboardFirstSteps extends App.Controller
e.preventDefault()
modal = new App.ControllerModal(
head: 'Test Ticket'
head: __('Test Ticket')
#container: @el.parents('.content')
content: @testTicketLoading
shown: true

View file

@ -2,22 +2,22 @@ class App.FirstStepsClues extends App.Controller
clues: [
{
container: '.js-dashboardMenuItem'
headline: 'Dashboard'
text: 'Here you see a quick overview of your and other agents\' performance.'
headline: __('Dashboard')
text: __('Here you see a quick overview of your and other agents\' performance.')
actions: [
'hover'
]
}
{
container: '.search-holder'
headline: 'Search'
text: 'Here you can search for tickets, customers and organizations. Use the wildcard §*§ to find everything. E. g. §smi*§ or §rosent*l§. You also can use ||double quotes|| for searching phrases §"some phrase"§.'
headline: __('Search')
text: __('Here you can search for tickets, customers and organizations. Use the wildcard §*§ to find everything. E. g. §smi*§ or §rosent*l§. You also can use ||double quotes|| for searching phrases §"some phrase"§.')
actions: []
}
{
container: '.user-menu .add'
headline: 'Create'
text: 'Here you can create new tickets. Also if you have the permissions you can create new customers and organizations.'
headline: __('Create')
text: __('Here you can create new tickets. Also if you have the permissions you can create new customers and organizations.')
actions: [
'hover .navigation',
'hover .user-menu .add'
@ -25,8 +25,8 @@ class App.FirstStepsClues extends App.Controller
}
{
container: '.user-menu .user .dropdown-menu'
headline: 'Personal Settings'
text: 'Here you can sign out, change the frontend language and see your last viewed items.'
headline: __('Personal Settings')
text: __('Here you can sign out, change the frontend language and see your last viewed items.')
actions: [
'hover .navigation',
'click .user-menu .user .js-action',
@ -35,8 +35,8 @@ class App.FirstStepsClues extends App.Controller
}
{
container: '.js-overviewsMenuItem'
headline: 'Overviews'
text: 'Here you find your ticket overviews for open, assigned and escalated tickets.'
headline: __('Overviews')
text: __('Here you find your ticket overviews for open, assigned and escalated tickets.')
actions: [
'hover'
]

View file

@ -24,7 +24,7 @@ class Stats extends App.ControllerDashboardStatsBase
for id, channel of data.StatsTicketChannelDistribution.channels
channel.overal_percentage = Math.round((channel.inbound + channel.outbound) / totalTickets * 100) || 0
data.StatsTicketChannelDistribution.description = 'How many of your tickets are coming from email, phone, Twitter, or Facebook? (Shows percentages for both inbound and outbound tickets.)'
data.StatsTicketChannelDistribution.description = __('How many of your tickets are coming from email, phone, Twitter, or Facebook? (Shows percentages for both inbound and outbound tickets.)')
content = App.view('dashboard/stats/ticket_channel_distribution')(data)

View file

@ -6,7 +6,7 @@ class Stats extends App.ControllerDashboardStatsBase
own: 0
total: 0
data.StatsTicketEscalation.description = 'How many escalated tickets do you have open? (Mr. Bubbles gets grumpy if you have too many…)'
data.StatsTicketEscalation.description = __('How many escalated tickets do you have open? (Mr. Bubbles gets grumpy if you have too many…)')
content = App.view('dashboard/stats/ticket_escalation')(data)

View file

@ -7,7 +7,7 @@ class Stats extends App.ControllerDashboardStatsBase
in_process: 0
average_per_agent: 0
data.StatsTicketInProcess.description = 'What percentage of your tickets have you responded to, updated, or modified in some way today?'
data.StatsTicketInProcess.description = __('What percentage of your tickets have you responded to, updated, or modified in some way today?')
content = App.view('dashboard/stats/ticket_in_process')(data)

View file

@ -8,7 +8,7 @@ class Stats extends App.ControllerDashboardStatsBase
total: 0
average_per_agent: 0
data.StatsTicketLoadMeasure.description = 'Out of all open tickets (company-wide), how many are assigned to you?'
data.StatsTicketLoadMeasure.description = __('Out of all open tickets (company-wide), how many are assigned to you?')
content = App.view('dashboard/stats/ticket_load_measure')(data)

View file

@ -6,7 +6,7 @@ class Stats extends App.ControllerDashboardStatsBase
percent: 0
average_per_agent: 0
data.StatsTicketReopen.description = 'How many of your tickets have been re-opened after being marked “closed”?'
data.StatsTicketReopen.description = __('How many of your tickets have been re-opened after being marked “closed”?')
content = App.view('dashboard/stats/ticket_reopen')(data)
if @$('.ticket_reopen').length > 0

View file

@ -7,7 +7,7 @@ class Stats extends App.ControllerDashboardStatsBase
state: 'supergood'
average_per_agent: 0
data.StatsTicketWaitingTime.description = 'How long has each customer had to wait, on average, to get a response from you today?'
data.StatsTicketWaitingTime.description = __('How long has each customer had to wait, on average, to get a response from you today?')
content = App.view('dashboard/stats/ticket_waiting_time')(data)
if @$('.ticket_waiting_time').length > 0

View file

@ -10,9 +10,9 @@ App.Config.set('User', {
class: 'user'
}, 'NavBarRight' )
App.Config.set('Admin', { prio: 9000, parent: '', name: 'Admin', translate: true, target: '#manage', icon: 'cog', permission: ['admin.*'] }, 'NavBarRight')
App.Config.set('New', { prio: 20000, parent: '', name: 'New', translate: true, target: '#new', class: 'add', icon: 'plus' }, 'NavBarRight')
App.Config.set('Misc', { prio: 90000, parent: '', name: 'Tools', translate: true, target: '#tools', child: true, class: 'tools' }, 'NavBar')
App.Config.set('Admin', { prio: 9000, parent: '', name: __('Admin'), translate: true, target: '#manage', icon: 'cog', permission: ['admin.*'] }, 'NavBarRight')
App.Config.set('New', { prio: 20000, parent: '', name: __('New'), translate: true, target: '#new', class: 'add', icon: 'plus' }, 'NavBarRight')
App.Config.set('Misc', { prio: 90000, parent: '', name: __('Tools'), translate: true, target: '#tools', child: true, class: 'tools' }, 'NavBar')
# only for testing
#App.Config.set('Misc1', { prio: 1600, parent: '#tools', name: 'Test 1', target: '#test1', permission: ['admin'] }, 'NavBar')
#App.Config.set('Misc2', { prio: 1700, parent: '#tools', name: 'Test 2', target: '#test2', permission: ['admin'] }, 'NavBar')
#App.Config.set('Misc1', { prio: 1600, parent: '#tools', name: __('Test 1'), target: '#test1', permission: ['admin'] }, 'NavBar')
#App.Config.set('Misc2', { prio: 1700, parent: '#tools', name: __('Test 2'), target: '#test2', permission: ['admin'] }, 'NavBar')

View file

@ -1,10 +1,10 @@
class CheckMk extends App.ControllerIntegrationBase
featureIntegration: 'check_mk_integration'
featureName: 'Checkmk'
featureName: __('Checkmk')
featureConfig: 'check_mk_config'
description: [
['This service receives http requests or emails from %s and creates tickets with host and service.', 'Checkmk']
['If the host and service has recovered, the ticket can be closed automatically.']
[__('This service receives http requests or emails from %s and creates tickets with host and service.'), 'Checkmk']
[__('If the host and service has recovered, the ticket can be closed automatically.')]
]
render: =>
@ -52,9 +52,9 @@ class Form extends App.Controller
App.Config.set(
'IntegrationCheckMk'
{
name: 'Checkmk'
name: __('Checkmk')
target: '#system/integration/check_mk'
description: 'An open source monitoring tool.'
description: __('An open source monitoring tool.')
controller: CheckMk
state: State
permission: ['admin.integration.check_mk']

View file

@ -1,10 +1,9 @@
class Clearbit extends App.ControllerIntegrationBase
featureIntegration: 'clearbit_integration'
featureName: 'Clearbit'
featureName: __('Clearbit')
featureConfig: 'clearbit_config'
description: [
['Automatically enrich your customers and organizations with fresh, up-to-date intel. Map data directly to object fields.
']
[__('Automatically enrich your customers and organizations with fresh, up-to-date intel. Map data directly to object fields.')]
]
render: =>
@ -62,9 +61,9 @@ class Form extends App.Controller
if !@config
@config = @currentConfig()
settings = [
{ name: 'api_key', display: 'API Key', tag: 'input', type: 'text', limit: 100, null: false, placeholder: '...', note: 'Your api key.' },
{ name: 'organization_autocreate', display: 'Auto create', tag: 'boolean', type: 'boolean', null: false, note: 'Create organizations automatically if record has one.' },
{ name: 'organization_shared', display: 'Shared', tag: 'boolean', type: 'boolean', null: false, note: 'New organizations are shared.' },
{ name: 'api_key', display: __('API Key'), tag: 'input', type: 'text', limit: 100, null: false, placeholder: '...', note: __('Your api key.') },
{ name: 'organization_autocreate', display: __('Auto create'), tag: 'boolean', type: 'boolean', null: false, note: __('Create organizations automatically if record has one.') },
{ name: 'organization_shared', display: __('Shared'), tag: 'boolean', type: 'boolean', null: false, note: __('New organizations are shared.') },
]
@html App.view('integration/clearbit')(
@ -152,9 +151,9 @@ class State
App.Config.set(
'IntegrationClearbit'
{
name: 'Clearbit'
name: __('Clearbit')
target: '#system/integration/clearbit'
description: 'A powerful service to get more information about your customers.'
description: __('A powerful service to get more information about your customers.')
controller: Clearbit
state: State
permission: ['admin.integration.clearbit']

View file

@ -1,10 +1,10 @@
class Cti extends App.ControllerIntegrationBase
featureIntegration: 'cti_integration'
featureName: 'CTI (generic)'
featureName: __('CTI (generic)')
featureConfig: 'cti_config'
description: [
['This service shows you contacts of incoming calls and a caller list in realtime.']
['Also caller id of outbound calls can be changed.']
[__('This service shows you contacts of incoming calls and a caller list in realtime.')]
[__('Also caller id of outbound calls can be changed.')]
]
events:
'click .js-select': 'selectAll'
@ -259,9 +259,9 @@ class State
App.Config.set(
'IntegrationCti'
{
name: 'CTI (generic)'
name: __('CTI (generic)')
target: '#system/integration/cti'
description: 'Generic API to integrate VoIP service provider with realtime push.'
description: __('Generic API to integrate VoIP service provider with realtime push.')
controller: Cti
state: State
}

View file

@ -1,9 +1,9 @@
class Exchange extends App.ControllerIntegrationBase
featureIntegration: 'exchange_integration'
featureName: 'Exchange'
featureName: __('Exchange')
featureConfig: 'exchange_config'
description: [
['This service enables Zammad to connect with your Exchange server.']
[__('This service enables Zammad to connect with your Exchange server.')]
]
events:
'change .js-switch input': 'switch'
@ -282,7 +282,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw)
@showSlide('js-discover')
@showAlert('js-discover', details.error || 'Unable to perform backend.')
@showAlert('js-discover', details.error || __('Unable to perform backend.'))
)
folders: (e) =>
@ -323,7 +323,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw)
@showSlide('js-bind')
@showAlert('js-bind', details.error || 'Unable to perform backend.')
@showAlert('js-bind', details.error || __('Unable to perform backend.'))
)
foldersShow: (alreadyShown) =>
@ -429,7 +429,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw)
@showSlide('js-folders')
@showAlert('js-folders', details.error || 'Unable to perform backend.')
@showAlert('js-folders', details.error || __('Unable to perform backend.'))
)
mappingShow: (alreadyShown) =>
@ -554,9 +554,9 @@ class ConnectionWizard extends App.ControllerWizardModal
App.Config.set(
'IntegrationExchange'
{
name: 'Exchange'
name: __('Exchange')
target: '#system/integration/exchange'
description: 'Exchange integration for contacts management.'
description: __('Exchange integration for contacts management.')
controller: Exchange
state: State
permission: ['admin.integration.exchange']

View file

@ -1,9 +1,9 @@
class GitHub extends App.ControllerIntegrationBase
featureIntegration: 'github_integration'
featureName: 'GitHub'
featureName: __('GitHub')
featureConfig: 'github_config'
description: [
['This service allows you to connect %s with %s.', 'GitHub', 'Zammad']
[__('This service allows you to connect %s with %s.'), 'GitHub', 'Zammad']
]
events:
'change .js-switch input': 'switch'
@ -60,7 +60,7 @@ class Form extends App.Controller
details = data.responseJSON || {}
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
}
)
@ -71,9 +71,9 @@ class State
App.Config.set(
'IntegrationGitHub'
{
name: 'GitHub'
name: __('GitHub')
target: '#system/integration/github'
description: 'Link GitHub issues to your tickets.'
description: __('Link GitHub issues to your tickets.')
controller: GitHub
state: State
}

View file

@ -1,9 +1,9 @@
class GitLab extends App.ControllerIntegrationBase
featureIntegration: 'gitlab_integration'
featureName: 'GitLab'
featureName: __('GitLab')
featureConfig: 'gitlab_config'
description: [
['This service allows you to connect %s with %s.', 'GitLab', 'Zammad']
[__('This service allows you to connect %s with %s.'), 'GitLab', 'Zammad']
]
events:
'change .js-switch input': 'switch'
@ -60,7 +60,7 @@ class Form extends App.Controller
details = data.responseJSON || {}
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
}
)
@ -71,9 +71,9 @@ class State
App.Config.set(
'IntegrationGitLab'
{
name: 'GitLab'
name: __('GitLab')
target: '#system/integration/gitlab'
description: 'Link GitLab issues to your tickets.'
description: __('Link GitLab issues to your tickets.')
controller: GitLab
state: State
}

View file

@ -1,10 +1,10 @@
class Icinga extends App.ControllerIntegrationBase
featureIntegration: 'icinga_integration'
featureName: 'Icinga'
featureName: __('Icinga')
featureConfig: 'icinga_config'
description: [
['This service receives emails from %s and creates tickets with host and service.', 'Icinga']
['If the host and service is recovered again, the ticket will be closed automatically.']
[__('This service receives emails from %s and creates tickets with host and service.'), 'Icinga']
[__('If the host and service is recovered again, the ticket will be closed automatically.')]
]
render: =>
@ -21,9 +21,9 @@ class State
App.Config.set(
'IntegrationIcinga'
{
name: 'Icinga'
name: __('Icinga')
target: '#system/integration/icinga'
description: 'An open source monitoring tool.'
description: __('An open source monitoring tool.')
controller: Icinga
state: State
permission: ['admin.integration.icinga']

View file

@ -3,7 +3,7 @@ class Idoit extends App.ControllerIntegrationBase
featureName: 'i-doit'
featureConfig: 'idoit_config'
description: [
['This service allows you to connect %s with %s.', 'i-doit', 'Zammad']
[__('This service allows you to connect %s with %s.'), 'i-doit', 'Zammad']
]
events:
'change .js-switch input': 'switch'
@ -73,7 +73,7 @@ class Form extends App.Controller
details = data.responseJSON || {}
@notify(
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
)
)
@ -86,7 +86,7 @@ App.Config.set(
{
name: 'i-doit'
target: '#system/integration/idoit'
description: 'CMDB to document complex relations of your network components.'
description: __('CMDB to document complex relations of your network components.')
controller: Idoit
state: State
}

View file

@ -1,9 +1,9 @@
class Ldap extends App.ControllerIntegrationBase
featureIntegration: 'ldap_integration'
featureName: 'LDAP'
featureName: __('LDAP')
featureConfig: 'ldap_config'
description: [
['This service enables Zammad to connect with your LDAP server.']
[__('This service enables Zammad to connect with your LDAP server.')]
]
events:
'change .js-switch input': 'switch'
@ -315,7 +315,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw)
@showSlide('js-discover')
@showAlert('js-discover', details.error || 'Unable to perform backend.')
@showAlert('js-discover', details.error || __('Unable to perform backend.'))
)
@ -345,12 +345,12 @@ class ConnectionWizard extends App.ControllerWizardModal
if _.isEmpty(data.user_attributes)
@showSlide('js-bind')
@showAlert('js-bind', 'Unable to retrive user information, please check your bind user permissions.')
@showAlert('js-bind', __('Unable to retrive user information, please check your bind user permissions.'))
return
if _.isEmpty(data.groups)
@showSlide('js-bind')
@showAlert('js-bind', 'Unable to retrive group information, please check your bind user permissions.')
@showAlert('js-bind', __('Unable to retrive group information, please check your bind user permissions.'))
return
# update config if successful
@ -384,7 +384,7 @@ class ConnectionWizard extends App.ControllerWizardModal
if !_.isEmpty(detailsRaw)
details = JSON.parse(detailsRaw)
@showSlide('js-bind')
@showAlert('js-bind', details.error || 'Unable to perform backend.')
@showAlert('js-bind', details.error || __('Unable to perform backend.'))
)
mappingShow: (alreadyShown) =>
@ -572,9 +572,9 @@ class ConnectionWizard extends App.ControllerWizardModal
App.Config.set(
'IntegrationLDAP'
{
name: 'LDAP'
name: __('LDAP')
target: '#system/integration/ldap'
description: 'LDAP integration for user management.'
description: __('LDAP integration for user management.')
controller: Ldap
state: State
permission: ['admin.integration.ldap']

View file

@ -1,10 +1,10 @@
class Monit extends App.ControllerIntegrationBase
featureIntegration: 'monit_integration'
featureName: 'Monit'
featureName: __('Monit')
featureConfig: 'monit_config'
description: [
['This service receives emails from %s and creates tickets with host and service.', 'Monit']
['If the host and service is recovered again, the ticket will be closed automatically.']
[__('This service receives emails from %s and creates tickets with host and service.'), 'Monit']
[__('If the host and service is recovered again, the ticket will be closed automatically.')]
]
render: =>
@ -21,9 +21,9 @@ class State
App.Config.set(
'IntegrationMonit'
{
name: 'Monit'
name: __('Monit')
target: '#system/integration/monit'
description: 'An open source monitoring tool.'
description: __('An open source monitoring tool.')
controller: Monit
state: State
permission: ['admin.integration.monit']

View file

@ -1,10 +1,10 @@
class Nagios extends App.ControllerIntegrationBase
featureIntegration: 'nagios_integration'
featureName: 'Nagios'
featureName: __('Nagios')
featureConfig: 'nagios_config'
description: [
['This service receives emails from %s and creates tickets with host and service.', 'Nagios']
['If the host and service is recovered again, the ticket will be closed automatically.']
[__('This service receives emails from %s and creates tickets with host and service.'), 'Nagios']
[__('If the host and service is recovered again, the ticket will be closed automatically.')]
]
render: =>
@ -21,9 +21,9 @@ class State
App.Config.set(
'IntegrationNagios'
{
name: 'Nagios'
name: __('Nagios')
target: '#system/integration/nagios'
description: 'An open source monitoring tool.'
description: __('An open source monitoring tool.')
controller: Nagios
state: State
}

View file

@ -1,10 +1,10 @@
class Placetel extends App.ControllerIntegrationBase
featureIntegration: 'placetel_integration'
featureName: 'Placetel'
featureName: __('Placetel')
featureConfig: 'placetel_config'
description: [
['This service shows you contacts of incoming calls and a caller list in realtime.']
['Also caller id of outbound calls can be changed.']
[__('This service shows you contacts of incoming calls and a caller list in realtime.')]
[__('Also caller id of outbound calls can be changed.')]
]
events:
'click .js-select': 'selectAll'
@ -185,9 +185,9 @@ class State
App.Config.set(
'IntegrationPlacetel'
{
name: 'Placetel'
name: __('Placetel')
target: '#system/integration/placetel'
description: 'VoIP service provider with realtime push.'
description: __('VoIP service provider with realtime push.')
controller: Placetel
state: State
}

View file

@ -3,8 +3,8 @@ class SipgateIo extends App.ControllerIntegrationBase
featureName: 'sipgate.io'
featureConfig: 'sipgate_config'
description: [
['This service shows you contacts of incoming calls and a caller list in realtime.']
['Also caller id of outbound calls can be changed.']
[__('This service shows you contacts of incoming calls and a caller list in realtime.')]
[__('Also caller id of outbound calls can be changed.')]
]
events:
'click .js-select': 'selectAll'
@ -187,7 +187,7 @@ App.Config.set(
{
name: 'sipgate.io'
target: '#system/integration/sipgate'
description: 'VoIP service provider with realtime push.'
description: __('VoIP service provider with realtime push.')
controller: SipgateIo
state: State
}

View file

@ -1,10 +1,10 @@
class Slack extends App.ControllerIntegrationBase
featureIntegration: 'slack_integration'
featureName: 'Slack'
featureName: __('Slack')
featureConfig: 'slack_config'
description: [
['This service sends notifications to your %s channel.', 'Slack']
['To set up this service you need to create a new |"Incoming webhook"| in your %s integration panel, and enter the web hook URL below.', 'Slack']
[__('This service sends notifications to your %s channel.'), 'Slack']
[__('To set up this service you need to create a new |"Incoming webhook"| in your %s integration panel, and enter the web hook URL below.'), 'Slack']
]
events:
'click .js-submit': 'update'
@ -26,12 +26,12 @@ class Slack extends App.ControllerIntegrationBase
escalation_warning: '5. Ticket Escalation Warning'
configureAttributes = [
{ name: 'types', display: 'Trigger', tag: 'checkbox', options: options, 'null': false, class: 'vertical', note: 'When notification is being sent.' },
{ name: 'group_ids', display: 'Group', tag: 'select', relation: 'Group', multiple: true, 'null': false, note: 'Only for these groups.' },
{ name: 'webhook', display: 'Webhook', tag: 'input', type: 'url', limit: 200, 'null': false, placeholder: 'https://hooks.slack.com/services/...' },
{ name: 'username', display: 'Username', tag: 'input', type: 'text', limit: 100, 'null': false, placeholder: 'username' },
{ name: 'channel', display: 'Channel', tag: 'input', type: 'text', limit: 100, 'null': true, placeholder: '#channel' },
{ name: 'icon_url', display: 'Icon Url', tag: 'input', type: 'url', limit: 200, 'null': true, placeholder: 'https://example.com/logo.png' },
{ name: 'types', display: __('Trigger'), tag: 'checkbox', options: options, 'null': false, class: 'vertical', note: __('When notification is being sent.') },
{ name: 'group_ids', display: __('Group'), tag: 'select', relation: 'Group', multiple: true, 'null': false, note: __('Only for these groups.') },
{ name: 'webhook', display: __('Webhook'), tag: 'input', type: 'url', limit: 200, 'null': false, placeholder: 'https://hooks.slack.com/services/...' },
{ name: 'username', display: __('Username'), tag: 'input', type: 'text', limit: 100, 'null': false, placeholder: 'username' },
{ name: 'channel', display: __('Channel'), tag: 'input', type: 'text', limit: 100, 'null': true, placeholder: '#channel' },
{ name: 'icon_url', display: __('Icon Url'), tag: 'input', type: 'url', limit: 200, 'null': true, placeholder: 'https://example.com/logo.png' },
]
settings = []
@ -81,10 +81,10 @@ class State
App.Config.set(
'IntegrationSlack'
{
name: 'Slack'
name: __('Slack')
target: '#system/integration/slack'
description: 'A team communication tool for the 21st century. Compatible with tools like %s.'
descriptionSubstitute: 'Mattermost, RocketChat'
description: __('A team communication tool for the 21st century. Compatible with tools like %s.')
descriptionSubstitute: __('Mattermost, RocketChat')
controller: Slack
state: State
}

View file

@ -1,9 +1,9 @@
class Index extends App.ControllerIntegrationBase
featureIntegration: 'smime_integration'
featureName: 'S/MIME'
featureName: __('S/MIME')
featureConfig: 'smime_config'
description: [
['S/MIME (Secure/Multipurpose Internet Mail Extensions) is a widely accepted method (or more precisely, a protocol) for sending digitally signed and encrypted messages.']
[__('S/MIME (Secure/Multipurpose Internet Mail Extensions) is a widely accepted method (or more precisely, a protocol) for sending digitally signed and encrypted messages.')]
]
events:
'change .js-switch input': 'switch'
@ -70,16 +70,16 @@ class Form extends App.Controller
class Certificate extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: 'Add'
buttonSubmit: __('Add')
autoFocusOnFirstInput: false
head: 'Add Certificate'
head: __('Add Certificate')
large: true
content: ->
# show start dialog
content = $(App.view('integration/smime_certificate_add')(
head: 'Add Certificate'
head: __('Add Certificate')
))
content
@ -106,23 +106,23 @@ class Certificate extends App.ControllerModal
details = data.responseJSON || {}
@notify
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to import!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to import!'))
timeout: 6000
)
class PrivateKey extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: 'Add'
buttonSubmit: __('Add')
autoFocusOnFirstInput: false
head: 'Add Private Key'
head: __('Add Private Key')
large: true
content: ->
# show start dialog
content = $(App.view('integration/smime_private_key_add')(
head: 'Add Private Key'
head: __('Add Private Key')
))
content
@ -149,7 +149,7 @@ class PrivateKey extends App.ControllerModal
details = data.responseJSON || {}
@notify
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to import!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to import!'))
timeout: 6000
)
@ -178,7 +178,7 @@ class List extends App.Controller
details = data.responseJSON || {}
@notify(
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to load list of certificates!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to load list of certificates!'))
)
# do something
@ -210,7 +210,7 @@ class List extends App.Controller
details = data.responseJSON || {}
@notify(
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to save!'))
)
)
@ -246,9 +246,9 @@ class State
App.Config.set(
'Integrationsmime'
{
name: 'S/MIME'
name: __('S/MIME')
target: '#system/integration/smime'
description: 'S/MIME enables you to send digitally signed and encrypted messages.'
description: __('S/MIME enables you to send digitally signed and encrypted messages.')
controller: Index
state: State
}

View file

@ -1,13 +1,13 @@
class Branding extends App.ControllerTabs
requiredPermission: 'admin.branding'
header: 'Branding'
header: __('Branding')
constructor: ->
super
@title 'Branding', true
@title __('Branding'), true
@tabs = [
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Branding' } }
{ name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Branding' } }
]
@render()
App.Config.set('SettingBranding', { prio: 1200, parent: '#settings', name: 'Branding', target: '#settings/branding', controller: Branding, permission: ['admin.branding'] }, 'NavBarAdmin')
App.Config.set('SettingBranding', { prio: 1200, parent: '#settings', name: __('Branding'), target: '#settings/branding', controller: Branding, permission: ['admin.branding'] }, 'NavBarAdmin')

View file

@ -1,5 +1,5 @@
class App.ManageKnowledgeBase extends App.ControllerTabs
header: 'Knowledge Base'
header: __('Knowledge Base')
headerSwitchName: 'kb-activate'
events:
@ -59,7 +59,7 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
@renderNonExistant()
renderNonExistant: ->
@renderScreenError(detail: 'No Knowledge Base. Please create first Knowledge Base', el: @$('.page-content'))
@renderScreenError(detail: __('No Knowledge Base. Please create first Knowledge Base'), el: @$('.page-content'))
@headerSwitchInput.prop('checked', false)
@modal = new App.KnowledgeBaseNewModal(
@ -96,22 +96,22 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
@tabs = [
{
name: 'Theme'
name: __('Theme')
target: 'style'
controller: App.KnowledgeBaseForm
params: _.extend({}, params, { screen: 'style', split: true })
},{
name: 'Languages'
name: __('Languages')
target: 'languages'
controller: App.KnowledgeBaseForm
params: _.extend({}, params, { screen: 'languages' })
},{
name: 'Public Menu'
name: __('Public Menu')
target: 'public_menu'
controller: App.KnowledgeBasePublicMenuManager
params: _.extend({}, params, { screen: 'public_menu' })
},{
name: 'Delete'
name: __('Delete')
target: 'delete'
controller: App.KnowledgeBaseDelete
params: params
@ -120,7 +120,7 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
if !App.Config.get('system_online_service')
@tabs.splice(-1, 0, {
name: 'Custom URL'
name: __('Custom URL')
target: 'custom_address'
controller: App.KnowledgeBaseCustomAddressForm,
params: _.extend({}, params, { screen: 'custom_address' })
@ -130,4 +130,4 @@ class App.ManageKnowledgeBase extends App.ControllerTabs
@headerSwitchInput.prop('checked', App.KnowledgeBase.find(@knowledge_base_id).active)
App.Config.set('KnowledgeBase', { prio: 10000, name: 'Knowledge Base', parent: '#manage', target: '#manage/knowledge_base', controller: App.ManageKnowledgeBase, permission: ['admin.knowledge_base'] }, 'NavBarAdmin')
App.Config.set('KnowledgeBase', { prio: 10000, name: __('Knowledge Base'), parent: '#manage', target: '#manage/knowledge_base', controller: App.ManageKnowledgeBase, permission: ['admin.knowledge_base'] }, 'NavBarAdmin')

View file

@ -1,17 +1,16 @@
class Security extends App.ControllerTabs
requiredPermission: 'admin.security'
header: 'Security'
header: __('Security')
constructor: ->
super
@title 'Security', true
@title __('Security'), true
@tabs = [
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Security::Base' } }
{ name: 'Password', 'target': 'password', controller: App.SettingsArea, params: { area: 'Security::Password' } }
#{ name: 'Authentication', 'target': 'auth', controller: App.SettingsArea, params: { area: 'Security::Authentication' } }
{ name: 'Third-party Applications', 'target': 'third_party_auth', controller: App.SettingsArea, params: { area: 'Security::ThirdPartyAuthentication' } }
{ name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'Security::Base' } }
{ name: __('Password'), 'target': 'password', controller: App.SettingsArea, params: { area: 'Security::Password' } }
#{ name: __('Authentication'), 'target': 'auth', controller: App.SettingsArea, params: { area: 'Security::Authentication' } }
{ name: __('Third-party Applications'), 'target': 'third_party_auth', controller: App.SettingsArea, params: { area: 'Security::ThirdPartyAuthentication' } }
]
@render()
App.Config.set('SettingSecurity', { prio: 1600, parent: '#settings', name: 'Security', target: '#settings/security', controller: Security, permission: ['admin.security'] }, 'NavBarAdmin')
App.Config.set('SettingSecurity', { prio: 1600, parent: '#settings', name: __('Security'), target: '#settings/security', controller: Security, permission: ['admin.security'] }, 'NavBarAdmin')

View file

@ -1,19 +1,19 @@
class System extends App.ControllerTabs
requiredPermission: 'admin.setting_system'
header: 'System'
header: __('System')
constructor: ->
super
@title 'System', true
@title __('System'), true
@tabs = []
if !App.Config.get('system_online_service')
@tabs.push { name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Base' } }
@tabs.push { name: 'Services', 'target': 'services', controller: App.SettingsArea, params: { area: 'System::Services' } }
@tabs.push { name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'System::Base' } }
@tabs.push { name: __('Services'), 'target': 'services', controller: App.SettingsArea, params: { area: 'System::Services' } }
if !App.Config.get('system_online_service')
@tabs.push { name: 'Storage', 'target': 'storage', controller: App.SettingsArea, params: { area: 'System::Storage' } }
@tabs.push { name: __('Storage'), 'target': 'storage', controller: App.SettingsArea, params: { area: 'System::Storage' } }
if !App.Config.get('system_online_service')
@tabs.push { name: 'Network', 'target': 'network', controller: App.SettingsArea, params: { area: 'System::Network' } }
@tabs.push { name: 'Frontend', 'target': 'ui', controller: App.SettingsArea, params: { area: 'System::UI' } }
@tabs.push { name: __('Network'), 'target': 'network', controller: App.SettingsArea, params: { area: 'System::Network' } }
@tabs.push { name: __('Frontend'), 'target': 'ui', controller: App.SettingsArea, params: { area: 'System::UI' } }
@render()
App.Config.set('SettingSystem', { prio: 1400, parent: '#settings', name: 'System', target: '#settings/system', controller: System, permission: ['admin.setting_system'] }, 'NavBarAdmin')
App.Config.set('SettingSystem', { prio: 1400, parent: '#settings', name: __('System'), target: '#settings/system', controller: System, permission: ['admin.setting_system'] }, 'NavBarAdmin')

View file

@ -1,15 +1,15 @@
class Ticket extends App.ControllerTabs
requiredPermission: 'admin.ticket'
header: 'Ticket'
header: __('Ticket')
constructor: ->
super
@title('Ticket', true)
@tabs = [
{ name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Ticket::Base' } }
{ name: 'Number', 'target': 'number', controller: App.SettingsArea, params: { area: 'Ticket::Number' } }
{ name: 'Auto Assignment', 'target': 'auto_assignment', controller: App.SettingTicketAutoAssignment }
{ name: __('Base'), 'target': 'base', controller: App.SettingsArea, params: { area: 'Ticket::Base' } }
{ name: __('Number'), 'target': 'number', controller: App.SettingsArea, params: { area: 'Ticket::Number' } }
{ name: __('Auto Assignment'), 'target': 'auto_assignment', controller: App.SettingTicketAutoAssignment }
]
@render()
App.Config.set('SettingTicket', { prio: 1700, parent: '#settings', name: 'Ticket', target: '#settings/ticket', controller: Ticket, permission: ['admin.ticket'] }, 'NavBarAdmin')
App.Config.set('SettingTicket', { prio: 1700, parent: '#settings', name: __('Ticket'), target: '#settings/ticket', controller: Ticket, permission: ['admin.ticket'] }, 'NavBarAdmin')

View file

@ -22,7 +22,7 @@ class App.SettingTicketAutoAssignment extends App.ControllerSubContent
@html(App.view('settings/ticket_auto_assignment')())
configure_attributes = [
{ name: 'condition', display: 'Conditions for effected objects', tag: 'ticket_selector', null: false, preview: false, action: false, hasChanged: false, article: false },
{ name: 'condition', display: __('Conditions for effected objects'), tag: 'ticket_selector', null: false, preview: false, action: false, hasChanged: false, article: false },
]
ticket_auto_assignment_selector = App.Setting.get('ticket_auto_assignment_selector')
@ -36,7 +36,7 @@ class App.SettingTicketAutoAssignment extends App.ControllerSubContent
)
configure_attributes = [
{ name: 'user_ids', display: 'Exception users', tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'firstname' },
{ name: 'user_ids', display: __('Exception users'), tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'firstname' },
]
ticket_auto_assignment_user_ids_ignore = App.Setting.get('ticket_auto_assignment_user_ids_ignore')

View file

@ -23,19 +23,19 @@ class ElectronEvents extends App.Controller
createDefault = ->
menu = new Menu()
menu.append(new MenuItem(
label: 'Cut',
label: __('Cut'),
role: 'cut'
))
menu.append(new MenuItem(
label: 'Copy',
label: __('Copy'),
role: 'copy'
))
menu.append(new MenuItem(
label: 'Paste',
label: __('Paste'),
role: 'paste'
))
menu.append(new MenuItem(
label: 'Select All',
label: __('Select All'),
role: 'selectall'
))
menu
@ -81,7 +81,7 @@ class ElectronEvents extends App.Controller
dataURL = badgeDataURL(content.toString())
sendBadge(dataURL, 'You have unread messages (' + content + ')')
else
sendBadge(null, 'You have no unread messages')
sendBadge(null, __('You have no unread messages'))
setBadgeOSX = (content) ->
remote.app.dock.setBadge(content)

View file

@ -1,7 +1,7 @@
class App.KeyboardShortcutModal extends App.ControllerModal
authenticateRequired: true
large: true
head: 'Keyboard Shortcuts'
head: __('Keyboard Shortcuts')
buttonClose: true
buttonCancel: false
buttonSubmit: false
@ -82,16 +82,16 @@ App.Config.set(
'keyboard_shortcuts',
[
{
headline: 'Navigation'
headline: __('Navigation')
location: 'left'
content: [
{
where: 'Used anywhere'
where: __('Used anywhere')
shortcuts: [
{
key: 'd'
hotkeys: true
description: 'Dashboard'
description: __('Dashboard')
globalEvent: 'dashboard'
callback: ->
$('#global-search').blur()
@ -101,7 +101,7 @@ App.Config.set(
{
key: 'o'
hotkeys: true
description: 'Overviews'
description: __('Overviews')
globalEvent: 'overview'
callback: ->
$('#global-search').blur()
@ -111,7 +111,7 @@ App.Config.set(
{
key: 's'
hotkeys: true
description: 'Search'
description: __('Search')
globalEvent: 'search'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -120,7 +120,7 @@ App.Config.set(
{
key: 'a'
hotkeys: true
description: 'Notifications'
description: __('Notifications')
globalEvent: 'notification'
callback: ->
$('#global-search').blur()
@ -130,7 +130,7 @@ App.Config.set(
{
key: 'n'
hotkeys: true
description: 'New Ticket'
description: __('New Ticket')
globalEvent: 'new-ticket'
callback: ->
$('#global-search').blur()
@ -140,7 +140,7 @@ App.Config.set(
{
key: 'e'
hotkeys: true
description: 'Logout'
description: __('Logout')
globalEvent: 'logout'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -149,7 +149,7 @@ App.Config.set(
{
key: 'h'
hotkeys: true
description: 'List of shortcuts'
description: __('List of shortcuts')
globalEvent: 'list-of-shortcuts'
callback: =>
if window.location.hash is '#keyboard_shortcuts'
@ -164,7 +164,7 @@ App.Config.set(
{
key: 'w'
hotkeys: true
description: 'Close current tab'
description: __('Close current tab')
globalEvent: 'close-current-tab'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -173,7 +173,7 @@ App.Config.set(
{
key: 'tab'
hotkeys: true
description: 'Next in tab'
description: __('Next in tab')
globalEvent: 'next-in-tab'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -196,7 +196,7 @@ App.Config.set(
{
key: 'shift+tab'
hotkeys: true
description: 'Previous tab'
description: __('Previous tab')
globalEvent: 'previous-in-tab'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -219,7 +219,7 @@ App.Config.set(
{
key: 'return'
hotkeys: true
description: 'Confirm/submit dialog'
description: __('Confirm/submit dialog')
globalEvent: 'submit'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -250,29 +250,29 @@ App.Config.set(
]
}
{
where: 'Used in lists (views and results)'
where: __('Used in lists (views and results)')
shortcuts: [
{
key: ['', '']
description: 'Move up and down'
description: __('Move up and down')
}
{
key: ['', '']
description: 'Move left and right'
description: __('Move left and right')
}
{
key: 'enter'
description: 'Select item',
description: __('Select item'),
}
]
}
{
where: 'Used in object views'
where: __('Used in object views')
shortcuts: [
{
key: '.'
hotkeys: true
description: 'Copy current object number (e. g. Ticket#) to clipboard'
description: __('Copy current object number (e. g. Ticket#) to clipboard')
callback: (shortcut, lastKey, modifier) ->
App.Event.trigger('keyboard_shortcuts_close')
text = $('.active.content .js-objectNumber').first().data('number') || ''
@ -298,30 +298,30 @@ App.Config.set(
keyPrefix: '2x'
key: '.'
hotkeys: true
description: '...add object title'
description: __('...add object title')
}
{
keyPrefix: '3x'
key: '.'
hotkeys: true
description: '...add object link URL'
description: __('...add object link URL')
}
]
}
]
}
{
headline: 'Translations'
headline: __('Translations')
location: 'left'
content: [
{
where: 'Used anywhere (admin only)'
where: __('Used anywhere (admin only)')
shortcuts: [
{
admin: true
key: 't'
hotkeys: true
description: 'Enable/disable inline translations'
description: __('Enable/disable inline translations')
globalEvent: 'translation-mode'
}
]
@ -329,16 +329,16 @@ App.Config.set(
]
}
{
headline: 'Tickets'
headline: __('Tickets')
location: 'right'
content: [
{
where: 'Used when viewing a Ticket'
where: __('Used when viewing a Ticket')
shortcuts: [
{
key: 'm'
hotkeys: true
description: 'Open note box'
description: __('Open note box')
globalEvent: 'article-note-open'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -348,7 +348,7 @@ App.Config.set(
{
key: 'g'
hotkeys: true
description: 'Reply to last article'
description: __('Reply to last article')
globalEvent: 'article-reply'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -362,7 +362,7 @@ App.Config.set(
{
key: 'j'
hotkeys: true
description: 'Set article to internal/public'
description: __('Set article to internal/public')
globalEvent: 'article-internal-public'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -371,7 +371,7 @@ App.Config.set(
#{
# key: 'm'
# hotkeys: true
# description: 'Open macro selection'
# description: __('Open macro selection')
# globalEvent: 'macro-open'
# callback: ->
# window.location.hash = '#ticket/create'
@ -379,7 +379,7 @@ App.Config.set(
{
key: 'c'
hotkeys: true
description: 'Update as closed'
description: __('Update as closed')
globalEvent: 'task-update-close'
callback: ->
App.Event.trigger('keyboard_shortcuts_close')
@ -390,29 +390,29 @@ App.Config.set(
{
key: ['', '']
hotkeys: true
description: 'Navigate through article'
description: __('Navigate through article')
}
]
},
{
where: 'Used when composing a Ticket article'
where: __('Used when composing a Ticket article')
shortcuts: [
{
key: '::'
hotkeys: false
description: 'Inserts Text module'
description: __('Inserts Text module')
globalEvent: 'richtext-insert-text-module'
}
{
key: '??'
hotkeys: false
description: 'Inserts Knowledge Base answer'
description: __('Inserts Knowledge Base answer')
globalEvent: 'richtext-insert-kb-answer'
}
{
key: '@@'
hotkeys: false
description: 'Inserts a mention for a user'
description: __('Inserts a mention for a user')
globalEvent: 'richtext-insert-mention-user'
}
]
@ -421,101 +421,101 @@ App.Config.set(
]
}
{
headline: 'Text editing'
headline: __('Text editing')
location: 'right'
content: [
{
where: 'Used when composing a text'
where: __('Used when composing a text')
shortcuts: [
{
key: 'u'
magicKey: true
description: 'Format as _underlined_'
description: __('Format as _underlined_')
globalEvent: 'richtext-underline'
}
{
key: 'b'
magicKey: true
description: 'Format as |bold|'
description: __('Format as |bold|')
globalEvent: 'richtext-bold'
}
{
key: 'i'
magicKey: true
description: 'Format as ||italic||'
description: __('Format as ||italic||')
globalEvent: 'richtext-italic'
}
{
key: 's'
magicKey: true
description: 'Format as //strikethrough//'
description: __('Format as //strikethrough//')
globalEvent: 'richtext-strikethrough'
}
{
key: 'v'
magicKey: true
description: 'Paste from clipboard'
description: __('Paste from clipboard')
globalEvent: 'clipboard-paste'
}
{
key: 'v'
magicKey: true
shiftKey: true
description: 'Paste from clipboard (plain text)'
description: __('Paste from clipboard (plain text)')
globalEvent: 'clipboard-paste-plain-text'
}
{
key: 'f'
hotkeys: true
description: 'Removes the formatting'
description: __('Removes the formatting')
globalEvent: 'richtext-remove-formating'
}
{
key: 'y'
hotkeys: true
description: '...of whole textarea'
description: __('...of whole textarea')
globalEvent: 'richtext-remove-formating-textarea'
}
{
key: 'z'
hotkeys: true,
description: 'Inserts a horizontal rule'
description: __('Inserts a horizontal rule')
globalEvent: 'richtext-hr'
}
{
key: 'l'
hotkeys: true,
description: 'Format as unordered list'
description: __('Format as unordered list')
globalEvent: 'richtext-ul'
}
{
key: 'k'
hotkeys: true,
description: 'Format as ordered list'
description: __('Format as ordered list')
globalEvent: 'richtext-ol'
}
{
key: '1'
hotkeys: true,
description: 'Format as h1 heading'
description: __('Format as h1 heading')
globalEvent: 'richtext-h1'
}
{
key: '2'
hotkeys: true,
description: 'Format as h2 heading'
description: __('Format as h2 heading')
globalEvent: 'richtext-h2'
}
{
key: '3'
hotkeys: true,
description: 'Format as h3 heading'
description: __('Format as h3 heading')
globalEvent: 'richtext-h3'
}
{
key: 'x'
hotkeys: true,
description: 'Removes any hyperlink'
description: __('Removes any hyperlink')
globalEvent: 'richtext-remove-hyperlink'
}
]

View file

@ -22,9 +22,9 @@ class Maintenance extends App.Controller
showMessage: (message = {}) =>
if message.reload
@disconnectClient()
button = 'Continue session'
button = __('Continue session')
else
button = 'Close'
button = __('Close')
new App.SessionMessage(
head: message.head
@ -62,7 +62,7 @@ class Maintenance extends App.Controller
maintanaceRestartManual: (data) =>
return if @messageRestartManual
@messageRestartManual = new App.SessionMessage(
head: App.i18n.translateInline('Zammad need a restart!')
head: App.i18n.translateInline('Zammad requires a restart!')
contentInline: App.i18n.translateInline('Some system settings have changed, please restart all Zammad processes! If you want to do this automatically, set environment variable APP_RESTART_CMD="/path/to/your_app_script.sh restart".')
keyboard: false
backdrop: false
@ -82,7 +82,7 @@ class Maintenance extends App.Controller
keyboard: false
backdrop: true
buttonClose: false
buttonSubmit: 'Continue session'
buttonSubmit: __('Continue session')
forceReload: true
)
@ -102,7 +102,7 @@ class Maintenance extends App.Controller
keyboard: false
backdrop: true
buttonClose: false
buttonSubmit: 'Continue session'
buttonSubmit: __('Continue session')
forceReload: true
)
@delay(message, 2000)

View file

@ -483,7 +483,7 @@ class Navigation extends App.Controller
navheader = false
if prio is 80
divider = true
navheader = 'Recently viewed'
navheader = __('Recently viewed')
prio++
NavBarRight['RecendViewed::' + item.o_id + item.object + '-' + prio ] = {

View file

@ -35,12 +35,12 @@ class SessionTakeOver extends App.Controller
# only if new client id isn't own client id
if data.taskbar_id isnt App.TaskManager.TaskbarId()
@error = new App.SessionMessage(
head: 'Session'
message: 'A new session was created with your account. This session will be stopped to prevent a conflict.'
head: __('Session')
message: __('A new session was created with your account. This session will be stopped to prevent a conflict.')
keyboard: false
backdrop: true
buttonClose: false
buttonSubmit: 'Continue session'
buttonSubmit: __('Continue session')
forceReload: true
)
@disconnectClient()

View file

@ -95,12 +95,12 @@ class SessionTimeout extends App.Controller
return if @warningDialog
@warningDialog = new App.ControllerModal(
head: 'Session'
message: 'Due to inactivity are automatically logged out within the next 30 seconds.'
head: __('Session')
message: __('Due to inactivity are automatically logged out within the next 30 seconds.')
keyboard: true
backdrop: true
buttonClose: true
buttonSubmit: 'Continue session'
buttonSubmit: __('Continue session')
onSubmit: =>
@lastEvent = @currentTime()
@checkLogout()

View file

@ -7,8 +7,8 @@ class TranslationSupport extends App.Controller
# only show if system is already up and running
return if !@Config.get('system_init_done')
# to not translate en
return if !App.i18n.notTranslatedFeatureEnabled(App.i18n.get())
# do not show for English locales
return if App.i18n.get().substr(0,2) is 'en'
# only show for admins
return if !@permissionCheck('admin.translation')
@ -30,13 +30,13 @@ class TranslationSupport extends App.Controller
if App.Session.get() isnt undefined
@delay(check, 2500, 'translation_support')
App.Config.set( 'translaton_support', TranslationSupport, 'Plugins' )
App.Config.set( 'translation_support', TranslationSupport, 'Plugins' )
class Modal extends App.ControllerModal
buttonClose: true
buttonCancel: 'No Thanks!'
buttonSubmit: 'Complete translations'
head: 'Help to improve Zammad!'
buttonCancel: __('No Thanks!')
buttonSubmit: __('Complete translations')
head: __('Help to improve Zammad!')
shown: false
constructor: ->
@ -45,12 +45,8 @@ class Modal extends App.ControllerModal
@render()
content: =>
better = false
if @percent > 80
better = true
App.view('translation/support')(
percent: @percent
better: better
)
onCancel: =>

View file

@ -31,11 +31,11 @@ class UserSignupCheck extends App.Controller
class Modal extends App.ControllerModal
backdrop: false
keyboard: false
head: 'Account not verified'
head: __('Account not verified')
small: true
buttonClose: false
buttonCancel: false
buttonSubmit: 'Resend verification email'
buttonSubmit: __('Resend verification email')
constructor: ->
super

View file

@ -1,6 +1,6 @@
class ProfileAvatar extends App.ControllerSubContent
requiredPermission: 'user_preferences.avatar'
header: 'Avatar'
header: __('Avatar')
elements:
'.js-upload': 'fileInput'
'.avatar-gallery': 'avatarGallery'
@ -145,13 +145,13 @@ class ProfileAvatar extends App.ControllerSubContent
reader.readAsDataURL(@)
App.Config.set('Avatar', { prio: 1100, name: 'Avatar', parent: '#profile', target: '#profile/avatar', controller: ProfileAvatar, permission: ['user_preferences.avatar'] }, 'NavBarProfile')
App.Config.set('Avatar', { prio: 1100, name: __('Avatar'), parent: '#profile', target: '#profile/avatar', controller: ProfileAvatar, permission: ['user_preferences.avatar'] }, 'NavBarProfile')
class ImageCropper extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: 'Save'
head: 'Crop Image'
buttonSubmit: __('Save')
head: __('Crop Image')
elements:
'.imageCropper-image': 'image'
@ -229,13 +229,13 @@ class ImageCropper extends App.ControllerModal
class Camera extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: 'Save'
buttonSubmit: __('Save')
buttonClass: 'btn--success is-disabled'
centerButtons: [{
className: 'btn--success js-shoot is-disabled',
text: 'Shoot'
text: __('Shoot')
}]
head: 'Camera'
head: __('Camera')
elements:
'.js-shoot': 'shootButton'

View file

@ -1,6 +1,6 @@
class ProfileCalendarSubscriptions extends App.ControllerSubContent
requiredPermission: 'user_preferences.calendar+ticket.agent'
header: 'Calendar'
header: __('Calendar')
elements:
'input[type=checkbox]': 'options'
'output': 'output'
@ -90,4 +90,4 @@ class ProfileCalendarSubscriptions extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message)
)
App.Config.set('CalendarSubscriptions', { prio: 3000, name: 'Calendar', parent: '#profile', target: '#profile/calendar_subscriptions', permission: ['user_preferences.calendar+ticket.agent'], controller: ProfileCalendarSubscriptions }, 'NavBarProfile')
App.Config.set('CalendarSubscriptions', { prio: 3000, name: __('Calendar'), parent: '#profile', target: '#profile/calendar_subscriptions', permission: ['user_preferences.calendar+ticket.agent'], controller: ProfileCalendarSubscriptions }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileDevices extends App.ControllerSubContent
requiredPermission: 'user_preferences.device'
header: 'Devices'
header: __('Devices')
events:
'click [data-type=delete]': 'delete'
@ -55,4 +55,4 @@ class ProfileDevices extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message)
)
App.Config.set('Devices', { prio: 3100, name: 'Devices', parent: '#profile', target: '#profile/devices', controller: ProfileDevices, permission: ['user_preferences.device'] }, 'NavBarProfile')
App.Config.set('Devices', { prio: 3100, name: __('Devices'), parent: '#profile', target: '#profile/devices', controller: ProfileDevices, permission: ['user_preferences.device'] }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileLanguage extends App.ControllerSubContent
requiredPermission: 'user_preferences.language'
header: 'Language'
header: __('Language')
events:
'submit form': 'update'
@ -69,4 +69,4 @@ class ProfileLanguage extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message)
)
App.Config.set('Language', { prio: 1000, name: 'Language', parent: '#profile', target: '#profile/language', controller: ProfileLanguage, permission: ['user_preferences.language'] }, 'NavBarProfile')
App.Config.set('Language', { prio: 1000, name: __('Language'), parent: '#profile', target: '#profile/language', controller: ProfileLanguage, permission: ['user_preferences.language'] }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileLinkedAccounts extends App.ControllerSubContent
requiredPermission: 'user_preferences.linked_accounts'
header: 'Linked Accounts'
header: __('Linked Accounts')
events:
'click .js-add': 'add'
'click .js-remove': 'remove'
@ -59,56 +59,56 @@ class ProfileLinkedAccounts extends App.ControllerSubContent
msg: App.i18n.translateContent(data.message)
)
App.Config.set('LinkedAccounts', { prio: 4000, name: 'Linked Accounts', parent: '#profile', target: '#profile/linked', controller: ProfileLinkedAccounts, permission: ['user_preferences.linked_accounts'] }, 'NavBarProfile')
App.Config.set('LinkedAccounts', { prio: 4000, name: __('Linked Accounts'), parent: '#profile', target: '#profile/linked', controller: ProfileLinkedAccounts, permission: ['user_preferences.linked_accounts'] }, 'NavBarProfile')
App.Config.set('auth_provider_all', {
facebook:
url: '/auth/facebook'
name: 'Facebook'
name: __('Facebook')
config: 'auth_facebook'
class: 'facebook'
twitter:
url: '/auth/twitter'
name: 'Twitter'
name: __('Twitter')
config: 'auth_twitter'
class: 'twitter'
linkedin:
url: '/auth/linkedin'
name: 'LinkedIn'
name: __('LinkedIn')
config: 'auth_linkedin'
class: 'linkedin'
github:
url: '/auth/github'
name: 'GitHub'
name: __('GitHub')
config: 'auth_github'
class: 'github'
gitlab:
url: '/auth/gitlab'
name: 'GitLab'
name: __('GitLab')
config: 'auth_gitlab'
class: 'gitlab'
microsoft_office365:
url: '/auth/microsoft_office365'
name: 'Office 365'
name: __('Office 365')
config: 'auth_microsoft_office365'
class: 'office365'
google_oauth2:
url: '/auth/google_oauth2'
name: 'Google'
name: __('Google')
config: 'auth_google_oauth2'
class: 'google'
weibo:
url: '/auth/weibo'
name: 'Weibo'
name: __('Weibo')
config: 'auth_weibo'
class: 'weibo'
saml:
url: '/auth/saml'
name: 'SAML'
name: __('SAML')
config: 'auth_saml'
class: 'saml'
sso:
url: '/auth/sso'
name: 'SSO'
name: __('SSO')
config: 'auth_sso'
class: 'sso'
})

View file

@ -1,45 +1,45 @@
class ProfileNotification extends App.ControllerSubContent
requiredPermission: 'user_preferences.notifications+ticket.agent'
header: 'Notifications'
header: __('Notifications')
events:
'submit form': 'update'
'change .js-notificationSound': 'previewSound'
sounds: [
{
name: 'Bell'
name: __('Bell')
file: 'Bell.mp3'
},
{
name: 'Kalimba'
name: __('Kalimba')
file: 'Kalimba.mp3'
},
{
name: 'Marimba'
name: __('Marimba')
file: 'Marimba.mp3'
},
{
name: 'Peep'
name: __('Peep')
file: 'Peep.mp3'
},
{
name: 'Plop'
name: __('Plop')
file: 'Plop.mp3'
},
{
name: 'Ring'
name: __('Ring')
file: 'Ring.mp3'
},
{
name: 'Space'
name: __('Space')
file: 'Space.mp3'
},
{
name: 'Wood'
name: __('Wood')
file: 'Wood.mp3'
},
{
name: 'Xylo'
name: __('Xylo')
file: 'Xylo.mp3'
}
]
@ -52,13 +52,13 @@ class ProfileNotification extends App.ControllerSubContent
matrix =
create:
name: 'New Ticket'
name: __('New Ticket')
update:
name: 'Ticket update'
name: __('Ticket update')
reminder_reached:
name: 'Ticket reminder reached'
name: __('Ticket reminder reached')
escalation:
name: 'Ticket escalation'
name: __('Ticket escalation')
config =
group_ids: []
@ -196,4 +196,4 @@ class ProfileNotification extends App.ControllerSubContent
return if !params.notification_sound.file
App.OnlineNotification.play(params.notification_sound.file)
App.Config.set('Notifications', { prio: 2600, name: 'Notifications', parent: '#profile', target: '#profile/notifications', permission: ['user_preferences.notifications+ticket.agent'], controller: ProfileNotification }, 'NavBarProfile')
App.Config.set('Notifications', { prio: 2600, name: __('Notifications'), parent: '#profile', target: '#profile/notifications', permission: ['user_preferences.notifications+ticket.agent'], controller: ProfileNotification }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileOutOfOffice extends App.ControllerSubContent
requiredPermission: 'user_preferences.out_of_office+ticket.agent'
header: 'Out of Office'
header: __('Out of Office')
events:
'submit form': 'submit'
'click .js-disabled': 'disable'
@ -70,7 +70,7 @@ class ProfileOutOfOffice extends App.ControllerSubContent
multiple: false
limit: 30
minLengt: 2
placeholder: 'Enter Person or Organization/Company'
placeholder: __('Enter Person or Organization/Company')
null: false
translate: false
disableCreateObject: true
@ -131,7 +131,7 @@ class ProfileOutOfOffice extends App.ControllerSubContent
@render()
@notify(
type: 'success'
msg: App.i18n.translateContent('Successfully!')
msg: App.i18n.translateContent('Update successful.')
timeout: 1000
)
else
@ -143,7 +143,7 @@ class ProfileOutOfOffice extends App.ControllerSubContent
else
@notify
type: 'error'
msg: 'Please contact your administrator.'
msg: __('Please contact your administrator.')
removeAll: true
@formEnable( @$('form') )
@ -168,4 +168,4 @@ class ProfileOutOfOffice extends App.ControllerSubContent
msg: App.i18n.translateContent(message)
removeAll: true
App.Config.set('OutOfOffice', { prio: 2800, name: 'Out of Office', parent: '#profile', target: '#profile/out_of_office', permission: ['user_preferences.out_of_office+ticket.agent'], controller: ProfileOutOfOffice }, 'NavBarProfile')
App.Config.set('OutOfOffice', { prio: 2800, name: __('Out of Office'), parent: '#profile', target: '#profile/out_of_office', permission: ['user_preferences.out_of_office+ticket.agent'], controller: ProfileOutOfOffice }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfilePassword extends App.ControllerSubContent
requiredPermission: 'user_preferences.password'
header: 'Password'
header: __('Password')
events:
'submit form': 'update'
@ -14,8 +14,8 @@ class ProfilePassword extends App.ControllerSubContent
html = $( App.view('profile/password')() )
configure_attributes = [
{ name: 'password_old', display: 'Current password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input', single: true },
{ name: 'password_new', display: 'New password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input', },
{ name: 'password_old', display: __('Current password'), tag: 'input', type: 'password', limit: 100, null: false, class: 'input', single: true },
{ name: 'password_new', display: __('New password'), tag: 'input', type: 'password', limit: 100, null: false, class: 'input', },
]
@form = new App.ControllerForm(
@ -37,14 +37,14 @@ class ProfilePassword extends App.ControllerSubContent
@$('[name=password_new_confirm]').val('')
@notify
type: 'error'
msg: 'Can\'t update password, your new passwords do not match. Please try again!'
msg: __('Can\'t update password, your new passwords do not match. Please try again!')
removeAll: true
return
if !params['password_new']
@formEnable(e)
@notify
type: 'error'
msg: 'Please supply your new password!'
msg: __('Please supply your new password!')
removeAll: true
return
@ -74,8 +74,8 @@ class ProfilePassword extends App.ControllerSubContent
else
@notify
type: 'error'
msg: 'Unable to set password. Please contact your administrator.'
msg: __('Unable to set password. Please contact your administrator.')
removeAll: true
@formEnable( @$('form') )
App.Config.set('Password', { prio: 2000, name: 'Password', parent: '#profile', target: '#profile/password', controller: ProfilePassword, permission: ['user_preferences.password'] }, 'NavBarProfile')
App.Config.set('Password', { prio: 2000, name: __('Password'), parent: '#profile', target: '#profile/password', controller: ProfilePassword, permission: ['user_preferences.password'] }, 'NavBarProfile')

View file

@ -1,6 +1,6 @@
class ProfileTokenAccess extends App.ControllerSubContent
requiredPermission: 'user_preferences.access_token'
header: 'Token Access'
header: __('Token Access')
events:
'click .js-delete': 'delete'
'click .js-create': 'create'
@ -63,7 +63,7 @@ class ProfileTokenAccess extends App.ControllerSubContent
)
new App.ControllerConfirm(
message: 'Sure?'
message: __('Sure?')
callback: callback
container: @el.closest('.content')
)
@ -76,8 +76,8 @@ class ProfileTokenAccess extends App.ControllerSubContent
)
class Create extends App.ControllerModal
head: 'Add a Personal Access Token'
buttonSubmit: 'Create'
head: __('Add a Personal Access Token')
buttonSubmit: __('Create')
buttonCancel: true
shown: true
@ -125,8 +125,8 @@ class Create extends App.ControllerModal
return if !@newToken
ui = @
new App.ControllerModal(
head: 'Your New Personal Access Token'
buttonSubmit: "OK, I've copied my token"
head: __('Your New Personal Access Token')
buttonSubmit: __("OK, I've copied my token")
content: ->
App.view('profile/token_access_created')(
name: ui.newToken.name
@ -149,7 +149,7 @@ class Create extends App.ControllerModal
App.Config.set('Token Access', {
prio: 3200,
name: 'Token Access',
name: __('Token Access'),
parent: '#profile',
target: '#profile/token_access',
controller: ProfileTokenAccess,

View file

@ -48,12 +48,11 @@ class App.SettingsAreaProxy extends App.Controller
}
return
new App.ControllerConfirm(
head: 'Error'
head: __('Error')
message: data.message
buttonClass: 'btn--success'
buttonCancel: false
buttonSubmit: 'Close'
buttonSubmit: __('Close')
container: @el.closest('.content')
)
)

View file

@ -90,7 +90,7 @@ class App.SettingsAreaSwitch extends App.Controller
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to update object!'))
timeout: 2000
}
)

View file

@ -126,7 +126,7 @@ class App.SettingsAreaTicketNumber extends App.Controller
ui.formEnable(e)
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to update object!'))
timeout: 2000
}
)

View file

@ -88,7 +88,7 @@ class App.SettingsForm extends App.Controller
fail: (settings, details) ->
App.Event.trigger('notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
msg: App.i18n.translateContent(details.error_human || details.error || __('Unable to update object!'))
timeout: 2000
})
)

View file

@ -5,59 +5,59 @@ class App.UiElement.ApplicationSelector
groups =
ticket:
name: 'Ticket'
name: __('Ticket')
model: 'Ticket'
article:
name: 'Article'
name: __('Article')
model: 'TicketArticle'
customer:
name: 'Customer'
name: __('Customer')
model: 'User'
organization:
name: 'Organization'
name: __('Organization')
model: 'Organization'
if attribute.executionTime
groups.execution_time =
name: 'Execution Time'
name: __('Execution Time')
operators_type =
'^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)']
'^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)']
'^datetime$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)')]
'^timestamp$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)')]
'^date$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)']
'boolean$': ['is', 'is not']
'integer$': ['is', 'is not']
'^radio$': ['is', 'is not']
'^select$': ['is', 'is not']
'^tree_select$': ['is', 'is not']
'^input$': ['contains', 'contains not']
'^richtext$': ['contains', 'contains not']
'^textarea$': ['contains', 'contains not']
'^tag$': ['contains all', 'contains one', 'contains all not', 'contains one not']
'boolean$': [__('is'), __('is not')]
'integer$': [__('is'), __('is not')]
'^radio$': [__('is'), __('is not')]
'^select$': [__('is'), __('is not')]
'^tree_select$': [__('is'), __('is not')]
'^input$': [__('contains'), __('contains not')]
'^richtext$': [__('contains'), __('contains not')]
'^textarea$': [__('contains'), __('contains not')]
'^tag$': [__('contains all'), __('contains one'), __('contains all not'), __('contains one not')]
if attribute.hasChanged
operators_type =
'^datetime$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)', 'has changed']
'^timestamp$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)', 'has changed']
'^date$': ['before (absolute)', 'after (absolute)', 'before (relative)', 'after (relative)', 'within next (relative)', 'within last (relative)', 'till (relative)', 'from (relative)', 'has changed']
'boolean$': ['is', 'is not', 'has changed']
'integer$': ['is', 'is not', 'has changed']
'^radio$': ['is', 'is not', 'has changed']
'^select$': ['is', 'is not', 'has changed']
'^tree_select$': ['is', 'is not', 'has changed']
'^input$': ['contains', 'contains not', 'has changed']
'^richtext$': ['contains', 'contains not', 'has changed']
'^textarea$': ['contains', 'contains not', 'has changed']
'^tag$': ['contains all', 'contains one', 'contains all not', 'contains one not']
'^datetime$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)'), __('has changed')]
'^timestamp$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)'), __('has changed')]
'^date$': [__('before (absolute)'), __('after (absolute)'), __('before (relative)'), __('after (relative)'), __('within next (relative)'), __('within last (relative)'), __('till (relative)'), __('from (relative)'), __('has changed')]
'boolean$': [__('is'), __('is not'), __('has changed')]
'integer$': [__('is'), __('is not'), __('has changed')]
'^radio$': [__('is'), __('is not'), __('has changed')]
'^select$': [__('is'), __('is not'), __('has changed')]
'^tree_select$': [__('is'), __('is not'), __('has changed')]
'^input$': [__('contains'), __('contains not'), __('has changed')]
'^richtext$': [__('contains'), __('contains not'), __('has changed')]
'^textarea$': [__('contains'), __('contains not'), __('has changed')]
'^tag$': [__('contains all'), __('contains one'), __('contains all not'), __('contains one not')]
operators_name =
'_id$': ['is', 'is not']
'_ids$': ['is', 'is not']
'_id$': [__('is'), __('is not')]
'_ids$': [__('is'), __('is not')]
if attribute.hasChanged
operators_name =
'_id$': ['is', 'is not', 'has changed']
'_ids$': ['is', 'is not', 'has changed']
'_id$': [__('is'), __('is not'), __('has changed')]
'_ids$': [__('is'), __('is not'), __('has changed')]
# merge config
elements = {}
@ -68,7 +68,7 @@ class App.UiElement.ApplicationSelector
if attribute.action
elements['ticket.action'] =
name: 'action'
display: 'Action'
display: __('Action')
tag: 'select'
null: false
translate: true
@ -77,19 +77,19 @@ class App.UiElement.ApplicationSelector
update: 'updated'
'update.merged_into': 'merged into'
'update.received_merge': 'received merge'
operator: ['is', 'is not']
operator: [__('is'), __('is not')]
for groupKey, groupMeta of groups
if groupKey is 'execution_time'
if attribute.executionTime
elements['execution_time.calendar_id'] =
name: 'calendar_id'
display: 'Calendar'
display: __('Calendar')
tag: 'select'
relation: 'Calendar'
null: false
translate: false
operator: ['is in working time', 'is not in working time']
operator: [__('is in working time'), __('is not in working time')]
else
for row in App[groupMeta.model].configure_attributes
@ -115,12 +115,12 @@ class App.UiElement.ApplicationSelector
if attribute.out_of_office
elements['ticket.out_of_office_replacement_id'] =
name: 'out_of_office_replacement_id'
display: 'Out of office replacement'
display: __('Out of office replacement')
tag: 'autocompletion_ajax'
relation: 'User'
null: false
translate: true
operator: ['is', 'is not']
operator: [__('is'), __('is not')]
# Remove 'has changed' operator from attributes which don't support the operator.
['ticket.created_at', 'ticket.updated_at'].forEach (element_name) ->
@ -128,12 +128,12 @@ class App.UiElement.ApplicationSelector
elements['ticket.mention_user_ids'] =
name: 'mention_user_ids'
display: 'Subscribe'
display: __('Subscribe')
tag: 'autocompletion_ajax'
relation: 'User'
null: false
translate: true
operator: ['is', 'is not']
operator: [__('is'), __('is not')]
[defaults, groups, elements]
@ -350,6 +350,9 @@ class App.UiElement.ApplicationSelector
@buildOperator(elementFull, elementRow, groupAndAttribute, elements, meta, attribute)
@mapOperatorDisplayName: (operator) ->
return operator
@buildOperator: (elementFull, elementRow, groupAndAttribute, elements, meta, attribute) ->
currentOperator = elementRow.find('.js-operator option:selected').attr('value')
@ -376,7 +379,7 @@ class App.UiElement.ApplicationSelector
break
for operator in attributeConfig.operator
operatorName = App.i18n.translateInline(operator.replace(/_/g, ' '))
operatorName = App.i18n.translateInline(@mapOperatorDisplayName(operator))
selected = ''
if !groupAndAttribute.match(/^ticket/) && operator is 'has changed'
# do nothing, only show "has changed" in ticket attributes

View file

@ -8,8 +8,8 @@ class App.UiElement.active extends App.UiElement.ApplicationUiElement
# build options list
attribute.options = [
{ name: 'active', value: true }
{ name: 'inactive', value: false }
{ name: __('active'), value: true }
{ name: __('inactive'), value: false }
]
# build options list based on config

View file

@ -145,14 +145,14 @@ class App.UiElement.basedate
@buildCustomDates: ->
data = {
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
daysMin: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
daysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
months: ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'],
monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
today: 'today',
clear: 'clear'
days: [__('Sunday'), __('Monday'), __('Tuesday'), __('Wednesday'), __('Thursday'), __('Friday'), __('Saturday')],
daysMin: [__('Sun'), __('Mon'), __('Tue'), __('Wed'), __('Thu'), __('Fri'), __('Sat')],
daysShort: [__('Sun'), __('Mon'), __('Tue'), __('Wed'), __('Thu'), __('Fri'), __('Sat')],
months: [__('January'), __('February'), __('March'), __('April'), __('May'), __('June'),
__('July'), __('August'), __('September'), __('October'), __('November'), __('December')],
monthsShort: [__('Jan'), __('Feb'), __('Mar'), __('Apr'), __('May'), __('Jun'), __('Jul'), __('Aug'), __('Sep'), __('Oct'), __('Nov'), __('Dec')],
today: __('today'),
clear: __('clear')
}
App.i18n.translateDeep(data)

View file

@ -5,8 +5,8 @@ class App.UiElement.boolean extends App.UiElement.ApplicationUiElement
# build options list
if _.isEmpty(attribute.options)
attribute.options = [
{ name: 'yes', value: true }
{ name: 'no', value: false }
{ name: __('yes'), value: true }
{ name: __('no'), value: false }
]
attribute.translate = true

View file

@ -5,38 +5,38 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
groups =
ticket:
name: 'Ticket'
name: __('Ticket')
model: 'Ticket'
model_show: ['Ticket']
group:
name: 'Group'
name: __('Group')
model: 'Group'
model_show: ['Group']
user:
name: 'User'
name: __('User')
model: 'User'
model_show: ['User']
customer:
name: 'Customer'
name: __('Customer')
model: 'User'
model_show: ['Ticket']
organization:
name: 'Organization'
name: __('Organization')
model: 'Organization'
model_show: ['User', 'Organization']
'customer.organization':
name: 'Organization'
name: __('Organization')
model: 'Organization'
model_show: ['Ticket']
session:
name: 'Session'
name: __('Session')
model: 'User'
model_show: ['Ticket']
showCustomModules = @coreWorkflowCustomModulesActive()
if showCustomModules
groups['custom'] =
name: 'Custom'
name: __('Custom')
model_show: ['Ticket', 'User', 'Organization', 'Sla']
currentObject = params.object
@ -49,16 +49,16 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
delete groups[key]
operatorsType =
'active$': ['is']
'boolean$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
'integer$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
'^select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
'^tree_select$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
'^(input|textarea|richtext)$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to', 'regex match', 'regex mismatch']
'active$': [__('is')]
'boolean$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'integer$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'^select$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'^tree_select$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'^(input|textarea|richtext)$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to'), __('regex match'), __('regex mismatch')]
operatorsName =
'_id$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
'_ids$': ['is', 'is not', 'is set', 'not set', 'has changed', 'changed to']
'_id$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
'_ids$': [__('is'), __('is not'), __('is set'), __('not set'), _('has changed'), __('changed to')]
# merge config
elements = {}
@ -73,76 +73,76 @@ class App.UiElement.core_workflow_condition extends App.UiElement.ApplicationSel
elements['custom.module'] = {
name: 'module',
display: 'Module',
display: __('Module'),
tag: 'select',
multiple: true,
options: options,
null: false,
operator: ['match one module', 'match all modules', 'match no modules']
operator: [__('match one module'), __('match all modules'), __('match no modules')]
}
continue
if groupKey is 'session'
elements['session.role_ids'] = {
name: 'role_ids',
display: 'Role',
display: __('Role'),
tag: 'select',
relation: 'Role',
null: false,
operator: ['is', 'is not'],
operator: [__('is'), __('is not')],
multiple: true
}
elements['session.group_ids_read'] = {
name: 'group_ids_read',
display: 'Group (read)',
display: __('Group (read)'),
tag: 'select',
relation: 'Group',
null: false,
operator: ['is', 'is not'],
operator: [__('is'), __('is not')],
multiple: true
}
elements['session.group_ids_create'] = {
name: 'group_ids_create',
display: 'Group (create)',
display: __('Group (create)'),
tag: 'select',
relation: 'Group',
null: false,
operator: ['is', 'is not'],
operator: [__('is'), __('is not')],
multiple: true
}
elements['session.group_ids_change'] = {
name: 'group_ids_change',
display: 'Group (change)',
display: __('Group (change)'),
tag: 'select',
relation: 'Group',
null: false,
operator: ['is', 'is not'],
operator: [__('is'), __('is not')],
multiple: true
}
elements['session.group_ids_overview'] = {
name: 'group_ids_overview',
display: 'Group (overview)',
display: __('Group (overview)'),
tag: 'select',
relation: 'Group',
null: false,
operator: ['is', 'is not'],
operator: [__('is'), __('is not')],
multiple: true
}
elements['session.group_ids_full'] = {
name: 'group_ids_full',
display: 'Group (full)',
display: __('Group (full)'),
tag: 'select',
relation: 'Group',
null: false,
operator: ['is', 'is not'],
operator: [__('is'), __('is not')],
multiple: true
}
elements['session.permission_ids'] = {
name: 'permission_ids',
display: 'Permissions',
display: __('Permissions'),
tag: 'select',
relation: 'Permission',
null: false,
operator: ['is', 'is not'],
operator: [__('is'), __('is not')],
multiple: true
}

View file

@ -60,7 +60,7 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
options = {}
for module in App.CoreWorkflowCustomModule.all()
options[module.name] = module.name
elements['custom.module'] = { name: 'module', display: 'Module', tag: 'select', multiple: true, options: options, null: false, operator: ['execute'] }
elements['custom.module'] = { name: 'module', display: __('Module'), tag: 'select', multiple: true, options: options, null: false, operator: ['execute'] }
continue
for row in App[groupMeta.model].configure_attributes
@ -136,6 +136,24 @@ class App.UiElement.core_workflow_perform extends App.UiElement.ApplicationSelec
config.nulloption = false
return config
@mapOperatorDisplayName: (operator) ->
names =
'show': __('show')
'hide': __('hide')
'remove': __('remove')
'set_mandatory': __('set mandatory')
'set_optional': __('set optional')
'set_readonly': __('set readonly')
'unset_readonly': __('unset readonly')
'add_option': __('add option')
'remove_option': __('remove option')
'set_fixed_to': __('set fixed to')
'select': __('select')
'auto_select': __('auto select')
'fill_in': __('fill in')
'fill_in_empty': __('fill in empty')
return names[operator] || operator
@HasPreCondition: ->
return false

View file

@ -30,13 +30,13 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
localItem.find('.js-dataScreens').html(@dataScreens(attribute, localParams, params))
options =
datetime: 'Datetime'
date: 'Date'
input: 'Text'
select: 'Select'
tree_select: 'Tree Select'
boolean: 'Boolean'
integer: 'Integer'
datetime: __('Datetime')
date: __('Date')
input: __('Text')
select: __('Select')
tree_select: __('Tree Select')
boolean: __('Boolean')
integer: __('Integer')
# if attribute already exists, do not allow to change it anymore
if params.data_type
@ -173,7 +173,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@input: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'input', type: 'text', null: true, default: '' },
{ name: 'data_option::default', display: __('Default'), tag: 'input', type: 'text', null: true, default: '' },
]
inputDefault = new App.ControllerForm(
model:
@ -182,7 +182,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::type', display: 'Type', tag: 'select', null: false, default: 'text', options: {text: 'Text', tel: 'Phone', email: 'Email', url: 'Url'}, translate: true },
{ name: 'data_option::type', display: __('Type'), tag: 'select', null: false, default: 'text', options: {text: __('Text'), tel: 'Phone', email: 'Email', url: 'Url'}, translate: true },
]
inputType = new App.ControllerForm(
model:
@ -191,7 +191,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::maxlength', display: 'Maxlength', tag: 'integer', null: false, default: 120 },
{ name: 'data_option::maxlength', display: __('Maxlength'), tag: 'integer', null: false, default: 120 },
]
inputMaxlength = new App.ControllerForm(
model:
@ -201,7 +201,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
)
configureAttributes = [
# coffeelint: disable=no_interpolation_in_single_quotes
{ name: 'data_option::linktemplate', display: 'Link-Template', tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{object.attribute_name} - use ticket, user or organization as object' },
{ name: 'data_option::linktemplate', display: __('Link-Template'), tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{object.attribute_name} - use ticket, user or organization as object' },
# coffeelint: enable=no_interpolation_in_single_quotes
]
inputLinkTemplate = new App.ControllerForm(
@ -226,7 +226,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@datetime: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::future', display: 'Allow future', tag: 'boolean', null: false, default: true },
{ name: 'data_option::future', display: __('Allow future'), tag: 'boolean', null: false, default: true },
]
datetimeFuture = new App.ControllerForm(
model:
@ -235,7 +235,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::past', display: 'Allow past', tag: 'boolean', null: false, default: true },
{ name: 'data_option::past', display: __('Allow past'), tag: 'boolean', null: false, default: true },
]
datetimePast = new App.ControllerForm(
model:
@ -244,7 +244,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::diff', display: 'Default time Diff (minutes)', tag: 'integer', null: true },
{ name: 'data_option::diff', display: __('Default time Diff (minutes)'), tag: 'integer', null: true },
]
datetimeDiff = new App.ControllerForm(
model:
@ -258,7 +258,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@date: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::diff', display: 'Default time Diff (hours)', tag: 'integer', null: true },
{ name: 'data_option::diff', display: __('Default time Diff (hours)'), tag: 'integer', null: true },
]
dateDiff = new App.ControllerForm(
model:
@ -270,7 +270,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@integer: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'integer', null: true, default: '', min: 1 },
{ name: 'data_option::default', display: __('Default'), tag: 'integer', null: true, default: '', min: 1 },
]
integerDefault = new App.ControllerForm(
model:
@ -279,7 +279,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::min', display: 'Minimal', tag: 'integer', null: false, default: 0, min: -2147483647, max: 2147483647 },
{ name: 'data_option::min', display: __('Minimal'), tag: 'integer', null: false, default: 0, min: -2147483647, max: 2147483647 },
]
integerMin = new App.ControllerForm(
model:
@ -288,7 +288,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::max', display: 'Maximal', tag: 'integer', null: false, min: -2147483647, max: 2147483647, default: 999999999 },
{ name: 'data_option::max', display: __('Maximal'), tag: 'integer', null: false, min: -2147483647, max: 2147483647, default: 999999999 },
]
integerMax = new App.ControllerForm(
model:
@ -339,7 +339,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
)
configureAttributes = [
# coffeelint: disable=no_interpolation_in_single_quotes
{ name: 'data_option::linktemplate', display: 'Link-Template', tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{ticket.attribute_name}' },
{ name: 'data_option::linktemplate', display: __('Link-Template'), tag: 'input', type: 'text', null: true, default: '', placeholder: 'https://example.com/?q=#{ticket.attribute_name}' },
# coffeelint: enable=no_interpolation_in_single_quotes
]
inputLinkTemplate = new App.ControllerForm(
@ -441,7 +441,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
@autocompletion: (item, localParams, params) ->
configureAttributes = [
{ name: 'data_option::default', display: 'Default', tag: 'input', type: 'text', null: true, default: '' },
{ name: 'data_option::default', display: __('Default'), tag: 'input', type: 'text', null: true, default: '' },
]
autocompletionDefault = new App.ControllerForm(
model:
@ -450,7 +450,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::url', display: 'Url (AJAX Endpoint)', tag: 'input', type: 'url', null: false, default: '', placeholder: 'https://example.com/serials' },
{ name: 'data_option::url', display: __('Url (AJAX Endpoint)'), tag: 'input', type: 'url', null: false, default: '', placeholder: 'https://example.com/serials' },
]
autocompletionUrl = new App.ControllerForm(
model:
@ -459,7 +459,7 @@ class App.UiElement.object_manager_attribute extends App.UiElement.ApplicationUi
params: params
)
configureAttributes = [
{ name: 'data_option::method', display: 'Method (AJAX Endpoint)', tag: 'input', type: 'url', null: false, default: '', placeholder: 'GET' },
{ name: 'data_option::method', display: __('Method (AJAX Endpoint)'), tag: 'input', type: 'url', null: false, default: '', placeholder: __('GET') },
]
autocompletionMethod = new App.ControllerForm(
model:

View file

@ -3,35 +3,35 @@ class App.UiElement.postmaster_match
@defaults: ->
groups =
general:
name: 'Basic Settings'
name: __('Basic Settings')
options: [
{
value: 'from'
name: 'From'
name: __('From')
},
{
value: 'to'
name: 'To'
name: __('To')
},
{
value: 'cc'
name: 'Cc'
name: __('Cc')
},
{
value: 'x-any-recipient'
name: 'Any Recipient'
name: __('Any Recipient')
},
{
value: 'subject'
name: 'Subject'
name: __('Subject')
},
{
value: 'body'
name: 'Body'
name: __('Body')
},
]
expert:
name: 'Expert Settings'
name: __('Expert Settings')
options: [
{
value: 'x-spam'

View file

@ -8,34 +8,34 @@ class App.UiElement.postmaster_set
options: [
{
value: 'priority_id'
name: 'Priority'
name: __('Priority')
relation: 'TicketPriority'
}
{
value: 'state_id'
name: 'State'
name: __('State')
relation: 'TicketState'
}
{
value: 'tags'
name: 'Tag'
name: __('Tag')
tag: 'tag'
}
{
value: 'customer_id'
name: 'Customer'
name: __('Customer')
relation: 'User'
tag: 'user_autocompletion'
disableCreateObject: true
}
{
value: 'group_id'
name: 'Group'
name: __('Group')
relation: 'Group'
}
{
value: 'owner_id'
name: 'Owner'
name: __('Owner')
relation: 'User'
tag: 'user_autocompletion'
disableCreateObject: true
@ -46,17 +46,17 @@ class App.UiElement.postmaster_set
options: [
{
value: 'x-zammad-article-internal'
name: 'Internal'
name: __('Internal')
options: { true: 'yes', false: 'no'}
}
{
value: 'x-zammad-article-type_id'
name: 'Type'
name: __('Type')
relation: 'TicketArticleType'
}
{
value: 'x-zammad-article-sender_id'
name: 'Sender'
name: __('Sender')
relation: 'TicketArticleSender'
}
]
@ -65,7 +65,7 @@ class App.UiElement.postmaster_set
options: [
{
value: 'x-zammad-ignore'
name: 'Ignore Message'
name: __('Ignore Message')
options: { true: 'yes', false: 'no'}
}
]

View file

@ -1,16 +1,16 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.embed_video extends App.UiElement.richtext.additions.RichTextToolButton
@icon: 'cloud'
@text: 'Video'
@text: __('Video')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupVideo
@initializeAttributes:
model:
configure_attributes: [
{
name: 'link'
display: 'Link'
display: __('Link')
tag: 'input'
placeholder: 'Youtube or Vimeo address'
placeholder: __('Youtube or Vimeo address')
}
]

View file

@ -1,14 +1,14 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.insert_image extends App.UiElement.richtext.additions.RichTextToolButton
@icon: 'web'
@text: 'Image'
@text: __('Image')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupImage
@initializeAttributes:
model:
configure_attributes: [
{
name: 'link'
display: 'Image'
display: __('Image')
tag: 'input'
type: 'file'
}

View file

@ -1,14 +1,14 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.link_answer extends App.UiElement.richtext.additions.RichTextToolButtonLink
@icon: 'knowledge-base-answer'
@text: 'Link Answer'
@text: __('Link Answer')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupAnswer
@initializeAttributes:
model:
configure_attributes: [
{
name: 'link'
display: 'Answer'
display: __('Answer')
relation: 'KnowledgeBaseAnswerTranslation'
tag: 'autocompletion_ajax'
}

View file

@ -1,14 +1,14 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.richtext.toolButtons.link extends App.UiElement.richtext.additions.RichTextToolButtonLink
@icon: 'chain'
@text: 'Weblink'
@text: __('Weblink')
@klass: -> App.UiElement.richtext.additions.RichTextToolPopupLink
@initializeAttributes:
model:
configure_attributes: [
{
name: 'link'
display: 'Link'
display: __('Link')
tag: 'input'
placeholder: 'http://'
}

View file

@ -36,7 +36,7 @@ class App.UiElement.richtext.additions.RichTextToolPopupVideo extends App.UiElem
if !markup
new App.ControllerErrorModal(
message: 'Invalid video URL'
message: __('Invalid video URL')
)
return

View file

@ -5,15 +5,15 @@ class App.UiElement.ticket_perform_action
groups =
ticket:
name: 'Ticket'
name: __('Ticket')
model: 'Ticket'
article:
name: 'Article'
name: __('Article')
model: 'Article'
if attribute.notification
groups.notification =
name: 'Notification'
name: __('Notification')
model: 'Notification'
# merge config
@ -21,11 +21,11 @@ class App.UiElement.ticket_perform_action
for groupKey, groupMeta of groups
if !groupMeta.model || !App[groupMeta.model]
if groupKey is 'notification'
elements["#{groupKey}.email"] = { name: 'email', display: 'Email' }
elements["#{groupKey}.sms"] = { name: 'sms', display: 'SMS' }
elements["#{groupKey}.webhook"] = { name: 'webhook', display: 'Webhook' }
elements["#{groupKey}.email"] = { name: 'email', display: __('Email') }
elements["#{groupKey}.sms"] = { name: 'sms', display: __('SMS') }
elements["#{groupKey}.webhook"] = { name: 'webhook', display: __('Webhook') }
else if groupKey is 'article'
elements["#{groupKey}.note"] = { name: 'note', display: 'Note' }
elements["#{groupKey}.note"] = { name: 'note', display: __('Note') }
else
for row in App[groupMeta.model].configure_attributes
@ -49,7 +49,7 @@ class App.UiElement.ticket_perform_action
if attribute.ticket_delete
elements['ticket.action'] =
name: 'action'
display: 'Action'
display: __('Action')
tag: 'select'
null: false
translate: true
@ -362,10 +362,10 @@ class App.UiElement.ticket_perform_action
elementRow.find('.js-setNotification').empty()
options =
'article_last_sender': 'Article Last Sender'
'ticket_owner': 'Owner'
'ticket_customer': 'Customer'
'ticket_agents': 'All Agents'
'article_last_sender': __('Article Last Sender')
'ticket_owner': __('Owner')
'ticket_customer': __('Customer')
'ticket_agents': __('All Agents')
name = "#{attribute.name}::notification.#{notificationType}"
@ -399,11 +399,11 @@ class App.UiElement.ticket_perform_action
name: "#{name}::recipient"
options: [
{
label: 'Variables',
label: __('Variables'),
group: columnSelectOptions
},
{
label: 'User',
label: __('User'),
group: columnSelectRecipientUserOptions
},
]
@ -459,7 +459,7 @@ class App.UiElement.ticket_perform_action
name: "#{name}::include_attachments"
multiple: false
null: false
options: { true: 'Yes', false: 'No' }
options: { true: __('Yes'), false: __('No') }
value: meta.include_attachments || 'false'
translate: true
)
@ -469,7 +469,7 @@ class App.UiElement.ticket_perform_action
notificationElement.find('.js-body div[contenteditable="true"]').ce(
mode: 'richtext'
placeholder: 'message'
placeholder: __('message')
maxlength: messageLength
)
new App.WidgetPlaceholder(
@ -478,17 +478,17 @@ class App.UiElement.ticket_perform_action
{
prefix: 'ticket'
object: 'Ticket'
display: 'Ticket'
display: __('Ticket')
},
{
prefix: 'article'
object: 'TicketArticle'
display: 'Article'
display: __('Article')
},
{
prefix: 'user'
object: 'User'
display: 'Current User'
display: __('Current User')
},
]
)
@ -500,9 +500,9 @@ class App.UiElement.ticket_perform_action
name: "#{name}::sign"
multiple: false
options: {
'no': 'Do not sign email'
'discard': 'Sign email (if not possible, discard notification)'
'always': 'Sign email (if not possible, send notification anyway)'
'no': __('Do not sign email')
'discard': __('Sign email (if not possible, discard notification)')
'always': __('Sign email (if not possible, send notification anyway)')
}
value: meta.sign
translate: true
@ -514,9 +514,9 @@ class App.UiElement.ticket_perform_action
name: "#{name}::encryption"
multiple: false
options: {
'no': 'Do not encrypt email'
'discard': 'Encrypt email (if not possible, discard notification)'
'always': 'Encrypt email (if not possible, send notification anyway)'
'no': __('Do not encrypt email')
'discard': __('Encrypt email (if not possible, discard notification)')
'always': __('Encrypt email (if not possible, send notification anyway)')
}
value: meta.encryption
translate: true
@ -535,7 +535,7 @@ class App.UiElement.ticket_perform_action
name: "#{name}::internal"
multiple: false
null: false
label: 'Visibility'
label: __('Visibility')
options: { true: 'internal', false: 'public' }
value: meta.internal
translate: true
@ -549,7 +549,7 @@ class App.UiElement.ticket_perform_action
articleElement.find('.js-internal').html(selection)
articleElement.find('.js-body div[contenteditable="true"]').ce(
mode: 'richtext'
placeholder: 'message'
placeholder: __('message')
maxlength: 200000
)
new App.WidgetPlaceholder(
@ -558,17 +558,17 @@ class App.UiElement.ticket_perform_action
{
prefix: 'ticket'
object: 'Ticket'
display: 'Ticket'
display: __('Ticket')
},
{
prefix: 'article'
object: 'TicketArticle'
display: 'Article'
display: __('Article')
},
{
prefix: 'user'
object: 'User'
display: 'Current User'
display: __('Current User')
},
]
)

View file

@ -2,11 +2,11 @@
class App.UiElement.time_range
@render: (attribute) ->
ranges =
minute: 'Minute(s)'
hour: 'Hour(s)'
day: 'Day(s)'
month: 'Month(s)'
year: 'Year(s)'
minute: __('Minute(s)')
hour: __('Hour(s)')
day: __('Day(s)')
month: __('Month(s)')
year: __('Year(s)')
for key, value of ranges
ranges[key] = App.i18n.translateInline(value)

View file

@ -2,13 +2,13 @@
class App.UiElement.timer
@render: (attribute) ->
days =
Mon: 'Monday'
Tue: 'Tuesday'
Wed: 'Wednesday'
Thu: 'Thursday'
Fri: 'Friday'
Sat: 'Saturday'
Sun: 'Sunday'
Mon: __('Monday')
Tue: __('Tuesday')
Wed: __('Wednesday')
Thu: __('Thursday')
Fri: __('Friday')
Sat: __('Saturday')
Sun: __('Sunday')
hours =
0: '12 am'
1: '1 am'

Some files were not shown because too many files have changed in this diff Show more