◷ Reading Time: 8 minutes
Scope
When business rules are used in many different locales (countries in which different languages are spoken), then it makes sense to model and designs them in a way that multilingual concepts apply, to the following topics:
- Notification messages
- Values (string values)
- Options (option for values in the glossary)
Let’s consider the following simple business rules:
If Accounting Concept is Payable then write Account concept is cash
As it is highlighted, the two parts of the rule must support multilingual.
Sample business rules
Let’s assume we want to model this rule in two languages – English and Spanish.
English
Let’s model this rule in a decision table that has an input parameter named ac:

In XML this will be modeled as follows:
<DecisionTable name="cash decision table">
<Columns>
<Condition name="Accounting Concept" expression="ac == $value" />
<Action name="Write Message" expression="$value" type="Notice" />
</Columns>
<Data>
<Row>
<Value>Payable</Value>
<Value>Account concept is cash</Value>
</Row>
</Data>
</DecisionTable>
Spanish
Let’s model this rule in a Decision Table that has an input parameter named ac:

In XML, this will be modeled as follows:
<DecisionTable name="cash decision table">
<Columns>
<Condition name="Concepto de Contabilidad" expression="ac == $value" />
<Action name="Escribe un mensaje" expression="$value" type="Notice" />
</Columns>
<Data>
<Row>
<Value>Pagadero</Value>
<Value>Concepto de cuentas es dinero en efectivo</Value>
</Row>
</Data>
</DecisionTable>
Concept
Values
In both rules, in the condition column:
- Payable
- Pagadero
are both referring to the same thing, but in different languages. In resolving the values on evaluation, all translations (e.g., English, Spanish, German, etc.) must be resolved against the same reference value. This reference value can be either a key (e.g., code, id, number, etc.) or a default main locale (e.g., in English, which is Payable).
Let’s say we are going to assume 1 is an identifier or code we want to be resolved for all the translations of Payable.
const string payableValue = "1";
var termsTranslation = new NameValueCollection
{
{"Payable", payableValue},
{"Pagadero", payableValue}
};
As you can see, at this stage all of the translations of the Payable term are registered against payableValue.
When the engine (i.e. RuntimeEngine) is created, simply use the Entries property and add these translations:
foreach (var term in termsTranslation.AllKeys)
engine.Entries.Add(term, string.Format("'{0}'", termsTranslation[term]), false);
Messages
Notification is smart and will simply pick the correct message based on the current Thread’s UI Culture. Therefore, all of the messages must be registered for the target culture:
var msg1_spanish = new Dictionary<string, string> { { "MSG_1", "Concepto de cuentas es dinero en efectivo" } };
var msg1_english = new Dictionary<string, string> { { "MSG_1", "Account concept is cash" } };
engine.OnRunning = (e) =>
{
e.ExecutorSetup.MultilingualMessages.Register(msg1_spanish, CultureInfo.GetCultureInfo("es-ES"));
e.ExecutorSetup.MultilingualMessages.Register(msg1_english, CultureInfo.GetCultureInfo("en-US"));
};
Please note that in this example, MSG_1 is the key to different translations of the message corresponding to the business rule. Make sure in your decision table you set useMessageId, or if you use FlexRule Designer then you need to set it as an Action of the Decision Table:

Final Decision Table
In the final Decision Table, we do not add a direct message, instead, we will be using a message code or id. Now for the values, all the translations of the term (i.e., Payable) will be accepted.

Putting it all together
public void test_multilingual_value_and_message()
{
const string dt = @"
<DecisionTable name=""DT1"">
<Declaration>
<Define direction=""in"" name=""ac"" />
</Declaration>
<Columns>
<Condition name=""Accounting Concept"" expression=""ac==$value"" />
<Action name=""Write Message"" expression=""$value"" type=""notice"" notice=""information"" useMessageId=""true""/>
</Columns>
<Data>
<Row>
<Value>Payable</Value>
<Value>MSG_1</Value>
</Row>
</Data>
</DecisionTable>
";
var engine = RuntimeEngine.FromXml(Encoding.UTF8.GetBytes(dt));
// Register all terms' value translations
const string payableValue = "1";
var termsTranslation = new NameValueCollection
{
{"Payable", payableValue},
{"Pagadero", payableValue}
};
foreach (var term in termsTranslation.AllKeys)
engine.Entries.Add(term, string.Format("'{0}'", termsTranslation[term]), false);
// Register notifications messages for different culture:
// load messageId and translation for Spanish
var msg1_spanish = new Dictionary<string, string> { { "MSG_1", "Concepto de cuentas es dinero en efectivo" } };
engine.RegisterMultilingualMessage(msg1_spanish, CultureInfo.GetCultureInfo("es-ES"));
// load messageId and translation for English
var msg1_english = new Dictionary<string, string> { { "MSG_1", "Account concept is cash" } };
engine.RegisterMultilingualMessage(msg1_english, CultureInfo.GetCultureInfo("en-US"));
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
var result = engine.Run(termsTranslation["Payable"]);
Assert.AreEqual(msg1_english["MSG_1"], result.Notifications.Default.Notices.First().Message);
result = engine.Run(termsTranslation["Pagadero"]);
Assert.AreEqual(msg1_english["MSG_1"], result.Notifications.Default.Notices.First().Message);
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("es-ES");
result = engine.Run(termsTranslation["Pagadero"]);
Assert.AreEqual(msg1_spanish["MSG_1"], result.Notifications.Default.Notices.First().Message);
result = engine.Run(termsTranslation["Payable"]);
Assert.AreEqual(msg1_spanish["MSG_1"], result.Notifications.Default.Notices.First().Message);
}
Business Glossary
A business glossary is a dictionary of the set of your business terminologies, definitions, synonyms, and translations. With the use of the business glossary, the rule integration, packaging, deployment, and integration of multilingual values are streamlined.
Sample
Let’s assume we have the following Decision Table and glossary defined and we load it as a ruleset:
private static IRuleSet GetAccountingRuleSet()
{
var mc = new ModelContainer();
const string dt = @"
<DecisionTable name=""cash decision table"">
<Declaration>
<Define direction=""in"" name=""ac"" />
</Declaration>
<Glossary>
<GlossarySource uri=""ruleset:///accounting terms""/>
</Glossary>
<Columns>
<Condition name=""Accounting Concept"" term=""Payable"" />
<Action name=""Write Message"" expression=""$value"" type=""notice"" notice=""information"" useMessageId=""true""/>
</Columns>
<Data>
<Row>
<Value>Payable</Value>
<Value>MSG_1</Value>
</Row>
</Data>
</DecisionTable>
";
LoadAdapterUtility.FillNavigableSource(mc, Encoding.UTF8.GetBytes(dt));
const string gl = @"
<Glossary name=""accounting terms"">
<Term name=""Payable"" expression=""ac==$value"">
<Synonym value=""Pagadero""/>
</Term>
</Glossary>
";
LoadAdapterUtility.FillNavigableSource(mc, Encoding.UTF8.GetBytes(gl));
var rs = RuleSet.HierarchicalRuleSet();
rs.AddModel("", mc);
return rs;
}
The Decision Table is the same as we discussed earlier, but in addition, it has a reference to a defined glossary.
Preloaded glossary
When your application maintains a business glossary outside of the rule definition, this approach allows you to load the business glossary from any external data source and inject it during business rule load and execution.
var rs = GetAccountingRuleSet();
const string rsUri = "ruleset:///cash decision table";
// load glossary from ruleset
var glossary = Glossary.Load(rs.SelectFirst(rsUri), rs);
// create engine instance and provide the pre-loaded glossary
var engine = RuntimeEngine.FromRuleSet(rs, rsUri, glossary);
// Register all terms' value translations
engine.Entries.ImportFromGlossary(glossary);
Embedded glossary
In this approach, the engine loads the glossary from the ruleset that is referenced in the Decision Table automatically.
var rs = GetAccountingRuleSet();
const string rsUri = "ruleset:///cash decision table";
// create engine instance and let the embedded glossary load and be utilised
var engine = RuntimeEngine.FromRuleSet(rs, rsUri);
// Get the glossary from the loaded ruleset
var glossary = Glossary.Load(rs.SelectFirst(rsUri), rs);
// Register all terms' value translations
engine.Entries.ImportFromGlossary(glossary);
Multilingual using Business Glossary Sample
The example below shows how to use the business glossary to make values multilingual in a sample Decision Table:
[TestMethod]
public void test_multilingual_load_glossary_onload()
{
var rs = GetAccountingRuleSet();
const string rsUri = "ruleset:///cash decision table";
var engine = RuntimeEngine.FromRuleSet(rs, rsUri);
var glossary = Glossary.Load(rs.SelectFirst(rsUri), rs);
// Register all terms' value translations
engine.Entries.ImportFromGlossary(glossary);
// Register notifications
var msg1_spanish = new Dictionary<string, string> { { "MSG_1", "Concepto de cuentas es dinero en efectivo" } };
engine.RegisterMultilingualMessage(msg1_spanish, CultureInfo.GetCultureInfo("es-ES"));
var msg1_english = new Dictionary<string, string> { { "MSG_1", "Account concept is cash" } };
engine.RegisterMultilingualMessage(msg1_english, CultureInfo.GetCultureInfo("en-US"));
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
var result = engine.Run(glossary.Lookup("Payable"));
Assert.AreEqual(msg1_english["MSG_1"], result.Notifications.Default.Notices.First().Message);
result = engine.Run(glossary.Lookup("Pagadero"));
Assert.AreEqual(msg1_english["MSG_1"], result.Notifications.Default.Notices.First().Message);
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("es-ES");
result = engine.Run(glossary.Lookup("Pagadero"));
Assert.AreEqual(msg1_spanish["MSG_1"], result.Notifications.Default.Notices.First().Message);
result = engine.Run(glossary.Lookup("Payable"));
Assert.AreEqual(msg1_spanish["MSG_1"], result.Notifications.Default.Notices.First().Message);
}
What’s next?
- Introduction to decision tables
- Preparing a decision table
- Modeling decision table
- Decision Model and Notation – decision table
- Check overlaps
- Decision Table final logic
- Multilingual decision table
- Decision Table 101