I18nOnSteroids
I18nOnSteroids is a Ruby gem that supercharges Rails' I18n functionality with advanced interpolation and piping features. Transform your translations with powerful data manipulation, conditional logic, and composable transformations—all directly in your locale files.
Features
✨ 18 Built-in Pipes - String manipulation, formatting, dates, currency, and more 🔗 Pipe Composition - Dynamic parameters with variable interpolations 🎯 Conditional Pipes - Apply transformations based on runtime conditions 🏷️ Namespaced Pipes - Organize custom pipes by domain ⚡ High Performance - Thread-safe with memoization and lazy evaluation 🛠️ Developer Tools - Interactive console, generator, and benchmarks 🔧 Highly Configurable - Debug mode, strict mode, custom pipes
Installation
Add this line to your application's Gemfile:
gem 'i18n_on_steroids'
And then execute:
bundle install
Or install it yourself as:
gem install i18n_on_steroids
Quick Start
Once installed, I18nOnSteroids automatically enhances your Rails I18n functionality.
Basic Pipes
Use ${} for advanced interpolation with pipes:
en:
users:
count: "There are ${count | number_with_delimiter} users"
greeting: "Hello ${name | titleize}!"
t('users.count', count: 1000)
# => "There are 1,000 users"
t('users.greeting', name: 'john doe')
# => "Hello John Doe!"
Chained Pipes
Chain multiple transformations:
en:
posts:
title: "${title | titleize | truncate: 50}"
t('posts.title', title: 'a very long blog post title that needs truncation')
# => "A Very Long Blog Post Title That Needs..."
Conditional Pipes
Apply pipes based on conditions:
en:
admin:
status: "${message | upcase if: admin}"
secret: "${data | default: N/A unless: authorized}"
t('admin.status', message: 'confidential', admin: true)
# => "CONFIDENTIAL"
t('admin.status', message: 'confidential', admin: false)
# => "confidential"
Pipe Composition
Use variable interpolations in pipe parameters:
en:
items:
count: "${number | pluralize: ${unit}}"
price: "${amount | currency: ${symbol}}"
t('items.count', number: 5, unit: 'item')
# => "5 items"
t('items.price', amount: 99.99, symbol: '€')
# => "€99.99"
Available Pipes
String Manipulation
| Pipe | Description | Example |
|---|---|---|
upcase |
Convert to uppercase | `${name \ |
downcase |
Convert to lowercase | `${NAME \ |
capitalize |
Capitalize first character | `${text \ |
titleize |
Convert to title case | `${text \ |
humanize |
Make string human-readable | `${field_name \ |
parameterize |
Convert to URL-safe format | `${title \ |
strip |
Remove leading/trailing whitespace | `${text \ |
squish |
Remove excess whitespace | `${text \ |
truncate |
Limit string length | `${text \ |
Aliases: truncate → trim, limit, shorten
Numbers & Currency
| Pipe | Description | Example |
|---|---|---|
number_with_delimiter |
Format with thousands separator | `${count \ |
round |
Round to precision | `${number \ |
currency |
Format as currency | `${price \ |
Dates & Times
| Pipe | Description | Example |
|---|---|---|
date_format |
Format date with strftime | `${date \ |
time_format |
Format time with strftime | `${time \ |
Text Processing
| Pipe | Description | Example |
|---|---|---|
pluralize |
Pluralize word based on count | `${word \ |
format |
Apply format string | `${value \ |
default |
Provide fallback for nil/empty | `${optional \ |
Rails Helpers
| Pipe | Description | Example |
|---|---|---|
html_safe |
Mark string as HTML safe | `${html \ |
Advanced Features
Custom Pipes
Register your own pipes:
# config/initializers/i18n_on_steroids.rb
I18nOnSteroids::TranslationHelper.register_pipe(:reverse, ->(value, params, ) {
value.to_s.reverse
})
Use in translations:
en:
fun:
backwards: "${text | reverse}"
Namespaced Pipes
Organize pipes by domain to avoid naming conflicts:
I18nOnSteroids::TranslationHelper.register_pipe(
:format,
->(value, params, ) { value.strftime(params) },
namespace: :datetime
)
en:
events:
date: "${created_at | datetime.format: %B %d, %Y}"
Pipe Aliases
Create shortcuts for commonly used pipes:
I18nOnSteroids::TranslationHelper.register_pipe_alias(:cut, :truncate)
en:
preview: "${text | cut: 100}" # Same as truncate
Rails Generator
Generate custom pipes with boilerplate:
rails generate i18n_pipe my_custom
rails generate i18n_pipe format --namespace=datetime
rails generate i18n_pipe humanize_bytes --description="Format bytes"
This creates:
app/pipes/my_custom_pipe.rb- Registers pipe in
config/initializers/i18n_on_steroids.rb
Configuration
Configure I18nOnSteroids in an initializer:
# config/initializers/i18n_on_steroids.rb
I18nOnSteroids.configure do |config|
# Default truncate length (default: 30)
config.default_truncate_length = 50
# Default rounding precision (default: 2)
config.default_round_precision = 2
# Return empty string for missing values instead of placeholder (default: false)
config.fallback_on_missing_value = false
# Raise error on unknown pipes (default: false)
config.raise_on_unknown_pipe = false
# Enable debug logging of pipe transformations (default: false)
config.debug_mode = false
# Strict mode: raise on all errors, not just unknown pipes (default: false)
config.strict_mode = false
end
Debug Mode
Enable debug mode to log all pipe transformations:
I18nOnSteroids.configure do |config|
config.debug_mode = true
end
Output:
[I18nOnSteroids] Processing translation for key: users.greeting
[I18nOnSteroids] Applying 1 pipe(s) to value: "john doe"
[I18nOnSteroids] Applying pipe 'titleize' with params: nil
[I18nOnSteroids] Result after 'titleize': "John Doe"
Strict Mode
Strict mode raises errors for all pipe failures:
I18nOnSteroids.configure do |config|
config.strict_mode = true # Raises on unknown pipes AND execution errors
end
Interactive Console
I18nOnSteroids includes helpful console commands for development:
bin/console
Available commands:
# Show interactive demo of all features
I18nOnSteroids.demo
# Test a pipe with sample input
I18nOnSteroids.test_pipe(:upcase, 'hello')
# => Input: "hello"
# Output: "HELLO"
# List all available pipes
I18nOnSteroids.list_pipes
# Get detailed info about a specific pipe
I18nOnSteroids.pipe_info(:truncate)
Performance
I18nOnSteroids is optimized for production use:
Optimizations
- ✅ Thread-safe - Safe for Puma/concurrent requests
- ✅ Pre-compiled regex - Patterns compiled once at load time
- ✅ Pipe cache - Parsed pipe chains cached automatically
- ✅ Lazy evaluation - Skip processing when no pipes present
- ✅ Zero allocations - Frozen constants and efficient string handling
Benchmarks
Run the benchmark suite:
BUNDLE_GEMFILE=gemfiles/ruby_3_plus.gemfile bundle exec ruby benchmark/performance.rb
Expected performance (typical hardware):
- Simple interpolations: ~100K-300K ops/sec
- Single pipe: ~50K-150K ops/sec
- Multiple pipes: ~20K-80K ops/sec
- Complex translations: ~10K-40K ops/sec
Cache hits are typically 10-50% faster than cache misses.
See benchmark/README.md for details.
Syntax Support
I18nOnSteroids supports three interpolation syntaxes:
en:
example:
dollar: "${value | upcase}" # Recommended
percent: "%{value | upcase}" # I18n compatible
double: "{{value | upcase}}" # Alternative
All syntaxes support the full feature set (pipes, conditions, composition).
Mixed Content
Combine standard I18n interpolation with advanced pipes:
en:
items:
summary: "Showing %{from} - %{to} of ${total | number_with_delimiter} ${item | pluralize:%{total}}"
t('items.summary', from: 1, to: 10, total: 1234, item: 'item')
# => "Showing 1 - 10 of 1,234 items"
Real-World Examples
E-commerce
en:
products:
price: "${amount | currency: ${locale_currency}}"
sale: "${original | currency: ${currency}} ${discount | round: 0}% off!"
stock: "${count | pluralize: ${unit}} ${available | default: in stock}"
Admin Dashboard
en:
admin:
user_info: "${name | titleize} (${email | downcase})"
sensitive: "${data | upcase if: admin}"
log: "${timestamp | date_format: %Y-%m-%d %H:%M} - ${message | truncate: 100}"
Content Management
en:
posts:
title: "${title | titleize | truncate: 60}"
slug: "${title | parameterize}"
summary: "${body | strip | squish | truncate: 200}"
meta: "By ${author | titleize} on ${published_at | date_format: %B %d, %Y}"
User Notifications
en:
notifications:
count: "You have ${count | pluralize: ${type}}"
amount: "${value | currency: $} ${status | default: pending}"
time: "${duration | round: 1} ${unit | pluralize: ${duration}}"
Error Handling
Unknown Pipes
By default, unknown pipes are silently ignored:
en:
test: "${value | nonexistent_pipe}"
t('test', value: 'hello')
# => "hello" (pipe ignored)
Enable strict error handling:
config.raise_on_unknown_pipe = true # Raises on unknown pipes only
config.strict_mode = true # Raises on all errors
Missing Values
Missing interpolation values return placeholder by default:
en:
test: "${missing | upcase}"
t('test')
# => "%{missing | upcase}"
Enable fallback mode:
config.fallback_on_missing_value = true
t('test')
# => "" (empty string)
Development
After checking out the repo:
bin/setup # Install dependencies
bundle exec rake test # Run tests
bin/console # Interactive prompt
To install this gem onto your local machine:
bundle exec rake install
Running Tests
# With specific Ruby/Rails version
BUNDLE_GEMFILE=gemfiles/ruby_3_plus.gemfile bundle exec rake test
# With code coverage
COVERAGE=true bundle exec rake test
Code Quality
bundle exec rubocop # Lint code
bundle exec rubocop -a # Auto-fix issues
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/games-directory/i18n_on_steroids.
- Fork the repository
- Create your feature branch (
git checkout -b my-new-feature) - Add tests for your changes
- Ensure tests pass (
bundle exec rake test) - Ensure Rubocop passes (
bundle exec rubocop) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Please make sure to update documentation and add tests before submitting.
Project Goals
The goal is to support all ActionView::Helpers methods that make sense in a translation context, while maintaining:
- High performance
- Thread safety
- Backward compatibility
- Clean, maintainable code
Versioning
I18nOnSteroids follows Semantic Versioning.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the I18nOnSteroids project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
Credits
Developed by Studio51 Solutions.