i18n-tasks 1.0.14 → 1.0.15
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/README.md +65 -38
- data/config/locales/en.yml +11 -1
- data/config/locales/ru.yml +11 -1
- data/i18n-tasks.gemspec +4 -1
- data/lib/i18n/tasks/command/commands/missing.rb +3 -1
- data/lib/i18n/tasks/command/option_parsers/enum.rb +4 -3
- data/lib/i18n/tasks/command/options/locales.rb +12 -3
- data/lib/i18n/tasks/configuration.rb +7 -2
- data/lib/i18n/tasks/scanners/ast_matchers/rails_model_matcher.rb +1 -1
- data/lib/i18n/tasks/scanners/local_ruby_parser.rb +2 -2
- data/lib/i18n/tasks/scanners/pattern_scanner.rb +1 -1
- data/lib/i18n/tasks/scanners/prism_scanner.rb +83 -0
- data/lib/i18n/tasks/scanners/prism_scanners/arguments_visitor.rb +41 -0
- data/lib/i18n/tasks/scanners/prism_scanners/nodes.rb +334 -0
- data/lib/i18n/tasks/scanners/prism_scanners/visitor.rb +273 -0
- data/lib/i18n/tasks/scanners/ruby_ast_scanner.rb +3 -3
- data/lib/i18n/tasks/scanners/ruby_key_literals.rb +1 -1
- data/lib/i18n/tasks/scanners/ruby_parser_factory.rb +27 -0
- data/lib/i18n/tasks/translation.rb +4 -1
- data/lib/i18n/tasks/translators/base_translator.rb +5 -1
- data/lib/i18n/tasks/translators/deepl_translator.rb +5 -0
- data/lib/i18n/tasks/translators/google_translator.rb +12 -4
- data/lib/i18n/tasks/translators/openai_translator.rb +34 -20
- data/lib/i18n/tasks/translators/watsonx_translator.rb +155 -0
- data/lib/i18n/tasks/translators/yandex_translator.rb +5 -1
- data/lib/i18n/tasks/used_keys.rb +3 -2
- data/lib/i18n/tasks/version.rb +1 -1
- data/lib/i18n/tasks.rb +1 -0
- data/templates/config/i18n-tasks.yml +2 -2
- metadata +31 -5
@@ -35,6 +35,8 @@ module I18n::Tasks::Translators
|
|
35
35
|
else
|
36
36
|
results += res.map(&:text)
|
37
37
|
end
|
38
|
+
|
39
|
+
@progress_bar.progress += parts.size
|
38
40
|
end
|
39
41
|
results
|
40
42
|
end
|
@@ -83,6 +85,9 @@ module I18n::Tasks::Translators
|
|
83
85
|
|
84
86
|
# Convert 'es-ES' to 'ES' but warn about locales requiring a specific variant
|
85
87
|
def to_deepl_target_locale(locale)
|
88
|
+
locale_aliases = @i18n_tasks.translation_config[:deepl_locale_aliases]
|
89
|
+
locale = locale_aliases[locale.to_s.downcase] || locale if locale_aliases.is_a?(Hash)
|
90
|
+
|
86
91
|
loc, sub = locale.to_s.split('-')
|
87
92
|
if SPECIFIC_TARGETS.include?(loc)
|
88
93
|
# Must see how the deepl api evolves, so this could be an error in the future
|
@@ -17,14 +17,18 @@ module I18n::Tasks::Translators
|
|
17
17
|
protected
|
18
18
|
|
19
19
|
def translate_values(list, **options)
|
20
|
-
restore_newlines(
|
20
|
+
result = restore_newlines(
|
21
21
|
EasyTranslate.translate(
|
22
22
|
replace_newlines_with_placeholder(list, options[:html]),
|
23
23
|
options,
|
24
|
-
format: :text
|
24
|
+
format: options[:html] ? :html : :text
|
25
25
|
),
|
26
26
|
options[:html]
|
27
27
|
)
|
28
|
+
|
29
|
+
@progress_bar.progress += result.size
|
30
|
+
|
31
|
+
result
|
28
32
|
end
|
29
33
|
|
30
34
|
def options_for_translate_values(from:, to:, **options)
|
@@ -69,7 +73,9 @@ module I18n::Tasks::Translators
|
|
69
73
|
return list unless html
|
70
74
|
|
71
75
|
list.map do |value|
|
72
|
-
value.gsub(
|
76
|
+
value.gsub(/\n(\s*)/) do
|
77
|
+
"<Z__#{::Regexp.last_match(1)&.length || 0}>"
|
78
|
+
end
|
73
79
|
end
|
74
80
|
end
|
75
81
|
|
@@ -77,7 +83,9 @@ module I18n::Tasks::Translators
|
|
77
83
|
return translations unless html
|
78
84
|
|
79
85
|
translations.map do |translation|
|
80
|
-
translation.gsub(
|
86
|
+
translation.gsub(/<Z__(\d+)>/) do
|
87
|
+
"\n#{' ' * ::Regexp.last_match(1).to_i}"
|
88
|
+
end
|
81
89
|
end
|
82
90
|
end
|
83
91
|
end
|
@@ -18,6 +18,10 @@ module I18n::Tasks::Translators
|
|
18
18
|
Variables (starting with %%{ and ending with }) must not be changed under any circumstance.
|
19
19
|
|
20
20
|
Keep in mind the context of all the strings for a more accurate translation.
|
21
|
+
It is CRITICAL you output only the result, without any additional information, code block syntax or comments.
|
22
|
+
PROMPT
|
23
|
+
JSON_FORMAT_INSTRUCTIONS_SYSTEM_PROMPT = <<~PROMPT.squish
|
24
|
+
Return the translations as a JSON object with a 'translations' array containing the translated strings.
|
21
25
|
PROMPT
|
22
26
|
|
23
27
|
def initialize(*)
|
@@ -51,7 +55,7 @@ module I18n::Tasks::Translators
|
|
51
55
|
private
|
52
56
|
|
53
57
|
def translator
|
54
|
-
@translator ||= OpenAI::Client.new(access_token: api_key)
|
58
|
+
@translator ||= OpenAI::Client.new(access_token: api_key, log_errors: true)
|
55
59
|
end
|
56
60
|
|
57
61
|
def api_key
|
@@ -64,11 +68,14 @@ module I18n::Tasks::Translators
|
|
64
68
|
end
|
65
69
|
|
66
70
|
def model
|
67
|
-
@model ||= @i18n_tasks.translation_config[:openai_model].presence || 'gpt-
|
71
|
+
@model ||= @i18n_tasks.translation_config[:openai_model].presence || 'gpt-4o-mini'
|
68
72
|
end
|
69
73
|
|
70
74
|
def system_prompt
|
71
|
-
@system_prompt ||=
|
75
|
+
@system_prompt ||=
|
76
|
+
(@i18n_tasks.translation_config[:openai_system_prompt].presence || DEFAULT_SYSTEM_PROMPT)
|
77
|
+
.concat("\n#{JSON_FORMAT_INSTRUCTIONS_SYSTEM_PROMPT}")
|
78
|
+
@system_prompt
|
72
79
|
end
|
73
80
|
|
74
81
|
def translate_values(list, from:, to:)
|
@@ -76,15 +83,37 @@ module I18n::Tasks::Translators
|
|
76
83
|
|
77
84
|
list.each_slice(BATCH_SIZE) do |batch|
|
78
85
|
translations = translate(batch, from, to)
|
86
|
+
result = JSON.parse(translations)
|
87
|
+
results << result
|
79
88
|
|
80
|
-
|
89
|
+
@progress_bar.progress += result.size
|
81
90
|
end
|
82
91
|
|
83
92
|
results.flatten
|
84
93
|
end
|
85
94
|
|
86
95
|
def translate(values, from, to)
|
87
|
-
|
96
|
+
response = translator.chat(
|
97
|
+
parameters: {
|
98
|
+
model: model,
|
99
|
+
messages: build_messages(values, from, to),
|
100
|
+
temperature: 0.0,
|
101
|
+
response_format: { type: 'json_object' }
|
102
|
+
}
|
103
|
+
)
|
104
|
+
|
105
|
+
translations = response.dig('choices', 0, 'message', 'content')
|
106
|
+
error = response['error']
|
107
|
+
|
108
|
+
fail "AI error: #{error}" if error.present?
|
109
|
+
|
110
|
+
# Extract the array from the JSON object response
|
111
|
+
result = JSON.parse(translations)
|
112
|
+
result['translations'].to_json
|
113
|
+
end
|
114
|
+
|
115
|
+
def build_messages(values, from, to)
|
116
|
+
[
|
88
117
|
{
|
89
118
|
role: 'system',
|
90
119
|
content: format(system_prompt, from: from, to: to)
|
@@ -98,21 +127,6 @@ module I18n::Tasks::Translators
|
|
98
127
|
content: values.to_json
|
99
128
|
}
|
100
129
|
]
|
101
|
-
|
102
|
-
response = translator.chat(
|
103
|
-
parameters: {
|
104
|
-
model: model,
|
105
|
-
messages: messages,
|
106
|
-
temperature: 0.0
|
107
|
-
}
|
108
|
-
)
|
109
|
-
|
110
|
-
translations = response.dig('choices', 0, 'message', 'content')
|
111
|
-
error = response['error']
|
112
|
-
|
113
|
-
fail "AI error: #{error}" if error.present?
|
114
|
-
|
115
|
-
translations
|
116
130
|
end
|
117
131
|
end
|
118
132
|
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'i18n/tasks/translators/base_translator'
|
4
|
+
require 'active_support/core_ext/string/filters'
|
5
|
+
|
6
|
+
module I18n::Tasks::Translators
|
7
|
+
class WatsonxTranslator < BaseTranslator
|
8
|
+
# max allowed texts per request
|
9
|
+
BATCH_SIZE = 50
|
10
|
+
DEFAULT_SYSTEM_PROMPT = <<~PROMPT.squish
|
11
|
+
You are a helpful assistant that translates content from the %{from} locale
|
12
|
+
to the %{to} locale in an i18n locale array.
|
13
|
+
You always preserve the structure and formatting exactly as it is.
|
14
|
+
|
15
|
+
The array has a structured format and contains multiple strings. Your task is to translate
|
16
|
+
each of these strings and create a new array with the translated strings.
|
17
|
+
|
18
|
+
Reminder:
|
19
|
+
- Translate only the text, preserving the structure and formatting.
|
20
|
+
- Do not translate any URLs.
|
21
|
+
- Do not translate HTML tags like `<details>` and `<summary>`.
|
22
|
+
- HTML markups (enclosed in < and > characters) must not be changed under any circumstance.
|
23
|
+
- Variables (starting with %%{ and ending with }) must not be changed under any circumstance.
|
24
|
+
- Output only the result, without any additional information or comments.
|
25
|
+
PROMPT
|
26
|
+
|
27
|
+
def options_for_translate_values(from:, to:, **options)
|
28
|
+
options.merge(
|
29
|
+
from: from,
|
30
|
+
to: to
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def options_for_html
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
|
38
|
+
def options_for_plain
|
39
|
+
{}
|
40
|
+
end
|
41
|
+
|
42
|
+
def no_results_error_message
|
43
|
+
I18n.t('i18n_tasks.watsonx_translate.errors.no_results')
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def translator
|
49
|
+
@translator ||= WatsonxClient.new(key: api_key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def api_key
|
53
|
+
@api_key ||= begin
|
54
|
+
key = @i18n_tasks.translation_config[:watsonx_api_key]
|
55
|
+
fail ::I18n::Tasks::CommandError, I18n.t('i18n_tasks.watsonx_translate.errors.no_api_key') if key.blank?
|
56
|
+
|
57
|
+
key
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def project_id
|
62
|
+
@project_id ||= begin
|
63
|
+
project_id = @i18n_tasks.translation_config[:watsonx_project_id]
|
64
|
+
if project_id.blank?
|
65
|
+
fail ::I18n::Tasks::CommandError,
|
66
|
+
I18n.t('i18n_tasks.watsonx_translate.errors.no_project_id')
|
67
|
+
end
|
68
|
+
|
69
|
+
project_id
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def model
|
74
|
+
@model ||= @i18n_tasks.translation_config[:watsonx_model].presence || 'meta-llama/llama-3-2-90b-vision-instruct'
|
75
|
+
end
|
76
|
+
|
77
|
+
def system_prompt
|
78
|
+
@system_prompt ||= @i18n_tasks.translation_config[:watsonx_system_prompt].presence || DEFAULT_SYSTEM_PROMPT
|
79
|
+
end
|
80
|
+
|
81
|
+
def translate_values(list, from:, to:)
|
82
|
+
results = []
|
83
|
+
|
84
|
+
list.each_slice(BATCH_SIZE) do |batch|
|
85
|
+
translations = translate(batch, from, to)
|
86
|
+
result = JSON.parse(translations)
|
87
|
+
results << result
|
88
|
+
|
89
|
+
@progress_bar.progress += results.size
|
90
|
+
end
|
91
|
+
|
92
|
+
results.flatten
|
93
|
+
end
|
94
|
+
|
95
|
+
def translate(values, from, to)
|
96
|
+
prompt = [
|
97
|
+
'<|eot_id|><|start_header_id|>system<|end_header_id|>',
|
98
|
+
format(system_prompt, from: from, to: to),
|
99
|
+
'<|eot_id|><|start_header_id|>user<|end_header_id|>Translate this array:',
|
100
|
+
"<|eot_id|><|start_header_id|>user<|end_header_id|>#{values.to_json}",
|
101
|
+
'<|eot_id|><|start_header_id|>assistant<|end_header_id|>'
|
102
|
+
].join
|
103
|
+
|
104
|
+
response = translator.generate_text(
|
105
|
+
model_id: model,
|
106
|
+
project_id: project_id,
|
107
|
+
input: prompt,
|
108
|
+
parameters: {
|
109
|
+
decoding_method: :greedy,
|
110
|
+
max_new_tokens: 2048,
|
111
|
+
repetition_penalty: 1
|
112
|
+
}
|
113
|
+
)
|
114
|
+
response.dig('results', 0, 'generated_text')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class WatsonxClient
|
120
|
+
WATSONX_BASE_URL = 'https://us-south.ml.cloud.ibm.com/ml/'
|
121
|
+
IBM_CLOUD_IAM_URL = 'https://iam.cloud.ibm.com/identity/token'
|
122
|
+
|
123
|
+
def initialize(key:)
|
124
|
+
begin
|
125
|
+
require 'faraday'
|
126
|
+
rescue LoadError
|
127
|
+
raise ::I18n::Tasks::CommandError, "Add gem 'faraday' to your Gemfile to use this command"
|
128
|
+
end
|
129
|
+
|
130
|
+
@http = Faraday.new(url: WATSONX_BASE_URL) do |conn|
|
131
|
+
conn.use Faraday::Response::RaiseError
|
132
|
+
conn.request :json
|
133
|
+
conn.response :json
|
134
|
+
conn.options.timeout = 600
|
135
|
+
conn.request :authorization, :Bearer, token(key)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def generate_text(**opts)
|
140
|
+
@http.post('v1/text/generation?version=2024-05-20', **opts).body
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def token(key)
|
146
|
+
Faraday.new(url: IBM_CLOUD_IAM_URL) do |conn|
|
147
|
+
conn.use Faraday::Response::RaiseError
|
148
|
+
conn.response :json
|
149
|
+
conn.params = {
|
150
|
+
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
|
151
|
+
apikey: key
|
152
|
+
}
|
153
|
+
end.post.body['access_token']
|
154
|
+
end
|
155
|
+
end
|
@@ -16,7 +16,11 @@ module I18n::Tasks::Translators
|
|
16
16
|
protected
|
17
17
|
|
18
18
|
def translate_values(list, **options)
|
19
|
-
list.map { |item| translator.translate(item, options) }
|
19
|
+
result = list.map { |item| translator.translate(item, options) }
|
20
|
+
|
21
|
+
@progress_bar.progress += result.size
|
22
|
+
|
23
|
+
result
|
20
24
|
end
|
21
25
|
|
22
26
|
def options_for_translate_values(from:, to:, **options)
|
data/lib/i18n/tasks/used_keys.rb
CHANGED
@@ -4,6 +4,7 @@ require 'find'
|
|
4
4
|
require 'i18n/tasks/scanners/pattern_with_scope_scanner'
|
5
5
|
require 'i18n/tasks/scanners/ruby_ast_scanner'
|
6
6
|
require 'i18n/tasks/scanners/erb_ast_scanner'
|
7
|
+
require 'i18n/tasks/scanners/prism_scanner'
|
7
8
|
require 'i18n/tasks/scanners/scanner_multiplexer'
|
8
9
|
require 'i18n/tasks/scanners/files/caching_file_finder_provider'
|
9
10
|
require 'i18n/tasks/scanners/files/caching_file_reader'
|
@@ -21,8 +22,8 @@ module I18n::Tasks
|
|
21
22
|
relative_roots: %w[app/controllers app/helpers app/mailers app/presenters app/views].freeze,
|
22
23
|
scanners: [
|
23
24
|
['::I18n::Tasks::Scanners::RubyAstScanner', { only: %w[*.rb] }],
|
24
|
-
['::I18n::Tasks::Scanners::ErbAstScanner', { only: %w[*.
|
25
|
-
['::I18n::Tasks::Scanners::PatternWithScopeScanner', { exclude: %w[*.
|
25
|
+
['::I18n::Tasks::Scanners::ErbAstScanner', { only: %w[*.erb] }],
|
26
|
+
['::I18n::Tasks::Scanners::PatternWithScopeScanner', { exclude: %w[*.erb *.rb] }]
|
26
27
|
],
|
27
28
|
ast_matchers: [],
|
28
29
|
strict: true
|
data/lib/i18n/tasks/version.rb
CHANGED
data/lib/i18n/tasks.rb
CHANGED
@@ -34,7 +34,7 @@ data:
|
|
34
34
|
## Example (replace %#= with %=):
|
35
35
|
# - "<%#= %x[bundle info vagrant --path].chomp %>/templates/locales/%{locale}.yml"
|
36
36
|
|
37
|
-
## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, or a custom class.
|
37
|
+
## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, isolating_router, or a custom class.
|
38
38
|
# router: conservative_router
|
39
39
|
|
40
40
|
yaml:
|
@@ -122,7 +122,7 @@ search:
|
|
122
122
|
# formality: prefer_less
|
123
123
|
# # OpenAI
|
124
124
|
# openai_api_key: "sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
125
|
-
# # openai_model: "gpt-
|
125
|
+
# # openai_model: "gpt-4o-mini" # see https://platform.openai.com/docs/models
|
126
126
|
# # may contain `%{from}` and `%{to}`, which will be replaced by source and target locale codes, respectively (using `Kernel.format`)
|
127
127
|
# # openai_system_prompt: >-
|
128
128
|
# # You are a professional translator that translates content from the %{from} locale
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i18n-tasks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- glebm
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-03-07 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activesupport
|
@@ -128,6 +127,26 @@ dependencies:
|
|
128
127
|
- - "<"
|
129
128
|
- !ruby/object:Gem::Version
|
130
129
|
version: '4.0'
|
130
|
+
- !ruby/object:Gem::Dependency
|
131
|
+
name: ruby-progressbar
|
132
|
+
requirement: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '1.8'
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 1.8.1
|
140
|
+
type: :runtime
|
141
|
+
prerelease: false
|
142
|
+
version_requirements: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - "~>"
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '1.8'
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.8.1
|
131
150
|
- !ruby/object:Gem::Dependency
|
132
151
|
name: terminal-table
|
133
152
|
requirement: !ruby/object:Gem::Requirement
|
@@ -402,12 +421,17 @@ files:
|
|
402
421
|
- lib/i18n/tasks/scanners/pattern_mapper.rb
|
403
422
|
- lib/i18n/tasks/scanners/pattern_scanner.rb
|
404
423
|
- lib/i18n/tasks/scanners/pattern_with_scope_scanner.rb
|
424
|
+
- lib/i18n/tasks/scanners/prism_scanner.rb
|
425
|
+
- lib/i18n/tasks/scanners/prism_scanners/arguments_visitor.rb
|
426
|
+
- lib/i18n/tasks/scanners/prism_scanners/nodes.rb
|
427
|
+
- lib/i18n/tasks/scanners/prism_scanners/visitor.rb
|
405
428
|
- lib/i18n/tasks/scanners/relative_keys.rb
|
406
429
|
- lib/i18n/tasks/scanners/results/key_occurrences.rb
|
407
430
|
- lib/i18n/tasks/scanners/results/occurrence.rb
|
408
431
|
- lib/i18n/tasks/scanners/ruby_ast_call_finder.rb
|
409
432
|
- lib/i18n/tasks/scanners/ruby_ast_scanner.rb
|
410
433
|
- lib/i18n/tasks/scanners/ruby_key_literals.rb
|
434
|
+
- lib/i18n/tasks/scanners/ruby_parser_factory.rb
|
411
435
|
- lib/i18n/tasks/scanners/scanner.rb
|
412
436
|
- lib/i18n/tasks/scanners/scanner_multiplexer.rb
|
413
437
|
- lib/i18n/tasks/split_key.rb
|
@@ -418,6 +442,7 @@ files:
|
|
418
442
|
- lib/i18n/tasks/translators/deepl_translator.rb
|
419
443
|
- lib/i18n/tasks/translators/google_translator.rb
|
420
444
|
- lib/i18n/tasks/translators/openai_translator.rb
|
445
|
+
- lib/i18n/tasks/translators/watsonx_translator.rb
|
421
446
|
- lib/i18n/tasks/translators/yandex_translator.rb
|
422
447
|
- lib/i18n/tasks/unused_keys.rb
|
423
448
|
- lib/i18n/tasks/used_keys.rb
|
@@ -429,8 +454,10 @@ homepage: https://github.com/glebm/i18n-tasks
|
|
429
454
|
licenses:
|
430
455
|
- MIT
|
431
456
|
metadata:
|
457
|
+
changelog_uri: https://github.com/glebm/i18n-tasks/blob/main/CHANGES.md
|
432
458
|
issue_tracker: https://github.com/glebm/i18n-tasks
|
433
459
|
rubygems_mfa_required: 'true'
|
460
|
+
source_code_uri: https://github.com/glebm/i18n-tasks
|
434
461
|
post_install_message: |
|
435
462
|
# Install default configuration:
|
436
463
|
cp $(bundle exec i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/
|
@@ -455,8 +482,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
455
482
|
- !ruby/object:Gem::Version
|
456
483
|
version: '0'
|
457
484
|
requirements: []
|
458
|
-
rubygems_version: 3.
|
459
|
-
signing_key:
|
485
|
+
rubygems_version: 3.6.3
|
460
486
|
specification_version: 4
|
461
487
|
summary: Manage localization and translation with the awesome power of static analysis
|
462
488
|
test_files: []
|