From 3c3f63bddf15862511fd60043dee9425cdf34345 Mon Sep 17 00:00:00 2001 From: Lewis_yan Date: Thu, 9 Apr 2026 12:01:34 +0800 Subject: [PATCH] feat(i18n): adapt all system pages to use i18n keys and replace django-vue3-admin references - Replace all django-vue-admin/django-vue3-admin references with "Procurement Inquiry System" - Add i18n support for system config, dept, dictionary, fileList, menu, messageCenter, role, whiteList pages - Add i18n files for fileSelector module - Backend: add SystemConfig model i18n fields (title_en, title_zh_tw) and migrations --- ...onfig_title_en_systemconfig_title_zh_tw.py | 23 +++ .../0005_fill_systemconfig_title_i18n.py | 39 ++++++ .../0005_fill_systemconfig_title_i18n.sql | 8 ++ ...06_fill_systemconfig_title_translations.py | 67 +++++++++ backend/dvadmin/system/models.py | 2 + backend/dvadmin/system/views/system_config.py | 54 +++++++- web/package.json | 1 - web/src/components/fileSelector/index.vue | 55 ++++---- web/src/i18n/pages/fileList/en.ts | 1 + web/src/i18n/pages/fileList/zh-cn.ts | 1 + web/src/i18n/pages/fileList/zh-tw.ts | 1 + web/src/i18n/pages/fileSelector/en.ts | 41 ++++++ web/src/i18n/pages/fileSelector/zh-cn.ts | 41 ++++++ web/src/i18n/pages/fileSelector/zh-tw.ts | 41 ++++++ web/src/i18n/pages/menu/en.ts | 2 + web/src/i18n/pages/menu/zh-cn.ts | 2 + web/src/i18n/pages/menu/zh-tw.ts | 2 + web/src/layout/navBars/breadcrumb/user.vue | 2 +- web/src/layout/upgrade/index.vue | 2 +- web/src/views/system/areas/crud.tsx | 24 ++-- web/src/views/system/config/index.vue | 5 +- .../dept/components/DeptFormCom/index.vue | 23 +-- .../dept/components/DeptTreeCom/index.vue | 23 +-- .../dept/components/DeptUserCom/crud.tsx | 70 +++++----- .../dept/components/DeptUserCom/index.vue | 49 ++++--- web/src/views/system/dept/index.vue | 11 +- web/src/views/system/dictionary/crud.tsx | 30 ++-- .../views/system/dictionary/subDict/crud.tsx | 131 ++++-------------- .../views/system/dictionary/subDict/index.vue | 9 +- web/src/views/system/downloadCenter/crud.tsx | 26 ++-- web/src/views/system/fileList/crud.tsx | 74 ++++------ web/src/views/system/fileList/index.vue | 8 +- .../views/system/log/operationLog/crud.tsx | 40 +++--- .../menu/components/MenuButtonCom/crud.tsx | 34 ++--- .../menu/components/MenuFieldCom/crud.tsx | 24 ++-- .../menu/components/MenuFieldCom/index.vue | 6 +- web/src/views/system/messageCenter/crud.tsx | 58 ++++---- web/src/views/system/messageCenter/index.vue | 7 +- web/src/views/system/role/crud.tsx | 25 ++-- web/src/views/system/whiteList/crud.tsx | 20 +-- web/src/views/template/crud.tsx | 2 +- web/src/views/template/index.vue | 10 +- 42 files changed, 683 insertions(+), 411 deletions(-) create mode 100644 backend/dvadmin/system/migrations/0004_systemconfig_title_en_systemconfig_title_zh_tw.py create mode 100644 backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.py create mode 100644 backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.sql create mode 100644 backend/dvadmin/system/migrations/0006_fill_systemconfig_title_translations.py create mode 100644 web/src/i18n/pages/fileSelector/en.ts create mode 100644 web/src/i18n/pages/fileSelector/zh-cn.ts create mode 100644 web/src/i18n/pages/fileSelector/zh-tw.ts diff --git a/backend/dvadmin/system/migrations/0004_systemconfig_title_en_systemconfig_title_zh_tw.py b/backend/dvadmin/system/migrations/0004_systemconfig_title_en_systemconfig_title_zh_tw.py new file mode 100644 index 0000000..190bdc7 --- /dev/null +++ b/backend/dvadmin/system/migrations/0004_systemconfig_title_en_systemconfig_title_zh_tw.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.14 on 2026-04-09 10:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0003_fill_menu_i18n_name'), + ] + + operations = [ + migrations.AddField( + model_name='systemconfig', + name='title_en', + field=models.CharField(blank=True, help_text='标题(English)', max_length=50, null=True, verbose_name='标题(English)'), + ), + migrations.AddField( + model_name='systemconfig', + name='title_zh_tw', + field=models.CharField(blank=True, help_text='标题(繁體中文)', max_length=50, null=True, verbose_name='标题(繁體中文)'), + ), + ] diff --git a/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.py b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.py new file mode 100644 index 0000000..e6bdbc5 --- /dev/null +++ b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +填充系统配置标题多语言翻译 +用法: python manage.py migrate system 0005 +""" +from django.db import migrations + + +def fill_systemconfig_title_i18n(apps, schema_editor): + SystemConfig = apps.get_model('system', 'SystemConfig') + + updated_count = 0 + for config in SystemConfig.objects.all(): + update_fields = [] + if not config.title_en: + config.title_en = config.title + update_fields.append('title_en') + if not config.title_zh_tw: + config.title_zh_tw = config.title + update_fields.append('title_zh_tw') + if update_fields: + config.save(update_fields=update_fields) + updated_count += 1 + print(f"Updated {updated_count} system config translations") + + +def reverse_func(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0004_systemconfig_title_en_systemconfig_title_zh_tw'), + ] + + operations = [ + migrations.RunPython(fill_systemconfig_title_i18n, reverse_func), + ] diff --git a/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.sql b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.sql new file mode 100644 index 0000000..a8edd47 --- /dev/null +++ b/backend/dvadmin/system/migrations/0005_fill_systemconfig_title_i18n.sql @@ -0,0 +1,8 @@ +/* +Fill SystemConfig title_i18n fields from existing title values +SQL reference (Django uses RunPython instead): + +UPDATE `dvadmin_system_config` +SET `title_en` = `title`, `title_zh_tw` = `title` +WHERE `title_en` IS NULL AND `title_zh_tw` IS NULL; +*/ diff --git a/backend/dvadmin/system/migrations/0006_fill_systemconfig_title_translations.py b/backend/dvadmin/system/migrations/0006_fill_systemconfig_title_translations.py new file mode 100644 index 0000000..1480274 --- /dev/null +++ b/backend/dvadmin/system/migrations/0006_fill_systemconfig_title_translations.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" +填充系统配置标题多语言翻译 +""" +from django.db import migrations + + +def fill_translations(apps, schema_editor): + SystemConfig = apps.get_model('system', 'SystemConfig') + + translations = { + '基础配置': {'en': 'Basic Config', 'zh_tw': '基礎配置'}, + '系统配置': {'en': 'System Config', 'zh_tw': '系統配置'}, + '登录页配置': {'en': 'Login Page Config', 'zh_tw': '登錄頁配置'}, + '文件存储配置': {'en': 'File Storage Config', 'zh_tw': '文件存儲配置'}, + '网站标题': {'en': 'Site Title', 'zh_tw': '網站標題'}, + '网站名称': {'en': 'Site Name', 'zh_tw': '網站名稱'}, + '登录网站logo': {'en': 'Login Logo', 'zh_tw': '登錄網站logo'}, + '登录页背景图': {'en': 'Login Background', 'zh_tw': '登錄頁背景圖'}, + '版权信息': {'en': 'Copyright', 'zh_tw': '版權信息'}, + '备案信息': {'en': 'Filing Info', 'zh_tw': '備案信息'}, + '帮助链接': {'en': 'Help Link', 'zh_tw': '幫助鏈接'}, + '隐私链接': {'en': 'Privacy Link', 'zh_tw': '隱私鏈接'}, + '条款链接': {'en': 'Terms Link', 'zh_tw': '條款鏈接'}, + '网页标题': {'en': 'Page Title', 'zh_tw': '網頁標題'}, + '网站小图标': {'en': 'Site Favicon', 'zh_tw': '網站小圖標'}, + '开启验证码': {'en': 'Enable Captcha', 'zh_tw': '開啟驗證碼'}, + '创建用户默认密码': {'en': 'Default Password', 'zh_tw': '創建用戶默認密碼'}, + '存储引擎': {'en': 'Storage Engine', 'zh_tw': '存儲引擎'}, + '文件是否备份': {'en': 'File Backup', 'zh_tw': '文件是否備份'}, + '阿里云-AccessKey': {'en': 'Aliyun AccessKey', 'zh_tw': '阿裡雲-AccessKey'}, + '阿里云-Secret': {'en': 'Aliyun Secret', 'zh_tw': '阿裡雲-Secret'}, + '阿里云-Endpoint': {'en': 'Aliyun Endpoint', 'zh_tw': '阿裡雲-Endpoint'}, + '阿里云-上传路径': {'en': 'Aliyun Upload Path', 'zh_tw': '阿裡雲-上傳路徑'}, + '阿里云-Bucket': {'en': 'Aliyun Bucket', 'zh_tw': '阿裡雲-Bucket'}, + '阿里云-cdn地址': {'en': 'Aliyun CDN URL', 'zh_tw': '阿裡雲-cdn地址'}, + '腾讯云-SecretId': {'en': 'Tencent SecretId', 'zh_tw': '騰訊雲-SecretId'}, + '腾讯云-SecretKey': {'en': 'Tencent SecretKey', 'zh_tw': '騰訊雲-SecretKey'}, + '腾讯云-Region': {'en': 'Tencent Region', 'zh_tw': '騰訊雲-Region'}, + '腾讯云-Bucket': {'en': 'Tencent Bucket', 'zh_tw': '騰訊雲-Bucket'}, + '腾讯云-上传路径': {'en': 'Tencent Upload Path', 'zh_tw': '騰訊雲-上傳路徑'}, + } + + updated = 0 + for config in SystemConfig.objects.all(): + if config.title in translations: + trans = translations[config.title] + config.title_en = trans['en'] + config.title_zh_tw = trans['zh_tw'] + config.save(update_fields=['title_en', 'title_zh_tw']) + updated += 1 + print(f"Updated {updated} config translations") + + +def reverse_func(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0005_fill_systemconfig_title_i18n'), + ] + + operations = [ + migrations.RunPython(fill_translations, reverse_func), + ] diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index da9a0a9..6aaec3d 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -567,6 +567,8 @@ class SystemConfig(CoreModel): help_text="父级", ) title = models.CharField(max_length=50, verbose_name="标题", help_text="标题") + title_en = models.CharField(max_length=50, verbose_name="标题(English)", blank=True, null=True, help_text="标题(English)") + title_zh_tw = models.CharField(max_length=50, verbose_name="标题(繁體中文)", blank=True, null=True, help_text="标题(繁體中文)") key = models.CharField(max_length=100, verbose_name="键", help_text="键", db_index=True) value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True) sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True) diff --git a/backend/dvadmin/system/views/system_config.py b/backend/dvadmin/system/views/system_config.py index 8168b50..65194a5 100644 --- a/backend/dvadmin/system/views/system_config.py +++ b/backend/dvadmin/system/views/system_config.py @@ -50,6 +50,32 @@ class SystemConfigSerializer(CustomModelSerializer): 系统配置-序列化器 """ form_item_type_label = serializers.CharField(source='get_form_item_type_display', read_only=True) + title_i18n = serializers.SerializerMethodField() + + def get_title_i18n(self, obj): + lang = None + req = getattr(self, 'request', None) + if req: + user = getattr(req, 'user', None) + if user and getattr(user, 'is_authenticated', False): + lang = getattr(user, 'language', None) + _qp_lang = req.query_params.get('language', None) + if _qp_lang: + lang = _qp_lang[0] if isinstance(_qp_lang, list) else _qp_lang + if not lang: + meta_lang = req.META.get('HTTP_ACCEPT_LANGUAGE', '') + # Parse Accept-Language header (e.g., "zh-TW,zh;q=0.9,en;q=0.8") + if 'en' in meta_lang: + lang = 'en' + elif 'zh-tw' in meta_lang or 'zh-hant' in meta_lang: + lang = 'zh-tw' + elif meta_lang.startswith('zh'): + lang = 'zh-cn' + if lang == 'en': + return obj.title_en or obj.title + elif lang == 'zh-tw': + return obj.title_zh_tw or obj.title + return obj.title class Meta: model = SystemConfig @@ -63,12 +89,38 @@ class SystemConfigChinldernSerializer(CustomModelSerializer): """ children = serializers.SerializerMethodField() form_item_type_label = serializers.CharField(source='get_form_item_type_display', read_only=True) + title_i18n = serializers.SerializerMethodField() def get_children(self, instance): queryset = SystemConfig.objects.filter(parent=instance) - serializer = SystemConfigSerializer(queryset, many=True) + serializer = SystemConfigSerializer(queryset, many=True, request=self.request) return serializer.data + def get_title_i18n(self, obj): + lang = None + req = getattr(self, 'request', None) + if req: + user = getattr(req, 'user', None) + if user and getattr(user, 'is_authenticated', False): + lang = getattr(user, 'language', None) + _qp_lang = req.query_params.get('language', None) + if _qp_lang: + lang = _qp_lang[0] if isinstance(_qp_lang, list) else _qp_lang + if not lang: + meta_lang = req.META.get('HTTP_ACCEPT_LANGUAGE', '') + # Parse Accept-Language header (e.g., "zh-TW,zh;q=0.9,en;q=0.8") + if 'en' in meta_lang: + lang = 'en' + elif 'zh-tw' in meta_lang or 'zh-hant' in meta_lang: + lang = 'zh-tw' + elif meta_lang.startswith('zh'): + lang = 'zh-cn' + if lang == 'en': + return obj.title_en or obj.title + elif lang == 'zh-tw': + return obj.title_zh_tw or obj.title + return obj.title + class Meta: model = SystemConfig fields = "__all__" diff --git a/web/package.json b/web/package.json index b34579a..c7e98b4 100644 --- a/web/package.json +++ b/web/package.json @@ -18,7 +18,6 @@ "@fast-crud/fast-extends": "^1.21.2", "@fast-crud/ui-element": "^1.21.2", "@fast-crud/ui-interface": "^1.21.2", - "@great-dream/dvadmin3-celery-web": "^3.1.3", "@iconify/vue": "^4.1.2", "@meetjs/vant4-kit": "^1.0.1", "@types/lodash": "^4.17.7", diff --git a/web/src/components/fileSelector/index.vue b/web/src/components/fileSelector/index.vue index 86192c9..8a4eb74 100644 --- a/web/src/components/fileSelector/index.vue +++ b/web/src/components/fileSelector/index.vue @@ -3,7 +3,7 @@
@@ -93,34 +93,34 @@
- - - - + + + + - - - - + + + +
-
- 一共选中 {{ data?.length || 0 }} 个文件 + {{ t('message.pages.fileSelector.selected', { count: data?.length || 0 }) }}
@@ -133,10 +133,10 @@ :data="{ upload_method: 1 }" :show-file-list="true" :accept="AcceptList[tabsActived % 4]" :on-success="() => { listRequest(); listRequestAll(); uploadRef.clearFiles(); }" v-if="props.showUploadButton"> - 上传{{ TypeLabel[tabsActived % 4] }} + {{ t('message.pages.fileSelector.upload.button', { type: TypeLabel[tabsActived % 4] }) }} - 网络{{ TypeLabel[tabsActived % 4] }} + {{ t('message.pages.fileSelector.upload.netButton', { type: TypeLabel[tabsActived % 4] }) }} @@ -144,7 +144,7 @@
- +
@@ -164,10 +164,10 @@
- - + +