Skip to content

Commit 5bbfb7c

Browse files
iHiDdem4ron
authored andcommitted
Load locales into db each time
1 parent 88e77f3 commit 5bbfb7c

File tree

9 files changed

+4576
-2
lines changed

9 files changed

+4576
-2
lines changed

AGENTS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ rm -rf .built-assets/ # Clear asset cache if needed
100100
- `app/controllers/spi/` - Internal service endpoints
101101
- `test/` - All test files
102102
- `docs/context/` - Detailed component documentation
103+
- `scripts/llm/` - Helper scripts created by AI agents
104+
105+
**Script Organization:**
106+
107+
- **Always create new scripts in `scripts/llm/`** - This keeps AI-generated scripts organized and separate from core application scripts
108+
- Use descriptive names like `merge_locales.rb` or `fix_data.rb`
109+
- Include comments explaining what the script does and when to use it
103110

104111
**When editing:**
105112

config/locale.yaml

Lines changed: 4404 additions & 0 deletions
Large diffs are not rendered by default.

db/schema.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[7.1].define(version: 2025_08_19_141635) do
13+
ActiveRecord::Schema[7.1].define(version: 2025_08_28_143607) do
1414
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
1515
t.string "name", null: false
1616
t.string "record_type", null: false
@@ -2029,4 +2029,4 @@
20292029
add_foreign_key "user_track_viewed_exercise_approaches", "users"
20302030
add_foreign_key "user_tracks", "tracks"
20312031
add_foreign_key "user_tracks", "users"
2032-
end
2032+
end

docs/context/overview.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ Exercism Website is a comprehensive Ruby on Rails application with React/TypeScr
44

55
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.
66

7+
## Internationalization (i18n) System
8+
9+
The application uses a custom database-backed i18n system instead of standard Rails YAML files:
10+
11+
- **Database Storage**: Translations are stored in `Localization::Translation` and `Localization::Original` models
12+
- **Custom Backend**: `config/initializers/i18n.rb` defines `I18n::Backend::Exercism` that loads translations from the database
13+
- **Translation Loading**: The backend loads all translations via `Localization::Translation.pluck(:locale, :key, :value)` and builds nested hashes
14+
- **Key Structure**: Uses dot-notation keys (e.g., `"tracks.index.header.title"`) that get converted to nested hashes
15+
- **Loading Process**: During tests, use `LoadLocaleIntoDB.call` to import locale data from `config/locale.yaml` into the database
16+
- **Locale Files**: Source files are in `config/locales.bk/` and get merged into `config/locale.yaml` with "en:" prefix removed
17+
718
## Documentation
819

920
There is a subdirectory called `docs/context` which contains detailed information that an LLM might find useful on different areas of the application. When working with specific components, always reference the RELEVANT docs in that directory first:

scripts/llm/merge_locales.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require 'yaml'
5+
6+
# Create merged locale.yaml from locales.bk directory
7+
# This script properly merges all YAML files and removes the "en" prefix
8+
9+
OUTPUT_FILE = "config/locale.yaml"
10+
LOCALES_DIR = "config/locales.bk"
11+
12+
puts "Merging locale files from #{LOCALES_DIR} into #{OUTPUT_FILE}"
13+
14+
# Collect all YAML content
15+
merged_data = {}
16+
17+
# Process all .yml files in locales.bk directory
18+
Dir.glob("#{LOCALES_DIR}/**/*.yml").each do |file|
19+
puts "Processing #{file}"
20+
21+
begin
22+
data = YAML.load_file(file) || {}
23+
en_data = data['en'] || {}
24+
25+
# Deep merge the en_data into merged_data
26+
def deep_merge!(target, source)
27+
source.each do |key, value|
28+
if target[key].is_a?(Hash) && value.is_a?(Hash)
29+
deep_merge!(target[key], value)
30+
else
31+
target[key] = value
32+
end
33+
end
34+
target
35+
end
36+
37+
deep_merge!(merged_data, en_data)
38+
rescue => e
39+
puts "Error processing #{file}: #{e}"
40+
next
41+
end
42+
end
43+
44+
# Write the merged data to the output file
45+
File.write(OUTPUT_FILE, YAML.dump(merged_data))
46+
47+
puts "Merge complete. Output written to #{OUTPUT_FILE}"
48+
puts "File size: #{File.readlines(OUTPUT_FILE).count} lines"
49+
puts "Keys found: #{merged_data.keys.count}"

scripts/llm/merge_locales.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
3+
# Create merged locale.yaml from locales.bk directory
4+
# This script merges all YAML files and removes the "en" prefix
5+
6+
OUTPUT_FILE="config/locale.yaml"
7+
LOCALES_DIR="config/locales.bk"
8+
9+
# Remove existing output file
10+
rm -f "$OUTPUT_FILE"
11+
12+
echo "Merging locale files from $LOCALES_DIR into $OUTPUT_FILE"
13+
14+
# Process all .yml files in locales.bk directory
15+
find "$LOCALES_DIR" -name "*.yml" -type f | while read -r file; do
16+
echo "Processing $file"
17+
18+
# Extract just the en: section and remove the "en:" prefix
19+
# Using yq would be ideal, but falling back to sed/awk for broader compatibility
20+
if command -v yq &> /dev/null; then
21+
# If yq is available, use it for proper YAML parsing
22+
yq eval '.en // {}' "$file" >> "$OUTPUT_FILE"
23+
else
24+
# Fallback: use awk to extract content under "en:" key
25+
awk '
26+
/^en:/ { in_en=1; next }
27+
/^[a-zA-Z]/ && !/^ / && in_en { in_en=0 }
28+
in_en && /^ / { print substr($0, 3) }
29+
' "$file" >> "$OUTPUT_FILE"
30+
fi
31+
done
32+
33+
echo "Merge complete. Output written to $OUTPUT_FILE"
34+
echo "File size: $(wc -l < "$OUTPUT_FILE") lines"

test/load_locale_into_db.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# frozen_string_literal: true
2+
3+
require "yaml"
4+
5+
class LoadLocaleIntoDB
6+
def self.call
7+
new.()
8+
end
9+
10+
def call
11+
puts "📂 Loading locale.yaml into database"
12+
13+
data = YAML.load_file(locale_file) || {}
14+
flatten_keys(data).each do |key, value|
15+
next if value.nil?
16+
17+
# Create the Original record
18+
original = Localization::Original::Create.(:website_server_side, key, value, nil, false)
19+
20+
# Create the Translation record for English
21+
Localization::Translation.create!(
22+
original: original,
23+
locale: "en",
24+
key: key,
25+
value: value
26+
)
27+
rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
28+
# Already added
29+
end
30+
end
31+
32+
private
33+
def locale_file
34+
Rails.root.join("config", "locale.yaml")
35+
end
36+
37+
# Flatten nested YAML into dot-notation keys
38+
def flatten_keys(obj, prefix = "")
39+
result = {}
40+
41+
case obj
42+
when Hash
43+
obj.each do |k, v|
44+
# Always stringify keys (so "1" instead of integer 1)
45+
k = k.to_s
46+
full_key = prefix.empty? ? k : "#{prefix}.#{k}"
47+
result.merge!(flatten_keys(v, full_key))
48+
end
49+
else
50+
# leaf value
51+
result[prefix] = obj
52+
end
53+
54+
result
55+
end
56+
end

test/test_helper.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require 'minitest/retry'
99
require_relative './helpers/turbo_assertions_helper'
1010
require 'generate_js_config'
11+
require_relative './load_locale_into_db'
1112

1213
# We need to write the manifest.json and env.json files as the
1314
# javascript:build rake task that is called below depends on it
@@ -168,6 +169,8 @@ class ActiveSupport::TestCase
168169
# parallelize(workers: :number_of_processors)
169170

170171
setup do
172+
# Load i18n data into database before each test
173+
LoadLocaleIntoDB.()
171174
reset_redis!
172175
reset_rack_attack!
173176

test/visual/tracks_page_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,18 @@ class TracksPageVisualTest < ApplicationSystemTestCase
1010
create :track, :random_slug, title: "JavaScript", blurb: "The language of the web"
1111
create :track, :random_slug, title: "Python", blurb: "A powerful programming language"
1212

13+
# Verify i18n is working for tracks page title
14+
translation = Localization::Translation.find_by(locale: "en", key: "tracks.index.header.title")
15+
assert translation.present?, "Translation for tracks.index.header.title should exist"
16+
assert_includes translation.value, "%<num_tracks>s languages", "Translation should contain expected text"
17+
1318
visit tracks_path
1419

20+
# Verify the translation is actually rendered (should contain "3 languages" not "Title")
21+
assert_no_text "Title"
22+
assert_no_text "Description HTML"
23+
assert_text "3 languages for you to master"
24+
1525
# Take screenshot
1626
page.save_screenshot('tmp/screenshots/visual_tracks_page.png')
1727

0 commit comments

Comments
 (0)