immosquare-translate 0.1.12 → 0.1.14

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67b99071be3a2d74bdeae22667154f01943fb334b9a0f17ad6304f78ee353344
4
- data.tar.gz: b9764f58e1a3ee769d240fc46a6077df76f2ce97edf85a4b13af640713f63ebd
3
+ metadata.gz: 87dc92e836e2e404cccfa85c01fac2b8edf396aecc1a8a74558f66532942d6fd
4
+ data.tar.gz: 449976467c208d55e4dc7bf95c1089fae2889a954d64c385186789aabb1bd228
5
5
  SHA512:
6
- metadata.gz: 6cc53f8830d8ee64362b2266d9d53198a13401ee3f7a815fd4500bf55fc8ab9afc78a625d3f7a017dfd879a4daad5e41d5a9f92089582ad492b2149468b00788
7
- data.tar.gz: ad7140a4cb11569f6d2c170f56a5ea967c7672c961282951fc28c70b1dec1dbaa89b899e60eb18ba6a04436502ad072237df75443294ad71cda3f7f85a1f0e10
6
+ metadata.gz: c89194b90aa0b774308113059769a2d3021a13d7359df80baeab1539562acbd0dc1e8f5088f48d5bb7d66efcf5a4214cf49aa71cbfa609da6013dd080d8bf2be
7
+ data.tar.gz: 2eb2ee90027ebbbfe1fb38aee306c070b785de2aed44001290225d67f83b25ad872efd09d2910832e68bff65e91507b200ea9bfa865fd8cbbc67bc85a9514c89
@@ -5,11 +5,18 @@ module ImmosquareTranslate
5
5
  SIMPLE_QUOTE = "'".freeze
6
6
  DOUBLE_QUOTE = '"'.freeze
7
7
 
8
-
9
- OPEN_AI_MODELS = [
10
- {:name => "gpt-3.5-turbo-0125", :window_tokens => 16_385, :output_tokens => 4096, :input_price_for_1m => 0.50, :output_price_for_1m => 1.50, :group_size => 75},
11
- {:name => "gpt-4-turbo", :window_tokens => 128_000, :output_tokens => 4096, :input_price_for_1m => 10.00, :output_price_for_1m => 30.00, :group_size => 75},
12
- {:name => "gpt-4o", :window_tokens => 128_000, :output_tokens => 4096, :input_price_for_1m => 5.00, :output_price_for_1m => 15.00, :group_size => 75}
8
+ ##============================================================##
9
+ ## https://platform.openai.com/docs/pricing
10
+ ## List updated on : 21/05/2025
11
+ ##============================================================##
12
+ OPEN_AI_MODELS = [
13
+ {:nickname => "gpt-3.5", :name => "gpt-3.5-turbo-0125", :default => false, :window_tokens => 16_385, :output_tokens => 4_096, :input_price_for_1m => 0.50, :output_price_for_1m => 1.50, :group_size => 75},
14
+ {:nickname => "gpt-4", :name => "gpt-4-turbo-2024-04-09", :default => false, :window_tokens => 128_000, :output_tokens => 4_096, :input_price_for_1m => 10.00, :output_price_for_1m => 30.00, :group_size => 75},
15
+ {:nickname => "gpt-4o-mini", :name => "gpt-4o-mini", :default => false, :window_tokens => 128_000, :output_tokens => 16_384, :input_price_for_1m => 0.15, :output_price_for_1m => 0.60, :group_size => 75},
16
+ {:nickname => "gpt-4o", :name => "gpt-4o-2024-08-06", :default => false, :window_tokens => 128_000, :output_tokens => 16_384, :input_price_for_1m => 2.50, :output_price_for_1m => 10.00, :group_size => 75},
17
+ {:nickname => "gpt-4.1-nano", :name => "gpt-4.1-nano", :default => false, :window_tokens => 1_000_000, :output_tokens => 32_768, :input_price_for_1m => 0.10, :output_price_for_1m => 0.40, :group_size => 75},
18
+ {:nickname => "gpt-4.1-mini", :name => "gpt-4.1-mini", :default => false, :window_tokens => 1_000_000, :output_tokens => 32_768, :input_price_for_1m => 0.40, :output_price_for_1m => 1.60, :group_size => 75},
19
+ {:nickname => "gpt-4.1", :name => "gpt-4.1-2025-04-14", :default => true, :window_tokens => 1_000_000, :output_tokens => 32_768, :input_price_for_1m => 2.00, :output_price_for_1m => 8.00, :group_size => 75}
13
20
  ].freeze
14
21
  end
15
22
  end
@@ -1,3 +1,7 @@
1
+ require "immosquare-constants"
2
+ require "iso-639"
3
+ require "countries"
4
+
1
5
  module ImmosquareTranslate
2
6
  module Translator
3
7
  extend SharedMethods
@@ -10,49 +14,78 @@ module ImmosquareTranslate
10
14
  ## from : string
11
15
  ## to : array
12
16
  ##============================================================##
13
- def translate(text, from, to)
17
+ def translate(texts, from, to)
14
18
  begin
15
19
  raise("Error: openai_api_key not found in config_dev.yml") if ImmosquareTranslate.configuration.openai_api_key.nil?
16
20
  raise("Error: locale from is not a locale") if !from.is_a?(String) || from.size != 2
17
- raise("Error: locales is not an array of locales") if !to.is_a?(Array) || to.empty? || to.any? {|l| !l.is_a?(String) || l.size != 2 }
21
+ raise("Error: locales is not an array of locales") if !to.is_a?(Array) || to.empty? || to.any? {|l| !l.is_a?(String) }
22
+
23
+ model_name = ImmosquareTranslate.configuration.openai_model
24
+ model = OPEN_AI_MODELS.find {|m| m[:name] == model_name || m[:nickname] == model_name }
25
+ model = OPEN_AI_MODELS.find {|m| m[:default] == true } if model.nil?
26
+
27
+ puts("To translate, we will use the model: #{model[:nickname]}")
18
28
 
19
- model_name = ImmosquareTranslate.configuration.openai_model
20
- model = OPEN_AI_MODELS.find {|m| m[:name] == model_name }
21
- model = OPEN_AI_MODELS.find {|m| m[:name] == "gpt-4o" } if model.nil?
22
- from_iso = ISO_639.find_by_code(from).english_name.split(";").first
23
- to_iso = to.map {|iso| [iso, ISO_639.find_by_code(iso).english_name.split(";").first] }
29
+ ##============================================================##
30
+ ## We get the language name and the country name for each locale
31
+ ##============================================================##
32
+ from_language_name = ISO_639.find_by_code(from).english_name.split(";").first
33
+ to_iso = to
34
+ .reject {|code| ImmosquareConstants::Locale.native_name_for_locale(code).nil? }
35
+ .map do |iso|
36
+ iso_parts = iso.split("-")
37
+ iso_language = iso_parts.first.downcase
38
+ iso_country = iso_parts.size > 1 ? iso_parts.last.downcase : nil
39
+ language_english_name = ISO_639.find_by_code(iso_language)&.english_name&.split(";")&.first
40
+ country_english_name = iso_country.nil? ? nil : ISO3166::Country.find_country_by_alpha2(iso_country)&.iso_short_name
41
+ [iso, language_english_name, country_english_name]
42
+ end
43
+
44
+ ##============================================================##
45
+ ## Request headers
46
+ ##============================================================##
24
47
  headers = {
25
48
  "Content-Type" => "application/json",
26
49
  "Authorization" => "Bearer #{ImmosquareTranslate.configuration.openai_api_key}"
27
50
  }
28
51
 
52
+ ##============================================================##
53
+ ## System prompt
54
+ ##============================================================##
29
55
  prompt_system = "As a sophisticated translation AI, your role is to translate sentences from a specified source language to multiple target languages.\n" \
56
+ "We pass you target languages as an array of arrays with this format: [iso_code to use (2 or 4 letters), language target name, country name (country vocabulary to use, this parameter is optional, can be null)].\n" \
30
57
  "Rules to respect:\n" \
31
- "- Use ISO 639-1 language codes for specifying languages." \
32
- "- Respond with an array of a flat objects in JSON (minified, without any extraneous characters or formatting)\n" \
33
- "- Format the translation output as a JSON string adhering to the following structure: {\"datas\":[{\"locale_iso\": \"Translated Text\"}]} where locale_iso is the ISO 639-1 language code for specifying languages." \
58
+ "- Use the inputted ISO codes for specifying languages.\n" \
59
+ "- Respond with an array of flat objects in JSON (minified, without any extraneous characters or formatting).\n" \
60
+ "- Format the translation output as a JSON string adhering to the following structure: {\"datas\":[{\"locale_iso\": \"Translated Text\"}]} where locale_iso is the language code specifying the language and regional variant.\n" \
34
61
  "- Ensure that the output does not include markdown (```json) or any other formatting characters. Adhere to the JSON structure meticulously.\n" \
35
62
  "- Correct any spelling or grammatical errors in the source text before translating.\n" \
36
- "- If the source language is also a target language, include the corrected version of the sentence for that language as well, if not dont include it.\n" \
63
+ "- If the source language is also a target language, include the corrected version of the sentence for that language.\n" \
37
64
  "- If string to translate is html, you should return the translated html.\n" \
38
- "- If string to translate contains underscores in row, keep them, don't remove them\n" \
39
-
40
-
41
-
42
- prompt = "Translate the #{text.size} following #{text.size == 1 ? "sentence" : "sentences"} from the source language (ISO 639-1 code: #{from}) to the target languages specified: #{to_iso.map {|iso, language| "#{language} (ISO 639-1 code: #{iso})" }.join(", ")}. "
65
+ "- If string to translate contains underscores in a row, keep them, don't remove them.\n" \
66
+ "- For regional variants (e.g., fr-CA or en-US), ensure the translation reflects the cultural and linguistic norms specific to that country. This includes word choices, idiomatic expressions, and spellings commonly used in that region.\n" \
67
+ "- Example: For the text 'I am going to the supermarket', the translation for 'fr-CA' should be 'Je vais à l'épicerie', as 'épicerie' is more common in Canadian French than 'supermarché'.\n" \
68
+ "- Ensure that translations for each input string are grouped together in a single JSON object. Each object must include all requested translations for that string, using the iso_code as keys.\n" \
69
+ "- For multiple input strings, return an array of objects, where each object corresponds to an input string and contains all its translations.\n" \
70
+ "- Example output for two input strings 'Hello' and 'Goodbye' with target languages ['en', 'es', 'fr']: [{\"en\":\"Hello\",\"es\":\"Hola\",\"fr\":\"Bonjour\"},{\"en\":\"Goodbye\",\"es\":\"Adiós\",\"fr\":\"Au revoir\"}].\n"
43
71
 
72
+ ##============================================================##
73
+ ## User prompt
74
+ ##============================================================##
75
+ prompt = "Translate the #{texts.size} following #{texts.size == 1 ? "text" : "texts"} from the source language: #{from_language_name} to the target languages specified: #{to_iso}."
44
76
 
45
77
  ##============================================================##
46
78
  ## we replace the \n \t by ___ to avoid JSON parsing errors
47
79
  ## We use the same symbol to replace the \n and \t because
48
80
  ## if we use different symbols sometimes the API inverse them.
49
81
  ##============================================================##
50
- text.each_with_index do |sentence, index|
82
+ texts.each_with_index do |sentence, index|
51
83
  prompt += "\n#{index + 1}: #{sentence.gsub("\n", "___").gsub("\t", "____")}"
52
84
  end
53
85
 
54
-
55
-
86
+ ##============================================================##
87
+ ## Request body
88
+ ##============================================================##
56
89
  body = {
57
90
  :model => model[:name],
58
91
  :messages => [
@@ -64,10 +97,11 @@ module ImmosquareTranslate
64
97
 
65
98
 
66
99
  t0 = Time.now
67
- call = HTTParty.post("https://api.openai.com/v1/chat/completions", :body => body.to_json, :headers => headers, :timeout => 500)
100
+ url = "https://api.openai.com/v1/chat/completions"
101
+ call = HTTParty.post(url, :body => body.to_json, :headers => headers, :timeout => 500)
68
102
 
69
103
 
70
- puts("responded in #{(Time.now - t0).round(2)} seconds")
104
+ puts("OpenAI api response in #{(Time.now - t0).round(2)} seconds")
71
105
  raise(call["error"]["message"]) if call.code != 200
72
106
 
73
107
  ##============================================================##
@@ -77,6 +111,7 @@ module ImmosquareTranslate
77
111
  choice = response["choices"][0]
78
112
  raise("Result is not complete") if choice["finish_reason"] != "stop"
79
113
 
114
+
80
115
  ##============================================================##
81
116
  ## We calculate the estimate price of the call
82
117
  ##============================================================##
@@ -85,17 +120,18 @@ module ImmosquareTranslate
85
120
  price = input_price + output_price
86
121
  puts("Estimate price => #{input_price.round(3)} + #{output_price.round(3)} = #{price.round(3)} USD")
87
122
 
88
-
89
123
  ##============================================================##
90
124
  ## On s'assure de ne renvoyer que les locales demandées
91
125
  ## car l'API peut renvoyer des locales non demandées...
92
126
  ##============================================================##
93
- content = JSON.parse(choice["message"]["content"])
94
- datas = content["datas"]
127
+ datas = JSON.parse(choice["message"]["content"])
95
128
  datas.map do |hash|
96
129
  hash
97
- .select {|key, _| to.include?(key) }
130
+ .select {|key, _| to.map(&:downcase).include?(key.downcase) }
98
131
  .transform_values {|value| value.gsub("____", "\t").gsub("___", "\n") }
132
+ .transform_keys do |key|
133
+ key.to_s.split("-").map.with_index {|part, index| index == 0 ? part.downcase : part.upcase }.join("-").to_sym
134
+ end
99
135
  end.reject(&:empty?)
100
136
  rescue StandardError => e
101
137
  puts(e.message)
@@ -1,3 +1,3 @@
1
1
  module ImmosquareTranslate
2
- VERSION = "0.1.12".freeze
2
+ VERSION = "0.1.14".freeze
3
3
  end
@@ -10,18 +10,18 @@ module ImmosquareTranslate
10
10
 
11
11
  def translate(file_path, locale_to, options = {})
12
12
  begin
13
- ##=============================================================##
13
+ ##============================================================##
14
14
  ## options
15
- ##=============================================================##
15
+ ##============================================================##
16
16
  options = {
17
17
  :reset_translations => false
18
18
  }.merge(options)
19
19
  options[:reset_translations] = false if ![true, false].include?(options[:reset_translations])
20
20
 
21
21
 
22
- ##=============================================================##
22
+ ##============================================================##
23
23
  ## Load config keys from config_dev.yml
24
- ##=============================================================##
24
+ ##============================================================##
25
25
  raise("Error: openai_api_key not found in config_dev.yml") if ImmosquareTranslate.configuration.openai_api_key.nil?
26
26
  raise("Error: File #{file_path} not found") if !File.exist?(file_path)
27
27
  raise("Error: locale is not a locale") if !locale_to.is_a?(String) || locale_to.size != 2
@@ -124,7 +124,8 @@ module ImmosquareTranslate
124
124
  ## format = "string" and keys_only = true => ["fr.demo1", "fr.demo2.demo2-1"]
125
125
  ## format = "array" and keys_only = false => [[["fr", "demo1"], "demo1"], [["fr", "demo2", "demo2-1"], "demo2-1"]]
126
126
  ## format = "array" and keys_only = true => [["fr", "demo1"], ["fr", "demo2", "demo2-1"]]
127
- ## ============================================================
127
+ ## ---------
128
+ ##============================================================##
128
129
  def translatable_array(hash, key = nil, result = [], **options)
129
130
  options = {
130
131
  :format => "string",
@@ -162,12 +163,8 @@ module ImmosquareTranslate
162
163
 
163
164
  ##============================================================##
164
165
  ## Translate with OpenAI
165
- ##
166
- ## [
167
- ## ["en.mlsconnect.contact_us", "Nous contacter", "Contact us"],
168
- ## ["en.mlsconnect.description", "Description", nil],
169
- ## ...
170
- ## ]
166
+ ## [["en.mlsconnect.contact_us", "Nous contacter", "Contact us"],
167
+ ## ["en.mlsconnect.description", "Description", nil]]
171
168
  ##============================================================##
172
169
  def translate_with_open_ai(array, from, to)
173
170
  ##============================================================##
@@ -175,8 +172,8 @@ module ImmosquareTranslate
175
172
  ## https://openai.com/pricing
176
173
  ##============================================================##
177
174
  model_name = ImmosquareTranslate.configuration.openai_model
178
- model = OPEN_AI_MODELS.find {|m| m[:name] == model_name }
179
- model = OPEN_AI_MODELS.find {|m| m[:name] == "gpt-4o" } if model.nil?
175
+ model = OPEN_AI_MODELS.find {|m| m[:name] == model_name || m[:nickname] == model_name }
176
+ model = OPEN_AI_MODELS.find {|m| m[:default] == true } if model.nil?
180
177
 
181
178
  ##============================================================##
182
179
  ## Manage blank values
@@ -192,7 +189,7 @@ module ImmosquareTranslate
192
189
  ## we want to send as little data as possible to openAI because
193
190
  ## we pay for the volume of data sent. So we're going to send. We put
194
191
  ## a number rather than a string for the translations to be made.
195
- ## --------
192
+ ## ---------
196
193
  ## Remove the translations that have already been made
197
194
  ##============================================================##
198
195
  data_open_ai = array.clone
@@ -247,7 +244,7 @@ module ImmosquareTranslate
247
244
  ## https://platform.openai.com/tokenizer
248
245
  ## English: 75 words => 100 tokens
249
246
  ## French : 55 words => 100 tokens
250
- ## -----------------
247
+ ## ---------
251
248
  ## For each array value we add 5 tokens for the array format.
252
249
  ## [1, "my_word"],
253
250
  ## [ => first token
@@ -255,8 +252,8 @@ module ImmosquareTranslate
255
252
  ## , => third token
256
253
  ## " => fourth token
257
254
  ## ]" => fifth token
258
- ## -----------------
259
- # data_open_ai.inspect.size => to get the total number of characters in the array
255
+ ## ---------
256
+ ## data_open_ai.inspect.size => to get the total number of characters in the array
260
257
  ## with the array structure [""],
261
258
  ##============================================================##
262
259
  estimation_for_100_tokens = from == "fr" ? 55 : 75
@@ -269,7 +266,7 @@ module ImmosquareTranslate
269
266
  ##============================================================##
270
267
  ## Now each slice of the array should no be more than window_tokens
271
268
  ## of the model.... We can now translate each slice.
272
- ## ---------------------------------
269
+ ## ---------
273
270
  ## Normally we could send the whole slice at once and tell the api to continue if its response is not tarnished...
274
271
  ## But it should manage if a word is cut etc...
275
272
  ## For the moment we cut it into small group for which we are sure not to exceed the limit
@@ -291,7 +288,8 @@ module ImmosquareTranslate
291
288
  :temperature => 0.0
292
289
  }
293
290
  t0 = Time.now
294
- call = HTTParty.post("https://api.openai.com/v1/chat/completions", :body => body.to_json, :headers => headers, :timeout => 500)
291
+ url = "https://api.openai.com/v1/chat/completions"
292
+ call = HTTParty.post(url, :body => body.to_json, :headers => headers, :timeout => 500)
295
293
 
296
294
  puts("responded in #{(Time.now - t0).round(2)} seconds")
297
295
  raise(call["error"]["message"]) if call.code != 200
@@ -5,15 +5,12 @@ require_relative "immosquare-translate/translator"
5
5
  require_relative "immosquare-translate/railtie" if defined?(Rails)
6
6
 
7
7
 
8
- ##===========================================================================##
9
- ##
10
- ##===========================================================================##
11
8
  module ImmosquareTranslate
12
9
  class << self
13
10
 
14
- ##===========================================================================##
11
+ ##============================================================##
15
12
  ## Gem configuration
16
- ##===========================================================================##
13
+ ##============================================================##
17
14
  attr_writer :configuration
18
15
 
19
16
  def configuration
@@ -25,6 +22,5 @@ module ImmosquareTranslate
25
22
  end
26
23
 
27
24
 
28
-
29
25
  end
30
26
  end
metadata CHANGED
@@ -1,63 +1,114 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immosquare-translate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - immosquare
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-10-21 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: httparty
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - "~>"
16
+ - - ">"
18
17
  - !ruby/object:Gem::Version
19
18
  version: '0'
19
+ - - "<="
20
+ - !ruby/object:Gem::Version
21
+ version: '100'
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
23
25
  requirements:
24
- - - "~>"
26
+ - - ">"
25
27
  - !ruby/object:Gem::Version
26
28
  version: '0'
29
+ - - "<="
30
+ - !ruby/object:Gem::Version
31
+ version: '100'
27
32
  - !ruby/object:Gem::Dependency
28
33
  name: immosquare-yaml
29
34
  requirement: !ruby/object:Gem::Requirement
30
35
  requirements:
31
- - - "~>"
36
+ - - ">"
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ - - "<="
40
+ - !ruby/object:Gem::Version
41
+ version: '100'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">"
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ - - "<="
50
+ - !ruby/object:Gem::Version
51
+ version: '100'
52
+ - !ruby/object:Gem::Dependency
53
+ name: immosquare-constants
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">"
32
57
  - !ruby/object:Gem::Version
33
58
  version: '0'
34
- - - ">="
59
+ - - "<="
35
60
  - !ruby/object:Gem::Version
36
- version: 0.1.26
61
+ version: '100'
37
62
  type: :runtime
38
63
  prerelease: false
39
64
  version_requirements: !ruby/object:Gem::Requirement
40
65
  requirements:
41
- - - "~>"
66
+ - - ">"
42
67
  - !ruby/object:Gem::Version
43
68
  version: '0'
44
- - - ">="
69
+ - - "<="
45
70
  - !ruby/object:Gem::Version
46
- version: 0.1.26
71
+ version: '100'
47
72
  - !ruby/object:Gem::Dependency
48
73
  name: iso-639
49
74
  requirement: !ruby/object:Gem::Requirement
50
75
  requirements:
51
- - - "~>"
76
+ - - ">"
52
77
  - !ruby/object:Gem::Version
53
78
  version: '0'
79
+ - - "<="
80
+ - !ruby/object:Gem::Version
81
+ version: '100'
54
82
  type: :runtime
55
83
  prerelease: false
56
84
  version_requirements: !ruby/object:Gem::Requirement
57
85
  requirements:
58
- - - "~>"
86
+ - - ">"
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - - "<="
90
+ - !ruby/object:Gem::Version
91
+ version: '100'
92
+ - !ruby/object:Gem::Dependency
93
+ name: countries
94
+ requirement: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">"
59
97
  - !ruby/object:Gem::Version
60
98
  version: '0'
99
+ - - "<="
100
+ - !ruby/object:Gem::Version
101
+ version: '100'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">"
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - - "<="
110
+ - !ruby/object:Gem::Version
111
+ version: '100'
61
112
  description: ImmosquareTranslate brings the power of OpenAI to Ruby applications,
62
113
  offering the ability to translate not just YAML files, but also arrays, web pages,
63
114
  and other data structures. Tailored for developers in multilingual settings, it
@@ -81,7 +132,6 @@ homepage: https://github.com/immosquare/immosquare-translate
81
132
  licenses:
82
133
  - MIT
83
134
  metadata: {}
84
- post_install_message:
85
135
  rdoc_options: []
86
136
  require_paths:
87
137
  - lib
@@ -89,15 +139,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
139
  requirements:
90
140
  - - ">="
91
141
  - !ruby/object:Gem::Version
92
- version: 2.7.2
142
+ version: 3.2.6
93
143
  required_rubygems_version: !ruby/object:Gem::Requirement
94
144
  requirements:
95
145
  - - ">="
96
146
  - !ruby/object:Gem::Version
97
147
  version: '0'
98
148
  requirements: []
99
- rubygems_version: 3.5.21
100
- signing_key:
149
+ rubygems_version: 3.6.9
101
150
  specification_version: 4
102
151
  summary: AI-powered translations for Ruby applications, supporting a wide range of
103
152
  formats.