From 64a87b1c67c2dad7e9311491115f73606b14acc6 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Mon, 15 Nov 2021 16:58:19 +0100 Subject: [PATCH] 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. --- .../rules/detect_translatable_string.coffee | 91 + .gitignore | 4 +- .gitlab/ci/pre.yml | 23 +- .overcommit.yml | 13 +- .../cop/zammad/detect_translatable_string.rb | 90 + .rubocop/default.yml | 14 + .rubocop/rubocop_zammad.rb | 1 + Gemfile | 3 + Gemfile.lock | 4 + .../_application_controller/_base.coffee | 2 +- .../_application_controller/_modal.coffee | 4 +- .../_application_controller/form.coffee | 12 +- .../generic_description.coffee | 4 +- .../generic_destroy_confirm.coffee | 8 +- .../generic_edit.coffee | 2 +- .../generic_error_modal.coffee | 4 +- .../generic_history.coffee | 2 +- .../generic_new.coffee | 2 +- .../reorder_modal.coffee | 5 +- .../_application_controller/table.coffee | 20 +- .../controllers/_channel/_email_filter.coffee | 6 +- .../_channel/_email_signature.coffee | 8 +- .../app/controllers/_channel/chat.coffee | 36 +- .../app/controllers/_channel/email.coffee | 84 +- .../app/controllers/_channel/facebook.coffee | 16 +- .../app/controllers/_channel/form.coffee | 4 +- .../app/controllers/_channel/google.coffee | 44 +- .../controllers/_channel/microsoft365.coffee | 44 +- .../app/controllers/_channel/sms.coffee | 34 +- .../app/controllers/_channel/telegram.coffee | 12 +- .../app/controllers/_channel/twitter.coffee | 14 +- .../app/controllers/_channel/web.coffee | 8 +- .../controllers/_dashboard/first_steps.coffee | 6 +- .../_dashboard/first_steps_clues.coffee | 20 +- .../stats/ticket_channel_distribution.coffee | 2 +- .../_dashboard/stats/ticket_escalation.coffee | 2 +- .../_dashboard/stats/ticket_in_process.coffee | 2 +- .../stats/ticket_load_measure.coffee | 2 +- .../_dashboard/stats/ticket_reopen.coffee | 2 +- .../stats/ticket_waiting_time.coffee | 2 +- .../app/controllers/_default_navbar.coffee | 10 +- .../controllers/_integration/check_mk.coffee | 10 +- .../controllers/_integration/clearbit.coffee | 15 +- .../app/controllers/_integration/cti.coffee | 10 +- .../controllers/_integration/exchange.coffee | 14 +- .../controllers/_integration/github.coffee | 10 +- .../controllers/_integration/gitlab.coffee | 10 +- .../controllers/_integration/icinga.coffee | 10 +- .../app/controllers/_integration/idoit.coffee | 6 +- .../app/controllers/_integration/ldap.coffee | 16 +- .../app/controllers/_integration/monit.coffee | 10 +- .../controllers/_integration/nagios.coffee | 10 +- .../controllers/_integration/placetel.coffee | 10 +- .../_integration/sipgate_io.coffee | 6 +- .../app/controllers/_integration/slack.coffee | 24 +- .../app/controllers/_integration/smime.coffee | 28 +- .../app/controllers/_manage/branding.coffee | 8 +- .../controllers/_manage/knowledge_base.coffee | 16 +- .../app/controllers/_manage/security.coffee | 15 +- .../app/controllers/_manage/system.coffee | 16 +- .../app/controllers/_manage/ticket.coffee | 10 +- .../_manage/ticket_auto_assignment.coffee | 4 +- .../_plugin/electron_events.coffee | 10 +- .../_plugin/keyboard_shortcuts.coffee | 108 +- .../controllers/_plugin/maintenance.coffee | 10 +- .../app/controllers/_plugin/navigation.coffee | 2 +- .../_plugin/session_taken_over.coffee | 6 +- .../_plugin/session_timeout.coffee | 6 +- .../_plugin/translation_support.coffee | 16 +- .../_plugin/user_signup_check.coffee | 4 +- .../app/controllers/_profile/avatar.coffee | 14 +- .../_profile/calendar_subscriptions.coffee | 4 +- .../app/controllers/_profile/devices.coffee | 4 +- .../app/controllers/_profile/language.coffee | 4 +- .../_profile/linked_accounts.coffee | 24 +- .../controllers/_profile/notification.coffee | 30 +- .../controllers/_profile/out_of_office.coffee | 10 +- .../app/controllers/_profile/password.coffee | 14 +- .../controllers/_profile/token_access.coffee | 14 +- .../controllers/_settings/area_proxy.coffee | 5 +- .../controllers/_settings/area_switch.coffee | 2 +- .../_settings/area_ticket_number.coffee | 2 +- .../app/controllers/_settings/form.coffee | 2 +- .../_ui_element/_application_selector.coffee | 85 +- .../app/controllers/_ui_element/active.coffee | 4 +- .../controllers/_ui_element/basedate.coffee | 16 +- .../controllers/_ui_element/boolean.coffee | 4 +- .../core_workflow_condition.coffee | 64 +- .../_ui_element/core_workflow_perform.coffee | 20 +- .../object_manager_attribute.coffee | 44 +- .../_ui_element/postmaster_match.coffee | 16 +- .../_ui_element/postmaster_set.coffee | 20 +- .../embed_video_button.coffee | 6 +- .../insert_image_button.coffee | 4 +- .../link_answer_button.coffee | 4 +- .../richtext_additions/link_button.coffee | 4 +- .../richtext_additions/popup_video.coffee | 2 +- .../_ui_element/ticket_perform_action.coffee | 60 +- .../controllers/_ui_element/time_range.coffee | 10 +- .../app/controllers/_ui_element/timer.coffee | 14 +- .../controllers/agent_ticket_create.coffee | 20 +- .../sidebar_customer.coffee | 10 +- .../sidebar_organization.coffee | 10 +- .../sidebar_template.coffee | 2 +- .../app/controllers/agent_ticket_merge.coffee | 4 +- .../javascripts/app/controllers/api.coffee | 16 +- .../app/controllers/calendar.coffee | 12 +- .../javascripts/app/controllers/chat.coffee | 16 +- .../app/controllers/core_workflow.coffee | 18 +- .../javascripts/app/controllers/cti.coffee | 20 +- .../controllers/customer_ticket_create.coffee | 10 +- .../app/controllers/dashboard.coffee | 6 +- .../app/controllers/data_privacy.coffee | 24 +- .../app/controllers/email_verify.coffee | 4 +- .../app/controllers/getting_started.coffee | 2 +- .../controllers/getting_started/admin.coffee | 4 +- .../controllers/getting_started/agent.coffee | 4 +- .../getting_started/auto_wizard.coffee | 2 +- .../controllers/getting_started/base.coffee | 2 +- .../getting_started/channel.coffee | 4 +- .../getting_started/channel_email.coffee | 36 +- .../channel_email_pre_configured.coffee | 2 +- .../getting_started/email_notification.coffee | 12 +- .../controllers/getting_started/finish.coffee | 2 +- .../javascripts/app/controllers/group.coffee | 12 +- .../controllers/idoit_object_selector.coffee | 7 +- .../javascripts/app/controllers/import.coffee | 2 +- .../app/controllers/import_freshdesk.coffee | 6 +- .../app/controllers/import_otrs.coffee | 6 +- .../app/controllers/import_zendesk.coffee | 6 +- .../app/controllers/integrations.coffee | 6 +- .../javascripts/app/controllers/job.coffee | 12 +- .../javascripts/app/controllers/karma.coffee | 2 +- .../app/controllers/keyboard_shortcuts.coffee | 2 +- .../knowledge_base/add_form.coffee | 2 +- .../knowledge_base/agent_controller.coffee | 12 +- .../content_can_be_published_dialog.coffee | 4 +- .../content_can_be_published_form.coffee | 26 +- .../knowledge_base/content_controller.coffee | 6 +- .../knowledge_base/delete_action.coffee | 10 +- .../knowledge_base/editor_coordinator.coffee | 2 +- .../knowledge_base/public_menu_form.coffee | 2 +- .../public_menu_form_item.coffee | 2 +- .../knowledge_base/public_menu_manager.coffee | 4 +- .../knowledge_base/reader_controller.coffee | 4 +- .../reader_list_controller.coffee | 4 +- .../knowledge_base/search_field_widget.coffee | 2 +- .../knowledge_base/sidebar/answers.coffee | 4 +- .../knowledge_base/sidebar/attachments.coffee | 4 +- .../knowledge_base/sidebar/categories.coffee | 4 +- .../app/controllers/layout_ref.coffee | 20 +- .../javascripts/app/controllers/login.coffee | 2 +- .../javascripts/app/controllers/logout.coffee | 2 +- .../javascripts/app/controllers/macro.coffee | 12 +- .../app/controllers/maintenance.coffee | 6 +- .../javascripts/app/controllers/manage.coffee | 8 +- .../app/controllers/monitoring.coffee | 4 +- .../app/controllers/object_manager.coffee | 20 +- .../app/controllers/organization.coffee | 14 +- .../controllers/organization_profile.coffee | 10 +- .../app/controllers/overview.coffee | 12 +- .../app/controllers/package.coffee | 6 +- .../app/controllers/password_reset.coffee | 4 +- .../controllers/password_reset_verify.coffee | 18 +- .../app/controllers/profile.coffee | 4 +- .../javascripts/app/controllers/report.coffee | 28 +- .../app/controllers/report_profile.coffee | 10 +- .../javascripts/app/controllers/role.coffee | 12 +- .../app/controllers/session.coffee | 4 +- .../javascripts/app/controllers/signup.coffee | 4 +- .../javascripts/app/controllers/sla.coffee | 16 +- .../javascripts/app/controllers/tag.coffee | 12 +- .../app/controllers/taskbar_widget.coffee | 4 +- .../app/controllers/text_module.coffee | 14 +- .../app/controllers/ticket_customer.coffee | 4 +- .../app/controllers/ticket_link_add.coffee | 6 +- .../app/controllers/ticket_overview.coffee | 16 +- .../app/controllers/ticket_zoom.coffee | 4 +- .../ticket_zoom/article_action/delete.coffee | 6 +- .../article_action/email_reply.coffee | 16 +- .../article_action/facebook_reply.coffee | 2 +- .../article_action/internal.coffee | 4 +- .../ticket_zoom/article_action/note.coffee | 2 +- .../article_action/phone_reply.coffee | 2 +- .../article_action/sms_reply.coffee | 2 +- .../ticket_zoom/article_action/split.coffee | 2 +- .../article_action/telegram.coffee | 2 +- .../article_action/twitter_reply.coffee | 8 +- .../ticket_zoom/article_image_view.coffee | 2 +- .../ticket_zoom/article_new.coffee | 6 +- .../ticket_zoom/article_view.coffee | 4 +- .../ticket_zoom/highlighter.coffee | 10 +- .../controllers/ticket_zoom/setting.coffee | 2 +- .../sidebar_article_attachments.coffee | 2 +- .../ticket_zoom/sidebar_customer.coffee | 14 +- .../ticket_zoom/sidebar_git_issue.coffee | 10 +- .../ticket_zoom/sidebar_idoit.coffee | 10 +- .../ticket_zoom/sidebar_organization.coffee | 10 +- .../ticket_zoom/sidebar_ticket.coffee | 8 +- .../ticket_zoom/time_accounting.coffee | 6 +- .../app/controllers/time_accounting.coffee | 30 +- .../app/controllers/translation.coffee | 94 +- .../app/controllers/trigger.coffee | 12 +- .../javascripts/app/controllers/user.coffee | 28 +- .../app/controllers/user_profile.coffee | 16 +- .../app/controllers/version.coffee | 4 +- .../app/controllers/webhook.coffee | 14 +- .../widget/button_with_dropdown.coffee | 2 +- .../app/controllers/widget/error_modal.coffee | 4 +- .../app/controllers/widget/http_log.coffee | 2 +- .../app/controllers/widget/import.coffee | 8 +- .../controllers/widget/import_result.coffee | 6 +- .../widget/import_try_result.coffee | 8 +- .../app/controllers/widget/link/ticket.coffee | 2 +- .../controllers/widget/payload_example.coffee | 5 +- .../widget/ticket_bulk_form.coffee | 7 +- .../controllers/widget/ticket_stats.coffee | 8 +- .../app/controllers/widget/user.coffee | 8 +- ..._object_organization_autocompletion.coffee | 2 +- .../javascripts/app/lib/app_post/ajax.coffee | 2 +- .../app/lib/app_post/browser.coffee | 2 +- .../app/lib/app_post/html5_upload.coffee | 4 +- .../javascripts/app/lib/app_post/i18n.coffee | 73 +- .../app/lib/app_post/iconset_picker.coffee | 10 +- .../app/lib/app_post/multi_locales_row.coffee | 2 +- .../user_organization_autocompletion.coffee | 6 +- .../app/lib/app_post/websocket.coffee | 4 +- .../app/models/_application_model.coffee | 8 +- .../javascripts/app/models/application.coffee | 10 +- .../javascripts/app/models/calendar.coffee | 24 +- app/assets/javascripts/app/models/chat.coffee | 25 +- .../app/models/chat_sessions.coffee | 12 +- .../app/models/core_workflow.coffee | 25 +- .../models/core_workflow_custom_module.coffee | 3 +- .../app/models/data_privacy_task.coffee | 16 +- .../app/models/email_address.coffee | 12 +- .../javascripts/app/models/group.coffee | 18 +- app/assets/javascripts/app/models/job.coffee | 32 +- .../app/models/knowledge_base.coffee | 30 +- .../app/models/knowledge_base_answer.coffee | 8 +- .../knowledge_base_answer_translation.coffee | 12 +- .../app/models/knowledge_base_category.coffee | 6 +- .../models/knowledge_base_translation.coffee | 2 +- .../javascripts/app/models/macro.coffee | 20 +- .../javascripts/app/models/mention.coffee | 2 +- .../models/object_manager_attribute.coffee | 14 +- .../app/models/organization.coffee | 16 +- .../javascripts/app/models/overview.coffee | 40 +- .../app/models/postmaster_filter.coffee | 22 +- .../app/models/report_profile.js.coffee | 8 +- app/assets/javascripts/app/models/role.coffee | 19 +- .../javascripts/app/models/setting.coffee | 2 +- .../javascripts/app/models/signature.coffee | 20 +- app/assets/javascripts/app/models/sla.coffee | 20 +- .../javascripts/app/models/text_module.coffee | 22 +- .../javascripts/app/models/ticket.coffee | 46 +- .../app/models/ticket_article.coffee | 28 +- .../app/models/ticket_priority.coffee | 8 +- .../app/models/ticket_state.coffee | 8 +- .../javascripts/app/models/trigger.coffee | 15 +- .../javascripts/app/models/twitter.coffee | 2 +- app/assets/javascripts/app/models/user.coffee | 18 +- .../javascripts/app/models/webhook.coffee | 18 +- .../app/views/knowledge_base/base_form.coffee | 4 +- .../app/views/knowledge_base/delete.coffee | 4 +- .../app/views/knowledge_base/new_modal.coffee | 4 +- .../knowledge_base/server_snippet.coffee | 2 +- .../app/views/translation/index.jst.eco | 2 - .../app/views/translation/list.jst.eco | 6 +- .../app/views/translation/support.jst.eco | 12 +- app/assets/javascripts/application.js | 5 + .../application_controller/authenticates.rb | 24 +- .../application_controller/handles_devices.rb | 2 +- .../application_controller/handles_errors.rb | 8 +- .../application_controller/has_user.rb | 2 +- .../application_controller/renders_models.rb | 2 +- .../application_controller/sets_headers.rb | 4 +- app/controllers/channels_email_controller.rb | 2 +- app/controllers/channels_google_controller.rb | 2 +- .../channels_microsoft365_controller.rb | 2 +- app/controllers/channels_sms_controller.rb | 8 +- .../channels_twitter_controller.rb | 10 +- .../clones_ticket_article_attachments.rb | 2 +- .../concerns/creates_ticket_articles.rb | 2 +- app/controllers/cti_controller.rb | 4 +- app/controllers/first_steps_controller.rb | 40 +- app/controllers/getting_started_controller.rb | 8 +- .../import_freshdesk_controller.rb | 14 +- app/controllers/import_kayako_controller.rb | 10 +- app/controllers/import_otrs_controller.rb | 18 +- app/controllers/import_zendesk_controller.rb | 18 +- .../integration/check_mk_controller.rb | 4 +- app/controllers/integration/cti_controller.rb | 8 +- .../integration/exchange_controller.rb | 6 +- .../integration/placetel_controller.rb | 8 +- .../integration/sipgate_controller.rb | 6 +- .../integration/smime_controller.rb | 2 +- .../knowledge_base/categories_controller.rb | 2 +- .../knowledge_base/manage_controller.rb | 2 +- app/controllers/links_controller.rb | 2 +- app/controllers/long_polling_controller.rb | 4 +- app/controllers/mentions_controller.rb | 2 +- app/controllers/organizations_controller.rb | 2 +- app/controllers/reports_controller.rb | 8 +- app/controllers/sessions_controller.rb | 2 +- app/controllers/settings_controller.rb | 10 +- app/controllers/text_modules_controller.rb | 2 +- app/controllers/ticket_articles_controller.rb | 8 +- app/controllers/tickets_controller.rb | 16 +- .../time_accountings_controller.rb | 12 +- app/controllers/translations_controller.rb | 16 - .../user_access_token_controller.rb | 4 +- app/controllers/users_controller.rb | 40 +- ...knowledge_base_public_page_title_helper.rb | 4 +- app/jobs/communicate_sms_job.rb | 2 +- .../can_creates_and_updates.rb | 2 +- app/models/calendar.rb | 2 +- app/models/channel/driver/sms/base.rb | 4 +- .../channel/driver/sms/massenversand.rb | 6 +- app/models/channel/driver/sms/message_bird.rb | 12 +- app/models/channel/driver/sms/twilio.rb | 16 +- app/models/channel/email_build.rb | 2 + app/models/channel/email_parser.rb | 4 +- .../concerns/checks_condition_validation.rb | 2 +- app/models/core_workflow/result.rb | 4 +- app/models/knowledge_base.rb | 4 +- app/models/locale.rb | 101 +- app/models/object_manager/attribute.rb | 8 +- app/models/object_manager/object.rb | 6 +- app/models/postmaster_filter.rb | 2 +- app/models/report.rb | 48 +- app/models/role.rb | 8 +- app/models/scheduler.rb | 4 +- app/models/smime_certificate.rb | 2 +- app/models/store.rb | 4 +- app/models/store/file.rb | 2 +- app/models/text_module.rb | 2 +- app/models/ticket.rb | 10 +- app/models/ticket/number.rb | 4 +- app/models/translation.rb | 299 +- .../translation/synchronizes_from_po.rb | 92 + app/models/user.rb | 10 +- app/models/webhook.rb | 6 +- .../controllers/taskbar_controller_policy.rb | 2 +- app/policies/pundit_policy.rb | 2 +- app/policies/ticket_policy.rb | 4 +- app/views/mailer/ticket_create/pl.html.erb | 2 +- app/views/mailer/ticket_create/pt-br.html.erb | 2 +- .../ticket_escalation_warning/pl.html.erb | 2 +- .../ticket_escalation_warning/pt-br.html.erb | 2 +- app/views/mailer/ticket_update/pt-br.html.erb | 2 +- coffeelint.json | 3 + config/initializers/generators.rb | 5 + config/locales.yml | 231 + contrib/packager.io/after.sh | 3 - contrib/packager.io/fetch_locales.rb | 50 - db/migrate/20120101000001_create_base.rb | 17 +- .../20170314000001_fixed_translation2.rb | 2 +- ...10903072144_map_translation_time_fields.rb | 22 + ...03073040_drop_translation_format_column.rb | 11 + ...0903085041_translation_add_sync_columns.rb | 15 + db/seeds.rb | 2 +- db/seeds/community_user_resources.rb | 4 +- db/seeds/groups.rb | 4 +- db/seeds/karma_activities.rb | 22 +- db/seeds/macros.rb | 4 +- db/seeds/object_manager_attributes.rb | 120 +- db/seeds/overviews.rb | 20 +- db/seeds/permissions.rb | 234 +- db/seeds/roles.rb | 12 +- db/seeds/schedulers.rb | 50 +- db/seeds/settings.rb | 1098 +- db/seeds/signatures.rb | 2 +- db/seeds/ticket_article_senders.rb | 6 +- db/seeds/ticket_article_types.rb | 26 +- db/seeds/ticket_priorities.rb | 6 +- db/seeds/ticket_state_types.rb | 14 +- db/seeds/ticket_states.rb | 14 +- i18n/zammad.ar.po | 11526 +++++++++++++++ i18n/zammad.bg.po | 11526 +++++++++++++++ i18n/zammad.cs.po | 11526 +++++++++++++++ i18n/zammad.da.po | 11526 +++++++++++++++ i18n/zammad.de-de.po | 11526 +++++++++++++++ i18n/zammad.el.po | 11526 +++++++++++++++ i18n/zammad.en-ca.po | 11526 +++++++++++++++ i18n/zammad.en-gb.po | 11526 +++++++++++++++ i18n/zammad.es-ca.po | 11526 +++++++++++++++ i18n/zammad.es-co.po | 11526 +++++++++++++++ i18n/zammad.es-es.po | 11526 +++++++++++++++ i18n/zammad.es-mx.po | 11526 +++++++++++++++ i18n/zammad.et.po | 11526 +++++++++++++++ i18n/zammad.fa-ir.po | 11526 +++++++++++++++ i18n/zammad.fi.po | 11526 +++++++++++++++ i18n/zammad.fr-ca.po | 11526 +++++++++++++++ i18n/zammad.fr-fr.po | 11526 +++++++++++++++ i18n/zammad.he-il.po | 11526 +++++++++++++++ i18n/zammad.hi-in.po | 11526 +++++++++++++++ i18n/zammad.hr.po | 11526 +++++++++++++++ i18n/zammad.hu.po | 11526 +++++++++++++++ i18n/zammad.is.po | 11526 +++++++++++++++ i18n/zammad.it-it.po | 11526 +++++++++++++++ i18n/zammad.ja.po | 11526 +++++++++++++++ i18n/zammad.ko-kr.po | 11526 +++++++++++++++ i18n/zammad.lt.po | 11526 +++++++++++++++ i18n/zammad.lv.po | 11526 +++++++++++++++ i18n/zammad.ms-my.po | 11526 +++++++++++++++ i18n/zammad.nl-nl.po | 11526 +++++++++++++++ i18n/zammad.no-no.po | 11526 +++++++++++++++ i18n/zammad.pl.po | 11526 +++++++++++++++ i18n/zammad.pot | 11619 ++++++++++++++++ i18n/zammad.pt-br.po | 11526 +++++++++++++++ i18n/zammad.pt-pt.po | 11526 +++++++++++++++ i18n/zammad.ro-ro.po | 11526 +++++++++++++++ i18n/zammad.ru.po | 11526 +++++++++++++++ i18n/zammad.rw.po | 11526 +++++++++++++++ i18n/zammad.sk.po | 11526 +++++++++++++++ i18n/zammad.sl.po | 11526 +++++++++++++++ i18n/zammad.sr-latn-rs.po | 11526 +++++++++++++++ i18n/zammad.sv-se.po | 11526 +++++++++++++++ i18n/zammad.tr.po | 11526 +++++++++++++++ i18n/zammad.uk.po | 11526 +++++++++++++++ i18n/zammad.vi.po | 11526 +++++++++++++++ i18n/zammad.zh-cn.po | 11526 +++++++++++++++ i18n/zammad.zh-tw.po | 11526 +++++++++++++++ lib/core_ext/kernel.rb | 8 + lib/email_helper.rb | 14 +- lib/email_helper/probe.rb | 28 +- lib/email_helper/verify.rb | 2 +- lib/excel_sheet/ticket.rb | 58 +- lib/external_credential/facebook.rb | 8 +- lib/external_credential/google.rb | 12 +- lib/external_credential/microsoft365.rb | 12 +- lib/external_credential/twitter.rb | 20 +- lib/generators/translation_catalog/USAGE | 9 + .../translation_catalog/extractor/base.rb | 25 + .../translation_catalog/extractor/erb.rb | 31 + .../translation_catalog/extractor/frontend.rb | 43 + .../translation_catalog/extractor/ruby.rb | 36 + .../translation_catalog_generator.rb | 145 + lib/github/credentials.rb | 2 +- lib/github/http_client.rb | 2 +- lib/github/linked_issue.rb | 2 +- lib/gitlab/credentials.rb | 2 +- lib/gitlab/http_client.rb | 2 +- lib/gitlab/linked_issue.rb | 2 +- lib/html_sanitizer.rb | 2 +- lib/idoit.rb | 4 +- lib/knowledge_base/menu_item_update_action.rb | 2 +- lib/knowledge_base/server_snippet.rb | 2 +- lib/password_policy/backend.rb | 2 +- lib/password_policy/digit.rb | 2 +- lib/password_policy/length.rb | 2 +- lib/password_policy/special_character.rb | 2 +- .../upper_and_lower_case_characters.rb | 2 +- lib/search_index_backend.rb | 10 +- lib/secure_mailing/smime/incoming.rb | 4 +- lib/sessions/event/chat_base.rb | 2 +- lib/sessions/event/chat_session_start.rb | 2 +- lib/sessions/event/chat_transfer.rb | 6 +- lib/sessions/event/spool.rb | 2 +- lib/static_assets.rb | 2 +- lib/stats.rb | 2 +- .../zammad/translations/sync_from_po.rake | 14 + lib/telegram.rb | 14 +- lib/user_agent.rb | 4 +- lib/user_context.rb | 6 +- public/assets/tests/qunit/table.js | 6 +- .../translation_catalog/extractor/erb_spec.rb | 42 + .../extractor/frontend_spec.rb | 41 + .../extractor/ruby_spec.rb | 43 + .../translation_catalog_generator_spec.rb | 12 + spec/models/locale_spec.rb | 17 + .../translation_synchronizes_from_po_spec.rb | 218 + spec/models/translation_spec.rb | 91 +- spec/models/trigger/sms_spec.rb | 2 +- spec/system/system/object_manager_spec.rb | 4 +- spec/system/system/translations_spec.rb | 129 +- spec/system/ticket/history_spec.rb | 3 + ...agent_ticket_email_reply_keep_body_test.rb | 3 + test/browser_test_helper.rb | 2 +- ...tification_factory_mailer_template_test.rb | 2 +- 481 files changed, 534434 insertions(+), 3442 deletions(-) create mode 100644 .coffeelint/rules/detect_translatable_string.coffee create mode 100644 .rubocop/cop/zammad/detect_translatable_string.rb create mode 100644 app/models/translation/synchronizes_from_po.rb create mode 100644 config/initializers/generators.rb create mode 100644 config/locales.yml delete mode 100755 contrib/packager.io/fetch_locales.rb create mode 100644 db/migrate/20210903072144_map_translation_time_fields.rb create mode 100644 db/migrate/20210903073040_drop_translation_format_column.rb create mode 100644 db/migrate/20210903085041_translation_add_sync_columns.rb create mode 100644 i18n/zammad.ar.po create mode 100644 i18n/zammad.bg.po create mode 100644 i18n/zammad.cs.po create mode 100644 i18n/zammad.da.po create mode 100644 i18n/zammad.de-de.po create mode 100644 i18n/zammad.el.po create mode 100644 i18n/zammad.en-ca.po create mode 100644 i18n/zammad.en-gb.po create mode 100644 i18n/zammad.es-ca.po create mode 100644 i18n/zammad.es-co.po create mode 100644 i18n/zammad.es-es.po create mode 100644 i18n/zammad.es-mx.po create mode 100644 i18n/zammad.et.po create mode 100644 i18n/zammad.fa-ir.po create mode 100644 i18n/zammad.fi.po create mode 100644 i18n/zammad.fr-ca.po create mode 100644 i18n/zammad.fr-fr.po create mode 100644 i18n/zammad.he-il.po create mode 100644 i18n/zammad.hi-in.po create mode 100644 i18n/zammad.hr.po create mode 100644 i18n/zammad.hu.po create mode 100644 i18n/zammad.is.po create mode 100644 i18n/zammad.it-it.po create mode 100644 i18n/zammad.ja.po create mode 100644 i18n/zammad.ko-kr.po create mode 100644 i18n/zammad.lt.po create mode 100644 i18n/zammad.lv.po create mode 100644 i18n/zammad.ms-my.po create mode 100644 i18n/zammad.nl-nl.po create mode 100644 i18n/zammad.no-no.po create mode 100644 i18n/zammad.pl.po create mode 100644 i18n/zammad.pot create mode 100644 i18n/zammad.pt-br.po create mode 100644 i18n/zammad.pt-pt.po create mode 100644 i18n/zammad.ro-ro.po create mode 100644 i18n/zammad.ru.po create mode 100644 i18n/zammad.rw.po create mode 100644 i18n/zammad.sk.po create mode 100644 i18n/zammad.sl.po create mode 100644 i18n/zammad.sr-latn-rs.po create mode 100644 i18n/zammad.sv-se.po create mode 100644 i18n/zammad.tr.po create mode 100644 i18n/zammad.uk.po create mode 100644 i18n/zammad.vi.po create mode 100644 i18n/zammad.zh-cn.po create mode 100644 i18n/zammad.zh-tw.po create mode 100644 lib/core_ext/kernel.rb create mode 100644 lib/generators/translation_catalog/USAGE create mode 100644 lib/generators/translation_catalog/extractor/base.rb create mode 100644 lib/generators/translation_catalog/extractor/erb.rb create mode 100644 lib/generators/translation_catalog/extractor/frontend.rb create mode 100644 lib/generators/translation_catalog/extractor/ruby.rb create mode 100644 lib/generators/translation_catalog/translation_catalog_generator.rb create mode 100644 lib/tasks/zammad/translations/sync_from_po.rake create mode 100644 spec/lib/generators/translation_catalog/extractor/erb_spec.rb create mode 100644 spec/lib/generators/translation_catalog/extractor/frontend_spec.rb create mode 100644 spec/lib/generators/translation_catalog/extractor/ruby_spec.rb create mode 100644 spec/lib/generators/translation_catalog/translation_catalog_generator_spec.rb create mode 100644 spec/models/translation/translation_synchronizes_from_po_spec.rb diff --git a/.coffeelint/rules/detect_translatable_string.coffee b/.coffeelint/rules/detect_translatable_string.coffee new file mode 100644 index 000000000..a0d30a47b --- /dev/null +++ b/.coffeelint/rules/detect_translatable_string.coffee @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1553bb770..e3b376028 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.gitlab/ci/pre.yml b/.gitlab/ci/pre.yml index 8b02c64bd..0b667323b 100644 --- a/.gitlab/ci/pre.yml +++ b/.gitlab/ci/pre.yml @@ -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 diff --git a/.overcommit.yml b/.overcommit.yml index e480513fe..e6503b4f6 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -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 - diff --git a/.rubocop/cop/zammad/detect_translatable_string.rb b/.rubocop/cop/zammad/detect_translatable_string.rb new file mode 100644 index 000000000..d56f4a702 --- /dev/null +++ b/.rubocop/cop/zammad/detect_translatable_string.rb @@ -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 diff --git a/.rubocop/default.yml b/.rubocop/default.yml index f08267c10..c3725f7ed 100644 --- a/.rubocop/default.yml +++ b/.rubocop/default.yml @@ -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" diff --git a/.rubocop/rubocop_zammad.rb b/.rubocop/rubocop_zammad.rb index f3e8764cd..9e8817085 100644 --- a/.rubocop/rubocop_zammad.rb +++ b/.rubocop/rubocop_zammad.rb @@ -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' diff --git a/Gemfile b/Gemfile index 6ce41c930..8c3226e5f 100644 --- a/Gemfile +++ b/Gemfile @@ -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 diff --git a/Gemfile.lock b/Gemfile.lock index ffd337b36..a66972b3f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 diff --git a/app/assets/javascripts/app/controllers/_application_controller/_base.coffee b/app/assets/javascripts/app/controllers/_application_controller/_base.coffee index 93a5c2c87..fb786a20f 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/_base.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/_base.coffee @@ -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: -> diff --git a/app/assets/javascripts/app/controllers/_application_controller/_modal.coffee b/app/assets/javascripts/app/controllers/_application_controller/_modal.coffee index e7302425d..878fb29ff 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/_modal.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/_modal.coffee @@ -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() diff --git a/app/assets/javascripts/app/controllers/_application_controller/form.coffee b/app/assets/javascripts/app/controllers/_application_controller/form.coffee index f19423c96..b923a64b2 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/form.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/form.coffee @@ -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: '»' 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 diff --git a/app/assets/javascripts/app/controllers/_application_controller/generic_description.coffee b/app/assets/javascripts/app/controllers/_application_controller/generic_description.coffee index d48f18b11..abe6831dc 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/generic_description.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/generic_description.coffee @@ -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)) diff --git a/app/assets/javascripts/app/controllers/_application_controller/generic_destroy_confirm.coffee b/app/assets/javascripts/app/controllers/_application_controller/generic_destroy_confirm.coffee index 6baba4c44..35f34c211 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/generic_destroy_confirm.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/generic_destroy_confirm.coffee @@ -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: -> diff --git a/app/assets/javascripts/app/controllers/_application_controller/generic_edit.coffee b/app/assets/javascripts/app/controllers/_application_controller/generic_edit.coffee index 568737b6f..eb01ec94b 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/generic_edit.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/generic_edit.coffee @@ -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!')) ) diff --git a/app/assets/javascripts/app/controllers/_application_controller/generic_error_modal.coffee b/app/assets/javascripts/app/controllers/_application_controller/generic_error_modal.coffee index ac2ee345c..4f094a063 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/generic_error_modal.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/generic_error_modal.coffee @@ -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 diff --git a/app/assets/javascripts/app/controllers/_application_controller/generic_history.coffee b/app/assets/javascripts/app/controllers/_application_controller/generic_history.coffee index 29f29905a..f603d77c6 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/generic_history.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/generic_history.coffee @@ -5,7 +5,7 @@ class App.GenericHistory extends App.ControllerModal buttonClose: true buttonCancel: false buttonSubmit: false - head: 'History' + head: __('History') shown: false constructor: -> diff --git a/app/assets/javascripts/app/controllers/_application_controller/generic_new.coffee b/app/assets/javascripts/app/controllers/_application_controller/generic_new.coffee index e965d3895..9f0023e45 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/generic_new.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/generic_new.coffee @@ -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!')) ) diff --git a/app/assets/javascripts/app/controllers/_application_controller/reorder_modal.coffee b/app/assets/javascripts/app/controllers/_application_controller/reorder_modal.coffee index 59957b9d2..0419c69eb 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/reorder_modal.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/reorder_modal.coffee @@ -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() - diff --git a/app/assets/javascripts/app/controllers/_application_controller/table.coffee b/app/assets/javascripts/app/controllers/_application_controller/table.coffee index 3056a6a32..30c878ade 100644 --- a/app/assets/javascripts/app/controllers/_application_controller/table.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller/table.coffee @@ -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' diff --git a/app/assets/javascripts/app/controllers/_channel/_email_filter.coffee b/app/assets/javascripts/app/controllers/_channel/_email_filter.coffee index 17ec6f63a..a270f131b 100644 --- a/app/assets/javascripts/app/controllers/_channel/_email_filter.coffee +++ b/app/assets/javascripts/app/controllers/_channel/_email_filter.coffee @@ -11,7 +11,7 @@ class App.ChannelEmailFilter extends App.Controller template = $( '
' + App.i18n.translateContent('New') + '
' ) - 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 diff --git a/app/assets/javascripts/app/controllers/_channel/_email_signature.coffee b/app/assets/javascripts/app/controllers/_channel/_email_signature.coffee index 5948003d8..c5f83bb06 100644 --- a/app/assets/javascripts/app/controllers/_channel/_email_signature.coffee +++ b/app/assets/javascripts/app/controllers/_channel/_email_signature.coffee @@ -11,11 +11,11 @@ class App.ChannelEmailSignature extends App.Controller template = $( '
' + App.i18n.translateContent('New') + '
' ) - 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!')) ) diff --git a/app/assets/javascripts/app/controllers/_channel/chat.coffee b/app/assets/javascripts/app/controllers/_channel/chat.coffee index cbea22b48..5f29a4ac0 100644 --- a/app/assets/javascripts/app/controllers/_channel/chat.coffee +++ b/app/assets/javascripts/app/controllers/_channel/chat.coffee @@ -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