feat: 中文化 django-admin 字段标签 (T-603)

This commit is contained in:
QiuSW
2026-07-04 16:27:45 +08:00
parent 4aceb3ae25
commit fb1037933d
14 changed files with 714 additions and 139 deletions
+11 -11
View File
@@ -8,10 +8,10 @@ from .security import AiKeyEncryptionError, encrypt_api_key
class AiModelAdminForm(forms.ModelForm):
api_key = forms.CharField(
label="API key",
label="API Key",
required=False,
widget=forms.PasswordInput(render_value=False),
help_text="Leave blank to keep the existing encrypted key.",
help_text="留空表示保留已有加密密钥。",
)
class Meta:
@@ -33,7 +33,7 @@ class AiModelAdminForm(forms.ModelForm):
cleaned_data = super().clean()
api_key = cleaned_data.get("api_key")
if not self.instance.pk and not api_key:
raise forms.ValidationError("API key is required when creating an AI model.")
raise forms.ValidationError("创建 AI 模型时必须填写 API Key。")
if api_key:
try:
self._api_key_encrypted = encrypt_api_key(api_key)
@@ -71,14 +71,14 @@ class AiModelAdmin(admin.ModelAdmin):
fieldsets = (
(None, {"fields": ("name", "url", "model", "api_type", "is_active")}),
(
"Credentials",
"凭证",
{
"fields": ("api_key", "api_key_status"),
"description": "The stored key is encrypted and never displayed.",
"description": "已保存的密钥会加密存储,后台不会回显明文。",
},
),
(
"Capabilities and request defaults",
"能力与请求默认值",
{
"fields": (
"capabilities",
@@ -88,16 +88,16 @@ class AiModelAdmin(admin.ModelAdmin):
)
},
),
("Timestamps", {"fields": ("created_at", "updated_at")}),
("时间信息", {"fields": ("created_at", "updated_at")}),
)
@admin.display(description="capabilities")
@admin.display(description="能力")
def capabilities_display(self, obj):
return ", ".join(sorted(obj.capabilities_set()))
@admin.display(description="API key")
@admin.display(description="API Key")
def api_key_status(self, obj):
return obj.api_key_masked or "not set"
return obj.api_key_masked or "未设置"
def save_model(self, request, obj, form, change):
before = _snapshot_existing(obj) if change else None
@@ -241,7 +241,7 @@ class AiConfigAuditLogAdmin(admin.ModelAdmin):
def has_delete_permission(self, request, obj=None):
return False
@admin.display(description="changed fields")
@admin.display(description="变更字段")
def changed_fields_display(self, obj):
return ", ".join(obj.changed_fields)
@@ -0,0 +1,151 @@
# Generated by Django 5.2.15 on 2026-07-04 08:02
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ai', '0003_alter_aiconfigauditlog_options_alter_aimodel_options_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='aiconfigauditlog',
name='action',
field=models.CharField(choices=[('create', '创建'), ('update', '更新'), ('delete', '删除')], max_length=16, verbose_name='动作'),
),
migrations.AlterField(
model_name='aiconfigauditlog',
name='actor',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='操作人'),
),
migrations.AlterField(
model_name='aiconfigauditlog',
name='changed_fields',
field=models.JSONField(blank=True, default=list, verbose_name='变更字段'),
),
migrations.AlterField(
model_name='aiconfigauditlog',
name='changes',
field=models.JSONField(blank=True, default=dict, verbose_name='变更内容'),
),
migrations.AlterField(
model_name='aiconfigauditlog',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
),
migrations.AlterField(
model_name='aiconfigauditlog',
name='target_id',
field=models.PositiveBigIntegerField(blank=True, null=True, verbose_name='目标 ID'),
),
migrations.AlterField(
model_name='aiconfigauditlog',
name='target_repr',
field=models.CharField(max_length=255, verbose_name='目标描述'),
),
migrations.AlterField(
model_name='aiconfigauditlog',
name='target_type',
field=models.CharField(choices=[('ai_model', 'AI 模型'), ('model_alias', '能力别名')], max_length=32, verbose_name='目标类型'),
),
migrations.AlterField(
model_name='aimodel',
name='api_key_encrypted',
field=models.TextField(blank=True, editable=False, verbose_name='加密 API Key'),
),
migrations.AlterField(
model_name='aimodel',
name='api_type',
field=models.CharField(choices=[('auto', '自动'), ('chat', '聊天补全'), ('gemini', 'Gemini'), ('images', '图片生成'), ('images_edits', '图片编辑')], default='auto', max_length=32, verbose_name='API 类型'),
),
migrations.AlterField(
model_name='aimodel',
name='capabilities',
field=models.JSONField(blank=True, default=list, verbose_name='能力'),
),
migrations.AlterField(
model_name='aimodel',
name='connect_timeout_seconds',
field=models.PositiveIntegerField(default=30, verbose_name='连接超时秒数'),
),
migrations.AlterField(
model_name='aimodel',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
),
migrations.AlterField(
model_name='aimodel',
name='extra_body',
field=models.JSONField(blank=True, default=dict, verbose_name='额外请求体'),
),
migrations.AlterField(
model_name='aimodel',
name='is_active',
field=models.BooleanField(default=True, verbose_name='启用'),
),
migrations.AlterField(
model_name='aimodel',
name='model',
field=models.CharField(max_length=128, verbose_name='模型标识'),
),
migrations.AlterField(
model_name='aimodel',
name='name',
field=models.CharField(max_length=128, unique=True, verbose_name='名称'),
),
migrations.AlterField(
model_name='aimodel',
name='timeout_seconds',
field=models.PositiveIntegerField(default=0, help_text='0 表示使用 provider 默认值。', verbose_name='请求超时秒数'),
),
migrations.AlterField(
model_name='aimodel',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
),
migrations.AlterField(
model_name='aimodel',
name='url',
field=models.URLField(max_length=500, verbose_name='接口 URL'),
),
migrations.AlterField(
model_name='modelalias',
name='ai_model',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='aliases', to='ai.aimodel', verbose_name='AI 模型'),
),
migrations.AlterField(
model_name='modelalias',
name='alias',
field=models.SlugField(max_length=64, verbose_name='能力别名'),
),
migrations.AlterField(
model_name='modelalias',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='创建时间'),
),
migrations.AlterField(
model_name='modelalias',
name='is_active',
field=models.BooleanField(default=True, verbose_name='启用'),
),
migrations.AlterField(
model_name='modelalias',
name='is_default',
field=models.BooleanField(default=False, verbose_name='默认别名'),
),
migrations.AlterField(
model_name='modelalias',
name='operation_type',
field=models.CharField(choices=[('title', '生成标题'), ('image', '生成图片')], max_length=32, verbose_name='操作类型'),
),
migrations.AlterField(
model_name='modelalias',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
),
]
+39 -35
View File
@@ -11,31 +11,33 @@ from .security import decrypt_api_key, encrypt_api_key, mask_secret
class AiModel(models.Model):
class ApiType(models.TextChoices):
AUTO = "auto", "Auto"
CHAT = "chat", "Chat completions"
AUTO = "auto", "自动"
CHAT = "chat", "聊天补全"
GEMINI = "gemini", "Gemini"
IMAGES = "images", "Images generations"
IMAGES_EDITS = "images_edits", "Images edits"
IMAGES = "images", "图片生成"
IMAGES_EDITS = "images_edits", "图片编辑"
name = models.CharField(max_length=128, unique=True)
url = models.URLField(max_length=500)
model = models.CharField(max_length=128)
name = models.CharField("名称", max_length=128, unique=True)
url = models.URLField("接口 URL", max_length=500)
model = models.CharField("模型标识", max_length=128)
api_type = models.CharField(
"API 类型",
max_length=32,
choices=ApiType.choices,
default=ApiType.AUTO,
)
api_key_encrypted = models.TextField(blank=True, editable=False)
capabilities = models.JSONField(default=list, blank=True)
api_key_encrypted = models.TextField("加密 API Key", blank=True, editable=False)
capabilities = models.JSONField("能力", default=list, blank=True)
timeout_seconds = models.PositiveIntegerField(
"请求超时秒数",
default=0,
help_text="0 means use provider resolution defaults.",
help_text="0 表示使用 provider 默认值。",
)
connect_timeout_seconds = models.PositiveIntegerField(default=30)
extra_body = models.JSONField(default=dict, blank=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
connect_timeout_seconds = models.PositiveIntegerField("连接超时秒数", default=30)
extra_body = models.JSONField("额外请求体", default=dict, blank=True)
is_active = models.BooleanField("启用", default=True)
created_at = models.DateTimeField("创建时间", auto_now_add=True)
updated_at = models.DateTimeField("更新时间", auto_now=True)
class Meta:
db_table = "ai_model"
@@ -88,20 +90,21 @@ class AiModel(models.Model):
class ModelAlias(models.Model):
class OperationType(models.TextChoices):
TITLE = "title", "Generate title"
IMAGE = "image", "Generate image"
TITLE = "title", "生成标题"
IMAGE = "image", "生成图片"
alias = models.SlugField(max_length=64)
operation_type = models.CharField(max_length=32, choices=OperationType.choices)
alias = models.SlugField("能力别名", max_length=64)
operation_type = models.CharField("操作类型", max_length=32, choices=OperationType.choices)
ai_model = models.ForeignKey(
AiModel,
verbose_name="AI 模型",
on_delete=models.PROTECT,
related_name="aliases",
)
is_default = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_default = models.BooleanField("默认别名", default=False)
is_active = models.BooleanField("启用", default=True)
created_at = models.DateTimeField("创建时间", auto_now_add=True)
updated_at = models.DateTimeField("更新时间", auto_now=True)
class Meta:
db_table = "model_alias"
@@ -142,28 +145,29 @@ class ModelAlias(models.Model):
class AiConfigAuditLog(models.Model):
class TargetType(models.TextChoices):
AI_MODEL = "ai_model", "AI model"
MODEL_ALIAS = "model_alias", "Model alias"
AI_MODEL = "ai_model", "AI 模型"
MODEL_ALIAS = "model_alias", "能力别名"
class Action(models.TextChoices):
CREATE = "create", "Create"
UPDATE = "update", "Update"
DELETE = "delete", "Delete"
CREATE = "create", "创建"
UPDATE = "update", "更新"
DELETE = "delete", "删除"
actor = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name="操作人",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
action = models.CharField(max_length=16, choices=Action.choices)
target_type = models.CharField(max_length=32, choices=TargetType.choices)
target_id = models.PositiveBigIntegerField(null=True, blank=True)
target_repr = models.CharField(max_length=255)
changed_fields = models.JSONField(default=list, blank=True)
changes = models.JSONField(default=dict, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
action = models.CharField("动作", max_length=16, choices=Action.choices)
target_type = models.CharField("目标类型", max_length=32, choices=TargetType.choices)
target_id = models.PositiveBigIntegerField("目标 ID", null=True, blank=True)
target_repr = models.CharField("目标描述", max_length=255)
changed_fields = models.JSONField("变更字段", default=list, blank=True)
changes = models.JSONField("变更内容", default=dict, blank=True)
created_at = models.DateTimeField("创建时间", auto_now_add=True)
class Meta:
db_table = "ai_config_audit_log"