{"id":37991682,"url":"https://github.com/alec-c4/inertia_i18n","last_synced_at":"2026-01-22T04:01:05.089Z","repository":{"id":332852703,"uuid":"1135002760","full_name":"alec-c4/inertia_i18n","owner":"alec-c4","description":"Translation management for Inertia.js applications with Rails backend","archived":false,"fork":false,"pushed_at":"2026-01-19T19:04:05.000Z","size":103,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-21T12:28:26.955Z","etag":null,"topics":["i18n","i18next","i18next-react","inertia","inertiajs","react","reactjs","ruby","ruby-on-rails","svelte","sveltejs","vue","vuejs"],"latest_commit_sha":null,"homepage":"https://alec-c4.com","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alec-c4.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-15T14:03:56.000Z","updated_at":"2026-01-20T20:38:09.000Z","dependencies_parsed_at":"2026-01-21T03:00:42.898Z","dependency_job_id":null,"html_url":"https://github.com/alec-c4/inertia_i18n","commit_stats":null,"previous_names":["alec-c4/inertia_i18n"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/alec-c4/inertia_i18n","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Finertia_i18n","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Finertia_i18n/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Finertia_i18n/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Finertia_i18n/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alec-c4","download_url":"https://codeload.github.com/alec-c4/inertia_i18n/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alec-c4%2Finertia_i18n/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28653620,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["i18n","i18next","i18next-react","inertia","inertiajs","react","reactjs","ruby","ruby-on-rails","svelte","sveltejs","vue","vuejs"],"created_at":"2026-01-16T18:44:01.155Z","updated_at":"2026-01-22T04:01:04.650Z","avatar_url":"https://github.com/alec-c4.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# InertiaI18n\n\nTranslation management for Inertia.js applications with Rails backend\n\n[![Gem Version](https://badge.fury.io/rb/inertia_i18n.svg)](https://badge.fury.io/rb/inertia_i18n)\n[![Build Status](https://github.com/alec-c4/inertia_i18n/actions/workflows/main.yml/badge.svg)](https://github.com/alec-c4/inertia_i18n/actions)\n\n## The Problem\n\nInertia.js applications have a split architecture:\n\n- **Backend (Rails):** Uses YAML locale files (`config/locales/*.yml`)\n- **Frontend (React/Svelte/Vue):** Uses i18next JSON files\n\nThis creates several challenges:\n\n1. **Duplicate management:** Maintaining translations in two formats\n2. **Sync issues:** Keys in YAML but missing in JSON (or vice versa)\n3. **No usage tracking:** Unused translation keys accumulate\n4. **Manual process:** Converting YAML → JSON by hand is error-prone\n\nExisting tools like [i18n-tasks](https://github.com/glebm/i18n-tasks) only handle Rails/backend translations.\n\n## The Solution\n\nInertiaI18n provides:\n\n- **YAML → JSON conversion** with interpolation mapping (`%{var}` → `{{var}}`)\n- **AST-based scanning** to find translation usage in `.svelte`, `.tsx`, `.vue` files\n- **Health checks** to detect missing, unused, and unsynchronized keys\n- **Watch mode** for automatic regeneration during development\n- **Rails integration** via initializers and rake tasks\n\n**One source of truth:** Rails YAML files, with JSON auto-generated.\n\n---\n\n## Installation\n\nAdd to your Gemfile:\n\n```ruby\ngem 'inertia_i18n'\n```\n\nRun the installer:\n\n```bash\nrails generate inertia_i18n:install\n```\n\nThis generator will:\n\n1. Create the locale directory structure (`config/locales/frontend`, `config/locales/backend`).\n2. Generate the configuration file (`config/initializers/inertia_i18n.rb`).\n3. Create a sample locale file.\n4. Detect your frontend framework (React, Vue, or Svelte) and add necessary dependencies (e.g., `react-i18next`) to your `package.json`.\n\n---\n\n## Recommended Directory Structure\n\nTo avoid conflicts between backend and frontend translation keys, it is recommended to separate your locale files into subdirectories:\n\n```\nconfig/\n└── locales/\n    ├── backend/      # Rails-specific translations\n    │   ├── en.yml\n    │   └── ru.yml\n    ├── frontend/     # Frontend-specific translations\n    │   ├── common.en.yml\n    │   ├── pages.en.yml\n    │   └── pages.ru.yml\n    └── en.yml          # Optional: shared or legacy keys\n```\n\nBy default, InertiaI18n will look for YAML files in `config/locales/frontend`. You can customize this using the `source_paths` configuration.\n\n---\n\n## Quick Start\n\n### 1. Configure\n\nThe installer creates a default configuration file. You can customize it in `config/initializers/inertia_i18n.rb`.\n\n```ruby\n# config/initializers/inertia_i18n.rb\nInertiaI18n.configure do |config|\n  # Recommended: point to a dedicated frontend folder\n  config.source_paths = [Rails.root.join('config', 'locales', 'frontend')]\n\n  config.target_path = Rails.root.join('app', 'frontend', 'locales')\n  config.locales = [:en, :ru]\n\n  # Scan paths are automatically set based on your detected framework\n  config.scan_paths = [\n    Rails.root.join('app', 'frontend', '**', '*.{svelte,tsx,vue}')\n  ]\nend\n```\n\n### 2. Convert YAML to JSON\n\n```bash\n# One-time conversion\nbundle exec rake inertia_i18n:convert\n\n# Watch mode (auto-convert on YAML changes)\nbundle exec rake inertia_i18n:watch\n```\n\n### 3. Check Translation Health\n\nThe recommended way to check translation health is by running the generated test as part of your test suite. See the [CI Integration](#ci-integration) section for details.\n\nYou can also run a manual check from the command line:\n\n```bash\n# Find missing, unused, and out-of-sync keys\nbundle exec rake inertia_i18n:health\n```\n\n---\n\n## CLI Usage\n\nAll CLI commands load the Rails environment, so they have access to your application's configuration and behave identically to the `rake` tasks.\n\n```bash\n# Generate a new configuration file\ninertia_i18n init\n\n# Convert YAML to JSON\ninertia_i18n convert\n\n# Convert specific locale\ninertia_i18n convert --locale=ru\n\n# Scan frontend code for translation usage\ninertia_i18n scan\n\n# Check translation health\ninertia_i18n health\n\n# Sort and format JSON locale files\ninertia_i18n normalize\n\n# Watch for changes and auto-convert\ninertia_i18n watch\n```\n\n---\n\n## Features\n\n### YAML → JSON Conversion\n\n**Input (Rails YAML):**\n\n```yaml\n# config/locales/en.yml\nen:\n  user:\n    greeting: \"Hello, %{name}!\"\n    items:\n      one: \"1 item\"\n      other: \"%{count} items\"\n```\n\n**Output (i18next JSON):**\n\n```json\n{\n  \"user\": {\n    \"greeting\": \"Hello, {{name}}!\",\n    \"items_one\": \"1 item\",\n    \"items_other\": \"{{count}} items\"\n  }\n}\n```\n\n### Smart Scanning\n\nDetects translation usage in:\n\n- Svelte: `{t('key')}` and `t('key')` in `\u003cscript\u003e`\n- React: `{t('key')}` in JSX\n- Vue: `{{ t('key') }}` and `t('key')` in script\n\nHandles:\n\n- Static keys: `t('user.greeting')`\n- Template literals: `t(\\`user.\\${type}.title\\`)` (flagged for review)\n- Dynamic patterns: `t(keyVariable)` (flagged for review)\n\n### Health Checks\n\n| Check            | Description                                      |\n| ---------------- | ------------------------------------------------ |\n| **Missing Keys** | Used in code but not in JSON (breaks app)        |\n| **Unused Keys**  | In JSON but never used (bloat)                   |\n| **Locale Sync**  | Key exists in `en.json` but missing in `ru.json` |\n\n### Watch Mode\n\nAuto-regenerates JSON when YAML files change:\n\n```bash\nbundle exec rake inertia_i18n:watch\n\n# Output:\n👀 Watching config/locales for YAML changes...\n📝 Detected locale file changes...\n   Changed: config/locales/hr.en.yml\n🔄 Regenerating JSON files...\n✅ Done!\n```\n\n---\n\n## CI Integration\n\nThe best way to ensure your translations stay healthy is to check them in your Continuous Integration (CI) pipeline.\n\n### Test-based Health Check (Recommended)\n\nGenerate a dedicated test file that runs the health check as part of your test suite:\n\n```bash\n# For RSpec\nrails g inertia_i18n:test\n# Creates spec/inertia_i18n_health_spec.rb\n\n# For Minitest\n# Creates test/inertia_i18n_health_test.rb\n```\n\nNow, your existing CI command will automatically catch translation issues:\n\n```bash\n# Run your full test suite\nbundle exec rspec\n# or\nbundle exec rails test\n```\n\nWhen issues are found, the test will fail with a detailed report:\n\n```\nFailure/Error: fail message.join(\"\\n\")\n\nRuntimeError:\n\n  Translation health check failed!\n\n  Missing Keys (1):\n    - home.title\n\n  Unused Keys (1):\n    - unused.key\n\n  Locale Synchronization Issues (2):\n    - unused.key (in ru)\n    - home.title (in ru)\n```\n\n### GitHub Actions Example\n\n```yaml\n# .github/workflows/ci.yml\nname: CI\n\non: [push, pull_request]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: ruby/setup-ruby@v1\n        with:\n          bundler-cache: true\n\n      # Run the full test suite, which now includes the translation health check\n      - name: Run tests\n        run: bundle exec rspec\n```\n\n---\n\n## Compatibility with i18n-tasks\n\nIf you use [i18n-tasks](https://github.com/glebm/i18n-tasks) for your backend translations, it might flag your frontend keys as \"unused\" or \"missing\". To prevent this, configure `i18n-tasks` to ignore the frontend locale directory.\n\nAdd this to your `config/i18n-tasks.yml`:\n\n```yaml\n# config/i18n-tasks.yml\ndata:\n  read:\n    - \"config/locales/backend/**/*.yml\" # Read only backend locales\n    - \"config/locales/*.yml\" # Optional: shared keys\n```\n\nAlternatively, you can exclude the frontend directory:\n\n```yaml\n# config/i18n-tasks.yml\nignore:\n  - \"frontend.*\" # Ignore all keys starting with \"frontend.\" (if namespaced)\n```\n\n---\n\n## Configuration Reference\n\n```ruby\nInertiaI18n.configure do |config|\n  # Source directories for your frontend YAML files.\n  # Default: ['config/locales/frontend']\n  config.source_paths = [\n    'config/locales/frontend',\n    'config/locales/common'\n  ]\n  config.source_pattern = '**/*.{yml,yaml}'\n\n  # Target: i18next JSON files\n  config.target_path = 'app/frontend/locales'\n\n  # Locales to process\n  config.locales = [:en, :ru, :de]\n\n  # Frontend paths to scan\n  config.scan_paths = [\n    'app/frontend/**/*.{js,ts,jsx,tsx,svelte,vue}'\n  ]\n\n  # Interpolation conversion\n  config.interpolation = { from: '%{', to: '{{' }\n\n  # Flatten nested keys (default: false)\n  config.flatten_keys = false\n\n  # Ignore patterns (don't scan these files)\n  config.ignore_patterns = [\n    '**/node_modules/**',\n    '**/vendor/**',\n    '**/*.test.{js,ts}'\n  ]\nend\n```\n\n---\n\n## Comparison with Alternatives\n\n| Feature                 | InertiaI18n | i18n-tasks        | i18next-parser       |\n| ----------------------- | ----------- | ----------------- | -------------------- |\n| Rails YAML support      | ✅          | ✅                | ❌                   |\n| i18next JSON support    | ✅          | ❌                | ✅                   |\n| YAML → JSON conversion  | ✅          | ❌                | ❌                   |\n| Frontend usage scanning | ✅          | ❌                | ✅ (extraction only) |\n| Missing keys detection  | ✅          | ✅ (backend only) | ✅ (frontend only)   |\n| Unused keys detection   | ✅          | ✅ (backend only) | ❌                   |\n| Locale sync check       | ✅          | ✅                | ✅                   |\n| Watch mode              | ✅          | ❌                | ✅                   |\n| Rails integration       | ✅          | ✅                | ❌                   |\n| Inertia.js specific     | ✅          | ❌                | ❌                   |\n\n**InertiaI18n = i18n-tasks + i18next-parser + YAML↔JSON bridge**\n\n---\n\n## Development\n\n```bash\n# Clone repository\ngit clone https://github.com/alec-c4/inertia_i18n.git\ncd inertia_i18n\n\n# Install dependencies\nbundle install\n\n# Run tests\nbundle exec rspec\n\n# Run locally in your Rails app\n# Add to Gemfile:\ngem 'inertia_i18n', path: '../inertia_i18n'\n```\n\n---\n\n## Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Write tests (TDD approach)\n4. Implement the feature\n5. Commit your changes (`git commit -m 'Add amazing feature'`)\n6. Push to the branch (`git push origin feature/amazing-feature`)\n7. Open a Pull Request\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for details.\n\n---\n\n## License\n\nMIT License - see [LICENSE.txt](LICENSE.txt)\n\n---\n\n## Credits\n\nCreated by [Alexey Poimtsev](https://alec-c4.com)\n\nInspired by:\n\n- [i18n-tasks](https://github.com/glebm/i18n-tasks) - Rails i18n management\n- [i18next-parser](https://github.com/i18next/i18next-parser) - Frontend key extraction\n- Real-world pain from managing translations in Inertia.js apps\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falec-c4%2Finertia_i18n","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falec-c4%2Finertia_i18n","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falec-c4%2Finertia_i18n/lists"}