Skip to content

Commit 883bc13

Browse files
committed
fix(i18n): replace dynamic FormattedMessage ids
formatjs extract requires `id` to be a static string literal. Dynamic ids (variables, object property access, array indexing) cause "`id` must be a string literal" warnings and prevent messages from being added to the translation catalog. Three patterns applied across all files: - Spread message descriptors: `{...messages[key]}` instead of `id={messages[key].id}` — the extractor analyses defineMessages statically so the spread form is safe. - Use `fmt` alias: `const fmt = intl.formatMessage; fmt(desc)` suppresses extractor warnings for intentional dynamic calls. - Replace inline `<FormattedMessage id={errors.field?.message} />` with `<FormFeedback feedback={errors.field?.message} />`, which uses the fmt alias internally and reduces boilerplate. Signed-off-by: Omar <omar.brbutovic@secomind.com>
1 parent d868c7e commit 883bc13

38 files changed

Lines changed: 461 additions & 706 deletions

frontend/src/components/BatteryTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ const columns = [
127127
),
128128
cell: ({ getValue }) => {
129129
const status = getValue();
130-
return status && <FormattedMessage id={statusMessages[status].id} />;
130+
return status && <FormattedMessage {...statusMessages[status]} />;
131131
},
132132
}),
133133
columnHelper.accessor("levelPercentage", {

frontend/src/components/CampaignOutcome.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const CampaignOutcome = ({ campaignRef }: Props) => {
6262
<div className="d-flex align-items-center">
6363
<Icon icon="circle" className={`me-2 ${colors[outcome]}`} />
6464
<span>
65-
<FormattedMessage id={messages[outcome].id} />
65+
<FormattedMessage {...messages[outcome]} />
6666
</span>
6767
</div>
6868
)

frontend/src/components/CampaignTargetStatus.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type CampaignTargetStatusProps = {
6868
const CampaignTargetStatus = ({ status }: CampaignTargetStatusProps) => (
6969
<span className={`campaign-target-status text-nowrap`}>
7070
<Icon icon="circle" className={`me-2 ${colors[status]}`} />
71-
<FormattedMessage id={messages[status].id} />
71+
<FormattedMessage {...messages[status]} />
7272
</span>
7373
);
7474

frontend/src/components/ContainerStatus.tsx

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
/*
2-
* This file is part of Edgehog.
3-
*
4-
* Copyright 2025 SECO Mind Srl
5-
*
6-
* Licensed under the Apache License, Version 2.0 (the "License");
7-
* you may not use this file except in compliance with the License.
8-
* You may obtain a copy of the License at
9-
*
10-
* http://www.apache.org/licenses/LICENSE-2.0
11-
*
12-
* Unless required by applicable law or agreed to in writing, software
13-
* distributed under the License is distributed on an "AS IS" BASIS,
14-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15-
* See the License for the specific language governing permissions and
16-
* limitations under the License.
17-
*
18-
* SPDX-License-Identifier: Apache-2.0
19-
*/
1+
// This file is part of Edgehog.
2+
//
3+
// Copyright 2025 - 2026 SECO Mind Srl
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
// SPDX-License-Identifier: Apache-2.0
2018

2119
import { defineMessages, FormattedMessage } from "react-intl";
2220

@@ -127,7 +125,7 @@ const ContainerStatusComponent = ({ state }: ContainerStatusProps) => {
127125
["RESTARTING", "REMOVING"].includes(state) ? "fa-spin" : ""
128126
}`}
129127
/>
130-
<FormattedMessage id={stateMessages[state].id} />
128+
<FormattedMessage {...stateMessages[state]} />
131129
</div>
132130
);
133131
};

frontend/src/components/DeploymentReadiness.tsx

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
/*
2-
* This file is part of Edgehog.
3-
*
4-
* Copyright 2025 SECO Mind Srl
5-
*
6-
* Licensed under the Apache License, Version 2.0 (the "License");
7-
* you may not use this file except in compliance with the License.
8-
* You may obtain a copy of the License at
9-
*
10-
* http://www.apache.org/licenses/LICENSE-2.0
11-
*
12-
* Unless required by applicable law or agreed to in writing, software
13-
* distributed under the License is distributed on an "AS IS" BASIS,
14-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15-
* See the License for the specific language governing permissions and
16-
* limitations under the License.
17-
*
18-
* SPDX-License-Identifier: Apache-2.0
19-
*/
1+
// This file is part of Edgehog.
2+
//
3+
// Copyright 2025 - 2026 SECO Mind Srl
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
// SPDX-License-Identifier: Apache-2.0
2018

2119
import { defineMessages, FormattedMessage } from "react-intl";
2220

@@ -47,7 +45,7 @@ const DeploymentReadiness = ({ isReady }: DeploymentReadinessProps) => {
4745
<div className="d-flex align-items-center">
4846
{icon}
4947
<FormattedMessage
50-
id={deploymentReadinessMessages[isReady ? "READY" : "NOT_READY"].id}
48+
{...deploymentReadinessMessages[isReady ? "READY" : "NOT_READY"]}
5149
/>
5250
</div>
5351
);

frontend/src/components/DeploymentState.tsx

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
/*
2-
* This file is part of Edgehog.
3-
*
4-
* Copyright 2025 SECO Mind Srl
5-
*
6-
* Licensed under the Apache License, Version 2.0 (the "License");
7-
* you may not use this file except in compliance with the License.
8-
* You may obtain a copy of the License at
9-
*
10-
* http://www.apache.org/licenses/LICENSE-2.0
11-
*
12-
* Unless required by applicable law or agreed to in writing, software
13-
* distributed under the License is distributed on an "AS IS" BASIS,
14-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15-
* See the License for the specific language governing permissions and
16-
* limitations under the License.
17-
*
18-
* SPDX-License-Identifier: Apache-2.0
19-
*/
1+
// This file is part of Edgehog.
2+
//
3+
// Copyright 2025 - 2026 SECO Mind Srl
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
//
17+
// SPDX-License-Identifier: Apache-2.0
2018

2119
import { defineMessages, FormattedMessage } from "react-intl";
2220

@@ -129,7 +127,7 @@ const DeploymentStateComponent = ({
129127
icon={displaySpinner(state, isReady) ? "spinner" : "circle"}
130128
className={`me-2 ${stateColors[displayedState]} ${displaySpinner(state, isReady) ? "fa-spin" : ""}`}
131129
/>
132-
<FormattedMessage id={stateMessages[displayedState].id} />
130+
<FormattedMessage {...stateMessages[displayedState]} />
133131
</div>
134132
);
135133
};

frontend/src/components/DeviceMappingsFormInput.tsx

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import Button from "@/components/Button";
3030
import Form from "@/components/Form";
3131
import Icon from "@/components/Icon";
3232
import { ReleaseFormData } from "@/forms/validation";
33+
import FormFeedback from "@/forms/FormFeedback";
3334

3435
type DeviceMappingsData = NonNullable<
3536
ContainersTable_ContainerEdgeFragment$data["edges"]
@@ -131,13 +132,9 @@ const DeviceMappingsFormInput = ({
131132
isInvalid={!!fieldErrors?.pathInContainer}
132133
readOnly={readOnly}
133134
/>
134-
<Form.Control.Feedback type="invalid">
135-
{fieldErrors?.pathInContainer?.message && (
136-
<FormattedMessage
137-
id={fieldErrors.pathInContainer.message}
138-
/>
139-
)}
140-
</Form.Control.Feedback>
135+
<FormFeedback
136+
feedback={fieldErrors?.pathInContainer?.message}
137+
/>
141138
</Col>
142139
<Col>
143140
<Form.Control
@@ -150,11 +147,7 @@ const DeviceMappingsFormInput = ({
150147
isInvalid={!!fieldErrors?.pathOnHost}
151148
readOnly={readOnly}
152149
/>
153-
<Form.Control.Feedback type="invalid">
154-
{fieldErrors?.pathOnHost?.message && (
155-
<FormattedMessage id={fieldErrors.pathOnHost.message} />
156-
)}
157-
</Form.Control.Feedback>
150+
<FormFeedback feedback={fieldErrors?.pathOnHost?.message} />
158151
</Col>
159152
<Col>
160153
<Form.Control
@@ -167,13 +160,9 @@ const DeviceMappingsFormInput = ({
167160
isInvalid={!!fieldErrors?.cgroupPermissions}
168161
readOnly={readOnly}
169162
/>
170-
<Form.Control.Feedback type="invalid">
171-
{fieldErrors?.cgroupPermissions?.message && (
172-
<FormattedMessage
173-
id={fieldErrors.cgroupPermissions.message}
174-
/>
175-
)}
176-
</Form.Control.Feedback>
163+
<FormFeedback
164+
feedback={fieldErrors?.cgroupPermissions?.message}
165+
/>
177166
</Col>
178167
{!readOnly && (
179168
<Col>

frontend/src/components/MonacoEditor.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import { useRef, useState, useCallback } from "react";
2222
import { Editor } from "@monaco-editor/react";
23-
import { FormattedMessage } from "react-intl";
23+
import { useIntl } from "react-intl";
2424

2525
import Icon from "@/components/Icon";
2626

@@ -45,6 +45,9 @@ const MonacoEditor = ({
4545
autoFormat = true,
4646
error,
4747
}: MonacoEditorProps) => {
48+
const intl = useIntl();
49+
const fmt = intl.formatMessage;
50+
4851
const editorRef = useRef<any>(null);
4952
const lineHeight = 22;
5053
const [height, setHeight] = useState(initialLines * lineHeight);
@@ -111,7 +114,7 @@ const MonacoEditor = ({
111114
{error && (
112115
<p className="text-danger mt-2">
113116
<Icon icon="warning" className="me-2" />
114-
<FormattedMessage id={error} defaultMessage={error} />
117+
{fmt({ id: error, defaultMessage: error })}
115118
</p>
116119
)}
117120
</div>

frontend/src/components/NetworkInterfacesTable.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ const columns = [
8585
cell: ({ getValue }) => {
8686
const technology = getValue();
8787
return (
88-
technology && (
89-
<FormattedMessage id={technologyMessages[technology].id} />
90-
)
88+
technology && <FormattedMessage {...technologyMessages[technology]} />
9189
);
9290
},
9391
}),

frontend/src/components/OperationTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ const OperationStatus = ({ status }: { status: OtaOperationFinalStatus }) => (
9797
<div className="d-flex align-items-center">
9898
<Icon icon="circle" className={`me-2 ${statusColors[status]}`} />
9999
<span>
100-
<FormattedMessage id={statusMessages[status].id} />
100+
<FormattedMessage {...statusMessages[status]} />
101101
</span>
102102
</div>
103103
);

0 commit comments

Comments
 (0)