From b0cb7111ab98873fc2a363d5ba54bb4fb100fbaa Mon Sep 17 00:00:00 2001 From: Daniele Santini Date: Mon, 2 Mar 2026 22:59:32 +0100 Subject: [PATCH] Allow localized & country-specific banners --- FAQ.md | 2 + app/helpers/banner_helper.rb | 6 +- test/helpers/banner_helper_test.rb | 213 +++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 1 deletion(-) mode change 100644 => 100755 app/helpers/banner_helper.rb create mode 100644 test/helpers/banner_helper_test.rb diff --git a/FAQ.md b/FAQ.md index e53c8dddb..b55b26b28 100644 --- a/FAQ.md +++ b/FAQ.md @@ -20,6 +20,8 @@ drive. This is a great way to reach a lot of people! * `link` - URL for your event page (e.g. `https://supporting.openstreetmap.org/`) * `img` - the filename for the banner image (e.g. `banners/donate-2017.jpg`) * `enddate` - the final date that the banner will be shown (e.g. `2017-oct-31`) + * `locales` - (optional) a list of locale codes to restrict the banner to. Codes must match the app's available locales in `config/locales/` (e.g. `it`, `en-GB`, `zh-TW`). If omitted, the banner is shown for all languages. + * `countries` - (optional) a list of ISO 3166 country codes to restrict the banner to. If omitted, the banner is shown in all countries. * (optional) Feel free to cleanup the old images from the `app/assets/images/banners/` folder and old entries in the `config/banners.yml` file. 4. The pull request must indicate when the banner should start being shown, which must be at least 7 days after the pull request was created. diff --git a/app/helpers/banner_helper.rb b/app/helpers/banner_helper.rb old mode 100644 new mode 100755 index f22eed361..971b0db1b --- a/app/helpers/banner_helper.rb +++ b/app/helpers/banner_helper.rb @@ -15,7 +15,11 @@ module BannerHelper enddate = nil end - startdate&.future? || enddate&.past? + wrong_locale = v[:locales]&.exclude?(I18n.locale.to_s) + remote_country = params[:country] || OSM.ip_to_country(request.remote_ip) + wrong_country = v[:countries]&.exclude?(remote_country) + + startdate&.future? || enddate&.past? || wrong_locale || wrong_country end end diff --git a/test/helpers/banner_helper_test.rb b/test/helpers/banner_helper_test.rb new file mode 100644 index 000000000..b8cd74a0f --- /dev/null +++ b/test/helpers/banner_helper_test.rb @@ -0,0 +1,213 @@ +# frozen_string_literal: true + +require "test_helper" + +class BannerHelperTest < ActionView::TestCase + def test_banner_without_locales + banners = { :global => make_banner("global") } + + stub_const(:BANNERS, banners) do + I18n.with_locale(:en) do + result = active_banners + assert_includes result.keys, :global + end + + I18n.with_locale(:"zh-TW") do + result = active_banners + assert_includes result.keys, :global + end + end + end + + def test_banner_with_single_locale + banners = { :german => make_banner("german", :locales => %w[de]) } + + stub_const(:BANNERS, banners) do + I18n.with_locale(:de) do + assert_includes active_banners.keys, :german + end + + I18n.with_locale(:en) do + assert_empty active_banners + end + end + end + + def test_banner_with_multiple_locales + banners = { :regional => make_banner("regional", :locales => %w[it zh-TW]) } + + stub_const(:BANNERS, banners) do + I18n.with_locale(:it) do + assert_includes active_banners.keys, :regional + end + + I18n.with_locale(:"zh-TW") do + assert_includes active_banners.keys, :regional + end + + I18n.with_locale(:fr) do + assert_empty active_banners + end + end + end + + def test_multiple_banners_single_locale + banners = { + :german => make_banner("german_only", :locales => %w[de]), + :chinese => make_banner("chinese_only", :locales => %w[zh-TW]) + } + + stub_const(:BANNERS, banners) do + I18n.with_locale(:it) do + assert_empty active_banners + end + + I18n.with_locale(:de) do + assert_includes active_banners.keys, :german + assert_not_includes active_banners.keys, :chinese + end + + I18n.with_locale(:"zh-TW") do + assert_includes active_banners.keys, :chinese + assert_not_includes active_banners.keys, :german + end + end + end + + def test_mixed_banner_locales + banners = { + :global => make_banner("global"), + :italian => make_banner("italian_only", :locales => %w[it]) + } + + stub_const(:BANNERS, banners) do + I18n.with_locale(:it) do + result = active_banners + assert_includes result.keys, :global + assert_includes result.keys, :italian + end + + I18n.with_locale(:en) do + result = active_banners + assert_includes result.keys, :global + assert_not_includes result.keys, :italian + end + end + end + + def test_banner_with_single_country + banners = { :germany => make_banner("germany", :countries => %w[DE]) } + + stub_const(:BANNERS, banners) do + # User country unknown + params.delete(:country) + assert_empty active_banners + + # User country is in banner list + params[:country] = "DE" + assert_includes active_banners.keys, :germany + + # User country is NOT in banner list + params[:country] = "US" + assert_empty active_banners + end + end + + def test_banner_with_multiple_countries + banners = { :itde => make_banner("itde", :countries => %w[IT DE]) } + + stub_const(:BANNERS, banners) do + # User country unknown + params.delete(:country) + assert_empty active_banners + + # User country is in banner list + params[:country] = "IT" + assert_includes active_banners.keys, :itde + + params[:country] = "DE" + assert_includes active_banners.keys, :itde + + # User country is NOT in banner list + params[:country] = "US" + assert_empty active_banners + end + end + + def test_multiple_banners_single_country + banners = { + :germany => make_banner("germany_only", :countries => %w[DE]), + :italy => make_banner("italy_only", :countries => %w[IT]) + } + + stub_const(:BANNERS, banners) do + # User country unknown + params.delete(:country) + assert_empty active_banners + + # User country is NOT in banner list + params[:country] = "US" + assert_empty active_banners + + # User country is in banner list + params[:country] = "DE" + assert_includes active_banners.keys, :germany + assert_not_includes active_banners.keys, :italy + + params[:country] = "IT" + assert_includes active_banners.keys, :italy + assert_not_includes active_banners.keys, :germany + end + end + + def test_mixed_banners_country_filtering + banners = { + :global => make_banner("global"), + :italy => make_banner("italy_only", :countries => %w[IT]) + } + + stub_const(:BANNERS, banners) do + # User country unknown + params.delete(:country) + result = active_banners + assert_includes result.keys, :global + assert_not_includes result.keys, :italy + + # User country is in local banner list + params[:country] = "IT" + result = active_banners + assert_includes result.keys, :global + assert_includes result.keys, :italy + + # User country is NOT in local banner list + params[:country] = "US" + result = active_banners + assert_includes result.keys, :global + assert_not_includes result.keys, :italy + end + end + + private + + def make_banner(id, locales: nil, countries: nil) + { + :id => id, + :alt => "Test Banner", + :link => "https://example.com", + :img => "banners/test.png", + :enddate => "2099-jan-01", + :locales => locales, + :countries => countries + }.compact + end + + def stub_const(name, value) + old = Object.const_get(name) + Object.send(:remove_const, name) + Object.const_set(name, value) + yield + ensure + Object.send(:remove_const, name) + Object.const_set(name, old) + end +end -- 2.39.5