Use the Smartcat template engine
Overview
Using Smartcat Template Engine technology, you can create your own templates that Smartcat uses to generate documentation. For example, you can create unique templates of invoices and quotes for a specific client.
How it works
Templates are formed according to special markup elements and a data model that you set in Smartcat for the corresponding type of document.
A data model is a set of fields containing information about model elements. For example, consider a model that Smartcat uses to generate documents for work done. The model has the following fields:
| Field name | Description |
|---|---|
| Name | The project name |
| Price | The cost of work |
| Words | Translated words |
| Date | Due date |
| Executive.FirstName | A linguist's name |
| Executive.LastName | A linguist's surname |
| Executive.Price | A linguist's rate |
A template contains text and tags, which are used to display model elements. To get output from a model, use the syntax <<[Field1]>>, where Field1 is the name of an element.
The following example shows information about a particular job:
| Template | Output |
|---|---|
Project: <<[Name]>> |
|
Deadline: <<[Date]>> |
|
Assignee: <<[Executive.FirstName + " " + Executive.LastName]>> |
|
Rate: <<[Executive.Price * Words]>> |
Project: PRESS_RELEASE_APR2020 |
| Deadline: 02.04.2020 | |
| Assignee: Oliver Cross | |
| Rate: 0.44 |
Smartcat Template Engine lets you perform arithmetic operations such as addition (+), subtraction (-), multiplication (*), and division (/) within a template. You can use each operation for numeric values such as rates and words. Fields that contain string values can be operated on too, in a different way. For example, FirstName and LastName form the full name in the output by using the plus sign.
Advanced features
Loops
The template engine also lets you use loops, which is especially useful for forming an extensive list of data. The loop feature has the following form:
<<foreach [item in Items]>>
<<[item]>>
<</foreach>>
Where Items is an array, item is a single element of an array, and [item] prints out an element.
Loop examples
Suppose the array Items contains the string values item1, item2, and item3. The output of the template looks like the table below.
| Template | Output |
|---|---|
<<foreach [item in items]>> |
<<[item]>>
<</foreach>> | item1
item2
item3 |
To display the elements as a numbered list:
| Template | Output |
|---|---|
1. <<foreach [item in items]>><<[item]>> |
<</foreach>> | 1. item1
2. item2
3. item3 |
How loops help to output data
Suppose you have the data model Contracts with a list of contracts. Each contract contains information about the price, client, and manager. The fields for the model are:
| Name | Description |
|---|---|
| Clients.Name | A client's company name |
| Price | The quote for a job |
| Managers.Name | A manager's name |
The template for the model is:
| Client | Manager | Price |
|---|---|---|
<<foreach [ c in Contracts ]>><<[c.Clients.Name]>> |
<<[c.Managers.Name]>> |
<<[c.Price]>><</foreach>> |
| Total: | <<[Contracts.Sum(c => c.Price)]>> |
Possible output:
| Client | Manager | Price |
|---|---|---|
| A Company | John Smith | 1200000 |
| B Ltd. | John Smith | 750000 |
| C & D | John Smith | 350000 |
| E Corp. | Tony Anderson | 650000 |
| F & Partners | Tony Anderson | 550000 |
| G & Co. | July James | 350000 |
| H Group | July James | 250000 |
| I & Sons | July James | 100000 |
| J Ent. | July James | 100000 |
| Total: | 4300000 |
Now change the data model slightly, so you have a list of managers where each one leads a list of contracts, and each contract relates to a client.
Template:
| Manager/Client | Price |
|---|---|
<<foreach [ m in Managers ]>><<[m.Name]>> |
<<[m.Contracts.Sum(c => c.Price)]>> |
<<foreach [ c in m.Contracts ]>> <<[c.Clients.Name]>> |
<<[c.Price]>><</foreach>><</foreach>> |
| Total: | <<[Contracts.Sum(c => c.Price)]>> |
Output:
| Manager/Client | Price |
|---|---|
| John Smith | 2300000 |
| A Company | 1200000 |
| B Ltd. | 750000 |
| C & D | 350000 |
| Tony Anderson | 1200000 |
| E Corp. | 650000 |
| F & Partners | 550000 |
| July James | 800000 |
| G & Co. | 350000 |
| H Group | 250000 |
| I & Sons | 100000 |
| J Ent. | 100000 |
| Total: | 4300000 |
Now form the client list for each manager.
Template:
| Manager | Clients |
|---|---|
<<foreach [ m in Managers ]>><<[m.Name]>> |
<<foreach [ c in m.Contracts ]>><<[c.Clients.Name]>> |
<</foreach>><</foreach>> |
Output:
| Manager | Clients |
|---|---|
| John Smith | A Company |
| B Ltd. | |
| C & D | |
| Tony Anderson | E Corp. |
| F & Partners | |
| July James | G & Co. |
| H Group | |
| I & Sons | |
| J Ent. |
You can set the manager column by using -greedy.
Template:
| Managers |
|---|
<<foreach [m in ds.Managers]>><<[m.Name]>><</foreach -greedy>> |
Output:
| Managers |
|---|
| John Smith |
| Tony Anderson |
| July James |
Conditional operators
If you want the output to match specific conditions, use conditional operators.
<<if [conditional_expression1]>>
template_option1
<<elseif [conditional_expression2]>>
template_option2
...
<<else>>
default_template_option
<</if>>
Conditional_expression is a condition check that returns either true or false.
Suppose you have the data model Items with the following strings: item1, item2, and item3.
The template below checks whether the elements of the array ([!items.Any()]) exist. If they do, the template displays the number of elements; otherwise, a no items message appears.
| Template | Output |
|---|---|
You have chosen <<if [!items.Any()]>>no items<<else>><<[items.Count()]>> item(s)<</if>>. |
You have chosen 3 item(s). |
The function IndexOf() marks even elements with yellow color:
| Template | Output |
|---|---|
<<foreach [item in items]>><<if [IndexOf() % 2 == 0]>><<[item]>> |
<<else>><<[item]>>
<</if>><</foreach>> | item1
item2
item3 |
Here you output elements of an array, or No data in case no element exists.
<<if [!items.Any()]>>No data.
<<else>><<foreach [item in items]>><<[item]>>
<</foreach>><</if>>
Tables and conditional operators
Using conditional operators, you can display data or apply styles to rows of a table depending on conditions. The rows in the table below are colored according to a specific conditional expression.
| Column 1 | Column 2 | Column 3 |
|---|---|---|
<<if ...>> ... |
... | ... |
<<elseif ...>> ... |
... | ... |
<<else>> ... |
... | ... <</if>> |
To showcase how it works, use the model Client with the following fields:
| Name | Description |
|---|---|
| Name | self-explanatory |
| Country | self-explanatory |
| LocalAddress | self-explanatory |
The template below outputs data related to all clients. New Zealand clients are marked with green color, and columns for such clients are merged and contain only LocalAddress values.
| Column 1 | Column 2 | Column 3 |
|---|---|---|
<<foreach [in clients]>><<if [Country == "New Zealand"]>><<[Name]>> |
<<[LocalAddress]>> |
|
<<else>><<[Name]>> |
<<[Country]>> |
<<[LocalAddress]>> |
Output:
| Client | Country | Local Address |
|---|---|---|
| A Company | Australia | 219-241 Cleveland St STRAWBERRY HILLS NSW 1427 |
| B Ltd. | Brazil | Avenida João Jorge, 112, ap. 31, Vila Industrial, Campinas — SP, 13035-680 |
| C & D | Canada | 101-3485 RUE DE LA MONTAGNE, MONTRÉAL (QUÉBEC) H3G 2A6 |
| E Corp. | 445 Mount Eden Road, Mount Eden, Auckland 1024 | |
| F & Partners | 20 Greens Road, Tuahiwi, Kaiapoi 7691 | |
| G & Co. | Greece | Karkisias 6, GR-111 42 ATHINA, GRÉCE |
| H Group | Hungary | Budapest, Fiktív utca 82., IV. em./28., 2806 |
| I & Sons | 43 Vogel Street, Roslyn, Palmerston North 4414 |
This template displays the No data message if no data is found.
| Client | Country | Local Address |
|---|---|---|
<<if [!clients.Any()]>>No data |
||
<<else>><<foreach [in clients]>><<[Name]>> |
<<[Country]>> |
<<[LocalAddress]>><</foreach>><</if>> |
Output:
| Client | Country | Local Address |
|---|---|---|
| No data |
You can also remove content conditionally:
| Template | Output |
|---|---|
| Header | |
<<if [false]>>Content to remove<</if>> |
|
| Footer | Header |
| Footer |
Here you use -greedy:
| Template | Output |
|---|---|
| Header | |
<<if [false]>>Content to remove<</if -greedy>> |
|
| Footer | Header |
| Footer |
Auxiliary methods for arrays
Smartcat Template Engine also lets you work flexibly with arrays by applying auxiliary methods.
Suppose you have a data model containing the field persons, that is, the list of employees.
The description of the data model Person:
| Name |
|---|
| Name |
| Age |
| Subordinates |
| Salary |
This template displays the names of employees older than 50 years:
| Template | Output |
|---|---|
<<foreach [person in persons.All(p => p.Age > 50)]>> |
<<[person.Name]>>
<</foreach>> | Joe Black
Ivan Lock
Jane Wood |
Now get a total of employees' salaries:
| Template | Output |
|---|---|
<<[persons.Sum( p => p.Salary)]>> |
490000 |
The table below describes all available methods, with examples drawn from two arrays, persons and otherPersons. Both hold information about the company's employees.
| Method | Template |
|---|---|
| All(Predicate) | persons.All(p => p.Age < 50) |
| Any() | persons.Any() |
| Average(Selector) | persons.Average(p => p.Age) |
| Concat(IEnumerable) | persons.Concat(otherPersons) |
| Contains(Object) | persons.Contains(otherPersons.First()) |
| Count() | persons.Count() |
| Count(Predicate) | persons.Count(p => p.Age > 30) |
| Distinct() | persons.Distinct() |
| First() | persons.First() |
| First(Predicate) | persons.First(p => p.Age > 30) |
| FirstOrDefault() | persons.FirstOrDefault() |
| FirstOrDefault(Predicate) | persons.FirstOrDefault(p => p.Age > 30) |
| GroupBy(Selector) | persons.GroupBy(p => p.Age) |
| Last() | persons.Last() |
| Last(Predicate) | persons.Last(p => p.Age > 100) |
| LastOrDefault() | persons.LastOrDefault() |
| LastOrDefault(Predicate) | persons.LastOrDefault(p => p.Age > 100) |
| Max(ComparableSelector) | persons.Max(p => p.Age) |
| Min(ComparableSelector) | persons.Min(p => p.Age) |
| OrderBy(ComparableSelector) | persons.OrderBy(p => p.Age) |
| OrderByDescending(ComparableSelector) | persons.OrderByDescending(p => p.Age) |
| Single() | persons.Single() |
| Single(Predicate) | persons.Single(p => p.Name == "John Smith") |
| SingleOrDefault() | persons.SingleOrDefault() |
| SingleOrDefault(Predicate) | persons.SingleOrDefault(p => p.Name == "John Smith") |
| Skip(int) | persons.Skip(10) |
| SkipWhile(Predicate) | persons.SkipWhile(p => p.Age < 21) |
| Sum(Selector) | persons.Sum(p => p.Children.Count()) |
| Take(int) | persons.Take(5) |
| TakeWhile(Predicate) | persons.TakeWhile(p => p.Age < 50) |
| Union(IEnumerable) | persons.Union(otherPersons) |
| Where(Predicate) | persons.Where(p => p.Age > 18) |
The OrderBy and OrderByDescending methods also support chaining with ThenBy and ThenByDescending, for example persons.OrderBy(p => p.Age).ThenByDescending(p => p.Name).ThenBy(p => p.Children.Count()).
Appendix for developers
Smartcat Template Engine uses a subset of C# syntax (C# Language Specification 5.0) in templates. For all primitive data types, such as strings, you can use BCL methods. The methods mentioned in the Auxiliary methods for arrays section are taken from LINQ.
Still need help?
Our support team responds within one business day.