Allow different locales per tenant

Note that, currently, we take these settings from the database but we
don't provide a way to edit them through the admin interface, so the
locales must be manually introduced through a Rails console.

While we did consider using a comma-separated list, we're using spaces
in order to be consistent with the way we store the allowed content
types settings.

The `enabled_locales` nomenclature, which contrasts with
`available_locales`, is probably subconsciously based on similar
patterns like the one Nginx uses to enable sites.

Note that we aren't using `Setting.enabled_locales` in the globalize
initializer when setting the fallbacks. This means the following test
(which we could add to the shared globalizable examples) would fail:

```
it "Falls back to an enabled locale if the fallback is not enabled" do
  Setting["locales.default"] = "en"
  Setting["locales.enabled"] = "fr en"
  allow(I18n.fallbacks).to receive(:[]).and_return([:fr, :es])
  Globalize.set_fallbacks_to_all_available_locales

  I18n.with_locale(:fr) do
    expect(record.send(attribute)).to eq "In English"
  end
end
```

The reason is that the code making this test pass could be:

```
def Globalize.set_fallbacks_to_all_available_locales
  Globalize.fallbacks = I18n.available_locales.index_with do |locale|
    ((I18n.fallbacks[locale] & Setting.enabled_locales) + Setting.enabled_locales).uniq
  end
end
```

However, this would make it impossible to run `rake db:migrate` on new
applications because the initializer would try to load the `Setting`
model but the `settings` table wouldn't exist at that point.

Besides, this is a really rare case that IMHO we don't need to support.
For this scenario, an installation would have to enable a locale, create
records with contents in that locale, then disable that locale and have
that locale as a fallback for a language where content for that record
wasn't created. If that happened, it would be solved by creating content
for that record in every enabled language.
This commit is contained in:
Javi Martín
2022-10-30 12:37:33 +01:00
parent 790d515afc
commit 647121d13e
40 changed files with 333 additions and 35 deletions

View File

@@ -574,6 +574,7 @@ RSpec/InstanceVariable:
Enabled: true Enabled: true
Exclude: Exclude:
- spec/controllers/concerns/has_orders_spec.rb - spec/controllers/concerns/has_orders_spec.rb
- spec/form_builders/translatable_form_builder_spec.rb
RSpec/LetBeforeExamples: RSpec/LetBeforeExamples:
Enabled: true Enabled: true

View File

@@ -6,7 +6,7 @@
<%= f.select :name, options_for_select(valid_blocks, selected_content_block) %> <%= f.select :name, options_for_select(valid_blocks, selected_content_block) %>
</div> </div>
<div class="small-12 medium-6 column"> <div class="small-12 medium-6 column">
<%= f.select :locale, I18n.available_locales %> <%= f.select :locale, Setting.enabled_locales %>
</div> </div>
<div class="small-12 column"> <div class="small-12 column">

View File

@@ -7,7 +7,7 @@
</div> </div>
<div class="small-12 medium-6 column"> <div class="small-12 medium-6 column">
<%= label_tag :locale %> <%= label_tag :locale %>
<%= select_tag :locale, options_for_select(I18n.available_locales, content_block.locale.to_sym) %> <%= select_tag :locale, options_for_select(Setting.enabled_locales, content_block.locale.to_sym) %>
</div> </div>
<div class="small-12 column"> <div class="small-12 column">
<%= label_tag :body %> <%= label_tag :body %>

View File

@@ -1,13 +1,13 @@
<%= render Shared::GlobalizeLocalesComponent.new %> <%= render Shared::GlobalizeLocalesComponent.new %>
<%= form_tag admin_site_customization_information_texts_path do %> <%= form_tag admin_site_customization_information_texts_path do %>
<% I18n.available_locales.each do |l| %> <% enabled_locales.each do |l| %>
<%= translation_enabled_tag l, site_customization_enable_translation?(l) %> <%= translation_enabled_tag l, site_customization_enable_translation?(l) %>
<% end %> <% end %>
<% contents.each do |group| %> <% contents.each do |group| %>
<% group.each do |content| %> <% group.each do |content| %>
<b><%= content.key %></b> <b><%= content.key %></b>
<% content.globalize_locales.each do |locale| %> <% (content.globalize_locales & enabled_locales.map(&:to_sym)).each do |locale| %>
<%= render Admin::SiteCustomization::InformationTexts::FormFieldComponent.new(content, locale: locale) %> <%= render Admin::SiteCustomization::InformationTexts::FormFieldComponent.new(content, locale: locale) %>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -11,4 +11,8 @@ class Admin::SiteCustomization::InformationTexts::FormComponent < ApplicationCom
def translation_enabled_tag(locale, enabled) def translation_enabled_tag(locale, enabled)
hidden_field_tag("enabled_translations[#{locale}]", (enabled ? 1 : 0)) hidden_field_tag("enabled_translations[#{locale}]", (enabled ? 1 : 0))
end end
def enabled_locales
Setting.enabled_locales
end
end end

View File

@@ -12,7 +12,7 @@ class Layout::LocaleSwitcherComponent < ApplicationComponent
end end
def locales def locales
I18n.available_locales Setting.enabled_locales
end end
def label def label

View File

@@ -13,7 +13,7 @@
<%= select_language_error %> <%= select_language_error %>
<div class="margin-bottom"> <div class="margin-bottom">
<% if manage_languages %> <% if manage_languages %>
<% I18n.available_locales.each do |locale| %> <% Setting.enabled_locales.each do |locale| %>
<%= link_to t("shared.translations.remove_language"), "#", <%= link_to t("shared.translations.remove_language"), "#",
style: display_destroy_locale_style(locale), style: display_destroy_locale_style(locale),
class: "delete js-delete-language js-delete-#{locale}", class: "delete js-delete-language js-delete-#{locale}",

View File

@@ -15,7 +15,7 @@ class Shared::GlobalizeLocalesComponent < ApplicationComponent
end end
def available_locales def available_locales
I18n.available_locales.select { |locale| enabled_locale?(resource, locale) }.map do |locale| Setting.enabled_locales.select { |locale| enabled_locale?(resource, locale) }.map do |locale|
[name_for_locale(locale), locale, { data: { locale: locale }}] [name_for_locale(locale), locale, { data: { locale: locale }}]
end end
end end
@@ -84,7 +84,7 @@ class Shared::GlobalizeLocalesComponent < ApplicationComponent
end end
def all_language_options def all_language_options
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
[name_for_locale(locale), locale] [name_for_locale(locale), locale]
end end
end end

View File

@@ -51,9 +51,9 @@ class ApplicationController < ActionController::Base
end end
def current_locale def current_locale
if I18n.available_locales.include?(params[:locale]&.to_sym) if Setting.enabled_locales.include?(params[:locale]&.to_sym)
params[:locale] params[:locale]
elsif I18n.available_locales.include?(session[:locale]&.to_sym) elsif Setting.enabled_locales.include?(session[:locale]&.to_sym)
session[:locale] session[:locale]
else else
I18n.default_locale I18n.default_locale

View File

@@ -40,7 +40,7 @@ class Management::BaseController < ActionController::Base
end end
def switch_locale(&action) def switch_locale(&action)
if params[:locale] && I18n.available_locales.include?(params[:locale].to_sym) if params[:locale] && Setting.enabled_locales.include?(params[:locale].to_sym)
session[:locale] = params[:locale].to_s session[:locale] = params[:locale].to_s
end end

View File

@@ -38,6 +38,6 @@ class SubscriptionsController < ApplicationController
end end
def find_locale def find_locale
I18n.available_locales.find { |locale| locale == @user.locale&.to_sym } || I18n.locale Setting.enabled_locales.find { |locale| locale == @user.locale&.to_sym } || I18n.locale
end end
end end

View File

@@ -63,7 +63,7 @@ class TranslatableFormBuilder < ConsulFormBuilder
def visible_locales def visible_locales
if @template.translations_interface_enabled? if @template.translations_interface_enabled?
@object.globalize_locales Setting.enabled_locales & @object.globalize_locales
else else
[I18n.locale] [I18n.locale]
end end

View File

@@ -1,6 +1,6 @@
class Budget class Budget
class ContentBlock < ApplicationRecord class ContentBlock < ApplicationRecord
validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) } validates :locale, presence: true, inclusion: { in: ->(*) { Setting.enabled_locales.map(&:to_s) }}
validates :heading, presence: true, uniqueness: { scope: :locale } validates :heading, presence: true, uniqueness: { scope: :locale }
belongs_to :heading belongs_to :heading

View File

@@ -119,7 +119,7 @@ class I18nContent < ApplicationRecord
end end
end end
def self.update(contents, enabled_translations = I18n.available_locales) def self.update(contents, enabled_translations = Setting.enabled_locales)
contents.each do |content| contents.each do |content|
values = content[:values].slice(*translation_params(enabled_translations)) values = content[:values].slice(*translation_params(enabled_translations))

View File

@@ -95,6 +95,7 @@ class Setting < ApplicationRecord
"html.per_page_code_body": "", "html.per_page_code_body": "",
# Code to be included at the top (inside <head>) of every page (useful for tracking) # Code to be included at the top (inside <head>) of every page (useful for tracking)
"html.per_page_code_head": "", "html.per_page_code_head": "",
"locales.enabled": nil,
"map.latitude": 51.48, "map.latitude": 51.48,
"map.longitude": 0.0, "map.longitude": 0.0,
"map.zoom": 10, "map.zoom": 10,
@@ -219,5 +220,11 @@ class Setting < ApplicationRecord
def archived_proposals_date_limit def archived_proposals_date_limit
Setting["months_to_archive_proposals"].to_i.months.ago Setting["months_to_archive_proposals"].to_i.months.ago
end end
def enabled_locales
locales = Setting["locales.enabled"].to_s.split.map(&:to_sym)
(locales & I18n.available_locales).presence || I18n.available_locales
end
end end
end end

View File

@@ -1,7 +1,7 @@
class SiteCustomization::ContentBlock < ApplicationRecord class SiteCustomization::ContentBlock < ApplicationRecord
VALID_BLOCKS = %w[top_links footer footer_legal subnavigation_left subnavigation_right].freeze VALID_BLOCKS = %w[top_links footer footer_legal subnavigation_left subnavigation_right].freeze
validates :locale, presence: true, inclusion: { in: I18n.available_locales.map(&:to_s) } validates :locale, presence: true, inclusion: { in: ->(*) { Setting.enabled_locales.map(&:to_s) }}
validates :name, presence: true, uniqueness: { scope: :locale }, inclusion: { in: ->(*) { VALID_BLOCKS }} validates :name, presence: true, uniqueness: { scope: :locale }, inclusion: { in: ->(*) { VALID_BLOCKS }}
def self.block_for(name, locale) def self.block_for(name, locale)

View File

@@ -23,7 +23,11 @@ def log(msg)
end end
def random_locales def random_locales
[I18n.default_locale, *(I18n.available_locales & %i[en es]), *I18n.available_locales.sample(4)].uniq.take(5) [
I18n.default_locale,
*(Setting.enabled_locales & %i[en es]),
*Setting.enabled_locales.sample(4)
].uniq.take(5)
end end
def random_locales_attributes(**attribute_names_with_values) def random_locales_attributes(**attribute_names_with_values)

View File

@@ -9,7 +9,7 @@ section "Creating banners" do
post_started_at: rand((1.week.ago)..(1.day.ago)), post_started_at: rand((1.week.ago)..(1.day.ago)),
post_ended_at: rand((1.day.ago)..(1.week.from_now)), post_ended_at: rand((1.day.ago)..(1.week.from_now)),
created_at: rand((1.week.ago)..Time.current)) created_at: rand((1.week.ago)..Time.current))
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
banner.description = "Description for locale #{locale}" banner.description = "Description for locale #{locale}"
banner.title = "Title for locale #{locale}" banner.title = "Title for locale #{locale}"

View File

@@ -41,7 +41,7 @@ section "Creating polls" do
Poll.find_each do |poll| Poll.find_each do |poll|
name = poll.name name = poll.name
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
poll.name = "#{name} (#{locale})" poll.name = "#{name} (#{locale})"
poll.summary = "Summary for locale #{locale}" poll.summary = "Summary for locale #{locale}"
@@ -59,7 +59,7 @@ section "Creating Poll Questions & Answers" do
question = Poll::Question.new(author: User.sample, question = Poll::Question.new(author: User.sample,
title: question_title, title: question_title,
poll: poll) poll: poll)
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
question.title = "#{question_title} (#{locale})" question.title = "#{question_title} (#{locale})"
end end
@@ -71,7 +71,7 @@ section "Creating Poll Questions & Answers" do
title: title.capitalize, title: title.capitalize,
description: description, description: description,
given_order: index + 1) given_order: index + 1)
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
answer.title = "#{title} (#{locale})" answer.title = "#{title} (#{locale})"
answer.description = "#{description} (#{locale})" answer.description = "#{description} (#{locale})"
@@ -235,7 +235,7 @@ section "Creating Poll Questions from Proposals" do
question = Poll::Question.new(poll: poll) question = Poll::Question.new(poll: poll)
question.copy_attributes_from_proposal(proposal) question.copy_attributes_from_proposal(proposal)
question_title = question.title question_title = question.title
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
question.title = "#{question_title} (#{locale})" question.title = "#{question_title} (#{locale})"
end end
@@ -247,7 +247,7 @@ section "Creating Poll Questions from Proposals" do
title: title.capitalize, title: title.capitalize,
description: description, description: description,
given_order: index + 1) given_order: index + 1)
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
answer.title = "#{title} (#{locale})" answer.title = "#{title} (#{locale})"
answer.description = "#{description} (#{locale})" answer.description = "#{description} (#{locale})"
@@ -265,7 +265,7 @@ section "Creating Successful Proposals" do
question = Poll::Question.new(poll: poll) question = Poll::Question.new(poll: poll)
question.copy_attributes_from_proposal(proposal) question.copy_attributes_from_proposal(proposal)
question_title = question.title question_title = question.title
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
question.title = "#{question_title} (#{locale})" question.title = "#{question_title} (#{locale})"
end end
@@ -277,7 +277,7 @@ section "Creating Successful Proposals" do
title: title.capitalize, title: title.capitalize,
description: description, description: description,
given_order: index + 1) given_order: index + 1)
I18n.available_locales.map do |locale| Setting.enabled_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
answer.title = "#{title} (#{locale})" answer.title = "#{title} (#{locale})"
answer.description = "#{description} (#{locale})" answer.description = "#{description} (#{locale})"

View File

@@ -104,7 +104,7 @@ end
if SiteCustomization::Page.find_by(slug: "accessibility").nil? if SiteCustomization::Page.find_by(slug: "accessibility").nil?
page = SiteCustomization::Page.new(slug: "accessibility", status: "published") page = SiteCustomization::Page.new(slug: "accessibility", status: "published")
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -7,7 +7,7 @@ end
if SiteCustomization::Page.find_by(slug: "census_terms").nil? if SiteCustomization::Page.find_by(slug: "census_terms").nil?
page = SiteCustomization::Page.new(slug: "census_terms", status: "published") page = SiteCustomization::Page.new(slug: "census_terms", status: "published")
page.print_content_flag = true page.print_content_flag = true
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -8,7 +8,7 @@ end
if SiteCustomization::Page.find_by(slug: "conditions").nil? if SiteCustomization::Page.find_by(slug: "conditions").nil?
page = SiteCustomization::Page.new(slug: "conditions", status: "published") page = SiteCustomization::Page.new(slug: "conditions", status: "published")
page.print_content_flag = true page.print_content_flag = true
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -5,7 +5,7 @@ def generate_content(page)
end end
if SiteCustomization::Page.find_by(slug: "faq").nil? if SiteCustomization::Page.find_by(slug: "faq").nil?
page = SiteCustomization::Page.new(slug: "faq", status: "published") page = SiteCustomization::Page.new(slug: "faq", status: "published")
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -7,7 +7,7 @@ end
if SiteCustomization::Page.find_by(slug: "privacy").nil? if SiteCustomization::Page.find_by(slug: "privacy").nil?
page = SiteCustomization::Page.new(slug: "privacy", status: "published") page = SiteCustomization::Page.new(slug: "privacy", status: "published")
page.print_content_flag = true page.print_content_flag = true
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -17,7 +17,7 @@ end
if SiteCustomization::Page.find_by(slug: "welcome_level_three_verified").nil? if SiteCustomization::Page.find_by(slug: "welcome_level_three_verified").nil?
page = SiteCustomization::Page.new(slug: "welcome_level_three_verified", status: "published") page = SiteCustomization::Page.new(slug: "welcome_level_three_verified", status: "published")
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -22,7 +22,7 @@ def generate_content(page)
end end
if SiteCustomization::Page.find_by(slug: "welcome_level_two_verified").nil? if SiteCustomization::Page.find_by(slug: "welcome_level_two_verified").nil?
page = SiteCustomization::Page.new(slug: "welcome_level_two_verified", status: "published") page = SiteCustomization::Page.new(slug: "welcome_level_two_verified", status: "published")
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -22,7 +22,7 @@ def generate_content(page)
end end
if SiteCustomization::Page.find_by(slug: "welcome_not_verified").nil? if SiteCustomization::Page.find_by(slug: "welcome_not_verified").nil?
page = SiteCustomization::Page.new(slug: "welcome_not_verified", status: "published") page = SiteCustomization::Page.new(slug: "welcome_not_verified", status: "published")
I18n.available_locales.each do |locale| Setting.enabled_locales.each do |locale|
I18n.with_locale(locale) { generate_content(page) } I18n.with_locale(locale) { generate_content(page) }
end end
end end

View File

@@ -0,0 +1,18 @@
require "rails_helper"
describe Admin::SiteCustomization::ContentBlocks::FormContentBlockComponent do
describe "locale selector" do
let(:content_block) { create(:site_customization_content_block, locale: "de") }
let(:component) do
Admin::SiteCustomization::ContentBlocks::FormContentBlockComponent.new(content_block)
end
it "only includes enabled settings" do
Setting["locales.enabled"] = "de fr"
render_inline component
expect(page).to have_select "locale", options: ["de", "fr"]
end
end
end

View File

@@ -0,0 +1,18 @@
require "rails_helper"
describe Admin::SiteCustomization::ContentBlocks::FormHeadingContentBlockComponent do
describe "locale selector" do
let(:content_block) { create(:heading_content_block, locale: "de") }
let(:component) do
Admin::SiteCustomization::ContentBlocks::FormHeadingContentBlockComponent.new(content_block)
end
it "only includes enabled settings" do
Setting["locales.enabled"] = "de fr"
render_inline component
expect(page).to have_select "locale", options: ["de", "fr"]
end
end
end

View File

@@ -0,0 +1,29 @@
require "rails_helper"
describe Admin::SiteCustomization::InformationTexts::FormComponent do
describe "enabled_translations fields" do
it "renders fields for enabled locales" do
Setting["locales.enabled"] = "en es"
content = create(:i18n_content)
render_inline Admin::SiteCustomization::InformationTexts::FormComponent.new([[content]])
expect(page).to have_css "input[name^='enabled_translations']", count: 2, visible: :all
expect(page).to have_css "input[name='enabled_translations[en]']", visible: :hidden
expect(page).to have_css "input[name='enabled_translations[es]']", visible: :hidden
end
end
describe "text fields" do
it "renders fields for enabled locales" do
Setting["locales.enabled"] = "en es"
content = create(:i18n_content, key: "system.failure")
render_inline Admin::SiteCustomization::InformationTexts::FormComponent.new([[content]])
expect(page).to have_css "textarea[name^='contents[content_system.failure]']", count: 2, visible: :all
expect(page).to have_field "contents[content_system.failure]values[value_en]"
expect(page).to have_field "contents[content_system.failure]values[value_es]"
end
end
end

View File

@@ -97,4 +97,20 @@ describe Layout::LocaleSwitcherComponent do
expect(page).to have_css "[href='/?locale=en'][data-turbolinks=true]" expect(page).to have_css "[href='/?locale=en'][data-turbolinks=true]"
end end
end end
context "when not all available locales are enabled" do
before do
allow(I18n).to receive(:available_locales).and_return(%i[en es fr])
Setting["locales.enabled"] = "es fr"
end
it "displays the enabled locales" do
render_inline component
expect(page).to have_link count: 2
expect(page).to have_link "Español"
expect(page).to have_link "Français"
expect(page).not_to have_link "English"
end
end
end end

View File

@@ -0,0 +1,49 @@
require "rails_helper"
describe Shared::GlobalizeLocalesComponent do
describe "Language selector" do
it "only includes enabled locales" do
Setting["locales.enabled"] = "en nl"
I18n.with_locale(:en) do
render_inline Shared::GlobalizeLocalesComponent.new
expect(page).to have_select options: ["Choose language", "English"]
end
I18n.with_locale(:es) do
render_inline Shared::GlobalizeLocalesComponent.new
expect(page).to have_select options: ["Seleccionar idioma"]
end
end
end
describe "links to destroy languages" do
it "only includes enabled locales" do
Setting["locales.enabled"] = "en nl"
I18n.with_locale(:en) do
render_inline Shared::GlobalizeLocalesComponent.new
expect(page).to have_css "a[data-locale]", count: 1
end
I18n.with_locale(:es) do
render_inline Shared::GlobalizeLocalesComponent.new
expect(page).not_to have_css "a[data-locale]"
end
end
end
describe "Add language selector" do
it "only includes enabled locales" do
Setting["locales.enabled"] = "en nl"
render_inline Shared::GlobalizeLocalesComponent.new
expect(page).to have_select options: ["Add language", "English", "Nederlands"]
end
end
end

View File

@@ -54,6 +54,22 @@ describe ApplicationController do
expect(response.body).to eq "es" expect(response.body).to eq "es"
end end
it "only accepts enabled locales" do
Setting["locales.enabled"] = "en es fr"
get :index, params: { locale: :es }
expect(response.body).to eq "es"
get :index, params: { locale: :de }
expect(response.body).to eq "es"
get :index, params: { locale: :fr }
expect(response.body).to eq "fr"
end
context "authenticated user" do context "authenticated user" do
let(:user) { create(:user) } let(:user) { create(:user) }
before { sign_in(user) } before { sign_in(user) }

View File

@@ -64,5 +64,21 @@ describe Management::BaseController do
expect(response.body).to eq "es" expect(response.body).to eq "es"
end end
it "only accepts enabled locales" do
Setting["locales.enabled"] = "en es fr"
get :index, params: { locale: :es }
expect(response.body).to eq "es"
get :index, params: { locale: :de }
expect(response.body).to eq "es"
get :index, params: { locale: :fr }
expect(response.body).to eq "fr"
end
end end
end end

View File

@@ -28,8 +28,10 @@ describe SubscriptionsController do
expect(session[:locale]).to eq "es" expect(session[:locale]).to eq "es"
end end
it "only accepts available locales" do it "only accepts enabled locales" do
create(:user, locale: "wl", subscriptions_token: "mytoken") Setting["locales.enabled"] = "en nl"
create(:user, locale: "es", subscriptions_token: "mytoken")
get :edit, params: { token: "mytoken" } get :edit, params: { token: "mytoken" }

View File

@@ -0,0 +1,46 @@
require "rails_helper"
describe TranslatableFormBuilder do
before do
dummy_banner = Class.new(ApplicationRecord) do
def self.name
"DummyBanner"
end
self.table_name = "banners"
translates :title, touch: true
include Globalizable
has_many :translations, class_name: "DummyBanner::Translation", foreign_key: "banner_id"
end
stub_const("DummyBanner", dummy_banner)
end
let(:builder) do
TranslatableFormBuilder.new(:dummy, DummyBanner.new, ApplicationController.new.view_context, {})
end
describe "#translatable_fields" do
it "renders fields for the enabled locales when the translation interface is enabled" do
Setting["feature.translation_interface"] = true
Setting["locales.enabled"] = "en fr"
builder.translatable_fields do |translations_builder|
render translations_builder.text_field :title
end
expect(page).to have_field "Title", count: 2
end
end
attr_reader :content
def render(content)
@content ||= ""
@content << content
end
def page
Capybara::Node::Simple.new(content)
end
end

View File

@@ -20,6 +20,14 @@ describe Budget::ContentBlock do
expect(valid_block).to be_valid expect(valid_block).to be_valid
end end
it "is not valid with a disabled locale" do
Setting["locales.enabled"] = "nl pt-BR"
block.locale = "en"
expect(block).not_to be_valid
end
describe "#name" do describe "#name" do
it "uses the heading name" do it "uses the heading name" do
block = Budget::ContentBlock.new(heading: Budget::Heading.new(name: "Central")) block = Budget::ContentBlock.new(heading: Budget::Heading.new(name: "Central"))

View File

@@ -160,6 +160,16 @@ RSpec.describe I18nContent do
end end
it "does not store new keys for disabled translations" do it "does not store new keys for disabled translations" do
Setting["locales.enabled"] = "es"
I18nContent.update([{ id: "shared.yes", values: { "value_en" => "Oh, yeah" }}])
expect(I18nContent.all).to be_empty
end
it "uses different enabled translations when given a parameter" do
Setting["locales.enabled"] = "en es"
I18nContent.update([{ id: "shared.yes", values: { "value_en" => "Oh, yeah" }}], [:es]) I18nContent.update([{ id: "shared.yes", values: { "value_en" => "Oh, yeah" }}], [:es])
expect(I18nContent.all).to be_empty expect(I18nContent.all).to be_empty

View File

@@ -258,4 +258,50 @@ describe Setting do
expect(Setting.force_presence_postal_code?).to be true expect(Setting.force_presence_postal_code?).to be true
end end
end end
describe ".available_locales" do
before { allow(I18n).to receive(:available_locales).and_return(%i[de en es pt-BR]) }
it "uses I18n available locales by default" do
Setting["locales.enabled"] = ""
expect(Setting.enabled_locales).to eq %i[de en es pt-BR]
end
it "defines available locales with a space-separated list" do
Setting["locales.enabled"] = "de es"
expect(Setting.enabled_locales).to eq %i[de es]
end
it "handles locales which include a dash" do
Setting["locales.enabled"] = "de en pt-BR"
expect(Setting.enabled_locales).to eq %i[de en pt-BR]
end
it "ignores extra whitespace between locales" do
Setting["locales.enabled"] = " de en pt-BR "
expect(Setting.enabled_locales).to eq %i[de en pt-BR]
end
it "ignores locales which aren't available" do
Setting["locales.enabled"] = "de es en-US fr zh-CN"
expect(Setting.enabled_locales).to eq %i[de es]
end
it "ignores words that don't make sense in this context" do
Setting["locales.enabled"] = "yes es 1234 en SuperCool"
expect(Setting.enabled_locales).to eq %i[es en]
end
it "uses I18n available locales when no locale is available" do
Setting["locales.enabled"] = "nl fr zh-CN"
expect(Setting.enabled_locales).to eq %i[de en es pt-BR]
end
end
end end

View File

@@ -27,4 +27,12 @@ RSpec.describe SiteCustomization::ContentBlock do
block.name = "top_links" block.name = "top_links"
expect(block).not_to be_valid expect(block).not_to be_valid
end end
it "is not valid with a disabled locale" do
Setting["locales.enabled"] = "nl pt-BR"
block.locale = "en"
expect(block).not_to be_valid
end
end end