better_seo 0.13.0 β 1.0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +121 -3
- data/README.md +299 -181
- data/docs/00_OVERVIEW.md +472 -0
- data/docs/01_CORE_AND_CONFIGURATION.md +913 -0
- data/docs/02_META_TAGS_AND_OPEN_GRAPH.md +251 -0
- data/docs/03_STRUCTURED_DATA.md +140 -0
- data/docs/04_SITEMAP_AND_ROBOTS.md +131 -0
- data/docs/05_RAILS_INTEGRATION.md +175 -0
- data/docs/06_I18N_PAGE_GENERATOR.md +233 -0
- data/docs/07_IMAGE_OPTIMIZATION.md +260 -0
- data/docs/DEPENDENCIES.md +383 -0
- data/docs/README.md +180 -0
- data/docs/TESTING_STRATEGY.md +663 -0
- data/lib/better_seo/analytics/google_analytics.rb +83 -0
- data/lib/better_seo/analytics/google_tag_manager.rb +74 -0
- data/lib/better_seo/configuration.rb +316 -0
- data/lib/better_seo/dsl/base.rb +86 -0
- data/lib/better_seo/dsl/meta_tags.rb +55 -0
- data/lib/better_seo/dsl/open_graph.rb +109 -0
- data/lib/better_seo/dsl/twitter_cards.rb +131 -0
- data/lib/better_seo/errors.rb +31 -0
- data/lib/better_seo/generators/amp_generator.rb +83 -0
- data/lib/better_seo/generators/breadcrumbs_generator.rb +126 -0
- data/lib/better_seo/generators/canonical_url_manager.rb +106 -0
- data/lib/better_seo/generators/meta_tags_generator.rb +100 -0
- data/lib/better_seo/generators/open_graph_generator.rb +110 -0
- data/lib/better_seo/generators/robots_txt_generator.rb +102 -0
- data/lib/better_seo/generators/twitter_cards_generator.rb +102 -0
- data/lib/better_seo/image/optimizer.rb +143 -0
- data/lib/better_seo/rails/helpers/controller_helpers.rb +118 -0
- data/lib/better_seo/rails/helpers/seo_helper.rb +176 -0
- data/lib/better_seo/rails/helpers/structured_data_helper.rb +123 -0
- data/lib/better_seo/rails/model_helpers.rb +62 -0
- data/lib/better_seo/rails/railtie.rb +22 -0
- data/lib/better_seo/sitemap/builder.rb +65 -0
- data/lib/better_seo/sitemap/generator.rb +57 -0
- data/lib/better_seo/sitemap/sitemap_index.rb +73 -0
- data/lib/better_seo/sitemap/url_entry.rb +157 -0
- data/lib/better_seo/structured_data/article.rb +55 -0
- data/lib/better_seo/structured_data/base.rb +73 -0
- data/lib/better_seo/structured_data/breadcrumb_list.rb +49 -0
- data/lib/better_seo/structured_data/event.rb +207 -0
- data/lib/better_seo/structured_data/faq_page.rb +55 -0
- data/lib/better_seo/structured_data/generator.rb +75 -0
- data/lib/better_seo/structured_data/how_to.rb +96 -0
- data/lib/better_seo/structured_data/local_business.rb +94 -0
- data/lib/better_seo/structured_data/organization.rb +67 -0
- data/lib/better_seo/structured_data/person.rb +51 -0
- data/lib/better_seo/structured_data/product.rb +123 -0
- data/lib/better_seo/structured_data/recipe.rb +135 -0
- data/lib/better_seo/validators/seo_recommendations.rb +165 -0
- data/lib/better_seo/validators/seo_validator.rb +195 -0
- data/lib/better_seo/version.rb +1 -1
- data/lib/better_seo.rb +5 -0
- data/lib/generators/better_seo/install_generator.rb +21 -0
- data/lib/generators/better_seo/templates/README +29 -0
- data/lib/generators/better_seo/templates/better_seo.rb +40 -0
- metadata +69 -2
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Step 06: Generatore Pagine i18n
|
|
2
|
+
|
|
3
|
+
**Versione Target**: 0.7.0
|
|
4
|
+
**Durata Stimata**: 1-2 settimane
|
|
5
|
+
**PrioritΓ **: π’ BASSA
|
|
6
|
+
**Dipendenze**: Step 01, 02, 05
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Obiettivi
|
|
11
|
+
|
|
12
|
+
1. β
Rails generator `rails g better_seo:page`
|
|
13
|
+
2. β
File YAML per locale in config/locales/seo/:locale/
|
|
14
|
+
3. β
Template per page types (website, article, product)
|
|
15
|
+
4. β
Helper `set_seo_from_locale`
|
|
16
|
+
5. β
Interpolazione variabili
|
|
17
|
+
6. β
Rake task validazione traduzioni
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## File da Creare
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
lib/generators/better_seo/page_generator.rb
|
|
25
|
+
lib/generators/better_seo/page/templates/page_it.yml.tt
|
|
26
|
+
lib/generators/better_seo/page/templates/page_en.yml.tt
|
|
27
|
+
lib/better_seo/i18n/loader.rb
|
|
28
|
+
lib/better_seo/tasks/i18n.rake
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Implementazione: Page Generator
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
# lib/generators/better_seo/page_generator.rb
|
|
37
|
+
module BetterSeo
|
|
38
|
+
module Generators
|
|
39
|
+
class PageGenerator < Rails::Generators::NamedBase
|
|
40
|
+
source_root File.expand_path("templates", __dir__)
|
|
41
|
+
|
|
42
|
+
class_option :type, type: :string, default: "website",
|
|
43
|
+
desc: "Type of page: website, article, product, organization"
|
|
44
|
+
|
|
45
|
+
class_option :locales, type: :array,
|
|
46
|
+
desc: "Locales to generate (default: from BetterSeo config)"
|
|
47
|
+
|
|
48
|
+
class_option :skip_twitter, type: :boolean, default: false
|
|
49
|
+
class_option :skip_open_graph, type: :boolean, default: false
|
|
50
|
+
class_option :skip_structured_data, type: :boolean, default: false
|
|
51
|
+
|
|
52
|
+
def create_locale_files
|
|
53
|
+
locales.each do |locale|
|
|
54
|
+
@locale = locale
|
|
55
|
+
@page_type = options[:type]
|
|
56
|
+
|
|
57
|
+
template_file = "page_#{locale}.yml"
|
|
58
|
+
output_path = "config/locales/seo/#{locale}/#{file_name}.yml"
|
|
59
|
+
|
|
60
|
+
template template_file, output_path
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def locales
|
|
67
|
+
options[:locales] || BetterSeo.configuration.available_locales
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def translations
|
|
71
|
+
# Template-specific translations
|
|
72
|
+
case @locale.to_sym
|
|
73
|
+
when :it
|
|
74
|
+
{
|
|
75
|
+
title: "Titolo Pagina",
|
|
76
|
+
description: "Descrizione per motori di ricerca",
|
|
77
|
+
keywords: ["parola1", "parola2"]
|
|
78
|
+
}
|
|
79
|
+
when :en
|
|
80
|
+
{
|
|
81
|
+
title: "Page Title",
|
|
82
|
+
description: "Description for search engines",
|
|
83
|
+
keywords: ["keyword1", "keyword2"]
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Template YAML
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
# lib/generators/better_seo/page/templates/page_it.yml.tt
|
|
98
|
+
it:
|
|
99
|
+
seo:
|
|
100
|
+
pages:
|
|
101
|
+
<%= file_name %>:
|
|
102
|
+
meta:
|
|
103
|
+
title: "<%= translations[:title] %>"
|
|
104
|
+
description: "<%= translations[:description] %>"
|
|
105
|
+
keywords: <%= translations[:keywords].to_yaml.sub("---\n", "") %>
|
|
106
|
+
|
|
107
|
+
<% unless options[:skip_open_graph] %>
|
|
108
|
+
open_graph:
|
|
109
|
+
title: "<%= translations[:title] %>"
|
|
110
|
+
description: "<%= translations[:description] %>"
|
|
111
|
+
type: "<%= @page_type %>"
|
|
112
|
+
image:
|
|
113
|
+
url: "https://example.com/images/og-<%= file_name %>-it.jpg"
|
|
114
|
+
width: 1200
|
|
115
|
+
height: 630
|
|
116
|
+
<% end %>
|
|
117
|
+
|
|
118
|
+
<% unless options[:skip_twitter] %>
|
|
119
|
+
twitter:
|
|
120
|
+
card: "summary_large_image"
|
|
121
|
+
title: "<%= translations[:title] %>"
|
|
122
|
+
description: "<%= translations[:description] %>"
|
|
123
|
+
<% end %>
|
|
124
|
+
|
|
125
|
+
<% unless options[:skip_structured_data] %>
|
|
126
|
+
structured_data:
|
|
127
|
+
type: "<%= structured_data_type %>"
|
|
128
|
+
data:
|
|
129
|
+
name: "<%= translations[:title] %>"
|
|
130
|
+
<% end %>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Helper set_seo_from_locale
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# Aggiunto a lib/better_seo/rails/concerns/seo_aware.rb
|
|
139
|
+
|
|
140
|
+
def set_seo_from_locale(page_name, &block)
|
|
141
|
+
locale_key = "seo.pages.#{page_name}"
|
|
142
|
+
|
|
143
|
+
# Load from i18n
|
|
144
|
+
meta_data = I18n.t("#{locale_key}.meta", default: {})
|
|
145
|
+
og_data = I18n.t("#{locale_key}.open_graph", default: {})
|
|
146
|
+
sd_data = I18n.t("#{locale_key}.structured_data", default: {})
|
|
147
|
+
|
|
148
|
+
# Build SEO config
|
|
149
|
+
set_seo do |seo|
|
|
150
|
+
seo.merge!(meta_data)
|
|
151
|
+
|
|
152
|
+
# Open Graph
|
|
153
|
+
seo.open_graph do |og|
|
|
154
|
+
og.merge!(og_data)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Structured Data
|
|
158
|
+
if sd_data[:type]
|
|
159
|
+
seo.structured_data sd_data[:type].underscore.to_sym do |sd|
|
|
160
|
+
sd.merge!(sd_data[:data] || {})
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Apply block overrides
|
|
166
|
+
yield(@_seo_builder) if block_given?
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Rake Task Validazione
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
# lib/better_seo/tasks/i18n.rake
|
|
176
|
+
namespace :better_seo do
|
|
177
|
+
namespace :i18n do
|
|
178
|
+
desc "Validate SEO translations across locales"
|
|
179
|
+
task validate: :environment do
|
|
180
|
+
locales = BetterSeo.configuration.available_locales
|
|
181
|
+
base_locale = locales.first
|
|
182
|
+
|
|
183
|
+
I18n.backend.load_translations
|
|
184
|
+
|
|
185
|
+
# Get all page keys from base locale
|
|
186
|
+
base_keys = I18n.t("seo.pages", locale: base_locale).keys
|
|
187
|
+
|
|
188
|
+
errors = []
|
|
189
|
+
|
|
190
|
+
base_keys.each do |page_key|
|
|
191
|
+
locales.each do |locale|
|
|
192
|
+
begin
|
|
193
|
+
page_data = I18n.t("seo.pages.#{page_key}", locale: locale)
|
|
194
|
+
|
|
195
|
+
# Check required keys
|
|
196
|
+
unless page_data.dig(:meta, :title)
|
|
197
|
+
errors << "Missing meta.title in #{locale}:seo.pages.#{page_key}"
|
|
198
|
+
end
|
|
199
|
+
rescue I18n::MissingTranslationData
|
|
200
|
+
errors << "Missing translation for #{locale}:seo.pages.#{page_key}"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
if errors.any?
|
|
206
|
+
puts "β Validation failed:\n"
|
|
207
|
+
errors.each { |e| puts " - #{e}" }
|
|
208
|
+
exit 1
|
|
209
|
+
else
|
|
210
|
+
puts "β All SEO translations validated successfully"
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Checklist
|
|
220
|
+
|
|
221
|
+
- [ ] Page generator con templates
|
|
222
|
+
- [ ] Template YAML per IT e EN
|
|
223
|
+
- [ ] Support page types (website, article, product)
|
|
224
|
+
- [ ] Options (--skip-twitter, --skip-open-graph, etc.)
|
|
225
|
+
- [ ] Helper `set_seo_from_locale`
|
|
226
|
+
- [ ] Interpolazione variabili (%{name})
|
|
227
|
+
- [ ] Rake task `better_seo:i18n:validate`
|
|
228
|
+
- [ ] Test generator output
|
|
229
|
+
- [ ] Documentation esempi
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
**Prossimi Passi**: Step 07 - Image Optimization
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Step 07: Ottimizzazione Immagini e WebP
|
|
2
|
+
|
|
3
|
+
**Versione Target**: 0.8.0
|
|
4
|
+
**Durata Stimata**: 2-3 settimane
|
|
5
|
+
**PrioritΓ **: π’ BASSA (Nice to have)
|
|
6
|
+
**Dipendenze**: Step 01, 05
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Obiettivi
|
|
11
|
+
|
|
12
|
+
1. β
Conversione JPEG/PNG β WebP
|
|
13
|
+
2. β
Resize automatico (varianti multiple)
|
|
14
|
+
3. β
Compressione intelligente
|
|
15
|
+
4. β
Helper `seo_image_tag` con lazy loading
|
|
16
|
+
5. β
Picture tag con fallback
|
|
17
|
+
6. β
Active Storage/CarrierWave/Shrine integration
|
|
18
|
+
7. β
Rake tasks batch processing
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Dipendenze Esterne
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# better_seo.gemspec
|
|
26
|
+
spec.add_dependency "image_processing", "~> 1.12" # Wrapper per vips/imagemagick
|
|
27
|
+
spec.add_dependency "ruby-vips", "~> 2.1" # Per WebP e performance
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Sistema**: Richiede `libvips` installato
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# macOS
|
|
34
|
+
brew install vips
|
|
35
|
+
|
|
36
|
+
# Ubuntu
|
|
37
|
+
apt-get install libvips-dev
|
|
38
|
+
|
|
39
|
+
# Check installation
|
|
40
|
+
vips --version
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## File da Creare
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
lib/better_seo/images/optimizer.rb
|
|
49
|
+
lib/better_seo/images/converter.rb
|
|
50
|
+
lib/better_seo/images/resizer.rb
|
|
51
|
+
lib/better_seo/images/compressor.rb
|
|
52
|
+
lib/better_seo/images/validator.rb
|
|
53
|
+
lib/better_seo/images/helpers/image_tag_helper.rb
|
|
54
|
+
lib/better_seo/tasks/images.rake
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Implementazione: WebP Converter
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
# lib/better_seo/images/converter.rb
|
|
63
|
+
require "image_processing/vips"
|
|
64
|
+
|
|
65
|
+
module BetterSeo
|
|
66
|
+
module Images
|
|
67
|
+
class Converter
|
|
68
|
+
def self.to_webp(source_path, output_path = nil, quality: 80)
|
|
69
|
+
output_path ||= source_path.sub(/\.(jpe?g|png|gif)$/i, ".webp")
|
|
70
|
+
|
|
71
|
+
ImageProcessing::Vips
|
|
72
|
+
.source(source_path)
|
|
73
|
+
.convert("webp")
|
|
74
|
+
.saver(quality: quality)
|
|
75
|
+
.call(destination: output_path)
|
|
76
|
+
|
|
77
|
+
output_path
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Implementazione: Image Resizer
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
# lib/better_seo/images/resizer.rb
|
|
90
|
+
module BetterSeo
|
|
91
|
+
module Images
|
|
92
|
+
class Resizer
|
|
93
|
+
def initialize(source_path)
|
|
94
|
+
@source_path = source_path
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def generate_variants(sizes_config)
|
|
98
|
+
variants = {}
|
|
99
|
+
|
|
100
|
+
sizes_config.each do |size_name, options|
|
|
101
|
+
output_path = variant_path(size_name)
|
|
102
|
+
|
|
103
|
+
pipeline = ImageProcessing::Vips.source(@source_path)
|
|
104
|
+
|
|
105
|
+
if options[:crop]
|
|
106
|
+
pipeline = pipeline.resize_to_fill(options[:width], options[:height])
|
|
107
|
+
else
|
|
108
|
+
pipeline = pipeline.resize_to_limit(options[:width], options[:height])
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
pipeline.call(destination: output_path)
|
|
112
|
+
variants[size_name] = output_path
|
|
113
|
+
|
|
114
|
+
# Generate WebP version
|
|
115
|
+
webp_path = Converter.to_webp(output_path)
|
|
116
|
+
variants["#{size_name}_webp".to_sym] = webp_path
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
variants
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def variant_path(size_name)
|
|
125
|
+
ext = File.extname(@source_path)
|
|
126
|
+
base = @source_path.sub(ext, "")
|
|
127
|
+
"#{base}-#{size_name}#{ext}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Helper: seo_image_tag
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
# lib/better_seo/images/helpers/image_tag_helper.rb
|
|
140
|
+
module BetterSeo
|
|
141
|
+
module Images
|
|
142
|
+
module Helpers
|
|
143
|
+
module ImageTagHelper
|
|
144
|
+
def seo_image_tag(source, alt:, sizes: [], webp: true, lazy: false, **options)
|
|
145
|
+
if webp && sizes.any?
|
|
146
|
+
# Generate picture tag with WebP + responsive
|
|
147
|
+
render_picture_tag(source, alt: alt, sizes: sizes, lazy: lazy, **options)
|
|
148
|
+
elsif webp
|
|
149
|
+
# Simple picture tag with WebP fallback
|
|
150
|
+
render_simple_picture_tag(source, alt: alt, lazy: lazy, **options)
|
|
151
|
+
else
|
|
152
|
+
# Standard img tag
|
|
153
|
+
image_tag(source, alt: alt, loading: (lazy ? "lazy" : nil), **options)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def render_picture_tag(source, alt:, sizes:, lazy:, **options)
|
|
160
|
+
webp_srcset = sizes.map { |s| "#{variant_path(source, s, 'webp')} #{size_width(s)}w" }.join(", ")
|
|
161
|
+
jpg_srcset = sizes.map { |s| "#{variant_path(source, s, 'jpg')} #{size_width(s)}w" }.join(", ")
|
|
162
|
+
|
|
163
|
+
content_tag(:picture, class: "seo-image #{'lazy-image' if lazy}") do
|
|
164
|
+
concat tag(:source, type: "image/webp", srcset: webp_srcset)
|
|
165
|
+
concat tag(:source, type: "image/jpeg", srcset: jpg_srcset)
|
|
166
|
+
concat image_tag(source, alt: alt, loading: (lazy ? "lazy" : nil), **options)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def render_simple_picture_tag(source, alt:, lazy:, **options)
|
|
171
|
+
webp_source = source.sub(/\.(jpe?g|png)$/i, ".webp")
|
|
172
|
+
|
|
173
|
+
content_tag(:picture) do
|
|
174
|
+
concat tag(:source, type: "image/webp", srcset: webp_source)
|
|
175
|
+
concat image_tag(source, alt: alt, loading: (lazy ? "lazy" : nil), **options)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def variant_path(source, size, format)
|
|
180
|
+
ext = File.extname(source)
|
|
181
|
+
base = source.sub(ext, "")
|
|
182
|
+
"#{base}-#{size}.#{format}"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def size_width(size_name)
|
|
186
|
+
config = BetterSeo.configuration.images.sizes[size_name]
|
|
187
|
+
config&.dig(:width) || 1200
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Rake Tasks
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
# lib/better_seo/tasks/images.rake
|
|
201
|
+
namespace :better_seo do
|
|
202
|
+
namespace :images do
|
|
203
|
+
desc "Convert all images to WebP"
|
|
204
|
+
task :convert_to_webp, [:path] => :environment do |t, args|
|
|
205
|
+
path = args[:path] || Rails.root.join("public", "images")
|
|
206
|
+
|
|
207
|
+
Dir.glob("#{path}/**/*.{jpg,jpeg,png}").each do |file|
|
|
208
|
+
puts "Converting #{file}..."
|
|
209
|
+
BetterSeo::Images::Converter.to_webp(file)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
puts "β WebP conversion completed"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
desc "Generate image variants"
|
|
216
|
+
task :generate_variants, [:path] => :environment do |t, args|
|
|
217
|
+
path = args[:path] || Rails.root.join("public", "images")
|
|
218
|
+
sizes = BetterSeo.configuration.images.sizes
|
|
219
|
+
|
|
220
|
+
Dir.glob("#{path}/**/*.{jpg,jpeg,png}").each do |file|
|
|
221
|
+
puts "Generating variants for #{file}..."
|
|
222
|
+
resizer = BetterSeo::Images::Resizer.new(file)
|
|
223
|
+
resizer.generate_variants(sizes.to_h)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
puts "β Image variants generated"
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
desc "Validate images for SEO"
|
|
230
|
+
task :validate => :environment do
|
|
231
|
+
# Implementation: check alt text, file sizes, etc.
|
|
232
|
+
puts "β Image validation completed"
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Checklist
|
|
241
|
+
|
|
242
|
+
- [ ] WebP converter con ruby-vips
|
|
243
|
+
- [ ] Image resizer per varianti
|
|
244
|
+
- [ ] Smart compressor (quality optimization)
|
|
245
|
+
- [ ] Helper `seo_image_tag`
|
|
246
|
+
- [ ] Picture tag con srcset responsive
|
|
247
|
+
- [ ] Lazy loading support
|
|
248
|
+
- [ ] Blur placeholder (optional)
|
|
249
|
+
- [ ] Active Storage integration
|
|
250
|
+
- [ ] CarrierWave integration
|
|
251
|
+
- [ ] Shrine integration
|
|
252
|
+
- [ ] Rake tasks (convert, resize, validate)
|
|
253
|
+
- [ ] Image validator (alt text, size, format)
|
|
254
|
+
- [ ] Test coverage > 85%
|
|
255
|
+
- [ ] Performance benchmarks
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
**Status**: Implementation Ready
|
|
260
|
+
**Note**: Richiede libvips system dependency
|