ruby-on-rails 使用可选路径前缀创建路由

1aaf6o9v  于 2023-03-24  发布在  Ruby
关注(0)|答案(6)|浏览(137)

如何使路由识别可选前缀参数,如下所示:

/*lang/controller/id

因为lang部分是可选的,如果URL中没有指定,则具有默认值:

/en/posts/1   => lang = en
/fr/posts/1   => lang = fr
/posts/1      => lang = en
  • 编辑 *

理想情况下,我希望通过Map一个命名空间来跨多个控制器和动作实现这一点:

map.namespace "*lang" do |lang|
  lang.resources :posts
  lang.resources :stories
end
cs7cruho

cs7cruho1#

好吧,我已经解决了这个问题:
默认情况下,在Rails中没有办法做到这一点(至少目前还没有)。
安装插件后,我将以下文件添加到lib目录:

require 'routing_filter/base'

module RoutingFilter
  class Locale < Base

    # remove the locale from the beginning of the path, pass the path
    # to the given block and set it to the resulting params hash
    def around_recognize(path, env, &block)
      locale = nil
      path.sub! %r(^/([a-zA-Z]{2})(?=/|$)) do locale = $1; '' end
      returning yield do |params|
        params[:locale] = locale || 'en'
      end
    end

    def around_generate(*args, &block)
      locale = args.extract_options!.delete(:locale) || 'en'
      returning yield do |result|
        if locale != 'en'
          result.sub!(%r(^(http.?://[^/]*)?(.*))){ "#{$1}/#{locale}#{$2}" }
        end 
      end
    end

  end
end

我将这一行添加到routes.rb:

map.filter 'locale'

这基本上填充了一个由插件生成的before和after钩子,它 Package 了rails路由。
当一个url被识别,并且在Rails开始对它做任何事情之前,around_recognize方法被调用。这将提取一个代表locale的两个字母的代码,并将其传递到参数中,如果没有指定locale,则默认为'en'。
同样,当生成一个url时,locale参数将被推送到左侧的URL中。
这会给我以下URL和Map:

/   => :locale => 'en'
/en => :locale => 'en'
/fr => :locale => 'fr'

所有现有的url helper都像以前一样工作,唯一的区别是除非指定了locale,否则它会被保留:

home_path                  => /
home_path(:locale => 'en') => /
home_path(:locale => 'fr') => /fr
7gyucuyw

7gyucuyw2#

您可以使用:defaults键在路由中定义默认值。尝试以下操作:

map.connect ':lang/posts/:id', :controller => 'posts', :action => 'show',
                               :defaults => { :lang => 'en' }
bpzcxfmw

bpzcxfmw3#

我明白这个老问题,但它可能是有用的。

map.with_options(:path_prefix => ":locale") do |m|
    m.resources :posts
    m.resources :stories        
  end

并且您必须在application controller中添加before filter来定义locale,例如

before_filter :define_locale

def define_locale
  if params[:locale] == nil
    I18n.locale = 'en'
  else
    I18n.locale = params[:locale]
  end
end
velaa5lx

velaa5lx4#

我猜(现在没有时间测试)这可能有效:

map.connect ':language/posts/:id', :controller => 'posts', :action => 'show'
map.connect 'posts/:id', :controller => 'posts', :action => 'show'

好吧,试过了--至少在Rails 2.1.1上可以工作。所以这很好。但是不能让:defaults的想法工作,这是一个遗憾,因为它更干燥。

hgc7kmma

hgc7kmma5#

我想你可以使用一个[key] =〉nil来指定一个可选参数。类似于:

map.connect ':lang/posts/:id', :controller => 'posts', :action => 'show', :lang => nil
8xiog9wr

8xiog9wr6#

在较新的Rails(3及以上版本)中,您可以使用可选的路径段来执行此操作:

第一步:

scope path: '(:lang)', constraints: { lang: /|de|en/ } do
  resources :posts
end

圆括号使:lang段成为可选的,约束将其限制为deen或空字符串。

第二步:

在你的控制器中,你可以从params hash中读取这个段:

class ApplicationController < ActionController::Base

  around_action :set_locale

  def set_locale(&action)
    locale = params[:lang] || I18n.default_locale # or your custom rules
    I18n.with_locale(locale, &action)
  end

end

第三步:

在执行重定向时有一个小问题::lang段丢失。要修复此覆盖default_url_options

class ApplicationController < ActionController::Base

  def default_url_options
    super.merge(lang: params[:lang])
  end

end

其他方面:

邮件地址:

如果您的邮件程序生成链接,并且您也希望在那里使用:lang前缀,则可以将default_url_options添加到ApplicationMailer类中,并从其他地方读取:lang,例如从用户记录(如果有)或从参数读取

默认路由:

如果希望所有生成的路径/url都尽快添加:lang段,则可以向路径添加默认值

scope path: '(:lang)', constraints: { lang: /|de|en/ }, defaults: { lang: 'de' } do
  # Your normal routes
end

...但恕我直言,这或多或少会破坏整个概念的目的,因为任何生成的路径/URL都会得到一个:lang前缀,不管传入路由如何。
我发现在控制器中处理所有默认值并将没有:lang前缀的路由视为主路由而不仅仅是从属别名更实用。

无前缀路由:

有一个小小的警告:当你像上面那样设置路由,然后重定向到一个没有:lang路径段的路由时,该值最终会出现在查询参数中:

scope path: '(:lang)', constraints: { lang: /|de|en/ } } do
  resources :posts
end

resource :home

/de/posts/1这样的路由重定向到主路由将导致如下URL:

/home?lang=de

...因为路由器没有路径段可以放入lang参数。从好的方面来说,如果你重定向回posts路由,用户将获得原始的传入路由前缀。

相关问题