From a908d97aa7c75f17401ccbc46a70e3f56fe2f9a6 Mon Sep 17 00:00:00 2001 From: Robert Smith Date: Tue, 12 Aug 2025 15:14:39 -0230 Subject: [PATCH] Document SEO helpers --- .../better_together/application_helper.rb | 16 +++++- docs/seo.md | 27 ++++++++++ .../application_helper_spec.rb | 50 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 docs/seo.md create mode 100644 spec/helpers/better_together/application_helper_spec.rb diff --git a/app/helpers/better_together/application_helper.rb b/app/helpers/better_together/application_helper.rb index d34ed789c..2dcf9dd8d 100644 --- a/app/helpers/better_together/application_helper.rb +++ b/app/helpers/better_together/application_helper.rb @@ -80,6 +80,12 @@ def host_community_logo_url rails_storage_proxy_url(attachment) end + # Returns the canonical URL for the current request, allowing overrides via + # +content_for(:canonical_url)+. + def canonical_url + content_for?(:canonical_url) ? content_for(:canonical_url) : request.original_url + end + # Builds SEO-friendly meta tags for the current view. Defaults are derived # from translations and fall back to the Open Graph description when set. # rubocop:todo Metrics/MethodLength @@ -94,9 +100,17 @@ def seo_meta_tags # rubocop:todo Metrics/AbcSize, Metrics/MethodLength keywords = content_for?(:meta_keywords) ? content_for(:meta_keywords) : nil + canonical = canonical_url + hreflang_tags = I18n.available_locales.map do |locale| + tag.link(rel: 'alternate', hreflang: locale, href: url_for(locale:, only_path: false)) + end + hreflang_tags << content_for(:hreflang_links) if content_for?(:hreflang_links) + tags = [] tags << tag.meta(name: 'description', content: description) tags << tag.meta(name: 'keywords', content: keywords) if keywords.present? + tags << tag.link(rel: 'canonical', href: canonical) + tags.concat(hreflang_tags) safe_join(tags, "\n") end @@ -121,7 +135,7 @@ def open_graph_meta_tags # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, M t('og.default_description', platform_name: host_platform.name) end - og_url = content_for?(:og_url) ? content_for(:og_url) : request.original_url + og_url = content_for?(:og_url) ? content_for(:og_url) : canonical_url og_image = content_for?(:og_image) ? content_for(:og_image) : host_community_logo_url diff --git a/docs/seo.md b/docs/seo.md new file mode 100644 index 000000000..2eec654f5 --- /dev/null +++ b/docs/seo.md @@ -0,0 +1,27 @@ +# SEO + +The engine exposes helpers that output common search engine optimisation tags. + +## Canonical URL + +`seo_meta_tags` includes a canonical `` tag pointing to the current request +URL. You can override the URL by setting a `content_for` block: + +```erb +<% content_for :canonical_url, article_url(@article, locale: :en) %> +``` + +## Hreflang links + +Alternate language links are generated for each available locale. Additional +links can be appended using `content_for :hreflang_links` and are merged with the +default set: + +```erb +<% content_for :hreflang_links do %> + <%= tag.link rel: 'alternate', hreflang: 'x-default', href: root_url %> +<% end %> +``` + +Links supplied via `content_for` are merged with the automatically generated +links rather than replacing them. diff --git a/spec/helpers/better_together/application_helper_spec.rb b/spec/helpers/better_together/application_helper_spec.rb new file mode 100644 index 000000000..f42a1f67e --- /dev/null +++ b/spec/helpers/better_together/application_helper_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'rails_helper' + +module BetterTogether + RSpec.describe ApplicationHelper, type: :helper do + before do + allow(helper).to receive(:host_platform).and_return(double(name: 'Test Platform', cache_key_with_version: 'test-platform')) + allow(helper).to receive(:host_community_logo_url).and_return(nil) + allow(controller.request).to receive(:original_url).and_return('http://test.host/en/current') + allow(I18n).to receive(:available_locales).and_return(%i[en fr]) + allow(helper).to receive(:url_for) do |opts| + "http://test.host/#{opts[:locale]}/current" + end + end + + describe '#seo_meta_tags' do + it 'includes default canonical and hreflang links' do + html = helper.seo_meta_tags + expect(html).to include('