bingo: redirect /en/ to /en, follow the view links

/en/ answered 200 with the same page as /en. The canonical tag covered
it, but a 301 keeps crawlers and shared links on a single URL.

The All / Operational toggle links were rel="nofollow". The filtered
page is already noindex with a canonical back to the locale URL, so
nofollow on our own links only threw away internal link signals.

Assisted-by: Claude:opus-5
This commit is contained in:
Sergei Poljanski 2026-10-10 09:27:02 +04:00
commit d2d6ac1d99
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
3 changed files with 15 additions and 2 deletions

View file

@ -97,7 +97,13 @@ class BingoWeb < Sinatra::Base
erb :sitemap, layout: false
end
get %r{/(ru|en|uk)/?} do |locale|
# One URL per page: /en/ is a duplicate of /en.
get %r{/(ru|en|uk)/} do |locale|
qs = request.query_string
redirect "/#{locale}#{"?#{qs}" unless qs.empty?}", 301
end
get %r{/(ru|en|uk)} do |locale|
@locale = locale
@view = Bingo::VIEWS.include?(params[:view]) ? params[:view] : 'all'
@board = Bingo::Board.load(@view)

View file

@ -136,6 +136,7 @@ class BingoTest < Minitest::Test
assert last_response.ok?
assert_includes last_response.body, 'noindex, follow'
assert_includes last_response.body, 'href="/en?view=operational"' # language links keep the view
assert_includes last_response.body, '<a href="/ru?view=operational" aria-current="page">' # followed internal link
assert_includes last_response.body, 'rel="canonical" href="https://example.org/ru"'
get '/ru?view=bogus'
assert_includes last_response.body, 'index, follow, max-image-preview'
@ -183,6 +184,12 @@ class BingoTest < Minitest::Test
assert_equal 404, last_response.status
end
def test_trailing_slash_redirects
get '/en/', view: 'operational'
assert_equal 301, last_response.status
assert_equal 'http://example.org/en?view=operational', last_response.location
end
def test_root_varies_on_language
get '/'
assert_equal 'Accept-Language', last_response.headers['Vary']

View file

@ -5,7 +5,7 @@
<div class="switches">
<nav class="toggle" aria-label="<%= t(:view_label) %>">
<% Bingo::VIEWS.each do |v| %>
<a href="<%= card_path(@locale, v) %>" rel="nofollow" <%== 'aria-current="page"' if v == @view %>><%= t(:"view_#{v}") %></a>
<a href="<%= card_path(@locale, v) %>" <%== 'aria-current="page"' if v == @view %>><%= t(:"view_#{v}") %></a>
<% end %>
</nav>
<nav class="langs">