Ruby on Rails网站语言国际化

涉及到在Git@OSC中的一些应用

Gem

Rails项目双语化的时候需要用到Rails-18nhttp_accept_languageGem

  • rails-i18n
    根据对应的rails版本添加到Gemfile中
    gem 'rails-i18n', '~> 4.0.0' # For 4.0.x
    gem 'rails-i18n', '~> 3.0.0' # For 3.x
    gem 'rails-i18n', github: 'svenfuchs/rails-i18n', branch: 'master' # For 4.x
    gem 'rails-i18n', github: 'svenfuchs/rails-i18n', branch: 'rails-3-x' # For 3.x
    
  • http_accept_language
    这个Gem能够把用户请求发送过来的请求头中把Accept-Language提取出来放在一个数组中。即就是能检测出用户浏览器中设置的语言偏好。
    gem 'http_accept_language'
    

configuration

config/application.rb文件中基本的设置如下

config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] #
config.i18n.default_locale = 'zh-CN'
config.i18n.available_locales = ['zh-CN', 'en', 'zh-TW']
config.i18n.fallbacks = true  #当应用程序需要的语言文件缺失时,使用默认的语言文件default_locale

同时I18n.config.enforce_available_locales默认是true,要改变为true

ApplicationController

用户请求通过路由器发送到对应的action之前应在application_controller中设置好网站的语言种类:
before_filter set_locale

def set_locale
  if cookies[:user_locale] && I18n.available_locales.include?(cookies[:user_locale].to_sym)
    l = cookies[:user_locale].to_sym
  else
    l = http_accept_language.compatible_language_from(I18n.available_locales)
    cookies.permanent[:user_locale] = l
  end
  I18n.locale = l || I18n.locale
end

Git@OSC网站国际化的策略是:首先读取用户的浏览器中是否包含语言设置的cookie(下文会涉及到把语言设置的cookie写到用户浏览器中)并且判断网站是非提供该语言;如果不满足上诉判断,则通过http_accept_language获取用户浏览器对语言设置的偏好,并且与网站中提供的语言匹配出来之后设置到用户浏览器的cookie中,以便下次读取用户的请求中判断是否包含已经设置的语言种类。

用户主动选择网站的语言

国际化的网站中都有语言列表供用户选择,这是用户主动根据自己的语言偏好设置对此网站进行访问。
Git@OSC对此的实现是这样的:

  • 在view中
    = link_to change_locale_path(locale: "zh-CN"), class: "ui button text-left" do
      %i.china.flag
      中文简体
    = link_to change_locale_path(locale: "en"), class: "ui button text-left" do
      %i.us.flag
      English
    
    那么用户选择了语言之后经过路由
  • 在routes中
    get '/language/:locale', to: 'home#language', as: :change_locale
    
    不言而明,经过路由分发之后由home_controller的language action处理用户请求。
  • 在控制器中
    def language
      l = params[:locale].to_s.strip.to_sym
      l = I18n.default_locale unless I18n.available_locales.include?(l)
      cookies.permanent[:user_locale] = l
      if request.env["HTTP_REFERER"].present?
        redirect_to :back
      else
        redirect_to '/'
      end
    end
    
    可以看出还会把语言设置写入用户的浏览器cookie,以便下次用户访问的时候能记住其语言偏好。
    以上就是用户主动选择语言种类时的后端处理。

view、controller、model、xxx.js.coffee中对语言配置文件的读取

rails项目config/locals目录下有各种语言以及Gem对应的语言版本配置文件,这些配置文件中rails项目各个模块中的引用不进相同。

  • view controller 例如Git@OSC个人主页中的“加载更多”可以这样子写 #{t('dashboard.active.loading_more_none')}
    例如Git@OSC新建项目成功时控制器返回的flash消息 flash[:notice] ="#{t('flash.new_repo_success')}"
  • model 在model中对语言文配置文件的读取与前面不同。
    例如Git@OSC中“观察者”,“开发者”,“管理员”这些角色字眼写在model中,此时对于语言配置文件的读取应是这样I18n.t('reporter') => REPORTER
  • xxx.js.coffee 写rails项目时很多时候经常把js单独写在与coffeescript文件中,那么rails中的ruby实例变量(在这里指的是从语言配置文件读取文件是产生的实例变量)如何与coffee script使用呢?其实有个gon这样的Gem,他们的自定义是

    Your Rails variables in your JS

    明白了吧?下面我们来看看如何使用。

    • = include_gon 这个推荐写在布局文件中,如果网站的header单独写在一个模版中,那么= include_gon写在这个模版中再好不过了。 - 在controller action中编写gon变量 视图渲染之前是一定是经过某个action(这里不包括nginx缓存前端模版啥的)那么在xxx.js.coffee想要使用rails项目中的变量就显得容易了,结合gon我们只在这对应的action中生成这个变量即可,举个例子。 Git@OSC项目主页中一键有克隆仓库地址的操作,然后有个popup提示用户“复制”或者“已复制”,这些都是写在coffscript文件中的。project_controller show action渲染项目主页,由此我们可以在此action中这样写:
      gon.cp = "#{t('gist.copy')}"
      gon.aready_cp = "#{t('gist.already_copy')}"
      
    • xxx.js.coffee中需要国际化文字 结合上面写的举个例子比较明显
      clip_holder.popup content: gon.aready_cp
      
      直接gon.aready_cp就可以了,方便吧。

网站中时间的国际化

不推荐使用rails的time_ago_in_words帮助方法。原因为这事后端渲染导致了一个问题。比如你打开一个网页,显示的是一分钟前,然而不关闭次网页,一段时间之后应比一分钟之前的“一分钟之前”还要长了,但是后端渲染无法做到动态改变此时间。那么timeago就应运而出现。
添加必要的jquery.timeago.js"这里有各种国际语言供你选择jquery-timeago
首先封装一个帮助方法以便我们在视图中使用app/helpers/time_helper.rb

module TimeHelper
  def timeago(time)
    content_tag(:span, time.iso8601, title: time.iso8601, class: 'timeago')
  end
end

然后在application.js中加入

$('.timeago').timeago()

最后就可以在视图中使用了

"posted #{timeago(post.created_at)} #{t('ago')}"

缓存相关

在rails项目中,国际化的网站有多少中语言就有多少套缓存文件,自然而然我们想到了用不同语言对应的键区分开来。

  • 在ApplicationController中写个帮助方法获取当前用户设置的语言偏好
    helper_method :getlocal
    
    def getlocal
      I18n.locale.to_s
    end
    
  • 生成缓存键的时候加上getlocal 例如在视图中生成的建[@project.id, '@tree_readme@', @path, tree.readme.id, getlocal].join(':')
0 条评论
您想说点什么吗?