如何在Rails中构建i18n yaml文件的结构?

52

我开始在Rails中填充一个英文yaml文件,我可以预见不久之后它会变得混乱无序。有没有一种约定来保持这个文件的组织结构?

到目前为止,我有以下结构:

language:
  resource:
    pages: # index, show, new, edit
      page html elements: # h1, title
  activerecord:
    attributes:
      model:
        property:

现在我有以下几件事情想要适配到这个结构中,但是我不知道如何做:

  1. 导航
  2. 按钮文本 (保存更改、创建账户等)
  3. 来自控制器闪存的错误信息
  4. 如何添加多个单词的键。我应该使用空格还是下划线? 例如:t(".update button")) 还是 t(".update_button")

有一种惯例来组织语言文件结构吗?


现在,您可以使用自己的自定义区域命名空间进行惰性查找:https://github.com/abitdodgy/i18n_lazy_scope - Mohamad
6个回答

63

我发现最好的整体策略是要在某种程度上复制文件结构,这样无论翻译什么,我都可以立即找到调用它的位置。这为我提供了一定的上下文,以便进行翻译。

大多数应用程序翻译都在视图中,因此我的最大顶级命名空间通常是views

我为控制器名称和使用的操作名称或部分创建子命名空间,例如:

  • views.users.index.title
  • views.articles._sidebar.header

这两个示例都应该清楚地说明我们正在翻译应用程序的哪个部分以及要查找哪个文件。

您提到导航和按钮,如果它们是通用的,则属于views.application命名空间,就像它们的视图等效项一样:

  • views.application._main_nav.links.about_us - 我们应用程序主导航部分中的链接
  • views.application.buttons.save
  • views.application.buttons.create - 当需要时,我有一堆这些按钮可用

Flash消息是从控制器生成的,因此它们的顶级命名空间是...controllers! :)

我们采用与视图相同的逻辑:

  • controllers.users.create.flash.success|alert|notice

同样,如果您想提供通用的Flash消息,例如:“操作成功”,则应编写以下内容:

  • controllers.application.create.flash.notice

最后,键可以是任何有效的YAML,但请遵守使用句点作为分隔符和下划线_之间的约定。

现在只剩下一个问题,就是将Rails的翻译放入其自己的命名空间中,以清理我们的顶级命名空间:)


太棒了!谢谢。我已经陷入了这种模式,但不确定我是否走对了路。 - Mohamad
我喜欢这个模式,但很遗憾在Rails中没有内置任何功能来管理flash消息,就像对于活动记录错误(具有级联)进行的那样。我们总是不得不一遍又一遍地输入相同的范围...根本不DRY... - Gauthier Delacroix
2
@GauthierDelacroix 我意识到了这一点,所以我建立了一个宝石(gem),让你可以使用自己的自定义命名空间(lazy lookup):https://github.com/abitdodgy/i18n_lazy_scope - Mohamad

51
我知道已经有一个被接受的答案了,但是这个问题让我有所思考,我想分享一下Rails i18n yml文件的另一种结构供您考虑/批评。
鉴于我想要:
1. 保持默认应用程序结构,以便在视图中可以使用简写的“懒惰”查找,如`t('.some_translation')`, 2. 尽可能避免字符串重复,特别是那些不仅相同,而且上下文/含义也相同的单词, 3. 只需更改一次键就能反映在所有引用它的地方,
对于一个类似于这样的config/locales/en.yml文件:
activerecord:
  attributes:
    user:
      email: Email
      name: Name
      password: Password
      password_confirmation: Confirmation
  models:
    user: User
users:
  fields:
    email: Email
    name: Name
    password: Password
    confirmation: Confirmation
sessions:
  new:
    email: Email
    password: Password

我可以看到有很多重复的内容,像"Email"和"Password"这样的单词在各自的视图中含义明确。如果我决定把"Email"改成"e-mail",那么去逐个更改会很烦人,所以我想重构字符串以引用某种字典。因此,可以在文件顶部添加一个带有一些&锚点的字典哈希表,比如这样:
dictionary:
  email: &email Email
  name: &name Name
  password: &password Password
  confirmation: &confirmation Confirmation

activerecord:
  attributes:
    user:
      email: *email
      name: *name
      password: *password
      password_confirmation: *confirmation
  models:
    user: User
users:
  fields:  
    email: *email
    name: *name
    password: *password
    confirmation: *confirmation
sessions:
  new:
    email: *email
    password: *password

每当您在视图中获得超过一个完全相同的单词/短语时,您可以将其重构为字典。如果基本语言中键的字典翻译对于目标语言没有意义,那么只需更改目标语言中引用值为静态字符串或将其作为目标语言字典的额外条目添加即可。我相信如果它们变得太大和难以处理,每种语言的字典都可以重构为另一个文件。
这种方式的i18n yaml文件结构似乎在我尝试的一些本地测试应用程序中运行良好。我希望美妙的Localeapp将来会提供对这种定位/引用的支持。但是无论如何,所有这些关于字典的讨论肯定不可能是一个原创的想法,所以YAML中是否存在其他锚定引用问题,或者可能与整个“字典”概念有关的其他问题?还是最好完全删除默认后端并使用Redis或其他东西替换它?

2
这是一个很好的问题。也许你可以发布它,看看你会得到什么回应? - Mohamad
1
感谢反馈!我在SO上发布了这个问题链接,让我们看看会发生什么。 - Paul Fioravanti
2
这是增加了额外的间接层(并增加了知识负担),为了一些可以在几个重复出现的情况下使用查找和替换来缓解的最小好处。此外,像电子邮件、密码、确认这样的简单术语的措辞有多经常更改?在应用程序的生命周期内一两次吗? - Magne
现在您可以使用自定义命名空间的惰性查找。这解决了组织本地化文件的最大问题:https://github.com/abitdodgy/i18n_lazy_scope - Mohamad
3
不同意@Magne的观点。如果你已经构建过复杂的Rails应用程序,你会发现与业务领域相关的单词几乎无处不在地重复出现。对我而言,我需要记住“在这个文件中a表示它是一个字典术语”的次数始终少于我添加/更改相同单词的翻译次数。我认为这是一种优雅的解决方案。 - Emil
当然,这是每个特定情况下的成本/效益决策。但不要只是为了做而做。那将是过早优化。等到它成为真正的问题(不仅仅是几个实例),然后处理它将是有意义的。在@Emil的特定情况中,问题是是否更明智地引用一个翻译键与提到的句子,并且不具有指向相同字典的重复键。 - Magne

9

我提出这个问题已经将近两年了,现在我想分享一些观点。我相信最佳的结构是根据MVC角色(模型,视图,控制器)来为翻译命名空间,这可以保持本地语言文件整洁,并防止命名空间冲突(例如,范围en.users可以表示视图或控制器)。

en:
  controllers:
    users:
      show:
        welcome_flash: "Welcome back!"
  mailers:
    users_mailer:
      welcome_email:
        subject: "Good of you to join us"
  views:
    users:
      show:
        notice: "Oh no!

但是,使用整洁的命名空间会破坏Rails中的懒惰查找功能。如果您使用懒惰查找,Rails将自动为您插入命名空间,并且不包括您创建的顶级命名空间(viewscontrollers等)。

例如,t('.welcome_flash')的范围解析为en.users.show。这很糟糕,因为用户没有清晰地定义。它是控制器吗?视图?还是其他什么东西?

为了解决这个问题,我创建了I18nLazyLookup宝石。它允许您在自己的自定义命名空间中使用懒惰查找。

您可以使用t_scoped('welcome_flash')代替t,这将自动将范围解析为en.controllers.users.show。它也适用于视图和邮件程序,并且您可以按照自己的喜好自定义命名空间。


把闪存放在它们被显示的视图中怎么样?所以 welcome_flash 将位于 view.users.show.welcome_flash 下。这样,您可以消除控制器/视图命名空间的区别,并隐含地假定 users.show.notice 是指视图(当然也会有控制器闪存)。似乎可以减少 YAML 文件的混乱程度。 - Magne
1
@Magne 我发现我更喜欢在设置字符串的位置对其进行命名空间。因此,我更喜欢在控制器中对控制器闪存消息进行命名空间。特别是因为大多数情况下,闪存对整个应用程序都是通用的,并且通常存在于布局中,而不属于特定操作。但两种方法都有各自的优点;我只是更喜欢更加明确。 - Mohamad
我还没有找到一种明显优于其他方案的方法。最终,我只是创建了自己的系统,使用我自己设计的I18n后端,使我能够在字符串中引用键。这使我可以按照自己的喜好组织文件,但仍然可以通过指定它们并指向YAML文件中的实际键来提供Rails正在查找的范围。对我来说这还可以,因为这是一个个人项目,但是在这方面缺乏标准化仍然使多人贡献变得困难。 - AvidArcher

9

你的问题不容易回答,而且关于这个主题的资料并不多。我找到的最好的资源是:

所以我将在这里直接尝试回答:

  • 导航

    我认为这里指的是像面包屑、选项卡等导航元素……你需要为它们定义视图,并遵循将所有视图元素移动到单独文件夹views中的规则(请参阅样式指南以了解规则)。

  • 按钮文本(保存更改、创建帐户等)

    视图元素也要放在同一个文件中。如果您在不同的视图中使用相同的按钮,请定义一个公共文件,然后使用它。

  • 控制器闪存的错误消息

    我会使用与视图相同的规则。定义一个单独的目录,将控制器文件包含在其中。

  • 如何添加多个单词的键。我使用空格还是下划线?例如,t(".update button")) 或 t(".update_button")

    我个人更喜欢使用.update_button而不是.update button,因为它更明确地表示这是一个键。


6
直接编辑yaml文件会导致混乱和难以阅读的文件。
此外,如果有一天你想让非开发人员添加新语言,那么提供翻译者访问权限将变得困难。
我建议使用localeapp,它可以生成单个yaml文件。
但是它可以让你轻松地在web界面中查看和管理翻译。
并且可以创建其他翻译者的访问权限。

谢谢。虽然我很感激您的意见,但它并没有回答我的问题。不过这确实是一个有趣的服务,我已经开始使用它了。 - Mohamad
使用Localeapp,您是否将某种语言的所有翻译键放入该单个yaml文件中?还是仍然使用application.*.yml进行某些操作? - Magne

0

这篇文章是几年后的回答,但是这里提供了一个(有点完全)不同的答案。

首先,我不喜欢使用默认命名空间基于文件结构的标准t('.xxx')风格。我也不太喜欢根据DOM结构对翻译进行分类。虽然这是一种很好的方法来处理非常结构化的翻译,但它经常是重复的,而且不太直观。

我更愿意将我的翻译重新分组到更有用的类别中,以便让我的翻译人员更容易工作,因为他们可以处理具体的主题,而不是一些奇怪的样式(有些翻译人员甚至不知道MVC是什么)

所以我的翻译文件结构如下:

fr:
  theme1:
    theme11:
      translationxxx: blabla
  theme2:
    translationyyy: blabla

根据需要,“主题”可以是模型、更抽象的上下文等,这对翻译人员来说最直观。
因为在我的视图中每次编写作用域都很麻烦,所以我在助手中添加了一些便利方法,以便具有基于堆栈的翻译上下文。
  • 通过调用 t_scope([new_scope]pop_t 在我的视图中将翻译范围推入/弹出堆栈
  • 我重写了 t 助手,以使用堆栈的最后一个作用域
翻译作用域方法的代码可在 该答案 中找到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接