mistral_translator 0.1.0 → 0.2.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +189 -121
  4. data/README_TESTING.md +33 -0
  5. data/SECURITY.md +157 -0
  6. data/docs/.nojekyll +2 -0
  7. data/docs/404.html +30 -0
  8. data/docs/README.md +153 -0
  9. data/docs/advanced-usage/batch-processing.md +158 -0
  10. data/docs/advanced-usage/error-handling.md +106 -0
  11. data/docs/advanced-usage/monitoring.md +133 -0
  12. data/docs/advanced-usage/summarization.md +86 -0
  13. data/docs/advanced-usage/translations.md +141 -0
  14. data/docs/api-reference/callbacks.md +231 -0
  15. data/docs/api-reference/configuration.md +74 -0
  16. data/docs/api-reference/errors.md +673 -0
  17. data/docs/api-reference/methods.md +539 -0
  18. data/docs/getting-started.md +179 -0
  19. data/docs/index.html +27 -0
  20. data/docs/installation.md +142 -0
  21. data/docs/migration-0.1.0-to-0.2.0.md +61 -0
  22. data/docs/rails-integration/adapters.md +84 -0
  23. data/docs/rails-integration/controllers.md +107 -0
  24. data/docs/rails-integration/jobs.md +97 -0
  25. data/docs/rails-integration/setup.md +339 -0
  26. data/examples/basic_usage.rb +129 -102
  27. data/examples/batch-job.rb +511 -0
  28. data/examples/monitoring-setup.rb +499 -0
  29. data/examples/rails-model.rb +399 -0
  30. data/lib/mistral_translator/adapters.rb +261 -0
  31. data/lib/mistral_translator/client.rb +103 -100
  32. data/lib/mistral_translator/client_helpers.rb +161 -0
  33. data/lib/mistral_translator/configuration.rb +171 -1
  34. data/lib/mistral_translator/errors.rb +16 -0
  35. data/lib/mistral_translator/helpers.rb +292 -0
  36. data/lib/mistral_translator/helpers_extensions.rb +150 -0
  37. data/lib/mistral_translator/levenshtein_helpers.rb +40 -0
  38. data/lib/mistral_translator/logger.rb +28 -4
  39. data/lib/mistral_translator/prompt_builder.rb +93 -41
  40. data/lib/mistral_translator/prompt_helpers.rb +83 -0
  41. data/lib/mistral_translator/prompt_metadata_helpers.rb +42 -0
  42. data/lib/mistral_translator/response_parser.rb +194 -23
  43. data/lib/mistral_translator/security.rb +72 -0
  44. data/lib/mistral_translator/summarizer.rb +41 -2
  45. data/lib/mistral_translator/translator.rb +174 -98
  46. data/lib/mistral_translator/translator_helpers.rb +268 -0
  47. data/lib/mistral_translator/version.rb +1 -1
  48. data/lib/mistral_translator.rb +51 -25
  49. metadata +39 -3
@@ -0,0 +1,339 @@
1
+ > **Navigation :** [🏠 Home](README.md) • [📖 API Reference](api-reference/methods.md) • [⚡ Advanced Usage](advanced-usage/translations.md) • [🛤️ Rails Integration](rails-integration/setup.md)
2
+
3
+ ---
4
+
5
+ # Configuration Rails
6
+
7
+ Intégration complète de MistralTranslator avec Ruby on Rails.
8
+
9
+ ## Initializer Rails
10
+
11
+ ```ruby
12
+ # config/initializers/mistral_translator.rb
13
+ MistralTranslator.configure do |config|
14
+ config.api_key = ENV['MISTRAL_API_KEY']
15
+ config.enable_metrics = Rails.env.production?
16
+
17
+ case Rails.env
18
+ when 'development'
19
+ config.setup_rails_logging
20
+ config.retry_delays = [1, 2, 4]
21
+
22
+ when 'production'
23
+ config.retry_delays = [2, 4, 8, 16, 32]
24
+
25
+ # Callbacks production
26
+ config.on_translation_error = ->(from, to, error, attempt, timestamp) {
27
+ if defined?(Sentry) && attempt > 2
28
+ Sentry.capture_exception(error, extra: {
29
+ translation: { from: from, to: to, attempt: attempt }
30
+ })
31
+ end
32
+ }
33
+
34
+ config.on_translation_complete = ->(from, to, orig_len, trans_len, duration) {
35
+ if defined?(StatsD)
36
+ StatsD.timing('mistral.translation.duration', duration * 1000)
37
+ StatsD.increment('mistral.translation.success')
38
+ end
39
+ }
40
+ end
41
+ end
42
+ ```
43
+
44
+ ## Helper Global
45
+
46
+ ```ruby
47
+ # app/helpers/application_helper.rb
48
+ module ApplicationHelper
49
+ def safe_translate(text, to:, context: nil)
50
+ return text if text.blank?
51
+ return text if I18n.locale.to_s == to.to_s
52
+
53
+ translator = MistralTranslator::Translator.new
54
+ translator.translate(
55
+ text,
56
+ from: I18n.locale.to_s,
57
+ to: to.to_s,
58
+ context: context
59
+ )
60
+ rescue MistralTranslator::Error => e
61
+ Rails.logger.error "Translation failed: #{e.message}"
62
+ text
63
+ end
64
+
65
+ def translate_with_cache(text, to:, **options)
66
+ cache_key = "translation:#{Digest::MD5.hexdigest(text)}:#{I18n.locale}:#{to}"
67
+
68
+ Rails.cache.fetch(cache_key, expires_in: 24.hours) do
69
+ safe_translate(text, to: to, **options)
70
+ end
71
+ end
72
+ end
73
+ ```
74
+
75
+ ## Service de Traduction
76
+
77
+ ```ruby
78
+ # app/services/translation_service.rb
79
+ class TranslationService
80
+ def self.translate_model(record, fields, target_locales = nil)
81
+ target_locales ||= I18n.available_locales - [I18n.locale]
82
+
83
+ success_count = 0
84
+ error_count = 0
85
+
86
+ target_locales.each do |locale|
87
+ begin
88
+ translate_fields_for_locale(record, fields, locale)
89
+ success_count += 1
90
+ rescue MistralTranslator::Error => e
91
+ Rails.logger.error "Translation failed for #{locale}: #{e.message}"
92
+ error_count += 1
93
+ end
94
+ end
95
+
96
+ { success: success_count, errors: error_count }
97
+ end
98
+
99
+ private
100
+
101
+ def self.translate_fields_for_locale(record, fields, locale)
102
+ Array(fields).each do |field|
103
+ source_value = record.send("#{field}_#{I18n.locale}")
104
+ next if source_value.blank?
105
+
106
+ translator = MistralTranslator::Translator.new
107
+ translated = translator.translate(
108
+ source_value,
109
+ from: I18n.locale.to_s,
110
+ to: locale.to_s,
111
+ context: "#{record.class.name} #{field}"
112
+ )
113
+
114
+ record.send("#{field}_#{locale}=", translated)
115
+ end
116
+
117
+ record.save!
118
+ end
119
+ end
120
+ ```
121
+
122
+ ## Concern pour Modèles
123
+
124
+ ```ruby
125
+ # app/models/concerns/translatable.rb
126
+ module Translatable
127
+ extend ActiveSupport::Concern
128
+
129
+ included do
130
+ after_save :auto_translate, if: :should_auto_translate?
131
+ end
132
+
133
+ class_methods do
134
+ def translatable_fields(*fields)
135
+ @translatable_fields = fields
136
+ end
137
+
138
+ def get_translatable_fields
139
+ @translatable_fields || []
140
+ end
141
+ end
142
+
143
+ def translate_to_all_locales
144
+ return false if self.class.get_translatable_fields.empty?
145
+
146
+ result = TranslationService.translate_model(
147
+ self,
148
+ self.class.get_translatable_fields
149
+ )
150
+
151
+ Rails.logger.info "Translated #{result[:success]} locales, #{result[:errors]} errors"
152
+ result[:errors] == 0
153
+ end
154
+
155
+ private
156
+
157
+ def should_auto_translate?
158
+ Rails.env.production? &&
159
+ self.class.get_translatable_fields.any? { |field| saved_change_to_attribute?("#{field}_#{I18n.locale}") }
160
+ end
161
+
162
+ def auto_translate
163
+ TranslateModelJob.perform_later(self.class.name, id)
164
+ end
165
+ end
166
+ ```
167
+
168
+ ## Exemple d'Usage
169
+
170
+ ```ruby
171
+ # app/models/article.rb
172
+ class Article < ApplicationRecord
173
+ include Translatable
174
+
175
+ translatable_fields :title, :content, :summary
176
+
177
+ # Méthode manuelle
178
+ def translate_title_to(locale)
179
+ translator = MistralTranslator::Translator.new
180
+ translator.translate(
181
+ title,
182
+ from: I18n.locale.to_s,
183
+ to: locale.to_s,
184
+ context: "Blog article title"
185
+ )
186
+ rescue MistralTranslator::Error
187
+ title
188
+ end
189
+ end
190
+
191
+ # Usage
192
+ article = Article.create!(title: "Mon article", content: "Contenu...")
193
+ article.translate_to_all_locales
194
+ ```
195
+
196
+ ## Job Asynchrone
197
+
198
+ ```ruby
199
+ # app/jobs/translate_model_job.rb
200
+ class TranslateModelJob < ApplicationJob
201
+ queue_as :translations
202
+
203
+ def perform(model_class, record_id)
204
+ model = model_class.constantize
205
+ record = model.find(record_id)
206
+
207
+ result = TranslationService.translate_model(record, model.get_translatable_fields)
208
+
209
+ Rails.logger.info "Translation job completed: #{result[:success]} success, #{result[:errors]} errors"
210
+ rescue ActiveRecord::RecordNotFound
211
+ Rails.logger.error "Record not found: #{model_class}##{record_id}"
212
+ rescue => e
213
+ Rails.logger.error "Translation job failed: #{e.message}"
214
+ raise e
215
+ end
216
+ end
217
+ ```
218
+
219
+ ## Variables d'Environnement
220
+
221
+ ```ruby
222
+ # .env.development
223
+ MISTRAL_API_KEY=your_dev_key_here
224
+
225
+ # .env.production
226
+ MISTRAL_API_KEY=your_prod_key_here
227
+
228
+ # config/application.yml (Figaro)
229
+ development:
230
+ MISTRAL_API_KEY: your_dev_key
231
+
232
+ production:
233
+ MISTRAL_API_KEY: your_prod_key
234
+ ```
235
+
236
+ ## Vues et Formulaires
237
+
238
+ ```erb
239
+ <!-- app/views/articles/show.html.erb -->
240
+ <div class="article">
241
+ <h1><%= @article.title %></h1>
242
+
243
+ <!-- Sélecteur de langue -->
244
+ <div class="language-selector">
245
+ <% I18n.available_locales.each do |locale| %>
246
+ <%= link_to locale.upcase,
247
+ article_path(@article, locale: locale),
248
+ class: ("active" if I18n.locale == locale) %>
249
+ <% end %>
250
+ </div>
251
+
252
+ <!-- Traduction à la demande -->
253
+ <% if I18n.locale != I18n.default_locale %>
254
+ <div class="translation-info">
255
+ <%= translate_with_cache(@article.content, to: I18n.locale,
256
+ context: "Blog article content") %>
257
+ </div>
258
+ <% else %>
259
+ <div class="content">
260
+ <%= @article.content %>
261
+ </div>
262
+ <% end %>
263
+ </div>
264
+ ```
265
+
266
+ ## Configuration Avancée
267
+
268
+ ```ruby
269
+ # config/initializers/mistral_translator.rb
270
+ MistralTranslator.configure do |config|
271
+ config.api_key = ENV['MISTRAL_API_KEY']
272
+
273
+ # Optimisation Rails
274
+ config.enable_metrics = true
275
+ config.default_max_tokens = 4000
276
+
277
+ # Cache intelligent des traductions
278
+ config.on_translation_complete = ->(from, to, orig_len, trans_len, duration) {
279
+ # Statistiques dans le cache Rails
280
+ Rails.cache.increment("translation_count_#{Date.current}", 1, expires_in: 1.day)
281
+
282
+ # Log des traductions coûteuses
283
+ if orig_len > 1000
284
+ Rails.logger.info "Large translation: #{orig_len} chars in #{duration.round(2)}s"
285
+ end
286
+ }
287
+
288
+ # Intégration avec ActiveSupport::Notifications
289
+ config.on_translation_start = ->(from, to, length, timestamp) {
290
+ ActiveSupport::Notifications.instrument("translation.mistral", {
291
+ from: from, to: to, length: length
292
+ })
293
+ }
294
+ end
295
+
296
+ # Écouter les notifications
297
+ ActiveSupport::Notifications.subscribe "translation.mistral" do |name, start, finish, id, payload|
298
+ Rails.logger.info "Translation notification: #{payload}"
299
+ end
300
+ ```
301
+
302
+ ## Tests Rails
303
+
304
+ ```ruby
305
+ # spec/support/mistral_translator.rb
306
+ RSpec.configure do |config|
307
+ config.before(:suite) do
308
+ MistralTranslator.configure do |c|
309
+ c.api_key = 'test-key'
310
+ c.enable_metrics = false
311
+ end
312
+ end
313
+ end
314
+
315
+ # spec/models/article_spec.rb
316
+ require 'rails_helper'
317
+
318
+ RSpec.describe Article do
319
+ before do
320
+ allow(MistralTranslator).to receive(:translate).and_return("Mocked translation")
321
+ end
322
+
323
+ describe '#translate_to_all_locales' do
324
+ it 'translates all fields to available locales' do
325
+ article = create(:article, title: "Test", content: "Content")
326
+
327
+ expect(MistralTranslator).to receive(:translate).twice
328
+ result = article.translate_to_all_locales
329
+
330
+ expect(result).to be true
331
+ end
332
+ end
333
+ end
334
+ ```
335
+
336
+ ---
337
+
338
+ **Rails Integration Navigation:**
339
+ [← Setup](rails-integration/setup.md) | [Adapters](rails-integration/adapters.md) | [Jobs](rails-integration/jobs.md) | [Controllers](rails-integration/controllers.md) →
@@ -1,135 +1,162 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Exemple d'utilisation de base de la gem MistralTranslator
4
+ # Exemple d'usage de base de MistralTranslator
5
+ # Usage: ruby examples/basic_usage.rb
6
+
7
+ # Préférer la version locale de la gem (pour tester la version du repo)
8
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
9
  require "mistral_translator"
6
10
 
7
- # Configuration
11
+ # Configuration minimale
8
12
  MistralTranslator.configure do |config|
9
13
  config.api_key = ENV["MISTRAL_API_KEY"] || "your_api_key_here"
10
- # config.api_url = 'https://api.mistral.ai' # optionnel
11
- # config.model = 'mistral-small' # optionnel
12
14
  end
13
15
 
14
16
  puts "=== MistralTranslator Examples ==="
15
- puts "Version: #{MistralTranslator.version}"
16
- puts
17
-
18
- # Vérification de la santé de l'API
19
- puts "🔍 Health Check:"
20
- health = MistralTranslator.health_check
21
- puts "Status: #{health[:status]} - #{health[:message]}"
22
17
  puts
23
18
 
24
- # Exemple 1: Traduction simple
25
- puts "📝 Traduction simple:"
19
+ # Vérification de base
26
20
  begin
27
- result = MistralTranslator.translate("Bonjour le monde", from: "fr", to: "en")
28
- puts "FR → EN: 'Bonjour le monde' → '#{result}'"
29
- rescue MistralTranslator::Error => e
30
- puts "Erreur: #{e.message}"
21
+ # Test simple pour vérifier que l'API fonctionne
22
+ test = MistralTranslator.translate("Hello", from: "en", to: "fr")
23
+ puts "Configuration OK - Test: #{test}"
24
+ rescue MistralTranslator::AuthenticationError
25
+ puts "ERREUR: Clé API invalide. Définissez MISTRAL_API_KEY"
26
+ exit 1
27
+ rescue MistralTranslator::ConfigurationError
28
+ puts "ERREUR: Configuration manquante"
29
+ exit 1
30
+ rescue StandardError => e
31
+ puts "ERREUR: #{e.message}"
32
+ exit 1
31
33
  end
32
34
  puts
33
35
 
34
- # Exemple 2: Traduction vers plusieurs langues
35
- puts "🌍 Traduction vers plusieurs langues:"
36
- begin
37
- results = MistralTranslator.translate_to_multiple(
38
- "Hello world",
39
- from: "en",
40
- to: %w[fr es de]
41
- )
42
-
43
- results.each do |locale, translation|
44
- puts "EN → #{locale.upcase}: '#{translation}'"
45
- end
46
- rescue MistralTranslator::Error => e
47
- puts "Erreur: #{e.message}"
48
- end
36
+ # 1. Traduction de base
37
+ puts "1. Traduction simple"
38
+ puts "-" * 20
39
+ result = MistralTranslator.translate("Bonjour le monde", from: "fr", to: "en")
40
+ puts "FR → EN: #{result}"
49
41
  puts
50
42
 
51
- # Exemple 3: Traduction en lot
52
- puts "📦 Traduction en lot:"
53
- begin
54
- texts = ["Good morning", "Good afternoon", "Good evening"]
55
- results = MistralTranslator.translate_batch(texts, from: "en", to: "fr")
56
-
57
- results.each do |index, translation|
58
- puts "#{index}: '#{texts[index]}' → '#{translation}'"
59
- end
60
- rescue MistralTranslator::Error => e
61
- puts "Erreur: #{e.message}"
62
- end
43
+ translator = MistralTranslator::Translator.new
44
+
45
+ # Détection de la prise en charge de `context` et `glossary` (>= 0.2.0)
46
+ supports_context_glossary = Gem::Version.new(MistralTranslator::VERSION) >= Gem::Version.new("0.2.0")
47
+
48
+ # 2. Traduction avec contexte
49
+ puts "2. Traduction avec contexte"
50
+ puts "-" * 27
51
+ result = if supports_context_glossary
52
+ translator.translate(
53
+ "Batterie faible",
54
+ from: "fr",
55
+ to: "en",
56
+ context: "Smartphone notification"
57
+ )
58
+ else
59
+ translator.translate(
60
+ "Batterie faible",
61
+ from: "fr",
62
+ to: "en"
63
+ )
64
+ end
65
+ puts "Avec contexte: #{result}"
63
66
  puts
64
67
 
65
- # Exemple 4: Auto-détection de langue
66
- puts "🔍 Auto-détection de langue:"
67
- begin
68
- result = MistralTranslator.translate_auto("¡Hola mundo!", to: "en")
69
- puts "Auto-détection → EN: '#{result}'"
70
- rescue MistralTranslator::Error => e
71
- puts "Erreur: #{e.message}"
68
+ # 3. Traduction avec glossaire
69
+ puts "3. Traduction avec glossaire"
70
+ puts "-" * 28
71
+ result = if supports_context_glossary
72
+ translator.translate(
73
+ "L'IA révolutionne le secteur",
74
+ from: "fr",
75
+ to: "en",
76
+ glossary: { "IA" => "AI" }
77
+ )
78
+ else
79
+ translator.translate(
80
+ "L'IA révolutionne le secteur",
81
+ from: "fr",
82
+ to: "en"
83
+ )
84
+ end
85
+ puts "Avec glossaire: #{result}"
86
+ puts
87
+
88
+ # 4. Auto-détection
89
+ puts "4. Auto-détection de langue"
90
+ puts "-" * 27
91
+ texts = ["¡Hola mundo!", "Guten Tag", "Ciao mondo"]
92
+ texts.each do |text|
93
+ result = MistralTranslator.translate_auto(text, to: "fr")
94
+ puts "#{text} → #{result}"
72
95
  end
73
96
  puts
74
97
 
75
- # Exemple 5: Résumé de texte
76
- puts "📄 Résumé de texte:"
77
- long_text = <<~TEXT
78
- Ruby on Rails est un framework de développement web écrit en Ruby qui suit le paradigme#{" "}
79
- Modèle-Vue-Contrôleur (MVC). Il privilégie la convention plutôt que la configuration,#{" "}
80
- ce qui permet aux développeurs de créer rapidement des applications web robustes.#{" "}
81
- Rails comprend de nombreux outils intégrés comme Active Record pour l'ORM,#{" "}
82
- Action View pour les templates, Action Controller pour la logique métier,#{" "}
83
- et bien d'autres composants. Le framework a été créé par David Heinemeier Hansson#{" "}
84
- en 2004 et continue d'évoluer avec une communauté active de développeurs.
98
+ # 5. Traduction vers plusieurs langues
99
+ puts "5. Multi-langues"
100
+ puts "-" * 14
101
+ results = translator.translate_to_multiple(
102
+ "Bienvenue",
103
+ from: "fr",
104
+ to: %w[en es de]
105
+ )
106
+ results.each { |lang, text| puts "#{lang.upcase}: #{text}" }
107
+ puts
108
+
109
+ # 6. Traduction par lot
110
+ puts "6. Traduction par lot"
111
+ puts "-" * 19
112
+ texts = ["Bonjour", "Merci", "Au revoir"]
113
+ results = MistralTranslator.translate_batch(texts, from: "fr", to: "en")
114
+ results.each { |i, translation| puts "#{texts[i]} → #{translation}" }
115
+ puts
116
+
117
+ # 7. Résumé simple
118
+ puts "7. Résumé de texte"
119
+ puts "-" * 17
120
+ article = <<~TEXT.strip
121
+ Ruby on Rails est un framework web MVC écrit en Ruby qui privilégie la
122
+ convention sur la configuration. Il inclut Active Record pour l'ORM, Action
123
+ View pour les vues, et Action Controller pour la logique métier.
85
124
  TEXT
86
125
 
87
- begin
88
- summary = MistralTranslator.summarize(long_text, language: "fr", max_words: 50)
89
- puts "Texte original: #{long_text.length} caractères"
90
- puts "Résumé (50 mots max): #{summary}"
91
- rescue MistralTranslator::Error => e
92
- puts "Erreur: #{e.message}"
93
- end
126
+ summary = MistralTranslator.summarize(article, language: "fr", max_words: 25)
127
+ puts "Résumé: #{summary}"
94
128
  puts
95
129
 
96
- # Exemple 6: Résumé avec traduction
97
- puts "🔄 Résumé + Traduction:"
98
- begin
99
- result = MistralTranslator.summarize_and_translate(
100
- long_text,
101
- from: "fr",
102
- to: "en",
103
- max_words: 75
104
- )
105
- puts "Résumé traduit EN (75 mots max): #{result}"
106
- rescue MistralTranslator::Error => e
107
- puts "Erreur: #{e.message}"
108
- end
130
+ # 8. Résumé avec traduction
131
+ puts "8. Résumé + traduction"
132
+ puts "-" * 22
133
+ result = MistralTranslator.summarize_and_translate(
134
+ article,
135
+ from: "fr",
136
+ to: "en",
137
+ max_words: 30
138
+ )
139
+ puts "EN: #{result}"
109
140
  puts
110
141
 
111
- # Exemple 7: Résumés par niveaux
112
- puts "📊 Résumés par niveaux:"
113
- begin
114
- tiered = MistralTranslator.summarize_tiered(
115
- long_text,
116
- language: "fr",
117
- short: 25,
118
- medium: 75,
119
- long: 150
120
- )
121
-
122
- puts "Court (25 mots): #{tiered[:short]}"
123
- puts "Moyen (75 mots): #{tiered[:medium]}"
124
- puts "Long (150 mots): #{tiered[:long]}"
125
- rescue MistralTranslator::Error => e
126
- puts "Erreur: #{e.message}"
127
- end
142
+ # 9. Résumés multi-niveaux
143
+ puts "9. Résumés par niveaux"
144
+ puts "-" * 22
145
+ tiered = MistralTranslator.summarize_tiered(
146
+ article,
147
+ language: "fr",
148
+ short: 15,
149
+ medium: 30,
150
+ long: 50
151
+ )
152
+ puts "Court: #{tiered[:short]}"
153
+ puts "Moyen: #{tiered[:medium]}"
128
154
  puts
129
155
 
130
- # Informations sur les langues supportées
131
- puts "🌐 Langues supportées:"
132
- puts MistralTranslator.supported_languages
156
+ # 10. Informations sur la gem
157
+ puts "10. Informations"
158
+ puts "-" * 15
159
+ puts "Langues: #{MistralTranslator.supported_locales.join(", ")}"
133
160
  puts
134
161
 
135
- puts " Exemples terminés!"
162
+ puts "Tous les exemples terminés !"