Skip to content

Commit 1846d73

Browse files
committed
Fix: force translations
1 parent 1548296 commit 1846d73

5 files changed

Lines changed: 57 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# webgate.pro
22

33
[![CI](https://github.com/WebgateSystems/webgate.pro/actions/workflows/rubyonrails.yml/badge.svg)](https://github.com/WebgateSystems/webgate.pro/actions/workflows/rubyonrails.yml)
4-
![Coverage](https://img.shields.io/badge/coverage-95.3%25-brightgreen)
4+
![Coverage](https://img.shields.io/badge/coverage-95.1%25-brightgreen)
55
[![Ruby](https://img.shields.io/badge/Ruby-3.2.2-CC342D?logo=ruby&logoColor=white)](https://www.ruby-lang.org/)
66
[![Rails](https://img.shields.io/badge/Rails-7.0.10-D30001?logo=rubyonrails&logoColor=white)](https://rubyonrails.org/)
77
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)

app/interactors/base_interactor.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,29 @@
11
class BaseInteractor
22
include Interactor
3+
4+
private
5+
6+
# Some GPT clients return Hashes with symbol keys; normalize everything to string keys.
7+
# Also accepts objects responding to `to_h`.
8+
def normalize_answer_hash(answer)
9+
hash = if answer.is_a?(Hash)
10+
answer
11+
else
12+
(answer.respond_to?(:to_h) ? answer.to_h : {})
13+
end
14+
deep_stringify_keys(hash)
15+
end
16+
17+
def deep_stringify_keys(obj)
18+
case obj
19+
when Hash
20+
obj.each_with_object({}) do |(k, v), acc|
21+
acc[k.to_s] = deep_stringify_keys(v)
22+
end
23+
when Array
24+
obj.map { |v| deep_stringify_keys(v) }
25+
else
26+
obj
27+
end
28+
end
329
end

app/interactors/member_translation.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,8 @@ def handle_field_retry(field, target_locale, retries, error)
365365

366366
def save_translation(target_locale, answer_gpt)
367367
I18n.with_locale(target_locale) do
368-
return unless answer_gpt.is_a?(Hash)
368+
answer_gpt = normalize_answer_hash(answer_gpt)
369+
return unless answer_gpt.is_a?(Hash) && answer_gpt.any?
369370

370371
assign_translation_fields(answer_gpt)
371372
save_and_verify_translation(target_locale)

app/interactors/project_translation.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ def log_chunk_failure(target_locale, index, error)
292292

293293
def save_translation(target_locale, answer_gpt)
294294
I18n.with_locale(target_locale) do
295+
answer_gpt = normalize_answer_hash(answer_gpt)
295296
return unless answer_gpt.is_a?(Hash) && answer_gpt['content'].present?
296297

297298
save_content_translation(target_locale, answer_gpt)

spec/interactors/member_translation_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@
1717
}
1818
end
1919

20+
let(:mock_translation_response_symbol_keys) do
21+
{
22+
name: 'John Doe',
23+
job_title: 'Developer',
24+
description: 'Description in English',
25+
motto: 'Motto in English',
26+
education: 'Education in English'
27+
}
28+
end
29+
2030
before do
2131
# Mock EasyAccessGpt::Translation::SingleLocale
2232
# Use allow_any_instance_of for more reliable mocking in tests
@@ -104,6 +114,23 @@
104114
end
105115
end
106116

117+
context 'when translation API returns symbol keys' do
118+
before do
119+
allow_any_instance_of(EasyAccessGpt::Translation::SingleLocale)
120+
.to receive(:call).and_return(mock_translation_response_symbol_keys)
121+
end
122+
123+
it 'persists translated fields (does not fallback to Polish)' do
124+
result = described_class.call(model: member, current_locale:, force_locale: :de)
125+
expect(result).to be_success
126+
127+
member.reload
128+
de_translation = member.translations.find_by(locale: 'de')
129+
expect(de_translation).to be_present
130+
expect(de_translation.name).to eq('John Doe')
131+
end
132+
end
133+
107134
context 'when force_locale is provided' do
108135
it 'translates only the specified locale' do
109136
result = described_class.call(

0 commit comments

Comments
 (0)