feat(i18n): 为评论系统添加国际化支持
- 在管理后台引入 vue-i18n 并添加多语言文件 - 为前端评论组件添加翻译功能,支持自动语言检测 - 在功能设置中新增管理后台和组件语言配置选项 - 更新 API 接口以支持语言设置存储 - 优化菜单项文本溢出显示样式
This commit is contained in:
@@ -3,6 +3,7 @@ import { Component } from './Component.js';
|
||||
export class AdminAuthModal extends Component {
|
||||
constructor(container, props = {}) {
|
||||
super(container, props);
|
||||
this.t = props.t || ((k) => k);
|
||||
this.state = {
|
||||
key: '',
|
||||
error: '',
|
||||
@@ -24,16 +25,16 @@ export class AdminAuthModal extends Component {
|
||||
this.createElement('div', {
|
||||
className: 'cwd-modal',
|
||||
children: [
|
||||
this.createTextElement('h3', '管理员身份验证', 'cwd-modal-title'),
|
||||
this.createTextElement('h3', this.t('verifyAdminTitle'), 'cwd-modal-title'),
|
||||
this.createElement('div', {
|
||||
className: 'cwd-modal-body',
|
||||
children: [
|
||||
this.createTextElement('p', '检测到管理员邮箱,请输入密钥以继续。', 'cwd-modal-desc'),
|
||||
this.createTextElement('p', this.t('verifyAdminDesc'), 'cwd-modal-desc'),
|
||||
this.createElement('input', {
|
||||
className: `cwd-form-input ${error ? 'cwd-input-error' : ''}`,
|
||||
attributes: {
|
||||
type: 'password',
|
||||
placeholder: '请输入管理员密钥',
|
||||
placeholder: this.t('adminKeyPlaceholder'),
|
||||
value: key,
|
||||
disabled: loading,
|
||||
onInput: (e) => this.setState({ key: e.target.value, error: '' }),
|
||||
@@ -50,7 +51,7 @@ export class AdminAuthModal extends Component {
|
||||
children: [
|
||||
this.createElement('button', {
|
||||
className: 'cwd-btn cwd-btn-secondary',
|
||||
text: '取消',
|
||||
text: this.t('cancel'),
|
||||
attributes: {
|
||||
type: 'button',
|
||||
disabled: loading,
|
||||
@@ -59,7 +60,7 @@ export class AdminAuthModal extends Component {
|
||||
}),
|
||||
this.createElement('button', {
|
||||
className: 'cwd-btn cwd-btn-primary',
|
||||
text: loading ? '验证中...' : '验证',
|
||||
text: loading ? this.t('verifying') : this.t('verify'),
|
||||
attributes: {
|
||||
type: 'button',
|
||||
disabled: loading || !key,
|
||||
@@ -116,7 +117,7 @@ export class AdminAuthModal extends Component {
|
||||
|
||||
if (this.elements.submitBtn) {
|
||||
this.elements.submitBtn.disabled = loading || !key;
|
||||
this.elements.submitBtn.textContent = loading ? '验证中...' : '验证';
|
||||
this.elements.submitBtn.textContent = loading ? this.t('verifying') : this.t('verify');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +127,7 @@ export class AdminAuthModal extends Component {
|
||||
this.setState({ loading: true });
|
||||
this.props.onSubmit(this.state.key)
|
||||
.catch(err => {
|
||||
this.setState({ error: err.message || '验证失败', loading: false });
|
||||
this.setState({ error: err.message || this.t('verifyFailed'), loading: false });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export class CommentForm extends Component {
|
||||
*/
|
||||
constructor(container, props = {}) {
|
||||
super(container, props);
|
||||
this.t = props.t || ((k) => k);
|
||||
// 确保 localForm 的各个字段都有初始值
|
||||
const initialForm = props.form || {};
|
||||
this.state = {
|
||||
@@ -53,7 +54,7 @@ export class CommentForm extends Component {
|
||||
},
|
||||
children: [
|
||||
// 标题
|
||||
this.createTextElement('h3', '发表评论', 'cwd-form-title'),
|
||||
this.createTextElement('h3', this.t('formTitle'), 'cwd-form-title'),
|
||||
|
||||
// 表单字段
|
||||
this.createElement('div', {
|
||||
@@ -64,19 +65,19 @@ export class CommentForm extends Component {
|
||||
className: 'cwd-form-row',
|
||||
children: [
|
||||
// 昵称
|
||||
this.createFormField('昵称 *', 'text', 'name', localForm.name, formErrors.name),
|
||||
this.createFormField(this.t('nickname'), 'text', 'name', localForm.name, formErrors.name),
|
||||
// 邮箱
|
||||
this.createElement('div', {
|
||||
className: 'cwd-form-field-wrapper',
|
||||
children: [
|
||||
this.createFormField('邮箱 *', 'email', 'email', localForm.email, formErrors.email),
|
||||
this.createFormField(this.t('email'), 'email', 'email', localForm.email, formErrors.email),
|
||||
isVerified
|
||||
? this.createElement('div', {
|
||||
className: 'cwd-admin-controls',
|
||||
children: [
|
||||
this.createElement('button', {
|
||||
className: 'cwd-btn-text',
|
||||
text: '退出验证',
|
||||
text: this.t('verifyAdmin'),
|
||||
attributes: {
|
||||
type: 'button',
|
||||
title: '清除管理员凭证',
|
||||
@@ -92,7 +93,7 @@ export class CommentForm extends Component {
|
||||
],
|
||||
}),
|
||||
// 网址
|
||||
this.createFormField('网址', 'url', 'url', localForm.url, formErrors.url),
|
||||
this.createFormField(this.t('website'), 'url', 'url', localForm.url, formErrors.url),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -100,7 +101,7 @@ export class CommentForm extends Component {
|
||||
this.createElement('div', {
|
||||
className: 'cwd-form-field',
|
||||
children: [
|
||||
this.createTextElement('label', '写下你的评论...', 'cwd-form-label'),
|
||||
this.createTextElement('label', this.t('writeComment'), 'cwd-form-label'),
|
||||
this.createElement('textarea', {
|
||||
className: `cwd-form-textarea ${formErrors.content ? 'cwd-input-error' : ''}`,
|
||||
attributes: {
|
||||
@@ -129,7 +130,7 @@ export class CommentForm extends Component {
|
||||
style: localForm.content?.trim() ? '' : 'display:none;',
|
||||
onClick: () => this.togglePreview(),
|
||||
},
|
||||
text: this.state.showPreview ? '关闭' : '预览',
|
||||
text: this.state.showPreview ? this.t('close') : this.t('preview'),
|
||||
}),
|
||||
this.createElement('button', {
|
||||
className: 'cwd-btn cwd-btn-primary',
|
||||
@@ -137,7 +138,7 @@ export class CommentForm extends Component {
|
||||
type: 'submit',
|
||||
disabled: submitting || !canSubmit,
|
||||
},
|
||||
text: submitting ? '提交中...' : '提交评论',
|
||||
text: submitting ? this.t('submitting') : this.t('submit'),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -209,7 +210,7 @@ export class CommentForm extends Component {
|
||||
const submitBtn = this.elements.root.querySelector('button[type="submit"]');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = submitting || !canSubmit;
|
||||
submitBtn.textContent = submitting ? '提交中...' : '提交评论';
|
||||
submitBtn.textContent = submitting ? this.t('submitting') : this.t('submit');
|
||||
}
|
||||
|
||||
// 更新预览按钮状态
|
||||
@@ -224,9 +225,9 @@ export class CommentForm extends Component {
|
||||
if (previewContainer) {
|
||||
previewContainer.remove();
|
||||
}
|
||||
previewBtn.textContent = '预览';
|
||||
previewBtn.textContent = this.t('preview');
|
||||
} else {
|
||||
previewBtn.textContent = this.state.showPreview ? '关闭' : '预览';
|
||||
previewBtn.textContent = this.state.showPreview ? this.t('close') : this.t('preview');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,6 +417,7 @@ export class CommentForm extends Component {
|
||||
this.modal = null;
|
||||
}
|
||||
},
|
||||
t: this.t
|
||||
});
|
||||
this.modal.render();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export class CommentItem extends Component {
|
||||
*/
|
||||
constructor(container, props = {}) {
|
||||
super(container, props);
|
||||
this.t = props.t || ((k) => k);
|
||||
this.replyEditor = null;
|
||||
this.childCommentItems = []; // 缓存嵌套回复的 CommentItem 实例
|
||||
}
|
||||
@@ -103,7 +104,7 @@ export class CommentItem extends Component {
|
||||
// 显示回复目标
|
||||
...(comment.replyToAuthor
|
||||
? [
|
||||
this.createTextElement('span', ' 回复 ', 'cwd-reply-to-separator'),
|
||||
this.createTextElement('span', ` ${this.t('reply')} `, 'cwd-reply-to-separator'),
|
||||
this.createTextElement('span', comment.replyToAuthor, 'cwd-reply-to-author'),
|
||||
]
|
||||
: []),
|
||||
@@ -119,7 +120,7 @@ export class CommentItem extends Component {
|
||||
attributes: {
|
||||
onClick: () => this.handleReply(),
|
||||
},
|
||||
text: '回复',
|
||||
text: this.t('reply'),
|
||||
}),
|
||||
...(this.props.enableCommentLike !== false
|
||||
? [
|
||||
@@ -169,7 +170,7 @@ export class CommentItem extends Component {
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
this.createTextElement('span', formatRelativeTime(comment.created), 'cwd-comment-time'),
|
||||
this.createTextElement('span', formatRelativeTime(comment.created, this.t), 'cwd-comment-time'),
|
||||
],
|
||||
}),
|
||||
],
|
||||
@@ -220,6 +221,7 @@ export class CommentItem extends Component {
|
||||
onCancel: () => this.handleCancelReply(),
|
||||
onClearError: () => this.handleClearReplyError(),
|
||||
placeholder: this.props.replyPlaceholder,
|
||||
t: this.t
|
||||
});
|
||||
this.replyEditor.render();
|
||||
this.replyEditor.focus();
|
||||
@@ -253,6 +255,7 @@ export class CommentItem extends Component {
|
||||
onCancelReply: this.props.onCancelReply,
|
||||
onUpdateReplyContent: this.props.onUpdateReplyContent,
|
||||
onClearReplyError: this.props.onClearReplyError,
|
||||
t: this.t
|
||||
});
|
||||
replyItem.render();
|
||||
this.childCommentItems.push(replyItem);
|
||||
|
||||
@@ -106,7 +106,8 @@ export class CommentList extends Component {
|
||||
onCancelReply: () => this.handleCancelReply(),
|
||||
onUpdateReplyContent: (content) => this.handleUpdateReplyContent(content),
|
||||
onClearReplyError: () => this.handleClearReplyError(),
|
||||
onLikeComment: (commentId, isLike) => this.handleLikeComment(commentId, isLike)
|
||||
onLikeComment: (commentId, isLike) => this.handleLikeComment(commentId, isLike),
|
||||
t: this.props.t
|
||||
});
|
||||
commentItem.render();
|
||||
// 缓存 CommentItem 实例
|
||||
|
||||
@@ -20,6 +20,7 @@ export class ReplyEditor extends Component {
|
||||
*/
|
||||
constructor(container, props = {}) {
|
||||
super(container, props);
|
||||
this.t = props.t || ((k) => k);
|
||||
const { currentUser } = props;
|
||||
this.state = {
|
||||
content: props.content || '',
|
||||
@@ -41,7 +42,7 @@ export class ReplyEditor extends Component {
|
||||
this.createElement('div', {
|
||||
className: 'cwd-reply-header',
|
||||
children: [
|
||||
this.createTextElement('span', `回复 @${this.props.replyToAuthor}`, 'cwd-reply-to'),
|
||||
this.createTextElement('span', `${this.t('reply')} @${this.props.replyToAuthor}`, 'cwd-reply-to'),
|
||||
this.createElement('button', {
|
||||
className: 'cwd-btn-close',
|
||||
attributes: {
|
||||
@@ -62,9 +63,9 @@ export class ReplyEditor extends Component {
|
||||
style: 'margin-bottom: 12px;',
|
||||
},
|
||||
children: [
|
||||
this.createFormField('昵称 *', 'text', 'name', currentUser?.name),
|
||||
this.createFormField('邮箱 *', 'email', 'email', currentUser?.email),
|
||||
this.createFormField('网址', 'url', 'url', currentUser?.url),
|
||||
this.createFormField(this.t('nickname'), 'text', 'name', currentUser?.name),
|
||||
this.createFormField(this.t('email'), 'email', 'email', currentUser?.email),
|
||||
this.createFormField(this.t('website'), 'url', 'url', currentUser?.url),
|
||||
],
|
||||
}),
|
||||
]
|
||||
@@ -113,7 +114,7 @@ export class ReplyEditor extends Component {
|
||||
disabled: this.props.submitting || !this.state.content.trim(),
|
||||
onClick: () => this.togglePreview(),
|
||||
},
|
||||
text: this.state.showPreview ? '关闭' : '预览',
|
||||
text: this.state.showPreview ? this.t('close') : this.t('preview'),
|
||||
}),
|
||||
this.createElement('button', {
|
||||
className: 'cwd-btn cwd-btn-primary cwd-btn-small',
|
||||
@@ -122,7 +123,7 @@ export class ReplyEditor extends Component {
|
||||
disabled: this.props.submitting || !this.state.content.trim(),
|
||||
onClick: () => this.handleSubmit(),
|
||||
},
|
||||
text: this.props.submitting ? '提交中...' : '提交回复',
|
||||
text: this.props.submitting ? this.t('submitting') : this.t('submit'),
|
||||
}),
|
||||
this.createElement('button', {
|
||||
className: 'cwd-btn cwd-btn-secondary cwd-btn-small',
|
||||
@@ -131,7 +132,7 @@ export class ReplyEditor extends Component {
|
||||
disabled: this.props.submitting,
|
||||
onClick: () => this.handleCancel(),
|
||||
},
|
||||
text: '取消',
|
||||
text: this.t('cancel'),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user