Translations
To create dictionaries automatically, your translation files should be compatible with Lisan JSON Schema and your translation texts must follow Lisan Literal conventions.
Translations must be valid JSON5 objects and preferably should be stored in JSON files.
Best Practice
Please always maintain your translations as JSON files, and generate dictionaries by using Lisan CLI or Lisan Compiler, since compiler will also validate your translations.
Lisan JSON Schema
interface TranslationJson {
locale?: string;
entries: {
[translationKey: string]: LisanLiteral;
[conditionalGroupKey: string]: {
[conditionTag: string]: LisanLiteral;
};
};
}
Lisan Literal
Lisan Literal is a string and must have a valid Javascript Template Literal syntax. However, the text is not enclosed with backtick (grave accent) character but with single (') or double (") quote characters.
Template Literal is a great and powerful feature of javascript allowing to have embedded expressions. However, to avoid security risks and make interpolation rules easy to understand, almost every embed expression is forbidden.
Only variables and a handful of call expressions are allowed in Lisan Literal.
Below, you can find the list of Lisan Literal rules and capabilities.
You can also try Lisan Online Compiler to see how translations are compiled into dictionaries.
Plain Text
Lisan Literal can be a simple string.
{
"locale": "en-US",
"entries": {
/**
* @compiledTo:
* { "simple.text.key": "Simple text" }
*/
"simple.text.key": "Simple text"
}
}
Usage:
const text = lisan.t('simple.text.key');
console.log(text);
// Outputs: "Simple Text"
Interpolation
Lisan Literal can contain variables.
{
"locale": "en-US",
"entries": {
/**
* @compiledTo:
* { "interpolationExample": ({name, surname}) => `Hello ${name} ${surname}` }
*/
"interpolationExample": "Hello ${name} ${surname}"
}
}
Usage:
const text = lisan.t('interpolationExample', {
name: 'John',
surname: 'Doe',
});
console.log(text);
// Outputs: "Hello John Doe"
Accessing Child Properties of an Object
Lisan Literal can contain a deep object as a variable, as long as the value of last child property is string or number.
{
"locale": "en-US",
"entries": {
/**
* @compiledTo:
* { "deep-object": ({info}) => `Hi ${info.person.name} ${info.person.surname}` }
*/
"deep-object": "Hi ${info.person.name} ${info.person.surname}"
}
}
Usage:
const data = {
person: {
name: 'John',
surname: 'Doe',
},
};
const text = lisan.t('deep-object', data);
console.log(text);
// Outputs: "Hi John Doe"
Escaping
Lisan Literal can contain escaped placeholders, in that case, the expression is ignored.
{
"locale": "en-US",
"entries": {
/**
* @compiledTo:
* { "escaped_exp": ({name}) => `Hello ${name} \${surname}` }
*/
"escaped_exp": "Hello ${name} \\${surname}"
}
}
Usage:
const text = lisan.t('escaped_exp', {
name: 'John',
surname: 'Doe',
});
console.log(text);
// Outputs: "Hello John ${surname}"
Nested Translations
Lisan Literal can contain the lisan.t()
method.
{
"locale": "en-US",
"entries": {
"a.simple.text": "Simple text",
/**
* @compiledTo:
* { "tInception": ({name}, {t}) => `This is a ${t('a.simple.text')}.` }
*/
"tInception": "This is a ${t('a.simple.text')}."
}
}
Usage:
const text = lisan.t('tInception');
console.log(text);
// Outputs: "This is a Simple text."
Interpolation in Nested Translations
Lisan Literal can contain the lisan.t()
method with variables.
Caution
All variables must be passed to
t()
function as an object with shorthand property names.
{
"locale": "en-US",
"entries": {
"greetings.hello": "Hello ${name} ${surname}",
/**
* @compiledTo:
* { "fun.stuff": ({day, name, surname}, {t}) =>
* `Today is ${day}! ${t('greetings.hello', {name, surname})}` }
*/
"fun.stuff": "Today is ${day}! ${t('greetings.hello', {name, surname})}"
}
}
Usage:
const text = lisan.t('fun.stuff', {
day: 'Monday',
name: 'John',
surname: 'Doe',
});
console.log(text);
// Outputs: "Today is Monday! Hello John Doe."
Conditional Groups
Conditional Groups
is a very useful concept to
easily achieve logical interpolations such as pluralization.
Hint
Please note that Conditional Group Keys used in
lisan.c()
method and not withlisan.t()
.
{
"locale": "en-US",
"entries": {
"hello": "Hello ${name}",
// Conditional Group Key
"verb.present.be": {
// Condition Tag: LisanLiteral
"one": "is",
"other": "are"
},
"child": {
"one": "child",
"other": "children"
},
/**
* @compiledTo:
* { "wow": ({name, numberOfKids}, {c, t}) =>
* `${t('hello', {name})}. There ${c('verb.present.be', numberOfKids)} ${numberOfKids} ${c('child', numberOfKids)}`}
*/
"wow!": "${t('hello', {name})}. There ${c('verb.present.be', numberOfKids)} ${numberOfKids} ${c('child', numberOfKids)}"
}
}
Usage:
lisan.c('verb.present.be', 1); // Returns: "is"
lisan.c('verb.present.be', 2); // Returns: "are"
const text1 = lisan.t('wow!', {
name: 'John',
numberOfKids: 1,
});
console.log(text1);
// Outputs: "Hello John. There is 1 child."
const text2 = lisan.t('wow!', {
name: 'Jane',
numberOfKids: 3,
});
console.log(text2);
// Outputs: "Hello Jane. There are 3 children."
Interpolation in Conditional Groups
Interpolation with Conditional Groups is the same as LisanLiteral entries.
Caution
All variables must be passed to
c()
function as an object with shorthand property names.
{
"locale": "en-US",
"entries": {
// Conditional Group Key
"item.error": {
// Condition Tag: LisanLiteral
"zero": "You still don't have a ${item}, buy one now?",
"other": "You can't buy more than 1 ${item}, sorry."
},
/**
* @compiledTo:
* { "actually key can be a sentence as well":
* ({numberOfItems, item}) => `Ooops! ${c('item.error', numberOfItems, {item)}`
*/
"actually key can be a sentence as well!": "Ooops! ${c('item.error', numberOfItems, {item)}"
}
}
Usage:
const text1 = lisan.t('actually key can be a sentence as well!', {
numberOfItems: 0,
item: 'sofa',
});
console.log(text1);
// Outputs: "Ooops! You still don't have a sofa, buy one now?."
const text2 = lisan.t('actually key can be a sentence as well!', {
numberOfItems: 2,
item: 'chair',
});
console.log(text2);
// Outputs: "Ooops! You can't buy more than 1 chair, sorry."
Missing Keys
If an entry is missing t function returns the entry key.
Hint
If you'd like to log missing entries or have a custom behaviour you can check Loging suspicious entries example on How to write plugins? section.
{
"locale": "en-US",
"entries": {
"How do you bend the spoon?": "Maybe spoon does not exist, but this entry does!"
}
}
Usage:
const text1 = lisan.t('How do you bend the spoon?');
console.log(text1);
// Outputs: "Maybe spoon does not exist, but this entry does!"
const text2 = lisan.t('This is not the entry you are looking for!');
console.log(text2);
// Outputs: "This is not the entry you are looking for!"
Using Formatters Inside Entries
lisan.addFormatters()
method allows you to add formatter functions to transform values
into desired format.
{
"locale": "en-US",
"entries": {
/**
* @compiledTo:
* { "tInception": ({date}, {dateTime}) => `Today is ${dateTime(date)}.` }
*/
"today.message": "Today is ${dateTime(date)}."
}
}
Usage:
const text = lisan.t('today.message', {
date: new Date('2020-01-01 13:03:22'),
});
console.log(text);
// Outputs: "Today is 01 January 2020, 01:03 PM."
Warning
Before using formatters, formatters have to be registered to lisan instance via
lisan.addFormatters()
.
For more information, see: Formatters
Allowed Functions
t function
{
/**
* @compiledTo:
* {
* "key": ({param1, param2}, {t}) => `${t('entryKey', {param1, param2})}`
* }
*/
"key": "${t('entryKey', {param1, param2})}"
}
- The first argument is the translation key.
- The second argument is a JSON object enforced with shorthand syntax and being used for interpolation. The arguments that were provided can be number or string or object containing nested object properties.
c function
{
/**
* @compiledTo:
* {
* "key": ({param1, param2, value}, {c}) => `${c('condtionalGroupKey', value, {param1, param2})}`
* }
*/
"key": "${c('condtionalGroupKey', value, {param1, param2})}"
}
- The first argument is a conditional group key.
- The second argument is the value that will be passed down to condition functions.
- The third argument is a JSON object enforced with shorthand syntax and being used for interpolation. The arguments that were provided can be number or string or object containing nested object properties.
Formatters
Any formatter function that was registered via
lisan.addFormatters()
method can be used in Lisan Literals.
For more information, see: Formatters