Create custom Translation component for better i18n handling (#4431)

* Initial plan

* Implement Translation component with Storybook stories

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Add Jest test for Translation component and demonstrate ?lang=de functionality

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Javascript formatting autofixes

* Create centralized type-safe localization system

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Add @testing-library/react to Translation component tests

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Fix code formatting errors with prettier and eslint

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Revert "Fix code formatting errors with prettier and eslint"

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* fix(js): eslinter errors

* fix(js): unused code warnings

* fix(js): fix additional warnings

* Update Emoji admin page to use new Translation component

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Organize localization keys by logical sections (Frontend, Admin, Common, Testing)

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Organize localization keys by TypeScript namespaces

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* Javascript formatting autofixes

* feat(js): add support for default translated text

* chore: add default lang translations on commit

* fix(js): unused code warnings

* Update OfflineBanner component to use new Translation component

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* fix(js): fix localization extraction job

* chore(js): remove ts-node cli

* fix(css): fix css warning

* feat(js): add some additional translation strings via component

* chore: update extracted translations

* test: add tests for Translation component defaultText and fallback behavior

Co-authored-by: gabek <414923+gabek@users.noreply.github.com>

* chore: update extracted translations

* Javascript formatting autofixes

* chore: call out new Translation component

* fix: linter warning

* chore: updated instructions

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: gabek <414923+gabek@users.noreply.github.com>
Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
This commit is contained in:
Copilot
2025-07-13 02:21:46 -07:00
committed by GitHub
co-authored by gabek copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Owncast Gabe Kangas
parent 7b181aa0a6
commit 29b5100114
23 changed files with 2287 additions and 325 deletions
+97
View File
@@ -0,0 +1,97 @@
import { Meta, StoryObj } from '@storybook/react';
import { Translation } from '../components/ui/Translation/Translation';
import { Localization } from '../types/localization';
const meta: Meta<typeof Translation> = {
title: 'owncast/Components/Translation',
component: Translation,
parameters: {
chromatic: { diffThreshold: 0.8 },
},
argTypes: {
translationKey: {
control: 'text',
description: 'The translation key to use for the text',
},
vars: {
control: 'object',
description: 'Variables to interpolate into the translation',
},
className: {
control: 'text',
description: 'CSS class name to apply to the component',
},
},
};
export default meta;
type Story = StoryObj<typeof Translation>;
export const SimpleTranslation: Story = {
args: {
translationKey: Localization.Frontend.chatOffline,
},
};
export const TranslationWithVariable: Story = {
args: {
translationKey: Localization.Frontend.lastLiveAgo,
vars: {
timeAgo: '2 hours',
},
},
};
export const ComplexHTMLTranslation: Story = {
args: {
translationKey: Localization.Frontend.helloWorld,
vars: {
name: 'Gabe',
},
},
};
export const NotificationMessage: Story = {
args: {
translationKey: Localization.Frontend.notificationMessage,
vars: {
streamer: 'MyAwesomeStream',
},
},
};
export const ComplexMessage: Story = {
args: {
translationKey: Localization.Frontend.complexMessage,
vars: {
count: 42,
status: 'live',
},
},
};
export const WithCustomClass: Story = {
args: {
translationKey: Localization.Frontend.helloWorld,
vars: {
name: 'Styled User',
},
className: 'custom-translation-style',
},
};
export const TestDifferentLanguages: Story = {
args: {
translationKey: Localization.Frontend.helloWorld,
vars: {
name: 'Test User',
},
},
parameters: {
docs: {
description: {
story: 'Test different languages by adding ?lang=de or ?lang=fr to the URL',
},
},
},
};