diff --git a/backend/app/assets/javascripts/spree/backend.js b/backend/app/assets/javascripts/spree/backend.js
index c313a8e319a..3bf92462153 100644
--- a/backend/app/assets/javascripts/spree/backend.js
+++ b/backend/app/assets/javascripts/spree/backend.js
@@ -37,6 +37,7 @@
//= require spree/backend/handlebars_extensions
//= require spree/backend/images/index
//= require spree/backend/images/upload
+//= require spree/backend/locale_selection
//= require spree/backend/navigation
//= require spree/backend/option_type_autocomplete
//= require spree/backend/option_value_picker
diff --git a/backend/app/assets/javascripts/spree/backend/locale_selection.js b/backend/app/assets/javascripts/spree/backend/locale_selection.js
new file mode 100644
index 00000000000..24a60533622
--- /dev/null
+++ b/backend/app/assets/javascripts/spree/backend/locale_selection.js
@@ -0,0 +1,18 @@
+Spree.ready(function() {
+ var localeSelect = document.querySelector('.js-locale-selection');
+ if (!localeSelect) return;
+
+ localeSelect.addEventListener('change', function() {
+ Spree.ajax({
+ type: "PUT",
+ dataType: "json",
+ url: Spree.pathFor("admin/locale/set"),
+ data: {
+ switch_to_locale: localeSelect.value
+ },
+ success: function(data) {
+ document.location.href = data.location;
+ }
+ });
+ });
+});
diff --git a/backend/app/assets/stylesheets/spree/backend/components/_navigation.scss b/backend/app/assets/stylesheets/spree/backend/components/_navigation.scss
index 7c9d6b718b4..8f2fd2978d6 100644
--- a/backend/app/assets/stylesheets/spree/backend/components/_navigation.scss
+++ b/backend/app/assets/stylesheets/spree/backend/components/_navigation.scss
@@ -160,6 +160,11 @@ nav.menu {
}
}
+.admin-locale-selection {
+ margin: 1em;
+ margin-bottom: 0;
+}
+
.admin-nav.fits .admin-nav-footer {
position: fixed;
bottom: 0;
diff --git a/backend/app/controllers/spree/admin/locale_controller.rb b/backend/app/controllers/spree/admin/locale_controller.rb
new file mode 100644
index 00000000000..a21d1ca2ce6
--- /dev/null
+++ b/backend/app/controllers/spree/admin/locale_controller.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Spree
+ module Admin
+ class LocaleController < Spree::Admin::BaseController
+ def set
+ locale = params[:switch_to_locale].to_s.presence
+
+ if locale && I18n.available_locales.include?(locale.to_sym)
+ I18n.locale = locale
+ session[:locale] = locale
+
+ respond_to do |format|
+ format.json { render json: { locale: locale, location: spree.admin_url } }
+ end
+ else
+ respond_to do |format|
+ format.json { render json: { locale: I18n.locale }, status: 404 }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/backend/app/views/spree/admin/shared/_locale_selection.html.erb b/backend/app/views/spree/admin/shared/_locale_selection.html.erb
new file mode 100644
index 00000000000..aa745688cc8
--- /dev/null
+++ b/backend/app/views/spree/admin/shared/_locale_selection.html.erb
@@ -0,0 +1,15 @@
+<% available_locales = Spree.i18n_available_locales %>
+<% if available_locales.size > 1 %>
+
+
+
+<% end %>
diff --git a/backend/app/views/spree/admin/shared/_navigation.html.erb b/backend/app/views/spree/admin/shared/_navigation.html.erb
index 42390adb71f..7f4bbc018e4 100644
--- a/backend/app/views/spree/admin/shared/_navigation.html.erb
+++ b/backend/app/views/spree/admin/shared/_navigation.html.erb
@@ -3,6 +3,7 @@
<%= render partial: 'spree/admin/shared/menu' %>
diff --git a/backend/config/routes.rb b/backend/config/routes.rb
index c82888713fd..318d68806c4 100644
--- a/backend/config/routes.rb
+++ b/backend/config/routes.rb
@@ -5,6 +5,8 @@
get '/search/users', to: "search#users", as: :search_users
get '/search/products', to: "search#products", as: :search_products
+ put '/locale/set', to: 'locale#set', defaults: { format: :json }, as: :set_locale
+
resources :dashboards, only: [] do
collection do
get :home
diff --git a/backend/spec/features/admin/locale_spec.rb b/backend/spec/features/admin/locale_spec.rb
index 9b8017d0d85..72fb641e886 100644
--- a/backend/spec/features/admin/locale_spec.rb
+++ b/backend/spec/features/admin/locale_spec.rb
@@ -13,6 +13,7 @@
month_names: []
},
spree: {
+ i18n: { this_file_language: "Français" },
admin: {
tab: { orders: "Ordres" }
},
diff --git a/core/lib/spree/core/controller_helpers/common.rb b/core/lib/spree/core/controller_helpers/common.rb
index bdae648043b..86c90f7e562 100644
--- a/core/lib/spree/core/controller_helpers/common.rb
+++ b/core/lib/spree/core/controller_helpers/common.rb
@@ -48,10 +48,17 @@ def accurate_title
private
def set_user_language
- locale = session[:locale]
- locale ||= config_locale if respond_to?(:config_locale, true)
- locale ||= Rails.application.config.i18n.default_locale
- locale ||= I18n.default_locale
+ available_locales = Spree.i18n_available_locales
+ locale = [
+ params[:locale],
+ session[:locale],
+ (config_locale if respond_to?(:config_locale, true)),
+ I18n.default_locale
+ ].detect do |candidate|
+ candidate &&
+ available_locales.include?(candidate.to_sym)
+ end
+ session[:locale] = locale
I18n.locale = locale
Carmen.i18n_backend.locale = locale
end
diff --git a/core/lib/spree/i18n.rb b/core/lib/spree/i18n.rb
index 8ffbd076e66..873c1494ec6 100644
--- a/core/lib/spree/i18n.rb
+++ b/core/lib/spree/i18n.rb
@@ -5,6 +5,12 @@
require 'action_view'
module Spree
+ def self.i18n_available_locales
+ I18n.available_locales.select do |locale|
+ I18n.t('spree.i18n.this_file_language', locale: locale, fallback: false, default: nil)
+ end
+ end
+
class TranslationHelperWrapper # :nodoc:
include ActionView::Helpers::TranslationHelper
end
diff --git a/core/spec/lib/i18n_spec.rb b/core/spec/lib/i18n_spec.rb
index 4e4c01ff751..231876aac6e 100644
--- a/core/spec/lib/i18n_spec.rb
+++ b/core/spec/lib/i18n_spec.rb
@@ -5,9 +5,15 @@
RSpec.describe "i18n" do
before do
+ # This reload avoids an issue with I18n.available_locales being cached
+ I18n.reload!
+
I18n.backend.store_translations(:en,
{
spree: {
+ i18n: {
+ this_file_language: "English"
+ },
foo: "bar",
bar: {
foo: "bar within bar scope",
@@ -19,6 +25,9 @@
}
})
end
+ after do
+ I18n.reload!
+ end
it "translates within the spree scope" do
expect(Spree.t(:foo)).to eql("bar")
@@ -44,4 +53,40 @@
it "should have a Spree::I18N_GENERIC_PLURAL constant" do
expect(Spree::I18N_GENERIC_PLURAL).to eq 2.1
end
+
+ describe "i18n_available_locales" do
+ it "should only return :en" do
+ expect(Spree.i18n_available_locales).to eq([:en])
+ end
+
+ context 'with unprefixed translations in another locale' do
+ before do
+ I18n.backend.store_translations(:fr, { cheese: "fromage" })
+ end
+
+ it "should only return :en" do
+ expect(Spree.i18n_available_locales).to eq([:en])
+ end
+ end
+
+ context 'with spree-prefixed translations in another locale' do
+ before do
+ I18n.backend.store_translations(:fr, spree: { cheese: "fromage" })
+ end
+
+ it "should return :en and :fr" do
+ expect(Spree.i18n_available_locales).to eq([:en])
+ end
+ end
+
+ context 'with specific desired key' do
+ before do
+ I18n.backend.store_translations(:fr, spree: { i18n: { this_file_language: "Français" } })
+ end
+
+ it "should return :en and :fr" do
+ expect(Spree.i18n_available_locales).to eq([:en, :fr])
+ end
+ end
+ end
end
diff --git a/frontend/spec/controllers/controller_helpers_spec.rb b/frontend/spec/controllers/controller_helpers_spec.rb
index 7c1e3e1d5b1..dcc8318ec19 100644
--- a/frontend/spec/controllers/controller_helpers_spec.rb
+++ b/frontend/spec/controllers/controller_helpers_spec.rb
@@ -9,9 +9,13 @@
before do
I18n.enforce_available_locales = false
Spree::Frontend::Config[:locale] = :de
+ I18n.backend.store_translations(:de, spree: {
+ i18n: { this_file_language: "Deutsch (DE)" }
+ })
end
after do
+ I18n.reload!
Spree::Frontend::Config[:locale] = :en
I18n.locale = :en
I18n.enforce_available_locales = true
diff --git a/frontend/spec/features/locale_spec.rb b/frontend/spec/features/locale_spec.rb
index 533fd088893..c1b9b9accfe 100644
--- a/frontend/spec/features/locale_spec.rb
+++ b/frontend/spec/features/locale_spec.rb
@@ -15,13 +15,17 @@ def with_locale(locale)
context 'shopping cart link and page' do
before do
- I18n.backend.store_translations(:fr,
- spree: {
- cart: 'Panier',
- shopping_cart: 'Panier'
+ I18n.backend.store_translations(:fr, spree: {
+ i18n: { this_file_language: "Français" },
+ cart: 'Panier',
+ shopping_cart: 'Panier'
})
end
+ after do
+ I18n.reload!
+ end
+
it 'should be in french' do
with_locale('fr') do
visit spree.root_path