From fb8d7e31bff404cfe4b647f26d6ce566a5515897 Mon Sep 17 00:00:00 2001 From: aki Date: Mon, 7 Jul 2025 13:20:02 +0900 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20Rails=20i18n=E3=81=A8=E3=81=AE?= =?UTF-8?q?=E4=BA=92=E6=8F=9B=E6=80=A7=E3=81=AE=E3=81=9F=E3=82=81=E3=81=AE?= =?UTF-8?q?=E5=80=A4=E5=A4=89=E6=8F=9B=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/copy_tuner_client/dotted_hash.rb | 22 +++++- lib/copy_tuner_client/i18n_backend.rb | 7 +- spec/copy_tuner_client/dotted_hash_spec.rb | 74 +++++++++++++++++++++ spec/copy_tuner_client/i18n_backend_spec.rb | 2 +- 4 files changed, 98 insertions(+), 7 deletions(-) diff --git a/lib/copy_tuner_client/dotted_hash.rb b/lib/copy_tuner_client/dotted_hash.rb index 750c86d..cbdb85e 100644 --- a/lib/copy_tuner_client/dotted_hash.rb +++ b/lib/copy_tuner_client/dotted_hash.rb @@ -3,7 +3,9 @@ module DottedHash def to_h(dotted_hash) hash = {} dotted_hash.to_h.transform_keys(&:to_s).sort.each do |key, value| - _hash = key.split('.').reverse.inject(value) { |memo, key| { key => memo } } + # Rails i18n標準との互換性のため、特定のキーを適切な型に変換 + converted_value = convert_value_type(key, value) + _hash = key.split('.').reverse.inject(converted_value) { |memo, key| { key => memo } } hash.deep_merge!(_hash) end hash @@ -27,6 +29,22 @@ def conflict_keys(dotted_hash) results end - module_function :to_h, :conflict_keys + private + + def convert_value_type(key, value) + return value unless value.is_a?(String) + + # Rails i18n標準で数値型として扱われるキー + if key.end_with?('.precision') + value.to_i + # Rails i18n標準で真偽値として扱われるキー + elsif key.end_with?('.significant', '.strip_insignificant_zeros') + value == 'true' + else + value + end + end + + module_function :to_h, :conflict_keys, :convert_value_type end end diff --git a/lib/copy_tuner_client/i18n_backend.rb b/lib/copy_tuner_client/i18n_backend.rb index b41fa8a..326fa41 100644 --- a/lib/copy_tuner_client/i18n_backend.rb +++ b/lib/copy_tuner_client/i18n_backend.rb @@ -80,10 +80,9 @@ def lookup(locale, key, scope = [], options = {}) return exact_match end - # NOTE: 色々考慮する必要があることが分かったため暫定対応として、ツリーキャッシュを使用しないようにしている - # ensure_tree_cache_current - # tree_result = lookup_in_tree_cache(parts) - # return tree_result if tree_result + ensure_tree_cache_current + tree_result = lookup_in_tree_cache(parts) + return tree_result if tree_result content = super diff --git a/spec/copy_tuner_client/dotted_hash_spec.rb b/spec/copy_tuner_client/dotted_hash_spec.rb index c1be0f7..0f6a72a 100644 --- a/spec/copy_tuner_client/dotted_hash_spec.rb +++ b/spec/copy_tuner_client/dotted_hash_spec.rb @@ -58,6 +58,80 @@ it { is_expected.to eq({ 'en' => { 'test' => { 'key' => 'en test value' } } }) } end + + context "with Rails i18n numeric precision keys" do + let(:dotted_hash) do + { + 'en.number.currency.format.precision' => '2', + 'en.number.format.precision' => '3', + } + end + + it "converts precision values to integers" do + is_expected.to eq({ + 'en' => { + 'number' => { + 'currency' => { + 'format' => { + 'precision' => 2, + }, + }, + 'format' => { + 'precision' => 3, + }, + }, + }, + }) + end + end + + context "with Rails i18n boolean keys" do + let(:dotted_hash) do + { + 'en.number.currency.format.significant' => 'false', + 'en.number.format.strip_insignificant_zeros' => 'true', + } + end + + it "converts boolean values to actual booleans" do + is_expected.to eq({ + 'en' => { + 'number' => { + 'currency' => { + 'format' => { + 'significant' => false, + }, + }, + 'format' => { + 'strip_insignificant_zeros' => true, + }, + }, + }, + }) + end + end + + context "with non-Rails i18n keys containing similar patterns" do + let(:dotted_hash) do + { + 'en.custom.precision' => 'custom_value', + 'en.other.significant_value' => 'true', + } + end + + it "converts only keys ending with Rails i18n patterns" do + is_expected.to eq({ + 'en' => { + 'custom' => { + 'precision' => 0, # .precision suffix triggers conversion + }, + 'other' => { + 'significant_value' => 'true', # no conversion for non-exact match + }, + }, + }) + end + end end describe ".conflict_keys" do diff --git a/spec/copy_tuner_client/i18n_backend_spec.rb b/spec/copy_tuner_client/i18n_backend_spec.rb index 5f5b6d1..3eecb5e 100644 --- a/spec/copy_tuner_client/i18n_backend_spec.rb +++ b/spec/copy_tuner_client/i18n_backend_spec.rb @@ -244,7 +244,7 @@ def build_backend end # NOTE: 色々考慮する必要があることが分かったため暫定対応として、ツリーキャッシュを使用しないようにしている - xdescribe 'ツリー構造のlookup' do # rubocop:disable Metrics/BlockLength + describe 'ツリー構造のlookup' do # rubocop:disable Metrics/BlockLength subject { build_backend } context '完全一致が存在する場合' do From bcffd3999824c9bf469fea20cd603ab2498e5441 Mon Sep 17 00:00:00 2001 From: aki Date: Mon, 7 Jul 2025 14:23:59 +0900 Subject: [PATCH 2/7] style: rubocop --- lib/copy_tuner_client/dotted_hash.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/copy_tuner_client/dotted_hash.rb b/lib/copy_tuner_client/dotted_hash.rb index cbdb85e..ce9cb4b 100644 --- a/lib/copy_tuner_client/dotted_hash.rb +++ b/lib/copy_tuner_client/dotted_hash.rb @@ -5,7 +5,7 @@ def to_h(dotted_hash) dotted_hash.to_h.transform_keys(&:to_s).sort.each do |key, value| # Rails i18n標準との互換性のため、特定のキーを適切な型に変換 converted_value = convert_value_type(key, value) - _hash = key.split('.').reverse.inject(converted_value) { |memo, key| { key => memo } } + _hash = key.split('.').reverse.inject(converted_value) { |memo, _key| { _key => memo } } hash.deep_merge!(_hash) end hash @@ -18,8 +18,8 @@ def conflict_keys(dotted_hash) all_keys.each_with_index do |key, index| prefix = "#{key}." conflict_keys = ((index + 1)..Float::INFINITY) - .take_while { |i| all_keys[i]&.start_with?(prefix) } - .map { |i| all_keys[i] } + .take_while { |i| all_keys[i]&.start_with?(prefix) } + .map { |i| all_keys[i] } if conflict_keys.present? results[key] = conflict_keys From a98a67ee97abdfbd9818081b9a57341f7d51f9ae Mon Sep 17 00:00:00 2001 From: aki Date: Mon, 7 Jul 2025 14:39:50 +0900 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20Ruby=E7=94=A8=E3=81=AE=E3=83=87?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AB=E3=83=88=E3=83=95=E3=82=A9=E3=83=BC?= =?UTF-8?q?=E3=83=9E=E3=83=83=E3=82=BF=E3=81=A8=E3=81=97=E3=81=A6Rubocop?= =?UTF-8?q?=E3=82=92=E8=A8=AD=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index ee81574..3608b33 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,4 +8,7 @@ "cSpell.words": [ "mkpath" ], + "[ruby]": { + "editor.defaultFormatter": "rubocop.vscode-rubocop", + } } From a27146ac7584f92cdc75aaf2afee29975a5357bf Mon Sep 17 00:00:00 2001 From: aki Date: Mon, 7 Jul 2025 14:43:40 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor:=20convert=5Fvalue=5Ftype=E3=83=A1?= =?UTF-8?q?=E3=82=BD=E3=83=83=E3=83=89=E3=82=92=E3=83=97=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=99=E3=83=BC=E3=83=88=E3=81=AB=E5=A4=89=E6=9B=B4=E3=81=97?= =?UTF-8?q?=E3=80=81module=5Ffunction=E3=81=AE=E5=AE=9A=E7=BE=A9=E3=82=92?= =?UTF-8?q?=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/copy_tuner_client/dotted_hash.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/copy_tuner_client/dotted_hash.rb b/lib/copy_tuner_client/dotted_hash.rb index ce9cb4b..3137e41 100644 --- a/lib/copy_tuner_client/dotted_hash.rb +++ b/lib/copy_tuner_client/dotted_hash.rb @@ -29,6 +29,8 @@ def conflict_keys(dotted_hash) results end + module_function :to_h, :conflict_keys # rubocop:disable Style/AccessModifierDeclarations + private def convert_value_type(key, value) @@ -44,7 +46,5 @@ def convert_value_type(key, value) value end end - - module_function :to_h, :conflict_keys, :convert_value_type end end From c3e7e2495ec4520a92380dced8b046513fdfd9f6 Mon Sep 17 00:00:00 2001 From: aki Date: Mon, 7 Jul 2025 14:44:03 +0900 Subject: [PATCH 5/7] =?UTF-8?q?fix:=20=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E5=90=8D?= =?UTF-8?q?=E3=82=92=E6=97=A5=E6=9C=AC=E8=AA=9E=E3=81=AB=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=97=E3=80=81=E8=AA=AC=E6=98=8E=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/copy_tuner_client/dotted_hash_spec.rb | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/spec/copy_tuner_client/dotted_hash_spec.rb b/spec/copy_tuner_client/dotted_hash_spec.rb index 0f6a72a..25be377 100644 --- a/spec/copy_tuner_client/dotted_hash_spec.rb +++ b/spec/copy_tuner_client/dotted_hash_spec.rb @@ -4,25 +4,25 @@ describe ".to_h" do subject { CopyTunerClient::DottedHash.to_h(dotted_hash) } - context 'empty keys' do + context '空のキーの場合' do let(:dotted_hash) { {} } it { is_expected.to eq({}) } end - context 'with single-level keys' do + context '1階層のキーの場合' do let(:dotted_hash) { { 'key' => 'test value', other_key: 'other value' } } it { is_expected.to eq({ 'key' => 'test value', 'other_key' => 'other value' }) } end - context 'array of key value pairs' do + context 'キーと値の配列の場合' do let(:dotted_hash) { [['key', 'test value'], ['other_key', 'other value']] } it { is_expected.to eq({ 'key' => 'test value', 'other_key' => 'other value' }) } end - context "with multi-level blurb keys" do + context "複数階層のblurbキーの場合" do let(:dotted_hash) do { 'en.test.key' => 'en test value', @@ -31,7 +31,7 @@ } end - it do + it "正しくネストされたハッシュに変換されること" do is_expected.to eq({ 'en' => { 'test' => { @@ -48,7 +48,7 @@ end end - context "with conflicting keys" do + context "キーの競合がある場合" do let(:dotted_hash) do { 'en.test' => 'invalid value', @@ -59,7 +59,7 @@ it { is_expected.to eq({ 'en' => { 'test' => { 'key' => 'en test value' } } }) } end - context "with Rails i18n numeric precision keys" do + context "Rails i18nの数値precisionキーの場合" do let(:dotted_hash) do { 'en.number.currency.format.precision' => '2', @@ -67,7 +67,7 @@ } end - it "converts precision values to integers" do + it "precision値を整数に変換する" do is_expected.to eq({ 'en' => { 'number' => { @@ -85,7 +85,7 @@ end end - context "with Rails i18n boolean keys" do + context "Rails i18nのbooleanキーの場合" do let(:dotted_hash) do { 'en.number.currency.format.significant' => 'false', @@ -93,7 +93,7 @@ } end - it "converts boolean values to actual booleans" do + it "boolean値を実際の真偽値に変換する" do is_expected.to eq({ 'en' => { 'number' => { @@ -111,7 +111,7 @@ end end - context "with non-Rails i18n keys containing similar patterns" do + context "Rails i18n以外で似たパターンを含むキーの場合" do let(:dotted_hash) do { 'en.custom.precision' => 'custom_value', @@ -119,7 +119,7 @@ } end - it "converts only keys ending with Rails i18n patterns" do + it "Rails i18nパターンで終わるキーのみ変換する" do is_expected.to eq({ 'en' => { 'custom' => { @@ -137,7 +137,7 @@ describe ".conflict_keys" do subject { CopyTunerClient::DottedHash.conflict_keys(dotted_hash) } - context 'valid keys' do + context '有効なキーの場合' do let(:dotted_hash) do { 'ja.hoge.test' => 'test', @@ -148,7 +148,7 @@ it { is_expected.to eq({}) } end - context 'invalid keys' do + context '無効なキーの場合' do let(:dotted_hash) do { 'ja.hoge.test' => 'test', @@ -159,7 +159,7 @@ } end - it do + it "競合するキーが正しく検出されること" do is_expected.to eq({ 'ja.fuga.test' => %w[ja.fuga.test.hoge], 'ja.hoge.test' => %w[ja.hoge.test.fuga ja.hoge.test.hoge], From a2da25abb9fdeefa00b6b212bc7c98b43834776f Mon Sep 17 00:00:00 2001 From: aki Date: Mon, 7 Jul 2025 14:47:33 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20module=5Ffunction=E3=81=AE=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E3=81=8C=E3=81=82=E3=81=A3=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/copy_tuner_client/dotted_hash.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/copy_tuner_client/dotted_hash.rb b/lib/copy_tuner_client/dotted_hash.rb index 3137e41..295af68 100644 --- a/lib/copy_tuner_client/dotted_hash.rb +++ b/lib/copy_tuner_client/dotted_hash.rb @@ -29,8 +29,6 @@ def conflict_keys(dotted_hash) results end - module_function :to_h, :conflict_keys # rubocop:disable Style/AccessModifierDeclarations - private def convert_value_type(key, value) @@ -46,5 +44,7 @@ def convert_value_type(key, value) value end end + + module_function :to_h, :conflict_keys, :convert_value_type # rubocop:disable Style/AccessModifierDeclarations end end From fa9789d2eb9b9ac0935fdaeea4fe0c434f94ed86 Mon Sep 17 00:00:00 2001 From: aki Date: Mon, 7 Jul 2025 16:21:13 +0900 Subject: [PATCH 7/7] bump --- lib/copy_tuner_client/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/copy_tuner_client/version.rb b/lib/copy_tuner_client/version.rb index 413fb61..7f433cf 100644 --- a/lib/copy_tuner_client/version.rb +++ b/lib/copy_tuner_client/version.rb @@ -1,6 +1,6 @@ module CopyTunerClient # Client version - VERSION = '1.1.2'.freeze + VERSION = '1.1.3'.freeze # API version being used to communicate with the server API_VERSION = '2.0'.freeze