mirror of
https://github.com/cupcakearmy/svelte-i18n.git
synced 2024-11-16 09:59:58 +01:00
test: 💍 add tests for formatters
This commit is contained in:
parent
300f965a87
commit
850fd09f32
@ -6,5 +6,8 @@
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/camelcase": "off"
|
||||
}
|
||||
}
|
||||
|
@ -3,47 +3,52 @@ import memoize from 'fast-memoize'
|
||||
|
||||
import { MemoizedIntlFormatter } from '../types'
|
||||
import { getCurrentLocale } from '../stores/locale'
|
||||
import { getFormats } from '../configs'
|
||||
import { getOptions } from '../configs'
|
||||
|
||||
const getIntlFormatterOptions = (
|
||||
type: 'time' | 'number' | 'date',
|
||||
name: string
|
||||
): any => {
|
||||
const formats = getFormats()
|
||||
const formats = getOptions().formats
|
||||
if (type in formats && name in formats[type]) {
|
||||
return formats[type][name]
|
||||
}
|
||||
|
||||
if (
|
||||
type in IntlMessageFormat.formats &&
|
||||
name in IntlMessageFormat.formats[type]
|
||||
) {
|
||||
return (IntlMessageFormat.formats[type] as any)[name]
|
||||
}
|
||||
|
||||
return null
|
||||
throw new Error(`[svelte-i18n] Unknown "${name}" ${type} format.`)
|
||||
}
|
||||
|
||||
export const getNumberFormatter: MemoizedIntlFormatter<
|
||||
Intl.NumberFormat,
|
||||
Intl.NumberFormatOptions
|
||||
> = memoize((options = {}) => {
|
||||
const locale = options.locale || getCurrentLocale()
|
||||
if (options.format) {
|
||||
const format = getIntlFormatterOptions('number', options.format)
|
||||
if (format) options = format
|
||||
> = memoize(({ locale, format, ...options } = {}) => {
|
||||
locale = locale || getCurrentLocale()
|
||||
if (locale == null) {
|
||||
throw new Error('[svelte-i18n] A "locale" must be set to format numbers')
|
||||
}
|
||||
|
||||
if (format) {
|
||||
options = getIntlFormatterOptions('number', format) || {}
|
||||
}
|
||||
|
||||
return new Intl.NumberFormat(locale, options)
|
||||
})
|
||||
|
||||
export const getDateFormatter: MemoizedIntlFormatter<
|
||||
Intl.DateTimeFormat,
|
||||
Intl.DateTimeFormatOptions
|
||||
> = memoize((options = { format: 'short' }) => {
|
||||
const locale = options.locale || getCurrentLocale()
|
||||
> = memoize(({ locale, format, ...options } = {}) => {
|
||||
locale = locale || getCurrentLocale()
|
||||
if (locale == null) {
|
||||
throw new Error('[svelte-i18n] A "locale" must be set to format dates')
|
||||
}
|
||||
|
||||
const format = getIntlFormatterOptions('date', options.format)
|
||||
if (format) options = format
|
||||
const hasInlineArgs = Object.keys(options).length > 0
|
||||
if (!hasInlineArgs) {
|
||||
options =
|
||||
typeof format === 'string'
|
||||
? getIntlFormatterOptions('date', format)
|
||||
: getIntlFormatterOptions('date', 'short')
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat(locale, options)
|
||||
})
|
||||
@ -51,16 +56,26 @@ export const getDateFormatter: MemoizedIntlFormatter<
|
||||
export const getTimeFormatter: MemoizedIntlFormatter<
|
||||
Intl.DateTimeFormat,
|
||||
Intl.DateTimeFormatOptions
|
||||
> = memoize((options = { format: 'short' }) => {
|
||||
const locale = options.locale || getCurrentLocale()
|
||||
> = memoize(({ locale, format, ...options } = {}) => {
|
||||
locale = locale || getCurrentLocale()
|
||||
if (locale == null) {
|
||||
throw new Error(
|
||||
'[svelte-i18n] A "locale" must be set to format time values'
|
||||
)
|
||||
}
|
||||
|
||||
const format = getIntlFormatterOptions('time', options.format)
|
||||
if (format) options = format
|
||||
const hasInlineArgs = Object.keys(options).length > 0
|
||||
if (!hasInlineArgs) {
|
||||
options =
|
||||
typeof format === 'string'
|
||||
? getIntlFormatterOptions('time', format)
|
||||
: getIntlFormatterOptions('time', 'short')
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat(locale, options)
|
||||
})
|
||||
|
||||
export const getMessageFormatter = memoize(
|
||||
(message: string, locale: string) =>
|
||||
new IntlMessageFormat(message, locale, getFormats())
|
||||
new IntlMessageFormat(message, locale, getOptions().formats)
|
||||
)
|
||||
|
@ -22,7 +22,7 @@ type IntlFormatterOptions<T> = T & {
|
||||
}
|
||||
|
||||
export interface MemoizedIntlFormatter<T, U> {
|
||||
(options: IntlFormatterOptions<U>): T
|
||||
(options?: IntlFormatterOptions<U>): T
|
||||
}
|
||||
|
||||
export interface Formatter extends FormatterFn {
|
||||
|
154
test/client/formatter.test.ts
Normal file
154
test/client/formatter.test.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import {
|
||||
getNumberFormatter,
|
||||
getDateFormatter,
|
||||
getTimeFormatter,
|
||||
getMessageFormatter,
|
||||
} from '../../src/client/includes/formatters'
|
||||
import { init } from '../../src/client/configs'
|
||||
|
||||
beforeEach(() => {
|
||||
init({ fallbackLocale: undefined })
|
||||
})
|
||||
|
||||
describe('number formatter', () => {
|
||||
const number = 123123
|
||||
test('should format a date according to the current locale', () => {
|
||||
init({ fallbackLocale: 'en' })
|
||||
expect(getNumberFormatter().format(number)).toBe('123,123')
|
||||
})
|
||||
|
||||
test('should format a number according to a locale', () => {
|
||||
init({ fallbackLocale: 'en' })
|
||||
expect(getNumberFormatter({ locale: 'pt-BR' }).format(number)).toBe(
|
||||
'123.123'
|
||||
)
|
||||
})
|
||||
|
||||
test('should format a number with a custom format', () => {
|
||||
init({
|
||||
fallbackLocale: 'en',
|
||||
formats: require('../fixtures/formats.json'),
|
||||
})
|
||||
|
||||
expect(getNumberFormatter({ format: 'brl' }).format(number)).toBe(
|
||||
'R$123,123.00'
|
||||
)
|
||||
})
|
||||
|
||||
test('should format a number with inline options', () => {
|
||||
init({ fallbackLocale: 'en' })
|
||||
|
||||
expect(
|
||||
getNumberFormatter({ style: 'currency', currency: 'BRL' }).format(number)
|
||||
).toBe('R$123,123.00')
|
||||
})
|
||||
})
|
||||
|
||||
describe('date formatter', () => {
|
||||
const date = new Date(2019, 1, 1)
|
||||
|
||||
test('should format a date according to the current locale', () => {
|
||||
init({ fallbackLocale: 'en' })
|
||||
expect(getDateFormatter().format(date)).toBe('2/1/19')
|
||||
})
|
||||
|
||||
test('should format a date according to a locale', () => {
|
||||
expect(getDateFormatter({ locale: 'pt-BR' }).format(date)).toBe('01/02/19')
|
||||
})
|
||||
|
||||
test('should throw if passed a non-existing format', () => {
|
||||
init({
|
||||
fallbackLocale: 'en',
|
||||
formats: require('../fixtures/formats.json'),
|
||||
})
|
||||
|
||||
expect(() =>
|
||||
getDateFormatter({ locale: 'pt-BR', format: 'foo' }).format(date)
|
||||
).toThrowError(`[svelte-i18n] Unknown "foo" date format.`)
|
||||
})
|
||||
|
||||
test('should format a date with a custom format', () => {
|
||||
init({
|
||||
fallbackLocale: 'en',
|
||||
formats: require('../fixtures/formats.json'),
|
||||
})
|
||||
|
||||
expect(getDateFormatter({ format: 'customDate' }).format(date)).toBe(
|
||||
'2019 AD'
|
||||
)
|
||||
})
|
||||
|
||||
test('should format a date with inline options', () => {
|
||||
init({ fallbackLocale: 'en' })
|
||||
|
||||
expect(
|
||||
getDateFormatter({ year: 'numeric', era: 'short' }).format(date)
|
||||
).toBe('2019 AD')
|
||||
})
|
||||
})
|
||||
|
||||
describe('time formatter', () => {
|
||||
const time = new Date(2019, 1, 1, 20, 37, 32)
|
||||
|
||||
test('should format a time according to the current locale', () => {
|
||||
init({ fallbackLocale: 'en' })
|
||||
expect(getTimeFormatter().format(time)).toBe('8:37 PM')
|
||||
})
|
||||
|
||||
test('should format a time according to a locale', () => {
|
||||
expect(getTimeFormatter({ locale: 'pt-BR' }).format(time)).toBe('20:37')
|
||||
})
|
||||
|
||||
test('should format a time with a custom format', () => {
|
||||
init({
|
||||
fallbackLocale: 'en',
|
||||
formats: require('../fixtures/formats.json'),
|
||||
})
|
||||
|
||||
expect(getTimeFormatter({ format: 'customTime' }).format(time)).toBe(
|
||||
'08:37:32 PM'
|
||||
)
|
||||
})
|
||||
|
||||
test('should throw if passed a non-existing format', () => {
|
||||
init({
|
||||
fallbackLocale: 'en',
|
||||
formats: require('../fixtures/formats.json'),
|
||||
})
|
||||
|
||||
expect(() =>
|
||||
getTimeFormatter({ locale: 'pt-BR', format: 'foo' }).format(time)
|
||||
).toThrowError(`[svelte-i18n] Unknown "foo" time format.`)
|
||||
})
|
||||
|
||||
test('should format a time with inline options', () => {
|
||||
init({ fallbackLocale: 'en' })
|
||||
|
||||
expect(
|
||||
getTimeFormatter({
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}).format(time)
|
||||
).toBe('08:37:32 PM')
|
||||
})
|
||||
})
|
||||
|
||||
describe('message formatter', () => {
|
||||
test('formats a message with interpolated values', () => {
|
||||
expect(
|
||||
getMessageFormatter('Page: {current,number}/{max,number}', 'en').format({
|
||||
current: 2,
|
||||
max: 10,
|
||||
})
|
||||
).toBe('Page: 2/10')
|
||||
})
|
||||
|
||||
test('formats number with custom formats', () => {
|
||||
expect(
|
||||
getMessageFormatter('Number: {n, number, compactShort}', 'en').format({
|
||||
n: 2000000,
|
||||
})
|
||||
).toBe('Number: 2M')
|
||||
})
|
||||
})
|
@ -1,172 +0,0 @@
|
||||
// // TODO remake this, it's a mess
|
||||
// import { Formatter } from '../../src/client/types'
|
||||
// import {
|
||||
// dictionary,
|
||||
// locale,
|
||||
// format,
|
||||
// addCustomFormats,
|
||||
// customFormats,
|
||||
// register,
|
||||
// waitLocale,
|
||||
// } from '../../src/client'
|
||||
// import { getClientLocale } from '../../src/client/includes/utils'
|
||||
|
||||
// global.Intl = require('intl')
|
||||
|
||||
// let _: Formatter
|
||||
// let currentLocale: string
|
||||
|
||||
// const dict = {
|
||||
// en: require('../fixtures/en.json'),
|
||||
// }
|
||||
|
||||
// register('en-GB', () => import('../fixtures/en-GB.json'))
|
||||
// register('pt', () => import('../fixtures/pt.json'))
|
||||
// register('pt-BR', () => import('../fixtures/pt-BR.json'))
|
||||
// register('pt-PT', () => import('../fixtures/pt-PT.json'))
|
||||
|
||||
// format.subscribe(formatFn => {
|
||||
// _ = formatFn
|
||||
// })
|
||||
// dictionary.set(dict)
|
||||
// locale.subscribe((l: string) => {
|
||||
// currentLocale = l
|
||||
// })
|
||||
|
||||
// describe('locale', () => {
|
||||
// it('should change locale', async () => {
|
||||
// await locale.set('en')
|
||||
// expect(currentLocale).toBe('en')
|
||||
|
||||
// await locale.set('en-US')
|
||||
// expect(currentLocale).toBe('en-US')
|
||||
// })
|
||||
// })
|
||||
|
||||
// describe('dictionary', () => {
|
||||
// it('load a partial dictionary and merge it with the existing one', async () => {
|
||||
// await locale.set('en')
|
||||
// register('en', () => import('../fixtures/partials/en.json'))
|
||||
// expect(_('page.title_about')).toBe('page.title_about')
|
||||
|
||||
// await waitLocale('en')
|
||||
// expect(_('page.title_about')).toBe('About')
|
||||
// })
|
||||
// })
|
||||
|
||||
// describe('formatting', () => {
|
||||
// it('should translate to current locale', async () => {
|
||||
// await locale.set('en')
|
||||
// expect(_('switch.lang')).toBe('Switch language')
|
||||
// })
|
||||
|
||||
// it('should fallback to message id if id is not found', async () => {
|
||||
// await locale.set('en')
|
||||
// expect(_('batatinha.quente')).toBe('batatinha.quente')
|
||||
// })
|
||||
|
||||
// it('should fallback to default value if id is not found', async () => {
|
||||
// await locale.set('en')
|
||||
// expect(_('batatinha.quente', { default: 'Hot Potato' })).toBe('Hot Potato')
|
||||
// })
|
||||
|
||||
// it('should fallback to generic locale XX if id not found in XX-YY', async () => {
|
||||
// await locale.set('en-GB')
|
||||
// expect(_('sneakers', { locale: 'en-GB' })).toBe('trainers')
|
||||
// })
|
||||
|
||||
// it('should fallback to generic locale XX if id not found in XX-YY', async () => {
|
||||
// await locale.set('en-GB')
|
||||
// expect(_('switch.lang')).toBe('Switch language')
|
||||
// })
|
||||
|
||||
// it('should accept single object with id prop as the message path', async () => {
|
||||
// await locale.set('en')
|
||||
// expect(_({ id: 'switch.lang' })).toBe('Switch language')
|
||||
// })
|
||||
|
||||
// it('should translate to passed locale', async () => {
|
||||
// await waitLocale('pt-BR')
|
||||
// expect(_('switch.lang', { locale: 'pt' })).toBe('Trocar idioma')
|
||||
// })
|
||||
|
||||
// it('should interpolate message with variables', async () => {
|
||||
// await locale.set('en')
|
||||
// expect(_('greeting.message', { values: { name: 'Chris' } })).toBe(
|
||||
// 'Hello Chris, how are you?'
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
|
||||
// describe('utilities', () => {
|
||||
|
||||
// describe('format utils', () => {
|
||||
// beforeAll(async () => {
|
||||
// await locale.set('en')
|
||||
// })
|
||||
|
||||
// it('should capital a translated message', () => {
|
||||
// expect(_.capital('hi')).toBe('Hi yo')
|
||||
// })
|
||||
|
||||
// it('should title a translated message', () => {
|
||||
// expect(_.title('hi')).toBe('Hi Yo')
|
||||
// })
|
||||
|
||||
// it('should lowercase a translated message', () => {
|
||||
// expect(_.lower('hi')).toBe('hi yo')
|
||||
// })
|
||||
|
||||
// it('should uppercase a translated message', () => {
|
||||
// expect(_.upper('hi')).toBe('HI YO')
|
||||
// })
|
||||
|
||||
// const date = new Date(2019, 3, 24, 23, 45)
|
||||
// it('should format a time value', async () => {
|
||||
// await locale.set('en')
|
||||
// expect(_.time(date)).toBe('11:45 PM')
|
||||
// expect(_.time(date, { format: 'medium' })).toBe('11:45:00 PM')
|
||||
// expect(_.time(date, { format: 'medium', locale: 'pt-BR' })).toBe(
|
||||
// '23:45:00'
|
||||
// )
|
||||
// })
|
||||
|
||||
// it('should format a date value', () => {
|
||||
// expect(_.date(date)).toBe('4/24/19')
|
||||
// expect(_.date(date, { format: 'medium' })).toBe('Apr 24, 2019')
|
||||
// })
|
||||
// // number
|
||||
// it('should format a date value', () => {
|
||||
// expect(_.number(123123123)).toBe('123,123,123')
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
|
||||
// describe('custom formats', () => {
|
||||
// it('should format messages with custom formats', async () => {
|
||||
// addCustomFormats({
|
||||
// number: {
|
||||
// usd: { style: 'currency', currency: 'USD' },
|
||||
// brl: { style: 'currency', currency: 'BRL' },
|
||||
// },
|
||||
// date: {
|
||||
// customDate: { year: 'numeric', era: 'short' },
|
||||
// },
|
||||
// time: {
|
||||
// customTime: { hour: '2-digit', minute: '2-digit' },
|
||||
// },
|
||||
// })
|
||||
|
||||
// await locale.set('en-US')
|
||||
|
||||
// expect(_.number(123123123, { format: 'usd' })).toContain('$123,123,123.00')
|
||||
|
||||
// expect(_.date(new Date(2019, 0, 1), { format: 'customDate' })).toEqual(
|
||||
// '2019 AD'
|
||||
// )
|
||||
|
||||
// expect(
|
||||
// _.time(new Date(2019, 0, 1, 2, 0, 0), { format: 'customTime' })
|
||||
// ).toEqual('02:00')
|
||||
// })
|
||||
// })
|
@ -1,3 +1,5 @@
|
||||
import { get } from 'svelte/store'
|
||||
|
||||
import {
|
||||
getDictionary,
|
||||
hasLocaleDictionary,
|
||||
@ -8,7 +10,6 @@ import {
|
||||
$locales,
|
||||
getLocaleDictionary,
|
||||
} from '../../../src/client/stores/dictionary'
|
||||
import { get } from 'svelte/store'
|
||||
|
||||
beforeEach(() => {
|
||||
$dictionary.set({})
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { lookupMessage } from './../../../src/client/includes/lookup'
|
||||
import { get } from 'svelte/store'
|
||||
import { lookupMessage } from '../../../src/client/includes/lookup'
|
||||
|
||||
|
||||
import {
|
||||
isFallbackLocaleOf,
|
||||
|
6
test/fixtures/formats.json
vendored
6
test/fixtures/formats.json
vendored
@ -7,6 +7,10 @@
|
||||
"customDate": { "year": "numeric", "era": "short" }
|
||||
},
|
||||
"time": {
|
||||
"customTime": { "hour": "2-digit", "minute": "2-digit" }
|
||||
"customTime": {
|
||||
"hour": "2-digit",
|
||||
"minute": "2-digit",
|
||||
"second": "2-digit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user