diff --git a/.circleci/config.yml b/.circleci/config.yml index 18a7a0af9..635f60c90 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,7 +7,7 @@ defaults: &defaults working_directory: ~/build docker: # specify the version you desire here - - image: circleci/ruby:2.7.1-node-browsers + - image: circleci/ruby:2.7.2-node-browsers # Specify service dependencies here if necessary # CircleCI maintains a library of pre-built images diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..b1bef7b28 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,51 @@ +# pre-build stage +ARG VARIANT=2.7 +FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT} + +# Update args in docker-compose.yaml to set the UID/GID of the "vscode" user. +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \ + groupmod --gid $USER_GID vscode \ + && usermod --uid $USER_UID --gid $USER_GID vscode \ + && chmod -R $USER_UID:$USER_GID /home/vscode; \ + fi + +# [Option] Install Node.js +ARG INSTALL_NODE="true" +ARG NODE_VERSION="lts/*" +RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + + +# tmux is for overmind +# TODO : install foreman in future +# packages: postgresql-server-dev-all +# may be postgres in same machine + +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends \ + libssl-dev \ + tar \ + tzdata \ + postgresql-client \ + yarn \ + git \ + imagemagick \ + tmux \ + zsh + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 + + +# Do the set up required for chatwoot app +WORKDIR /workspace +COPY . /workspace + +# TODO: figure out installing rvm +# RUN rvm install +COPY Gemfile Gemfile.lock ./ +RUN gem install bundler +RUN bundle install +COPY package.json yarn.lock ./ +RUN yarn install diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..5f878e5e0 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +{ + "name": "Chatwoot Development Codespace", + "service": "app", + "dockerComposeFile": "docker-compose.yml", + + "settings": { + "terminal.integrated.shell.linux": "/bin/zsh" + }, + + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "rebornix.Ruby", + "misogi.ruby-rubocop", + "wingrunr21.vscode-ruby" + ], + + + // TODO: figure whether we can get all this ports work properly + + // 3000 rails + // 3035 webpacker + // 5432 postgres + // 6379 redis + // 1025,8025 mailhog + "forwardPorts": [5432, 6379, 1025, 8025], + //your application may need to listen on all interfaces (0.0.0.0) not just localhost for it to be available externally. Defaults to [] + "appPort": [3000, 3035], + + // Use 'postCreateCommand' to run commands after the container is created. + // #TODO: can we move logic of copy env file into dockerfile ? + "postCreateCommand": "cp .env.example .env", +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 000000000..6a2ffbea6 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,56 @@ +# https://github.com/microsoft/vscode-dev-containers/blob/master/containers/python-3-postgres/.devcontainer/docker-compose.yml +# https://github.com/microsoft/vscode-dev-containers/blob/master/containers/ruby-rails/.devcontainer/devcontainer.json +# + +version: '3' + +services: + app: + build: + context: .. + dockerfile: .devcontainer/Dockerfile + args: + # Update 'VARIANT' to pick a Ruby version: 2, 2.7, 2.6, 2.5 + VARIANT: 2.7 + # [Choice] Install Node.js + INSTALL_NODE: "true" + NODE_VERSION: "lts/*" + # On Linux, you may need to update USER_UID and USER_GID below if not your local UID is not 1000. + USER_UID: 1000 + USER_GID: 1000 + + volumes: + - ..:/workspace:cached + + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + + db: + image: postgres:latest + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: postgres + POSTGRES_DB: postgres + POSTGRES_PASSWORD: postgres + + redis: + image: redis:latest + restart: unless-stopped + network_mode: service:db + volumes: + - redis-data:/data + + mailhog: + restart: unless-stopped + image: mailhog/mailhog + network_mode: service:db + +volumes: + postgres-data: + redis-data: + \ No newline at end of file diff --git a/.env.example b/.env.example index 405548032..4fe8ec3c8 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,10 @@ SECRET_KEY_BASE=replace_with_lengthy_secure_hex # Replace with the URL you are planning to use for your app FRONTEND_URL=http://0.0.0.0:3000 +# If the variable is set, all non-authenticated pages would fallback to the default locale. +# Whenever a new account is created, the default language will be DEFAULT_LOCALE instead of en +# DEFAULT_LOCALE=en + # If you plan to use CDN for your assets, set Asset CDN Host ASSET_CDN_HOST= @@ -129,5 +133,3 @@ USE_INBOX_AVATAR_FOR_BOT=true ## Development Only Config # if you want to use letter_opener for local emails # LETTER_OPENER=true - - diff --git a/.ruby-version b/.ruby-version index 860487ca1..37c2961c2 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.1 +2.7.2 diff --git a/Gemfile b/Gemfile index ba24662fb..88f2fc75c 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -ruby '2.7.1' +ruby '2.7.2' ##-- base gems for rails --## gem 'rack-cors', require: 'rack/cors' diff --git a/Gemfile.lock b/Gemfile.lock index 8b9ea88b3..d6692e402 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -325,7 +325,7 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (4.0.5) - puma (4.3.5) + puma (4.3.6) nio4r (~> 2.0) pundit (2.1.0) activesupport (>= 3.0.0) @@ -638,7 +638,7 @@ DEPENDENCIES wisper (= 2.0.0) RUBY VERSION - ruby 2.7.1p83 + ruby 2.7.2p137 BUNDLED WITH 2.1.4 diff --git a/README.md b/README.md index 0eb841bb8..a27754a70 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Now, a failed project is back on track and the prospects are looking great. The ### Features -Chatwoot gives an integrated view of conversations happening in different communication channels. +Chatwoot gives an integrated view of conversations happening in different communication channels. It supports the following conversation channels: @@ -44,7 +44,7 @@ It supports the following conversation channels: - **API Channel**: Build custom communication channels using our API channel. - **Email (beta)**: Forward all your email queries to Chatwoot and view it in our integrated dashboard. -Other features include: +Other features include: - **Multi-brand inboxes**: Manage multiple brands or pages using a single dashboard. - **Private notes**: Inter team communication is possible using private notes in a conversation. @@ -62,12 +62,16 @@ Other features include: Detailed documentation is available at [www.chatwoot.com/help-center](https://www.chatwoot.com/help-center). +### Translation process + +The translation process for Chatwoot web and mobile app is managed at [https://translate.chatwoot.com](https://translate.chatwoot.com) using Crowdin. Please read the [translation guide](https://www.chatwoot/docs/contributing/translating-chatwoot-to-your-language) for contributing to Chatwoot. + --- ### Branching model We use the [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) branching model. The base branch is `develop`. -If you are looking for a stable version, please use the `master` or tags labelled as `v1.x.x`. +If you are looking for a stable version, please use the `master` or tags labelled as `v1.x.x`. --- diff --git a/app.json b/app.json index 794f8de70..b321e9b89 100644 --- a/app.json +++ b/app.json @@ -34,6 +34,10 @@ "web": { "quantity": 1, "size": "FREE" + }, + "worker": { + "quantity": 1, + "size": "FREE" } }, "image": "heroku/ruby", diff --git a/app/builders/account_builder.rb b/app/builders/account_builder.rb index ae6db3b4e..3d81a8e68 100644 --- a/app/builders/account_builder.rb +++ b/app/builders/account_builder.rb @@ -40,7 +40,7 @@ class AccountBuilder end def create_account - @account = Account.create!(name: @account_name) + @account = Account.create!(name: @account_name, locale: I18n.locale) Current.account = @account end @@ -67,7 +67,8 @@ class AccountBuilder end def create_user - password = Time.now.to_i + password = SecureRandom.alphanumeric(12) + @user = User.new(email: @email, password: password, password_confirmation: password, diff --git a/app/controllers/api/v1/accounts/base_controller.rb b/app/controllers/api/v1/accounts/base_controller.rb index d0ae4c31d..ef52206db 100644 --- a/app/controllers/api/v1/accounts/base_controller.rb +++ b/app/controllers/api/v1/accounts/base_controller.rb @@ -1,5 +1,7 @@ class Api::V1::Accounts::BaseController < Api::BaseController + include SwitchLocale before_action :current_account + around_action :switch_locale_using_account_locale private diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index d476c744c..2ea843f9b 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -14,7 +14,10 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController def search render json: { error: 'Specify search string with parameter q' }, status: :unprocessable_entity if params[:q].blank? && return - contacts = resolved_contacts.where('name LIKE :search OR email LIKE :search', search: "%#{params[:q]}%") + contacts = resolved_contacts.where( + 'name ILIKE :search OR email ILIKE :search OR phone_number ILIKE :search', + search: "%#{params[:q]}%" + ) @contacts_count = contacts.count @contacts = fetch_contact_last_seen_at(contacts) end @@ -32,7 +35,6 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController def create ActiveRecord::Base.transaction do @contact = Current.account.contacts.new(contact_params) - set_ip @contact.save! @contact_inbox = build_contact_inbox end @@ -40,7 +42,6 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController def update @contact.assign_attributes(contact_update_params) - set_ip @contact.save! rescue ActiveRecord::RecordInvalid => e render json: { @@ -96,11 +97,4 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController def fetch_contact @contact = Current.account.contacts.includes(contact_inboxes: [:inbox]).find(params[:id]) end - - def set_ip - return if @contact.account.feature_enabled?('ip_lookup') - - @contact[:additional_attributes][:created_at_ip] ||= request.remote_ip - @contact[:additional_attributes][:updated_at_ip] = request.remote_ip - end end diff --git a/app/controllers/api/v1/accounts/integrations/slack_controller.rb b/app/controllers/api/v1/accounts/integrations/slack_controller.rb index 1002f5752..5ded9f512 100644 --- a/app/controllers/api/v1/accounts/integrations/slack_controller.rb +++ b/app/controllers/api/v1/accounts/integrations/slack_controller.rb @@ -2,13 +2,15 @@ class Api::V1::Accounts::Integrations::SlackController < Api::V1::Accounts::Base before_action :fetch_hook, only: [:update, :destroy] def create - builder = Integrations::Slack::HookBuilder.new( - account: Current.account, - code: params[:code], - inbox_id: params[:inbox_id] - ) - @hook = builder.perform - create_chatwoot_slack_channel + ActiveRecord::Base.transaction do + builder = Integrations::Slack::HookBuilder.new( + account: Current.account, + code: params[:code], + inbox_id: params[:inbox_id] + ) + @hook = builder.perform + create_chatwoot_slack_channel + end end def update diff --git a/app/controllers/api/v1/widget/base_controller.rb b/app/controllers/api/v1/widget/base_controller.rb index f8b2e2c96..0227bd0ba 100644 --- a/app/controllers/api/v1/widget/base_controller.rb +++ b/app/controllers/api/v1/widget/base_controller.rb @@ -1,4 +1,6 @@ class Api::V1::Widget::BaseController < ApplicationController + include SwitchLocale + before_action :set_web_widget before_action :set_contact diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 58246bb73..8fbc8ff45 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,7 @@ class ApplicationController < ActionController::Base include DeviseTokenAuth::Concerns::SetUserByToken include Pundit + include SwitchLocale protect_from_forgery with: :null_session @@ -60,28 +61,6 @@ class ApplicationController < ActionController::Base render json: exception.to_hash, status: exception.http_status end - def locale_from_params - I18n.available_locales.map(&:to_s).include?(params[:locale]) ? params[:locale] : nil - end - - def locale_from_account(account) - return unless account - - I18n.available_locales.map(&:to_s).include?(account.locale) ? account.locale : nil - end - - def switch_locale(&action) - # priority is for locale set in query string (mostly for widget/from js sdk) - locale ||= locale_from_params - # if local is not set in param, lets try account - locale ||= locale_from_account(@current_account) - # if nothing works we rely on default locale - locale ||= I18n.default_locale - # ensure locale won't bleed into other requests - # https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests - I18n.with_locale(locale, &action) - end - def pundit_user { user: Current.user, diff --git a/app/controllers/concerns/switch_locale.rb b/app/controllers/concerns/switch_locale.rb new file mode 100644 index 000000000..5f308b8ab --- /dev/null +++ b/app/controllers/concerns/switch_locale.rb @@ -0,0 +1,42 @@ +module SwitchLocale + extend ActiveSupport::Concern + + private + + def switch_locale(&action) + # priority is for locale set in query string (mostly for widget/from js sdk) + locale ||= locale_from_params + # if locale is not set in account, let's use DEFAULT_LOCALE env variable + locale ||= locale_from_env_variable + set_locale(locale, &action) + end + + def switch_locale_using_account_locale(&action) + locale = locale_from_account(@current_account) + set_locale(locale, &action) + end + + def set_locale(locale, &action) + # if locale is empty, use default_locale + locale ||= I18n.default_locale + # Ensure locale won't bleed into other requests + # https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests + I18n.with_locale(locale, &action) + end + + def locale_from_params + I18n.available_locales.map(&:to_s).include?(params[:locale]) ? params[:locale] : nil + end + + def locale_from_account(account) + return unless account + + I18n.available_locales.map(&:to_s).include?(account.locale) ? account.locale : nil + end + + def locale_from_env_variable + return unless ENV.fetch('DEFAULT_LOCALE', nil) + + I18n.available_locales.map(&:to_s).include?(ENV.fetch('DEFAULT_LOCALE')) ? ENV.fetch('DEFAULT_LOCALE') : nil + end +end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 27fa7fb86..52f392add 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,6 +1,5 @@ class DashboardController < ActionController::Base before_action :set_global_config - layout 'vueapp' def index; end diff --git a/app/controllers/devise_overrides/sessions_controller.rb b/app/controllers/devise_overrides/sessions_controller.rb index 9ebb3b435..289684f17 100644 --- a/app/controllers/devise_overrides/sessions_controller.rb +++ b/app/controllers/devise_overrides/sessions_controller.rb @@ -2,8 +2,38 @@ class DeviseOverrides::SessionsController < ::DeviseTokenAuth::SessionsControlle # Prevent session parameter from being passed # Unpermitted parameter: session wrap_parameters format: [] + before_action :process_sso_auth_token, only: [:create] + + def create + # Authenticate user via the temporary sso auth token + if params[:sso_auth_token].present? && @resource.present? + authenticate_resource_with_sso_token + yield @resource if block_given? + render_create_success + else + super + end + end def render_create_success render partial: 'devise/auth.json', locals: { resource: @resource } end + + private + + def authenticate_resource_with_sso_token + @token = @resource.create_token + @resource.save + + sign_in(:user, @resource, store: false, bypass: false) + # invalidate the token after the user is signed in + @resource.invalidate_sso_auth_token(params[:sso_auth_token]) + end + + def process_sso_auth_token + return if params[:email].blank? + + user = User.find_by(email: params[:email]) + @resource = user if user&.valid_sso_auth_token?(params[:sso_auth_token]) + end end diff --git a/app/controllers/widgets_controller.rb b/app/controllers/widgets_controller.rb index 957203f16..da634875a 100644 --- a/app/controllers/widgets_controller.rb +++ b/app/controllers/widgets_controller.rb @@ -41,13 +41,21 @@ class WidgetsController < ActionController::Base def build_contact return if @contact.present? - contact_inbox = @web_widget.create_contact_inbox + contact_inbox = @web_widget.create_contact_inbox(additional_attributes) @contact = contact_inbox.contact payload = { source_id: contact_inbox.source_id, inbox_id: @web_widget.inbox.id } @token = ::Widget::TokenService.new(payload: payload).generate_token end + def additional_attributes + if @web_widget.inbox.account.feature_enabled?('ip_lookup') + { created_at_ip: request.remote_ip } + else + {} + end + end + def permitted_params params.permit(:website_token, :cw_conversation) end diff --git a/app/javascript/dashboard/App.vue b/app/javascript/dashboard/App.vue index bef1323e2..2d196af04 100644 --- a/app/javascript/dashboard/App.vue +++ b/app/javascript/dashboard/App.vue @@ -8,7 +8,6 @@ diff --git a/app/javascript/dashboard/routes/login/Login.vue b/app/javascript/dashboard/routes/login/Login.vue index a905864f4..1dfa6625d 100644 --- a/app/javascript/dashboard/routes/login/Login.vue +++ b/app/javascript/dashboard/routes/login/Login.vue @@ -13,7 +13,7 @@
-
+
+
" + + " end - def create_contact_inbox + def create_contact_inbox(additional_attributes = {}) ActiveRecord::Base.transaction do - contact = inbox.account.contacts.create!(name: ::Haikunator.haikunate(1000)) + contact = inbox.account.contacts.create!( + name: ::Haikunator.haikunate(1000), + additional_attributes: additional_attributes + ) contact_inbox = ::ContactInbox.create!( contact_id: contact.id, inbox_id: inbox.id, diff --git a/app/models/concerns/sso_authenticatable.rb b/app/models/concerns/sso_authenticatable.rb new file mode 100644 index 000000000..aca28b663 --- /dev/null +++ b/app/models/concerns/sso_authenticatable.rb @@ -0,0 +1,23 @@ +module SsoAuthenticatable + extend ActiveSupport::Concern + + def generate_sso_auth_token + token = SecureRandom.hex(32) + ::Redis::Alfred.setex(sso_token_key(token), true, 5.minutes) + token + end + + def invalidate_sso_auth_token(token) + ::Redis::Alfred.delete(sso_token_key(token)) + end + + def valid_sso_auth_token?(token) + ::Redis::Alfred.get(sso_token_key(token)).present? + end + + private + + def sso_token_key(token) + format(::Redis::RedisKeys::USER_SSO_AUTH_TOKEN, user_id: id, token: token) + end +end diff --git a/app/models/contact.rb b/app/models/contact.rb index c6f6a00a4..82ece1176 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -38,9 +38,8 @@ class Contact < ApplicationRecord has_many :messages, as: :sender, dependent: :destroy before_validation :prepare_email_attribute - after_create_commit :dispatch_create_event + after_create_commit :dispatch_create_event, :ip_lookup after_update_commit :dispatch_update_event - after_commit :ip_lookup def get_source_id(inbox_id) contact_inboxes.find_by!(inbox_id: inbox_id).source_id diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 53e30daea..3a90d1f82 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -98,7 +98,7 @@ class Conversation < ApplicationRecord end def muted? - !Redis::Alfred.get(mute_key).nil? + Redis::Alfred.get(mute_key).present? end def lock! @@ -287,7 +287,7 @@ class Conversation < ApplicationRecord end def mute_key - format('CONVERSATION::%d::MUTED', id: id) + format(Redis::RedisKeys::CONVERSATION_MUTE_KEY, id: id) end def mute_period diff --git a/app/models/integrations/app.rb b/app/models/integrations/app.rb index f5e783f04..7861cc5c8 100644 --- a/app/models/integrations/app.rb +++ b/app/models/integrations/app.rb @@ -10,11 +10,11 @@ class Integrations::App end def name - params[:name] + I18n.t("integration_apps.#{params[:i18n_key]}.name") end def description - params[:description] + I18n.t("integration_apps.#{params[:i18n_key]}.description") end def logo diff --git a/app/models/user.rb b/app/models/user.rb index 88c906431..19b17731c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -44,6 +44,7 @@ class User < ApplicationRecord include Pubsubable include Rails.application.routes.url_helpers include Reportable + include SsoAuthenticatable devise :database_authenticatable, :registerable, diff --git a/app/views/api/v1/conversations/partials/_conversation.json.jbuilder b/app/views/api/v1/conversations/partials/_conversation.json.jbuilder index 729b52379..517f31054 100644 --- a/app/views/api/v1/conversations/partials/_conversation.json.jbuilder +++ b/app/views/api/v1/conversations/partials/_conversation.json.jbuilder @@ -3,7 +3,7 @@ json.meta do json.partial! 'api/v1/models/contact.json.jbuilder', resource: conversation.contact end json.channel conversation.inbox.try(:channel_type) - if conversation.assignee + if conversation.assignee&.account json.assignee do json.partial! 'api/v1/models/agent.json.jbuilder', resource: conversation.assignee end @@ -14,7 +14,7 @@ json.id conversation.display_id if conversation.unread_incoming_messages.count.zero? json.messages [conversation.messages.includes([{ attachments: [{ file_attachment: [:blob] }] }]).last.try(:push_event_data)] else - json.messages conversation.unread_messages.includes([:user, { attachments: [{ file_attachment: [:blob] }] }]).map(&:push_event_data) + json.messages conversation.unread_messages.includes([:user, { attachments: [{ file_attachment: [:blob] }] }]).last(10).map(&:push_event_data) end json.inbox_id conversation.inbox_id diff --git a/app/views/api/v2/accounts/reports/agents.csv.erb b/app/views/api/v2/accounts/reports/agents.csv.erb index 2986c6491..3675c0a66 100644 --- a/app/views/api/v2/accounts/reports/agents.csv.erb +++ b/app/views/api/v2/accounts/reports/agents.csv.erb @@ -8,5 +8,6 @@ until: params[:until] }).summary %> <% row = [ agent.name, agent_report[:conversations_count], (agent_report[:avg_first_response_time]/60).to_i, (agent_report[:avg_resolution_time]/60).to_i ] %> -<%= CSV.generate_line row %> -<% end %> \ No newline at end of file + <%= CSV.generate_line row %> +<% end %> +<%= CSV.generate_line [I18n.t('reports.period', { since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s') })] %> diff --git a/app/views/layouts/vueapp.html.erb b/app/views/layouts/vueapp.html.erb index c2dae5279..40d9b7a1b 100644 --- a/app/views/layouts/vueapp.html.erb +++ b/app/views/layouts/vueapp.html.erb @@ -35,7 +35,8 @@ <% if ENV['VAPID_PUBLIC_KEY'] %> vapidPublicKey: new Uint8Array(<%= Base64.urlsafe_decode64(ENV['VAPID_PUBLIC_KEY']).bytes %>), <% end %> - enabledLanguages: <%= available_locales_with_name.to_json.html_safe %> + enabledLanguages: <%= available_locales_with_name.to_json.html_safe %>, + selectedLocale: '<%= I18n.locale %>' } window.globalConfig = <%= raw @global_config.to_json %> diff --git a/config/app.yml b/config/app.yml index c6fcd07fc..c360ac4dd 100644 --- a/config/app.yml +++ b/config/app.yml @@ -1,5 +1,5 @@ shared: &shared - version: '1.10.0' + version: '1.11.0' development: <<: *shared diff --git a/config/initializers/languages.rb b/config/initializers/languages.rb index 1b0e32662..92ab876e0 100644 --- a/config/initializers/languages.rb +++ b/config/initializers/languages.rb @@ -10,7 +10,7 @@ LANGUAGES_CONFIG = { 5 => { name: 'हिन्दी (hi)', iso_639_3_code: 'hin', iso_639_1_code: 'hi', enabled: false }, 6 => { name: 'Italiano (it)', iso_639_3_code: 'ita', iso_639_1_code: 'it', enabled: true }, 7 => { name: '日本語 (ja)', iso_639_3_code: 'jpn', iso_639_1_code: 'ja', enabled: true }, - 8 => { name: '한국어 (ko)', iso_639_3_code: 'kor', iso_639_1_code: 'ko', enabled: false }, + 8 => { name: '한국어 (ko)', iso_639_3_code: 'kor', iso_639_1_code: 'ko', enabled: true }, 9 => { name: 'Português (pt)', iso_639_3_code: 'por', iso_639_1_code: 'pt', enabled: true }, 10 => { name: 'русский (ru)', iso_639_3_code: 'rus', iso_639_1_code: 'ru', enabled: true }, 11 => { name: '中文 (zh)', iso_639_3_code: 'zho', iso_639_1_code: 'zh', enabled: false }, @@ -23,7 +23,12 @@ LANGUAGES_CONFIG = { 18 => { name: 'தமிழ் (ta)', iso_639_3_code: 'tam', iso_639_1_code: 'ta', enabled: true }, 19 => { name: 'فارسی (fa)', iso_639_3_code: 'fas', iso_639_1_code: 'fa', enabled: true }, 20 => { name: '中文 (台湾) (zh-TW)', iso_639_3_code: 'zho', iso_639_1_code: 'zh_TW', enabled: true }, - 21 => { name: 'Tiếng Việt (vi)', iso_639_3_code: 'vie', iso_639_1_code: 'vi', enabled: true } + 21 => { name: 'Tiếng Việt (vi)', iso_639_3_code: 'vie', iso_639_1_code: 'vi', enabled: true }, + 22 => { name: 'dansk (da)', iso_639_3_code: 'dan', iso_639_1_code: 'da', enabled: true }, + 23 => { name: 'Türkçe (tr)', iso_639_3_code: 'tur', iso_639_1_code: 'tr', enabled: true }, + 24 => { name: 'čeština (cs)', iso_639_3_code: 'ces', iso_639_1_code: 'cs', enabled: true }, + 25 => { name: 'suomi, suomen kieli (fi)', iso_639_3_code: 'fin', iso_639_1_code: 'fi', enabled: true }, + 26 => { name: 'Bahasa Indonesia (id)', iso_639_3_code: 'ind', iso_639_1_code: 'id', enabled: true } }.filter { |_key, val| val[:enabled] }.freeze Rails.configuration.i18n.available_locales = LANGUAGES_CONFIG.map { |_index, lang| lang[:iso_639_1_code].to_sym } diff --git a/config/integration/apps.yml b/config/integration/apps.yml index e6b349698..d58254bde 100644 --- a/config/integration/apps.yml +++ b/config/integration/apps.yml @@ -1,12 +1,10 @@ slack: id: slack - name: Slack logo: slack.png - description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + i18n_key: slack action: https://slack.com/oauth/v2/authorize?scope=commands,chat:write,channels:read,channels:manage,channels:join,groups:write,im:write,mpim:write,users:read,users:read.email,chat:write.customize,channels:history,groups:history,mpim:history,im:history webhooks: id: webhook - name: Webhooks logo: cable.svg - description: Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks. + i18n_key: webhooks action: /webhook diff --git a/config/locales/ar.yml b/config/locales/ar.yml index df6ec9182..6e2e8e5eb 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -27,6 +27,8 @@ ar: invalid_email: لقد قمت بإدخال عنوان بريد إلكتروني غير صالح email_already_exists: "لقد قمت بالفعل بتسجيل حساب سابقاً بالعنوان %{email}" failed: فشلت عملية التسجيل + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ ar: reply: email_subject: "رسائل جديدة في هذه المحادثة" transcript_subject: "نص المحادثة" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 1db2a51c9..55ca5653e 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -27,6 +27,8 @@ ca: invalid_email: Heu introduït un correu electrònic no vàlid email_already_exists: "Ja us heu registrat amb el compte %{email}" failed: El registre ha fallat + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ ca: reply: email_subject: "Missatges nous en aquesta conversa" transcript_subject: "Transcripció de conversa" + integration_apps: + slack: + name: "Slack" + description: "Slack és una eina de xat que uneix tota la teva comunicació en un sol lloc. Integrant Slack, podràs rebre notificacions de totes les noves conversacions a la teva compte directament a dins l'Slack." + webhooks: + name: "Webhooks" + description: "Els esdeveniments de Webhook us proporcionen informació en temps real sobre el que passa al vostre compte de Chatwoot. Podeu utilitzar els webhooks per comunicar els esdeveniments a les vostres aplicacions preferides com Slack o Github. Feu clic a Configura per configurar els enllaços web." diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 4c230eb4a..992b98b1d 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -27,6 +27,8 @@ cs: invalid_email: Zadali jste neplatný e-mail email_already_exists: "Již jste se zaregistrovali k účtu s %{email}" failed: Registrace se nezdařila + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ cs: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/da.yml b/config/locales/da.yml index 0b1d69118..144259dda 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -17,35 +17,44 @@ #To learn more, please read the Rails Internationalization guide #available at https://guides.rubyonrails.org/i18n.html. da: - hello: "Hello world" + hello: "Hej verden" messages: - reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions. - reset_password_failure: Uh ho! We could not find any user with the specified email. + reset_password_success: Woot! Anmodning om nulstilling af adgangskode er vellykket. Tjek din mail for instruktioner. + reset_password_failure: Åh nej! Vi kunne ikke finde nogen bruger med den angivne e-mail. errors: signup: - disposable_email: We do not allow disposable emails - invalid_email: You have entered an invalid email - email_already_exists: "You have already signed up for an account with %{email}" - failed: Signup failed + disposable_email: Vi tillader ikke engangs e-mails + invalid_email: Du har indtastet en ugyldig e-mail + email_already_exists: "Du har allerede tilmeldt dig en konto med %{email}" + failed: Tilmelding mislykkedes + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: - resolved: "Conversation was marked resolved by %{user_name}" - open: "Conversation was reopened by %{user_name}" - auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity" + resolved: "Samtalen blev markeret som løst af %{user_name}" + open: "Samtalen blev genåbnet af %{user_name}" + auto_resolved: "Samtalen blev markeret som løst af systemet på grund af %{duration} dages inaktivitet" assignee: - self_assigned: "%{user_name} self-assigned this conversation" - assigned: "Assigned to %{assignee_name} by %{user_name}" - removed: "Conversation unassigned by %{user_name}" + self_assigned: "%{user_name} selv-tildelte denne samtale" + assigned: "Tildelt %{assignee_name} af %{user_name}" + removed: "Samtale fjernet tildeling af %{user_name}" labels: - added: "%{user_name} added %{labels}" - removed: "%{user_name} removed %{labels}" - muted: "%{user_name} has muted the conversation" - unmuted: "%{user_name} has unmuted the conversation" + added: "%{user_name} tilføjede %{labels}" + removed: "%{user_name} fjernede %{labels}" + muted: "%{user_name} har slukket for samtalen" + unmuted: "%{user_name} har genaktiveret samtalen" templates: - greeting_message_body: "%{account_name} typically replies in a few hours." - ways_to_reach_you_message_body: "Give the team a way to reach you." - email_input_box_message_body: "Get notified by email" + greeting_message_body: "%{account_name} svarer typisk på et par timer." + ways_to_reach_you_message_body: "Giv teamet en måde at kontakte dig på." + email_input_box_message_body: "Få besked via e-mail" reply: - email_subject: "New messages on this conversation" - transcript_subject: "Conversation Transcript" + email_subject: "Nye beskeder i denne samtale" + transcript_subject: "Samtaleudskrift" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/de.yml b/config/locales/de.yml index 8af185395..a5e71bd86 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -27,6 +27,8 @@ de: invalid_email: Sie haben eine ungültige E-Mail-Adresse eingegeben email_already_exists: "Sie haben sich bereits für ein Konto bei %{email} angemeldet." failed: Anmeldung gescheitert + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ de: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/devise.da.yml b/config/locales/devise.da.yml index fe5fb7196..dfa9a8613 100644 --- a/config/locales/devise.da.yml +++ b/config/locales/devise.da.yml @@ -2,60 +2,60 @@ da: devise: confirmations: - confirmed: "Your email address has been successfully confirmed." - send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." + confirmed: "Din e-mailadresse er blevet bekræftet." + send_instructions: "Du vil modtage en e-mail med instruktioner til, hvordan du bekræfter din e-mail-adresse om et par minutter." + send_paranoid_instructions: "Hvis din e-mailadresse findes i vores database, du vil modtage en e-mail med instruktioner til, hvordan du bekræfter din e-mail-adresse om et par minutter." failure: - already_authenticated: "You are already signed in." - inactive: "Your account is not activated yet." - invalid: "Invalid %{authentication_keys}/password or account is not verified yet." - locked: "Your account is locked." - last_attempt: "You have one more attempt before your account is locked." - not_found_in_database: "Invalid %{authentication_keys} or password." - timeout: "Your session expired. Please sign in again to continue." - unauthenticated: "You need to sign in or sign up before continuing." - unconfirmed: "You have to confirm your email address before continuing." + already_authenticated: "Du er allerede logget ind." + inactive: "Din konto er ikke aktiveret endnu." + invalid: "Ugyldig %{authentication_keys}/password eller konto er ikke bekræftet endnu." + locked: "Din konto er låst." + last_attempt: "Du har et forsøg mere, før din konto er låst." + not_found_in_database: "Ugyldig %{authentication_keys} eller adgangskode." + timeout: "Din session er udløbet. Log ind igen for at fortsætte." + unauthenticated: "Du skal logge ind eller tilmelde dig, før du fortsætter." + unconfirmed: "Du skal bekræfte din e-mailadresse, før du fortsætter." mailer: confirmation_instructions: - subject: "Confirmation Instructions" + subject: "Bekræftelsesinstruktioner" reset_password_instructions: - subject: "Reset password instructions" + subject: "Nulstil adgangskodeinstruktioner" unlock_instructions: - subject: "Unlock instructions" + subject: "Lås instruktioner op" password_change: - subject: "Password Changed" + subject: "Adgangskode Ændret" omniauth_callbacks: - failure: "Could not authenticate you from %{kind} because \"%{reason}\"." - success: "Successfully authenticated from %{kind} account." + failure: "Kunne ikke godkende dig fra %{kind} fordi \"%{reason}\"." + success: "Godkendt fra %{kind} -kontoen." passwords: - no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." - updated: "Your password has been changed successfully. You are now signed in." - updated_not_active: "Your password has been changed successfully." + no_token: "Du kan ikke få adgang til denne side uden at komme fra en e-mailen til nulstilling af adgangskoden. Hvis du kommer fra en e-mail til nulstilling af adgangskoden, skal du sørge for at bruge den fulde URL, der er angivet." + send_instructions: "Du vil om få minutter modtage en e-mail med instruktioner om, hvordan du nulstiller din adgangskode." + send_paranoid_instructions: "Hvis din e-mailadresse findes i vores database, vil du om få minutter modtage et link til genoprettelse af adgangskoden på din e-mailadresse." + updated: "Din adgangskode er blevet ændret. Du er nu logget ind." + updated_not_active: "Din adgangskode er blevet ændret." registrations: - destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." - signed_up: "Welcome! You have signed up successfully." - signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." - signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." - signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." - update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." - updated: "Your account has been updated successfully." + destroyed: "Farvel! Din konto er blevet annulleret. Vi håber snart at se dig igen." + signed_up: "Velkommen! Du er nu tilmeldt." + signed_up_but_inactive: "Du har tilmeldt dig. Vi kunne dog ikke logge dig på, fordi din konto endnu ikke er aktiveret." + signed_up_but_locked: "Du har tilmeldt dig. Vi kunne dog ikke logge dig ind, fordi din konto er låst." + signed_up_but_unconfirmed: "En besked med et bekræftelseslink er blevet sendt til din e-mailadresse. Følg linket for at aktivere din konto." + update_needs_confirmation: "Din konto er opdateret, men vi er nødt til at bekræfte din nye e-mailadresse. Tjek venligst din e-mail og følg linket for at bekræfte din nye e-mailadresse." + updated: "Din konto er blevet opdateret." sessions: - signed_in: "Signed in successfully." - signed_out: "Signed out successfully." - already_signed_out: "Signed out successfully." + signed_in: "Logget ind." + signed_out: "Logget ud." + already_signed_out: "Logget ud." unlocks: - send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." - send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." - unlocked: "Your account has been unlocked successfully. Please sign in to continue." + send_instructions: "Du vil modtage en e-mail med instruktioner til, hvordan du låser din konto op om et par minutter." + send_paranoid_instructions: "Hvis din konto findes, vil du modtage en e-mail med instruktioner til, hvordan du låser den op om et par minutter." + unlocked: "Din konto er blevet låst op. Log venligst ind for at fortsætte." errors: messages: - already_confirmed: "was already confirmed, please try signing in" - confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" - expired: "has expired, please request a new one" - not_found: "not found" - not_locked: "was not locked" + already_confirmed: "var allerede bekræftet, prøv venligst at logge ind" + confirmation_period_expired: "skal bekræftes inden for %{period}, anmod venligst om en ny" + expired: "er udløbet, anmod venligst om en ny" + not_found: "ikke fundet" + not_locked: "var ikke låst" not_saved: - one: "1 error prohibited this %{resource} from being saved:" - other: "%{count} errors prohibited this %{resource} from being saved:" + one: "1 fejl forhindrede denne %{resource} i at blive gemt:" + other: "%{count} fejl forhindrede denne %{resource} i at blive gemt:" diff --git a/config/locales/devise.fi.yml b/config/locales/devise.fi.yml index e2aa34829..9a6b213b5 100644 --- a/config/locales/devise.fi.yml +++ b/config/locales/devise.fi.yml @@ -2,60 +2,60 @@ fi: devise: confirmations: - confirmed: "Your email address has been successfully confirmed." - send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." + confirmed: "Sähköpostiosoitteesi on vahvistettu onnistuneesti." + send_instructions: "Saat sähköpostiviestin, jossa on ohjeet siitä, miten voit vahvistaa sähköpostiosoitteesi muutamassa minuutissa." + send_paranoid_instructions: "Jos sähköpostiosoitteesi on olemassa tietokannassamme, saat sähköpostiviestin, jossa on ohjeet siitä, miten voit vahvistaa sähköpostiosoitteesi muutamassa minuutissa." failure: - already_authenticated: "You are already signed in." - inactive: "Your account is not activated yet." - invalid: "Invalid %{authentication_keys}/password or account is not verified yet." - locked: "Your account is locked." - last_attempt: "You have one more attempt before your account is locked." - not_found_in_database: "Invalid %{authentication_keys} or password." - timeout: "Your session expired. Please sign in again to continue." - unauthenticated: "You need to sign in or sign up before continuing." - unconfirmed: "You have to confirm your email address before continuing." + already_authenticated: "Olet jo kirjautunut sisään." + inactive: "Tilisi ei ole vielä aktivoitu." + invalid: "Virheellinen %{authentication_keys}/salasana tai tiliä ei ole vielä vahvistettu." + locked: "Tilisi on lukittu." + last_attempt: "Sinulla on vielä yksi yritys ennen kuin tilisi lukitaan." + not_found_in_database: "Virheellinen %{authentication_keys} tai salasana." + timeout: "Istuntosi on vanhentunut. Ole hyvä ja kirjaudu sisään jatkaaksesi." + unauthenticated: "Sinun täytyy kirjautua sisään tai rekisteröityä ennen jatkamista." + unconfirmed: "Sinun on vahvistettava sähköpostiosoitteesi ennen jatkamista." mailer: confirmation_instructions: - subject: "Confirmation Instructions" + subject: "Vahvistusohjeet" reset_password_instructions: - subject: "Reset password instructions" + subject: "Salasanan nollausohjeet" unlock_instructions: - subject: "Unlock instructions" + subject: "Avausohjeet" password_change: - subject: "Password Changed" + subject: "Salasana vaihdettu" omniauth_callbacks: - failure: "Could not authenticate you from %{kind} because \"%{reason}\"." - success: "Successfully authenticated from %{kind} account." + failure: "Sinua ei voitu todentaa %{kind}-tililtä koska \"%{reason}\"." + success: "Onnistuneesti todennettu %{kind}-tililtä." passwords: - no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." - updated: "Your password has been changed successfully. You are now signed in." - updated_not_active: "Your password has been changed successfully." + no_token: "Et voi käyttää tätä sivua ilman salasanan nollaussähköpostia. Jos tulet sivulle sähköpostin linkin kautta, varmista, että olet käyttänyt koko URL-osoite." + send_instructions: "Saat sähköpostiviestin, jossa on ohjeet siitä, miten salasanasi palautetaan muutamassa minuutissa." + send_paranoid_instructions: "Jos sähköpostiosoitteesi on olemassa tietokannassamme, saat sähköpostiviestin, jossa on ohjeet siitä, miten voit vaihtaa salasanan muutamassa minuutissa." + updated: "Salasanasi on vaihdettu onnistuneesti. Olet nyt kirjautunut sisään." + updated_not_active: "Salasanasi on vaihdettu onnistuneesti." registrations: - destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." - signed_up: "Welcome! You have signed up successfully." - signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." - signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." - signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." - update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." - updated: "Your account has been updated successfully." + destroyed: "Heippa! Tilisi on peruutettu onnistuneesti. Toivomme näkevämme sinut pian uudelleen." + signed_up: "Tervetuloa! Olet rekisteröitynyt onnistuneesti." + signed_up_but_inactive: "Olet rekisteröitynyt onnistuneesti. Emme kuitenkaan voineet kirjata sinua sisään, koska tilisi ei ole vielä aktivoitu." + signed_up_but_locked: "Olet rekisteröitynyt onnistuneesti. Emme kuitenkaan voineet kirjata sinua sisään, koska tilisi on lukittu." + signed_up_but_unconfirmed: "Sähköpostiosoitteeseesi on lähetetty viesti, jossa on vahvistuslinkki. Ole hyvä ja seuraa linkkiä aktivoidaksesi tilisi." + update_needs_confirmation: "Olet päivittänyt tilisi onnistuneesti, mutta meidän on vahvistettava uusi sähköpostiosoitteesi. Ole hyvä ja tarkista sähköpostisi ja seuraa vahvistuslinkkiä vahvistaaksesi uuden sähköpostiosoitteesi." + updated: "Tilisi on päivitetty onnistuneesti." sessions: - signed_in: "Signed in successfully." - signed_out: "Signed out successfully." - already_signed_out: "Signed out successfully." + signed_in: "Kirjauduttu sisään onnistuneesti." + signed_out: "Kirjauduttu ulos onnistuneesti." + already_signed_out: "Kirjauduttu ulos onnistuneesti." unlocks: - send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." - send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." - unlocked: "Your account has been unlocked successfully. Please sign in to continue." + send_instructions: "Saat sähköpostiviestin, jossa on ohjeet siitä, miten tilin lukitus avataan muutamassa minuutissa." + send_paranoid_instructions: "Jos tilisi on olemassa, saat sähköpostiviestin, jossa on ohjeet sen avaamisesta muutamassa minuutissa." + unlocked: "Tilisi lukitus on poistettu onnistuneesti. Kirjaudu sisään jatkaaksesi." errors: messages: - already_confirmed: "was already confirmed, please try signing in" - confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" - expired: "has expired, please request a new one" - not_found: "not found" - not_locked: "was not locked" + already_confirmed: "on jo vahvistettu, yritä kirjautua sisään" + confirmation_period_expired: "täytyy vahvistaa %{period} päivän sisällä, pyydä uusi versio" + expired: "on vanhentunut, pyydä uusi" + not_found: "ei löydy" + not_locked: "ei ollut lukittu" not_saved: - one: "1 error prohibited this %{resource} from being saved:" - other: "%{count} errors prohibited this %{resource} from being saved:" + one: "1 virhe esti tämän %{resource} tallennuksen:" + other: "%{count} virhettä esti tämän %{resource} tallennuksen:" diff --git a/config/locales/devise.id.yml b/config/locales/devise.id.yml new file mode 100644 index 000000000..fc6bfb26a --- /dev/null +++ b/config/locales/devise.id.yml @@ -0,0 +1,60 @@ +#Additional translations at https://github.com/plataformatec/devise/wiki/I18n +id: + devise: + confirmations: + confirmed: "Alamat email Anda telah berhasil dikonfirmasi." + send_instructions: "Anda akan menerima email dengan instruksi bagaimana untuk mengkonfirmasi alamat email Anda dalam beberapa menit." + send_paranoid_instructions: "Jika alamat email Anda ada di database kami, Anda akan menerima email dengan instruksi bagaimana untuk mengkonfirmasi alamat email Anda dalam beberapa menit." + failure: + already_authenticated: "Anda berhasil masuk." + inactive: "Akun Anda belum diaktifkan." + invalid: "%{authentication_keys} / password atau akun tidak valid." + locked: "Akun Anda terkunci." + last_attempt: "Anda memiliki satu kesempatan lagi sebelum akun Anda dikunci." + not_found_in_database: "%{authentication_keys} atau password tidak valid." + timeout: "Sesi Anda kadalwarsa. Harap masuk lagi untuk melanjutkan." + unauthenticated: "Anda harus masuk atau mendaftar sebelum melanjutkan." + unconfirmed: "Anda harus mengkonfirmasi alamat email Anda sebelum melanjutkan." + mailer: + confirmation_instructions: + subject: "Petunjuk Konfirmasi" + reset_password_instructions: + subject: "Instruksi ubah kata sandi" + unlock_instructions: + subject: "Petunjuk unlock" + password_change: + subject: "Kata Sandi Diubah" + omniauth_callbacks: + failure: "Tidak dapat mengautentikasi Anda dari %{kind} karena \"%{reason}\"." + success: "Berhasil diautentikasi dari akun %{kind}." + passwords: + no_token: "Anda tidak dapat mengakses halaman ini tanpa datang dari email pengaturan ulang kata sandi. Jika Anda memang datang dari email pengaturan ulang kata sandi, harap pastikan Anda menggunakan URL lengkap yang telah disediakan." + send_instructions: "Anda akan menerima email dengan instruksi tentang cara mengatur ulang kata sandi Anda dalam beberapa menit." + send_paranoid_instructions: "Jika alamat email Anda ada di database kami, Anda akan menerima tautan pemulihan kata sandi di alamat email Anda dalam beberapa menit." + updated: "Kata sandi Anda telah berhasil diubah. Anda sekarang sudah masuk." + updated_not_active: "Kata sandi Anda berhasil diubah." + registrations: + destroyed: "Selamat tinggal! Akun Anda telah berhasil dibatalkan. Kami berharap dapat bertemu Anda lagi segera." + signed_up: "Selamat datang! Anda berhasil mendaftar." + signed_up_but_inactive: "Anda berhasil mendaftar. Namun, Anda tidak dapat masuk karena akun Anda belum diaktifkan." + signed_up_but_locked: "Anda berhasil mendaftar. Namun, Anda tidak dapat masuk karena akun Anda terkunci." + signed_up_but_unconfirmed: "Sebuah pesan dengan link konfirmasi telah dikirim ke alamat email Anda. Silakan ikuti link untuk mengaktifkan akun Anda." + update_needs_confirmation: "Anda berhasil memperbarui akun Anda, tetapi kami perlu memverifikasi alamat email baru Anda. Silakan periksa email Anda dan ikuti tautan konfirmasi untuk mengonfirmasi alamat email baru Anda." + updated: "Akun Anda berhasil diperbarui." + sessions: + signed_in: "Berhasil masuk." + signed_out: "Berhasil keluar." + already_signed_out: "Berhasil keluar." + unlocks: + send_instructions: "Anda akan menerima email dengan instruksi bagaimana membuka akun Anda dalam beberapa menit." + send_paranoid_instructions: "Jika akun Anda ada, Anda akan menerima email dengan instruksi tentang cara membukanya dalam beberapa menit." + unlocked: "Kunci akun Anda berhasil dibuka. Silahkan masuk untuk melanjutkan." + errors: + messages: + already_confirmed: "sudah dikonfirmasi, silakan coba masuk" + confirmation_period_expired: "perlu dikonfirmasi dalam %{period}, harap minta yang baru" + expired: "telah kedaluwarsa, silakan minta yang baru" + not_found: "tidak ditemukan" + not_locked: "tidak terkunci" + not_saved: + other: "%{count} kesalahan mengakibatkan %{resource} ini tidak dapat disimpan:" diff --git a/config/locales/devise.pt_BR.yml b/config/locales/devise.pt_BR.yml index bca956f73..3175718e3 100644 --- a/config/locales/devise.pt_BR.yml +++ b/config/locales/devise.pt_BR.yml @@ -1,5 +1,5 @@ #Additional translations at https://github.com/plataformatec/devise/wiki/I18n -pt: +pt_BR: devise: confirmations: confirmed: "Seu e-mail foi confirmado com sucesso." diff --git a/config/locales/devise.tr.yml b/config/locales/devise.tr.yml index fae21f441..ef10b8d87 100644 --- a/config/locales/devise.tr.yml +++ b/config/locales/devise.tr.yml @@ -2,60 +2,60 @@ tr: devise: confirmations: - confirmed: "Your email address has been successfully confirmed." - send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." + confirmed: "E-posta adresiniz başarıyla onaylanmıştır." + send_instructions: "Birkaç dakika içinde e-posta adresinizi nasıl onaylayacağınıza dair talimatları içeren bir e-posta alacaksınız." + send_paranoid_instructions: "E-posta adresiniz Veri tabanımızda mevcutsa, birkaç dakika içinde e-posta adresinizi nasıl onaylayacağınıza dair talimatları içeren bir e-posta alacaksınız." failure: - already_authenticated: "You are already signed in." - inactive: "Your account is not activated yet." - invalid: "Invalid %{authentication_keys}/password or account is not verified yet." - locked: "Your account is locked." - last_attempt: "You have one more attempt before your account is locked." - not_found_in_database: "Invalid %{authentication_keys} or password." - timeout: "Your session expired. Please sign in again to continue." - unauthenticated: "You need to sign in or sign up before continuing." - unconfirmed: "You have to confirm your email address before continuing." + already_authenticated: "Zaten oturum açmış durumdasınız." + inactive: "Hesabınız henüz aktifleştirilmedi." + invalid: "Geçersiz %{authentication_keys} / parola veya hesap henüz doğrulanmadı." + locked: "Hesabınız kilitlendi." + last_attempt: "Hesabınız kilitlenmeden önce bir deneme hakkınız daha var." + not_found_in_database: "Geçersiz %{authentication_keys} veya parola." + timeout: "Oturumunuzun süresi doldu. Devam etmek için lütfen tekrar oturum açın." + unauthenticated: "Devam etmeden önce oturum açmanız veya kaydolmanız gerekiyor." + unconfirmed: "Devam etmeden önce e-posta adresinizi onaylamalısınız." mailer: confirmation_instructions: - subject: "Confirmation Instructions" + subject: "Onay Talimatları" reset_password_instructions: - subject: "Reset password instructions" + subject: "Şifre talimatlarını sıfırla" unlock_instructions: - subject: "Unlock instructions" + subject: "Talimatların kilidini aç" password_change: - subject: "Password Changed" + subject: "Şifre değişti" omniauth_callbacks: - failure: "Could not authenticate you from %{kind} because \"%{reason}\"." - success: "Successfully authenticated from %{kind} account." + failure: "%{kind} tarafından kimliğiniz doğrulanamadı çünkü \"%{reason}\"." + success: "%{kind} hesabından başarıyla kimlik doğrulaması yapıldı." passwords: - no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." - updated: "Your password has been changed successfully. You are now signed in." - updated_not_active: "Your password has been changed successfully." + no_token: "Parola sıfırlama e-postasından gelmeden bu sayfaya erişemezsiniz. Bir şifre sıfırlama e-postasından geliyorsanız, lütfen sağlanan tam URL'yi kullandığınızdan emin olun." + send_instructions: "Birkaç dakika içinde şifrenizi nasıl sıfırlayacağınıza dair talimatları içeren bir e-posta alacaksınız." + send_paranoid_instructions: "E-posta adresiniz veritabanımızda mevcutsa, birkaç dakika içinde e-posta adresinize bir şifre kurtarma bağlantısı alacaksınız." + updated: "Şifreniz başarıyla değiştirildi. Şimdi giriş yaptınız." + updated_not_active: "Şifreniz başarıyla değiştirildi." registrations: - destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." - signed_up: "Welcome! You have signed up successfully." - signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." - signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." - signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." - update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." - updated: "Your account has been updated successfully." + destroyed: "Hoşçakal! Hesabınız başarıyla iptal edildi. Kısa süre sonra tekrar görüşmeyi umuyoruz." + signed_up: "Hoşgeldiniz! Başarıyla kaydoldunuz." + signed_up_but_inactive: "Başarıyla kaydoldunuz. Ancak, hesabınız henüz etkinleştirilmediğinden oturumunuzu açamadık." + signed_up_but_locked: "Başarıyla kaydoldunuz. Ancak, hesabınız kilitli olduğu için oturumunuzu açamadık." + signed_up_but_unconfirmed: "E-posta adresinize onay bağlantısı içeren bir mesaj gönderildi. Hesabınızı etkinleştirmek için lütfen bağlantıyı takip edin." + update_needs_confirmation: "Hesabınızı başarıyla güncellediniz, ancak yeni e-posta adresinizi doğrulamamız gerekiyor. Lütfen e-postanızı kontrol edin ve yeni e-posta adresinizi onaylamak için onay bağlantısını takip edin." + updated: "Hesabınız başarıyla güncellendi." sessions: - signed_in: "Signed in successfully." - signed_out: "Signed out successfully." - already_signed_out: "Signed out successfully." + signed_in: "Başarıyla oturum açıldı." + signed_out: "Başarıyla çıkış yapıldı." + already_signed_out: "Başarıyla çıkış yapıldı." unlocks: - send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." - send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." - unlocked: "Your account has been unlocked successfully. Please sign in to continue." + send_instructions: "Birkaç dakika içinde hesabınızın kilidini nasıl açacağınıza dair talimatları içeren bir e-posta alacaksınız." + send_paranoid_instructions: "Hesabınız varsa, birkaç dakika içinde kilidini nasıl açacağınıza dair talimatları içeren bir e-posta alacaksınız." + unlocked: "Hesabınızın kilidi başarıyla açıldı. Devam etmek için giriş yapın." errors: messages: - already_confirmed: "was already confirmed, please try signing in" - confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" - expired: "has expired, please request a new one" - not_found: "not found" - not_locked: "was not locked" + already_confirmed: "zaten onaylandı, lütfen oturum açmayı deneyin" + confirmation_period_expired: "%{period} içinde onaylanması gerekiyor, lütfen yeni bir tane isteyin" + expired: "süresi doldu, lütfen yeni bir tane isteyin" + not_found: "bulunamadı" + not_locked: "kilitli değil" not_saved: - one: "1 error prohibited this %{resource} from being saved:" - other: "%{count} errors prohibited this %{resource} from being saved:" + one: "1 hata bu %{resource} 'un kaydedilmesini engelledi:" + other: "%{count} hatası, bu %{resource} kaynağının kaydedilmesini yasakladı:" diff --git a/config/locales/devise.zh_CN.yml b/config/locales/devise.zh_CN.yml index f8613c84c..2bf2831a8 100644 --- a/config/locales/devise.zh_CN.yml +++ b/config/locales/devise.zh_CN.yml @@ -1,5 +1,5 @@ #Additional translations at https://github.com/plataformatec/devise/wiki/I18n -zh-CN: +zh_CN: devise: confirmations: confirmed: "您的电子邮件地址已成功确认。" diff --git a/config/locales/devise.zh_TW.yml b/config/locales/devise.zh_TW.yml index d31370d3b..f892bf796 100644 --- a/config/locales/devise.zh_TW.yml +++ b/config/locales/devise.zh_TW.yml @@ -1,4 +1,4 @@ -# Additional translations at https://github.com/plataformatec/devise/wiki/I18n +#Additional translations at https://github.com/plataformatec/devise/wiki/I18n zh_TW: devise: confirmations: diff --git a/config/locales/el.yml b/config/locales/el.yml index 2a10147ab..a40efd8e2 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -27,6 +27,8 @@ el: invalid_email: Έχετε καταχωρήσει ακατάλληλο email email_already_exists: "Έχει ήδη καταχωρηθεί λογαριασμός στο %{email}" failed: Η εγγραφή απέτυχε + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ el: reply: email_subject: "νέα μηνύματα σε αυτήν την συνομιλία" transcript_subject: "Μεταγραφή Συνομιλίας" + integration_apps: + slack: + name: "Slack" + description: "Το Slack είναι ένα εργαλείο συνομιλιών (chat) το οποίο ενώνει σε ένα σημείο όλες τις επικοινωνίες. Ενοποιώντας με το Slack, μπορείτε να λαμβάνετε ειδοποιήσεις για όλες τις συνομιλίες στον λογαριασμό σας από το Slack." + webhooks: + name: "Webhooks" + description: "Τα συμβάντα Webhook μας εφοδιάζουν με πληροφορίες πραγματικού χρόνου σχετικά με το τι συμβαίνει στο λογαριασμό σας. Μπορείτε να χρησιμοποιήσετε τα webhooks για να μεταφέρετε τα συμβάντα σε άλλες εφαρμογές ή υπηρεσίες όπως το Slack ή Github. Πατήστε στην Διαμόρφωση για να ενημερώστε τα δικά σας webhooks." diff --git a/config/locales/en.yml b/config/locales/en.yml index e3b1db2c6..f21b162f9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -42,6 +42,9 @@ en: email_already_exists: "You have already signed up for an account with %{email}" failed: Signup failed + reports: + period: Reporting period %{since} to %{until} + conversations: activity: status: @@ -64,3 +67,11 @@ en: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/es.yml b/config/locales/es.yml index d6cbca9af..a8832a6ef 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -27,6 +27,8 @@ es: invalid_email: Ha introducido un correo electrónico no válido email_already_exists: "Ya te has registrado en una cuenta con %{email}" failed: Registro fallido + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ es: reply: email_subject: "Nuevos mensajes en esta conversación" transcript_subject: "Transcripción de la conversación" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 26cd7ca19..59d102334 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -27,6 +27,8 @@ fa: invalid_email: ایمیل وارد شده معتبر نیست email_already_exists: "قبلا کاربری با ایمیل %{email} ثبت نام کرده است." failed: ثبت نام ناموفق بود + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ fa: reply: email_subject: "پیام‌های جدید در این مکالمه" transcript_subject: "متن مکالمه" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 584d961fd..c58ed7abc 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -17,35 +17,44 @@ #To learn more, please read the Rails Internationalization guide #available at https://guides.rubyonrails.org/i18n.html. fi: - hello: "Hello world" + hello: "Hei maailma" messages: - reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions. - reset_password_failure: Uh ho! We could not find any user with the specified email. + reset_password_success: Woot! Salasanan nollauspyyntö onnistui. Tarkista sähköpostisi saadaksesi ohjeita. + reset_password_failure: Hö! Emme löytäneet yhtään käyttäjää määritellyllä sähköpostilla. errors: signup: - disposable_email: We do not allow disposable emails - invalid_email: You have entered an invalid email - email_already_exists: "You have already signed up for an account with %{email}" - failed: Signup failed + disposable_email: Emme salli kertakäyttöisiä sähköposteja + invalid_email: Olet syöttänyt virheellisen sähköpostin + email_already_exists: "Olet jo rekisteröitynyt tiliin sähköpostin %{email} kanssa" + failed: Rekisteröityminen epäonnistui + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: - resolved: "Conversation was marked resolved by %{user_name}" - open: "Conversation was reopened by %{user_name}" - auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity" + resolved: "%{user_name} merkitsi keskustelun ratkaistuksi" + open: "%{user_name} uudelleenavasi keskustelun" + auto_resolved: "Keskustelu merkittiin ratkaistuksi automaattisesti, sillä on kulunut %{duration} päivää viimeisestä viestistä" assignee: - self_assigned: "%{user_name} self-assigned this conversation" - assigned: "Assigned to %{assignee_name} by %{user_name}" - removed: "Conversation unassigned by %{user_name}" + self_assigned: "%{user_name} ilmottautui keskusteluun" + assigned: "Delegoitu edustajalle %{assignee_name} edustajan %{user_name} toimesta" + removed: "%{user_name} poistui keskustelusta" labels: - added: "%{user_name} added %{labels}" - removed: "%{user_name} removed %{labels}" - muted: "%{user_name} has muted the conversation" - unmuted: "%{user_name} has unmuted the conversation" + added: "%{user_name} lisäsi tunnisteet %{labels}" + removed: "%{user_name} poisti tunnisteet %{labels}" + muted: "%{user_name} mykisti keskustelun" + unmuted: "%{user_name} poisti keskustelun mykistyksen" templates: - greeting_message_body: "%{account_name} typically replies in a few hours." - ways_to_reach_you_message_body: "Give the team a way to reach you." - email_input_box_message_body: "Get notified by email" + greeting_message_body: "%{account_name} vastaa tyypillisesti muutamassa tunnissa." + ways_to_reach_you_message_body: "Anna tiimillemme tapa tavoittaa sinut." + email_input_box_message_body: "Saa ilmoitus sähköpostitse" reply: - email_subject: "New messages on this conversation" - transcript_subject: "Conversation Transcript" + email_subject: "Uusia viestejä" + transcript_subject: "Keskustelukopio" + integration_apps: + slack: + name: "Slack" + description: "Slack on chat-työkalu, joka tuo kaiken viestintäsi yhteen paikkaan. Integroimalla Slackin voit saada ilmoituksen kaikista uusista keskusteluista Slackin sisällä." + webhooks: + name: "Webhookit" + description: "Webhook-tapahtumat antavat sinulle reaaliaikaista tietoa siitä, mitä Chatwoot-tililläsi tapahtuu. Voit käyttää webhookeja ja välittää tapahtumat suosikkiohjelmillesi, kuten Slackiin tai Githubiiin. Klikkaa \"Määrittele\" määrittääksesi webhookisi." diff --git a/config/locales/fr.yml b/config/locales/fr.yml index dea756ed3..4fcd33aad 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -27,6 +27,8 @@ fr: invalid_email: Vous avez entré un courriel non valide email_already_exists: "Vous avez déjà créé un compte avec %{email}" failed: L'inscription a échoué + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ fr: reply: email_subject: "Nouveaux messages dans cette conversation" transcript_subject: "Transcription de conversation" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/hi.yml b/config/locales/hi.yml index f630407e4..f6c69191d 100644 --- a/config/locales/hi.yml +++ b/config/locales/hi.yml @@ -27,6 +27,8 @@ hi: invalid_email: You have entered an invalid email email_already_exists: "You have already signed up for an account with %{email}" failed: Signup failed + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ hi: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 4b8d0bc4c..d885ab298 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -27,6 +27,8 @@ hu: invalid_email: You have entered an invalid email email_already_exists: "You have already signed up for an account with %{email}" failed: Signup failed + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ hu: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/id.yml b/config/locales/id.yml new file mode 100644 index 000000000..acfc2f7ff --- /dev/null +++ b/config/locales/id.yml @@ -0,0 +1,60 @@ +#Files in the config/locales directory are used for internationalization +#and are automatically loaded by Rails. If you want to use locales other +#than English, add the necessary files in this directory. +#To use the locales, use `I18n.t`: +#I18n.t 'hello' +#In views, this is aliased to just `t`: +#<%= t('hello') %> +#To use a different locale, set it with `I18n.locale`: +#I18n.locale = :es +#This would use the information in config/locales/es.yml. +#The following keys must be escaped otherwise they will not be retrieved by +#the default I18n backend: +#true, false, on, off, yes, no +#Instead, surround them with single quotes. +#en: +#'true': 'foo' +#To learn more, please read the Rails Internationalization guide +#available at https://guides.rubyonrails.org/i18n.html. +id: + hello: "Hello world" + messages: + reset_password_success: Woot! Permintaan pengaturan ulang kata sandi berhasil. Periksa email Anda untuk mendapatkan petunjuk. + reset_password_failure: Aduh! Kami tidak dapat menemukan pengguna dengan email yang dimasukkan. + errors: + signup: + disposable_email: Kami tidak mengizinkan email sekali pakai + invalid_email: Anda telah memasukkan email yang tidak valid + email_already_exists: "Anda telah mendaftar untuk sebuah akun dengan %{email}" + failed: Pendaftaran gagal + reports: + period: Reporting period %{since} to %{until} + conversations: + activity: + status: + resolved: "Percakapan ditandai selesai oleh %{user_name}" + open: "Percakapan telah dibuka kembali oleh %{user_name}" + auto_resolved: "Percakapan ditandai terselesaikan oleh sistem karena tidak ada aktifitas dalam %{duration} hari" + assignee: + self_assigned: "%{user_name} menetapkan sendiri percakapan ini" + assigned: "Ditugaskan ke %{assignee_name} oleh %{user_name}" + removed: "Percakapan batal ditetapkan oleh %{user_name}" + labels: + added: "%{user_name} menambahkan %{labels}" + removed: "%{user_name} menghapus %{labels}" + muted: "%{user_name} me-mute percakapan" + unmuted: "%{user_name} telah un-mute percakapan" + templates: + greeting_message_body: "%{account_name} biasanya membalas dalam beberapa jam." + ways_to_reach_you_message_body: "Beri tim cara untuk menghubungi Anda." + email_input_box_message_body: "Dapatkan pemberitahuan melalui email" + reply: + email_subject: "Pesan baru pada percakapan ini" + transcript_subject: "Transkrip Percakapan" + integration_apps: + slack: + name: "Slack" + description: "Slack adalah alat obrolan yang menyatukan semua komunikasi Anda di satu tempat. Dengan mengintegrasikan Slack, Anda bisa mendapatkan pemberitahuan tentang semua percakapan baru di akun Anda tepat di dalam Slack Anda." + webhooks: + name: "Webhooks" + description: "Webhook event memberi Anda informasi realtime tentang apa yang terjadi di akun Anda. Anda dapat menggunakan webhook untuk mengkomunikasikan acara ke aplikasi favorit Anda seperti Slack atau GitHub. Klik Konfigurasi untuk mengatur webhook Anda." diff --git a/config/locales/it.yml b/config/locales/it.yml index d26fd0254..de8617b46 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -27,6 +27,8 @@ it: invalid_email: Hai inserito un'email non valida email_already_exists: "Ti sei già registrato per un account con %{email}" failed: Iscrizione fallita + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ it: reply: email_subject: "Nuovi messaggi in questa conversazione" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 7f3d6d896..0dc2434fd 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -27,6 +27,8 @@ ja: invalid_email: 無効なメールアドレスを入力しました。 email_already_exists: "あなたは既に %{email} でアカウントにサインアップしています" failed: サインアップに失敗しました + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ ja: reply: email_subject: "この会話に新着メッセージがあります" transcript_subject: "会話の記録" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/ko.yml b/config/locales/ko.yml index ce75fd92d..477719749 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -27,6 +27,8 @@ ko: invalid_email: You have entered an invalid email email_already_exists: "You have already signed up for an account with %{email}" failed: Signup failed + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ ko: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/ml.yml b/config/locales/ml.yml index 2e4190331..c88aa6550 100644 --- a/config/locales/ml.yml +++ b/config/locales/ml.yml @@ -27,6 +27,8 @@ ml: invalid_email: നിങ്ങൾ ഒരു അസാധുവായ ഇമെയിൽ നൽകി email_already_exists: "നിങ്ങൾ ഇതിനകം ഈ %{email} ഉപയോഗിച്ചു ഒരു അക്കൗണ്ടിനായി സൈൻ അപ്പ് ചെയ്തു" failed: സൈനപ്പ് പരാജയപ്പെട്ടു + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ ml: reply: email_subject: "ഈ സംഭാഷണത്തിലെ പുതിയ സന്ദേശങ്ങൾ" transcript_subject: "സംഭാഷണ ട്രാൻസ്ക്രിപ്റ്റ്" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 28a79c4a0..83d4e57a1 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -27,6 +27,8 @@ nl: invalid_email: Je hebt een ongeldig e-mailadres ingevoerd email_already_exists: "Je hebt je al aangemeld voor een account bij %{email}" failed: Aanmelden mislukt + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ nl: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/pl.yml b/config/locales/pl.yml index f6ac67850..4a23b9082 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -27,6 +27,8 @@ pl: invalid_email: Wprowadzono nieprawidłowy adres e-mail email_already_exists: "Już zarejestrowałeś konto w %{email}" failed: Rejestracja nie powiodła się + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ pl: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 78215e321..5d7bb4dd2 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -27,6 +27,8 @@ pt: invalid_email: Você digitou um email inválido email_already_exists: "Você já se inscreveu para uma conta com %{email}" failed: Falha na inscrição + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ pt: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml index a0dc75497..0453ba863 100644 --- a/config/locales/pt_BR.yml +++ b/config/locales/pt_BR.yml @@ -16,7 +16,7 @@ #'true': 'foo' #To learn more, please read the Rails Internationalization guide #available at https://guides.rubyonrails.org/i18n.html. -pt: +pt_BR: hello: "Olá, mundo" messages: reset_password_success: Legal! A solicitação de alteração de senha foi bem sucedida. Verifique seu e-mail para obter instruções. @@ -27,21 +27,23 @@ pt: invalid_email: Você digitou um email inválido email_already_exists: "Você já se cadastrou para uma conta com %{email}" failed: Registro falhou + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: resolved: "Conversa foi marcada como resolvida por %{user_name}" open: "Conversa foi reaberta por %{user_name}" - auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity" + auto_resolved: "Conversa foi marcada como resolvida pelo sistema por ter %{duration} dias de inatividade" assignee: - self_assigned: "%{user_name} self-assigned this conversation" + self_assigned: "%{user_name} atribuiu a si mesmo essa conversa" assigned: "Atribuído a %{assignee_name} por %{user_name}" removed: "Conversa não atribuída por %{user_name}" labels: - added: "%{user_name} added %{labels}" - removed: "%{user_name} removed %{labels}" - muted: "%{user_name} has muted the conversation" - unmuted: "%{user_name} has unmuted the conversation" + added: "%{user_name} adicionou %{labels}" + removed: "%{user_name} removeu %{labels}" + muted: "%{user_name} silenciou a conversa" + unmuted: "%{user_name} reativou a conversa" templates: greeting_message_body: "%{account_name} normalmente responde em algumas horas." ways_to_reach_you_message_body: "Informe uma forma para entrarmos em contato com você." @@ -49,3 +51,10 @@ pt: reply: email_subject: "Novas mensagens nesta conversa" transcript_subject: "Transcrição da conversa" + integration_apps: + slack: + name: "Slack" + description: "Slack é uma ferramenta que reúne todas as suas comunicações em um só lugar. Ao integrar o Slack, você pode ser notificado de todas as novas conversas da sua conta diretamente no seu Slack." + webhooks: + name: "Webhooks" + description: "Webhooks fornecem informações em tempo real sobre o que está acontecendo em sua conta. Você pode usar os webhooks para comunicar eventos com seus aplicativos favoritos como Slack ou Github. Clique em Configurar para configurar seus webhooks." diff --git a/config/locales/ro.yml b/config/locales/ro.yml index 2906e27e6..95918b8eb 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -27,6 +27,8 @@ ro: invalid_email: Ați introdus un e-mail invalid email_already_exists: "V-ați înregistrat deja cu un cont cu %{email}" failed: Înregistrare eșuată + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ ro: reply: email_subject: "Mesaje noi în această conversație" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/ru.yml b/config/locales/ru.yml index b774787d2..8a38431c9 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -27,6 +27,8 @@ ru: invalid_email: Вы ввели неверный email email_already_exists: "Вы уже зарегистрировались для учётной записи с %{email}" failed: Ошибка регистрации + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -38,8 +40,8 @@ ru: assigned: "%{user_name} назначил %{assignee_name} ответственным" removed: "Ответственный снят %{user_name}" labels: - added: "%{user_name} added %{labels}" - removed: "%{user_name} removed %{labels}" + added: "%{user_name} добавил %{labels}" + removed: "%{user_name} удалил %{labels}" muted: "%{user_name} has muted the conversation" unmuted: "%{user_name} has unmuted the conversation" templates: @@ -49,3 +51,10 @@ ru: reply: email_subject: "Новые сообщения в этом диалоге" transcript_subject: "Субтитры общения" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/sk.yml b/config/locales/sk.yml index b011ccfc6..a0a77e3e4 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -27,6 +27,8 @@ sk: invalid_email: You have entered an invalid email email_already_exists: "You have already signed up for an account with %{email}" failed: Signup failed + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ sk: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 820b4b4d9..078d0baf6 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -27,6 +27,8 @@ sv: invalid_email: Du har angett en ogiltig e-postadress email_already_exists: "Du har redan registrerat dig för ett konto med %{email}" failed: Registrering misslyckades + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ sv: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/ta.yml b/config/locales/ta.yml index a42bfc15c..bd1efbef0 100644 --- a/config/locales/ta.yml +++ b/config/locales/ta.yml @@ -27,6 +27,8 @@ ta: invalid_email: நீங்கள் தவறான ஈ-மெயிலை உள்ளிட்டுள்ளீர்கள் email_already_exists: "நீங்கள் ஏற்கனவே %{email} கொண்டு கணக்கிற்கு பதிவு செய்துள்ளீர்கள்" failed: உள்நுழையும் முயறிசி தோல்வி அடைந்துள்ளது + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ ta: reply: email_subject: "இந்த உரையாடலில் புதிய செய்திகள்" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/th.yml b/config/locales/th.yml index 91b8af59b..0ebe59b74 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -27,6 +27,8 @@ th: invalid_email: You have entered an invalid email email_already_exists: "You have already signed up for an account with %{email}" failed: Signup failed + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ th: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 09e8cd685..0ca0df313 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -17,35 +17,44 @@ #To learn more, please read the Rails Internationalization guide #available at https://guides.rubyonrails.org/i18n.html. tr: - hello: "Hello world" + hello: "Merhaba Dünya" messages: - reset_password_success: Woot! Request for password reset is successful. Check your mail for instructions. - reset_password_failure: Uh ho! We could not find any user with the specified email. + reset_password_success: Parola sıfırlama isteği başarılı. Talimatlar için postanızı kontrol edin. + reset_password_failure: Belirtilen e-postaya sahip herhangi bir kullanıcı bulamadık. errors: signup: - disposable_email: We do not allow disposable emails - invalid_email: You have entered an invalid email - email_already_exists: "You have already signed up for an account with %{email}" - failed: Signup failed + disposable_email: Tek kullanımlık e-postalara izin vermiyoruz + invalid_email: Geçersiz bir e-posta girdiniz + email_already_exists: "%{email} ile zaten bir hesaba kaydoldunuz" + failed: Kayıt başarısız oldu + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: - resolved: "Conversation was marked resolved by %{user_name}" - open: "Conversation was reopened by %{user_name}" - auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity" + resolved: "Görüşme %{user_name} tarafından çözüldü olarak işaretlendi" + open: "Görüşme %{user_name} tarafından çözüldü olarak işaretlendi" + auto_resolved: " %{duration} günlük hareketsizlik nedeniyle görüşme, sistem tarafından çözümlendi olarak işaretlendi" assignee: - self_assigned: "%{user_name} self-assigned this conversation" - assigned: "Assigned to %{assignee_name} by %{user_name}" - removed: "Conversation unassigned by %{user_name}" + self_assigned: "%{user_name} bu görüşmeyi kendisi atadı" + assigned: "%{user_name} tarafından %{assignee_name} adına atandı" + removed: "Görüşmenin ataması %{user_name} tarafından kaldırıldı" labels: - added: "%{user_name} added %{labels}" - removed: "%{user_name} removed %{labels}" - muted: "%{user_name} has muted the conversation" - unmuted: "%{user_name} has unmuted the conversation" + added: "%{user_name},%{labels} ekledi" + removed: "%{user_name} , %{labels} kaldırdı" + muted: "%{user_name}, sohbeti sessize aldı" + unmuted: "%{user_name} görüşmenin sesini açtı" templates: - greeting_message_body: "%{account_name} typically replies in a few hours." - ways_to_reach_you_message_body: "Give the team a way to reach you." - email_input_box_message_body: "Get notified by email" + greeting_message_body: "%{account_name} genellikle birkaç saat içinde yanıt verir." + ways_to_reach_you_message_body: "Ekibin size ulaşması için bir bilgi verin." + email_input_box_message_body: "E-posta ile haberdar olun" reply: - email_subject: "New messages on this conversation" - transcript_subject: "Conversation Transcript" + email_subject: "Bu görüşmedeki yeni mesajlar" + transcript_subject: "Konuşma Metni" + integration_apps: + slack: + name: "Slack" + description: "Slack, tüm iletişiminizi tek bir yerde bir araya getiren bir sohbet aracıdır. Slack'i entegre ederek, hesabınızdaki tüm yeni konuşmalardan Slack'inizin içinde bildirim alabilirsiniz." + webhooks: + name: "Webhooks" + description: "Webhook olayları size hesabınızda gerçekleşen gerçek zamanlı bilgileri getirmenizi sağlar. Bu webhookları kullanarak olaylar ile favori uygulamalarınızı haberleştirebilirsiniz(ör: Slack , \n Github). Yapılandıra basarak webhooklarınızı ayarlayabilirsiniz." diff --git a/config/locales/uk.yml b/config/locales/uk.yml index e35a07374..09db2e5a2 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -27,6 +27,8 @@ uk: invalid_email: Ви ввели неправильну адресу електронної пошти email_already_exists: "Ви вже зареєстровані з адресою %{email}" failed: Помилка реєстрації + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ uk: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 588433857..f1c521332 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -27,6 +27,8 @@ vi: invalid_email: Bạn đã nhập một email không hợp lệ email_already_exists: "Bạn đã đăng ký một tài khoản với %{email}" failed: Đăng ký thât bại + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ vi: reply: email_subject: "Tin nhắn mới về cuộc trò chuyện này" transcript_subject: "Bản ghi cuộc hội thoại" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/zh_CN.yml b/config/locales/zh_CN.yml index 508458cf4..64537ee01 100644 --- a/config/locales/zh_CN.yml +++ b/config/locales/zh_CN.yml @@ -16,7 +16,7 @@ #'true': 'foo' #To learn more, please read the Rails Internationalization guide #available at https://guides.rubyonrails.org/i18n.html. -zh-CN: +zh_CN: hello: "您好世界" messages: reset_password_success: 哇!密码重置请求成功。请检查您的邮件获取说明。 @@ -27,6 +27,8 @@ zh-CN: invalid_email: 您输入了一个无效的电子邮件 email_already_exists: "您已经注册了 %{email} 的帐户" failed: 注册失败 + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ zh-CN: reply: email_subject: "此对话中的新消息" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/config/locales/zh_TW.yml b/config/locales/zh_TW.yml index 2552d737b..46853545d 100644 --- a/config/locales/zh_TW.yml +++ b/config/locales/zh_TW.yml @@ -16,7 +16,7 @@ #'true': 'foo' #To learn more, please read the Rails Internationalization guide #available at https://guides.rubyonrails.org/i18n.html. -zh-TW: +zh_TW: hello: "你好。" messages: reset_password_success: 密碼重設成功,請確認您的信箱有收到重設信件。 @@ -27,6 +27,8 @@ zh-TW: invalid_email: 您輸入的電子郵件無效。 email_already_exists: "您已經註冊了一個帳號%{email}" failed: 註冊失敗。 + reports: + period: Reporting period %{since} to %{until} conversations: activity: status: @@ -49,3 +51,10 @@ zh-TW: reply: email_subject: "New messages on this conversation" transcript_subject: "Conversation Transcript" + integration_apps: + slack: + name: "Slack" + description: "Slack is a chat tool that brings all your communication together in one place. By integrating Slack, you can get notified of all the new conversations in your account right inside your Slack." + webhooks: + name: "Webhooks" + description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." diff --git a/db/seeds.rb b/db/seeds.rb index b56da8239..3b1395004 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -12,6 +12,8 @@ user = User.new(name: 'John', email: 'john@acme.inc', password: '123456') user.skip_confirmation! user.save! +SuperAdmin.create!(email: 'john@acme.inc', password: '123456') unless Rails.env.production? + AccountUser.create!( account_id: account.id, user_id: user.id, diff --git a/deployment/chatwoot-web.1.service b/deployment/chatwoot-web.1.service index 56d6a283c..9ef63aeb6 100644 --- a/deployment/chatwoot-web.1.service +++ b/deployment/chatwoot-web.1.service @@ -16,10 +16,10 @@ KillMode=mixed StandardInput=null SyslogIdentifier=%p -Environment="PATH=/home/chatwoot/.rvm/gems/ruby-2.7.1/bin:/home/chatwoot/.rvm/gems/ruby-2.7.1@global/bin:/home/chatwoot/.rvm/rubies/ruby-2.7.1/bin:/home/chatwoot/.rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/chatwoot/.rvm/bin:/home/chatwoot/.rvm/bin" +Environment="PATH=/home/chatwoot/.rvm/gems/ruby-2.7.2/bin:/home/chatwoot/.rvm/gems/ruby-2.7.2@global/bin:/home/chatwoot/.rvm/rubies/ruby-2.7.2/bin:/home/chatwoot/.rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/chatwoot/.rvm/bin:/home/chatwoot/.rvm/bin" Environment="PORT=3000" Environment="RAILS_ENV=production" Environment="NODE_ENV=production" Environment="RAILS_LOG_TO_STDOUT=true" -Environment="GEM_HOME=/home/chatwoot/.rvm/gems/ruby-2.7.1" -Environment="GEM_PATH=/home/chatwoot/.rvm/gems/ruby-2.7.1:/home/chatwoot/.rvm/gems/ruby-2.7.1@global" \ No newline at end of file +Environment="GEM_HOME=/home/chatwoot/.rvm/gems/ruby-2.7.2" +Environment="GEM_PATH=/home/chatwoot/.rvm/gems/ruby-2.7.2:/home/chatwoot/.rvm/gems/ruby-2.7.2@global" diff --git a/deployment/chatwoot-worker.1.service b/deployment/chatwoot-worker.1.service index 8b3f0b1cb..ac24709cd 100644 --- a/deployment/chatwoot-worker.1.service +++ b/deployment/chatwoot-worker.1.service @@ -16,10 +16,10 @@ KillMode=mixed StandardInput=null SyslogIdentifier=%p -Environment="PATH=/home/chatwoot/.rvm/gems/ruby-2.7.1/bin:/home/chatwoot/.rvm/gems/ruby-2.7.1@global/bin:/home/chatwoot/.rvm/rubies/ruby-2.7.1/bin:/home/chatwoot/.rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/chatwoot/.rvm/bin:/home/chatwoot/.rvm/bin" +Environment="PATH=/home/chatwoot/.rvm/gems/ruby-2.7.2/bin:/home/chatwoot/.rvm/gems/ruby-2.7.2@global/bin:/home/chatwoot/.rvm/rubies/ruby-2.7.2/bin:/home/chatwoot/.rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/chatwoot/.rvm/bin:/home/chatwoot/.rvm/bin" Environment="PORT=3000" Environment="RAILS_ENV=production" Environment="NODE_ENV=production" Environment="RAILS_LOG_TO_STDOUT=true" -Environment="GEM_HOME=/home/chatwoot/.rvm/gems/ruby-2.7.1" -Environment="GEM_PATH=/home/chatwoot/.rvm/gems/ruby-2.7.1:/home/chatwoot/.rvm/gems/ruby-2.7.1@global" \ No newline at end of file +Environment="GEM_HOME=/home/chatwoot/.rvm/gems/ruby-2.7.2" +Environment="GEM_PATH=/home/chatwoot/.rvm/gems/ruby-2.7.2:/home/chatwoot/.rvm/gems/ruby-2.7.2@global" diff --git a/deployment/setup_18.04.sh b/deployment/setup_18.04.sh index cacd995e9..25af78256 100644 --- a/deployment/setup_18.04.sh +++ b/deployment/setup_18.04.sh @@ -43,12 +43,16 @@ RAILS_ENV=production sudo -i -u chatwoot << EOF rvm --version rvm autolibs disable -rvm install "ruby-2.7.1" -rvm use 2.7.1 --default +rvm install "ruby-2.7.2" +rvm use 2.7.2 --default git clone https://github.com/chatwoot/chatwoot.git cd chatwoot -git checkout master +if [[ -z "$1" ]]; then + git checkout master; +else + git checkout $1; +fi bundle yarn diff --git a/deployment/setup_20.04.sh b/deployment/setup_20.04.sh index 6541b6a5e..19f32fe12 100644 --- a/deployment/setup_20.04.sh +++ b/deployment/setup_20.04.sh @@ -43,12 +43,16 @@ RAILS_ENV=production sudo -i -u chatwoot << EOF rvm --version rvm autolibs disable -rvm install "ruby-2.7.1" -rvm use 2.7.1 --default +rvm install "ruby-2.7.2" +rvm use 2.7.2 --default git clone https://github.com/chatwoot/chatwoot.git cd chatwoot -git checkout master +if [[ -z "$1" ]]; then + git checkout master; +else + git checkout $1; +fi bundle yarn diff --git a/docker/Dockerfile b/docker/Dockerfile index 8093fdb02..99a82b8b2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ # pre-build stage -FROM ruby:2.7.1-alpine AS pre-builder +FROM ruby:2.7.2-alpine AS pre-builder # ARG default to production settings # For development docker-compose file overrides ARGS @@ -51,7 +51,7 @@ RUN if [ "$RAILS_ENV" = "production" ]; then \ fi # final build stage -FROM ruby:2.7.1-alpine +FROM ruby:2.7.2-alpine ARG BUNDLE_WITHOUT="development:test" ENV BUNDLE_WITHOUT ${BUNDLE_WITHOUT} diff --git a/docs/contributing.md b/docs/contributing.md index aa7a00df2..4ac423be8 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -19,15 +19,15 @@ Thank you for taking an interest in contributing to Chatwoot. Before submitting - It's okay and encouraged to have multiple small commits as you work on the PR - we will squash the commits before merging. -### Getting Started +### Getting Started - Before starting your work, ensure an [issue](https://github.com/chatwoot/chatwoot/issues) exist for it. If not feel free to create one. -- Add a comment on the issue and wait for the issue to be assigned before you start working on it. - - This helps to avoid multiple people working on similar issues. -- If the solution is complex, propose the solution on the issue and wait for one of the core contributors to approve before going into the implementation. +- Add a comment on the issue and wait for the issue to be assigned before you start working on it. + - This helps to avoid multiple people working on similar issues. +- If the solution is complex, propose the solution on the issue and wait for one of the core contributors to approve before going into the implementation. - This helps in shorter turn around times in merging PRs -- For new feature requests, Provide a convincing reason to add this feature. Real-life business use-cases will be super helpful. -- Feel free to join our [discord community](https://discord.gg/cJXdrwS), if you need further discussions with the core team. +- For new feature requests, Provide a convincing reason to add this feature. Real-life business use-cases will be super helpful. +- Feel free to join our [discord community](https://discord.gg/cJXdrwS), if you need further discussions with the core team. ### Developing a new feature: @@ -38,9 +38,7 @@ Thank you for taking an interest in contributing to Chatwoot. Before submitting - If you are resolving a particular issue, add `Bug: Fix xxxx` (#xxxx is the issue) in your PR title. - Provide a detailed description of the bug in the PR. - Add appropriate test coverage if applicable. - -### Translations: -- When you are introducing new text copies, you only need to worry about making changes to english language files. -- We accept language translations / updates for existing translations through [crowdin](https://translate.chatwoot.com/) - - If a language doesn't exist in our crowdin, please feel free to create an [issue](https://github.com/chatwoot/chatwoot/issues) to get it enabled. - + +### Translations + +For language translations, please read the guide [translating Chatwoot to your language](/docs/contributing/translating-chatwoot-to-your-language) diff --git a/docs/contributing/translating-chatwoot-to-your-language.md b/docs/contributing/translating-chatwoot-to-your-language.md new file mode 100644 index 000000000..4c9f149e3 --- /dev/null +++ b/docs/contributing/translating-chatwoot-to-your-language.md @@ -0,0 +1,34 @@ +--- +path: "/docs/contributing/translating-chatwoot-to-your-language" +title: "Translate Chatwoot to your language" +--- + +Chatwoot uses American English by default. Each and every string available in Chatwoot can be translated to the language of your choice. Chatwoot uses Crowdin to manage the translation process. The updates from Crowdin is also included along with every release. + +### How do I see the strings that needs to be translated? + +In the codebase the string are placed in the following locations. + +- `app/javascript/dashboard/i18n` - The strings related to the agent dashboard +- `app/javascript/widget/i18n` - The strings related to the web widget +- `config/locales` - The strings used in backend messages or API response. + +Alternatively, you can login to the **Crowdin** ([https://translate.chatwoot.com](https://translate.chatwoot.com)) and create an account to view the strings that needs to be translated. + +### How to contribute? + +If you don't find your language on Crowdin, please create an issue on [Github](https://github.com/chatwoot/chatwoot/issues) to add the language. + +#### Translate Strings + +The translation process for Chatwoot web and mobile app is managed at [https://translate.chatwoot.com](https://translate.chatwoot.com) using Crowdin. You will have to create an account at Crowdin before you can select a language and contribute. + +#### Proofreading + +Proofreading helps ensure the accuracy and consistency of translations. Right now, the translations are being accepted without a proof reading step. This would be changed in the future as an when there are more contributors on each language. + +*Note:* If you are the only person contributing to a language, make sure that you inform any of the Chatwoot members to gain access to manage the language. + +#### Releasing a new language + +All the translated strings would be included in the next release. If a language has *60% or more translated strings* in Crowdin, we would enable the language in Chatwoot app during the next release. diff --git a/docs/deployment/production/docker.md b/docs/deployment/production/docker.md index 92249c6c9..57db212dd 100644 --- a/docs/deployment/production/docker.md +++ b/docs/deployment/production/docker.md @@ -12,8 +12,8 @@ We publish our base images to docker hub. Build your web/worker images from thes ``` FROM chatwoot/chatwoot:latest RUN chmod +x docker/entrypoints/rails.sh -ENTRYPOINT [\"docker/entrypoints/rails.sh\"] -CMD bundle exec bundle exec rails s -b 0.0.0.0 -p 3000" +ENTRYPOINT ["docker/entrypoints/rails.sh"] +CMD bundle exec bundle exec rails s -b 0.0.0.0 -p 3000 ``` ### worker @@ -21,8 +21,8 @@ CMD bundle exec bundle exec rails s -b 0.0.0.0 -p 3000" ``` FROM chatwoot/chatwoot:latest RUN chmod +x docker/entrypoints/rails.sh -ENTRYPOINT [\"docker/entrypoints/rails.sh\"] -CMD bundle exec sidekiq -C config/sidekiq.yml" +ENTRYPOINT ["docker/entrypoints/rails.sh"] +CMD bundle exec sidekiq -C config/sidekiq.yml ``` The app servers will available on port `3000`. Ensure the images are connected to the same database and Redis servers. Provide the configuration for these services via environment variables. diff --git a/docs/deployment/production/linux-vm.md b/docs/deployment/production/linux-vm.md index 0aeb94b2c..39e507ab7 100644 --- a/docs/deployment/production/linux-vm.md +++ b/docs/deployment/production/linux-vm.md @@ -26,7 +26,7 @@ https://raw.githubusercontent.com/chatwoot/chatwoot/develop/deployment/setup_20. ```bash wget -O setup.sh chmod 755 setup.sh -./setup.sh +./setup.sh master ``` 2. Execute the script and it will take care of the initial **Chatwoot** setup. @@ -147,7 +147,21 @@ rake assets:precompile RAILS_ENV=production # Migrate the database schema RAILS_ENV=production bundle exec rake db:migrate +# Copy the updated targets +cp /home/chatwoot/chatwoot/deployment/chatwoot-web.1.service /etc/systemd/system/chatwoot-web.1.service +cp /home/chatwoot/chatwoot/deployment/chatwoot-worker.1.service /etc/systemd/system/chatwoot-worker.1.service +cp /home/chatwoot/chatwoot/deployment/chatwoot.target /etc/systemd/system/chatwoot.target + # Restart the chatwoot server systemctl restart chatwoot.target +``` + +#### If precompile fails + +If the asset precompilation step fails with `ActionView::Template::Error (Webpacker can't find application.css in /home/chatwoot/chatwoot/public/packs/manifest.json)` or if you face issues while restarting the server, try the following command and restart the server. ``` +RAILS_ENV=production rake assets:clean assets:clobber assets:precompile +``` + +This command would clear the existing compiled assets and would recompile all the assets. Read more about it [here](https://edgeguides.rubyonrails.org/command_line.html#bin-rails-assets) diff --git a/docs/development/environment-setup/mac-os.md b/docs/development/environment-setup/mac-os.md index d856b5b15..8f11469af 100644 --- a/docs/development/environment-setup/mac-os.md +++ b/docs/development/environment-setup/mac-os.md @@ -28,28 +28,26 @@ brew install git ### Install RVM -You need software-properties-common installed in order to add PPA repositories. - ```bash curl -L https://get.rvm.io | bash -s stable -source ~/.rvm/scripts/rvm ``` ### Install Ruby -Chatwoot APIs are built on Ruby on Rails, you need install ruby 2.7.1 +Chatwoot APIs are built on Ruby on Rails, you need install ruby 2.7.2 If you are using `rvm` : ```bash -rvm install ruby-2.7.1 -rvm use 2.7.1 +rvm install ruby-2.7.2 +rvm use 2.7.2 +source ~/.rvm/scripts/rvm ``` If you are using `rbenv` to manage ruby versions do : ```bash -rbenv install 2.7.1 +rbenv install 2.7.2 ``` `rbenv` identifies the ruby version from `.ruby-version` file on the root of the project and loads it automatically. @@ -72,7 +70,13 @@ brew install yarn ### Install postgres -The database used in Chatwoot is PostgreSQL. Use the following commands to install postgres. +The database used in Chatwoot is PostgreSQL. + +1) Install PostgresApp (https://postgresapp.com). This is easiest way to get started with PostgreSQL on mac. + +or + +2) Use the following commands to install postgres. ```bash brew install postgresql @@ -92,10 +96,10 @@ Chatwoot uses Redis server in agent assignments and reporting. To install `redis brew install redis ``` -Enable Redis to start on system boot. +Start the redis service. ```bash -launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.redis.plist +brew services start redis ``` ### Install imagemagick diff --git a/docs/development/environment-setup/ubuntu.md b/docs/development/environment-setup/ubuntu.md index 7e6570f47..a97c6a46a 100644 --- a/docs/development/environment-setup/ubuntu.md +++ b/docs/development/environment-setup/ubuntu.md @@ -33,16 +33,16 @@ Enable `Run command as a login shell` in terminal `Preferences`. Restart your co ### Install Ruby -Chatwoot APIs are built on Ruby on Rails. You need to install ruby 2.7.1: +Chatwoot APIs are built on Ruby on Rails. You need to install ruby 2.7.2: ```bash -rvm install ruby-2.7.1 +rvm install ruby-2.7.2 ``` -Use ruby 2.7.1 as default: +Use ruby 2.7.2 as default: ```bash -rvm use 2.7.1 --default +rvm use 2.7.2 --default ``` ### Install Node.js diff --git a/docs/development/environment-setup/windows.md b/docs/development/environment-setup/windows.md index 25e7c0121..db308ed4a 100644 --- a/docs/development/environment-setup/windows.md +++ b/docs/development/environment-setup/windows.md @@ -26,15 +26,15 @@ sudo apt-get update sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev ``` -Install RVM & ruby version 2.7.1 +Install RVM & ruby version 2.7.2 ```bash sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB curl -sSL https://get.rvm.io | bash -s stable source ~/.rvm/scripts/rvm -rvm install 2.7.1 -rvm use 2.7.1 --default +rvm install 2.7.2 +rvm use 2.7.2 --default ruby -v ``` diff --git a/docs/development/project-setup/environment-variables.md b/docs/development/project-setup/environment-variables.md index d9ae9542c..faa04f9e4 100644 --- a/docs/development/project-setup/environment-variables.md +++ b/docs/development/project-setup/environment-variables.md @@ -93,6 +93,12 @@ Provide the following value as frontend url FRONTEND_URL='http://localhost:3000' ``` +### Configure default language + +```bash +DEFAULT_LOCALE='en' +``` + ### Configure storage Chatwoot uses [active storage](https://edgeguides.rubyonrails.org/active_storage_overview.html) for storing attachments. The default storage option is the local storage on your server. diff --git a/docs/development/project-setup/errors.md b/docs/development/project-setup/errors.md index d4e5b9e02..7453745f6 100644 --- a/docs/development/project-setup/errors.md +++ b/docs/development/project-setup/errors.md @@ -5,8 +5,35 @@ title: "Common Errors" ### Errors you might encounter while setting up the project + +#### Redis connection error + ```bash ArgumentError: invalid uri scheme ``` This is an error thrown from redis connector. You might not have setup the redis environment variables properly. Please refer to dependencies section to install redis-server and [Configure Redis URL](https://www.chatwoot.com/docs/environment-variables) in the environment-variables section. + + +#### pg gem Installation error + +If you see the following error while bundle installation, provide the Postgres path as pg_config. + +``` +Gem::Ext::BuildError: ERROR: Failed to build gem native extension. + +An error occurred while installing pg (1.2.3), and Bundler cannot +continue. +Make sure that `gem install pg -v '1.2.3' --source 'https://rubygems.org/'` +succeeds before bundling. + +checking for pg_config... no +No pg_config... trying anyway. If building fails, please try again with + --with-pg-config=/path/to/pg_config +``` + +To fix this, try executing + +``` +gem install pg -v '1.2.3' --source 'https://rubygems.org/' -- --with-pg-config=path-to-postgres-installation/12/bin/pg_config +``` diff --git a/docs/development/project-setup/quick-setup.md b/docs/development/project-setup/quick-setup.md index 2c802bead..8ad42acaf 100644 --- a/docs/development/project-setup/quick-setup.md +++ b/docs/development/project-setup/quick-setup.md @@ -11,6 +11,8 @@ Use the following command to install ruby dependencies. bundle ``` +If you face issue with pg gem, please refer to [Common Errors](/docs/common-errors#pg-gem-installation-error) + ### Install JavaScript dependencies ```bash @@ -19,6 +21,12 @@ yarn This would install all required dependencies for Chatwoot application. +### Setup environment variables. + +``` +cp .env.example .env +``` + Please refer to [environment-variables](./environment-variables) to read on setting environment variables. ### Setup rails server @@ -81,7 +89,7 @@ docker-compose run -rm server bundle exec rake db:reset This command essentially runs postgres and redis containers and then run the rake command inside the chatwoot server container. -### Running Cypress Tests +### Running Cypress Tests Refer the docs to learn how to write cypress specs https://github.com/shakacode/cypress-on-rails @@ -89,7 +97,7 @@ https://docs.cypress.io/guides/overview/why-cypress.html ``` # in terminal tab1 -overmind start -f Procfile.test +overmind start -f Procfile.test # in terminal tab2 yarn cypress open --project ./test ``` diff --git a/docs/self-hosted/enable-ip-logging.md b/docs/self-hosted/enable-ip-logging.md new file mode 100644 index 000000000..aba6edde8 --- /dev/null +++ b/docs/self-hosted/enable-ip-logging.md @@ -0,0 +1,32 @@ +--- +path: "/docs/self-hosted/enable-ip-logging" +title: "Chatwoot Production deployment guide" +--- + +Chatwoot allows you to identify the location of the user by geocoding the IP address. For IP Address geocoding, we support MaxmindDB services. This lookup provides methods for geocoding IP addresses without making a call to a remote API everytime. To setup your self-hosted instance with the geocoding, follow the steps below. + +**Step 1:** Create an account at [MaxmindDB](https://www.maxmind.com) and create an API key. + +**Step 2:** Add the following environment variables. + +```bash +IP_LOOKUP_SERVICE=geoip2 +IP_LOOKUP_API_KEY=your-api-key +``` + +With this step, Chatwoot would automatically download the [MaxmindDB downloadable databases](https://dev.maxmind.com/geoip/geoip2/downloadable/) and cache it locally. + +**Step 3:** Enable IP Lookup on your account. + +Login to Rails console + +``` +RAILS_ENV=production bundle exec rails console +``` + +```rb +account_id = 1 // Please fill your account id instead of 1 +account = Account.find(account_id) +account.enable_features('ip_lookup') +account.save! +``` diff --git a/lib/integrations/slack/channel_builder.rb b/lib/integrations/slack/channel_builder.rb index aa2aa992a..2092f3594 100644 --- a/lib/integrations/slack/channel_builder.rb +++ b/lib/integrations/slack/channel_builder.rb @@ -21,8 +21,8 @@ class Integrations::Slack::ChannelBuilder end def find_or_create_channel - exisiting_channel = slack_client.conversations_list.channels.find { |channel| channel['name'] == params[:channel] } - @channel = exisiting_channel || slack_client.conversations_create(name: params[:channel])['channel'] + existing_channel = slack_client.conversations_list.channels.find { |channel| channel['name'] == params[:channel] } + @channel = existing_channel || slack_client.conversations_create(name: params[:channel])['channel'] end def update_reference_id diff --git a/lib/integrations/slack/send_on_slack_service.rb b/lib/integrations/slack/send_on_slack_service.rb index 89c6d0b32..87040c04a 100644 --- a/lib/integrations/slack/send_on_slack_service.rb +++ b/lib/integrations/slack/send_on_slack_service.rb @@ -24,7 +24,11 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService def perform_reply send_message + + return unless @slack_message + update_reference_id + update_external_source_id_slack end def message_content @@ -68,6 +72,12 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService conversation.update!(identifier: @slack_message['ts']) end + def update_external_source_id_slack + return unless @slack_message['message'] + + message.update!(external_source_id_slack: "cw-origin-#{@slack_message['message']['ts']}") + end + def slack_client @slack_client ||= Slack::Web::Client.new(token: hook.access_token) end diff --git a/lib/redis/redis_keys.rb b/lib/redis/redis_keys.rb index 78a10e277..454a3715a 100644 --- a/lib/redis/redis_keys.rb +++ b/lib/redis/redis_keys.rb @@ -1,7 +1,17 @@ module Redis::RedisKeys + ## Inbox Keys + # Array storing the ordered ids for agent round robin assignment ROUND_ROBIN_AGENTS = 'ROUND_ROBIN_AGENTS:%d'.freeze + ## Conversation keys + # Detect whether to send an email reply to the conversation CONVERSATION_MAILER_KEY = 'CONVERSATION::%d'.freeze + # Whether a conversation is muted ? + CONVERSATION_MUTE_KEY = 'CONVERSATION::%d::MUTED'.freeze + + ## User Keys + # SSO Auth Tokens + USER_SSO_AUTH_TOKEN = 'USER_SSO_AUTH_TOKEN::%d::%s'.freeze ## Online Status Keys # hash containing user_id key and status as value @@ -12,6 +22,7 @@ module Redis::RedisKeys ONLINE_PRESENCE_USERS = 'ONLINE_PRESENCE::%d::USERS'.freeze ## Authorization Status Keys + # Used to track token expiry and such issues for facebook slack integrations etc AUTHORIZATION_ERROR_COUNT = 'AUTHORIZATION_ERROR_COUNT:%s:%d'.freeze REAUTHORIZATION_REQUIRED = 'REAUTHORIZATION_REQUIRED:%s:%d'.freeze end diff --git a/package.json b/package.json index caae6fd58..85a9b5bf8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chatwoot/chatwoot", - "version": "1.10.0", + "version": "1.11.0", "license": "MIT", "scripts": { "eslint": "eslint app/javascript --fix", @@ -21,10 +21,11 @@ "chart.js": "~2.5.0", "copy-text-to-clipboard": "^2.1.1", "core-js": "3", + "country-code-emoji": "^1.0.0", "date-fns": "^2.16.1", "dotenv": "^8.0.0", "foundation-sites": "~6.5.3", - "highlight.js": "^9.15.10", + "highlight.js": "~10.4.1", "ionicons": "~2.0.1", "js-cookie": "^2.2.1", "lodash.groupby": "^4.6.0", @@ -40,8 +41,7 @@ "vue-chartjs": "^3.4.2", "vue-clickaway": "~2.1.0", "vue-color": "^2.7.1", - "vue-highlight.js": "^3.1.0", - "vue-i18n": "~5.0.3", + "vue-i18n": "^8.22.1", "vue-loader": "^15.7.0", "vue-multiselect": "~2.1.6", "vue-router": "~2.2.0", diff --git a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb index 486a844b4..413b9cd94 100644 --- a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb @@ -90,7 +90,7 @@ RSpec.describe 'Contacts API', type: :request do context 'when it is an authenticated user' do let(:admin) { create(:user, account: account, role: :administrator) } let!(:contact1) { create(:contact, account: account) } - let!(:contact2) { create(:contact, account: account, email: 'test@test.com') } + let!(:contact2) { create(:contact, name: 'testcontact', account: account, email: 'test@test.com') } it 'returns all contacts with contact inboxes' do get "/api/v1/accounts/#{account.id}/contacts/search", @@ -102,6 +102,28 @@ RSpec.describe 'Contacts API', type: :request do expect(response.body).to include(contact2.email) expect(response.body).not_to include(contact1.email) end + + it 'matches the contact ignoring the case in email' do + get "/api/v1/accounts/#{account.id}/contacts/search", + params: { q: 'Test@Test.com' }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(contact2.email) + expect(response.body).not_to include(contact1.email) + end + + it 'matches the contact ignoring the case in name' do + get "/api/v1/accounts/#{account.id}/contacts/search", + params: { q: 'TestContact' }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(contact2.email) + expect(response.body).not_to include(contact1.email) + end end end diff --git a/spec/controllers/devise/session_controller_spec.rb b/spec/controllers/devise/session_controller_spec.rb index 0beb3ebfa..ef51f5e4b 100644 --- a/spec/controllers/devise/session_controller_spec.rb +++ b/spec/controllers/devise/session_controller_spec.rb @@ -30,5 +30,36 @@ RSpec.describe 'Session', type: :request do expect(response.body).to include(user.email) end end + + context 'when it is invalid sso auth token' do + let!(:user) { create(:user, password: 'test1234', account: account) } + + it 'returns unauthorized' do + params = { email: user.email, sso_auth_token: SecureRandom.hex(32) } + + post new_user_session_url, + params: params, + as: :json + expect(response).to have_http_status(:unauthorized) + expect(response.body).to include('Invalid login credentials') + end + end + + context 'when with valid sso auth token' do + let!(:user) { create(:user, password: 'test1234', account: account) } + + it 'returns successful auth response' do + params = { email: user.email, sso_auth_token: user.generate_sso_auth_token } + + post new_user_session_url, params: params, as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(user.email) + + # token won't work on a subsequent request + post new_user_session_url, params: params, as: :json + expect(response).to have_http_status(:unauthorized) + end + end end end diff --git a/spec/factories/contacts.rb b/spec/factories/contacts.rb index ec5046fbf..eaecd67e9 100644 --- a/spec/factories/contacts.rb +++ b/spec/factories/contacts.rb @@ -2,8 +2,8 @@ FactoryBot.define do factory :contact do - sequence(:name) { |n| "Widget #{n}" } - sequence(:email) { |n| "widget-#{n}@example.com" } + sequence(:name) { |n| "Contact #{n}" } + sequence(:email) { |n| "contact-#{n}@example.com" } phone_number { '+123456789011' } avatar { fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') } account diff --git a/spec/lib/integrations/slack/send_on_slack_service_spec.rb b/spec/lib/integrations/slack/send_on_slack_service_spec.rb index dc36de65f..234a2180b 100644 --- a/spec/lib/integrations/slack/send_on_slack_service_spec.rb +++ b/spec/lib/integrations/slack/send_on_slack_service_spec.rb @@ -1,50 +1,78 @@ require 'rails_helper' describe Integrations::Slack::SendOnSlackService do - let(:account) { create(:account) } - let!(:inbox) { create(:inbox, account: account) } let!(:contact) { create(:contact) } - + let!(:conversation) { create(:conversation, contact: contact, identifier: nil) } + let(:account) { conversation.account } let!(:hook) { create(:integrations_hook, account: account) } - let!(:conversation) { create(:conversation, account: account, inbox: inbox, contact: contact) } - let!(:message) { create(:message, account: account, inbox: inbox, conversation: conversation) } + let!(:message) do + create(:message, account: conversation.account, inbox: conversation.inbox, conversation: conversation) + end + let(:slack_message) { double } + let(:slack_message_content) { double } + let(:slack_client) { double } + let(:builder) { described_class.new(message: message, hook: hook) } + + before do + allow(builder).to receive(:slack_client).and_return(slack_client) + allow(slack_message).to receive(:[]).with('ts').and_return('12345.6789') + allow(slack_message).to receive(:[]).with('message').and_return(slack_message_content) + allow(slack_message_content).to receive(:[]).with('ts').and_return('6789.12345') + end describe '#perform' do - it 'sent message to slack' do - builder = described_class.new(message: message, hook: hook) - stub_request(:post, 'https://slack.com/api/chat.postMessage') - .to_return(status: 200, body: '', headers: {}) - slack_client = double - expect(builder).to receive(:slack_client).and_return(slack_client) + context 'without identifier' do + it 'updates slack thread id in conversation' do + inbox = conversation.inbox - expect(slack_client).to receive(:chat_postMessage).with( - channel: hook.reference_id, - text: message.content, - username: "Contact: #{message.sender.name}", - thread_ts: conversation.identifier, - icon_url: anything - ) + expect(slack_client).to receive(:chat_postMessage).with( + channel: hook.reference_id, + text: "*Inbox: #{inbox.name} [#{inbox.inbox_type}]* \n\n #{message.content}", + username: "Contact: #{message.sender.name}", + thread_ts: nil, + icon_url: anything + ).and_return(slack_message) - builder.perform + builder.perform + + expect(conversation.reload.identifier).to eq '12345.6789' + end end - it 'disables hook on Slack AccountInactive error' do - builder = described_class.new(message: message, hook: hook) - slack_client = double - expect(builder).to receive(:slack_client).and_return(slack_client) - expect(slack_client).to receive(:chat_postMessage).with( - channel: hook.reference_id, - text: message.content, - username: "Contact: #{message.sender.name}", - thread_ts: conversation.identifier, - icon_url: anything - ).and_raise(Slack::Web::Api::Errors::AccountInactive.new('Account disconnected')) + context 'with identifier' do + before do + conversation.update!(identifier: 'random_slack_thread_ts') + end - allow(hook).to receive(:authorization_error!) + it 'sent message to slack' do + expect(slack_client).to receive(:chat_postMessage).with( + channel: hook.reference_id, + text: message.content, + username: "Contact: #{message.sender.name}", + thread_ts: conversation.identifier, + icon_url: anything + ).and_return(slack_message) - builder.perform - expect(hook).to be_disabled - expect(hook).to have_received(:authorization_error!) + builder.perform + + expect(message.external_source_id_slack).to eq 'cw-origin-6789.12345' + end + + it 'disables hook on Slack AccountInactive error' do + expect(slack_client).to receive(:chat_postMessage).with( + channel: hook.reference_id, + text: message.content, + username: "Contact: #{message.sender.name}", + thread_ts: conversation.identifier, + icon_url: anything + ).and_raise(Slack::Web::Api::Errors::AccountInactive.new('Account disconnected')) + + allow(hook).to receive(:authorization_error!) + + builder.perform + expect(hook).to be_disabled + expect(hook).to have_received(:authorization_error!) + end end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0321fb93a..225eedeb7 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -26,4 +26,25 @@ RSpec.describe User do it { expect(user.pubsub_token).not_to eq(nil) } it { expect(user.saved_changes.keys).not_to eq('pubsub_token') } end + + context 'sso_auth_token' do + it 'can generate multiple sso tokens which can be validated' do + sso_auth_token1 = user.generate_sso_auth_token + sso_auth_token2 = user.generate_sso_auth_token + expect(sso_auth_token1).present? + expect(sso_auth_token2).present? + expect(user.valid_sso_auth_token?(sso_auth_token1)).to eq true + expect(user.valid_sso_auth_token?(sso_auth_token2)).to eq true + end + + it 'wont validate an invalid token' do + expect(user.valid_sso_auth_token?(SecureRandom.hex(32))).to eq false + end + + it 'wont validate an invalidated token' do + sso_auth_token = user.generate_sso_auth_token + user.invalidate_sso_auth_token(sso_auth_token) + expect(user.valid_sso_auth_token?(sso_auth_token)).to eq false + end + end end diff --git a/yarn.lock b/yarn.lock index 6a816b2ed..189b91323 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3213,6 +3213,11 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +country-code-emoji@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/country-code-emoji/-/country-code-emoji-1.0.0.tgz#7c77791839c9e9921beec08ef080caa7cfb8b40c" + integrity sha512-fBM5A49oZkOxOVb0bx7q7Hanlfh8e3z/r6/ZnFhbL57JXGIgWPC2HYrjXEyiGML7OFftDV/WfAlJdDkoAbj1Rg== + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -3747,11 +3752,6 @@ detect-file@^1.0.0: resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= -detect-indent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" - integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -5228,10 +5228,10 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -highlight.js@^9.15.10: - version "9.18.1" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c" - integrity sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg== +highlight.js@~10.4.1: + version "10.4.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" + integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== hmac-drbg@^1.0.0: version "1.0.1" @@ -5545,9 +5545,9 @@ inherits@2.0.3: integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@^1.3.4, ini@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + version "1.3.7" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" + integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== inquirer@^7.0.0: version "7.1.0" @@ -9238,14 +9238,6 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -redent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" - integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= - dependencies: - indent-string "^3.0.0" - strip-indent "^2.0.0" - reduce-css-calc@^2.1.6: version "2.1.7" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2" @@ -10310,11 +10302,6 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-indent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" - integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= - strip-json-comments@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -10718,7 +10705,7 @@ tsconfig@^7.0.0: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.9.0: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== @@ -11062,24 +11049,15 @@ vue-eslint-parser@^7.0.0: esquery "^1.0.1" lodash "^4.17.15" -vue-highlight.js@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/vue-highlight.js/-/vue-highlight.js-3.1.0.tgz#87b60b4931fd310b318f2b2c9116fe71b69dd053" - integrity sha512-i55SERtdV0CYQppGo29iT6NOq+oOenOKVwkLWZRt7bSynbsQoj/e8GJy/5xL1s5OOYObC/CxA39bRadVyPQt1A== - dependencies: - detect-indent "^5.0.0" - redent "^2.0.0" - tslib "^1.9.3" - vue-hot-reload-api@^2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog== -vue-i18n@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-5.0.3.tgz#b6d96cc832604237e6139de471e0d4c820aedbed" - integrity sha1-ttlsyDJgQjfmE53kceDUyCCu2+0= +vue-i18n@^8.22.1: + version "8.22.1" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.22.1.tgz#b9dd098a17e1f5adb91bdf9611f0385310da7cb1" + integrity sha512-JNgiEJ5a8YPfk5y2lKyfOAGLmkpAVfhaUi+T4wGpSppRYZ3XSyawSDDketY5KV2CsAiBLAGEIO6jO+0l2hQubg== vue-jest@^3.0.5: version "3.0.5"