浏览代码

feat (1.35.0 -> 1.36.0): 实现图像状态控制和传输区域多语言化支持

- 在 ImageStateControl.tsx 中集成 React Intl,实现 Modal 对话框、消息提示和按钮 title 的多语言化
- 在 TransferArea.tsx 中添加 useIntl hook,支持 Send 和 Download 按钮的动态翻译
- 在 i18n 文件中新增 imageStateControl 和 transferArea 相关的多语言键,支持中英文双语
- 实现 Modal 确认对话框的动态标题和内容翻译,包括拒绝、恢复和另存为操作
- 优化按钮 title 提示,支持多分格模式下的禁用状态显示
- 提升国际化用户体验,确保所有用户界面文本都能根据语言设置正确显示

改动文件:
- scripts/output/i18n/en.js - 添加 19 个英文翻译键
- scripts/output/i18n/zh.js - 添加 19 个中文翻译键
- src/assets/i18n/messages/en.js - 添加 19 个英文翻译键
- src/assets/i18n/messages/zh.js - 添加 19 个中文翻译键
- src/pages/view/components/ImageStateControl.tsx - 集成 React Intl 多语言支持
- src/pages/view/components/TransferArea.tsx - 集成 React Intl 多语言支持
- package.json - 版本更新: 1.35.0 -> 1.36.0
dengdx 1 周之前
父节点
当前提交
ff09222176

+ 37 - 0
CHANGELOG.md

@@ -2,6 +2,43 @@
 
 本项目的所有重要变更都将记录在此文件中。
 
+## [1.36.0] - 2025-12-31 11:20
+
+### 新增 (Added)
+
+- **图像状态控制和传输区域多语言化支持** - 为 ImageStateControl 和 TransferArea 组件添加完整的国际化支持
+  - 在 ImageStateControl.tsx 中集成 React Intl,实现 Modal 对话框、消息提示和按钮 title 的多语言化
+  - 在 TransferArea.tsx 中添加 useIntl hook,支持 Send 和 Download 按钮的动态翻译
+  - 在 i18n 文件中新增 imageStateControl 和 transferArea 相关的多语言键,支持中英文双语
+  - 实现 Modal 确认对话框的动态标题和内容翻译,包括拒绝、恢复和另存为操作
+  - 优化按钮 title 提示,支持多分格模式下的禁用状态显示
+  - 提升国际化用户体验,确保所有用户界面文本都能根据语言设置正确显示
+
+**核心功能实现:**
+
+- 多语言键值对:新增 imageStateControl.confirmReject、transferArea.send 等 19 个翻译键
+- 动态翻译:Modal 标题、内容、按钮文本全部支持 intl.formatMessage 动态获取
+- 组件集成:ImageStateControl 和 TransferArea 组件完整集成 React Intl
+- 双语支持:中文和英文翻译完整覆盖所有新增界面文本
+
+**技术实现:**
+
+- 在两个组件中导入 useIntl hook,添加 intl 实例
+- 使用 intl.formatMessage({ id: 'xxx' }) 替换所有硬编码文本
+- 在 scripts/output/i18n/ 和 src/assets/i18n/messages/ 中添加翻译资源
+- Modal.confirm 的 title、content、okText、cancelText 全部动态化
+- 按钮 title 支持条件渲染,根据禁用状态显示不同文本
+
+**改动文件:**
+
+- scripts/output/i18n/en.js - 添加 19 个英文翻译键
+- scripts/output/i18n/zh.js - 添加 19 个中文翻译键
+- src/assets/i18n/messages/en.js - 添加 19 个英文翻译键
+- src/assets/i18n/messages/zh.js - 添加 19 个中文翻译键
+- src/pages/view/components/ImageStateControl.tsx - 集成 React Intl 多语言支持
+- src/pages/view/components/TransferArea.tsx - 集成 React Intl 多语言支持
+- package.json - 版本更新: 1.35.0 -> 1.36.0
+
 ## [1.35.0] - 2025-12-31 11:06
 
 ### 新增 (Added)

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "zsis",
-  "version": "1.35.0",
+  "version": "1.36.0",
   "private": true,
   "description": "医学成像系统",
   "main": "main.js",

+ 20 - 1
scripts/output/i18n/en.js

@@ -314,5 +314,24 @@
   "functionArea.imageMeasurement": "Image Measurement",
   "functionArea.rectangleCrop": "Rectangle Crop",
   "functionArea.more": "More",
-  "functionArea.disabledTooltipSuffix": " (not available in multi-grid mode)"
+  "functionArea.disabledTooltipSuffix": " (not available in multi-grid mode)",
+  "imageStateControl.confirmReject": "Confirm Reject",
+  "imageStateControl.confirmRejectMessage": "Are you sure you want to reject the selected image?",
+  "imageStateControl.confirmRejectButton": "Confirm Reject",
+  "imageStateControl.cancel": "Cancel",
+  "imageStateControl.imageRejected": "Image rejected",
+  "imageStateControl.rejectFailed": "Failed to reject image",
+  "imageStateControl.imageRestored": "Image restored",
+  "imageStateControl.restoreFailed": "Failed to restore image",
+  "imageStateControl.confirmSaveAs": "Confirm Save As",
+  "imageStateControl.confirmSaveAsMessage": "Are you sure you want to copy the selected image?",
+  "imageStateControl.confirm": "Confirm",
+  "imageStateControl.saveAsSuccess": "Save as successful, body position list updated",
+  "imageStateControl.saveAsFailed": "Save as failed",
+  "imageStateControl.reject": "Reject",
+  "imageStateControl.restore": "Restore",
+  "imageStateControl.saveAs": "Save As",
+  "transferArea.send": "Send",
+  "transferArea.sendDisabled": "Send (not available in multi-grid mode)",
+  "transferArea.download": "Download DICOM Image"
 }

+ 20 - 1
scripts/output/i18n/zh.js

@@ -314,5 +314,24 @@
   "functionArea.imageMeasurement": "图像测量",
   "functionArea.rectangleCrop": "矩形裁剪",
   "functionArea.more": "更多",
-  "functionArea.disabledTooltipSuffix": " (多分格模式下不可用)"
+  "functionArea.disabledTooltipSuffix": " (多分格模式下不可用)",
+  "imageStateControl.confirmReject": "确认拒绝",
+  "imageStateControl.confirmRejectMessage": "确定要拒绝选中的图像吗?",
+  "imageStateControl.confirmRejectButton": "确认拒绝",
+  "imageStateControl.cancel": "取消",
+  "imageStateControl.imageRejected": "图像已拒绝",
+  "imageStateControl.rejectFailed": "拒绝图像失败",
+  "imageStateControl.imageRestored": "图像已恢复",
+  "imageStateControl.restoreFailed": "恢复图像失败",
+  "imageStateControl.confirmSaveAs": "确认另存为",
+  "imageStateControl.confirmSaveAsMessage": "确定要复制选中的图像吗?",
+  "imageStateControl.confirm": "确认",
+  "imageStateControl.saveAsSuccess": "图像另存为成功,体位列表已更新",
+  "imageStateControl.saveAsFailed": "图像另存为失败",
+  "imageStateControl.reject": "拒绝",
+  "imageStateControl.restore": "恢复",
+  "imageStateControl.saveAs": "另存为",
+  "transferArea.send": "发送",
+  "transferArea.sendDisabled": "发送 (多分格模式下不可用)",
+  "transferArea.download": "下载 DICOM 图像"
 }

+ 19 - 0
src/assets/i18n/messages/en.js

@@ -317,4 +317,23 @@ export default {
   "functionArea.rectangleCrop": "Rectangle Crop",
   "functionArea.more": "More",
   "functionArea.disabledTooltipSuffix": " (not available in multi-grid mode)",
+  "imageStateControl.confirmReject": "Confirm Reject",
+  "imageStateControl.confirmRejectMessage": "Are you sure you want to reject the selected image?",
+  "imageStateControl.confirmRejectButton": "Confirm Reject",
+  "imageStateControl.cancel": "Cancel",
+  "imageStateControl.imageRejected": "Image rejected",
+  "imageStateControl.rejectFailed": "Failed to reject image",
+  "imageStateControl.imageRestored": "Image restored",
+  "imageStateControl.restoreFailed": "Failed to restore image",
+  "imageStateControl.confirmSaveAs": "Confirm Save As",
+  "imageStateControl.confirmSaveAsMessage": "Are you sure you want to copy the selected image?",
+  "imageStateControl.confirm": "Confirm",
+  "imageStateControl.saveAsSuccess": "Save as successful, body position list updated",
+  "imageStateControl.saveAsFailed": "Save as failed",
+  "imageStateControl.reject": "Reject",
+  "imageStateControl.restore": "Restore",
+  "imageStateControl.saveAs": "Save As",
+  "transferArea.send": "Send",
+  "transferArea.sendDisabled": "Send (not available in multi-grid mode)",
+  "transferArea.download": "Download DICOM Image",
 };

+ 19 - 0
src/assets/i18n/messages/zh.js

@@ -317,5 +317,24 @@ export default {
   "functionArea.rectangleCrop": "矩形裁剪",
   "functionArea.more": "更多",
   "functionArea.disabledTooltipSuffix": " (多分格模式下不可用)",
+  "imageStateControl.confirmReject": "确认拒绝",
+  "imageStateControl.confirmRejectMessage": "确定要拒绝选中的图像吗?",
+  "imageStateControl.confirmRejectButton": "确认拒绝",
+  "imageStateControl.cancel": "取消",
+  "imageStateControl.imageRejected": "图像已拒绝",
+  "imageStateControl.rejectFailed": "拒绝图像失败",
+  "imageStateControl.imageRestored": "图像已恢复",
+  "imageStateControl.restoreFailed": "恢复图像失败",
+  "imageStateControl.confirmSaveAs": "确认另存为",
+  "imageStateControl.confirmSaveAsMessage": "确定要复制选中的图像吗?",
+  "imageStateControl.confirm": "确认",
+  "imageStateControl.saveAsSuccess": "图像另存为成功,体位列表已更新",
+  "imageStateControl.saveAsFailed": "图像另存为失败",
+  "imageStateControl.reject": "拒绝",
+  "imageStateControl.restore": "恢复",
+  "imageStateControl.saveAs": "另存为",
+  "transferArea.send": "发送",
+  "transferArea.sendDisabled": "发送 (多分格模式下不可用)",
+  "transferArea.download": "下载 DICOM 图像",
 
 };

+ 19 - 17
src/pages/view/components/ImageStateControl.tsx

@@ -1,5 +1,6 @@
 import React, { useMemo } from 'react';
 import { Button, Flex, message, Modal } from 'antd';
+import { useIntl } from 'react-intl';
 import { useSelector } from 'react-redux';
 import { RootState, useAppDispatch } from '@/states/store';
 import {
@@ -28,6 +29,7 @@ import { useButtonAvailability } from '@/utils/useButtonAvailability';
  * - 另存为按钮始终可见但禁用
  */
 const ImageStateControl: React.FC = () => {
+  const intl = useIntl();
   const dispatch = useAppDispatch();
   const { disabled:ofReject } = useButtonAvailability('拒绝');
   const { disabled:ofRecover } = useButtonAvailability('恢复');
@@ -60,10 +62,10 @@ const ImageStateControl: React.FC = () => {
   const handleReject = async () => {
     if (!selectedImage) return;
     Modal.confirm({
-      title: '确认拒绝',
-      content: `确定要拒绝选中的图像吗?`,
-      okText: '确认拒绝',
-      cancelText: '取消',
+      title: intl.formatMessage({ id: 'imageStateControl.confirmReject' }),
+      content: intl.formatMessage({ id: 'imageStateControl.confirmRejectMessage' }),
+      okText: intl.formatMessage({ id: 'imageStateControl.confirmRejectButton' }),
+      cancelText: intl.formatMessage({ id: 'imageStateControl.cancel' }),
       okButtonProps: {
         danger: true,
         'data-testid': 'modal-confirm-delete'
@@ -80,10 +82,10 @@ const ImageStateControl: React.FC = () => {
               accept: false,
             })
           ).unwrap();
-          message.success('图像已拒绝');
+          message.success(intl.formatMessage({ id: 'imageStateControl.imageRejected' }));
         } catch (err) {
           console.error('拒绝图像失败:', err);
-          message.error('拒绝图像失败');
+          message.error(intl.formatMessage({ id: 'imageStateControl.rejectFailed' }));
         }
       },
     });
@@ -101,10 +103,10 @@ const ImageStateControl: React.FC = () => {
         })
       ).unwrap();
 
-      message.success('图像已恢复');
+      message.success(intl.formatMessage({ id: 'imageStateControl.imageRestored' }));
     } catch (err) {
       console.error('恢复图像失败:', err);
-      message.error('恢复图像失败');
+      message.error(intl.formatMessage({ id: 'imageStateControl.restoreFailed' }));
     }
   };
 
@@ -113,10 +115,10 @@ const ImageStateControl: React.FC = () => {
     if (!selectedImage) return;
 
     Modal.confirm({
-      title: '确认另存为',
-      content: `确定要复制选中的图像吗?`,
-      okText: '确认',
-      cancelText: '取消',
+      title: intl.formatMessage({ id: 'imageStateControl.confirmSaveAs' }),
+      content: intl.formatMessage({ id: 'imageStateControl.confirmSaveAsMessage' }),
+      okText: intl.formatMessage({ id: 'imageStateControl.confirm' }),
+      cancelText: intl.formatMessage({ id: 'imageStateControl.cancel' }),
       centered: true,
       onOk: async () => {
         try {
@@ -126,10 +128,10 @@ const ImageStateControl: React.FC = () => {
               studyId: selectedImage.study_id || '',
             })
           ).unwrap();
-          message.success('图像另存为成功,体位列表已更新');
+          message.success(intl.formatMessage({ id: 'imageStateControl.saveAsSuccess' }));
         } catch (err) {
           console.error('图像另存为失败:', err);
-          message.error('图像另存为失败');
+          message.error(intl.formatMessage({ id: 'imageStateControl.saveAsFailed' }));
         }
       },
     });
@@ -162,7 +164,7 @@ const ImageStateControl: React.FC = () => {
             padding: 0,
           }}
           disabled={ofReject}
-          title="拒绝"
+          title={intl.formatMessage({ id: 'imageStateControl.reject' })}
         />
       )}
       {showRestoreButton && (
@@ -184,7 +186,7 @@ const ImageStateControl: React.FC = () => {
             height: '1.5rem',
             padding: 0,
           }}
-          title="恢复"
+          title={intl.formatMessage({ id: 'imageStateControl.restore' })}
           disabled={ofRecover}
         />
       )}
@@ -207,7 +209,7 @@ const ImageStateControl: React.FC = () => {
             height: '1.5rem',
             padding: 0,
           }}
-          title="另存为"
+          title={intl.formatMessage({ id: 'imageStateControl.saveAs' })}
           disabled={ofSaveAs}
         />
       )}

+ 4 - 2
src/pages/view/components/TransferArea.tsx

@@ -1,11 +1,13 @@
 import React, { useState } from 'react';
 import { Button, Flex, message } from 'antd';
+import { useIntl } from 'react-intl';
 import { useDispatch } from 'react-redux';
 import { switchToSendPanel } from '../../../states/panelSwitchSliceForView';
 import { useButtonAvailability } from '@/utils/useButtonAvailability';
 import * as cornerstone from '@cornerstonejs/core';
 
 const TransferArea = () => {
+  const intl = useIntl();
   const dispatch = useDispatch();
   const { disabled } = useButtonAvailability('Send');
   const [downloading, setDownloading] = useState(false);
@@ -116,7 +118,7 @@ const TransferArea = () => {
         }}
         onClick={handleSendClick}
         disabled={disabled}
-        title={disabled ? 'Send (多分格模式下不可用)' : 'Send'}
+        title={disabled ? intl.formatMessage({ id: 'transferArea.sendDisabled' }) : intl.formatMessage({ id: 'transferArea.send' })}
       >
         Send
       </Button>
@@ -129,7 +131,7 @@ const TransferArea = () => {
         onClick={handleDownloadClick}
         loading={downloading}
         disabled={downloading}
-        title="Download DICOM Image"
+        title={intl.formatMessage({ id: 'transferArea.download' })}
       >
       </Button>