fix(web): FormatMessage development keys (#10536)

This commit is contained in:
Michel Heusschen 2024-06-22 18:08:56 +02:00 committed by GitHub
parent 4cb165304b
commit 6164640575
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 46 additions and 44 deletions

View file

@ -2,13 +2,10 @@ import FormatTagB from '$lib/components/i18n/__test__/format-tag-b.svelte';
import FormatMessage from '$lib/components/i18n/format-message.svelte';
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/svelte';
import { init, json, locale, register, waitLocale } from 'svelte-i18n';
import { get } from 'svelte/store';
import { init, locale, register, waitLocale } from 'svelte-i18n';
import { describe } from 'vitest';
describe('FormatMessage component', () => {
let $json: (id: string, locale?: string | undefined) => unknown;
beforeAll(async () => {
register('en', () =>
Promise.resolve({
@ -21,12 +18,11 @@ describe('FormatMessage component', () => {
await init({ fallbackLocale: 'en' });
await waitLocale('en');
$json = get(json);
});
it('formats a plain text message', () => {
render(FormatMessage, {
message: $json('hello'),
key: 'hello',
values: { name: 'test' },
});
expect(screen.getByText('Hello test')).toBeInTheDocument();
@ -34,20 +30,20 @@ describe('FormatMessage component', () => {
it('throws an error when locale is empty', async () => {
await locale.set(undefined);
expect(() => render(FormatMessage, { message: undefined })).toThrowError();
expect(() => render(FormatMessage, { key: '' })).toThrowError();
await locale.set('en');
});
it('shows raw message when value is empty', () => {
render(FormatMessage, {
message: $json('hello'),
key: 'hello',
});
expect(screen.getByText('Hello {name}')).toBeInTheDocument();
});
it('shows message when slot is empty', () => {
render(FormatMessage, {
message: $json('html'),
key: 'html',
values: { name: 'test' },
});
expect(screen.getByText('Hello test')).toBeInTheDocument();
@ -55,7 +51,7 @@ describe('FormatMessage component', () => {
it('renders a message with html', () => {
const { container } = render(FormatTagB, {
message: $json('html'),
key: 'html',
values: { name: 'test' },
});
expect(container.innerHTML).toBe('Hello <strong>test</strong>');
@ -63,7 +59,7 @@ describe('FormatMessage component', () => {
it('renders a message with html and plural', () => {
const { container } = render(FormatTagB, {
message: $json('plural'),
key: 'plural',
values: { count: 1 },
});
expect(container.innerHTML).toBe('You have <strong>1 item</strong>');
@ -71,8 +67,13 @@ describe('FormatMessage component', () => {
it('protects agains XSS injection', () => {
render(FormatMessage, {
message: $json('xss'),
key: 'xss',
});
expect(screen.getByText('<image/src/onerror=prompt(8)>')).toBeInTheDocument();
});
it('displays the message key when not found', () => {
render(FormatMessage, { key: 'invalid.key' });
expect(screen.getByText('invalid.key')).toBeInTheDocument();
});
});

View file

@ -2,11 +2,11 @@
import FormatMessage from '../format-message.svelte';
import type { ComponentProps } from 'svelte';
export let message: unknown;
export let key: string;
export let values: ComponentProps<FormatMessage>['values'];
</script>
<FormatMessage {message} {values} let:tag let:message>
<FormatMessage {key} {values} let:tag let:message>
{#if tag === 'b'}
<strong>{message}</strong>
{/if}

View file

@ -1,11 +1,11 @@
<script lang="ts">
import { IntlMessageFormat, type FormatXMLElementFn, type PrimitiveType } from 'intl-messageformat';
import { TYPE, type MessageFormatElement } from '@formatjs/icu-messageformat-parser';
import { locale as i18nLocale } from 'svelte-i18n';
import { locale as i18nLocale, json } from 'svelte-i18n';
type InterpolationValues = Record<string, PrimitiveType | FormatXMLElementFn<unknown>>;
export let message: unknown;
export let key: string;
export let values: InterpolationValues = {};
const getLocale = (locale?: string | null) => {
@ -16,13 +16,13 @@
return locale;
};
const getElements = (message: unknown, locale: string): MessageFormatElement[] => {
const getElements = (message: string, locale: string): MessageFormatElement[] => {
return new IntlMessageFormat(message as string, locale, undefined, {
ignoreTag: false,
}).getAst();
};
const getParts = (message: unknown, locale: string) => {
const getParts = (message: string, locale: string) => {
try {
const elements = getElements(message, locale);
@ -38,12 +38,13 @@
});
} catch (error) {
if (error instanceof Error) {
console.warn(`Message "${message}" has syntax error:`, error.message);
console.warn(`Message "${key}" has syntax error:`, error.message);
}
return [{ message: message as string, tag: undefined }];
}
};
$: message = ($json(key) as string) || key;
$: locale = getLocale($i18nLocale);
$: parts = getParts(message, locale);
</script>