mirror of
https://github.com/fantasy-ke/obsidian-cf-imgbed.git
synced 2026-07-22 06:43:08 +00:00
feat: Integrate internationalization support for enhanced user experience
- Add I18n class for language management and translations. - Update settings tab and image handler to utilize internationalization for labels and messages. - Modify event handlers to support localized menu titles. - Ensure language settings are configurable and persist across sessions.
This commit is contained in:
parent
39ff979c1b
commit
601a926858
7 changed files with 420 additions and 67 deletions
9
main.ts
9
main.ts
|
|
@ -4,20 +4,25 @@ import { UploadService } from './src/upload/uploadService';
|
|||
import { ImageHandler } from './src/upload/imageHandler';
|
||||
import { EventHandlers } from './src/events/eventHandlers';
|
||||
import { CFImageBedSettingTab } from './src/settings/settingsTab';
|
||||
import { I18n } from './src/utils/i18n';
|
||||
|
||||
export default class CFImageBedPlugin extends Plugin {
|
||||
settings: CFImageBedSettings;
|
||||
private uploadService: UploadService;
|
||||
private imageHandler: ImageHandler;
|
||||
private eventHandlers: EventHandlers;
|
||||
private i18n: I18n;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// 初始化i18n
|
||||
this.i18n = new I18n(this.settings.language || 'zh');
|
||||
|
||||
// 初始化服务
|
||||
this.uploadService = new UploadService(this.settings);
|
||||
this.imageHandler = new ImageHandler(this.app, this.uploadService, () => this.settings);
|
||||
this.eventHandlers = new EventHandlers(this.imageHandler);
|
||||
this.imageHandler = new ImageHandler(this.app, this.uploadService, () => this.settings, this.i18n);
|
||||
this.eventHandlers = new EventHandlers(this.imageHandler, this.i18n);
|
||||
|
||||
// 注册事件处理器
|
||||
this.eventHandlers.registerDragAndDropEvents(this);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import { Plugin, Menu, Editor, MarkdownView } from 'obsidian';
|
||||
import { ImageHandler } from '../upload/imageHandler';
|
||||
import { I18n } from '../utils/i18n';
|
||||
|
||||
export class EventHandlers {
|
||||
constructor(private imageHandler: ImageHandler) {}
|
||||
constructor(
|
||||
private imageHandler: ImageHandler,
|
||||
private i18n: I18n
|
||||
) {}
|
||||
|
||||
registerDragAndDropEvents(plugin: Plugin): void {
|
||||
// 添加拖拽上传功能
|
||||
|
|
@ -57,7 +61,7 @@ export class EventHandlers {
|
|||
plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownView) => {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle('Upload image to CF ImageBed')
|
||||
.setTitle(this.i18n.t('menu.uploadImage'))
|
||||
.setIcon('upload')
|
||||
.onClick(() => {
|
||||
this.imageHandler.selectAndUploadImage();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import { App, PluginSettingTab, Setting } from 'obsidian';
|
||||
import CFImageBedPlugin from '../../main';
|
||||
import { I18n } from '../utils/i18n';
|
||||
|
||||
export class CFImageBedSettingTab extends PluginSettingTab {
|
||||
plugin: CFImageBedPlugin;
|
||||
private i18n: I18n;
|
||||
|
||||
constructor(app: App, plugin: CFImageBedPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
this.i18n = new I18n(plugin.settings.language || 'zh');
|
||||
}
|
||||
|
||||
display(): void {
|
||||
|
|
@ -14,20 +17,39 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
containerEl.empty();
|
||||
|
||||
// 更新语言
|
||||
this.i18n.setLanguage(this.plugin.settings.language || 'zh');
|
||||
|
||||
// 添加插件专用的CSS类名,限制样式作用域
|
||||
containerEl.addClass('cf-imagebed-settings');
|
||||
|
||||
new Setting(containerEl).setName('CF ImageBed settings').setHeading();
|
||||
new Setting(containerEl).setName(this.i18n.t('settings.title')).setHeading();
|
||||
|
||||
// 语言设置(在顶部)
|
||||
new Setting(containerEl)
|
||||
.setName(this.i18n.t('settings.language.name'))
|
||||
.setDesc(this.i18n.t('settings.language.desc'))
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('zh', '中文')
|
||||
.addOption('en', 'English')
|
||||
.setValue(this.plugin.settings.language || 'zh')
|
||||
.onChange(async (value: 'zh' | 'en') => {
|
||||
this.plugin.settings.language = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.i18n.setLanguage(value);
|
||||
// 重新渲染设置界面
|
||||
this.display();
|
||||
}));
|
||||
|
||||
// 创建选项卡容器
|
||||
const tabContainer = containerEl.createDiv('cf-imagebed-tabs');
|
||||
const tabContent = containerEl.createDiv('cf-imagebed-tab-content');
|
||||
|
||||
// 创建选项卡按钮
|
||||
const basicTab = tabContainer.createEl('button', { text: '基础设置', cls: 'cf-tab-button active' });
|
||||
const advancedTab = tabContainer.createEl('button', { text: '高级设置', cls: 'cf-tab-button' });
|
||||
const userTab = tabContainer.createEl('button', { text: '用户体验', cls: 'cf-tab-button' });
|
||||
const backupTab = tabContainer.createEl('button', { text: '备份设置', cls: 'cf-tab-button' });
|
||||
const basicTab = tabContainer.createEl('button', { text: this.i18n.t('settings.tabs.basic'), cls: 'cf-tab-button active' });
|
||||
const advancedTab = tabContainer.createEl('button', { text: this.i18n.t('settings.tabs.advanced'), cls: 'cf-tab-button' });
|
||||
const userTab = tabContainer.createEl('button', { text: this.i18n.t('settings.tabs.userExperience'), cls: 'cf-tab-button' });
|
||||
const backupTab = tabContainer.createEl('button', { text: this.i18n.t('settings.tabs.backup'), cls: 'cf-tab-button' });
|
||||
|
||||
// 创建选项卡内容区域
|
||||
const basicContent = tabContent.createDiv('cf-tab-panel active');
|
||||
|
|
@ -67,8 +89,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
private createBasicSettings(container: HTMLElement): void {
|
||||
// API URL 设置
|
||||
new Setting(container)
|
||||
.setName('API URL')
|
||||
.setDesc('CloudFlare ImgBed API address (e.g., https://your.domain)')
|
||||
.setName(this.i18n.t('settings.basic.apiUrl.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.apiUrl.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder('https://your.domain')
|
||||
.setValue(this.plugin.settings.apiUrl)
|
||||
|
|
@ -79,8 +101,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 认证码设置
|
||||
new Setting(container)
|
||||
.setName('Auth code')
|
||||
.setDesc('Upload authentication code')
|
||||
.setName(this.i18n.t('settings.basic.authCode.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.authCode.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder('your_authCode')
|
||||
.setValue(this.plugin.settings.authCode)
|
||||
|
|
@ -91,8 +113,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 上传渠道设置
|
||||
new Setting(container)
|
||||
.setName('Upload channel')
|
||||
.setDesc('Select upload channel')
|
||||
.setName(this.i18n.t('settings.basic.uploadChannel.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.uploadChannel.desc'))
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('telegram', 'Telegram')
|
||||
.addOption('cfr2', 'CloudFlare R2')
|
||||
|
|
@ -105,8 +127,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 文件命名方式设置
|
||||
new Setting(container)
|
||||
.setName('File naming method')
|
||||
.setDesc('Select file naming method')
|
||||
.setName(this.i18n.t('settings.basic.uploadNameType.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.uploadNameType.desc'))
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('default', '默认前缀_原名命名')
|
||||
.addOption('index', '仅前缀命名')
|
||||
|
|
@ -120,8 +142,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 返回格式设置
|
||||
new Setting(container)
|
||||
.setName('Return link format')
|
||||
.setDesc('Select return link format')
|
||||
.setName(this.i18n.t('settings.basic.returnFormat.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.returnFormat.desc'))
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('default', '默认格式 /file/id')
|
||||
.addOption('full', '完整链接格式')
|
||||
|
|
@ -133,8 +155,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 上传目录设置
|
||||
new Setting(container)
|
||||
.setName('Upload folder')
|
||||
.setDesc('Upload folder, use relative path (e.g., img/test)')
|
||||
.setName(this.i18n.t('settings.basic.uploadFolder.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.uploadFolder.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder('img/test')
|
||||
.setValue(this.plugin.settings.uploadFolder)
|
||||
|
|
@ -145,8 +167,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 服务端压缩设置
|
||||
new Setting(container)
|
||||
.setName('Server compression')
|
||||
.setDesc('Enable server-side compression (only for Telegram channel image files)')
|
||||
.setName(this.i18n.t('settings.basic.serverCompress.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.serverCompress.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.serverCompress)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -156,8 +178,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 自动重试设置
|
||||
new Setting(container)
|
||||
.setName('Auto retry')
|
||||
.setDesc('Automatically switch channels and retry on failure')
|
||||
.setName(this.i18n.t('settings.basic.autoRetry.name'))
|
||||
.setDesc(this.i18n.t('settings.basic.autoRetry.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.autoRetry)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -169,8 +191,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
private createAdvancedSettings(container: HTMLElement): void {
|
||||
// 文件大小限制
|
||||
new Setting(container)
|
||||
.setName('Maximum file size')
|
||||
.setDesc('Set maximum size for uploaded files (MB)')
|
||||
.setName(this.i18n.t('settings.advanced.maxFileSize.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.maxFileSize.desc'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(1, 50, 1)
|
||||
.setValue(this.plugin.settings.maxFileSize)
|
||||
|
|
@ -182,8 +204,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 允许的文件类型
|
||||
new Setting(container)
|
||||
.setName('Allowed file types')
|
||||
.setDesc('Set allowed file types for upload (comma-separated)')
|
||||
.setName(this.i18n.t('settings.advanced.allowedFileTypes.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.allowedFileTypes.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder('jpg,jpeg,png,gif,webp,bmp')
|
||||
.setValue(this.plugin.settings.allowedFileTypes.join(','))
|
||||
|
|
@ -194,8 +216,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 水印设置
|
||||
new Setting(container)
|
||||
.setName('Enable watermark')
|
||||
.setDesc('Add watermark to uploaded images')
|
||||
.setName(this.i18n.t('settings.advanced.enableWatermark.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.enableWatermark.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableWatermark)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -205,7 +227,12 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
const fields = container.querySelectorAll('input, select');
|
||||
fields.forEach((el) => {
|
||||
const label = (el.closest('.setting-item')?.querySelector('.setting-item-name')?.textContent || '').trim();
|
||||
const dependent = ['Watermark text', 'Watermark position', 'Watermark font size', 'Watermark opacity'];
|
||||
const dependent = [
|
||||
this.i18n.t('settings.advanced.watermarkText.name'),
|
||||
this.i18n.t('settings.advanced.watermarkPosition.name'),
|
||||
this.i18n.t('settings.advanced.watermarkSize.name'),
|
||||
this.i18n.t('settings.advanced.watermarkOpacity.name')
|
||||
];
|
||||
if (dependent.some(d => label.includes(d))) {
|
||||
(el as HTMLInputElement | HTMLSelectElement).disabled = !value;
|
||||
}
|
||||
|
|
@ -214,8 +241,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 水印文字
|
||||
new Setting(container)
|
||||
.setName('Watermark text')
|
||||
.setDesc('Set watermark text content')
|
||||
.setName(this.i18n.t('settings.advanced.watermarkText.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.watermarkText.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder('水印文字')
|
||||
.setValue(this.plugin.settings.watermarkText)
|
||||
|
|
@ -227,8 +254,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 水印位置
|
||||
new Setting(container)
|
||||
.setName('Watermark position')
|
||||
.setDesc('Set watermark position in image')
|
||||
.setName(this.i18n.t('settings.advanced.watermarkPosition.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.watermarkPosition.desc'))
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('top-left', '左上角')
|
||||
.addOption('top-right', '右上角')
|
||||
|
|
@ -244,8 +271,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 水印字体大小
|
||||
new Setting(container)
|
||||
.setName('Watermark font size')
|
||||
.setDesc('Set watermark text font size (pixels)')
|
||||
.setName(this.i18n.t('settings.advanced.watermarkSize.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.watermarkSize.desc'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(12, 72, 2)
|
||||
.setValue(this.plugin.settings.watermarkSize)
|
||||
|
|
@ -258,8 +285,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 水印透明度
|
||||
new Setting(container)
|
||||
.setName('Watermark opacity')
|
||||
.setDesc('Set watermark opacity (0-1)')
|
||||
.setName(this.i18n.t('settings.advanced.watermarkOpacity.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.watermarkOpacity.desc'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(0.1, 1, 0.1)
|
||||
.setValue(this.plugin.settings.watermarkOpacity)
|
||||
|
|
@ -272,8 +299,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 客户端压缩设置
|
||||
new Setting(container)
|
||||
.setName('Enable client compression')
|
||||
.setDesc('Automatically compress images before upload to reduce file size')
|
||||
.setName(this.i18n.t('settings.advanced.enableClientCompress.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.enableClientCompress.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableClientCompress)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -283,7 +310,10 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
const fields = container.querySelectorAll('input');
|
||||
fields.forEach((el) => {
|
||||
const label = (el.closest('.setting-item')?.querySelector('.setting-item-name')?.textContent || '').trim();
|
||||
const dependent = ['Compression threshold', 'Target size'];
|
||||
const dependent = [
|
||||
this.i18n.t('settings.advanced.compressThreshold.name'),
|
||||
this.i18n.t('settings.advanced.targetSize.name')
|
||||
];
|
||||
if (dependent.some(d => label.includes(d))) {
|
||||
(el as HTMLInputElement).disabled = !value;
|
||||
}
|
||||
|
|
@ -292,8 +322,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 压缩阈值
|
||||
new Setting(container)
|
||||
.setName('Compression threshold')
|
||||
.setDesc('Set image size threshold, files exceeding this will be automatically compressed (MB)')
|
||||
.setName(this.i18n.t('settings.advanced.compressThreshold.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.compressThreshold.desc'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(0.5, 10, 0.5)
|
||||
.setValue(this.plugin.settings.compressThreshold)
|
||||
|
|
@ -306,8 +336,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 期望大小
|
||||
new Setting(container)
|
||||
.setName('Target size')
|
||||
.setDesc('Set expected size for compressed images (MB)')
|
||||
.setName(this.i18n.t('settings.advanced.targetSize.name'))
|
||||
.setDesc(this.i18n.t('settings.advanced.targetSize.desc'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(0.1, 5, 0.1)
|
||||
.setValue(this.plugin.settings.targetSize)
|
||||
|
|
@ -322,8 +352,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
private createUserExperienceSettings(container: HTMLElement): void {
|
||||
// 显示上传进度
|
||||
new Setting(container)
|
||||
.setName('Show upload progress')
|
||||
.setDesc('Show progress information during upload')
|
||||
.setName(this.i18n.t('settings.userExperience.showUploadProgress.name'))
|
||||
.setDesc(this.i18n.t('settings.userExperience.showUploadProgress.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showUploadProgress)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -333,8 +363,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 显示成功通知
|
||||
new Setting(container)
|
||||
.setName('Show success notification')
|
||||
.setDesc('Show notification message on successful upload')
|
||||
.setName(this.i18n.t('settings.userExperience.showSuccessNotification.name'))
|
||||
.setDesc(this.i18n.t('settings.userExperience.showSuccessNotification.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showSuccessNotification)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -344,8 +374,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 显示错误通知
|
||||
new Setting(container)
|
||||
.setName('Show error notification')
|
||||
.setDesc('Show error message when upload fails')
|
||||
.setName(this.i18n.t('settings.userExperience.showErrorNotification.name'))
|
||||
.setDesc(this.i18n.t('settings.userExperience.showErrorNotification.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showErrorNotification)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -355,8 +385,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 通知持续时间
|
||||
new Setting(container)
|
||||
.setName('Notification duration')
|
||||
.setDesc('Set duration for notification display (seconds)')
|
||||
.setName(this.i18n.t('settings.userExperience.notificationDuration.name'))
|
||||
.setDesc(this.i18n.t('settings.userExperience.notificationDuration.desc'))
|
||||
.addSlider(slider => slider
|
||||
.setLimits(1, 10, 1)
|
||||
.setValue(this.plugin.settings.notificationDuration)
|
||||
|
|
@ -372,8 +402,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
private createBackupSettings(container: HTMLElement): void {
|
||||
// 启用本地备份
|
||||
new Setting(container)
|
||||
.setName('Enable local backup')
|
||||
.setDesc('Save a local backup while uploading to cloud')
|
||||
.setName(this.i18n.t('settings.backup.enableLocalBackup.name'))
|
||||
.setDesc(this.i18n.t('settings.backup.enableLocalBackup.desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableLocalBackup)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -386,8 +416,8 @@ export class CFImageBedSettingTab extends PluginSettingTab {
|
|||
|
||||
// 备份路径
|
||||
new Setting(container)
|
||||
.setName('Backup path')
|
||||
.setDesc('Set local backup storage path (relative to vault root)')
|
||||
.setName(this.i18n.t('settings.backup.backupPath.name'))
|
||||
.setDesc(this.i18n.t('settings.backup.backupPath.desc'))
|
||||
.addText(text => text
|
||||
.setPlaceholder('attachments/backup')
|
||||
.setValue(this.plugin.settings.backupPath)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ export interface CFImageBedSettings {
|
|||
// 备份配置
|
||||
enableLocalBackup: boolean;
|
||||
backupPath: string;
|
||||
|
||||
// 语言配置
|
||||
language: 'zh' | 'en';
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: CFImageBedSettings = {
|
||||
|
|
@ -68,5 +71,8 @@ export const DEFAULT_SETTINGS: CFImageBedSettings = {
|
|||
// 备份配置
|
||||
enableLocalBackup: false,
|
||||
backupPath: 'attachments/backup',
|
||||
|
||||
// 语言配置
|
||||
language: 'zh',
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { App, MarkdownView, Notice, Platform } from 'obsidian';
|
||||
import { CFImageBedSettings } from '../types';
|
||||
import { UploadService } from './uploadService';
|
||||
import { I18n } from '../utils/i18n';
|
||||
|
||||
export class ImageHandler {
|
||||
constructor(
|
||||
private app: App,
|
||||
private uploadService: UploadService,
|
||||
private getSettings?: () => CFImageBedSettings
|
||||
private getSettings?: () => CFImageBedSettings,
|
||||
private i18n?: I18n
|
||||
) {}
|
||||
|
||||
async uploadImageFromFile(file: File, deleteLocal: boolean = false): Promise<void> {
|
||||
|
|
@ -106,22 +108,22 @@ export class ImageHandler {
|
|||
dialog.className = 'cf-imagebed-dialog';
|
||||
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = 'Select image source';
|
||||
title.textContent = this.i18n?.t('mobile.selectSource') || 'Select image source';
|
||||
title.className = 'cf-imagebed-dialog-title';
|
||||
|
||||
const buttonContainer = document.createElement('div');
|
||||
buttonContainer.className = 'cf-imagebed-button-container';
|
||||
|
||||
const cameraBtn = document.createElement('button');
|
||||
cameraBtn.textContent = '📷 Take photo';
|
||||
cameraBtn.textContent = this.i18n?.t('mobile.takePhoto') || '📷 Take photo';
|
||||
cameraBtn.className = 'cf-imagebed-camera-btn';
|
||||
|
||||
const galleryBtn = document.createElement('button');
|
||||
galleryBtn.textContent = '🖼️ Select from gallery';
|
||||
galleryBtn.textContent = this.i18n?.t('mobile.selectFromGallery') || '🖼️ Select from gallery';
|
||||
galleryBtn.className = 'cf-imagebed-gallery-btn';
|
||||
|
||||
const cancelBtn = document.createElement('button');
|
||||
cancelBtn.textContent = 'Cancel';
|
||||
cancelBtn.textContent = this.i18n?.t('mobile.cancel') || 'Cancel';
|
||||
cancelBtn.className = 'cf-imagebed-cancel-btn';
|
||||
|
||||
// 相机拍照
|
||||
|
|
|
|||
307
src/utils/i18n.ts
Normal file
307
src/utils/i18n.ts
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
export type Language = 'zh' | 'en';
|
||||
|
||||
export interface Translations {
|
||||
[key: string]: string | Translations;
|
||||
}
|
||||
|
||||
const translations: Record<Language, Translations> = {
|
||||
zh: {
|
||||
settings: {
|
||||
title: 'CF ImageBed 设置',
|
||||
tabs: {
|
||||
basic: '基础设置',
|
||||
advanced: '高级设置',
|
||||
userExperience: '用户体验',
|
||||
backup: '备份设置'
|
||||
},
|
||||
basic: {
|
||||
apiUrl: {
|
||||
name: 'API URL',
|
||||
desc: 'CloudFlare ImgBed 的 API 地址(例如:https://your.domain)'
|
||||
},
|
||||
authCode: {
|
||||
name: '认证码',
|
||||
desc: '上传认证码'
|
||||
},
|
||||
uploadChannel: {
|
||||
name: '上传渠道',
|
||||
desc: '选择上传渠道'
|
||||
},
|
||||
uploadNameType: {
|
||||
name: '文件命名方式',
|
||||
desc: '选择文件命名方式'
|
||||
},
|
||||
returnFormat: {
|
||||
name: '返回链接格式',
|
||||
desc: '选择返回链接格式'
|
||||
},
|
||||
uploadFolder: {
|
||||
name: '上传目录',
|
||||
desc: '上传目录,用相对路径表示(例如:img/test)'
|
||||
},
|
||||
serverCompress: {
|
||||
name: '服务端压缩',
|
||||
desc: '启用服务端压缩(仅针对 Telegram 渠道的图片文件)'
|
||||
},
|
||||
autoRetry: {
|
||||
name: '自动重试',
|
||||
desc: '失败时自动切换渠道重试'
|
||||
}
|
||||
},
|
||||
advanced: {
|
||||
maxFileSize: {
|
||||
name: '最大文件大小',
|
||||
desc: '设置上传文件的最大大小(MB)'
|
||||
},
|
||||
allowedFileTypes: {
|
||||
name: '允许的文件类型',
|
||||
desc: '设置允许上传的文件类型(用逗号分隔)'
|
||||
},
|
||||
enableWatermark: {
|
||||
name: '启用水印',
|
||||
desc: '为上传的图片添加水印'
|
||||
},
|
||||
watermarkText: {
|
||||
name: '水印文字',
|
||||
desc: '设置水印文字内容'
|
||||
},
|
||||
watermarkPosition: {
|
||||
name: '水印位置',
|
||||
desc: '设置水印在图片中的位置'
|
||||
},
|
||||
watermarkSize: {
|
||||
name: '水印字体大小',
|
||||
desc: '设置水印文字的字体大小(像素)'
|
||||
},
|
||||
watermarkOpacity: {
|
||||
name: '水印透明度',
|
||||
desc: '设置水印的透明度(0-1)'
|
||||
},
|
||||
enableClientCompress: {
|
||||
name: '启用客户端压缩',
|
||||
desc: '在上传前自动压缩图片以减少文件大小'
|
||||
},
|
||||
compressThreshold: {
|
||||
name: '压缩阈值',
|
||||
desc: '设置图片大小阈值,超过此值将自动压缩(MB)'
|
||||
},
|
||||
targetSize: {
|
||||
name: '期望大小',
|
||||
desc: '设置压缩后图片大小期望值(MB)'
|
||||
}
|
||||
},
|
||||
userExperience: {
|
||||
showUploadProgress: {
|
||||
name: '显示上传提示',
|
||||
desc: '在上传过程中显示提示信息'
|
||||
},
|
||||
showSuccessNotification: {
|
||||
name: '显示成功通知',
|
||||
desc: '上传成功后显示通知消息'
|
||||
},
|
||||
showErrorNotification: {
|
||||
name: '显示错误通知',
|
||||
desc: '上传失败时显示错误消息'
|
||||
},
|
||||
notificationDuration: {
|
||||
name: '通知持续时间',
|
||||
desc: '设置通知消息显示的持续时间(秒)'
|
||||
}
|
||||
},
|
||||
backup: {
|
||||
enableLocalBackup: {
|
||||
name: '启用本地备份',
|
||||
desc: '在上传到云端的同时,在本地保存一份备份'
|
||||
},
|
||||
backupPath: {
|
||||
name: '备份路径',
|
||||
desc: '设置本地备份的存储路径(相对于库根目录)'
|
||||
}
|
||||
},
|
||||
language: {
|
||||
name: '语言设置',
|
||||
desc: '选择界面显示语言'
|
||||
}
|
||||
},
|
||||
menu: {
|
||||
uploadImage: '上传图片到 CF ImageBed'
|
||||
},
|
||||
mobile: {
|
||||
selectSource: '选择图片来源',
|
||||
takePhoto: '📷 拍照',
|
||||
selectFromGallery: '🖼️ 从相册选择',
|
||||
cancel: '取消'
|
||||
}
|
||||
},
|
||||
en: {
|
||||
settings: {
|
||||
title: 'CF ImageBed settings',
|
||||
tabs: {
|
||||
basic: 'Basic settings',
|
||||
advanced: 'Advanced settings',
|
||||
userExperience: 'User experience',
|
||||
backup: 'Backup settings'
|
||||
},
|
||||
basic: {
|
||||
apiUrl: {
|
||||
name: 'API URL',
|
||||
desc: 'CloudFlare ImgBed API address (e.g., https://your.domain)'
|
||||
},
|
||||
authCode: {
|
||||
name: 'Auth code',
|
||||
desc: 'Upload authentication code'
|
||||
},
|
||||
uploadChannel: {
|
||||
name: 'Upload channel',
|
||||
desc: 'Select upload channel'
|
||||
},
|
||||
uploadNameType: {
|
||||
name: 'File naming method',
|
||||
desc: 'Select file naming method'
|
||||
},
|
||||
returnFormat: {
|
||||
name: 'Return link format',
|
||||
desc: 'Select return link format'
|
||||
},
|
||||
uploadFolder: {
|
||||
name: 'Upload folder',
|
||||
desc: 'Upload folder, use relative path (e.g., img/test)'
|
||||
},
|
||||
serverCompress: {
|
||||
name: 'Server compression',
|
||||
desc: 'Enable server-side compression (only for Telegram channel image files)'
|
||||
},
|
||||
autoRetry: {
|
||||
name: 'Auto retry',
|
||||
desc: 'Automatically switch channels and retry on failure'
|
||||
}
|
||||
},
|
||||
advanced: {
|
||||
maxFileSize: {
|
||||
name: 'Maximum file size',
|
||||
desc: 'Set maximum size for uploaded files (MB)'
|
||||
},
|
||||
allowedFileTypes: {
|
||||
name: 'Allowed file types',
|
||||
desc: 'Set allowed file types for upload (comma-separated)'
|
||||
},
|
||||
enableWatermark: {
|
||||
name: 'Enable watermark',
|
||||
desc: 'Add watermark to uploaded images'
|
||||
},
|
||||
watermarkText: {
|
||||
name: 'Watermark text',
|
||||
desc: 'Set watermark text content'
|
||||
},
|
||||
watermarkPosition: {
|
||||
name: 'Watermark position',
|
||||
desc: 'Set watermark position in image'
|
||||
},
|
||||
watermarkSize: {
|
||||
name: 'Watermark font size',
|
||||
desc: 'Set watermark text font size (pixels)'
|
||||
},
|
||||
watermarkOpacity: {
|
||||
name: 'Watermark opacity',
|
||||
desc: 'Set watermark opacity (0-1)'
|
||||
},
|
||||
enableClientCompress: {
|
||||
name: 'Enable client compression',
|
||||
desc: 'Automatically compress images before upload to reduce file size'
|
||||
},
|
||||
compressThreshold: {
|
||||
name: 'Compression threshold',
|
||||
desc: 'Set image size threshold, files exceeding this will be automatically compressed (MB)'
|
||||
},
|
||||
targetSize: {
|
||||
name: 'Target size',
|
||||
desc: 'Set expected size for compressed images (MB)'
|
||||
}
|
||||
},
|
||||
userExperience: {
|
||||
showUploadProgress: {
|
||||
name: 'Show upload progress',
|
||||
desc: 'Show progress information during upload'
|
||||
},
|
||||
showSuccessNotification: {
|
||||
name: 'Show success notification',
|
||||
desc: 'Show notification message on successful upload'
|
||||
},
|
||||
showErrorNotification: {
|
||||
name: 'Show error notification',
|
||||
desc: 'Show error message when upload fails'
|
||||
},
|
||||
notificationDuration: {
|
||||
name: 'Notification duration',
|
||||
desc: 'Set duration for notification display (seconds)'
|
||||
}
|
||||
},
|
||||
backup: {
|
||||
enableLocalBackup: {
|
||||
name: 'Enable local backup',
|
||||
desc: 'Save a local backup while uploading to cloud'
|
||||
},
|
||||
backupPath: {
|
||||
name: 'Backup path',
|
||||
desc: 'Set local backup storage path (relative to vault root)'
|
||||
}
|
||||
},
|
||||
language: {
|
||||
name: 'Language',
|
||||
desc: 'Select interface display language'
|
||||
}
|
||||
},
|
||||
menu: {
|
||||
uploadImage: 'Upload image to CF ImageBed'
|
||||
},
|
||||
mobile: {
|
||||
selectSource: 'Select image source',
|
||||
takePhoto: '📷 Take photo',
|
||||
selectFromGallery: '🖼️ Select from gallery',
|
||||
cancel: 'Cancel'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export class I18n {
|
||||
private currentLanguage: Language;
|
||||
|
||||
constructor(language: Language = 'zh') {
|
||||
this.currentLanguage = language;
|
||||
}
|
||||
|
||||
setLanguage(language: Language): void {
|
||||
this.currentLanguage = language;
|
||||
}
|
||||
|
||||
getLanguage(): Language {
|
||||
return this.currentLanguage;
|
||||
}
|
||||
|
||||
t(key: string): string {
|
||||
const keys = key.split('.');
|
||||
let value: any = translations[this.currentLanguage];
|
||||
|
||||
for (const k of keys) {
|
||||
if (value && typeof value === 'object' && k in value) {
|
||||
value = value[k];
|
||||
} else {
|
||||
// Fallback to English if key not found
|
||||
value = translations.en;
|
||||
for (const k2 of keys) {
|
||||
if (value && typeof value === 'object' && k2 in value) {
|
||||
value = value[k2];
|
||||
} else {
|
||||
return key; // Return key if translation not found
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof value === 'string' ? value : key;
|
||||
}
|
||||
}
|
||||
|
||||
export const i18n = new I18n('zh');
|
||||
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
/*
|
||||
|
||||
This CSS file will be included with your plugin, and
|
||||
available in the app when your plugin is enabled.
|
||||
|
||||
If your plugin does not need CSS, delete this file.
|
||||
|
||||
*/
|
||||
|
||||
/* CF ImageBed 插件专用样式 - 限制作用域避免影响全局 */
|
||||
|
|
@ -280,4 +278,5 @@ If your plugin does not need CSS, delete this file.
|
|||
.cf-imagebed-cancel-btn:hover {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue