【问题标题】:Rails app logic internationalizationRails 应用逻辑国际化
【发布时间】:2015-07-29 09:10:16
【问题描述】:

我正在开发一个 Rails 应用程序,该应用程序将在不同国家/地区运行单独的实例。国际化处理语言问题,但是当有一些小的逻辑变化时,我不知道如何处理。一个例子是支付方式——在每个国家,不同的选项将显示给具有不同集成的用户。另一个例子是工资——税收和社会保障因国家而异,所以我们需要考虑到这一点。

差异并不大,所以总的来说,我不想为每个国家/地区创建不同的 git 分支,但我可能错了。

我目前正在考虑的解决方案是为每个国家/地区设置不同的环境 - “production_germany”、“production_france”等,并据此加载包含所有必要变量的不同 yml 文件需要这个国家。然后创建一些“管理器”类,该类将决定要加载哪些特殊类以及要在视图上显示哪些内容(无论哪里有差异)。例如,我可以在“salary_calculation/de”中设置德国的工资计算类,在“salary_calculation/fr”中设置法国的工资计算类,然后只加载我需要的。

您的解决方案是什么?

【问题讨论】:

  • 我回答之前的一个问题,不同的国家真的需要共享数据吗?你真的期望国家之间的差异有多大?如果没有,那么您可以使用不同的数据库,如果没有,那么您可以将应用程序构建为多租户应用程序(谷歌为“多租户 Rails 应用程序”),告诉我更多信息,我会写更多信息:)
  • 您好,感谢您的提问。这与数据库无关,应用程序将位于不同的服务器上,它们不需要共享数据。但根据法规和一些当地设置,应用程序中的某些逻辑需要在不同国家/地区进行更改。问题是如何以干净的方式做到这一点?

标签: ruby-on-rails internationalization


【解决方案1】:

我会尽可能多地使用 Rails 的I18n。如果您还没有,请查看rails-i18n

对于 Rails 无法处理的特殊情况,我会像 Rails 自己那样处理。例如,在您的config/locales 中,您可能对某个国家/地区的税收进行了一些配置:

en:
  taxes:
    name: Tax
    rate: 0.065
de:
  taxes:
    name: USt
    rate: 0.19

然后您编写一个TaxesHelper 模块,该模块将使用语言环境文件中的信息来显示税率。看看 Rails NumberHelper 包。

如果您实际上使用税率之类的东西进行计算,我会在您进行计算的模型中保留该因素。这样,当税率发生变化时,您的计算就不会混乱。假设您有以下模型:

class Invoice < ActiveRecord::Base
  # Injected by AR
  # attr_accessor :net_price
  # attr_accessor :tax_rate
  def gross_price
    net_price * tax_rate
  end

  before_create do
    tax_rate = I18n.t(:rate, scope: [ :taxes ]).to_f
  end
end

这样一来,在特定国家/地区的税率发生变化后,您的总价就不会因保留发票而发生变化。

【讨论】:

  • 谢谢!。不过我想澄清一些事情,因为我不是 100% 确定什么是正确的方法。在英国,税率将为 0.065,并将存储在 en locale 中。如果我希望网站有德语翻译怎么办?如果用户正在查看英国网站的德语翻译,我不想使用 0.19 的税率。所以也许我会创建一个新的“en-de”语言环境,用于英国网站的德语翻译。但是我会重复“de”和“en-de”语言环境之间的 90% 的翻译。如何避免?
  • 我想在那种情况下它不再是真正的 i18n 了。方法是相同的,只是您使用自定义配置而不是“滥用”Rails 的 i18n。见guides.rubyonrails.org/configuring.html#custom-configurationjustinweiss.com/blog/2014/08/25/…
  • 另外,在您的情况下,“de”和“en”实际上是不够的,因为您需要针对国家/地区而不是语言进行配置。
【解决方案2】:

Amiuhle 就如何处理静态参数化(语言、税率等)给出了一个很好的提示,

当您需要更多业务逻辑时,事情会变得更加复杂,并且没有万能的解决方案,请务必了解您可以使用的工具并确保您适合正确的工具,这里还有一个似乎在 ruby​​ 社区中没有得到充分利用:

依赖注入

例如:http://solnic.eu/2013/12/17/the-world-needs-another-post-about-dependency-injection-in-ruby.html

这种模式通常适用于管理支付提供商网关等

class Transaction < ActiveRecord::Base
  def gateway_class
    I18n.t(:class_name, scope: [ :payment_provider ]).constantize
  end
  def gateway
    @gateway ||= gateway_class.new(self)
  end
end

I18n 根据国家/地区参数化支付提供商类:

en:
  payment_provider:
    class_name: PaymentProvider
de:
  payment_provider:
    class_name: AnotherPaymentProvider

并且您确保支付提供商类响应相同的公共 API,因此对于整个应用程序而言,无需知道使用哪个支付提供商,只需接口支付提供商类即可。

另一个好处是您可以创建一个FakePaymentProvider 类用于测试和开发,这将大大加快您的测试速度——即使您通常使用诸如vcr 之类的网络模拟工具,您仍然可以使用vcr 之类的东西来测试集成,并且只是集成,与应用架构的其余部分解耦。

实际上,看看这里:https://github.com/activemerchant/active_merchant 有人已经为这个计划做了很多工作:)

【讨论】:

  • payment_providerpayment_providers 在 ruby​​ 与 yaml 中。无法编辑,因为它必须至少有 6 个字符...
  • 谢谢,依赖注入似乎很重要!但是,也许将其放在语言环境中并不是一个很好的选择,因为这将取决于用户选择的语言:在网站将运行的每个国家/地区,它将具有母语和英语翻译。我认为一个更好的解决方案是将所有位置特定的设置存储在一个单独的文件中,比如“france.yml”。我还没有弄清楚如何启动 rails 服务器并告诉它从“france.yml”而不是“germany.yml”加载设置。
  • Kazmin,假设它们都运行不同的主机,你需要有一个 env 变量告诉系统运行什么配置
  • 谢谢,只是要确保这是正确的做法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-25
  • 1970-01-01
相关资源
最近更新 更多