banana-i18n
Version:
Banana Internationalization library
934 lines (878 loc) • 31.5 kB
JavaScript
import Banana from '../src/index.js'
import assert from 'assert'
import fs from 'fs'
import path from 'path'
const isNodeVersionAbove12 = () => parseInt(process.versions.node) > 12
const __dirname = path.dirname(new URL(import.meta.url).pathname)
const grammarTests = {
bs: [{
word: 'word',
grammarForm: 'instrumental',
expected: 's word',
description: 'Grammar test for instrumental case'
}, {
word: 'word',
grammarForm: 'lokativ',
expected: 'o word',
description: 'Grammar test for lokativ case'
}],
dsb: [{
word: 'word',
grammarForm: 'instrumental',
expected: 'z word',
description: 'Grammar test for instrumental case'
}, {
word: 'word',
grammarForm: 'lokatiw',
expected: 'wo word',
description: 'Grammar test for lokatiw case'
}],
fi: [{
word: 'talo',
grammarForm: 'genitive',
expected: 'talon',
description: 'Grammar test for genitive case'
}, {
word: 'linux',
grammarForm: 'genitive',
expected: 'linuxin',
description: 'Grammar test for genitive case'
}, {
word: 'talo',
grammarForm: 'elative',
expected: 'talosta',
description: 'Grammar test for elative case'
}, {
word: 'pastöroitu',
grammarForm: 'partitive',
expected: 'pastöroitua',
description: 'Grammar test for partitive case'
}, {
word: 'talo',
grammarForm: 'partitive',
expected: 'taloa',
description: 'Grammar test for partitive case'
}, {
word: 'talo',
grammarForm: 'illative',
expected: 'taloon',
description: 'Grammar test for illative case'
}, {
word: 'linux',
grammarForm: 'inessive',
expected: 'linuxissa',
description: 'Grammar test for inessive case'
}],
ga: [{
word: 'an Domhnach',
grammarForm: 'ainmlae',
expected: 'Dé Domhnaigh',
description: 'Grammar test for ainmlae case'
}, {
word: 'an Luan',
grammarForm: 'ainmlae',
expected: 'Dé Luain',
description: 'Grammar test for ainmlae case'
}, {
word: 'an Satharn',
grammarForm: 'ainmlae',
expected: 'Dé Sathairn',
description: 'Grammar test for ainmlae case'
}],
he: [{
word: 'ויקיפדיה',
grammarForm: 'prefixed',
expected: 'וויקיפדיה',
description: 'Duplicate the "Waw" if prefixed'
}, {
word: 'וולפגנג',
grammarForm: 'prefixed',
expected: 'וולפגנג',
description: 'Duplicate the "Waw" if prefixed, but not if it is already duplicated.'
}, {
word: 'הקובץ',
grammarForm: 'prefixed',
expected: 'קובץ',
description: 'Remove the "He" if prefixed'
}, {
word: 'Wikipedia',
grammarForm: 'תחילית',
expected: '־Wikipedia',
description: 'Add a hyphen (maqaf) before non-Hebrew letters'
}, {
word: '1995',
grammarForm: 'תחילית',
expected: '־1995',
description: 'Add a hyphen (maqaf) before numbers'
}],
hsb: [{
word: 'word',
grammarForm: 'instrumental',
expected: 'z word',
description: 'Grammar test for instrumental case'
}, {
word: 'word',
grammarForm: 'lokatiw',
expected: 'wo word',
description: 'Grammar test for lokatiw case'
}],
hu: [{
word: 'Wikipédiá',
grammarForm: 'rol',
expected: 'Wikipédiáról',
description: 'Grammar test for rol case'
}, {
word: 'Wikipédiá',
grammarForm: 'ba',
expected: 'Wikipédiába',
description: 'Grammar test for ba case'
}, {
word: 'Wikipédiá',
grammarForm: 'k',
expected: 'Wikipédiák',
description: 'Grammar test for k case'
}],
hy: [{
word: 'Մաունա',
grammarForm: 'genitive',
expected: 'Մաունայի',
description: 'Grammar test for genitive case'
}, {
word: 'հետո',
grammarForm: 'genitive',
expected: 'հետոյի',
description: 'Grammar test for genitive case'
}, {
word: 'գիրք',
grammarForm: 'genitive',
expected: 'գրքի',
description: 'Grammar test for genitive case'
}, {
word: 'ժամանակի',
grammarForm: 'genitive',
expected: 'ժամանակիի',
description: 'Grammar test for genitive case'
}],
la: [{
word: 'Translatio',
grammarForm: 'genitive',
expected: 'Translationis',
description: 'Grammar test for genitive case'
}, {
word: 'Translatio',
grammarForm: 'accusative',
expected: 'Translationem',
description: 'Grammar test for accusative case'
}, {
word: 'Translatio',
grammarForm: 'ablative',
expected: 'Translatione',
description: 'Grammar test for ablative case'
}],
os: [{
word: 'бæстæ',
grammarForm: 'genitive',
expected: 'бæсты',
description: 'Grammar test for genitive case'
}, {
word: 'бæстæ',
grammarForm: 'allative',
expected: 'бæстæм',
description: 'Grammar test for allative case'
}, {
word: 'Тигр',
grammarForm: 'dative',
expected: 'Тигрæн',
description: 'Grammar test for dative case'
}, {
word: 'цъити',
grammarForm: 'dative',
expected: 'цъитийæн',
description: 'Grammar test for dative case'
}, {
word: 'лæппу',
grammarForm: 'genitive',
expected: 'лæппуйы',
description: 'Grammar test for genitive case'
}, {
word: '2011',
grammarForm: 'equative',
expected: '2011-ау',
description: 'Grammar test for equative case'
}],
ru: [{
word: 'транслэйтвики',
grammarForm: 'genitive',
expected: 'транслэйтвики',
description: 'Grammar test for genitive case'
}, {
word: 'тесть',
grammarForm: 'genitive',
expected: 'тестя',
description: 'Grammar test for genitive case'
}, {
word: 'привилегия',
grammarForm: 'genitive',
expected: 'привилегии',
description: 'Grammar test for genitive case'
}, {
word: 'установка',
grammarForm: 'genitive',
expected: 'установки',
description: 'Grammar test for genitive case'
}, {
word: 'похоти',
grammarForm: 'genitive',
expected: 'похотей',
description: 'Grammar test for genitive case'
}, {
word: 'доводы',
grammarForm: 'genitive',
expected: 'доводов',
description: 'Grammar test for genitive case'
}, {
word: 'песчаник',
grammarForm: 'genitive',
expected: 'песчаника',
description: 'Grammar test for genitive case'
}],
sl: [{
word: 'word',
grammarForm: 'orodnik',
expected: 'z word',
description: 'Grammar test for orodnik case'
}, {
word: 'word',
grammarForm: 'mestnik',
expected: 'o word',
description: 'Grammar test for mestnik case'
}],
uk: [{
word: 'транслейтвікі',
grammarForm: 'genitive',
expected: 'транслейтвікі',
description: 'Grammar test for genitive case'
}, {
word: 'тесть',
grammarForm: 'genitive',
expected: 'тестя',
description: 'Grammar test for genitive case'
}, {
word: 'Вікіпедія',
grammarForm: 'genitive',
expected: 'Вікіпедії',
description: 'Grammar test for genitive case'
}, {
word: 'установка',
grammarForm: 'genitive',
expected: 'установки',
description: 'Grammar test for genitive case'
}, {
word: 'похоти',
grammarForm: 'genitive',
expected: 'похотей',
description: 'Grammar test for genitive case'
}, {
word: 'доводы',
grammarForm: 'genitive',
expected: 'доводов',
description: 'Grammar test for genitive case'
}, {
word: 'песчаник',
grammarForm: 'genitive',
expected: 'песчаника',
description: 'Grammar test for genitive case'
}, {
word: 'Вікіпедія',
grammarForm: 'accusative',
expected: 'Вікіпедію',
description: 'Grammar test for accusative case'
}]
}
describe('Banana', function () {
it('should parse and localize to English', () => {
const locale = 'en'
const banana = new Banana(locale, {})
const messages = fs.readFileSync(path.join(__dirname, 'i18n', `${locale}.json`))
banana.load(JSON.parse(messages), locale)
assert.strictEqual(banana.i18n('msg-one'), 'One')
assert.strictEqual(banana.i18n('msg-two', 10), '10 results')
assert.strictEqual(banana.i18n('msg-three', 10), '10 results')
assert.strictEqual(banana.i18n('msg-three', 1), 'One result')
assert.strictEqual(banana.i18n('msg-four', 10, 4), 'There are 10 results in 4 files')
})
it('should load the messages for multiple locales', () => {
const banana = new Banana()
const messages = {
en: {
message_1: 'Message one',
message_2: 'Message two'
},
ml: {
message_1: 'ഒന്നാമത്തെ മെസ്സേജ്'
},
es: {
message_1: 'Message one'
}
}
banana.load(messages)
assert.ok(banana.messageStore.hasLocale('en'))
assert.ok(banana.messageStore.hasLocale('ml'))
banana.setLocale('en')
assert.strictEqual(banana.i18n('message_1'), 'Message one')
banana.setLocale('ml')
assert.strictEqual(banana.i18n('message_1'), 'ഒന്നാമത്തെ മെസ്സേജ്')
banana.setLocale('es')
assert.strictEqual(banana.i18n('message_2'), 'Message two', 'Fallbacks to en message')
banana.setLocale('uk')
assert.strictEqual(banana.i18n('message_2'), 'Message two', 'Fallbacks to en message by first checking ru.')
})
it('should respect finalFallback option', () => {
const banana = new Banana('es', {
messages: {
ml: {
message_1: 'ഒന്നാമത്തെ മെസ്സേജ്',
message_2: 'രണ്ടാമത്തെ മെസ്സേജ്'
},
en: {
message_1: 'Message one',
message_2: 'Message two'
}
},
finalFallback: 'ml'
})
assert.ok(!banana.messageStore.hasLocale('es'))
assert.ok(banana.messageStore.hasLocale('en'))
assert.ok(banana.messageStore.hasLocale('ml'))
assert.strictEqual(banana.i18n('message_2'), 'രണ്ടാമത്തെ മെസ്സേജ്')
})
it('should respect locales with country codes', () => {
const banana = new Banana('en-GB', {
messages: {
en: {
message_1: 'Message one',
message_2: 'Message two'
}
}
})
assert.strictEqual(banana.i18n('message_2'), 'Message two')
})
it('should throw errors on invalid locales', () => {
assert.throws(() => {
new Banana('es/en', {
messages: {
message_1: 'Message one',
message_2: 'Message two'
}
})
}, Error, 'Invalid locale es/en')
})
it('should throw errors on invalid message source', () => {
assert.throws(() => {
new Banana('es/en', {
messages: []
})
}, Error, 'Invalid message source.')
})
it('should throw errors on invalid message key', () => {
assert.throws(() => {
new Banana('es/en', {
messages: {
message_1: ['Message one'],
message_2: 'Message two'
}
})
}, Error, 'Invalid message key.')
})
it('should handle messages that are an empty string', () => {
const banana = new Banana('zh-hans', {
messages: {
'zh-hans': {
'word-separator': ''
}
}
})
assert.strictEqual(banana.i18n('word-separator'), '', 'Empty string message')
})
it('should merge messages when added to an existing locale', () => {
const banana = new Banana('ca', {
messages: {
ca: {
message_1: 'Message one',
message_2: 'Message two'
}
}
})
// Add some more messages
banana.load({
message_2: 'Message two - new',
message_3: 'Message three'
}, 'ca')
assert.strictEqual(banana.i18n('message_1'), 'Message one')
assert.strictEqual(banana.i18n('message_2'), 'Message two - new')
assert.strictEqual(banana.i18n('message_3'), 'Message three')
})
it('should parse the plural and gender', () => {
const locale = 'en'
const banana = new Banana(locale, {})
const messages = fs.readFileSync(path.join(__dirname, 'i18n', `${locale}.json`))
banana.load(JSON.parse(messages), locale)
assert.strictEqual(
banana.i18n('This message key does not exist'),
'This message key does not exist',
'This message key does not exist'
)
assert.strictEqual(banana.i18n('Hello $1', 'Bob'), 'Hello Bob', 'Parameter replacement')
const pluralAndGenderMessage = '$1 has $2 {{plural:$2|kitten|kittens}}. ' +
'{{gender:$3|He|She}} loves to play with {{plural:$2|it|them}}.'
const pluralAndGenderMessageWithLessParaMS = '$1 has $2 {{plural:$2|kitten}}. ' +
'{{gender:$3|He|She}} loves to play with {{plural:$2|it}}.'
const pluralAndGenderMessageWithCase = '$1 has $2 {{plURAl:$2|kitten}}. ' +
'{{genDER:$3|He|She}} loves to play with {{pLural:$2|it}}.'
const pluralAndGenderMessageWithSyntaxError = '$1 has $2 {{plural:$2|kitten}. ' +
'{{gender:$3|He|She}} loves to play with {plural:$2|it}}.'
const pluralAndGenderMessageWithSyntaxError2 = '$1 has $2 {{plural:$2|kitten}}. ' +
'{gender:$3|He|She}} loves to play with {plural:$2|it}}.'
assert.strictEqual(
banana.i18n(pluralAndGenderMessage, 'Meera', 1, 'female'),
'Meera has 1 kitten. She loves to play with it.',
'Plural and gender test - female, singular'
)
assert.throws(
function () {
banana.i18n(pluralAndGenderMessageWithSyntaxError, 'Meera', 1, 'female')
},
/Parse error at position 10/,
'Message has syntax error'
)
assert.throws(
function () {
banana.i18n(pluralAndGenderMessageWithSyntaxError2, 'Meera', 1, 'female')
},
/Parse error at position 32/,
'Message has syntax error'
)
assert.strictEqual(
banana.i18n(pluralAndGenderMessageWithLessParaMS, 'Meera', 1, 'female'),
'Meera has 1 kitten. She loves to play with it.',
'Plural and gender test - female, singular, but will less parameters in message'
)
assert.strictEqual(
banana.i18n(pluralAndGenderMessageWithCase, 'Meera', 1, 'female'),
'Meera has 1 kitten. She loves to play with it.',
'Plural and gender test - female, singular. Plural, gender keywords with upper and lower case'
)
assert.strictEqual(
banana.i18n(pluralAndGenderMessage, 'Meera', 1, 'randomtext'),
'Meera has 1 kitten. He loves to play with it.',
'Plural and gender test - wrong gender- fallback to fist gender'
)
assert.strictEqual(
banana.i18n(pluralAndGenderMessage),
'$1 has $2 kittens. He loves to play with them.',
'Plural and gender test - no params passed. Should not fail'
)
assert.strictEqual(
banana.i18n(pluralAndGenderMessage, 'Meera', 1, 'randomtext', 'extraparam'),
'Meera has 1 kitten. He loves to play with it.',
'Plural and gender test - more params passed. Should not fail'
)
assert.strictEqual(
banana.i18n(pluralAndGenderMessage, 'Harry', 2, 'male'),
'Harry has 2 kittens. He loves to play with them.',
'Plural and gender test - male, plural'
)
assert.strictEqual(
banana.i18n('This costs $1.'),
'This costs $1.',
'No parameter supplied, $1 appears as is'
)
const genderMessageWithoutExplicitGender = '{{gender:|He|She|They}}'
assert.strictEqual(
banana.i18n(genderMessageWithoutExplicitGender),
'They',
'Gender test - no gender passed'
)
})
it('should parse formatnum', () => {
const locale = 'ar'
const banana = new Banana(locale)
if (!isNodeVersionAbove12()) { return }
assert.strictEqual(
banana.i18n('{{formatnum:34242}}'),
'٣٤٬٢٤٢',
'Arabic numerals'
)
})
const formatnumTests = [
{
lang: 'en',
number: 987654321.654321,
result: '987,654,321.654',
description: 'formatnum test for English, decimal separator'
},
{
lang: 'ar',
number: 987654321.654321,
result: '٩٨٧٬٦٥٤٬٣٢١٫٦٥٤',
description: 'formatnum test for Arabic, with decimal separator'
},
{
lang: 'ar',
number: '٩٨٧٦٥٤٣٢١٫٦٥٤٣٢١',
result: '987654321',
integer: true,
description: 'formatnum test for Arabic, with decimal separator, reverse'
},
{
lang: 'ar',
number: -12.89,
result: '-١٢٫٨٩',
description: 'formatnum test for Arabic, negative number'
},
{
lang: 'ar',
number: '-١٢٫٨٩',
result: '-12',
integer: true,
description: 'formatnum test for Arabic, negative number, reverse'
},
{
lang: 'nl',
number: 987654321.654321,
result: '987.654.321,654',
description: 'formatnum test for Nederlands, decimal separator'
},
{
lang: 'nl',
number: -12.89,
result: '-12,89',
description: 'formatnum test for Nederlands, negative number'
},
{
lang: 'nl',
number: '.89',
result: '0,89',
description: 'formatnum test for Nederlands'
},
{
lang: 'nl',
number: 'invalidnumber',
result: 'invalidnumber',
description: 'formatnum test for Nederlands, invalid number'
},
{
lang: 'ml',
number: '1000000000',
result: '1,00,00,00,000',
description: 'formatnum test for Malayalam'
},
{
lang: 'ml',
number: '-1000000000',
result: '-1,00,00,00,000',
description: 'formatnum test for Malayalam, negative number'
},
{
lang: 'mr',
number: '123456789.123456789',
result: '१२,३४,५६,७८९.१२३',
description: 'formatnum test for Marathi'
},
{
lang: 'hi',
number: '123456789.123456789',
result: '12,34,56,789.123',
description: 'formatnum test for Hindi'
},
{
lang: 'fr',
number: '1234.56',
result: '1 234,56',
description: 'formatnum test for French'
},
{
lang: 'ban',
number: '123456.789',
result: '123.456,789',
description: 'formatnum test for Balinese using fallback language'
},
{
lang: 'hi',
number: '१२,३४,५६,७८९',
result: '१२,३४,५६,७८९',
description: 'formatnum test for Hindi, Devanagari digits passed'
}
]
it('formatnum tests', () => {
if (!isNodeVersionAbove12()) { return }
const formatNumMsg = '{{formatnum:$1}}'
const formatNumMsgInt = '{{formatnum:$1|R}}'
formatnumTests.forEach((test) => {
const banana = new Banana(test.lang)
assert.strictEqual(
banana.i18n(test.integer ? formatNumMsgInt : formatNumMsg,
test.number),
test.result,
test.description
)
})
})
it('should allow custom parser plugins', () => {
const locale = 'en'
const banana = new Banana(locale)
banana.registerParserPlugin('foobar', (nodes) => {
return nodes[0] === 'foo' ? nodes[1] : nodes[2]
})
assert.strictEqual(
banana.i18n('{{foobar:foo|first|second}}'),
'first',
'Emits first argument on passing foo to foobar plugin hook'
)
assert.strictEqual(
banana.i18n('{{foobar:bar|first|second}}'),
'second',
'Emits second argument on passing bar to foobar plugin hook'
)
banana.registerParserPlugin('sitename', () => {
return 'Wikipedia'
})
banana.registerParserPlugin('link', (nodes) => {
return '<a href="' + nodes[1] + '">' + nodes[0] + '</a>'
})
assert.strictEqual(
banana.i18n('{{link:{{SITENAME}}|https://en.wikipedia.org}}'),
'<a href="https://en.wikipedia.org">Wikipedia</a>',
'complex use of custom parser plugins'
)
})
it('should parse the Arabic message', () => {
const locale = 'ar'
const banana = new Banana(locale)
assert.strictEqual(banana.locale, 'ar', 'Locale is Arabic')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 1), 'one',
'Arabic plural test for one')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', '٠'), 'zero',
'Arabic plural test for arabic digit zero')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 2), 'two',
'Arabic plural test for two')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 3), 'few',
'Arabic plural test for few')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', '٨'), 'few',
'Arabic plural test for few')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 9), 'few',
'Arabic plural test for few')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 110), 'few',
'Arabic plural test for few')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 11), 'many',
'Arabic plural test for many')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 15), 'many',
'Arabic plural test for many')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 99), 'many',
'Arabic plural test for many')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 9999), 'many',
'Arabic plural test for many')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 100), 'other',
'Arabic plural test for other')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 102), 'other',
'Arabic plural test for other')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 1000), 'other',
'Arabic plural test for other')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', 1.7), 'other',
'Arabic decimal plural test for one')
assert.strictEqual(banana.i18n('{{plural:$1|zero|one|two|few|many|other}}', '٠١٢٣٤٥٦٧٨٩'), 'many',
'Arabic plural test for ۰۱۲۳۴۵۶۷۸۹')
})
it('should parse explicit plural forms correctly', () => {
const banana = new Banana('en')
const language = banana.parser.emitter.language
assert.strictEqual(language.convertPlural(0, ['0=Explicit Zero', 'Singular', 'Plural']),
'Explicit Zero', 'Explicit Zero')
assert.strictEqual(language.convertPlural(1, ['0=Explicit Zero', 'Singular', 'Plural', '1=Explicit One']),
'Explicit One', 'Explicit One')
assert.strictEqual(language.convertPlural(3, ['0=Explicit Zero', '1=Explicit One', 'Singular', 'Plural']),
'Plural', 'Plural')
assert.strictEqual(language.convertPlural(1, ['0=Explicit Zero', 'Singular', 'Plural']),
'Singular', 'Singular')
// See https://bugzilla.wikimedia.org/69993
assert.strictEqual(banana.i18n('Found {{PLURAL:$1|$1 results|1=$1 result}}', 1), 'Found 1 result', 'Plural message with explicit plural forms, plural form contains placeholder.')
})
it('should use digit transform table and localize digits', function () {
const langCode = 'fa'
const banana = new Banana(langCode)
assert.strictEqual(banana.parser.emitter.locale, langCode, 'Locale is ' + langCode)
assert.strictEqual(banana.parser.emitter.language.convertNumber('8', true), 8,
'Persian transform of 8')
assert.strictEqual(banana.parser.emitter.language.convertNumber('۰۱۲۳۴۵۶۷۸۹', true), 123456789,
'Persian transform of ۰۱۲۳۴۵۶۷۸۹')
if (!isNodeVersionAbove12()) { return }
// Rest of the tests need Intl.NumberFormat.
assert.strictEqual(banana.parser.emitter.language.convertNumber('8'), '۸',
'Persian transform of 8')
assert.strictEqual(banana.parser.emitter.language.convertNumber('0123456789'), '۱۲۳٬۴۵۶٬۷۸۹',
'Persian transform of 0123456789')
})
it('should localize the messages with bidi arguments', () => {
const banana = new Banana('he')
banana.load({
'greet-msg': 'שלום {{bidi:$1}} הי!'
}, 'he')
assert.strictEqual(
banana.i18n('greet-msg', '123'),
'שלום ' + '123' + ' הי!',
'Bidi with neutral argument'
)
assert.strictEqual(
banana.i18n('greet-msg', 'Ben_(WMF)'),
'שלום ' + '\u202A' + 'Ben_(WMF)' + '\u202C' + ' הי!',
'Bidi with LTR argument'
)
assert.strictEqual(
banana.i18n('greet-msg', 'יהודי (מנוחין)'),
'שלום ' + '\u202B' + 'יהודי (מנוחין)' + '\u202C' + ' הי!',
'Bidi with RTL argument'
)
})
it('should localize the messages with wiki liinks', () => {
const banana = new Banana('en', { wikilinks: true })
banana.load({
'msg-with-extlink': 'This is a link to [https://wikipedia.org wikipedia]',
'msg-with-wikilink': 'This is a link to [[Apple|Apple Page]]',
'msg-with-wikilink-no-anchor': 'This is a link to [[Apple]]'
}, 'en')
banana.load({
'msg-with-extlink': 'ഇത് [https://wikipedia.org വിക്കിപീഡീയ] ലിങ്ക്'
}, 'ml')
assert.strictEqual(
banana.i18n('msg-with-extlink'),
'This is a link to <a href="https://wikipedia.org">wikipedia</a>',
'External link'
)
assert.strictEqual(
banana.i18n('msg-with-wikilink'),
'This is a link to <a href="./Apple" title="Apple">Apple Page</a>',
'Internal Wiki style link with link and title being different'
)
assert.strictEqual(
banana.i18n('msg-with-wikilink-no-anchor'),
'This is a link to <a href="./Apple" title="Apple">Apple</a>',
'Internal Wiki style link with link and title being same'
)
banana.setLocale('ml')
assert.strictEqual(
banana.i18n('msg-with-extlink'),
'ഇത് <a href="https://wikipedia.org">വിക്കിപീഡീയ</a> ലിങ്ക്',
'External link, after changing locale'
)
})
it('should skip wiki links if disabled', () => {
const banana = new Banana('en', { wikilinks: false })
banana.load({
'msg-with-extlink': 'This is reference [10]',
'msg-with-wikilink': '$1 more {{plural:$1|item|items}} [[...]]'
}, 'en')
assert.strictEqual(
banana.i18n('msg-with-extlink'),
'This is reference [10]'
)
assert.strictEqual(
banana.i18n('msg-with-wikilink', 10),
'10 more items [[...]]'
)
})
it('should parse and localize html content safely', () => {
const banana = new Banana('en', { wikilinks: true })
banana.load({
'msg-for-html-sanitize-script': 'This is <em>link</em> and it is <script>[[Foo|bar]]</script>',
'msg-for-html-sanitize-onclick': 'This is <em id="34" class="c-em">link</em> and it is <a onclick="alert()" href="#">a problem</a>',
'msg-for-html-sanitize-mismatched': '<i class="important">test</b>',
'msg-for-html-sanitize-script-and-external-link': '<script>alert( "script-and-external-link test" );</script> [http://example.com <i>Foo</i> bar]',
'msg-for-html-sanitize-script-as-link': '[http://example.com <script>alert( "link-script test" );</script>]',
'msg-for-html-sanitize-attribute-quotes': '<i id="double">Double</i> <i id=\'single\'>Single</i> <i style="font-family:"Arial"">Styled</i>',
'msg-for-html-sanitize-special-content': '<a href="example.com">/></a>',
'msg-for-html-sanitize-placeholder-with-extra': '<span>$1%</span> of apples are good'
}, 'en')
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-script'),
'This is <em>link</em> and it is <script><a href="./Foo" title="Foo">bar</a></script>',
'Script tag text is escaped because that element is not allowed, but link inside is still HTML'
)
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-onclick'),
'This is <em id="34" class="c-em">link</em> and it is <a onclick="alert()" href="#">a problem</a>',
'Common attributes are preseved and link is escaped'
)
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-mismatched'),
'<i class="important">test</b>',
'Mismatched HTML start and end tag treated as text'
)
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-script-and-external-link'),
'<script>alert( "script-and-external-link test" );</script> <a href="http://example.com"><i>Foo</i> bar</a>',
'HTML tags in external links not interfering with escaping of other tags'
)
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-script-as-link'),
'<a href="http://example.com"><script>alert( "link-script test" );</script></a>',
'HTML tag <script> is not in the list of allowed ones. Content is escaped'
)
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-attribute-quotes'),
'<i id="double">Double</i> <i id="single">Single</i> <i style="font-family:"Arial"">Styled</i>',
'Attributes with double quotes are preserved as such. Attributes with single quotes are normalized to double. Escaped attributes are parsed correctly'
)
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-special-content'),
'<a href="example.com">/></a>',
'HTML reserved characters like / and > can appear in the content inside tags. They should not be treated with any HTML related meaning.'
)
assert.strictEqual(
banana.i18n('msg-for-html-sanitize-placeholder-with-extra'),
'<span>$1%</span> of apples are good',
'Placeholders with extra characters like % should be parsed correctly with placeholder replacement'
)
})
for (const langCode in grammarTests) {
grammarTest(langCode, grammarTests[langCode])
}
it('should give correct fallback locales', () => {
const fallbacks = new Banana('sa').getFallbackLocales()
assert.deepStrictEqual(
fallbacks,
['hi', 'en'],
'Correct fallback locales'
)
})
it('should treat locale case insensitively', () => {
const banana = new Banana('zh-Hans', {
messages: {
'ZH-hAnS': {
message_1: '消息1'
},
'zh-haNs': {
message_2: '消息2'
}
}
})
assert.ok(banana.messageStore.hasLocale('zh-hans'))
assert.strictEqual(banana.i18n('message_1'), '消息1')
assert.strictEqual(banana.i18n('message_2'), '消息2')
banana.setLocale('ZH-HANS')
assert.strictEqual(banana.i18n('message_1'), '消息1')
assert.strictEqual(banana.i18n('message_2'), '消息2')
})
})
function grammarTest (langCode, test) {
it('should correctly apply the grammar for language ' + langCode, () => {
const banana = new Banana(langCode)
assert.strictEqual(banana.locale, langCode, 'Locale is ' + langCode)
for (let i = 0; i < test.length; i++) {
const grammarMessage = '{{GRAMMAR:' + test[i].grammarForm + '|' +
test[i].word + '}}'
assert.strictEqual(banana.i18n(grammarMessage), test[i].expected,
test[i].description)
}
})
}