使用词典重构Ruby on Rails i18n YAML文件

Pau*_*nti 23 refactoring yaml structure ruby-on-rails internationalization

这个 StackOverflow问题给了我一些关于Rails i18n文件的良好结构的思考,所以我想我会分享另一个结构来重构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
Run Code Online (Sandbox Code Playgroud)

我可以看到存在重大的重复,并且诸如"电子邮件"和"密码"之类的词语的上下文是明确的并且在它们各自的视图中具有相同的含义.如果我决定将"电子邮件"更改为"电子邮件",那么必须去更改它们会有点烦人,所以我想重构字符串以引用某种字典.那么,如何使用&这样的锚点将字典哈希添加到文件的顶部:

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
Run Code Online (Sandbox Code Playgroud)

您仍然可以继续使用静态字符串(例如上面的"用户"),但只要您在视图中获得完全相同的单词/短语的多个实例,就可以将其重构为字典.如果基本语言中的键的字典翻译对目标语言没有意义,则只需将目标语言中的引用值更改为静态字符串,或将其作为额外条目添加到目标语言的字典中.我确信每种语言的字典都可以重构成另一个文件,如果它们变得太大而且不实用(只要它然后在翻译文件的顶部重新导入,这样引用就可以了).

这种结构化i18n yaml文件的方式似乎适用于我尝试过的一些本地测试应用程序.我希望美妙的Localeapp将来会为这种锚定/引用提供支持.但无论如何,所有这些词典谈话都不可能是一个原创的想法,所以在YAML中是否存在锚定引用的其他问题,或者可能只是整个"词典"概念?或者,如果你有超出Rails默认i18n约定的需求,那么完全删除默认后端并用Redis替换它会更好吗?

编辑:

我想尝试解决下面评论中提到的tigrish的工作流程示例,而不是作为他答案下面的另一条评论.如果我似乎没有得到积分或者我只是天真,请原谅我:

第1点:您有一个ActiveRecord模型的通用"名称"属性,它们都只是指向通用字典的名称:

dictionary:
  name: &name Name

activerecord:
  attributes:
    name: *name
    user:
      name: *name
    product:
      name: *name
Run Code Online (Sandbox Code Playgroud)

第2点:仅需要更改用户模型的名称.其他名字保持不变.

选项1:在后端保持模型字段名称相同,只需更改它指向的前端翻译.

dictionary:
  name: &name Name
  full_name: &full_name Full Name

activerecord:
  attributes:
    name: *name
    user:
      name: *full_name
    product:
      name: *name
Run Code Online (Sandbox Code Playgroud)

选项2:也更改用户模型字段名称.这将需要在代码中更改对此键的任何引用,以及change_table/ rename_columnmigration.

dictionary:
  name: &name Name
  full_name: &full_name Full Name

activerecord:
  attributes:
    name: *name
    user:
      full_name: *full_name
    product:
      name: *name
Run Code Online (Sandbox Code Playgroud)

选项3:如果您想要非常彻底,请将"名称"中包含的信息重构为单独的数据库/ Activemodel字段,这些字段需要新的字典条目和迁移.您可以决定如何显示"全名"的视图:

dictionary:
  name: &name Name
  name_prefix: &name_prefix Prefix
  first_name: &first_name First
  middle_name: &middle_name Middle
  last_name: &last_name Last
  name_suffix: &name_suffix Suffix

activerecord:
  attributes:
    name: *name
    user:
      name_prefix: *name_prefix
      first_name: *first_name
      middle_name: *middle_name
      last_name: *last_name
      name_suffix: *name_suffix
    product:
      name: *name
Run Code Online (Sandbox Code Playgroud)

第3点:任何人因任何原因需要翻译,在这种情况下需要营销.我将从第2点选项1的例子开始

选项1:模型字段名称相同,只需更改前端翻译.

dictionary:
  name: &name Name
  full_name: &full_name Full Name
  funky_name: &funky_name Ur Phunky Phresh Naym

activerecord:
  attributes:
    name: *name
    user:
      name: *full_name
    product:
      name: *name
sessions: # Sign up page keys
  new:
    name: *funky_name
Run Code Online (Sandbox Code Playgroud)

选项2:由于某种原因,迫切需要将"Funky name"保存到数据库中.username如果没有人反对(或者funky_name由于某种原因营销坚持),让我们称之为.

dictionary:
  name: &name Name
  full_name: &full_name Full Name
  funky_name: &funky_name Ur Phunky Phresh Naym

activerecord:
  attributes:
    name: *name
    user:
      name: *full_name
      username: *funky_name
    product:
      name: *name
sessions: # Sign up page keys
  new:
    name: *name
    funky_name: *funky_name
Run Code Online (Sandbox Code Playgroud)

是的,所以我承认我不知道自己在做什么,但是,为了理解为什么这种在Haml中使用i18n的方式在Rails应用程序中是一个坏主意,我愿意被公开击落.难以阅读?维修噩梦?如果我使用(我认为是)语言的一个特征,它真的被认为是"黑客文件格式"吗?

再次感谢tigrish让我把这一切都拿出来.

tig*_*ish 11

TLDNR; 不要破解你的文件格式,改进rails帮助程序并帮助建立标准化的密钥结构!

TLDR;

不想在你的游行中下雨,但我对这种技术有一些问题.在哪里使用点快捷键以及铁轨助手的键结构如何不同的困境可能有点令人费解.

据我了解,问题基本上是关于干燥你的语言环境文件和使用YAML语言的功能来实现这一点.

首先,锚点只能保证适用于YAML,所以这个解决方案不能一般地应用于I18n.如果使用不同的后端,此技术可能不可行.无论是SQL,Redis还是Json,我都不知道它们中是否有任何类型的符号链接功能.而且,事实上,翻译实际上是重复的.

我遇到的第二个也是更大的问题是关于语言学.您的示例表明所有这些术语在上下文和含义上完全相同.不幸的是,这只是非常简单的例子.

毫无疑问,随着您的应用程序的增长或添加其他语言,您会发现Person的"name"属性必须与Book的"name"属性区别开来,在英语中我们称之为"title" - 好的,这个例子真的很复杂;)但是当你混合使用越来越多的语言时,这种情况确实经常发生,理想情况下,我们需要一种处理它的通用方法.

我认为在很大程度上,复杂性来自使用不同默认值的rails帮助程序,而没有关键结构的约定.

回到你的例子,你提到了我认为非常不同的两件事:activerecord属性翻译使用rails助手和使用点快捷键的视图翻译.

让我举一个超常频繁的工作流程示例:

  1. 在这种情况下,您使用用户的"名称"字段创建表单,您希望使用通用的"名称"属性转换(label_tag应使用类似:'attributes.name').这是最简单的DRYest案例,可以帮助您快速启动和运行,批量转换简单属性.
  2. 不久之后,您决定只为此模型将用户的"名称"翻译为"全名",这样您才能在label_tag的查询调用中创建一个具有更高优先级的新翻译(例如:'activerecord.attributes.users.name' ))
  3. 后来,营销人员有一个绝妙的想法,就是在这个页面上显示这个字段的标签为"输入你时髦的新名字"(并且只在这个页面上).我们不再描述name属性了,我们正在描述这种形式的特定视图; 这是点快捷方式转换为:'.form.name'的地方,如':users.new.form.name'.

我们无法用共享的"字典"处理这种情况.当然我们的语言环境文件会干,但我们的语言/翻译问题与我们的开发人员关注点(遗憾地)有很大不同.

从好的方面来说,我们可以更清楚地了解我们正在描述的内容,并在我们的关键结构和工具中反映出来 - 这对我来说是前进的方向!:)


小智 5

我只是发布一个名为i18n-recursive-lookup的gem,它允许定义通过引入特殊的嵌入式标记$ {}来包含对其他定义的嵌入式引用

https://github.com/annkissam/i18n-recursive-lookup

使用它你可以将你的例子重构为:

dictionary:
  email: Email
  name: Name
  password: Password
  confirmation: Confirmation

activerecord:
  attributes:
    user:
      email: ${dictionary.email}
      name: ${dictionary.name}
      password: ${dictionary.password}
      password_confirmation: ${dictionary.confirmation}
  models:
    user: User
users:
  fields:  
    email: ${dictionary.email}
    name: ${dictionary.name}
    password: ${dictionary.password}
    confirmation: ${dictionary.confirmation}
sessions:
  new:
    email: ${dictionary.email}
    password: ${dictionary.password}
Run Code Online (Sandbox Code Playgroud)

好消息是,一旦编译,翻译就会被写回翻译存储,以便所有插值/递归查找都会发生一次.

我知道这可能无法回答关于干燥翻译的"正确"方法的更多哲学问题,但我认为这是使用&label参考YML hack的更好的替代方法.