mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
chore(web): enforce valid translation keys using typescript (#12106)
This commit is contained in:
parent
bab5ad7ebd
commit
9f5a3f1e84
7 changed files with 48 additions and 49 deletions
|
|
@ -2,7 +2,7 @@ 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, locale, register, waitLocale } from 'svelte-i18n';
|
||||
import { init, locale, register, waitLocale, type Translations } from 'svelte-i18n';
|
||||
import { describe } from 'vitest';
|
||||
|
||||
describe('FormatMessage component', () => {
|
||||
|
|
@ -25,7 +25,7 @@ describe('FormatMessage component', () => {
|
|||
|
||||
it('formats a plain text message', () => {
|
||||
render(FormatMessage, {
|
||||
key: 'hello',
|
||||
key: 'hello' as Translations,
|
||||
values: { name: 'test' },
|
||||
});
|
||||
expect(screen.getByText('Hello test')).toBeInTheDocument();
|
||||
|
|
@ -33,20 +33,20 @@ describe('FormatMessage component', () => {
|
|||
|
||||
it('throws an error when locale is empty', async () => {
|
||||
await locale.set(undefined);
|
||||
expect(() => render(FormatMessage, { key: '' })).toThrowError();
|
||||
expect(() => render(FormatMessage, { key: '' as Translations })).toThrowError();
|
||||
await locale.set('en');
|
||||
});
|
||||
|
||||
it('shows raw message when value is empty', () => {
|
||||
render(FormatMessage, {
|
||||
key: 'hello',
|
||||
key: 'hello' as Translations,
|
||||
});
|
||||
expect(screen.getByText('Hello {name}')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows message when slot is empty', () => {
|
||||
render(FormatMessage, {
|
||||
key: 'html',
|
||||
key: 'html' as Translations,
|
||||
values: { name: 'test' },
|
||||
});
|
||||
expect(screen.getByText('Hello test')).toBeInTheDocument();
|
||||
|
|
@ -54,7 +54,7 @@ describe('FormatMessage component', () => {
|
|||
|
||||
it('renders a message with html', () => {
|
||||
const { container } = render(FormatTagB, {
|
||||
key: 'html',
|
||||
key: 'html' as Translations,
|
||||
values: { name: 'test' },
|
||||
});
|
||||
expect(container.innerHTML).toBe('Hello <strong>test</strong>');
|
||||
|
|
@ -62,7 +62,7 @@ describe('FormatMessage component', () => {
|
|||
|
||||
it('renders a message with html and plural', () => {
|
||||
const { container } = render(FormatTagB, {
|
||||
key: 'plural',
|
||||
key: 'plural' as Translations,
|
||||
values: { count: 1 },
|
||||
});
|
||||
expect(container.innerHTML).toBe('You have <strong>1 item</strong>');
|
||||
|
|
@ -70,19 +70,19 @@ describe('FormatMessage component', () => {
|
|||
|
||||
it('protects agains XSS injection', () => {
|
||||
render(FormatMessage, {
|
||||
key: 'xss',
|
||||
key: 'xss' as Translations,
|
||||
});
|
||||
expect(screen.getByText('<image/src/onerror=prompt(8)>')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays the message key when not found', () => {
|
||||
render(FormatMessage, { key: 'invalid.key' });
|
||||
render(FormatMessage, { key: 'invalid.key' as Translations });
|
||||
expect(screen.getByText('invalid.key')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('supports html tags inside plurals', () => {
|
||||
const { container } = render(FormatTagB, {
|
||||
key: 'plural_with_html',
|
||||
key: 'plural_with_html' as Translations,
|
||||
values: { count: 10 },
|
||||
});
|
||||
expect(container.innerHTML).toBe('You have <strong>10</strong> items');
|
||||
|
|
@ -90,7 +90,7 @@ describe('FormatMessage component', () => {
|
|||
|
||||
it('supports html tags inside select', () => {
|
||||
const { container } = render(FormatTagB, {
|
||||
key: 'select_with_html',
|
||||
key: 'select_with_html' as Translations,
|
||||
values: { status: true },
|
||||
});
|
||||
expect(container.innerHTML).toBe('Item is <strong>disabled</strong>');
|
||||
|
|
@ -98,7 +98,7 @@ describe('FormatMessage component', () => {
|
|||
|
||||
it('supports html tags inside selectordinal', () => {
|
||||
const { container } = render(FormatTagB, {
|
||||
key: 'ordinal_with_html',
|
||||
key: 'ordinal_with_html' as Translations,
|
||||
values: { count: 4 },
|
||||
});
|
||||
expect(container.innerHTML).toBe('<strong>4th</strong> item');
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<script lang="ts">
|
||||
import type { Translations } from 'svelte-i18n';
|
||||
import FormatMessage from '../format-message.svelte';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
export let key: string;
|
||||
export let key: Translations;
|
||||
export let values: ComponentProps<FormatMessage>['values'];
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<script lang="ts">
|
||||
import FormatMessage from '$lib/components/i18n/format-message.svelte';
|
||||
import type { InterpolationValues } from '$lib/components/i18n/format-message.svelte';
|
||||
import type { Translations } from 'svelte-i18n';
|
||||
|
||||
export let key: string;
|
||||
export let key: Translations;
|
||||
export let values: InterpolationValues = {};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@
|
|||
type PluralElement,
|
||||
type SelectElement,
|
||||
} from '@formatjs/icu-messageformat-parser';
|
||||
import { locale as i18nLocale, json } from 'svelte-i18n';
|
||||
import { locale as i18nLocale, json, type Translations } from 'svelte-i18n';
|
||||
|
||||
type MessagePart = {
|
||||
message: string;
|
||||
tag?: string;
|
||||
};
|
||||
|
||||
export let key: string;
|
||||
export let key: Translations;
|
||||
export let values: InterpolationValues = {};
|
||||
|
||||
const getLocale = (locale?: string | null) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue