diff --git a/Gemfile.lock b/Gemfile.lock
index 5f93136..5d56ca7 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -191,6 +191,8 @@ GEM
net-smtp (0.3.3)
net-protocol
nio4r (2.5.9)
+ nokogiri (1.15.4-x86_64-darwin)
+ racc (~> 1.4)
nokogiri (1.15.4-x86_64-linux)
racc (~> 1.4)
orm_adapter (0.5.0)
@@ -340,6 +342,7 @@ GEM
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
+ sqlite3 (1.6.4-x86_64-darwin)
sqlite3 (1.6.4-x86_64-linux)
stimulus-rails (1.2.2)
railties (>= 6.0.0)
@@ -375,6 +378,7 @@ GEM
zeitwerk (2.6.11)
PLATFORMS
+ x86_64-darwin-22
x86_64-linux
DEPENDENCIES
diff --git a/app/assets/images/icons/en.svg b/app/assets/images/icons/en.svg
new file mode 100644
index 0000000..a722047
--- /dev/null
+++ b/app/assets/images/icons/en.svg
@@ -0,0 +1,9 @@
+
diff --git a/app/assets/images/icons/ru.svg b/app/assets/images/icons/ru.svg
new file mode 100644
index 0000000..485c24e
--- /dev/null
+++ b/app/assets/images/icons/ru.svg
@@ -0,0 +1,7 @@
+
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 7b0f46e..4053b6a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,14 +1,32 @@
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
+ before_action :set_current_locale
+ helper_method :current_locale
rescue_from ActionPolicy::Unauthorized do |_e|
redirect_to root_path
end
+ def set_current_locale
+ Current.locale = extract_locale_from_user || extract_locale_from_session || I18n.default_locale
+ end
+
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: %i[name avatar])
devise_parameter_sanitizer.permit(:account_update, keys: %i[name avatar])
end
+
+ def current_locale
+ Current.locale
+ end
+
+ def extract_locale_from_user
+ current_user&.locale if defined?(current_user) && current_user&.locale
+ end
+
+ def extract_locale_from_session
+ session[:locale]
+ end
end
diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb
index a15ef40..51f948e 100644
--- a/app/controllers/pages_controller.rb
+++ b/app/controllers/pages_controller.rb
@@ -1,3 +1,33 @@
class PagesController < ApplicationController
def welcome; end
+
+ def switch_locale
+ Current.locale = locale
+ I18n.locale = Current.locale
+ store_locale
+
+ redirect_back(fallback_location: root_path)
+ end
+
+ private
+
+ def params_locale
+ params[:locale].to_sym
+ end
+
+ def default_locale
+ @default_locale ||= current_user&.locale || I18n.default_locale
+ end
+
+ def locale
+ I18n.available_locales.include?(params_locale) ? params_locale : default_locale
+ end
+
+ def store_locale
+ if user_signed_in?
+ current_user.update(locale: I18n.locale)
+ else
+ session[:locale] = I18n.locale
+ end
+ end
end
diff --git a/app/helpers/navbar_helper.rb b/app/helpers/navbar_helper.rb
new file mode 100644
index 0000000..5753c44
--- /dev/null
+++ b/app/helpers/navbar_helper.rb
@@ -0,0 +1,11 @@
+module NavbarHelper
+ def selected_language_icon
+ Rails.cache.fetch("selected_language_name/#{current_locale}", expires_in: 1.day) do
+ image_tag "icons/#{current_locale}.svg", size: "23", class: "rounded-2", title: t("navbar.language")
+ end
+ end
+
+ def next_locale
+ I18n.locale == :ru ? :en : :ru
+ end
+end
diff --git a/app/models/current.rb b/app/models/current.rb
new file mode 100644
index 0000000..43c511a
--- /dev/null
+++ b/app/models/current.rb
@@ -0,0 +1,3 @@
+class Current < ActiveSupport::CurrentAttributes
+ thread_mattr_accessor :locale
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 7ba6348..9d3bdc4 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -30,10 +30,29 @@ class User < ApplicationRecord
foreign_key: :recipient_id, dependent: :destroy, inverse_of: :recipient
enum :role, %i[applicant company moderator admin]
+ # Нежелательное использование колбека в модели, но это врменное решение до определения логики devise
+ before_validation :set_name, if: -> { name.blank? }, on: :create
validates :locale, presence: true
def owner?(vacancy)
id == vacancy.user_id
end
+
+ def display_name
+ name || name_from_email
+ end
+
+ private
+
+ def set_name
+ self.name = name_from_email
+ end
+
+ def name_from_email
+ name_part = email.split("@").first
+ return name_part if [".", "_", "-"].include?(name_part[1])
+
+ name_part.humanize
+ end
end
diff --git a/app/views/layouts/_header.html.slim b/app/views/layouts/_header.html.slim
index e1f063e..f1b7f55 100644
--- a/app/views/layouts/_header.html.slim
+++ b/app/views/layouts/_header.html.slim
@@ -13,10 +13,18 @@ nav.navbar.navbar-expand-lg.bg-body-tertiary
- if user_signed_in?
li.nav-item
= link_to "My Vacancies", my_vacancies_vacancies_path, class: "nav-link fs-4 text-black"
- ul.navbar-nav.ms-3
+ ul.navbar-nav.ms-3.gap-1
+ li.nav-item[data-turbo="false"]
+ = link_to switch_locale_path(locale: next_locale), class: "btn btn-outline-primary border-0 me-2" do
+ = selected_language_icon
+
li.nav-item.dropdown
- if user_signed_in?
- = link_to current_user.name? ? current_user.name : current_user.email, "#", class: "dropdown-toggle btn btn-outline-primary", id: "navbarDropdown", role: "button", "data-bs-toggle" => "dropdown", "aria-expanded" => "false"
+ = link_to "javascript:void(0);",
+ class: "dropdown-toggle btn btn-outline-primary", id: "navbarDropdown", role: "button",
+ "data-bs-toggle" => "dropdown", "aria-expanded" => "false" do
+ span.ms-1.me-2
+ = current_user.display_name
.dropdown-menu[aria-labelledby="navbarDropdown"]
= link_to "Edit profile", edit_user_registration_path, class: "dropdown-item"
.li.hr.dropdown-divider
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 8ca56fc..8bd27a9 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -31,3 +31,5 @@
en:
hello: "Hello world"
+ navbar:
+ language: Change language
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 81161ed..c351439 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -269,3 +269,6 @@ ru:
long: "%d %B %Y, %H:%M"
short: "%d %b, %H:%M"
pm: вечера
+ navbar:
+ language: Смена языка
+
diff --git a/config/routes.rb b/config/routes.rb
index 0537a94..4f43680 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -6,4 +6,6 @@
resources :vacancies do
get :my_vacancies, on: :collection
end
+
+ get "/switch_locale/:locale", to: "pages#switch_locale", as: :switch_locale
end
diff --git a/db/schema.rb b/db/schema.rb
index f55922e..2de1faa 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -24,8 +24,8 @@
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
- t.integer "record_id", null: false
- t.integer "blob_id", null: false
+ t.bigint "record_id", null: false
+ t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
@@ -44,7 +44,7 @@
end
create_table "active_storage_variant_records", force: :cascade do |t|
- t.integer "blob_id", null: false
+ t.bigint "blob_id", null: false
t.string "variation_digest", null: false
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
diff --git a/spec/requests/pages_controller_spec.rb b/spec/requests/pages_controller_spec.rb
new file mode 100644
index 0000000..c4766ba
--- /dev/null
+++ b/spec/requests/pages_controller_spec.rb
@@ -0,0 +1,72 @@
+require "rails_helper"
+
+describe PagesController, type: :request do
+ describe "GET /welcome" do
+ it "returns http status success" do
+ get root_url
+ expect(response).to have_http_status(:success)
+ end
+ end
+
+ describe "GET #switch_locale" do
+ let(:user) { create(:user) }
+
+ context "when user is signed in" do
+ before { sign_in user }
+
+ it "changes the I18n locale" do
+ get switch_locale_path(locale: "ru")
+ expect(I18n.locale).to eq(:ru)
+ end
+
+ it "changes the user locale" do
+ get switch_locale_path(locale: "ru")
+ expect(user.reload.locale).to eq("ru")
+ end
+
+ it "clears the session locale" do
+ get switch_locale_path(locale: "ru")
+ expect(session[:locale]).to be_nil
+ end
+
+ it "redirects back" do
+ get switch_locale_path(locale: "ru")
+ expect(response).to redirect_to(root_path)
+ end
+ end
+
+ context "when user is not signed in" do
+ it "changes the I18n locale" do
+ get switch_locale_path(locale: "ru")
+ expect(I18n.locale).to eq(:ru)
+ end
+
+ it "changes the session locale" do
+ get switch_locale_path(locale: "ru")
+ expect(session[:locale]).to eq(:ru)
+ end
+
+ it "redirects back" do
+ get switch_locale_path(locale: "ru")
+ expect(response).to redirect_to(root_path)
+ end
+ end
+
+ context "when the requested locale is not available" do
+ it "falls back to the default I18n locale" do
+ get switch_locale_path(locale: "invalid_locale")
+ expect(I18n.locale).to eq(:en)
+ end
+
+ it "falls back to the default session locale" do
+ get switch_locale_path(locale: "invalid_locale")
+ expect(session[:locale]).to eq(:en)
+ end
+
+ it "redirects back" do
+ get switch_locale_path(locale: "invalid_locale")
+ expect(response).to redirect_to(root_path)
+ end
+ end
+ end
+end
diff --git a/spec/requests/pages_spec.rb b/spec/requests/pages_spec.rb
deleted file mode 100644
index 7d39ee9..0000000
--- a/spec/requests/pages_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-require "rails_helper"
-
-describe "GET /welcome" do
- it "returns http status success" do
- get root_url
- expect(response).to have_http_status(:success)
- end
-end