Skip to content

7.1 Language Context Rules

berk edited this page Nov 14, 2011 · 3 revisions

Language context rules are used to define a context for any translation of the original phrase. The easiest way to understand it is to start with a simple example.

tr("You have {count||message}", "Inbox count label", :count => 5)

Note: In the above example, we used a transform token to make it easy to evaluate the numeric rule for the base language (which is English in our case). More on it later.

In many languages the translated word “messages” depends on the value of the token {count}. In languages, like English, there are only two context rules that are necessary to correctly translate the above phrase:

  • Rule 1: {count} is 1

  • Rule 2: {count} is not 1

When a translator opens the translation window for the above phrase, he/she can indicate that the phrase translations depend on the context rules (based on some tokens) and provide the translation for each of the rule:

"You have 1 message!"
Context: count is 1

"You have 2 messages!"
Context: count is not 1

But this is not the case with other language. For instant, some Slavic languages, like Russian, have 3 or more rules for number based tokens. Let’s use the following code and translate it into Russian language:

<% 0.upto(5) do |i| %>
   <%= tr("You have {count||message}", "Inbox count label", :count => i) %>
<% end %>

To try out this example, do the following in your tr8n instance:

  • Make sure you are a registered user and have logged into the site

  • Choose a language other than your sites default language

  • Press Ctrl+Shift+I to enable the inline translations or choose that options from the language selector.

  • Right-mouse-click on any one of the highlighted rows above to open the translator window

  • Click on the “generate context rules for this phrase” link:

  • Check the number dependency checkbox in the count token row:

  • Click “Continue…” button to see the translation option for each context rule available for the language:

Based on the language you chose, you will get a list of pre-generated default translations for each context rule for the given phrase. In the above example, we chose Russian language, which has three rules for number dependent tokens.

If you followed the instructions above, you should see your translations looking something like this:

Congratulations! You should now have an idea on how the rules engine works.

Lets take a look now at the various rules that Tr8n rules engine comes with.

Numeric Rules

The example above used the numeric rules dependency. It showed how translators can specify context rules to translate a sentence, but what it didn’t show was the following:

  • Where did Tr8n get the 3 rules defined for Russian language?

  • How on Earth did Tr8n know that the {count} token was dependent on numeric rules?

  • How does it evaluate the rules at run-time to get the appropriate translation for the given phrase?

Let’s start from the beginning. Tr8n engine has three modes: user facing functions, language manager functions and administrative functions. User facing function showed everything that we’ve done during the translation process in the above example. Language manager function is what we are going to look at right now. Admin facing functions is what only site administrators can see. We will look into it later.

If you are a language manager for any particular language, then you have access to the following interface of that language:

There are a few different tools that language managers can use to change each language behavior. In our case, let’s concentrate on the Context Rules section.

If you click on the edit button on the top right corner, you will enter the edit mode of the context rules:

In that screen language managers can change rule definitions for each rule type. As it was mentioned earlier, in English language the rules would simply be:

  • is 1

  • is not 1

Numeric Rule Definition

  • Number rule can be simple or complex. You can use “more” and “less” links to use the double conditions for any rule line.

  • The first operation options are: “is”, “is not”, “ends in”, “does not end in”

  • The linking operator options are: “and”, “or”

  • The last operation options are: “is”, “is not”, “ends in”, “does not end in”

We have answered the first question of where Tr8n got the rules. Now let’s look at how Tr8n knew about the token-rule dependency.

Numeric Rule Configuration

Tr8n Rules engine itself does not know and really care about what rules it has and what it should do with them. The rules of Tr8n can be extended, modified, added and removed by the developers. Take a look at the Rule Engine section in the config/tr8n/config.yml file:

#############################################################################
# Rules Engine Settings
#############################################################################
rules_engine:  
  language_rule_classes:    [Tr8n::NumericRule, Tr8n::GenderRule, Tr8n::GenderListRule, Tr8n::DateRule, Tr8n::ValueRule]
  data_token_classes:       [Tr8n::Tokens::DataToken, Tr8n::Tokens::HiddenToken, Tr8n::Tokens::MethodToken, Tr8n::Tokens::TransformToken]
  decoration_token_classes: [Tr8n::Tokens::DecorationToken]
  allow_nil_token_values:   true
  numeric_rule:             
    token_suffixes:         [count, num, age, hours, minutes, years, seconds]
    object_method:          to_i
  gender_rule:  
    token_suffixes:         [user, profile, actor, target, partner, parent, child, sibling, manager]
    object_method:          gender
    method_values:  
      female:               female
      male:                 male
      neutral:              neutral
      unknown:              unknown
  gender_list_rule:                 
    object_method:          size
    token_suffixes:         [list]
  date_rule:                
    token_suffixes:         [date]
    object_method:          to_date
  value_rule:               
    token_suffixes:         *
    object_method:          to_s

The most important parts of the file that are applicable to the Number Rule are in bold. When Tr8n boots up for the first time, it looks at the list of rule classes under the language_rule_classes key and loads the classes it needs. Each Rule class extends the basic Tr8n::LanguageRule class, which provides the basic rule behavior. The rule configuration options and UI are provided by the rules themselves. So it is fairly easy to add a new rule to the engine.

Since Tr8n comes with the 6 default rules, I have added sections in the rules_engine config section where these rules can be configured. Each rule may need some ability to be customized for the site where Tr8n will be used.

Number Rule is implemented by the Tr8n::NumericRule class and uses the numeric_rule section of the rules_engine configuration.

  • token_suffixes indicates the set of token names that have numerical values and can be translated using numeric context rule

  • object_method indicates the method that must be applied on the token object in order to get its numerical value

This means that any of the following tokens will give user an option to translate the phrase based on the numeric value of the token: {count}, {num}, {message_num}, {user_count}, etc… Notice that in our above example we used a simple number for the token value. It worked great because, in Ruby, the numbers are objects that respond to to_i method as well:

<syntaxhighlight lang=“ruby”> tr(“You have {count||message}”, “Inbox count label”, :count => 5) </syntaxhighlight>

But the value of the {count} could have been a string or any other object, as long as it implemented the to_i method. If your site has other conventions, you can easily change the numeric_rule definition and everything would still work.

Gender Rules

Consider the following example:

tr("Dear {user},", "Reference to a user in a heading of an email", :user => [current_user, :name])

tr("{user} updated {user| his, her} profile.", "Newsfeed story heading", :user => [some_user, :name])

tr("{user| Added On:}", "Label for when the user was added to the site", :user => some_user)

All of the above examples have dependency on the gender of the {user} token. Each sentence will have 2 or 3 translations based on the rule definition of the language the phrase is translated to.

Let’s take the second example and look at what it would be translated to in Russian:

Choose the “generate context rules” link and select “gender” for the “user” token:

Translating the above sentence into Russian will require setting up a dependency on the {user} token. Russian language was configured to have 3 rules to handle male, female and unknown gender cases.

Some languages can treat unknown genders as male genders. The gender rules for that language can simply state:

  • user is a female

  • user is not a female

Gender Rule Definition

  • A rule consists of 2 parts: operator and gender.

  • The operator options are: “is”, “is not”

  • The gender options are: “a male”, “a female”, “neutral”, “unknown”

Gender Rule Configuration

rules_engine:
  ...  
  gender_rule:  
    token_suffixes:         [user, profile, actor, target, partner, parent, child, sibling, manager]
    object_method:          gender
    method_values:  
      female:               female
      male:                 male
      neutral:              neutral
      unknown:              unknown
   ...

Gender Rule is implemented by the Tr8n::GenderRule class and uses the gender_rule section of the rules_engine configuration.

  • token_suffixes indicates the set of token names that can be translated using gender context rule

  • object_method indicates the method that must be applied on the token object in order to get its gender value

  • method_values maps the return values from the object_method that would tell the rule if it met the evaluation criteria

List Rules

Consider the following examples:

user1 = User.new(:name => "Michael", :gender => 'male')
user2 = User.new(:name => "Amos", :gender => 'male')
user3 = User.new(:name => "Tina", :gender => 'female')
users = [user1, user2, user3]

tr("{user_list} updated {user_list| his profile, her profile, their profiles}.", "Newsfeed story title", :user_list => [users, :name])

tr("{user_list} updated {user_list| his profile, her profile, their profiles}.", "Newsfeed story title", :user_list => [[user1], :name])

tr("{user_list|| likes, like} this post.", "Newsfeed story title", :user_list => [users, :name])

tr("{user_list|| likes, like} this post.", "Newsfeed story title", :user_list => [[user1], :name])

The above examples only registered two phrases in the system. Let’s take the first example and look at what it would be translated to in Russian:

Translating the above sentence into Russian will require setting up a dependency on the {user_list} token. Russian language was configured to have 4 rules to handle male, female, unknown gender cases for a single element in the list and one rule for plural case.

Some languages, like Hebrew, can specify the genders for the plural cases as well:

  • user_list contains at least 2 elements that are all male

  • user_list contains at least 2 elements that are all female

  • user_list contains at least 2 elements that are of mixed gender

List Rule Definition

  • List rule can be simple or complex. You can use “more” and “less” links to use the double conditions for any rule line.

  • First operator options: “contains”

  • First operator values: “one element”, “at least 2 elements”

  • Second operator options: “that is”, “that is not”, “that are”, “that are not”

  • Second operator values: “male”, “female”, “unknown”, “neutral”, “all male”, “all female”, “of mixed gender”

List Rule Configuration

rules_engine:
  ...  
  gender_list_rule:                 
      object_method:          size
      token_suffixes:         [list]
   ...

List Rule is implemented by the Tr8n::GenderListRule class and uses the gender_list_rule section of the rules_engine configuration. It is important to note that the list rule works together with the gender rule to use the gender method configurations.

  • token_suffixes indicates the set of token names that can be translated using gender context rule

  • object_method indicates the method that must be applied on the token object in order to get its gender value

List View Options

When a token contains a collection of values instead of a single value (whether it needs to be rule dependent or not) it has some nice extra features in terms of how it can be presented to the user.

users = [user1, user2, user3] 
tr("Hello {user_list}!", "Example fragment", {:user_list => [ users, :name]}}

First element is an array, the rest of the elements are similar to the regular tokens lambda, symbol, string, with parameters that follow:

tr("{user_list} joined Geni", "", 
   {:user_list => [users, 
      [:name],      # this can be any of the value methods
      { :expandable => true, 
         :to_sentence => true, 
         :limit => 4, 
         :separator => ',',
         :translate_items => false,
         :minimizable => true}
       ]
  })

As you can see from the definition above, if you make the second parameter of the tr function an array as well, you can provide a third parameter that would indicate how to display the array values. Let’s try the following example:

users = 1.upto(10).collect{|i| User.new(:name => "User #{i}", :gender => 'male')}
tr("{users} joined the site!", "Newsfeed story header", :users => [users, :name])

The above example uses the second parameter as method symbol, which indicates that default display params will be used, resulting in:

User 1, User 2, User 3, User 4 and 6 others joined the site!

Where ”6 others” is a link that expands the list of user names.

Date Rules

Date Rule is a very simple rule. It is used to evaluate the default as well as the translated phrases using the date object. For instance:

tr("{user} {birth_date| celebrated, celebrates, will celebrate} {user|his,her} birthday on {birth_date}!", "Birthday notification", :user => current_user, :birth_date => Date.today)

This looks a little cryptic at first because it actually compresses three sentences and a bunch of translations into a simple phrase. Let’s take a closer look:

  • token_suffixes indicates the set of token names that have numerical values and can be translated using numeric context rule

  • object_method indicates the method that must be applied on the token object in order to get its numerical value

Let’s try the following variations:

tr("{user} {birth_date| celebrated, celebrates, will celebrate} {user|his,her} birthday on {birth_date}!", 
"Birthday notification", :user => current_user, :birth_date => [Date.today - 1.day, :strftime, "%D"])

tr("{user} {birth_date| celebrated, celebrates, will celebrate} {user|his,her} birthday on {birth_date}!", 
"Birthday notification", :user => current_user, :birth_date => [Date.today, :strftime, "%D"])

tr("{user} {birth_date| celebrated, celebrates, will celebrate} {user|his,her} birthday on {birth_date}!", 
"Birthday notification", :user => current_user, :birth_date => [Date.today + 1.day, :strftime, "%D"])

All those variations have only created a single translation key in the default language. But translating them into other languages is a whole different story.

Translating the above sentece into Russian will require setting up a dependency on the {user} token, as well as {date} token. Both of the tokens have 3 rules and will result in the total of 9 translations.

Date Rule Definition

  • A rule consists of 2 parts: operator and timeframe.

  • The operator options are: “is in the”, “is not in the”

  • The timeframe options are: “past”, “present”, “future”

Date Rule Configuration

rules_engine:
  ...  
  date_rule:                
      token_suffixes:         [date]
      object_method:          to_date
  ...

Date Rule is implemented by the Tr8n::DateRule class and uses the date_rule section of the rules_engine configuration.

  • token_suffixes indicates the set of token names that have date values and can be translated using date context rule

  • object_method indicates the method that must be applied on the token object in order to get its date value

Value Rules

Value rules are used for contextual translations of phrases where other parts of the phrase depends on the value of the token. Those rules are usually used in combination with the Language Case Rules, which will be described in the later sections.

Value Rule Definition

  • A rule consists of 2 parts: operator and value.

  • The operator options are: “is”, “is not”, “starts with”, “does not start with”, “ends in”, “does not end in”

  • The value can be anything provided by the language manager

Value Rule Configuration

rules_engine:
  ...  
  value_rule:                
      token_suffixes:         *
      object_method:          to_s
  ...

Value Rule is implemented by the Tr8n::ValueRule class and uses the value_rule section of the rules_engine configuration.

  • token_suffixes indicates that it can be applied to any token

  • object_method indicates the method that must be applied on the token object in order to get its date value