better_seo 0.14.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/README.md +297 -179
  4. data/docs/00_OVERVIEW.md +472 -0
  5. data/docs/01_CORE_AND_CONFIGURATION.md +913 -0
  6. data/docs/02_META_TAGS_AND_OPEN_GRAPH.md +251 -0
  7. data/docs/03_STRUCTURED_DATA.md +140 -0
  8. data/docs/04_SITEMAP_AND_ROBOTS.md +131 -0
  9. data/docs/05_RAILS_INTEGRATION.md +175 -0
  10. data/docs/06_I18N_PAGE_GENERATOR.md +233 -0
  11. data/docs/07_IMAGE_OPTIMIZATION.md +260 -0
  12. data/docs/DEPENDENCIES.md +383 -0
  13. data/docs/README.md +180 -0
  14. data/docs/TESTING_STRATEGY.md +663 -0
  15. data/lib/better_seo/analytics/google_analytics.rb +83 -0
  16. data/lib/better_seo/analytics/google_tag_manager.rb +74 -0
  17. data/lib/better_seo/configuration.rb +316 -0
  18. data/lib/better_seo/dsl/base.rb +86 -0
  19. data/lib/better_seo/dsl/meta_tags.rb +55 -0
  20. data/lib/better_seo/dsl/open_graph.rb +109 -0
  21. data/lib/better_seo/dsl/twitter_cards.rb +131 -0
  22. data/lib/better_seo/errors.rb +31 -0
  23. data/lib/better_seo/generators/amp_generator.rb +83 -0
  24. data/lib/better_seo/generators/breadcrumbs_generator.rb +126 -0
  25. data/lib/better_seo/generators/canonical_url_manager.rb +106 -0
  26. data/lib/better_seo/generators/meta_tags_generator.rb +100 -0
  27. data/lib/better_seo/generators/open_graph_generator.rb +110 -0
  28. data/lib/better_seo/generators/robots_txt_generator.rb +102 -0
  29. data/lib/better_seo/generators/twitter_cards_generator.rb +102 -0
  30. data/lib/better_seo/image/optimizer.rb +143 -0
  31. data/lib/better_seo/rails/helpers/controller_helpers.rb +118 -0
  32. data/lib/better_seo/rails/helpers/seo_helper.rb +176 -0
  33. data/lib/better_seo/rails/helpers/structured_data_helper.rb +123 -0
  34. data/lib/better_seo/rails/model_helpers.rb +62 -0
  35. data/lib/better_seo/rails/railtie.rb +22 -0
  36. data/lib/better_seo/sitemap/builder.rb +65 -0
  37. data/lib/better_seo/sitemap/generator.rb +57 -0
  38. data/lib/better_seo/sitemap/sitemap_index.rb +73 -0
  39. data/lib/better_seo/sitemap/url_entry.rb +157 -0
  40. data/lib/better_seo/structured_data/article.rb +55 -0
  41. data/lib/better_seo/structured_data/base.rb +73 -0
  42. data/lib/better_seo/structured_data/breadcrumb_list.rb +49 -0
  43. data/lib/better_seo/structured_data/event.rb +207 -0
  44. data/lib/better_seo/structured_data/faq_page.rb +55 -0
  45. data/lib/better_seo/structured_data/generator.rb +75 -0
  46. data/lib/better_seo/structured_data/how_to.rb +96 -0
  47. data/lib/better_seo/structured_data/local_business.rb +94 -0
  48. data/lib/better_seo/structured_data/organization.rb +67 -0
  49. data/lib/better_seo/structured_data/person.rb +51 -0
  50. data/lib/better_seo/structured_data/product.rb +123 -0
  51. data/lib/better_seo/structured_data/recipe.rb +135 -0
  52. data/lib/better_seo/validators/seo_recommendations.rb +165 -0
  53. data/lib/better_seo/validators/seo_validator.rb +195 -0
  54. data/lib/better_seo/version.rb +1 -1
  55. data/lib/better_seo.rb +1 -0
  56. data/lib/generators/better_seo/install_generator.rb +21 -0
  57. data/lib/generators/better_seo/templates/README +29 -0
  58. data/lib/generators/better_seo/templates/better_seo.rb +40 -0
  59. metadata +55 -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