Module: AlRtl

Defined in:
lib/al_rtl.rb,
lib/al_rtl/version.rb

Overview

Right-to-left language support.

Direction is set with dir on the element rather than on a wrapper div, which is what the original v0.x proposal did. That matters for more than tidiness: dir on is what makes CSS logical properties, the rtl: Tailwind variant, form controls, scrollbar placement and the browser's own bidi algorithm behave. A wrapper div leaves the chrome outside it — navbar, footer, skip links — still laid out left-to-right.

It also drops the align="right" the original used. That attribute was deprecated in HTML 4.01, does nothing in modern engines under a Tailwind reset, and conflates alignment with direction: RTL text is right-aligned as a consequence of direction, and forcing it breaks centred headings.

Defined Under Namespace

Modules: Filters Classes: AssetsGenerator, HtmlAttrsTag, IfRtlBlock, PluginStaticFile, StylesTag

Constant Summary collapse

PLUGIN_ROOT =
File.expand_path("..", __dir__)
LIB_ROOT =

Jekyll writes a StaticFile to /

/, where is relative to the base below. That base must be lib/, not the gem root, or assets land at /lib/assets/... while every tag in this file points at /assets/... .

__dir__
ASSETS_ROOT =
File.join(LIB_ROOT, "assets")
DEFAULT_LANGS =

ISO 639 codes for the scripts written right-to-left. Overridable via al_rtl.langs, but shipping a default means most sites configure nothing.

%w[ar arc az ckb dv fa he ku ps sd ug ur yi].freeze
VERSION =
"1.0.0"

Class Method Summary collapse

Class Method Details

.asset_entriesObject

[relative_dir, filename] for everything this gem publishes. Exposed so the destination path can be asserted without booting a full Jekyll site.



92
93
94
95
96
# File 'lib/al_rtl.rb', line 92

def asset_entries
  Dir.glob(File.join(ASSETS_ROOT, "**", "*")).sort.reject { |p| File.directory?(p) }.map do |source_path|
    [File.dirname(source_path).sub("#{LIB_ROOT}/", ""), File.basename(source_path)]
  end
end

.config(site) ⇒ Object



37
38
39
40
41
# File 'lib/al_rtl.rb', line 37

def config(site)
  return {} unless site

  site.config["al_rtl"] || {}
end

.language_for(site, page) ⇒ Object



70
71
72
73
74
75
# File 'lib/al_rtl.rb', line 70

def language_for(site, page)
  page_lang = page_value(page, "lang")
  return page_lang unless page_lang.to_s.strip.empty?

  site&.config&.[]("lang")
end

.language_tag(site, page) ⇒ Object

Emits the raw language tag for the lang attribute, preserving any region subtag ("fa-IR"), since that is legitimate content for lang.



86
87
88
# File 'lib/al_rtl.rb', line 86

def language_tag(site, page)
  language_for(site, page).to_s.strip
end

.languages(site) ⇒ Object



43
44
45
46
47
# File 'lib/al_rtl.rb', line 43

def languages(site)
  configured = config(site)["langs"]
  list = configured.is_a?(Array) && !configured.empty? ? configured : DEFAULT_LANGS
  list.map { |code| normalize(code) }
end

.normalize(code) ⇒ Object

"fa-IR" and "FA" both mean Persian. Compare on the primary subtag, lowercased, or a site that writes the region form gets silently left-to-right.



51
52
53
# File 'lib/al_rtl.rb', line 51

def normalize(code)
  code.to_s.strip.downcase.split(/[-_]/).first.to_s
end

.page_value(page, key) ⇒ Object

The page's own lang wins over the site default, so a single RTL post on an otherwise English site renders correctly.

Reads through [] rather than testing for Hash: at render time Jekyll hands registers[:page] a Drop (Jekyll::Drops::DocumentDrop), not a Hash, so an is_a?(Hash) guard silently discards every page's front matter and falls back to the site language.



62
63
64
65
66
67
68
# File 'lib/al_rtl.rb', line 62

def page_value(page, key)
  return nil unless page.respond_to?(:[])

  page[key]
rescue StandardError
  nil
end

.rtl?(site, page) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
80
81
82
# File 'lib/al_rtl.rb', line 77

def rtl?(site, page)
  lang = normalize(language_for(site, page))
  return false if lang.empty?

  languages(site).include?(lang)
end