From fb369c14a00fabeb86df8991bb21ee3ac6c14968 Mon Sep 17 00:00:00 2001 From: "Ryan B. Harvey" Date: Sat, 17 Aug 2019 02:15:37 -0500 Subject: [PATCH 1/3] Implement internationalization based on URL path not query string --- rails/app/assets/stylesheets/application.scss | 4 ++ .../app/controllers/application_controller.rb | 20 ++++---- rails/app/views/layouts/application.html.erb | 1 + rails/app/views/shared/_locale_links.html.erb | 10 ++++ rails/config/locales/fr.yml | 1 + rails/config/routes.rb | 48 ++++++++++--------- 6 files changed, 54 insertions(+), 30 deletions(-) create mode 100644 rails/app/views/shared/_locale_links.html.erb diff --git a/rails/app/assets/stylesheets/application.scss b/rails/app/assets/stylesheets/application.scss index 5aea03d1..141de34d 100644 --- a/rails/app/assets/stylesheets/application.scss +++ b/rails/app/assets/stylesheets/application.scss @@ -166,6 +166,10 @@ $highlight-text: #C06D19; font-size: 1.8rem; } + #active-lang { + color: $white; + } + // Buttons // a.btn { diff --git a/rails/app/controllers/application_controller.rb b/rails/app/controllers/application_controller.rb index b6f6154b..369aaaa9 100644 --- a/rails/app/controllers/application_controller.rb +++ b/rails/app/controllers/application_controller.rb @@ -2,18 +2,22 @@ class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? before_action :authenticate_user! - before_action :set_locale + around_action :set_locale - def set_locale - I18n.locale = I18n.default_locale + protected + + def set_locale(&action) locale = params[:locale].to_s.strip.to_sym - - I18n.locale = locale if I18n.available_locales.include?(locale) + if (!I18n.available_locales.include?(locale)) + locale = I18n.default_locale + end + I18n.with_locale(locale, &action) end - # def default_url_options(options = {}) - # options.merge(locale: I18n.locale) - # end + def default_url_options(options = {}) + # options.merge(locale: I18n.locale) + { locale: I18n.locale == I18n.default_locale ? I18n.default_locale : I18n.locale } + end protected diff --git a/rails/app/views/layouts/application.html.erb b/rails/app/views/layouts/application.html.erb index 02e2e332..ef487375 100644 --- a/rails/app/views/layouts/application.html.erb +++ b/rails/app/views/layouts/application.html.erb @@ -49,6 +49,7 @@ <%= link_to t('.logout'), destroy_user_session_path, method: :delete, data: { confirm: 'Please confirm you wish to log out.' }, :class =>"nav-link" %> <% end %> + <%= render 'shared/locale_links' %> <%# End navbar %> diff --git a/rails/app/views/shared/_locale_links.html.erb b/rails/app/views/shared/_locale_links.html.erb new file mode 100644 index 00000000..fb165782 --- /dev/null +++ b/rails/app/views/shared/_locale_links.html.erb @@ -0,0 +1,10 @@ + diff --git a/rails/config/locales/fr.yml b/rails/config/locales/fr.yml index e831a905..87a96b01 100644 --- a/rails/config/locales/fr.yml +++ b/rails/config/locales/fr.yml @@ -42,6 +42,7 @@ fr: title: "En plongée #%{dive_id}" finished_at_log_message: "Terminé à %{finished_at}." finish_button: "Terminer la plongée" + log_restoration: "Enregistrer une activité de restauration" nursery_tables: index: title: "Tables de Pépinière" diff --git a/rails/config/routes.rb b/rails/config/routes.rb index db665b79..88797655 100644 --- a/rails/config/routes.rb +++ b/rails/config/routes.rb @@ -1,30 +1,34 @@ Rails.application.routes.draw do - root to: "home#index" + get "/*path", to: redirect("/#{I18n.default_locale}/%{path}"), + constraints: { + path: %r{(?!(#{I18n.available_locales.join('|')})).*} + } + # I18n.available_locales.map(&:to_s).includes?(path.split('/').first) - # User routes - devise_for :users - resources :users, only: %i[index show] - devise_scope :user do - get "sign_in", to: "devise/sessions#new" - get "sign_up", to: "devise/registrations#new" - end + scope ":locale", locale: /#{I18n.available_locales.join("|")}/ do + root to: "home#index" - resources :restoration_activity_log_entries - resources :nursery_tables - resources :zones - resources :dives, only: %i[new create show] do - post :finish, on: :member - resources :restoration_activity_log_entries, only: %i[new create] - end + # User routes + devise_for :users + resources :users, only: %i[index show] + devise_scope :user do + get "sign_in", to: "devise/sessions#new" + get "sign_up", to: "devise/registrations#new" + end - devise_scope :user do - get "sign_in", to: "devise/sessions#new" - get "sign_up", to: "devise/registrations#new" - end + resources :restoration_activity_log_entries + resources :nursery_tables + resources :zones + resources :dives, only: %i[new create show] do + post :finish, on: :member + resources :restoration_activity_log_entries, only: %i[new create] + end - resources :users, only: %i[index show] + devise_scope :user do + get "sign_in", to: "devise/sessions#new" + get "sign_up", to: "devise/registrations#new" + end - scope "(:locale)", locale: /en|fr/ do - root to: "home#index" + resources :users, only: %i[index show] end end From 98f1f9572e86e92f48478df2d85a675c73f93b82 Mon Sep 17 00:00:00 2001 From: "Ryan B. Harvey" Date: Mon, 19 Aug 2019 22:02:31 -0500 Subject: [PATCH 2/3] Path-based I18n using Rack::Rewrite for root paths with no locale --- rails/Gemfile | 2 + rails/Gemfile.lock | 4 +- .../app/controllers/application_controller.rb | 4 +- rails/app/views/dives/new.html.erb | 4 +- rails/app/views/dives/show.html.erb | 4 +- rails/config/application.rb | 15 ++++ rails/config/routes.rb | 6 -- rails/spec/helpers.rb | 8 +++ rails/spec/rails_helper.rb | 3 + rails/spec/requests/dives_spec.rb | 70 ++++++++++++------- rails/spec/requests/home_spec.rb | 17 ++++- rails/spec/requests/nursery_tables_spec.rb | 4 +- 12 files changed, 96 insertions(+), 45 deletions(-) create mode 100644 rails/spec/helpers.rb diff --git a/rails/Gemfile b/rails/Gemfile index f0c04e83..501dc88a 100644 --- a/rails/Gemfile +++ b/rails/Gemfile @@ -18,6 +18,8 @@ gem "webpacker" # See https://github.com/rails/execjs#readme for more supported runtimes # gem 'mini_racer', platforms: :ruby gem "coffee-rails" +# Use rack-rewrite to ensure default locale is always used +gem "rack-rewrite", "~> 1.5.1" # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks gem "turbolinks", "~> 5" diff --git a/rails/Gemfile.lock b/rails/Gemfile.lock index 0ce87c43..36c5f16f 100644 --- a/rails/Gemfile.lock +++ b/rails/Gemfile.lock @@ -151,6 +151,7 @@ GEM rack rack-proxy (0.6.5) rack + rack-rewrite (1.5.1) rack-test (1.1.0) rack (>= 1.0, < 3) rails (5.2.3) @@ -306,6 +307,7 @@ DEPENDENCIES pg (>= 0.18, < 2.0) pry puma (~> 3.11) + rack-rewrite (~> 1.5.1) rails (~> 5.2.3) rails-i18n rspec-rails @@ -329,4 +331,4 @@ RUBY VERSION ruby 2.6.3p62 BUNDLED WITH - 2.0.1 + 2.0.2 diff --git a/rails/app/controllers/application_controller.rb b/rails/app/controllers/application_controller.rb index 369aaaa9..7de7bc43 100644 --- a/rails/app/controllers/application_controller.rb +++ b/rails/app/controllers/application_controller.rb @@ -5,7 +5,7 @@ class ApplicationController < ActionController::Base around_action :set_locale protected - + def set_locale(&action) locale = params[:locale].to_s.strip.to_sym if (!I18n.available_locales.include?(locale)) @@ -19,8 +19,6 @@ def default_url_options(options = {}) { locale: I18n.locale == I18n.default_locale ? I18n.default_locale : I18n.locale } end - protected - def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: %i[name email password password_confirmation]) devise_parameter_sanitizer.permit(:account_update, keys: %i[name email password password_confirmation current_password]) diff --git a/rails/app/views/dives/new.html.erb b/rails/app/views/dives/new.html.erb index 05da32d8..7a029459 100644 --- a/rails/app/views/dives/new.html.erb +++ b/rails/app/views/dives/new.html.erb @@ -1,6 +1,6 @@ -

<%=t '.title' %>

+

<%= t('.title') %>

-

<%=t '.checklist_preface' %>

+

<%= t('.checklist_preface') %>

<%= simple_form_for dive do |f| %> <%= f.input :i_have_been_responsible, as: :boolean %> diff --git a/rails/app/views/dives/show.html.erb b/rails/app/views/dives/show.html.erb index 19322a11..dc5a43e5 100644 --- a/rails/app/views/dives/show.html.erb +++ b/rails/app/views/dives/show.html.erb @@ -1,7 +1,7 @@ -

<%=t '.title', dive_id: dive.id %>

+

<%= t('.title', dive_id: dive.id) %>

<%- if dive.finished? %> - <%=t '.finished_at_log_message', finished_at: dive.finished_at.to_formatted_s(:db) %> + <%= t('.finished_at_log_message', finished_at: dive.finished_at.to_formatted_s(:db)) %> <%- else %> diff --git a/rails/config/application.rb b/rails/config/application.rb index 602e7bc3..fc702b8f 100644 --- a/rails/config/application.rb +++ b/rails/config/application.rb @@ -12,6 +12,7 @@ require "action_cable/engine" require "sprockets/railtie" # require "rails/test_unit/railtie" +require "rack/rewrite" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. @@ -34,5 +35,19 @@ class Application < Rails::Application config.i18n.default_locale = :en config.i18n.available_locales = %i[en fr] + + # Rewrite non-locale-specific URLs to use default locale before processing + locales_neg_regex = "#{ + config.i18n.available_locales.map{ |l| + "\\/#{l.to_s}$|\\/#{l.to_s}\\/" + }.join("|") + }" + default_locale = config.i18n.default_locale.to_s + puts locales_neg_regex + config.middleware.insert_before(Rack::Runtime, Rack::Rewrite) do + rewrite %r{^(https?\:\/\/(?:[\w\.]+)(?::\d+)?)?((?!#{locales_neg_regex})\/[\w\/\?,\-=%+]+)$}, + "$1/#{default_locale}/$2" + rewrite '/', "/#{default_locale}/" + end end end diff --git a/rails/config/routes.rb b/rails/config/routes.rb index 88797655..26747b9f 100644 --- a/rails/config/routes.rb +++ b/rails/config/routes.rb @@ -1,10 +1,4 @@ Rails.application.routes.draw do - get "/*path", to: redirect("/#{I18n.default_locale}/%{path}"), - constraints: { - path: %r{(?!(#{I18n.available_locales.join('|')})).*} - } - # I18n.available_locales.map(&:to_s).includes?(path.split('/').first) - scope ":locale", locale: /#{I18n.available_locales.join("|")}/ do root to: "home#index" diff --git a/rails/spec/helpers.rb b/rails/spec/helpers.rb new file mode 100644 index 00000000..6baa0dc8 --- /dev/null +++ b/rails/spec/helpers.rb @@ -0,0 +1,8 @@ +module Helpers + def locale_path_prefixes + I18n.available_locales.map{ |l| "/#{l}" } << "" + end + def default_locale + I18n.default_locale + end +end \ No newline at end of file diff --git a/rails/spec/rails_helper.rb b/rails/spec/rails_helper.rb index 9f435361..6b789fb5 100644 --- a/rails/spec/rails_helper.rb +++ b/rails/spec/rails_helper.rb @@ -8,6 +8,7 @@ # Add additional requires below this line. Rails is not loaded until this point! require_relative "./support/capybara" +require "./spec/helpers" # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are @@ -69,4 +70,6 @@ Capybara.server_port = 4000 Capybara.app_host = "http://web:4000" end + + config.include Helpers end diff --git a/rails/spec/requests/dives_spec.rb b/rails/spec/requests/dives_spec.rb index b7510e05..fa01a6b3 100644 --- a/rails/spec/requests/dives_spec.rb +++ b/rails/spec/requests/dives_spec.rb @@ -1,61 +1,72 @@ require "rails_helper" require "support/authentication" -RSpec.describe "Dives", type: :request do +RSpec.describe "Dives", type: :request, focus: true do include_context "logged in" describe "GET show" do let!(:dive) { FactoryBot.create(:dive) } it "renders the dive hub" do - get "/dives/#{dive.id}" - - expect(response).to be_successful + locale_path_prefixes.each do |locale| + get "#{locale}/dives/#{dive.id}" + expect(response).to be_successful + end end end describe "GET new" do it "succeeds" do - get "/dives/new" - - expect(response).to be_successful + locale_path_prefixes.each do |locale| + get "#{locale}/dives/new" + expect(response).to be_successful + end end end describe "POST create" do context "given a complete checklist" do subject do - lambda { - post "/dives", params: { dive: { i_have_been_responsible: 1 } } + lambda { |locale| + post_path = "#{locale}/dives" + post post_path, params: { dive: { i_have_been_responsible: 1 } } } end it "redirects to the dive page" do - subject.call - expect(response).to be_redirect + locale_path_prefixes.each do |locale| + subject.call(locale) + expect(response).to be_redirect - follow_redirect! - expect(response).to be_successful + follow_redirect! + expect(response).to be_successful + end end it "creates the dive" do - expect { subject.call }.to change { Dive.count }.by(1) + locale_path_prefixes.each do |locale| + expect { subject.call(locale) }.to change { Dive.count }.by(1) + end end end context "given an incomplete checklist" do subject do - lambda { - post "/dives", params: { dive: { i_have_been_responsible: nil } } + lambda { |locale| + post "#{locale}/dives", params: { dive: { i_have_been_responsible: nil } } } end it "does not create the dive" do - expect { subject.call }.not_to change(Dive, :count) + locale_path_prefixes.each do |locale| + expect { subject.call(locale) }.not_to change(Dive, :count) + end end it "renders an error page" do - subject.call - expect(response.body).to match(/can't be blank/) + locale_path_prefixes.each do |locale| + subject.call(locale) + expect(response.body).to match(/can't be blank/) + end end end end @@ -64,24 +75,29 @@ let!(:dive) { FactoryBot.create(:dive) } subject do - lambda { + lambda { |locale| post "/dives/#{dive.id}/finish" } end it "redirects to the dive page" do - subject.call - expect(response).to be_redirect + locale_path_prefixes.each do |locale| + subject.call(locale) + expect(response).to be_redirect - follow_redirect! - expect(response).to be_successful + follow_redirect! + expect(response).to be_successful + end end it "marks the dive as finished" do - subject.call + locale_path_prefixes.each do |locale| + subject.call(locale) - dive.reload - expect(dive.finished_at).not_to be_nil + dive.reload + follow_redirect! + expect(dive.finished_at).not_to be_nil + end end end end diff --git a/rails/spec/requests/home_spec.rb b/rails/spec/requests/home_spec.rb index dd21e7d5..bc764bee 100644 --- a/rails/spec/requests/home_spec.rb +++ b/rails/spec/requests/home_spec.rb @@ -4,12 +4,25 @@ include_context "logged in" it "can render the root page" do get "/" - + puts response.inspect expect(response).to be_successful sign_out current_user get "/" - expect(response).to redirect_to("/users/sign_in") + expect(response).to redirect_to("/#{default_locale}/users/sign_in") + end + xit "can render a locale-specific root page" do + locale_path_prefixes.each do |locale| + puts locale + puts "Getting #{locale}/" + get "#{locale}/" + expect(response).to be_successful + + sign_out current_user + get "#{locale}/" + + expect(response).to redirect_to("#{locale}/users/sign_in") + end end end diff --git a/rails/spec/requests/nursery_tables_spec.rb b/rails/spec/requests/nursery_tables_spec.rb index cf1fb5d1..d1634283 100644 --- a/rails/spec/requests/nursery_tables_spec.rb +++ b/rails/spec/requests/nursery_tables_spec.rb @@ -11,13 +11,13 @@ let!(:zone) { FactoryBot.create(:zone) } it "renders the nursery table" do - get nursery_tables_path + get nursery_tables_path(locale: default_locale) expect(response).to be_successful end it "allows to create nursery table" do - post nursery_tables_path, params: { nursery_table: { capacity: 24, name: "Blah", zone: zone } } + post nursery_tables_path(locale: default_locale), params: { nursery_table: { capacity: 24, name: "Blah", zone: zone } } expect(response).to have_http_status(200) end From e1829096788c5bffd52e35f6f816464aee8803d4 Mon Sep 17 00:00:00 2001 From: "Ryan B. Harvey" Date: Mon, 19 Aug 2019 22:09:43 -0500 Subject: [PATCH 3/3] Cleanup extraneous puts statements --- rails/config/application.rb | 2 +- rails/spec/requests/home_spec.rb | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/rails/config/application.rb b/rails/config/application.rb index fc702b8f..ef1b9c30 100644 --- a/rails/config/application.rb +++ b/rails/config/application.rb @@ -43,7 +43,7 @@ class Application < Rails::Application }.join("|") }" default_locale = config.i18n.default_locale.to_s - puts locales_neg_regex + config.middleware.insert_before(Rack::Runtime, Rack::Rewrite) do rewrite %r{^(https?\:\/\/(?:[\w\.]+)(?::\d+)?)?((?!#{locales_neg_regex})\/[\w\/\?,\-=%+]+)$}, "$1/#{default_locale}/$2" diff --git a/rails/spec/requests/home_spec.rb b/rails/spec/requests/home_spec.rb index bc764bee..735769f9 100644 --- a/rails/spec/requests/home_spec.rb +++ b/rails/spec/requests/home_spec.rb @@ -4,7 +4,6 @@ include_context "logged in" it "can render the root page" do get "/" - puts response.inspect expect(response).to be_successful sign_out current_user @@ -14,8 +13,6 @@ end xit "can render a locale-specific root page" do locale_path_prefixes.each do |locale| - puts locale - puts "Getting #{locale}/" get "#{locale}/" expect(response).to be_successful