Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions dynamic-demo-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"name": "@console/dynamic-demo-plugin",
"version": "0.0.0",
"private": true,
"resolutions": {
"@types/react": "17.0.39"
},
"scripts": {
"clean": "rm -rf dist",
"build": "yarn clean && NODE_ENV=production yarn ts-node node_modules/.bin/webpack",
Expand All @@ -20,7 +23,7 @@
"@patternfly/react-core": "^6.2.2",
"@patternfly/react-icons": "^6.2.2",
"@patternfly/react-table": "^6.2.2",
"@types/react": "16.8.13",
"@types/react": "17.0.39",
"@types/react-router": "^5.1.20",
"@types/react-router-dom": "5.3.x",
"copy-webpack-plugin": "^6.4.1",
Expand All @@ -29,9 +32,9 @@
"http-server": "0.12.x",
"i18next-parser": "^3.3.0",
"js-yaml": "^4.1.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-i18next": "^11.7.3",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-i18next": "^15.1.4",
"react-router": "5.3.x",
"react-router-dom": "5.3.x",
"react-router-dom-v5-compat": "^6.11.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as React from 'react';
import {
K8sResourceCommon,
useK8sWatchResource,
Expand Down
2,511 changes: 1,575 additions & 936 deletions dynamic-demo-plugin/yarn.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@
"graphql": "^14.0.0",
"history": "^4.9.0",
"hoist-non-react-statics": "3.x",
"i18next": "^21.10.0",
"i18next": "^23.16.8",
"i18next-browser-languagedetector": "^6.0.1",
"i18next-conv": "12.1.1",
"i18next-http-backend": "^1.0.21",
Expand All @@ -202,7 +202,7 @@
"react-dnd-html5-backend": "^11.1.3",
"react-dom": "^17.0.1",
"react-helmet-async": "^2.0.5",
"react-i18next": "^11.12.0",
"react-i18next": "^15.1.4",
"react-linkify": "^0.2.2",
"react-modal": "^3.16.3",
"react-redux": "7.2.9",
Expand Down
10 changes: 6 additions & 4 deletions frontend/packages/console-app/src/components/nodes/NodesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,12 @@ const useNodesColumns = (): TableColumn<NodeRowItem>[] => {
const CPUCell: React.FC<{ cores: number; totalCores: number }> = ({ cores, totalCores }) => {
const { t } = useTranslation();
return Number.isFinite(cores) && Number.isFinite(totalCores) ? (
t('console-app~{{formattedCores}} cores / {{totalCores}} cores', {
formattedCores: formatCores(cores),
totalCores,
})
<>
{t('console-app~{{formattedCores}} cores / {{totalCores}} cores', {
formattedCores: formatCores(cores),
totalCores,
})}
</>
) : (
<>{DASH}</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ describe('translateExtension', () => {
},
};

const t = jest.fn<string, [string]>((key) => `translated: ${key}`);
const mockFn = jest.fn((key) => `translated: ${key}`);
const t = mockFn;

expect(translateExtension(testExtension, t)).toEqual({
type: 'Foo/Bar',
Expand All @@ -59,8 +60,8 @@ describe('translateExtension', () => {
},
});

expect(t.mock.calls.length).toBe(5);
expect(t.mock.calls).toEqual([
expect(mockFn.mock.calls.length).toBe(5);
expect(mockFn.mock.calls).toEqual([
['%test~1%'],
['%test~3%'],
['%test~5%'],
Expand All @@ -71,9 +72,10 @@ describe('translateExtension', () => {

it('returns the same extension instance', () => {
const testExtension: Extension = { type: 'Foo/Bar', properties: {} };
const t = jest.fn<string, []>();
const mockFn = jest.fn((key) => key);
const t = mockFn;

expect(translateExtension(testExtension, t)).toBe(testExtension);
expect(t).not.toHaveBeenCalled();
expect(mockFn).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export const translateExtensionDeep = <E extends Extension>(
* Recursively updates the extension's properties, replacing all translatable string values
* via the provided `t` function.
*/
export const translateExtension = <E extends Extension>(extension: E, t: TFunction): E => {
export const translateExtension = <E extends Extension>(
extension: E,
t: TFunction | ((key: string) => string),
): E => {
translateExtensionDeep(extension, isTranslatableString, (value, key, obj) => {
obj[key] = t(value);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { useCallback } from 'react';
import { TFunction } from 'i18next';
import { Namespace, useTranslation, UseTranslationOptions } from 'react-i18next';
import { Namespace } from 'i18next';
import { useTranslation, UseTranslationOptions } from 'react-i18next';
import { isTranslatableString, getTranslationKey } from './extension-i18n';

/**
* Extends i18next `useTranslation` hook and overrides the `t` function.
*
* Translatable strings in Console application must use the `%key%` pattern.
*/
const useTranslationExt = (ns?: Namespace, options?: UseTranslationOptions) => {
const useTranslationExt = (ns?: Namespace, options?: UseTranslationOptions<undefined>) => {
const result = useTranslation(ns, options);
const { t } = result;
const cb: TFunction = useCallback(
const cb = useCallback(
(value: string) => (isTranslatableString(value) ? t(getTranslationKey(value)) : value),
[t],
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { TFunction } from 'i18next';
import { CatalogItem } from '@console/dynamic-plugin-sdk/src';
import { ConsoleSample } from '../../../../types';
import { normalizeConsoleSamples } from '../useConsoleSamples';
import { gitImportSample, containerImportSample } from './useConsoleSamples.data';

export const t: TFunction = (key: string) =>
export const t = (key: string): string =>
key.includes('~') ? key.substring(key.indexOf('~') + 1) : key;

describe('normalizeConsoleSamples', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
useSamples,
} from '../../../utils/samples';

export const normalizeConsoleSamples = (activeNamespace: string, t: TFunction) => {
export const normalizeConsoleSamples = (
activeNamespace: string,
t: TFunction | ((key: string) => string),
) => {
const createLabel = t('devconsole~Create');

return (sample: ConsoleSample): CatalogItem<ConsoleSample> | null => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ const {
} = submitUtils;

describe('Import Submit Utils', () => {
const t = jest.fn();
const mockFn = jest.fn();
const t = mockFn;

describe('createDeployment tests', () => {
beforeAll(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ export const createDevfileResources = async (
};

export const createOrUpdateResources = async (
t: TFunction,
t: TFunction | ((key: string) => string),
formData: GitImportFormData,
imageStream: K8sResourceKind,
createNewProject?: boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TFunction } from 'react-i18next';
import { TFunction } from 'i18next';
import * as yup from 'yup';
import { RESOURCE_NAME_REGEX } from './regex';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,6 @@
"Managed by": "Managed by",
"Installing": "Installing",
"Valid subscriptions": "Valid subscriptions",
"Pending": "Pending",
"OK": "OK",
"Failed": "Failed",
"This operator includes a console plugin which provides a custom interface that can be included in the console. The console plugin will prompt for the console to be refreshed once it has been enabled. Make sure you trust this console plugin before enabling.": "This operator includes a console plugin which provides a custom interface that can be included in the console. The console plugin will prompt for the console to be refreshed once it has been enabled. Make sure you trust this console plugin before enabling.",
"Hide operator description": "Hide operator description",
"Show operator description": "Show operator description",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,12 @@ const ManagedNamespaces: React.FC<ManagedNamespacesProps> = ({ obj }) => {

switch (managedNamespaces.length) {
case 0:
return t('olm~All Namespaces');
return <>{t('olm~All Namespaces')}</>;
case 1:
return managedNamespaces[0] ? (
<ResourceLink kind="Namespace" title={managedNamespaces[0]} name={managedNamespaces[0]} />
) : (
t('olm~All Namespaces')
<>{t('olm~All Namespaces')}</>
);
default:
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,18 @@ export const getCSVStatus = (
// TODO Get rid of let.
let status: ClusterServiceVersionStatus;
if (pendingPhases.includes(statusPhase)) {
status = i18n.t('olm~Pending');
status = ClusterServiceVersionStatus.Pending;
} else {
switch (statusPhase) {
case ClusterServiceVersionPhase.CSVPhaseSucceeded:
status = i18n.t('olm~OK');
status = ClusterServiceVersionStatus.OK;
break;
case ClusterServiceVersionPhase.CSVPhaseFailed:
status = i18n.t('olm~Failed');
status = ClusterServiceVersionStatus.Failed;
break;
default:
return {
status: i18n.t('olm~Unknown'),
status: ClusterServiceVersionStatus.Unknown,
title: statusPhase,
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react';
import { TFunction } from 'react-i18next';
import { TFunction } from 'i18next';
import { getTopologyShortcuts } from '../components/graph-view/TopologyShortcuts';
import { TopologyViewType } from '../topology-types';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@
"Prometheus query failed.": "Prometheus query failed.",
"Invalid credentials": "Invalid credentials",
"Failing {{reason}}": "Failing {{reason}}",
"Failed to load kubecontrollermanager": "Failed to load kubecontrollermanager",
"Failed to parse cloud provider config {{cm}}": "Failed to parse cloud provider config {{cm}}",
"The following content was expected to be defined in the configMap: {{ expectedValues }}": "The following content was expected to be defined in the configMap: {{ expectedValues }}",
"Failed to persist {{secret}}": "Failed to persist {{secret}}",
"Failed to patch kubecontrollermanager": "Failed to patch kubecontrollermanager",
"Failed to patch cloud provider config": "Failed to patch cloud provider config",
"Failed to add taint to nodes": "Failed to add taint to nodes",
"Failed to patch infrastructure spec": "Failed to patch infrastructure spec",
"Unexpected error": "Unexpected error",
"vCenter": "vCenter",
"Enter the network address of the vCenter server. It can either be a domain name or IP address": "Enter the network address of the vCenter server. It can either be a domain name or IP address",
"vCenter cluster": "vCenter cluster",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
PrometheusHealthHandler,
} from '@console/dynamic-plugin-sdk';
import { ConfigMap } from '../../resources';
import { getVSphereHealth } from '../getVSphereHealth';
import { getVSphereHealth, TranslationFunction } from '../getVSphereHealth';
import { VSphereConnectionModal } from '../VSphereConnectionModal';
import { VSphereOperatorStatuses } from '../VSphereOperatorStatuses';
import './VSphereStatus.css';
Expand Down Expand Up @@ -54,7 +54,8 @@ const VSphereStatus: React.FC<PrometheusHealthPopupProps> = ({ hide, responses,
};

export const healthHandler: PrometheusHealthHandler = (responses, t, additionalResource) => {
const health = getVSphereHealth(t || (() => ''), responses, additionalResource);
const fallbackT: TranslationFunction = (key: string) => key;
const health = getVSphereHealth(t || fallbackT, responses, additionalResource);
const { state } = health;

let message: string | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
t_color_green_50 as okColor,
t_color_red_60 as errorColor,
} from '@patternfly/react-tokens';
import { TFunction } from 'i18next';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom-v5-compat';
import {
Expand All @@ -19,6 +18,7 @@ import {
import { ClusterOperator } from '@console/internal/module/k8s';
import { CONSOLE_PREFIX_CLUSTER_OPERATOR, getCondition } from '../resources';
import { K8sResourceConditionStatus } from '../resources/k8sResource';
import { TranslationFunction } from './getVSphereHealth';

let ohlCounter = 0;
const OperatorHealthLevel: { [key: string]: number } = {
Expand Down Expand Up @@ -47,7 +47,7 @@ const getWorstIconState = (states: OperatorHealthType[]): OperatorHealthType['ic
return worst.icon;
};

const useOperatorHealth = (t: TFunction, name: string): OperatorHealthType => {
const useOperatorHealth = (t: TranslationFunction, name: string): OperatorHealthType => {
const [operator, isLoaded, error] = useK8sWatchResource<ClusterOperator>({
groupVersionKind: { group: 'config.openshift.io', version: 'v1', kind: 'ClusterOperator' },
name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { TFunction } from 'i18next';
import { toInteger } from 'lodash';
import {
HealthState,
Expand All @@ -9,13 +8,15 @@ import {
} from '@console/dynamic-plugin-sdk';
import { ConfigMap } from '../resources';

export type TranslationFunction = (key: string, options?: Record<string, unknown>) => string;

Comment on lines +11 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n frontend/packages/vsphere-plugin/src/components/getVSphereHealth.ts

Repository: openshift/console

Length of output: 3712


🏁 Script executed:

rg -nP --type=ts --type=tsx -C3 'getVSphereHealth\s*\(' -g '!*.spec.ts' -g '!*.test.ts'

Repository: openshift/console

Length of output: 88


🏁 Script executed:

rg -nP --type=ts -C3 'getVSphereHealth\s*\(' -g '!*.spec.ts' -g '!*.test.ts'

Repository: openshift/console

Length of output: 1848


🏁 Script executed:

cat -n frontend/packages/vsphere-plugin/src/components/ClusterOverview/VSphereStatus.tsx

Repository: openshift/console

Length of output: 2823


Type abstraction is well-designed and consumers are compatible. The TranslationFunction type successfully abstracts the translation interface, improving testability as shown by the fallback implementation in healthHandler. All consumers pass compatible signatures.

However, fix missing translation wrapper at line 76: the message 'vsphere-plugin~Synchronization failed' should be wrapped with t() to match the pattern used elsewhere in the function and ensure proper translation:

message: t('vsphere-plugin~Synchronization failed')
🤖 Prompt for AI Agents
In frontend/packages/vsphere-plugin/src/components/getVSphereHealth.ts around
lines 11 and 76, the TranslationFunction type is fine but at line 76 the error
message string 'vsphere-plugin~Synchronization failed' is not wrapped with the
translation function; update that message to call
t('vsphere-plugin~Synchronization failed') so it follows the established pattern
and returns a translated string consistent with the rest of the function.

const getPrometheusMetricValue = (
prometheusResult: PrometheusResult[],
reason: string,
): PrometheusValue | undefined => prometheusResult.find((r) => r.metric.reason === reason)?.value;

export const getVSphereHealth = (
t: TFunction,
t: TranslationFunction,
responses: PrometheusHealthPopupProps['responses'],
configMapResult: PrometheusHealthPopupProps['k8sResult'],
): SubsystemHealth => {
Expand Down
14 changes: 7 additions & 7 deletions frontend/packages/vsphere-plugin/src/components/persist.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { safeLoad, dump } from 'js-yaml';
import { TFunction } from 'react-i18next';
import {
k8sCreate,
k8sGet,
Expand All @@ -15,6 +14,7 @@ import {
VSPHERE_CREDS_SECRET_NAMESPACE,
} from '../constants';
import { ConfigMap, Infrastructure, KubeControllerManager, Secret } from '../resources';
import { TranslationFunction } from './getVSphereHealth';
import { ConnectionFormFormikValues, PersistOp, ProviderCM } from './types';
import { encodeBase64, getErrorMessage } from './utils';

Expand Down Expand Up @@ -92,7 +92,7 @@ const getPersistSecretOp = async (
};

const getPatchKubeControllerManagerOp = async (
t: TFunction<'vsphere-plugin'>,
t: TranslationFunction,
kubeControllerManagerModel: K8sModel,
): Promise<PersistOp> => {
let cm: KubeControllerManager;
Expand Down Expand Up @@ -129,7 +129,7 @@ const getPatchKubeControllerManagerOp = async (
};

const updateYamlFormat = (
t: TFunction<'vsphere-plugin'>,
t: TranslationFunction,
values: ConnectionFormFormikValues,
initValues: ConnectionFormFormikValues,
cloudProviderConfig: ConfigMap,
Expand Down Expand Up @@ -249,7 +249,7 @@ const getUpdatedConfigMapResourcePool = (
};

const updateIniFormat = (
t: TFunction<'vsphere-plugin'>,
t: TranslationFunction,
values: ConnectionFormFormikValues,
initValues: ConnectionFormFormikValues,
cloudProviderConfig: ConfigMap,
Expand Down Expand Up @@ -360,7 +360,7 @@ const fixConfigMap = (values: ConnectionFormFormikValues) => {
};

const getPersistProviderConfigMapOp = async (
t: TFunction<'vsphere-plugin'>,
t: TranslationFunction,
configMapModel: K8sModel,
values: ConnectionFormFormikValues,
initValues: ConnectionFormFormikValues,
Expand Down Expand Up @@ -551,7 +551,7 @@ const runPatches = async ({
addTaints,
queryParams,
}: {
t: TFunction<'vsphere-plugin'>;
t: TranslationFunction;
persistSecret: PersistOp;
persistKubeControllerManager: PersistOp;
persistProviderCM: PersistOp;
Expand Down Expand Up @@ -599,7 +599,7 @@ const runPatches = async ({
};

export const persist = async (
t: TFunction<'vsphere-plugin'>,
t: TranslationFunction,
{
secretModel,
configMapModel,
Expand Down
Loading