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

时间:2012-06-19 08:50:50

标签: ruby-on-rails internationalization refactoring structure yaml

This StackOverflow问题让我对Rails i18n文件的优秀结构有了深思,所以我想我会分享另一个结构来重构Rails i18n yml文件供您考虑/批评。

鉴于我想

  1. 保留默认的应用结构,以便在我的视图中使用简写的“懒惰”查找,例如t('.some_translation'),并了解应用中翻译的使用情况,
  2. 避免尽可能多的字符串重复,特别是单词不仅相同,但也有相同的上下文/含义,
  3. 只需更改一次密钥就可以反映出它所引用的任何地方,
  4. 对于 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
    

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

    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中的锚点引用还有其他问题吗,或者可能只是整个“字典”概念?或者,如果你有超出Rails默认i18n约定的需求,那么完全删除默认后端并用Redis或其他东西替换它会更好吗?

    修改

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

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

    dictionary:
      name: &name Name
    
    activerecord:
      attributes:
        name: *name
        user:
          name: *name
        product:
          name: *name
    

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

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

    dictionary:
      name: &name Name
      full_name: &full_name Full Name
    
    activerecord:
      attributes:
        name: *name
        user:
          name: *full_name
        product:
          name: *name
    

    选项2 :同时更改用户模型字段名称。这需要更改代码中对此键的任何引用,以及change_table / rename_column迁移。

    dictionary:
      name: &name Name
      full_name: &full_name Full Name
    
    activerecord:
      attributes:
        name: *name
        user:
          full_name: *full_name
        product:
          name: *name
    

    选项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
    

    第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
    

    选项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
    

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

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

4 个答案:

答案 0 :(得分: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'。
  4. 我们无法用共享的“字典”处理这种情况。当然我们的语言环境文件是干的,但我们的语言/翻译问题与我们的开发人员关注的问题大不相同(遗憾的是)。

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

答案 1 :(得分: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}

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

我知道这可能无法回答关于干燥翻译的“正确”方式的更多哲学问题,但我认为这是使用& amp;的更好的替代方法。标签参考YML hack。

答案 2 :(得分:0)

改进重新呈现YAML文件,特别是对于那些拥有多种模型的人:

ru:
  dictionary:
    name: &name "Имя"
    title_ru: &title_ru "Заголовок (ru)"
    title_en: &title_en "Заголовок (en)"
    content_ru: &content_ru "Содержание (ru)"
    content_en: &content_en "Содержание (en)"
    role: &role "Роль"
    created_at: &created_at "Создано в"
    updated_at: &updated_at "Обновлено в"
    published: &published "Опубликовано"

    nomination: &nomination
      name: *name
      title_ru: *title_ru
      title_en: *title_en

    post: &post
      content_ru: *content_ru
      content_en: *content_en
      published: *published

    dates: &dates
      created_at: *created_at
      updated_at: *updated_at

  activerecord:
    attributes:
      article:
        <<: *nomination
        <<: *post
        <<: *dates

      user:
        <<: *dates
        role: *role
        email: "Электропочта"

用户link

答案 3 :(得分:0)

我刚刚发布了名为dry_i18n的宝石:https://rubygems.org/gems/dry_i18n

我创建了这个gem来解决你要求的问题。有了这个宝石,你甚至可以重复使用带插值的键并嵌套它们。

我希望它很有用。