polyglot_routes 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d6cd3af8eca58dbea83f3a544870c6eda563a4863ffd12dfe0dd203dd48c9236
4
+ data.tar.gz: 50be1e14228851e8857388e5878a935dd16c127b721d264a554dc7ae1a59f503
5
+ SHA512:
6
+ metadata.gz: 483b19a6846c37da4dee1ea84f3efb85602989693b1eb05b16871a6a7329b3c0dc8ffdb34cce2de8877bcf7a6413b4ae6dd7292fb0c464ab0925bf1baecb571f
7
+ data.tar.gz: 5defc3ceadbdf6e3fa9005924744699fe0c97f5a2dde6f714980526d28e048db938e992328d65e40d242cdb5823c639709f8bbb608a3cd7db809375debd5142f
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bruno Costanzo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # polyglot_routes
2
+
3
+ [![Gem Version](https://img.shields.io/gem/v/polyglot_routes)](https://rubygems.org/gems/polyglot_routes)
4
+ [![CI](https://github.com/bruno-costanzo/polyglot_routes/actions/workflows/ci.yml/badge.svg)](https://github.com/bruno-costanzo/polyglot_routes/actions/workflows/ci.yml)
5
+ [![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](test)
6
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.2-CC342D)](polyglot_routes.gemspec)
7
+ [![Rails](https://img.shields.io/badge/rails-%3E%3D%207.1-D30001)](polyglot_routes.gemspec)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE.txt)
9
+
10
+ Translated Rails routes for every locale. No setup: you only write routes.
11
+
12
+ ## Install
13
+
14
+ ```ruby
15
+ gem "polyglot_routes"
16
+ ```
17
+
18
+ ## Use
19
+
20
+ ```ruby
21
+ # config/routes.rb
22
+ Rails.application.routes.draw do
23
+ localized do
24
+ root "pages#home"
25
+ resources :products
26
+ end
27
+ end
28
+ ```
29
+
30
+ ```yaml
31
+ # config/locales/es.yml
32
+ es:
33
+ routes:
34
+ products: productos
35
+ new: nuevo
36
+ edit: editar
37
+ ```
38
+
39
+ With `I18n.available_locales = [:en, :es]` and `:en` as the default locale:
40
+
41
+ | Path | Controller | `I18n.locale` |
42
+ |----------------------------|-------------------|---------------|
43
+ | `/products/new` | `products#new` | `en` |
44
+ | `/es/productos/nuevo` | `products#new` | `es` |
45
+ | `/es` | `pages#home` | `es` |
46
+
47
+ - The default locale has no prefix. Other locales get `/<locale>`.
48
+ - A segment without a translation stays the same.
49
+ - Each request sets `I18n.locale` from the URL.
50
+
51
+ ## Helpers
52
+
53
+ ```ruby
54
+ products_path # follows I18n.locale
55
+ products_path(locale: :es) # "/es/productos"
56
+ products_es_path # one helper per locale
57
+ switch_locale_path(:en) # current page in another locale (views and controllers)
58
+ ```
59
+
60
+ Only some locales: `localized(locales: %i[en es]) { ... }`.
61
+
62
+ ## License
63
+
64
+ MIT
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PolyglotRoutes
4
+ # Included in every controller by the Railtie.
5
+ module Controller
6
+ def self.included(base)
7
+ base.around_action :polyglot_routes_with_locale
8
+ base.helper_method :switch_locale_path if base.respond_to?(:helper_method)
9
+ end
10
+
11
+ # The current page in another locale: switch_locale_path(:es)
12
+ def switch_locale_path(locale)
13
+ url_for(request.path_parameters.merge(locale: locale.to_s, only_path: true,
14
+ params: request.query_parameters))
15
+ end
16
+
17
+ private
18
+
19
+ def polyglot_routes_with_locale(&action)
20
+ locale = request.path_parameters[:locale]
21
+ locale ? I18n.with_locale(locale, &action) : yield
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PolyglotRoutes
4
+ # Prepended into ActionDispatch::Routing::Mapper.
5
+ module Mapper
6
+ # Draws every route in the block once per locale.
7
+ #
8
+ # localized do
9
+ # resources :products
10
+ # end
11
+ def localized(locales: PolyglotRoutes.locales)
12
+ locales = locales.map(&:to_s)
13
+ unknown = locales - PolyglotRoutes.locales
14
+ raise ArgumentError, "Unknown locales for localized: #{unknown.join(", ")}" if unknown.any?
15
+
16
+ previous = @polyglot_routes_locales
17
+ @polyglot_routes_locales = locales
18
+ yield
19
+ ensure
20
+ @polyglot_routes_locales = previous
21
+ end
22
+
23
+ private
24
+
25
+ def add_route(*args)
26
+ return super if @polyglot_routes_locales.nil? || @polyglot_routes_locale
27
+
28
+ @polyglot_routes_locales.each do |locale|
29
+ @polyglot_routes_locale = locale
30
+ @scope = @scope.new(defaults: (@scope[:defaults] || {}).merge(locale: locale))
31
+ super
32
+ ensure
33
+ @scope = @scope.parent
34
+ @polyglot_routes_locale = nil
35
+ end
36
+
37
+ define_polyglot_routes_helpers
38
+ end
39
+
40
+ def path_for_action(action, path)
41
+ result = super
42
+ @polyglot_routes_locale ? PolyglotRoutes.translate_path(result, @polyglot_routes_locale) : result
43
+ end
44
+
45
+ def name_for_action(as, action)
46
+ name = super
47
+ return name unless @polyglot_routes_locale && name
48
+
49
+ localized = "#{name}_#{@polyglot_routes_locale}"
50
+ return if as == ActionDispatch::Routing::Mapper::DEFAULT && has_named_route?(localized)
51
+
52
+ @polyglot_routes_last_name = name
53
+ localized
54
+ end
55
+
56
+ # Defines `products_path` / `products_url` that call the helper for I18n.locale.
57
+ def define_polyglot_routes_helpers
58
+ name = @polyglot_routes_last_name
59
+ @polyglot_routes_last_name = nil
60
+ return unless name
61
+
62
+ { "path" => @set.named_routes.path_helpers_module,
63
+ "url" => @set.named_routes.url_helpers_module }.each do |kind, helpers|
64
+ helpers.define_method("#{name}_#{kind}") do |*args|
65
+ PolyglotRoutes.call_helper(self, name, kind, args)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ ActionDispatch::Routing::Mapper.prepend(PolyglotRoutes::Mapper)
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PolyglotRoutes
4
+ class Railtie < Rails::Railtie
5
+ initializer "polyglot_routes.controller" do
6
+ ActiveSupport.on_load(:action_controller) { include PolyglotRoutes::Controller }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PolyglotRoutes
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "i18n"
4
+ require "action_dispatch"
5
+ require "polyglot_routes/version"
6
+ require "polyglot_routes/mapper"
7
+ require "polyglot_routes/controller"
8
+ require "polyglot_routes/railtie" if defined?(Rails::Railtie)
9
+
10
+ module PolyglotRoutes
11
+ DYNAMIC_SEGMENT = /[:*()]/
12
+
13
+ module_function
14
+
15
+ def locales
16
+ I18n.available_locales.map(&:to_s)
17
+ end
18
+
19
+ def default_locale
20
+ I18n.default_locale.to_s
21
+ end
22
+
23
+ # "/products/:id/edit" + "es" => "/es/productos/:id/editar"
24
+ def translate_path(path, locale)
25
+ segments = path.split("/").map do |segment|
26
+ next segment if segment.empty? || segment.match?(DYNAMIC_SEGMENT)
27
+
28
+ I18n.t(segment, scope: :routes, locale: locale, default: segment)
29
+ end
30
+
31
+ translated = segments.join("/")
32
+ translated = "/#{translated}" unless translated.start_with?("/")
33
+ return translated if locale == default_locale
34
+
35
+ translated == "/" ? "/#{locale}" : "/#{locale}#{translated}"
36
+ end
37
+
38
+ # Called by `products_path`: picks `products_es_path` for the current locale.
39
+ def call_helper(context, name, kind, args)
40
+ args = args.dup
41
+ options = args.last.is_a?(Hash) ? args.pop.dup : {}
42
+ wanted = options.key?(:locale) ? options.delete(:locale) : I18n.locale
43
+ args << options unless options.empty?
44
+
45
+ locale = [wanted.to_s, default_locale, *locales].find do |candidate|
46
+ context.respond_to?("#{name}_#{candidate}_#{kind}")
47
+ end
48
+ context.public_send("#{name}_#{locale}_#{kind}", *args)
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: polyglot_routes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Bruno Costanzo
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: actionpack
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: railties
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '7.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '7.1'
40
+ description: Wrap your routes in `localized` and get one translated route per locale,
41
+ locale-aware URL helpers and I18n.locale set from the URL.
42
+ email:
43
+ - dev.bcostanzo@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE.txt
49
+ - README.md
50
+ - lib/polyglot_routes.rb
51
+ - lib/polyglot_routes/controller.rb
52
+ - lib/polyglot_routes/mapper.rb
53
+ - lib/polyglot_routes/railtie.rb
54
+ - lib/polyglot_routes/version.rb
55
+ homepage: https://github.com/bruno-costanzo/polyglot_routes
56
+ licenses:
57
+ - MIT
58
+ metadata: {}
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '3.2'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 4.0.10
74
+ specification_version: 4
75
+ summary: Translated Rails routes for every locale, with no setup.
76
+ test_files: []