為 Rails 項(xiàng)目添加動(dòng)態(tài) I18n 內(nèi)容

場景:對(duì)于一個(gè)已經(jīng)做好靜態(tài) I18n 的 Rails 項(xiàng)目,需要對(duì)動(dòng)態(tài)數(shù)據(jù)內(nèi)容也適配國際化。

首先,動(dòng)態(tài)內(nèi)容的數(shù)據(jù)肯定是存在數(shù)據(jù)庫中的,并且字段名也采用統(tǒng)一的 fieldname_#{I18n.locale} ,方便統(tǒng)一管理。

問題的核心是如何在盡量不修改現(xiàn)有代碼的情況下動(dòng)態(tài)讀取模型某些字段的當(dāng)前 locale 的值。

比如:原來的 user.name #=> Marry ,現(xiàn)在需要根據(jù)當(dāng)前 locale (比如 cn)變成 user.name # => 翠花 。

首先接口肯定不能改,view 層中有很多很多的 user.name 的調(diào)用,就算用批量修改的方式改為類似 user.name_#{I18n.locale} 的寫法,
也很不優(yōu)雅,何況不只是 name 屬性需要國際化,以后每增加一個(gè)字段或模型的國際化都將成為很大的負(fù)擔(dān)。

說到如何在原有類的基礎(chǔ)上增加功能,那自然會(huì)想到使用裝飾器模式了。

關(guān)于裝飾器模式的實(shí)現(xiàn),一種方式是使用 delegate :

require 'delegate'

class User < ApplicationRecord
  # attribute name
end

class InternationalUserDecorator < SimpleDelegator
  def name
    __getobj__.send("name_#{I18n.locale}")
  end
end

user = InternationalUserDecorator.new(User.find(12345))

I18n.locale = :en
user.name # => 'Marry'

I18n.locale = :cn
user.name # => '翠花'

但是這顯然是不行的,因?yàn)檫@需要修改每一個(gè) User 實(shí)例的生成,使用 Decorator 去顯式地包裝它。

類似的,還有一種通過繼承 module 的方式,同樣需要顯示的修改每一個(gè)模型的實(shí)例,這樣的改動(dòng)對(duì)原代碼改變很大,也不能使用。

module EnglishUser
  def name
    "Marry"
  end
end

module ChineseUser
  def name
   "翠花"
  end
end

user = User.find(123)
user.extend(EnglishUser) #=> name "Marry"
user.extend(ChineseUser) #=> name "翠花"

可見,需要在獲取模型實(shí)例時(shí)進(jìn)行修改的思路是行不通,如果項(xiàng)目一開始就使用倉儲(chǔ)模式的話,修改起來會(huì)容易很多,不過這超出了本文的范圍。

想要盡可能小的修改原代碼,那只能使用元編程了,我們需要一個(gè) Module,來動(dòng)態(tài)生成 locale 對(duì)應(yīng)的 field 供模型調(diào)用。

當(dāng) localecn 的時(shí)候, user.name => user.name_cn
當(dāng) localeen 的時(shí)候, user.name => user.name_en

當(dāng)然,不是模型所有的 field 都需要做國際化,必須可以指定需要國際化的字段

client 端的代碼應(yīng)該是這樣的:

class User < ApplicationRecord
  # attributes :name, :position, :age
  include I18nDecorator.new(:name, :position)
end

這里的難點(diǎn)在于,需要傳參數(shù)給這個(gè) Module,可 include 的時(shí)候是不能傳參的。
在這里,我們把 I18nDecorator 定義為 Module 的一個(gè)子類,這樣就可以通過 new 的時(shí)候的 initialize 方法中,
對(duì)父類進(jìn)行元編程,動(dòng)態(tài)定義 Module 的方法,這樣就能在模型中進(jìn)行調(diào)用了。

class I18nDecorator < Module
  def initialize(*attrs)
    super() do
      attrs.each do |attr|
        define_method attr do
          send("#{attr}_#{I18n.locale}")
        end
      end
    end
  end
end

現(xiàn)在,當(dāng)任何 User 類的實(shí)例調(diào)用 name 或者 position 的方法時(shí),就會(huì)被 I18nDecorator 動(dòng)態(tài)轉(zhuǎn)發(fā)給 name_cn 或者 position_en 的屬性上了。

就這樣,一共十幾行代碼,我們完成了一個(gè) Rails 項(xiàng)目簡單的動(dòng)態(tài) I18n 的功能,以后需要增加模型或?qū)傩缘臅r(shí)候,都只需要 include 這一行代碼就可以了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,533評(píng)論 19 139
  • 9.2 添加國際化和本地化 Django提供了完整的國際化和本地化支持。它允許你把應(yīng)用翻譯為多種語言,它會(huì)處理特定...
    lakerszhy閱讀 1,296評(píng)論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,637評(píng)論 18 399
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,872評(píng)論 25 709
  • 剛參加工作的年輕人,不少仍是為了滿足“馬斯諾五大條理理論”中的最低需求,就是保留必要而工作,工作后贏利養(yǎng)活本身就意...
    智盛心法閱讀 338評(píng)論 0 0

友情鏈接更多精彩內(nèi)容