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