Skip to content

Commit

Permalink
feat(i18n): allow setting i18n namespaces via an environment variable (
Browse files Browse the repository at this point in the history
  • Loading branch information
aptmac authored Jan 9, 2025
1 parent d63322c commit 534f939
Show file tree
Hide file tree
Showing 91 changed files with 410 additions and 421 deletions.
8 changes: 4 additions & 4 deletions LOCALIZATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ Cryostat-web uses [i18next](https://www.i18next.com/) as an internationalization

### Adding a new translation

The current list of language locales supported in Cryostat can be found in `src/i18n/config.ts`. The translations themselves can be found in `locales/{LOCALE_CODE}`
The current list of language locales supported in Cryostat can be found in `src/i18n/i18nextUtil.ts`. The translations themselves can be found in `locales/{LOCALE_CODE}`

To add a new language, add a new entry to the `i18nResources` object in `src/i18n.ts`. The key should be the language locale, and the value should be the translation object containing the corresponding namespace json files in `locales`.
To add a new language, add a new entry to the `i18nResources` object in `src/i18nextUtil.ts`. The key should be the language locale, and the value should be the translation object containing the corresponding namespace json files in `locales`.

To add a new localization key for a user-facing string in `cryostat-web`, use the `t` function from `react-i18next`:

```tsx
import { useTranslation } from 'react-i18next';
import { useCryostatTranslation } from 'react-i18next';
...
export const SomeFC = (props) => {
const { t } = useTranslation();
const { t } = useCryostatTranslation();

return (
<div>
Expand Down
6 changes: 3 additions & 3 deletions src/app/About/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ import { BreadcrumbPage } from '@app/BreadcrumbPage/BreadcrumbPage';
import build from '@app/build.json';
import { ThemeSetting } from '@app/Settings/types';
import { useTheme } from '@app/utils/hooks/useTheme';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import { Brand, Card, CardBody, CardFooter, CardHeader } from '@patternfly/react-core';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { AboutDescription } from './AboutDescription';

export interface AboutProps {}

export const About: React.FC<AboutProps> = (_) => {
const { t } = useTranslation('public');
const { t } = useCryostatTranslation();
const [theme] = useTheme();

const logo = React.useMemo(() => (theme === ThemeSetting.DARK ? cryostatLogoDark : cryostatLogo), [theme]);
Expand All @@ -42,7 +42,7 @@ export const About: React.FC<AboutProps> = (_) => {
<CardBody>
<AboutDescription />
</CardBody>
<CardFooter>{t('CRYOSTAT_TRADEMARK', { ns: 'common' })}</CardFooter>
<CardFooter>{t('CRYOSTAT_TRADEMARK')}</CardFooter>
</Card>
</BreadcrumbPage>
);
Expand Down
6 changes: 3 additions & 3 deletions src/app/About/AboutCryostatModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import bkgImg from '@app/assets/cryostat_icon_bg.svg';
import cryostatLogo from '@app/assets/cryostat_icon_rgb_reverse.svg';
import build from '@app/build.json';
import { portalRoot } from '@app/utils/utils';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import { AboutModal } from '@patternfly/react-core';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { AboutDescription } from './AboutDescription';

export interface AboutCryostatModalProps {
Expand All @@ -28,7 +28,7 @@ export interface AboutCryostatModalProps {
}

export const AboutCryostatModal: React.FC<AboutCryostatModalProps> = ({ isOpen, onClose }) => {
const { t } = useTranslation();
const { t } = useCryostatTranslation();
return (
<AboutModal
appendTo={portalRoot}
Expand All @@ -37,7 +37,7 @@ export const AboutCryostatModal: React.FC<AboutCryostatModalProps> = ({ isOpen,
brandImageAlt="Cryostat Logo"
isOpen={isOpen}
onClose={onClose}
trademark={t('CRYOSTAT_TRADEMARK', { ns: 'common' })}
trademark={t('CRYOSTAT_TRADEMARK')}
backgroundImageSrc={bkgImg}
>
<AboutDescription />
Expand Down
4 changes: 2 additions & 2 deletions src/app/About/AboutDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { BuildInfo } from '@app/Shared/Services/api.types';
import { NotificationsContext } from '@app/Shared/Services/Notifications.service';
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import { Text, TextContent, TextList, TextListItem, TextVariants } from '@patternfly/react-core';
import * as React from 'react';
import { useTranslation } from 'react-i18next';

export const VERSION_REGEX = /^(v?[0-9]+\.[0-9]+\.[0-9]+)(?:-(.+))?$/;

Expand All @@ -30,7 +30,7 @@ export const AboutDescription: React.FC = () => {
const notificationsContext = React.useContext(NotificationsContext);
const [cryostatVersion, setCryostatVersion] = React.useState(undefined as string | undefined);
const [buildInfo, setBuildInfo] = React.useState<BuildInfo>({ git: { hash: '' } });
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const addSubscription = useSubscriptions();

React.useEffect(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/app/Agent/AgentLiveProbes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { EventProbe, NotificationCategory } from '@app/Shared/Services/api.types
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { sortResources, TableColumn } from '@app/utils/utils';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import {
Button,
Toolbar,
Expand Down Expand Up @@ -51,7 +52,6 @@ import {
} from '@patternfly/react-table';
import _ from 'lodash';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { combineLatest } from 'rxjs';
import { AboutAgentCard } from './AboutAgentCard';

Expand Down Expand Up @@ -89,7 +89,7 @@ export interface AgentLiveProbesProps {}

export const AgentLiveProbes: React.FC<AgentLiveProbesProps> = () => {
const context = React.useContext(ServiceContext);
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const addSubscription = useSubscriptions();

const [probes, setProbes] = React.useState<EventProbe[]>([]);
Expand Down
14 changes: 7 additions & 7 deletions src/app/Agent/AgentProbeTemplates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ProbeTemplate, NotificationCategory } from '@app/Shared/Services/api.ty
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { TableColumn, portalRoot, sortResources } from '@app/utils/utils';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import {
ActionGroup,
Button,
Expand Down Expand Up @@ -62,7 +63,6 @@ import {
} from '@patternfly/react-table';
import _ from 'lodash';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { forkJoin, Observable, of } from 'rxjs';
import { catchError, defaultIfEmpty, first, tap } from 'rxjs/operators';
import { AboutAgentCard } from './AboutAgentCard';
Expand All @@ -86,7 +86,7 @@ export interface AgentProbeTemplatesProps {

export const AgentProbeTemplates: React.FC<AgentProbeTemplatesProps> = ({ agentDetected }) => {
const context = React.useContext(ServiceContext);
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const addSubscription = useSubscriptions();

const [templates, setTemplates] = React.useState<ProbeTemplate[]>([]);
Expand Down Expand Up @@ -365,7 +365,7 @@ export interface AgentProbeTemplateUploadModalProps {
}

export const AgentProbeTemplateUploadModal: React.FC<AgentProbeTemplateUploadModalProps> = ({ onClose, isOpen }) => {
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const addSubscription = useSubscriptions();
const context = React.useContext(ServiceContext);
const submitRef = React.useRef<HTMLDivElement>(null); // Use ref to refer to submit trigger div
Expand Down Expand Up @@ -478,7 +478,7 @@ export const AgentProbeTemplateUploadModal: React.FC<AgentProbeTemplateUploadMod
<ActionGroup>
{allOks && numOfFiles ? (
<Button variant="primary" onClick={handleClose}>
{t('CLOSE', { ns: 'common' })}
{t('CLOSE')}
</Button>
) : (
<>
Expand All @@ -488,10 +488,10 @@ export const AgentProbeTemplateUploadModal: React.FC<AgentProbeTemplateUploadMod
isDisabled={!numOfFiles || uploading}
{...submitButtonLoadingProps}
>
{t('SUBMIT', { ns: 'common' })}
{t('SUBMIT')}
</Button>
<Button variant="link" onClick={handleClose}>
{t('CANCEL', { ns: 'common' })}
{t('CANCEL')}
</Button>
</>
)}
Expand All @@ -508,7 +508,7 @@ export interface AgentTemplateActionProps {
}

export const AgentTemplateAction: React.FC<AgentTemplateActionProps> = ({ onInsert, onDelete, template }) => {
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const [isOpen, setIsOpen] = React.useState(false);

const actionItems = React.useMemo(() => {
Expand Down
5 changes: 3 additions & 2 deletions src/app/AppLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { useTheme } from '@app/utils/hooks/useTheme';
import { saveToLocalStorage } from '@app/utils/LocalStorage';
import { cleanDataId, isAssetNew, openTabForUrl, portalRoot } from '@app/utils/utils';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import {
Alert,
AlertActionCloseButton,
Expand Down Expand Up @@ -79,7 +80,7 @@ import {
} from '@patternfly/react-icons';
import _ from 'lodash';
import * as React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';
import { Link, matchPath, NavLink, useLocation, useNavigate } from 'react-router-dom';
import { map } from 'rxjs/operators';
import { LogoutIcon } from './LogoutIcon';
Expand All @@ -93,7 +94,7 @@ export const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
const serviceContext = React.useContext(ServiceContext);
const notificationsContext = React.useContext(NotificationsContext);
const addSubscription = useSubscriptions();
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const {
setState: setJoyState,
state: joyState,
Expand Down
4 changes: 2 additions & 2 deletions src/app/AppLayout/ThemeToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
import { ThemeSetting } from '@app/Settings/types';
import { ServiceContext } from '@app/Shared/Services/Services';
import { useTheme } from '@app/utils/hooks/useTheme';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import { Icon, ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
import { SunIcon, MoonIcon } from '@patternfly/react-icons';
import * as React from 'react';
import { useTranslation } from 'react-i18next';

export interface ThemeToggleProps {}

export const ThemeToggle: React.FC<ThemeToggleProps> = () => {
const context = React.useContext(ServiceContext);
const [_theme] = useTheme();
const { t } = useTranslation();
const { t } = useCryostatTranslation();

const handleThemeSelect = React.useCallback(
(_, setting: ThemeSetting) => {
Expand Down
4 changes: 2 additions & 2 deletions src/app/Archives/AllArchivedRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ServiceContext } from '@app/Shared/Services/Services';
import { useSort } from '@app/utils/hooks/useSort';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { TableColumn, portalRoot, sortResources } from '@app/utils/utils';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import {
Toolbar,
ToolbarContent,
Expand Down Expand Up @@ -54,7 +55,6 @@ import {
} from '@patternfly/react-table';
import _ from 'lodash';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { Observable, of } from 'rxjs';
import { getTargetFromDirectory, includesDirectory, indexOfDirectory } from './utils';

Expand Down Expand Up @@ -82,7 +82,7 @@ export interface AllArchivedRecordingsTableProps {}

export const AllArchivedRecordingsTable: React.FC<AllArchivedRecordingsTableProps> = () => {
const context = React.useContext(ServiceContext);
const { t } = useTranslation();
const { t } = useCryostatTranslation();

const [directories, setDirectories] = React.useState<_RecordingDirectory[]>([]);
const [searchText, setSearchText] = React.useState('');
Expand Down
4 changes: 2 additions & 2 deletions src/app/Archives/AllTargetsArchivedRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ServiceContext } from '@app/Shared/Services/Services';
import { useSort } from '@app/utils/hooks/useSort';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { hashCode, sortResources, TableColumn } from '@app/utils/utils';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import {
Toolbar,
ToolbarContent,
Expand Down Expand Up @@ -53,7 +54,6 @@ import {
import { TFunction } from 'i18next';
import _ from 'lodash';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';

Expand Down Expand Up @@ -103,7 +103,7 @@ export interface AllTargetsArchivedRecordingsTableProps {}

export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecordingsTableProps> = () => {
const context = React.useContext(ServiceContext);
const { t } = useTranslation();
const { t } = useCryostatTranslation();

const [searchText, setSearchText] = React.useState('');
const [archivesForTargets, setArchivesForTargets] = React.useState<ArchivesForTarget[]>([]);
Expand Down
10 changes: 5 additions & 5 deletions src/app/Dashboard/AddCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { fakeChartContext, fakeServices } from '@app/utils/fakeData';
import { useFeatureLevel } from '@app/utils/hooks/useFeatureLevel';
import { useSubscriptions } from '@app/utils/hooks/useSubscriptions';
import { portalRoot } from '@app/utils/utils';
import { useCryostatTranslation } from '@i18n/i18nextUtil';
import { CatalogTile, CatalogTileBadge } from '@patternfly/react-catalog-view-extension';
import {
Bullseye,
Expand Down Expand Up @@ -79,7 +80,6 @@ import { PlusCircleIcon } from '@patternfly/react-icons';
import { TFunction } from 'i18next';
import { nanoid } from 'nanoid';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { Observable, of } from 'rxjs';
import { ChartContext } from './Charts/context';
Expand All @@ -92,7 +92,7 @@ export interface AddCardProps {

export const AddCard: React.FC<AddCardProps> = ({ variant }) => {
const dispatch = useDispatch<StateDispatch>();
const { t } = useTranslation();
const { t } = useCryostatTranslation();

const [showWizard, setShowWizard] = React.useState(false);
const [selection, setSelection] = React.useState('');
Expand Down Expand Up @@ -240,7 +240,7 @@ export const AddCard: React.FC<AddCardProps> = ({ variant }) => {
>
<WizardStep
id="card-type-select"
name={t('CARD_TYPE', { ns: 'common' })}
name={t('CARD_TYPE')}
footer={{
isNextDisabled: !selection,
nextButtonText:
Expand Down Expand Up @@ -312,7 +312,7 @@ export interface CardGalleryProps {
}

export const CardGallery: React.FC<CardGalleryProps> = ({ selection, onSelect }) => {
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const activeLevel = useFeatureLevel();
const [toViewCard, setToViewCard] = React.useState<DashboardCardDescriptor>();

Expand Down Expand Up @@ -444,7 +444,7 @@ interface PropsConfigFormProps {
}

const PropsConfigForm: React.FC<PropsConfigFormProps> = ({ onChange, ...props }) => {
const { t } = useTranslation();
const { t } = useCryostatTranslation();
const handleChange = React.useCallback(
(k: string) => (e: unknown) => {
const copy = { ...props.config };
Expand Down
Loading

0 comments on commit 534f939

Please sign in to comment.