diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb958a..0d6ac91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # 📦 更新日志(CHANGELOG) +## [2.1.0] - 完整国际化支持与界面优化 (2025-05-26) + +### 🎉 新功能 +* **🌐 完整国际化支持**:添加英文语言支持,提升国际用户体验 +* **🔄 语言设置**:新增语言设置页面,可在设置中切换中英文界面 +* **📝 资源文件**:使用标准的资源文件实现国际化,便于后续扩展 +* **📋 菜单国际化**:完成右键菜单和所有操作的国际化支持 + +### 🔧 改进 +* **代码质量**:重构硬编码字符串,提高代码可维护性 +* **用户体验**:根据系统语言自动选择界面语言 +* **兼容性**:确保在所有平台上显示一致的界面文本 +* **全面覆盖**:所有用户界面元素现在都支持多语言显示 + +### 🐛 修复 +* **语言切换问题**:修复了语言设置保存后重启不生效的问题 +* **资源束缓存**:优化资源束加载机制,确保语言变化时正确重新加载 +* **菜单显示问题**:修复了顶部菜单显示国际化键名而非实际文本的问题 +* **SVG图标错误**:移除了错误的PNG文件(被误命名为SVG),解决UTF-8解析错误 +* **动态语言获取**:改进了语言设置的动态获取机制,提高稳定性 + ## [1.2.1] - 注释标记标签功能 (2025-05-26) ### 🎉 新功能 diff --git a/README.md b/README.md index a2dd516..bc31469 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,8 @@ - 📝 **代码注释集成**:通过注释标记添加图钉,支持标签和行号范围 - 🏷️ **标签系统**:使用标签组织和筛选图钉,提高工作效率 - 🔄 **数据管理**:自动同步,导入导出,持久化存储 -- 🌐 **团队协作**:导入/导出功能,便于团队共享 +- 🌐 **国际化支持**:完整的中英文界面,根据系统语言自动切换 +- 🤝 **团队协作**:导入/导出功能,便于团队共享 ## 🚀 快速开始 diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..82ca872 --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,479 @@ +# 🛠️ CodePins 开发指南 + +本文档为 CodePins 插件的开发提供详细指导,包括项目结构、开发规范、国际化、测试等内容。 + +## 📋 目录 + +- [项目概述](#项目概述) +- [开发环境](#开发环境) +- [项目结构](#项目结构) +- [开发规范](#开发规范) +- [国际化开发](#国际化开发) +- [UI 开发指南](#ui-开发指南) +- [测试指南](#测试指南) +- [构建和发布](#构建和发布) + +## 🎯 项目概述 + +CodePins 是一个现代化的 IntelliJ IDEA 代码书签插件,使用以下技术栈: + +- **语言**: Java 17+ +- **UI 框架**: Swing (传统组件) +- **构建工具**: Gradle + IntelliJ Platform Plugin +- **IDE 平台**: IntelliJ Platform SDK +- **国际化**: Java ResourceBundle + +## 🔧 开发环境 + +### 必需工具 +- **JDK**: 17 或更高版本 +- **IDE**: IntelliJ IDEA (推荐 Ultimate 版本) +- **Gradle**: 8.0+ (通过 Gradle Wrapper) + +### 环境配置 +1. 克隆项目: + ```bash + git clone https://github.com/08820048/codepins.git + cd codepins + ``` + +2. 导入到 IntelliJ IDEA: + - File → Open → 选择项目根目录 + - 等待 Gradle 同步完成 + +3. 配置 Plugin SDK: + - File → Project Structure → SDKs + - 添加 IntelliJ Platform Plugin SDK + +## 📁 项目结构 + +``` +CodePins/ +├── src/main/ +│ ├── java/cn/ilikexff/codepins/ +│ │ ├── core/ # 核心功能 +│ │ │ ├── PinStateService.java # 图钉状态管理 +│ │ │ └── Pin.java # 图钉数据模型 +│ │ ├── ui/ # 用户界面 +│ │ │ ├── PinsToolWindow.java # 主工具窗口 +│ │ │ ├── dialogs/ # 对话框组件 +│ │ │ └── panels/ # 面板组件 +│ │ ├── actions/ # IDE 动作 +│ │ │ ├── AddPinAction.java # 添加图钉动作 +│ │ │ └── NavigateAction.java # 导航动作 +│ │ ├── settings/ # 设置管理 +│ │ │ ├── CodePinsSettings.java +│ │ │ └── LanguageSettings.java +│ │ ├── i18n/ # 国际化 +│ │ │ └── CodePinsBundle.java # 国际化工具类 +│ │ ├── services/ # 服务类 +│ │ └── utils/ # 工具类 +│ └── resources/ +│ ├── META-INF/ +│ │ └── plugin.xml # 插件配置 +│ ├── messages/ # 国际化资源 +│ │ ├── CodePinsBundle.properties +│ │ ├── CodePinsBundle_en.properties +│ │ └── CodePinsBundle_zh_CN.properties +│ └── icons/ # 图标资源 +├── src/test/ # 测试代码 +├── docs/ # 文档 +├── build.gradle.kts # 构建配置 +└── README.md +``` + +## 📝 开发规范 + +### 代码风格 +- 使用 4 空格缩进 +- 类名使用 PascalCase +- 方法和变量使用 camelCase +- 常量使用 UPPER_SNAKE_CASE +- 包名使用小写字母 + +### 注释规范 +```java +/** + * 图钉数据模型类 + * + * @author CodePins Team + * @since 1.0.0 + */ +public class Pin { + /** + * 图钉的唯一标识符 + */ + private String id; + + /** + * 创建新的图钉实例 + * + * @param filePath 文件路径 + * @param lineNumber 行号 + * @param note 备注信息 + * @return 新创建的图钉实例 + */ + public static Pin create(String filePath, int lineNumber, String note) { + // 实现代码 + } +} +``` + +### Git 提交规范 +使用 Conventional Commits 格式: + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +类型说明: +- `feat`: 新功能 +- `fix`: 修复 bug +- `docs`: 文档更新 +- `style`: 代码格式调整 +- `refactor`: 代码重构 +- `test`: 测试相关 +- `chore`: 构建过程或辅助工具的变动 + +示例: +``` +feat(i18n): 添加完整的中英文国际化支持 + +- 实现动态语言切换功能 +- 添加语言设置页面 +- 修复资源束缓存问题 + +Closes #123 +``` + +## 🌐 国际化开发 + +### 概述 +CodePins 使用 Java ResourceBundle 实现国际化,目前支持中文和英文。 + +### 资源文件结构 +``` +src/main/resources/messages/ +├── CodePinsBundle.properties # 默认资源(英文) +├── CodePinsBundle_en.properties # 英文资源 +└── CodePinsBundle_zh_CN.properties # 中文资源 +``` + +### 添加新的国际化文本 + +#### 1. 定义资源键 +使用层次化的键名结构: + +```properties +# 按功能分组 +button.add=Add +button.edit=Edit +button.delete=Delete + +# 对话框 +dialog.confirm.title=Confirmation +dialog.confirm.delete=Are you sure you want to delete this pin? + +# 通知 +notification.success.title=Success +notification.error.title=Error + +# 设置 +settings.general.title=General Settings +settings.language.title=Language +``` + +#### 2. 添加到所有语言文件 +确保在所有支持的语言文件中添加相同的键: + +**CodePinsBundle.properties (英文)**: +```properties +pin.add.success=Pin added successfully +pin.delete.confirm=Are you sure you want to delete this pin? +``` + +**CodePinsBundle_zh_CN.properties (中文)**: +```properties +pin.add.success=图钉添加成功 +pin.delete.confirm=您确定要删除这个图钉吗? +``` + +#### 3. 在代码中使用 +```java +// 简单文本 +String message = CodePinsBundle.message("pin.add.success"); + +// 带参数的文本 +String message = CodePinsBundle.message("pins.count", count); +``` + +### 参数化文本 +对于包含动态内容的文本,使用 MessageFormat 参数: + +```properties +# 资源文件 +notification.pins.exported=Successfully exported {0} pins to {1} +file.info=File: {0}, Size: {1} KB + +# 中文 +notification.pins.exported=成功导出 {0} 个图钉到 {1} +file.info=文件:{0},大小:{1} KB +``` + +```java +// Java 代码 +String message = CodePinsBundle.message("notification.pins.exported", count, filePath); +``` + +### 国际化最佳实践 + +1. **避免硬编码文本**: + ```java + // ❌ 错误 + JButton button = new JButton("Add Pin"); + + // ✅ 正确 + JButton button = new JButton(CodePinsBundle.message("button.add.pin")); + ``` + +2. **键名命名规范**: + - 使用描述性的键名 + - 按功能模块分组 + - 保持一致的命名风格 + +3. **文本编写原则**: + - 简洁明了 + - 用户友好 + - 保持一致性 + +## 🎨 UI 开发指南 + +### UI 框架选择 +CodePins 使用传统的 Swing 组件而非 IntelliJ Platform UI DSL,原因: +- 更好的兼容性 +- 更灵活的布局控制 +- 团队熟悉度高 + +### 主题适配 +确保 UI 组件在亮色和暗色主题下都能正常显示: + +```java +public class ThemedPanel extends JPanel { + public ThemedPanel() { + // 使用 IDE 主题颜色 + setBackground(UIUtil.getPanelBackground()); + setForeground(UIUtil.getLabelForeground()); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + // 自定义绘制逻辑 + } +} +``` + +### 响应式设计 +```java +public class ResponsiveDialog extends JDialog { + private void setupLayout() { + setLayout(new BorderLayout()); + + // 使用合适的布局管理器 + JPanel contentPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + + // 设置组件约束 + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1.0; + + add(contentPanel, BorderLayout.CENTER); + } +} +``` + +### 图标使用 +```java +// 加载 SVG 图标 +Icon icon = IconUtil.loadIcon("/icons/pin-add.svg"); +JButton button = new JButton(icon); + +// 支持主题切换的图标 +Icon themedIcon = IconUtil.getThemedIcon("/icons/pin.svg"); +``` + +## 🧪 测试指南 + +### 单元测试 +```java +public class PinTest { + @Test + public void testPinCreation() { + Pin pin = Pin.create("/path/to/file.java", 10, "Test note"); + + assertNotNull(pin.getId()); + assertEquals("/path/to/file.java", pin.getFilePath()); + assertEquals(10, pin.getLineNumber()); + assertEquals("Test note", pin.getNote()); + } +} +``` + +### 国际化测试 +```java +public class I18nTest { + @Test + public void testMessageRetrieval() { + String message = CodePinsBundle.message("button.add"); + assertNotNull(message); + assertFalse(message.isEmpty()); + } + + @Test + public void testParameterizedMessage() { + String message = CodePinsBundle.message("pins.count", 5); + assertTrue(message.contains("5")); + } +} +``` + +### UI 测试 +```java +public class UITest { + @Test + public void testDialogCreation() { + SwingUtilities.invokeAndWait(() -> { + AddPinDialog dialog = new AddPinDialog(); + assertNotNull(dialog); + assertTrue(dialog.isDisplayable()); + }); + } +} +``` + +## 🚀 构建和发布 + +### 本地构建 +```bash +# 编译项目 +./gradlew build + +# 运行测试 +./gradlew test + +# 构建插件 +./gradlew buildPlugin +``` + +### 调试运行 +```bash +# 启动带插件的 IDE 实例 +./gradlew runIde +``` + +### 发布准备 +1. 更新版本号(`build.gradle.kts`) +2. 更新 `CHANGELOG.md` +3. 确保所有测试通过 +4. 构建最终插件包 + +### 版本管理 +- 使用语义化版本号:`MAJOR.MINOR.PATCH` +- 在 `plugin.xml` 中更新版本信息 +- 创建 Git 标签:`git tag v1.2.0` + +## 📞 获取帮助 + +如果在开发过程中遇到问题: + +1. **查看文档**:首先查阅本开发指南和 IntelliJ Platform SDK 文档 +2. **代码参考**:查看项目中现有的实现 +3. **提交 Issue**:在 GitHub 上提交详细的问题描述 +4. **参与讨论**:在项目讨论区与其他开发者交流 + +## 🤝 贡献流程 + +1. Fork 项目 +2. 创建功能分支:`git checkout -b feature/new-feature` +3. 提交更改:`git commit -m 'feat: add new feature'` +4. 推送分支:`git push origin feature/new-feature` +5. 创建 Pull Request + +## 🔧 常用开发命令 + +### Gradle 命令 +```bash +# 清理构建 +./gradlew clean + +# 编译代码 +./gradlew compileJava + +# 运行所有测试 +./gradlew test + +# 检查代码质量 +./gradlew check + +# 构建插件包 +./gradlew buildPlugin + +# 发布到本地仓库 +./gradlew publishToMavenLocal +``` + +### Git 工作流 +```bash +# 创建新功能分支 +git checkout -b feature/i18n-support + +# 查看状态 +git status + +# 添加文件 +git add . + +# 提交更改 +git commit -m "feat(i18n): add internationalization support" + +# 推送到远程 +git push origin feature/i18n-support +``` + +## 📚 参考资源 + +### 官方文档 +- [IntelliJ Platform SDK](https://plugins.jetbrains.com/docs/intellij/) +- [Swing Tutorial](https://docs.oracle.com/javase/tutorial/uiswing/) +- [Java ResourceBundle](https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html) + +### 项目相关 +- [项目 GitHub](https://github.com/08820048/codepins) +- [问题追踪](https://github.com/08820048/codepins/issues) +- [讨论区](https://github.com/08820048/codepins/discussions) + +## 🐛 常见问题解决 + +### 编译问题 +**问题**: `Cannot resolve symbol 'ApplicationManager'` +**解决**: 确保正确导入 IntelliJ Platform 依赖 + +**问题**: 国际化文本显示为键名 +**解决**: 检查资源文件路径和键名是否正确 + +### 运行时问题 +**问题**: 插件加载失败 +**解决**: 检查 `plugin.xml` 配置和类路径 + +**问题**: UI 组件在暗色主题下显示异常 +**解决**: 使用 `UIUtil` 类获取主题相关的颜色 + +--- + +**记住**:良好的代码不仅要功能正确,还要易于理解和维护!💻✨ + +**Happy Coding!** 🎉 diff --git a/src/main/java/cn/ilikexff/codepins/I18n.java b/src/main/java/cn/ilikexff/codepins/I18n.java index 1abc3c9..aa131b3 100644 --- a/src/main/java/cn/ilikexff/codepins/I18n.java +++ b/src/main/java/cn/ilikexff/codepins/I18n.java @@ -1,38 +1,111 @@ package cn.ilikexff.codepins; +import cn.ilikexff.codepins.i18n.CodePinsBundle; +import cn.ilikexff.codepins.settings.LanguageSettings; +import com.intellij.openapi.application.ApplicationManager; + +import java.text.MessageFormat; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; /** - * 国际化工具类:自动加载 messages/CodePinsBundle,根据系统 locale 显示。 - * 支持 fallback,当找不到 key 时返回默认英文值。 + * Internationalization utility class: automatically loads messages/CodePinsBundle based on selected language. + * Supports fallback to default English values when a key is not found. */ public class I18n { private static final String BUNDLE_NAME = "messages.CodePinsBundle"; - private static final ResourceBundle BUNDLE; + private static ResourceBundle BUNDLE; - static { - ResourceBundle temp; - try { - temp = ResourceBundle.getBundle(BUNDLE_NAME, Locale.getDefault()); - } catch (MissingResourceException e) { - temp = null; + /** + * Get the current resource bundle based on user's language settings + * @return The resource bundle for the current language + */ + private static ResourceBundle getBundle() { + if (BUNDLE == null) { + try { + // Try to get language settings from application service + LanguageSettings settings = null; + try { + if (ApplicationManager.getApplication() != null) { + settings = LanguageSettings.getInstance(); + } + } catch (Exception e) { + // Ignore, might be in unit tests or during startup + } + + // Get locale from settings or use system default + Locale locale = settings != null ? settings.getSelectedLocale() : Locale.getDefault(); + BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME, locale); + } catch (MissingResourceException e) { + // Fallback to default locale if bundle not found + try { + BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME, Locale.ENGLISH); + } catch (MissingResourceException ex) { + // If still not found, return null (will use default values) + BUNDLE = null; + } + } } - BUNDLE = temp; + return BUNDLE; + } + + /** + * Reset the cached bundle to force reload on next access + * Call this when language settings change + */ + public static void resetBundle() { + BUNDLE = null; } /** - * 获取国际化文本(带默认 fallback 英文) + * Get localized text with fallback to default English value * - * @param key 资源 key - * @param defaultValue 找不到时默认值 - * @return 本地化字符串 + * @param key Resource key + * @param defaultValue Default value if key not found + * @return Localized string */ public static String get(String key, String defaultValue) { - if (BUNDLE != null && BUNDLE.containsKey(key)) { - return BUNDLE.getString(key); + ResourceBundle bundle = getBundle(); + if (bundle != null && bundle.containsKey(key)) { + try { + return bundle.getString(key); + } catch (MissingResourceException e) { + return defaultValue; + } } return defaultValue; } + + /** + * Get localized text with parameters + * + * @param key Resource key + * @param defaultValue Default value if key not found + * @param params Parameters for message formatting + * @return Formatted localized string + */ + public static String get(String key, String defaultValue, Object... params) { + String pattern = get(key, defaultValue); + if (params.length == 0) { + return pattern; + } + try { + return MessageFormat.format(pattern, params); + } catch (Exception e) { + return pattern; + } + } + + /** + * Get localized text using CodePinsBundle + * This is a convenience method that delegates to CodePinsBundle.message + * + * @param key Resource key + * @param params Parameters for message formatting + * @return Localized string + */ + public static String message(String key, Object... params) { + return CodePinsBundle.message(key, params); + } } \ No newline at end of file diff --git a/src/main/java/cn/ilikexff/codepins/PinAction.java b/src/main/java/cn/ilikexff/codepins/PinAction.java index 942b9f4..0d0b960 100644 --- a/src/main/java/cn/ilikexff/codepins/PinAction.java +++ b/src/main/java/cn/ilikexff/codepins/PinAction.java @@ -2,6 +2,7 @@ import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.ui.SimpleTagEditorDialog; import com.intellij.openapi.actionSystem.AnAction; @@ -21,21 +22,20 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; -import org.jetbrains.annotations.NotNull; +// Removed unused import: org.jetbrains.annotations.NotNull import java.util.ArrayList; import java.util.List; -import java.util.Map; /** - * 动作:在当前行或选区添加一个图钉,并可附加备注。 + * Action: Add a pin to the current line or selection with optional note. */ public class PinAction extends AnAction { public PinAction() { - // 注意:图标在plugin.xml中设置,这里不需要设置 - // 使用空构造函数,避免覆盖plugin.xml中的设置 - System.out.println("[CodePins] PinAction registered"); // 插件加载时输出 + // Note: Icon is set in plugin.xml, no need to set it here + // Use empty constructor to avoid overriding plugin.xml settings + System.out.println("[CodePins] PinAction registered"); // Output when plugin loads } @Override @@ -52,8 +52,8 @@ public void actionPerformed(AnActionEvent e) { String note = Messages.showInputDialog( project, - "请输入图钉备注(可选):", - "添加图钉", + CodePinsBundle.message("note.placeholder"), + CodePinsBundle.message("pin.add"), Messages.getQuestionIcon() ); @@ -86,13 +86,13 @@ public void actionPerformed(AnActionEvent e) { boolean isBlock = caret.hasSelection(); - // 记录调试信息 + // Log debug information if (isBlock) { int startLine = document.getLineNumber(caret.getSelectionStart()) + 1; int endLine = document.getLineNumber(caret.getSelectionEnd()) + 1; - System.out.println("[CodePins] 创建代码块图钉,行范围: " + startLine + "-" + endLine); + System.out.println("[CodePins] Creating block pin, line range: " + startLine + "-" + endLine); } else { - System.out.println("[CodePins] 创建单行图钉,行号: " + (document.getLineNumber(caret.getOffset()) + 1)); + System.out.println("[CodePins] Creating single line pin, line: " + (document.getLineNumber(caret.getOffset()) + 1)); } TextRange range = isBlock @@ -115,53 +115,37 @@ public void actionPerformed(AnActionEvent e) { boolean success = PinStorage.addPin(pin); - // 状态栏和通知提示 + // Status bar and notification tips StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); if (success) { - // 添加成功 + // Add success if (statusBar != null) { - StatusBar.Info.set("✅ 图钉已添加", project); + StatusBar.Info.set("✅ " + CodePinsBundle.message("pin.added", document.getLineNumber(caret.getOffset()) + 1), project); } Notifications.Bus.notify(new Notification( "CodePins", - "图钉添加成功", - isBlock ? "已添加一段代码块图钉" : "已添加单行图钉", + CodePinsBundle.message("notification.success"), + isBlock ? CodePinsBundle.message("pin.type.block") + " " + CodePinsBundle.message("pin.added", document.getLineNumber(caret.getOffset()) + 1) : + CodePinsBundle.message("pin.type.single") + " " + CodePinsBundle.message("pin.added", document.getLineNumber(caret.getOffset()) + 1), NotificationType.INFORMATION ), project); } else { - // 添加失败 + // Add failure if (statusBar != null) { - StatusBar.Info.set("❌ 图钉添加失败", project); + StatusBar.Info.set("❌ " + CodePinsBundle.message("pin.invalid"), project); } - // 获取图钉数量信息 - Map pinsInfo = PinStorage.getPinsCountInfo(); - int currentPins = pinsInfo.get("current"); - int maxPins = pinsInfo.get("max"); + // Plugin is now completely free, this should not show any limitation errors + String failureReason = CodePinsBundle.message("notification.error"); - // 获取标签数量信息 - Map tagsInfo = PinStorage.getTagsCountInfo(); - int currentTagTypes = tagsInfo.get("current"); - int maxTagTypes = tagsInfo.get("max"); - int maxTagsPerPin = tagsInfo.get("perPin"); - - // 确定失败原因 - String failureReason; - String featureName; - - // 插件现在完全免费,这里不应该出现限制错误 - failureReason = "添加图钉失败,请稍后重试"; - - // 创建通知 + // Create notification Notification notification = new Notification( "CodePins", - "图钉添加失败", + CodePinsBundle.message("notification.error"), failureReason, NotificationType.WARNING ); - // 插件现在完全免费,移除升级按钮 - Notifications.Bus.notify(notification, project); } } diff --git a/src/main/java/cn/ilikexff/codepins/PinHoverPreview.java b/src/main/java/cn/ilikexff/codepins/PinHoverPreview.java index bce4e74..eb3aeb5 100644 --- a/src/main/java/cn/ilikexff/codepins/PinHoverPreview.java +++ b/src/main/java/cn/ilikexff/codepins/PinHoverPreview.java @@ -1,6 +1,7 @@ package cn.ilikexff.codepins; import cn.ilikexff.codepins.core.PinEntry; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; @@ -210,7 +211,7 @@ private static JPanel createPreviewPanel(PinEntry pin) { titlePanel.setBorder(JBUI.Borders.emptyBottom(10)); // 创建标题标签 - JLabel titleLabel = new JLabel(pin.isBlock ? "📌 代码块图钉" : "📌 单行图钉"); + JLabel titleLabel = new JLabel(pin.isBlock ? CodePinsBundle.message("tooltip.blockPin") : CodePinsBundle.message("tooltip.linePin")); titleLabel.setForeground(new JBColor(new Color(255, 255, 255), new Color(255, 255, 255))); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 14.0f)); titlePanel.add(titleLabel); @@ -225,19 +226,19 @@ private static JPanel createPreviewPanel(PinEntry pin) { panel.add(Box.createVerticalStrut(10)); // 分隔线后的空间 // 添加文件路径 - addInfoRow(panel, "路径", pin.filePath, new JBColor(new Color(255, 203, 107), new Color(255, 203, 107))); + addInfoRow(panel, CodePinsBundle.message("tooltip.path"), pin.filePath, new JBColor(new Color(255, 203, 107), new Color(255, 203, 107))); // 添加行号 - addInfoRow(panel, "行号", String.valueOf(line + 1), new JBColor(new Color(247, 140, 108), new Color(247, 140, 108))); + addInfoRow(panel, CodePinsBundle.message("tooltip.line"), String.valueOf(line + 1), new JBColor(new Color(247, 140, 108), new Color(247, 140, 108))); // 添加备注 - addInfoRow(panel, "备注", note, new JBColor(new Color(64, 191, 255), new Color(64, 191, 255))); + addInfoRow(panel, CodePinsBundle.message("tooltip.note"), note, new JBColor(new Color(64, 191, 255), new Color(64, 191, 255))); // 添加创建时间 - addInfoRow(panel, "创建于", time, new JBColor(new Color(130, 170, 255), new Color(130, 170, 255))); + addInfoRow(panel, CodePinsBundle.message("tooltip.createdAt"), time, new JBColor(new Color(130, 170, 255), new Color(130, 170, 255))); // 添加作者 - addInfoRow(panel, "作者", author, new JBColor(new Color(199, 146, 234), new Color(199, 146, 234))); + addInfoRow(panel, CodePinsBundle.message("tooltip.author"), author, new JBColor(new Color(199, 146, 234), new Color(199, 146, 234))); return panel; } catch (Exception e) { diff --git a/src/main/java/cn/ilikexff/codepins/PinsToolWindow.java b/src/main/java/cn/ilikexff/codepins/PinsToolWindow.java index 5f5a000..558fa99 100644 --- a/src/main/java/cn/ilikexff/codepins/PinsToolWindow.java +++ b/src/main/java/cn/ilikexff/codepins/PinsToolWindow.java @@ -1,5 +1,6 @@ package cn.ilikexff.codepins; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; import cn.ilikexff.codepins.core.PinState; @@ -48,7 +49,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; public class PinsToolWindow implements ToolWindowFactory { @@ -159,7 +159,6 @@ public void mouseMoved(MouseEvent e) { renderer.setHoverIndex(index); // 获取单元格组件 - @SuppressWarnings("unchecked") // 添加注解来抑制警告 Component cellComponent = list.getCellRenderer().getListCellRendererComponent( list, entry, index, false, false); // 这里的调用是安全的 @@ -227,7 +226,6 @@ private void showPopup(MouseEvent e) { Icon codeIcon = IconUtil.loadIcon("/icons/view.svg", getClass()); Icon editIcon = IconUtil.loadIcon("/icons/edit.svg", getClass()); Icon tagIcon = IconUtil.loadIcon("/icons/tag.svg", getClass()); - Icon shareIcon = IconUtil.loadIcon("/icons/share.svg", getClass()); Icon shareIconMini = IconUtil.loadIcon("/icons/share-mini.svg", getClass()); Icon deleteIcon = IconUtil.loadIcon("/icons/trash.svg", getClass()); Icon refreshIcon = IconUtil.loadIcon("/icons/refresh.svg", getClass()); @@ -235,7 +233,7 @@ private void showPopup(MouseEvent e) { // 根据图钉类型添加不同的菜单项 if (selected.isBlock) { // 如果是代码块图钉,添加代码预览项 - JMenuItem codeItem = new JMenuItem("查看代码块", codeIcon); + JMenuItem codeItem = new JMenuItem(CodePinsBundle.message("context.view.code"), codeIcon); // 应用自定义UI cn.ilikexff.codepins.ui.CustomMenuItemUI.apply(codeItem); codeItem.addActionListener(event -> { @@ -247,13 +245,13 @@ private void showPopup(MouseEvent e) { } // 添加编辑备注项 - JMenuItem editItem = new JMenuItem("修改备注", editIcon); + JMenuItem editItem = new JMenuItem(CodePinsBundle.message("context.edit.note"), editIcon); // 应用自定义UI cn.ilikexff.codepins.ui.CustomMenuItemUI.apply(editItem); editItem.addActionListener(event -> { // 添加按钮动画效果 AnimationUtil.buttonClickEffect(editItem); - String newNote = JOptionPane.showInputDialog(null, "请输入新的备注:", selected.note); + String newNote = JOptionPane.showInputDialog(null, CodePinsBundle.message("note.placeholder"), selected.note); if (newNote != null) { PinStorage.updateNote(selected, newNote.trim()); } @@ -261,7 +259,7 @@ private void showPopup(MouseEvent e) { menu.add(editItem); // 添加编辑标签项 - JMenuItem tagItem = new JMenuItem("编辑标签", tagIcon); + JMenuItem tagItem = new JMenuItem(CodePinsBundle.message("context.edit.tags"), tagIcon); // 应用自定义UI cn.ilikexff.codepins.ui.CustomMenuItemUI.apply(tagItem); tagItem.addActionListener(event -> { @@ -277,7 +275,7 @@ private void showPopup(MouseEvent e) { // 添加复制图钉项 Icon copyIcon = IconUtil.loadIcon("/icons/copy.svg", getClass()); - JMenuItem copyItem = new JMenuItem("复制图钉", copyIcon); + JMenuItem copyItem = new JMenuItem(CodePinsBundle.message("context.copy.pin"), copyIcon); // 应用自定义UI cn.ilikexff.codepins.ui.CustomMenuItemUI.apply(copyItem); copyItem.addActionListener(event -> { @@ -290,7 +288,7 @@ private void showPopup(MouseEvent e) { menu.add(copyItem); // 添加分享项 - JMenuItem shareItem = new JMenuItem("分享图钉", shareIconMini); + JMenuItem shareItem = new JMenuItem(CodePinsBundle.message("context.share.pin"), shareIconMini); // 应用自定义UI cn.ilikexff.codepins.ui.CustomMenuItemUI.apply(shareItem); shareItem.addActionListener(event -> { @@ -306,7 +304,7 @@ private void showPopup(MouseEvent e) { menu.add(shareItem); // 添加删除项 - JMenuItem deleteItem = new JMenuItem("删除本钉", deleteIcon); + JMenuItem deleteItem = new JMenuItem(CodePinsBundle.message("context.delete.pin"), deleteIcon); // 应用自定义UI cn.ilikexff.codepins.ui.CustomMenuItemUI.apply(deleteItem); deleteItem.addActionListener(event -> { @@ -337,7 +335,7 @@ private void showPopup(MouseEvent e) { menu.add(deleteItem); // 添加刷新项 - JMenuItem refreshItem = new JMenuItem("刷新", refreshIcon); + JMenuItem refreshItem = new JMenuItem(CodePinsBundle.message("tag.refresh"), refreshIcon); // 应用自定义UI cn.ilikexff.codepins.ui.CustomMenuItemUI.apply(refreshItem); refreshItem.addActionListener(event -> { @@ -464,7 +462,7 @@ public void contentsChanged(javax.swing.event.ListDataEvent e) { */ private JComponent createSearchField() { // 创建现代化搜索框 - this.searchField = new SearchTextField("搜索图钉(支持备注与路径)"); + this.searchField = new SearchTextField(CodePinsBundle.message("tooltip.searchPlaceholder")); // 创建容器面板,添加边距,减少上下边距以便更好地垂直居中 JPanel container = new JPanel(new BorderLayout()); @@ -516,11 +514,6 @@ private void updateContentView(CardLayout cardLayout, JPanel contentPanel) { */ private void updatePinCountLabel() { if (pinCountLabel != null) { - // 获取最新的图钉数量信息 - Map countInfo = PinStorage.getPinsCountInfo(); - int currentCount = countInfo.get("current"); - int maxCount = countInfo.get("max"); - // 创建新的图钉计数标签 JComponent newCountLabel = createPinCountLabel(); @@ -583,9 +576,9 @@ private void setupDragAndDrop() { private int dragIndex = -1; @Override - @SuppressWarnings("unchecked") // 添加注解来抑制警告 protected Transferable createTransferable(JComponent c) { - JList list = (JList) c; // 这里的转换是安全的,因为我们知道c是一个 JList + @SuppressWarnings("unchecked") // 这里的注解是必要的,因为我们知道c是一个 JList + JList list = (JList) c; dragIndex = list.getSelectedIndex(); // 创建一个简单的 Transferable 对象,包含拖动的索引 @@ -642,8 +635,7 @@ public boolean importData(TransferSupport support) { int fromIndex = Integer.parseInt(dragIndexStr); // 获取放置的位置 - @SuppressWarnings("unchecked") // 添加注解来抑制警告 - JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); // 这里的转换是安全的 + JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); int toIndex = dl.getIndex(); // 如果放置位置在拖动索引之后,需要调整 @@ -729,7 +721,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { // 分享按钮 Icon shareIcon = IconUtil.loadIcon("/icons/share.svg", getClass()); - group.add(new AnAction("分享图钉", "分享选中的图钉", shareIcon) { + group.add(new AnAction(CodePinsBundle.message("toolbar.share"), CodePinsBundle.message("toolbar.share.desc"), shareIcon) { @Override public void actionPerformed(@NotNull AnActionEvent e) { List selectedPins = list.getSelectedValuesList(); @@ -737,8 +729,8 @@ public void actionPerformed(@NotNull AnActionEvent e) { // 如果没有选中的图钉,提示用户 Messages.showInfoMessage( project, - "请先选择要分享的图钉", - "分享图钉" + CodePinsBundle.message("toolbar.select.prompt"), + CodePinsBundle.message("toolbar.share") ); return; } @@ -757,22 +749,22 @@ public void update(@NotNull AnActionEvent e) { // 批量删除按钮 Icon deleteMultipleIcon = IconUtil.loadIcon("/icons/delete-multiple.svg", getClass()); - group.add(new AnAction("批量删除", "删除选中的图钉", deleteMultipleIcon) { + group.add(new AnAction(CodePinsBundle.message("toolbar.delete.multiple"), CodePinsBundle.message("toolbar.delete.multiple.desc"), deleteMultipleIcon) { @Override public void actionPerformed(@NotNull AnActionEvent e) { List selectedPins = list.getSelectedValuesList(); if (selectedPins.isEmpty()) { Messages.showInfoMessage( project, - "请先选择要删除的图钉", - "批量删除" + CodePinsBundle.message("toolbar.select.prompt"), + CodePinsBundle.message("toolbar.delete.multiple") ); return; } int confirm = JOptionPane.showConfirmDialog(null, - "确定要删除选中的 " + selectedPins.size() + " 个图钉吗?", - "确认删除", JOptionPane.YES_NO_OPTION); + CodePinsBundle.message("toolbar.confirm.delete", selectedPins.size()), + CodePinsBundle.message("toolbar.confirm.delete.title"), JOptionPane.YES_NO_OPTION); if (confirm == JOptionPane.YES_OPTION) { deleteSelectedPins(selectedPins); @@ -788,34 +780,34 @@ public void update(@NotNull AnActionEvent e) { // 排序按钮 Icon sortIcon = IconUtil.loadIcon("/icons/sort.svg", getClass()); - DefaultActionGroup sortGroup = new DefaultActionGroup("排序", true); + DefaultActionGroup sortGroup = new DefaultActionGroup(CodePinsBundle.message("toolbar.sort"), true); sortGroup.getTemplatePresentation().setIcon(sortIcon); - sortGroup.getTemplatePresentation().setText("排序图钉"); - sortGroup.getTemplatePresentation().setDescription("按不同条件排序图钉"); + sortGroup.getTemplatePresentation().setText(CodePinsBundle.message("toolbar.sort")); + sortGroup.getTemplatePresentation().setDescription(CodePinsBundle.message("toolbar.sort")); // 添加排序选项 - sortGroup.add(new AnAction("按创建时间(新→旧)") { + sortGroup.add(new AnAction(CodePinsBundle.message("toolbar.sort.time.desc")) { @Override public void actionPerformed(@NotNull AnActionEvent e) { sortPins(SortType.TIME_DESC); } }); - sortGroup.add(new AnAction("按创建时间(旧→新)") { + sortGroup.add(new AnAction(CodePinsBundle.message("toolbar.sort.time.asc")) { @Override public void actionPerformed(@NotNull AnActionEvent e) { sortPins(SortType.TIME_ASC); } }); - sortGroup.add(new AnAction("按文件名") { + sortGroup.add(new AnAction(CodePinsBundle.message("toolbar.sort.name.asc")) { @Override public void actionPerformed(@NotNull AnActionEvent e) { sortPins(SortType.FILENAME); } }); - sortGroup.add(new AnAction("按备注") { + sortGroup.add(new AnAction(CodePinsBundle.message("toolbar.sort.path.asc")) { @Override public void actionPerformed(@NotNull AnActionEvent e) { sortPins(SortType.NOTE); @@ -826,11 +818,11 @@ public void actionPerformed(@NotNull AnActionEvent e) { // 清空按钮 Icon clearIcon = IconUtil.loadIcon("/icons/x-octagon.svg", getClass()); - group.add(new AnAction("清空图钉", "清除所有图钉记录", clearIcon) { + group.add(new AnAction(CodePinsBundle.message("button.clear"), CodePinsBundle.message("button.clear"), clearIcon) { @Override public void actionPerformed(@NotNull AnActionEvent e) { int confirm = JOptionPane.showConfirmDialog(null, - "确定要清空所有图钉吗?", "确认清空", JOptionPane.YES_NO_OPTION); + CodePinsBundle.message("dialog.confirm.clear"), CodePinsBundle.message("dialog.confirm.title"), JOptionPane.YES_NO_OPTION); if (confirm == JOptionPane.YES_OPTION) { PinStorage.clearAll(); allPins = PinStorage.getPins(); @@ -864,7 +856,7 @@ private JComponent createPinCountLabel() { Color successColor = JBColor.namedColor("Plugins.tagForeground", new JBColor(new Color(0x008000), new Color(0x369E6A))); // 设置标签文本和样式 - 插件现在完全免费开源 - countLabel.setText("当前钉数: " + currentCount); + countLabel.setText(CodePinsBundle.message("toolbar.pin.count", currentCount)); countLabel.setForeground(successColor); countLabel.setIcon(IconUtil.loadIcon("/icons/pin-small.svg", getClass())); // 设置字体和边距,使用更紧凑的边距 @@ -1112,8 +1104,8 @@ private void copyPin(PinEntry original) { Messages.showInfoMessage( project, - "图钉复制成功!", - "复制成功" + CodePinsBundle.message("notification.copy.success"), + CodePinsBundle.message("notification.copy.success.title") ); } else { Messages.showErrorDialog( diff --git a/src/main/java/cn/ilikexff/codepins/actions/AddPinAction.java b/src/main/java/cn/ilikexff/codepins/actions/AddPinAction.java index c8a0ae6..916a5a1 100644 --- a/src/main/java/cn/ilikexff/codepins/actions/AddPinAction.java +++ b/src/main/java/cn/ilikexff/codepins/actions/AddPinAction.java @@ -1,9 +1,8 @@ package cn.ilikexff.codepins.actions; -import cn.ilikexff.codepins.PinAction; import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; -import cn.ilikexff.codepins.services.LicenseService; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.ui.SimpleTagEditorDialog; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; @@ -23,7 +22,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * 添加图钉的快捷键 Action @@ -59,8 +57,8 @@ public void actionPerformed(@NotNull AnActionEvent e) { // 请求用户输入备注 String note = Messages.showInputDialog( project, - "请输入图钉备注(可选):", - "添加图钉", + CodePinsBundle.message("action.add.pin.note.prompt"), + CodePinsBundle.message("action.add.pin.title"), null ); @@ -103,50 +101,38 @@ public void actionPerformed(@NotNull AnActionEvent e) { if (success) { // 添加成功 if (statusBar != null) { - StatusBar.Info.set("✅ 图钉已添加", project); + StatusBar.Info.set(CodePinsBundle.message("status.pin.added"), project); } // 显示成功消息 Messages.showInfoMessage( project, - "已成功添加图钉到第 " + lineNumber + " 行", - "添加图钉" + CodePinsBundle.message("pin.added", lineNumber), + CodePinsBundle.message("action.add.pin.title") ); } else { // 添加失败 if (statusBar != null) { - StatusBar.Info.set("❌ 图钉添加失败", project); + StatusBar.Info.set(CodePinsBundle.message("status.pin.add.failed"), project); } - // 获取图钉数量信息 - Map pinsInfo = PinStorage.getPinsCountInfo(); - int currentPins = pinsInfo.get("current"); - int maxPins = pinsInfo.get("max"); - - // 获取标签数量信息 - Map tagsInfo = PinStorage.getTagsCountInfo(); - int currentTagTypes = tagsInfo.get("current"); - int maxTagTypes = tagsInfo.get("max"); - int maxTagsPerPin = tagsInfo.get("perPin"); - // 确定失败原因 String failureReason; - String featureName; // 插件现在完全免费,这里不应该出现限制错误 - failureReason = "添加图钉失败,请稍后重试"; + failureReason = CodePinsBundle.message("pin.add.failed.retry"); // 显示错误消息 Messages.showWarningDialog( project, failureReason, - "添加图钉失败" + CodePinsBundle.message("action.add.pin.failed.title") ); // 创建通知 Notification notification = new Notification( "CodePins", - "图钉添加失败", + CodePinsBundle.message("action.add.pin.failed.title"), failureReason, NotificationType.WARNING ); diff --git a/src/main/java/cn/ilikexff/codepins/actions/TogglePinsToolWindowAction.java b/src/main/java/cn/ilikexff/codepins/actions/TogglePinsToolWindowAction.java index dc7938a..c7d4f06 100644 --- a/src/main/java/cn/ilikexff/codepins/actions/TogglePinsToolWindowAction.java +++ b/src/main/java/cn/ilikexff/codepins/actions/TogglePinsToolWindowAction.java @@ -1,5 +1,6 @@ package cn.ilikexff.codepins.actions; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; @@ -20,7 +21,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { } ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); - ToolWindow toolWindow = toolWindowManager.getToolWindow("CodePins"); + ToolWindow toolWindow = toolWindowManager.getToolWindow(CodePinsBundle.message("ui.app.name")); if (toolWindow != null) { if (toolWindow.isVisible()) { diff --git a/src/main/java/cn/ilikexff/codepins/extensions/PinCommentAction.java b/src/main/java/cn/ilikexff/codepins/extensions/PinCommentAction.java index 79a5824..54a18fa 100644 --- a/src/main/java/cn/ilikexff/codepins/extensions/PinCommentAction.java +++ b/src/main/java/cn/ilikexff/codepins/extensions/PinCommentAction.java @@ -2,6 +2,7 @@ import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.settings.CodePinsSettings; import cn.ilikexff.codepins.ui.SimpleTagEditorDialog; import com.intellij.notification.Notification; @@ -112,8 +113,8 @@ public void actionPerformed(@NotNull AnActionEvent e) { // 显示通知,确认动作被触发 Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 注释检测", - "正在检测文件中的注释标记...", + CodePinsBundle.message("comment.detection.title"), + CodePinsBundle.message("comment.detection.scanning"), NotificationType.INFORMATION )); @@ -123,8 +124,8 @@ public void actionPerformed(@NotNull AnActionEvent e) { // 显示通知,报告检测完成 Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 注释检测", - "注释标记检测完成", + CodePinsBundle.message("comment.detection.title"), + CodePinsBundle.message("comment.detection.completed"), NotificationType.INFORMATION )); } @@ -395,11 +396,11 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf if (SHOW_NOTIFICATIONS) { Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 准备创建图钉", - "文件: " + file.getPath() + "\n" + - "偏移量: " + startOffset + "-" + endOffset + "\n" + - "备注: " + note + "\n" + - "是否代码块: " + isBlock, + CodePinsBundle.message("pin.prepare.title"), + CodePinsBundle.message("debug.file", file.getPath()) + "\n" + + CodePinsBundle.message("debug.offset", startOffset, endOffset) + "\n" + + CodePinsBundle.message("debug.note", note) + "\n" + + CodePinsBundle.message("debug.is.block", isBlock), NotificationType.INFORMATION )); } @@ -420,8 +421,8 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf if (SHOW_NOTIFICATIONS) { Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 图钉已存在", - "该范围已有图钉,不重复添加", + CodePinsBundle.message("pin.exists.title"), + CodePinsBundle.message("pin.exists.range"), NotificationType.WARNING )); } @@ -438,7 +439,7 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf // 如果设置了自动添加"快捷添加"标签,则添加该标签 if (autoAddQuickTag) { - initialTags.add("快捷添加"); + initialTags.add(CodePinsBundle.message("tag.quick.add")); } // 在 UI 线程中创建图钉 @@ -449,8 +450,8 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf // 注释中已经有备注了,所以这里只请求用户确认或修改 String confirmedNote = Messages.showInputDialog( project, - "请确认或修改图钉备注:", - "添加图钉", + CodePinsBundle.message("pin.confirm.note"), + CodePinsBundle.message("action.add.pin.title"), null, note, null @@ -493,8 +494,8 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf if (SHOW_NOTIFICATIONS) { Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 图钉创建成功", - "图钉已成功创建: " + confirmedNote, + CodePinsBundle.message("pin.created.title"), + CodePinsBundle.message("pin.created.success", confirmedNote), NotificationType.INFORMATION )); } @@ -507,8 +508,8 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf // 始终显示这个通知,不受 SHOW_NOTIFICATIONS 控制 Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 图钉已添加", - "根据注释指令自动添加了图钉: " + note, + CodePinsBundle.message("pin.added.title"), + CodePinsBundle.message("pin.added.from.comment", note), NotificationType.INFORMATION )); @@ -516,8 +517,8 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf if (SHOW_NOTIFICATIONS) { Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 正在创建图钉", - "正在创建图钉...", + CodePinsBundle.message("pin.creating.title"), + CodePinsBundle.message("pin.creating.message"), NotificationType.INFORMATION )); } @@ -540,8 +541,8 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf if (SHOW_NOTIFICATIONS) { Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 图钉创建成功", - "图钉已成功创建: " + pinEntry.note, + CodePinsBundle.message("pin.created.title"), + CodePinsBundle.message("pin.created.success", pinEntry.note), NotificationType.INFORMATION )); } @@ -549,8 +550,8 @@ private void createPinWithCheck(VirtualFile file, Document document, int startOf // 显示通知,报告错误 Notifications.Bus.notify(new Notification( "CodePins", - "CodePins 图钉创建失败", - "创建图钉时发生错误: " + e.getMessage(), + CodePinsBundle.message("pin.create.failed.title"), + CodePinsBundle.message("pin.create.failed.message", e.getMessage()), NotificationType.ERROR )); e.printStackTrace(); diff --git a/src/main/java/cn/ilikexff/codepins/extensions/PinSelectionPopup.java b/src/main/java/cn/ilikexff/codepins/extensions/PinSelectionPopup.java index f393fc9..764330f 100644 --- a/src/main/java/cn/ilikexff/codepins/extensions/PinSelectionPopup.java +++ b/src/main/java/cn/ilikexff/codepins/extensions/PinSelectionPopup.java @@ -1,5 +1,7 @@ package cn.ilikexff.codepins.extensions; +import cn.ilikexff.codepins.actions.AddPinAction; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; import cn.ilikexff.codepins.settings.CodePinsSettings; @@ -200,8 +202,8 @@ private void addPinForSelection() { // 请求用户输入备注 String note = Messages.showInputDialog( project, - "请输入图钉备注(可选):", - "添加图钉", + CodePinsBundle.message("note.add.dialog.message"), + CodePinsBundle.message("note.add.dialog.title"), null ); diff --git a/src/main/java/cn/ilikexff/codepins/i18n/CodePinsBundle.java b/src/main/java/cn/ilikexff/codepins/i18n/CodePinsBundle.java new file mode 100644 index 0000000..35c3511 --- /dev/null +++ b/src/main/java/cn/ilikexff/codepins/i18n/CodePinsBundle.java @@ -0,0 +1,101 @@ +package cn.ilikexff.codepins.i18n; + +import cn.ilikexff.codepins.settings.LanguageSettings; +import com.intellij.openapi.application.ApplicationManager; +import org.jetbrains.annotations.NotNull; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * CodePins 国际化消息束类 + */ +public class CodePinsBundle { + private static final String BUNDLE_NAME = "messages.CodePinsBundle"; + private static ResourceBundle bundle; + private static Locale cachedLocale; // 缓存的语言设置,用于检测语言变化 + + // 移除静态的 currentLocale,改为动态获取 + + /** + * 获取当前语言设置 + * @return 当前语言区域设置 + */ + public static Locale getCurrentLocale() { + try { + if (ApplicationManager.getApplication() != null) { + LanguageSettings settings = LanguageSettings.getInstance(); + if (settings != null) { + return settings.getSelectedLocale(); + } + } + } catch (Exception e) { + // 在启动过程中或测试环境中可能会出现异常,使用默认语言 + System.out.println("[CodePins] 无法获取语言设置,使用默认语言: " + e.getMessage()); + } + return Locale.ENGLISH; // 默认使用英文 + } + + /** + * 重置资源束,强制在下次访问时重新加载 + */ + public static void resetBundle() { + bundle = null; + cachedLocale = null; + } + + /** + * 获取当前语言设置的资源束 + * @return 资源束 + */ + private static ResourceBundle getBundle() { + try { + Locale currentLocale = getCurrentLocale(); + + // 检查语言是否发生变化,如果变化了就重新加载资源束 + if (bundle == null || !currentLocale.equals(cachedLocale)) { + bundle = ResourceBundle.getBundle(BUNDLE_NAME, currentLocale); + cachedLocale = currentLocale; + } + return bundle; + } catch (MissingResourceException e) { + // 如果找不到资源,尝试使用英文 + try { + bundle = ResourceBundle.getBundle(BUNDLE_NAME, Locale.ENGLISH); + cachedLocale = Locale.ENGLISH; + return bundle; + } catch (MissingResourceException ex) { + System.err.println("[CodePins] 无法加载资源束: " + ex.getMessage()); + return null; + } + } + } + + /** + * 获取国际化消息 + * @param key 消息键 + * @param params 格式化参数 + * @return 国际化消息 + */ + public static String message(@NotNull String key, @NotNull Object... params) { + try { + ResourceBundle resourceBundle = getBundle(); + if (resourceBundle != null && resourceBundle.containsKey(key)) { + String pattern = resourceBundle.getString(key); + if (params.length > 0) { + return MessageFormat.format(pattern, params); + } else { + return pattern; + } + } + } catch (Exception e) { + System.err.println("[CodePins] 获取消息失败: " + key + ", " + e.getMessage()); + } + + // 如果找不到消息,返回键名 + return key; + } + +} diff --git a/src/main/java/cn/ilikexff/codepins/settings/CodePinsSettingsComponent.java b/src/main/java/cn/ilikexff/codepins/settings/CodePinsSettingsComponent.java index f4ff755..cba0009 100644 --- a/src/main/java/cn/ilikexff/codepins/settings/CodePinsSettingsComponent.java +++ b/src/main/java/cn/ilikexff/codepins/settings/CodePinsSettingsComponent.java @@ -1,6 +1,7 @@ package cn.ilikexff.codepins.settings; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.utils.IconUtil; import com.intellij.ide.BrowserUtil; import com.intellij.ui.components.JBCheckBox; @@ -20,57 +21,51 @@ */ public class CodePinsSettingsComponent { private final JPanel mainPanel; - private final JBCheckBox confirmDeleteCheckBox = new JBCheckBox("删除图钉时确认"); + private final JBCheckBox confirmDeleteCheckBox = new JBCheckBox(CodePinsBundle.message("settings.general.confirm.delete")); private final JBTextField previewHeightTextField = new JBTextField(); - private final JBCheckBox showNoteDialogOnQuickAddCheckBox = new JBCheckBox("快捷添加图钉时显示备注和标签对话框"); + private final JBCheckBox showNoteDialogOnQuickAddCheckBox = new JBCheckBox(CodePinsBundle.message("settings.pin.add.show.note.dialog")); // 注释指令添加图钉设置控件 - private final JBCheckBox showNoteDialogOnCommentPinCheckBox = new JBCheckBox("注释指令添加图钉时显示备注和标签对话框"); - private final JBCheckBox autoAddQuickTagCheckBox = new JBCheckBox("自动添加“快捷添加”标签"); - private final JBCheckBox useCompletionSymbolCheckBox = new JBCheckBox("使用完成指令符号"); + private final JBCheckBox showNoteDialogOnCommentPinCheckBox = new JBCheckBox(CodePinsBundle.message("settings.comment.show.note.dialog")); + private final JBCheckBox autoAddQuickTagCheckBox = new JBCheckBox(CodePinsBundle.message("settings.comment.auto.add.tag")); + private final JBCheckBox useCompletionSymbolCheckBox = new JBCheckBox(CodePinsBundle.message("settings.comment.use.completion.symbol")); private final JBTextField completionSymbolTextField = new JBTextField(); public CodePinsSettingsComponent() { // 创建常规设置面板 JPanel generalPanel = FormBuilder.createFormBuilder() - .addLabeledComponent(new JBLabel("预览窗口高度:"), previewHeightTextField, 1, false) + .addLabeledComponent(new JBLabel(CodePinsBundle.message("settings.general.preview.height")), previewHeightTextField, 1, false) .addComponent(confirmDeleteCheckBox) .getPanel(); - generalPanel.setBorder(BorderFactory.createTitledBorder("常规设置")); + generalPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("settings.general"))); // 创建图钉添加设置面板 JPanel pinAddPanel = FormBuilder.createFormBuilder() .addComponent(showNoteDialogOnQuickAddCheckBox) - .addComponent(new JBLabel("开启后,使用选择文本浮动按钮添加图钉时,将显示备注和标签对话框")) + .addComponent(new JBLabel("" + CodePinsBundle.message("settings.pin.add.show.note.dialog.desc") + "")) .getPanel(); - pinAddPanel.setBorder(BorderFactory.createTitledBorder("图钉添加设置")); + pinAddPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("settings.pin.add"))); // 创建注释指令设置面板 JPanel commentPinPanel = FormBuilder.createFormBuilder() .addComponent(showNoteDialogOnCommentPinCheckBox) - .addComponent(new JBLabel("开启后,使用注释指令添加图钉时,将显示备注和标签对话框")) + .addComponent(new JBLabel("" + CodePinsBundle.message("settings.comment.show.note.dialog.desc") + "")) .addComponent(autoAddQuickTagCheckBox) - .addComponent(new JBLabel("开启后,使用注释指令添加图钉时,自动添加“快捷添加”标签")) + .addComponent(new JBLabel("" + CodePinsBundle.message("settings.comment.auto.add.tag.desc") + "")) .addComponent(useCompletionSymbolCheckBox) - .addLabeledComponent(new JBLabel("完成指令符号:"), completionSymbolTextField, 1, false) - .addComponent(new JBLabel("开启后,只有在注释指令后输入完成符号时才会触发图钉添加,避免自动保存导致过早触发")) + .addLabeledComponent(new JBLabel(CodePinsBundle.message("settings.comment.completion.symbol")), completionSymbolTextField, 1, false) + .addComponent(new JBLabel("" + CodePinsBundle.message("settings.comment.completion.symbol.desc") + "")) .getPanel(); - commentPinPanel.setBorder(BorderFactory.createTitledBorder("注释指令设置")); + commentPinPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("settings.comment"))); // 创建快捷键信息面板 JPanel shortcutsInfoPanel = new JPanel(new BorderLayout()); - shortcutsInfoPanel.setBorder(BorderFactory.createTitledBorder("快捷键设置")); + shortcutsInfoPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("settings.shortcuts"))); JBTextArea shortcutsInfoText = new JBTextArea( - "CodePins 提供以下默认快捷键:\n\n" + - "- 添加图钉: Alt+Shift+P\n" + - "- 导航到下一个图钉: Alt+Shift+Right\n" + - "- 导航到上一个图钉: Alt+Shift+Left\n" + - "- 切换图钉工具窗口: Alt+Shift+T\n\n" + - "您可以在 IDE 的'设置 > 键盘快捷键'中自定义这些快捷键。\n" + - "搜索 \"CodePins\" 以找到所有相关操作。" + CodePinsBundle.message("settings.shortcuts.info") ); shortcutsInfoText.setEditable(false); shortcutsInfoText.setBackground(shortcutsInfoPanel.getBackground()); @@ -78,7 +73,7 @@ public CodePinsSettingsComponent() { shortcutsInfoText.setLineWrap(true); shortcutsInfoText.setWrapStyleWord(true); - JButton openKeyMapSettingsButton = new JButton("打开键盘快捷键设置"); + JButton openKeyMapSettingsButton = new JButton(CodePinsBundle.message("settings.shortcuts.open")); openKeyMapSettingsButton.addActionListener(e -> openKeyMapSettings()); shortcutsInfoPanel.add(shortcutsInfoText, BorderLayout.CENTER); @@ -89,7 +84,7 @@ public CodePinsSettingsComponent() { // 创建快捷键信息面板的标签面板 JPanel labeledShortcutsPanel = new JPanel(new BorderLayout()); - JLabel shortcutsLabel = new JBLabel("快捷键信息:"); + JLabel shortcutsLabel = new JBLabel(CodePinsBundle.message("settings.shortcuts") + ":"); labeledShortcutsPanel.add(shortcutsLabel, BorderLayout.NORTH); labeledShortcutsPanel.add(shortcutsInfoPanel, BorderLayout.CENTER); @@ -120,24 +115,18 @@ private void openKeyMapSettings() { */ private JPanel createDonationPanel() { JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createTitledBorder("支持开发")); + panel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("settings.support"))); JPanel contentPanel = new JPanel(new BorderLayout()); contentPanel.setBorder(JBUI.Borders.empty(10)); // 状态标签 JLabel statusLabel = new JBLabel("" + - "
✓ CodePins 现在完全免费开源!
" + - "
感谢您使用 CodePins!如果这个插件对您有帮助," + - "
请考虑通过以下方式支持我们的开发:" + + "
" + CodePinsBundle.message("settings.support.free") + "
" + + "
" + CodePinsBundle.message("settings.support.thanks") + "

" + - "
🤝 欢迎加入开源贡献!
" + - "
我们诚挚邀请您一起维护这个开源项目:" + - "
• 🐛 报告 Bug 和提出改进建议" + - "
• 💡 贡献新功能和代码优化" + - "
• 📖 完善文档和使用指南" + - "
• 🌍 帮助翻译到更多语言" + - "
• 📢 向其他开发者推荐 CodePins" + + "
" + CodePinsBundle.message("settings.support.contribute") + "
" + + "
" + CodePinsBundle.message("settings.support.contribute.desc").replace("\n", "
") + ""); contentPanel.add(statusLabel, BorderLayout.CENTER); @@ -145,19 +134,25 @@ private JPanel createDonationPanel() { JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); // GitHub 按钮 - JButton githubButton = new JButton("⭐ GitHub"); + JButton githubButton = new JButton(CodePinsBundle.message("settings.support.github")); githubButton.addActionListener(e -> { BrowserUtil.browse("https://github.com/08820048/codepins"); }); // 参与贡献按钮 - JButton contributeButton = new JButton("🤝 参与贡献"); + JButton contributeButton = new JButton(CodePinsBundle.message("settings.support.contribute.button")); contributeButton.addActionListener(e -> { BrowserUtil.browse("https://github.com/08820048/codepins/blob/main/CONTRIBUTING.md"); }); + // 问题报告按钮 + JButton issueButton = new JButton(CodePinsBundle.message("settings.support.issue")); + issueButton.addActionListener(e -> { + BrowserUtil.browse("https://github.com/08820048/codepins/issues"); + }); + // 捐赠按钮 - JButton donateButton = new JButton("☕ 请我喝咖啡"); + JButton donateButton = new JButton(CodePinsBundle.message("settings.support.donate")); donateButton.addActionListener(e -> { BrowserUtil.browse("https://docs.codepins.cn/donate"); }); @@ -170,6 +165,7 @@ private JPanel createDonationPanel() { buttonPanel.add(githubButton); buttonPanel.add(contributeButton); + buttonPanel.add(issueButton); buttonPanel.add(donateButton); contentPanel.add(buttonPanel, BorderLayout.SOUTH); diff --git a/src/main/java/cn/ilikexff/codepins/settings/LanguageSettings.java b/src/main/java/cn/ilikexff/codepins/settings/LanguageSettings.java new file mode 100644 index 0000000..e391c03 --- /dev/null +++ b/src/main/java/cn/ilikexff/codepins/settings/LanguageSettings.java @@ -0,0 +1,76 @@ +package cn.ilikexff.codepins.settings; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.Service; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Locale; + +/** + * Language settings for CodePins + */ +@Service +@State( + name = "CodePinsLanguageSettings", + storages = @Storage("codepins-language-settings.xml") +) +public final class LanguageSettings implements PersistentStateComponent { + private State myState = new State(); + + public enum Language { + SYSTEM("settings.language.system", null), + ENGLISH("settings.language.english", Locale.ENGLISH), + CHINESE("settings.language.chinese", Locale.SIMPLIFIED_CHINESE); + + private final String displayNameKey; + private final Locale locale; + + Language(String displayNameKey, Locale locale) { + this.displayNameKey = displayNameKey; + this.locale = locale; + } + + public String getDisplayNameKey() { + return displayNameKey; + } + + public Locale getLocale() { + return locale != null ? locale : Locale.getDefault(); + } + } + + public static class State { + public Language language = Language.SYSTEM; + } + + public static LanguageSettings getInstance() { + return ApplicationManager.getApplication().getService(LanguageSettings.class); + } + + @Nullable + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(@NotNull State state) { + myState = state; + } + + public Language getLanguage() { + return myState.language; + } + + public void setLanguage(Language language) { + myState.language = language; + } + + public Locale getSelectedLocale() { + return myState.language.getLocale(); + } +} diff --git a/src/main/java/cn/ilikexff/codepins/settings/LanguageSettingsConfigurable.java b/src/main/java/cn/ilikexff/codepins/settings/LanguageSettingsConfigurable.java new file mode 100644 index 0000000..aef1e7c --- /dev/null +++ b/src/main/java/cn/ilikexff/codepins/settings/LanguageSettingsConfigurable.java @@ -0,0 +1,105 @@ +package cn.ilikexff.codepins.settings; + +import cn.ilikexff.codepins.I18n; +import cn.ilikexff.codepins.i18n.CodePinsBundle; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.Messages; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBPanel; +import com.intellij.util.ui.FormBuilder; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +/** + * Configurable for CodePins language settings + */ +public class LanguageSettingsConfigurable implements Configurable { + private JComboBox languageComboBox; + private final LanguageSettings settings = LanguageSettings.getInstance(); + + @Nls(capitalization = Nls.Capitalization.Title) + @Override + public String getDisplayName() { + return CodePinsBundle.message("settings.language"); + } + + @Nullable + @Override + public JComponent createComponent() { + JBPanel panel = new JBPanel<>(new BorderLayout()); + + // Create language combo box + languageComboBox = new ComboBox<>(); + for (LanguageSettings.Language language : LanguageSettings.Language.values()) { + languageComboBox.addItem(new LanguageItem(language)); + } + + // Set current language + for (int i = 0; i < languageComboBox.getItemCount(); i++) { + LanguageItem item = languageComboBox.getItemAt(i); + if (item.getLanguage() == settings.getLanguage()) { + languageComboBox.setSelectedIndex(i); + break; + } + } + + // Build form + JBLabel languageLabel = new JBLabel(CodePinsBundle.message("settings.language") + ":"); + panel.add(FormBuilder.createFormBuilder() + .addLabeledComponent(languageLabel, languageComboBox) + .addComponentFillVertically(new JPanel(), 0) + .getPanel()); + + return panel; + } + + @Override + public boolean isModified() { + LanguageItem selectedItem = (LanguageItem) languageComboBox.getSelectedItem(); + return selectedItem != null && selectedItem.getLanguage() != settings.getLanguage(); + } + + @Override + public void apply() throws ConfigurationException { + LanguageItem selectedItem = (LanguageItem) languageComboBox.getSelectedItem(); + if (selectedItem != null) { + // 保存语言设置 + settings.setLanguage(selectedItem.getLanguage()); + + // 重置资源束,强制重新加载 + I18n.resetBundle(); + CodePinsBundle.resetBundle(); + + // 显示重启提示 + Messages.showInfoMessage( + CodePinsBundle.message("settings.language.change.message"), + CodePinsBundle.message("settings.language.change.title") + ); + } + } + + /** + * Helper class to display language items in combo box + */ + private static class LanguageItem { + private final LanguageSettings.Language language; + + public LanguageItem(LanguageSettings.Language language) { + this.language = language; + } + + public LanguageSettings.Language getLanguage() { + return language; + } + + @Override + public String toString() { + return CodePinsBundle.message(language.getDisplayNameKey()); + } + } +} diff --git a/src/main/java/cn/ilikexff/codepins/startup/CodePinsStartupActivity.java b/src/main/java/cn/ilikexff/codepins/startup/CodePinsStartupActivity.java new file mode 100644 index 0000000..8653a1a --- /dev/null +++ b/src/main/java/cn/ilikexff/codepins/startup/CodePinsStartupActivity.java @@ -0,0 +1,19 @@ +package cn.ilikexff.codepins.startup; + +import cn.ilikexff.codepins.settings.LanguageSettings; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupActivity; +import org.jetbrains.annotations.NotNull; + +/** + * CodePins 启动活动 + * 在插件启动时初始化语言设置 + */ +public class CodePinsStartupActivity implements StartupActivity { + @Override + public void runActivity(@NotNull Project project) { + // 插件启动时的初始化逻辑 + // 语言设置会在需要时自动加载 + } +} diff --git a/src/main/java/cn/ilikexff/codepins/ui/EmptyStatePanel.java b/src/main/java/cn/ilikexff/codepins/ui/EmptyStatePanel.java index 5ba48b0..e1beddf 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/EmptyStatePanel.java +++ b/src/main/java/cn/ilikexff/codepins/ui/EmptyStatePanel.java @@ -1,5 +1,6 @@ package cn.ilikexff.codepins.ui; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.utils.IconUtil; import com.intellij.ide.BrowserUtil; import com.intellij.ui.JBColor; @@ -30,7 +31,7 @@ public EmptyStatePanel() { iconLabel.setAlignmentX(Component.CENTER_ALIGNMENT); // 添加标题 - JLabel titleLabel = new JLabel("还没有添加任何图钉"); + JLabel titleLabel = new JLabel(CodePinsBundle.message("empty.title")); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 16f)); titleLabel.setForeground(new JBColor(new Color(220, 220, 220), new Color(220, 220, 220))); titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT); @@ -38,10 +39,12 @@ public EmptyStatePanel() { // 添加说明文字 JTextPane descriptionPane = new JTextPane(); descriptionPane.setContentType("text/html"); + String description = CodePinsBundle.message("empty.description"); + // 将\n替换为
+ description = description.replace("\n", "
"); descriptionPane.setText( "
" + - "图钉可以帮助您标记重要的代码位置,
并随时快速返回查看。

" + - "在编辑器中右键点击代码行,
选择\"Add CodePin Here\"添加图钉。" + + description + "
" ); descriptionPane.setEditable(false); @@ -54,7 +57,7 @@ public EmptyStatePanel() { buttonPanel.setOpaque(false); buttonPanel.setAlignmentX(Component.CENTER_ALIGNMENT); - JButton helpButton = new JButton("查看帮助"); + JButton helpButton = new JButton(CodePinsBundle.message("empty.help.button")); helpButton.setFocusPainted(false); // 添加点击事件,打开文档地址 diff --git a/src/main/java/cn/ilikexff/codepins/ui/ExportDialog.java b/src/main/java/cn/ilikexff/codepins/ui/ExportDialog.java index d794fb0..a63c386 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/ExportDialog.java +++ b/src/main/java/cn/ilikexff/codepins/ui/ExportDialog.java @@ -1,6 +1,7 @@ package cn.ilikexff.codepins.ui; import cn.ilikexff.codepins.core.PinEntry; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.core.PinStorage; import cn.ilikexff.codepins.utils.ImportExportUtil; import com.intellij.openapi.fileChooser.FileChooserFactory; @@ -57,7 +58,7 @@ public ExportDialog(Project project) { pinsList.setCellRenderer(new PinListCellRenderer()); pinsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - setTitle("导出图钉"); + setTitle(CodePinsBundle.message("export.dialog.title")); setSize(650, 600); init(); } @@ -76,12 +77,11 @@ public ExportDialog(Project project) { JBUI.Borders.empty(0, 0, 15, 0) )); - JLabel titleLabel = new JLabel("导出图钉数据"); + JLabel titleLabel = new JLabel(CodePinsBundle.message("export.dialog.description")); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 16f)); titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT); - JLabel descLabel = new JLabel("您可以选择导出全部图钉或仅导出选中的图钉。
" + - "如需选择特定图钉,请选择'仅导出选中的图钉'选项,然后在下方列表中选择要导出的图钉。"); + JLabel descLabel = new JLabel("" + CodePinsBundle.message("export.description.html") + ""); descLabel.setAlignmentX(Component.LEFT_ALIGNMENT); infoPanel.add(titleLabel); @@ -98,15 +98,15 @@ public ExportDialog(Project project) { optionsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); // 导出选项 - JLabel exportOptionsLabel = new JLabel("导出选项:"); + JLabel exportOptionsLabel = new JLabel(CodePinsBundle.message("export.mode") + ":"); exportOptionsLabel.setFont(exportOptionsLabel.getFont().deriveFont(Font.BOLD)); exportOptionsLabel.setAlignmentX(Component.LEFT_ALIGNMENT); - exportAllRadio = new JRadioButton("导出所有图钉"); + exportAllRadio = new JRadioButton(CodePinsBundle.message("export.mode.all")); exportAllRadio.setAlignmentX(Component.LEFT_ALIGNMENT); exportAllRadio.setSelected(true); - exportSelectedRadio = new JRadioButton("仅导出选中的图钉"); + exportSelectedRadio = new JRadioButton(CodePinsBundle.message("export.mode.selected")); exportSelectedRadio.setAlignmentX(Component.LEFT_ALIGNMENT); ButtonGroup exportGroup = new ButtonGroup(); @@ -124,7 +124,7 @@ public ExportDialog(Project project) { JPanel listPanel = new JPanel(new BorderLayout()); listPanel.setBorder(JBUI.Borders.empty(5, 0, 0, 0)); - JLabel pinsLabel = new JLabel("可用图钉:"); + JLabel pinsLabel = new JLabel(CodePinsBundle.message("export.pins.list") + ":"); pinsLabel.setFont(pinsLabel.getFont().deriveFont(Font.BOLD)); JBScrollPane scrollPane = new JBScrollPane(pinsList); @@ -176,8 +176,8 @@ protected void doOKAction() { if (pinsToExport.isEmpty()) { Messages.showErrorDialog( project, - "没有图钉可导出。请选择至少一个图钉进行导出。", - "导出错误" + CodePinsBundle.message("export.no.pins"), + CodePinsBundle.message("export.file.select") ); return; } @@ -195,8 +195,8 @@ protected void doOKAction() { if (success) { Messages.showInfoMessage( project, - "成功导出 " + pinsToExport.size() + " 个图钉到文件:\n" + file.getAbsolutePath(), - "导出成功" + CodePinsBundle.message("export.success", pinsToExport.size(), file.getAbsolutePath()), + CodePinsBundle.message("export.success.title") ); super.doOKAction(); } diff --git a/src/main/java/cn/ilikexff/codepins/ui/ImportDialog.java b/src/main/java/cn/ilikexff/codepins/ui/ImportDialog.java index 4592dda..82acaaa 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/ImportDialog.java +++ b/src/main/java/cn/ilikexff/codepins/ui/ImportDialog.java @@ -1,5 +1,6 @@ package cn.ilikexff.codepins.ui; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.utils.ImportExportUtil; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; @@ -36,7 +37,7 @@ public ImportDialog(Project project) { super(project); this.project = project; - setTitle("导入图钉"); + setTitle(CodePinsBundle.message("import.dialog.title")); setSize(500, 300); init(); } @@ -55,11 +56,11 @@ public ImportDialog(Project project) { JBUI.Borders.empty(0, 0, 10, 0) )); - JLabel titleLabel = new JLabel("导入图钉数据"); + JLabel titleLabel = new JLabel(CodePinsBundle.message("import.dialog.header")); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 16f)); titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT); - JLabel descLabel = new JLabel("选择要导入的文件,并设置导入选项。"); + JLabel descLabel = new JLabel(CodePinsBundle.message("import.dialog.instruction")); descLabel.setAlignmentX(Component.LEFT_ALIGNMENT); infoPanel.add(titleLabel); @@ -71,16 +72,16 @@ public ImportDialog(Project project) { filePanel.setBorder(JBUI.Borders.empty(10, 0)); filePanel.setAlignmentX(Component.LEFT_ALIGNMENT); - JLabel fileLabel = new JLabel("导入文件:"); + JLabel fileLabel = new JLabel(CodePinsBundle.message("import.file.label") + ":"); fileLabel.setFont(fileLabel.getFont().deriveFont(Font.BOLD)); - filePathLabel = new JLabel("未选择文件"); + filePathLabel = new JLabel(CodePinsBundle.message("import.file.none")); filePathLabel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(JBColor.border()), JBUI.Borders.empty(5) )); - JButton browseButton = new JButton("浏览..."); + JButton browseButton = new JButton(CodePinsBundle.message("import.file.select")); browseButton.addActionListener(e -> selectImportFile()); filePanel.add(fileLabel, BorderLayout.NORTH); @@ -94,15 +95,15 @@ public ImportDialog(Project project) { optionsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); // 导入选项 - JLabel importOptionsLabel = new JLabel("导入选项:"); + JLabel importOptionsLabel = new JLabel(CodePinsBundle.message("import.options.label")); importOptionsLabel.setFont(importOptionsLabel.getFont().deriveFont(Font.BOLD)); importOptionsLabel.setAlignmentX(Component.LEFT_ALIGNMENT); - mergeRadio = new JRadioButton("合并模式:保留现有图钉,添加导入的图钉"); + mergeRadio = new JRadioButton(CodePinsBundle.message("import.mode.merge")); mergeRadio.setAlignmentX(Component.LEFT_ALIGNMENT); mergeRadio.setSelected(true); - replaceRadio = new JRadioButton("替换模式:清空现有图钉,只保留导入的图钉"); + replaceRadio = new JRadioButton(CodePinsBundle.message("import.mode.replace")); replaceRadio.setAlignmentX(Component.LEFT_ALIGNMENT); ButtonGroup importGroup = new ButtonGroup(); @@ -121,7 +122,8 @@ public ImportDialog(Project project) { JBUI.Borders.empty(10, 0, 0, 0) )); - JLabel warningLabel = new JLabel("注意:导入操作可能会覆盖现有图钉。请确保您已备份重要数据。"); + JLabel warningLabel = new JLabel("" + CodePinsBundle.message("import.warning") + " " + CodePinsBundle.message("import.warning.desc") + ""); + // 确保警告文本使用当前语言设置 warningLabel.setForeground(JBColor.RED); warningPanel.add(warningLabel, BorderLayout.CENTER); @@ -139,14 +141,14 @@ public ImportDialog(Project project) { */ private void selectImportFile() { FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) - .withTitle("选择导入文件") - .withDescription("选择要导入的 JSON 文件") + .withTitle(CodePinsBundle.message("import.file.chooser.title")) + .withDescription(CodePinsBundle.message("import.file.chooser.desc")) .withFileFilter(file -> file.getExtension() != null && file.getExtension().equalsIgnoreCase("json")); VirtualFile[] files = FileChooser.chooseFiles(descriptor, project, null); if (files.length > 0) { selectedFile = new File(files[0].getPath()); - filePathLabel.setText(selectedFile.getAbsolutePath()); + filePathLabel.setText(CodePinsBundle.message("import.file.selected", selectedFile.getAbsolutePath())); } } @@ -169,8 +171,8 @@ protected void doOKAction() { if (importCount >= 0) { Messages.showInfoMessage( project, - "成功导入 " + importCount + " 个图钉。", - "导入成功" + CodePinsBundle.message("import.success", importCount), + CodePinsBundle.message("import.success.title") ); super.doOKAction(); } diff --git a/src/main/java/cn/ilikexff/codepins/ui/ShareDialog.java b/src/main/java/cn/ilikexff/codepins/ui/ShareDialog.java index 5b8b4f6..7f0007b 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/ShareDialog.java +++ b/src/main/java/cn/ilikexff/codepins/ui/ShareDialog.java @@ -1,6 +1,7 @@ package cn.ilikexff.codepins.ui; import cn.ilikexff.codepins.core.PinEntry; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.utils.AnimationUtil; import cn.ilikexff.codepins.utils.ImageGenerator; import cn.ilikexff.codepins.utils.SensitiveInfoDetector; @@ -19,8 +20,6 @@ import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.Nullable; -import java.util.List; - import javax.swing.*; import java.awt.*; import java.io.File; @@ -66,7 +65,7 @@ public ShareDialog(Project project, List pins) { this.project = project; this.pins = new ArrayList<>(pins); - setTitle("分享图钉"); + setTitle(CodePinsBundle.message("share.dialog.title")); setSize(500, 400); // 设置对话框尺寸 init(); } @@ -78,14 +77,14 @@ public ShareDialog(Project project, List pins) { // 创建格式选择面板 JPanel formatPanel = new JPanel(new GridLayout(7, 1)); - formatPanel.setBorder(BorderFactory.createTitledBorder("分享格式")); + formatPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("share.format"))); markdownRadio = new JBRadioButton("Markdown"); markdownRadio.setSelected(true); htmlRadio = new JBRadioButton("HTML"); jsonRadio = new JBRadioButton("JSON"); - codeOnlyRadio = new JBRadioButton("仅代码"); - imageRadio = new JBRadioButton("图片"); + codeOnlyRadio = new JBRadioButton(CodePinsBundle.message("share.format.code.only")); + imageRadio = new JBRadioButton(CodePinsBundle.message("share.format.image")); svgRadio = new JBRadioButton("SVG"); ButtonGroup formatGroup = new ButtonGroup(); @@ -96,7 +95,7 @@ public ShareDialog(Project project, List pins) { formatGroup.add(imageRadio); formatGroup.add(svgRadio); - formatPanel.add(new JBLabel("选择分享格式:")); + formatPanel.add(new JBLabel(CodePinsBundle.message("share.format") + ":")); formatPanel.add(markdownRadio); formatPanel.add(htmlRadio); formatPanel.add(jsonRadio); @@ -106,11 +105,11 @@ public ShareDialog(Project project, List pins) { // 添加图片主题选择面板 themePanel = new JPanel(new BorderLayout()); - themePanel.setBorder(BorderFactory.createTitledBorder("图片主题")); + themePanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("share.watermark"))); themeComboBox = new JComboBox<>(ImageGenerator.Theme.values()); themeComboBox.setSelectedItem(ImageGenerator.Theme.DARK); // 默认选择暗色主题 - themePanel.add(new JBLabel("选择主题:"), BorderLayout.WEST); + themePanel.add(new JBLabel(CodePinsBundle.message("share.watermark") + ":"), BorderLayout.WEST); themePanel.add(themeComboBox, BorderLayout.CENTER); themePanel.setVisible(false); // 初始不可见 @@ -148,17 +147,17 @@ public ShareDialog(Project project, List pins) { // 创建分享方式面板 JPanel methodPanel = new JPanel(new GridLayout(4, 1)); - methodPanel.setBorder(BorderFactory.createTitledBorder("分享方式")); + methodPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("share.method"))); - clipboardRadio = new JBRadioButton("复制到剪贴板"); + clipboardRadio = new JBRadioButton(CodePinsBundle.message("share.method.clipboard")); clipboardRadio.setSelected(true); - fileRadio = new JBRadioButton("导出到文件"); + fileRadio = new JBRadioButton(CodePinsBundle.message("share.method.file")); ButtonGroup methodGroup = new ButtonGroup(); methodGroup.add(clipboardRadio); methodGroup.add(fileRadio); - methodPanel.add(new JBLabel("选择分享方式:")); + methodPanel.add(new JBLabel(CodePinsBundle.message("share.method") + ":")); methodPanel.add(clipboardRadio); methodPanel.add(fileRadio); @@ -166,7 +165,7 @@ public ShareDialog(Project project, List pins) { optionsPanel = new JPanel(new GridLayout(2, 1, 0, 5)); // 添加"只分享代码"选项 - codeOnlyCheckBox = new JCheckBox("只分享代码内容(不包含元数据)"); + codeOnlyCheckBox = new JCheckBox(CodePinsBundle.message("share.exclude.metadata")); // 检查是否有代码块图钉 boolean hasCodeBlock = false; for (PinEntry pin : pins) { @@ -177,11 +176,11 @@ public ShareDialog(Project project, List pins) { } codeOnlyCheckBox.setEnabled(hasCodeBlock); if (!hasCodeBlock) { - codeOnlyCheckBox.setToolTipText("没有代码块图钉可供分享"); + codeOnlyCheckBox.setToolTipText(CodePinsBundle.message("pin.type.block")); } // 添加行号显示选项 - showLineNumbersCheckBox = new JCheckBox("显示行号"); + showLineNumbersCheckBox = new JCheckBox(CodePinsBundle.message("share.show.line.numbers")); showLineNumbersCheckBox.setSelected(true); // 默认选中 // 添加到选项面板 @@ -193,9 +192,9 @@ public ShareDialog(Project project, List pins) { // 创建信息面板 JPanel infoPanel = new JPanel(new BorderLayout()); - infoPanel.setBorder(BorderFactory.createTitledBorder("分享信息")); + infoPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("share.info", pins.size()))); - JLabel infoLabel = new JBLabel("将分享 " + pins.size() + " 个图钉"); + JLabel infoLabel = new JBLabel(CodePinsBundle.message("share.info", pins.size())); infoPanel.add(infoLabel, BorderLayout.CENTER); // 添加高级功能按钮 @@ -203,7 +202,7 @@ public ShareDialog(Project project, List pins) { // 添加社交分享按钮 Icon webIcon = IconLoader.getIcon("/icons/web.svg", getClass()); - JButton socialShareButton = new JButton("社交分享", webIcon); + JButton socialShareButton = new JButton(CodePinsBundle.message("share.social"), webIcon); socialShareButton.addActionListener(e -> { // 添加按钮动画效果 AnimationUtil.buttonClickEffect(socialShareButton); @@ -221,7 +220,7 @@ public ShareDialog(Project project, List pins) { // 添加水印设置按钮 Icon settingsIcon = IconLoader.getIcon("/icons/settings.svg", getClass()); - JButton watermarkButton = new JButton("水印设置", settingsIcon); + JButton watermarkButton = new JButton(CodePinsBundle.message("share.watermark"), settingsIcon); watermarkButton.addActionListener(e -> { // 添加按钮动画效果 AnimationUtil.buttonClickEffect(watermarkButton); diff --git a/src/main/java/cn/ilikexff/codepins/ui/SimpleTagEditorDialog.java b/src/main/java/cn/ilikexff/codepins/ui/SimpleTagEditorDialog.java index 9e73e26..dc3ecb4 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/SimpleTagEditorDialog.java +++ b/src/main/java/cn/ilikexff/codepins/ui/SimpleTagEditorDialog.java @@ -2,22 +2,19 @@ import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; +import cn.ilikexff.codepins.i18n.CodePinsBundle; // 移除不再需要的许可证服务引用 import cn.ilikexff.codepins.utils.IconUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; import com.intellij.ui.JBColor; import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; -import cn.ilikexff.codepins.ui.AnimationUtil; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.border.Border; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; @@ -25,7 +22,6 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.HashSet; @@ -34,8 +30,6 @@ */ public class SimpleTagEditorDialog extends DialogWrapper { - private final Project project; - private final PinEntry pinEntry; private final DefaultListModel tagsModel; private final JBList tagsList; private final JBTextField newTagField; @@ -46,8 +40,6 @@ public class SimpleTagEditorDialog extends DialogWrapper { public SimpleTagEditorDialog(Project project, PinEntry pinEntry) { super(project); - this.project = project; - this.pinEntry = pinEntry; this.currentTags = new ArrayList<>(pinEntry.getTags()); // 获取所有已有标签 @@ -69,7 +61,7 @@ public SimpleTagEditorDialog(Project project, PinEntry pinEntry) { existingTagsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5)); // 设置对话框标题和尺寸 - setTitle("编辑标签"); + setTitle(CodePinsBundle.message("tag.dialog.title")); init(); } @@ -83,15 +75,9 @@ public SimpleTagEditorDialog(Project project, PinEntry pinEntry) { // 插件现在完全免费开源,不再需要检查标签限制信息 // 添加说明标签 - StringBuilder instructionText = new StringBuilder("标签使用说明:
" + - "1. 在下方输入框中输入标签名称,然后按回车或点击添加按钮
" + - "2. 选中列表中的标签,然后点击删除按钮可删除标签
" + - "3. 选中列表中的标签,然后点击编辑按钮或双击标签可编辑标签
" + - "4. 标签数量无限制,插件已完全免费开源"); + String instructionText = CodePinsBundle.message("tag.dialog.instruction"); - instructionText.append(""); - - JLabel instructionLabel = new JLabel(instructionText.toString()); + JLabel instructionLabel = new JLabel(instructionText); instructionLabel.setAlignmentX(Component.LEFT_ALIGNMENT); instructionLabel.setBorder(JBUI.Borders.emptyBottom(10)); @@ -100,7 +86,7 @@ public SimpleTagEditorDialog(Project project, PinEntry pinEntry) { mainPanel.add(instructionLabel); // 添加当前标签标签 - JLabel currentTagsLabel = new JLabel("当前标签:"); + JLabel currentTagsLabel = new JLabel(CodePinsBundle.message("tag.current.label")); currentTagsLabel.setAlignmentX(Component.LEFT_ALIGNMENT); mainPanel.add(currentTagsLabel); @@ -115,19 +101,19 @@ public SimpleTagEditorDialog(Project project, PinEntry pinEntry) { scrollPane.setBorder(BorderFactory.createLineBorder(JBColor.border(), 1)); mainPanel.add(scrollPane); - // 添加标签操作按钮面板 - tagActionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0)); + // 创建标签操作面板 + tagActionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + JButton editButton = new JButton(CodePinsBundle.message("tag.dialog.edit")); tagActionPanel.setAlignmentX(Component.LEFT_ALIGNMENT); tagActionPanel.setBorder(JBUI.Borders.empty(8, 0, 8, 0)); // 删除按钮 - JButton removeButton = new JButton("删除"); + JButton removeButton = new JButton(CodePinsBundle.message("tag.dialog.delete")); removeButton.setIcon(IconUtil.loadIcon("/icons/trash.svg", getClass())); removeButton.addActionListener(e -> removeSelectedTags()); removeButton.setFocusPainted(false); // 编辑按钮 - JButton editButton = new JButton("编辑"); editButton.setIcon(IconUtil.loadIcon("/icons/edit.svg", getClass())); editButton.addActionListener(e -> editSelectedTag()); editButton.setFocusPainted(false); @@ -156,7 +142,7 @@ public void mouseClicked(MouseEvent e) { mainPanel.add(Box.createVerticalStrut(15)); // 添加已有标签区域 - JLabel existingTagsLabel = new JLabel("当前已有标签(点击添加):"); + JLabel existingTagsLabel = new JLabel(CodePinsBundle.message("tag.dialog.existing")); existingTagsLabel.setAlignmentX(Component.LEFT_ALIGNMENT); mainPanel.add(existingTagsLabel); @@ -175,7 +161,7 @@ public void mouseClicked(MouseEvent e) { mainPanel.add(Box.createVerticalStrut(15)); // 添加新标签标签 - JLabel newTagLabel = new JLabel("添加新标签:"); + JLabel newTagLabel = new JLabel(CodePinsBundle.message("tag.dialog.add")); newTagLabel.setAlignmentX(Component.LEFT_ALIGNMENT); mainPanel.add(newTagLabel); @@ -185,7 +171,7 @@ public void mouseClicked(MouseEvent e) { inputPanel.setAlignmentX(Component.LEFT_ALIGNMENT); // 设置输入框提示 - newTagField.putClientProperty("JTextField.placeholderText", "输入标签名称,按回车添加"); + newTagField.putClientProperty("JTextField.placeholderText", CodePinsBundle.message("tag.dialog.input.placeholder")); newTagField.setPreferredSize(new Dimension(300, 32)); newTagField.setMaximumSize(new Dimension(Short.MAX_VALUE, 32)); newTagField.setBorder(BorderFactory.createCompoundBorder( @@ -201,7 +187,7 @@ public void keyPressed(KeyEvent e) { } }); - JButton addButton = new JButton("添加"); + JButton addButton = new JButton(CodePinsBundle.message("tag.dialog.add")); addButton.setIcon(IconUtil.loadIcon("/icons/plus.svg", getClass())); addButton.addActionListener(e -> addNewTag()); addButton.setFocusPainted(false); @@ -268,11 +254,11 @@ private void editSelectedTag() { int selectedIndex = tagsList.getSelectedIndex(); if (selectedIndex >= 0) { // 编辑按钮动画效果 - AnimationUtil.buttonClickEffect((JButton)tagActionPanel.getComponent(1)); + // AnimationUtil.buttonClickEffect((JButton)tagActionPanel.getComponent(1)); String oldTag = tagsModel.getElementAt(selectedIndex); String newTag = JOptionPane.showInputDialog(this.getRootPane(), - "请输入新的标签名称", oldTag); + CodePinsBundle.message("tag.dialog.edit"), oldTag); if (newTag != null && !newTag.trim().isEmpty() && !newTag.equals(oldTag)) { // 检查新标签是否已存在 @@ -291,8 +277,8 @@ private void editSelectedTag() { populateExistingTags(); } else { JOptionPane.showMessageDialog(this.getRootPane(), - "标签 '"+newTag.trim()+"' 已存在", - "标签重复", + CodePinsBundle.message("tag.dialog.exists", newTag.trim()), + CodePinsBundle.message("tag.dialog.exists.title"), JOptionPane.WARNING_MESSAGE); } } @@ -314,7 +300,7 @@ private void populateExistingTags() { // 如果没有已有标签,显示提示信息 if (existingTags.isEmpty()) { - JLabel emptyLabel = new JLabel("暂无已有标签"); + JLabel emptyLabel = new JLabel(CodePinsBundle.message("tag.dialog.empty")); emptyLabel.setForeground(JBColor.GRAY); existingTagsPanel.add(emptyLabel); return; @@ -326,7 +312,7 @@ private void populateExistingTags() { // 如果过滤后没有可用标签,显示提示信息 if (availableTags.isEmpty()) { - JLabel emptyLabel = new JLabel("所有标签已添加"); + JLabel emptyLabel = new JLabel(CodePinsBundle.message("tag.dialog.all")); emptyLabel.setForeground(JBColor.GRAY); existingTagsPanel.add(emptyLabel); return; @@ -431,9 +417,8 @@ public void mouseClicked(MouseEvent e) { private static class TagCellRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - // 创建自定义标签面板 - JPanel tagPanel = new JPanel(new BorderLayout(8, 0)); - tagPanel.setBorder(JBUI.Borders.empty(8, 10)); + // 创建标签单元格面板 + JPanel tagPanel = new JPanel(new BorderLayout()); // 获取标签文本 String tag = value.toString(); diff --git a/src/main/java/cn/ilikexff/codepins/ui/SocialShareDialog.java b/src/main/java/cn/ilikexff/codepins/ui/SocialShareDialog.java index 0c57fa1..f84a9d5 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/SocialShareDialog.java +++ b/src/main/java/cn/ilikexff/codepins/ui/SocialShareDialog.java @@ -1,9 +1,8 @@ package cn.ilikexff.codepins.ui; import cn.ilikexff.codepins.core.PinEntry; -import cn.ilikexff.codepins.services.LicenseService; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.utils.*; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; @@ -26,7 +25,6 @@ public class SocialShareDialog extends DialogWrapper { private final Project project; private final List pins; - private final String shareContent; private final SharingUtil.SharingFormat format; private JRadioButton[] platformRadios; @@ -50,10 +48,10 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari this.pins = new ArrayList<>(pins); this.format = format; - // 生成分享内容 - this.shareContent = SharingUtil.formatPins(project, pins, format, codeOnly, showLineNumbers); + // 注意:分享内容将在需要时生成,不再存储为字段 + // SharingUtil.formatPins(project, pins, format, codeOnly, showLineNumbers); - setTitle("分享到社交媒体"); + setTitle(CodePinsBundle.message("social.share.dialog.title")); setSize(600, 600); // 增加对话框尺寸,确保有足够的空间 setResizable(true); // 允许用户调整大小 init(); @@ -66,7 +64,7 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari // 创建平台选择面板 JPanel platformPanel = new JPanel(new BorderLayout()); - platformPanel.setBorder(BorderFactory.createTitledBorder("选择平台")); + platformPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("social.share.platform"))); platformPanel.setBorder(BorderFactory.createCompoundBorder( platformPanel.getBorder(), JBUI.Borders.empty(5, 5, 5, 5))); // 添加内边距 @@ -79,7 +77,7 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari // 创建平台面板 JPanel platformsPanel = new JPanel(new GridLayout(0, 3, 10, 5)); // 3列网格 - platformsPanel.setBorder(BorderFactory.createTitledBorder("分享平台")); + platformsPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("social.share.platform.list"))); // 添加平台选项 int firstPlatformIndex = -1; @@ -115,14 +113,14 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari // 创建链接选项面板 JPanel linkPanel = new JPanel(new BorderLayout(0, 5)); // 减小垂直间距 - linkPanel.setBorder(BorderFactory.createTitledBorder("链接选项")); + linkPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("social.share.link.options"))); linkPanel.setBorder(BorderFactory.createCompoundBorder( linkPanel.getBorder(), JBUI.Borders.empty(5, 5, 5, 5))); // 添加内边距 // 过期时间选择 JPanel expirationPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - expirationPanel.add(new JBLabel("链接有效期:")); + expirationPanel.add(new JBLabel(CodePinsBundle.message("social.share.link.expiration") + ":")); expirationComboBox = new JComboBox<>(ShareLinkGenerator.ExpirationTime.values()); expirationComboBox.setSelectedItem(ShareLinkGenerator.ExpirationTime.ONE_DAY); @@ -130,7 +128,7 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari // 禁用过期时间选择,标记为未来开发功能 expirationComboBox.setEnabled(false); - expirationPanel.add(new JBLabel(" (长期有效,限制功能将在未来版本中提供)")); + expirationPanel.add(new JBLabel(" " + CodePinsBundle.message("social.share.link.expiration.future"))); // 插件现在完全免费,移除升级对话框点击事件 @@ -138,7 +136,7 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari // 密码保护选项 passwordPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - passwordCheckBox = new JCheckBox("密码保护:"); + passwordCheckBox = new JCheckBox(CodePinsBundle.message("social.share.password.protection") + ":"); passwordField = new JPasswordField(15); passwordField.setEnabled(false); @@ -150,7 +148,7 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari // 禁用密码保护选项,标记为未来开发功能 passwordCheckBox.setEnabled(false); passwordField.setEnabled(false); - passwordPanel.add(new JBLabel(" (未来功能)")); + passwordPanel.add(new JBLabel(" " + CodePinsBundle.message("social.share.password.future"))); // 插件现在完全免费,移除升级对话框点击事件 @@ -158,7 +156,7 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari // 创建信息面板 JPanel infoPanel = new JPanel(new BorderLayout()); - infoPanel.setBorder(BorderFactory.createTitledBorder("分享信息")); + infoPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("social.share.info"))); infoPanel.setBorder(BorderFactory.createCompoundBorder( infoPanel.getBorder(), JBUI.Borders.empty(5, 5, 5, 5))); // 添加内边距 @@ -169,9 +167,8 @@ public SocialShareDialog(Project project, List pins, SharingUtil.Shari infoArea.setLineWrap(true); infoArea.setWrapStyleWord(true); infoArea.setBackground(JBColor.background()); - infoArea.setText("将分享 " + pins.size() + " 个图钉到社交媒体。\n\n" + - "注意:分享链接使用GitHub Gist服务存储,链接长期有效。\n" + - "自定义过期时间和密码保护功能将在未来版本中提供。"); + // 确保正确传递参数以显示图钉数量 + infoArea.setText(CodePinsBundle.message("social.share.info.text", pins.size())); // 设置固定高度,避免文本区域过大 JScrollPane infoScrollPane = new JScrollPane(infoArea); @@ -224,8 +221,8 @@ protected void doOKAction() { if (selectedPlatform == null) { Messages.showErrorDialog( project, - "请选择一个社交媒体平台", - "分享错误" + CodePinsBundle.message("social.share.error.select.platform"), + CodePinsBundle.message("social.share.error.title") ); return; } @@ -242,11 +239,11 @@ protected void doOKAction() { project, pins, format, false, true, expiration, requiresPassword, password); // 分享到社交媒体 - String title = "CodePins分享"; + String title; if (pins.size() == 1 && pins.get(0).name != null && !pins.get(0).name.trim().isEmpty()) { - title += ": " + pins.get(0).name; + title = CodePinsBundle.message("social.share.title.single", pins.get(0).name); } else { - title += ": " + pins.size() + "个图钉"; + title = CodePinsBundle.message("social.share.title.multiple", pins.size()); } // 确保链接URL不为空 diff --git a/src/main/java/cn/ilikexff/codepins/ui/TagEditorDialog.java b/src/main/java/cn/ilikexff/codepins/ui/TagEditorDialog.java index e850e8e..3d89136 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/TagEditorDialog.java +++ b/src/main/java/cn/ilikexff/codepins/ui/TagEditorDialog.java @@ -2,6 +2,7 @@ import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.IconLoader; @@ -62,7 +63,7 @@ public TagEditorDialog(Project project, PinEntry pinEntry) { suggestionsList.setCellRenderer(new TagCellRenderer()); // 设置对话框标题和尺寸 - setTitle("编辑标签"); + setTitle(CodePinsBundle.message("tag.editor.title")); setSize(500, 500); // 增大对话框尺寸 init(); } @@ -81,14 +82,14 @@ public TagEditorDialog(Project project, PinEntry pinEntry) { JBUI.Borders.empty(5, 5, 10, 5) )); - JLabel instructionTitle = new JLabel("标签使用说明"); + JLabel instructionTitle = new JLabel(CodePinsBundle.message("tag.editor.instruction.title")); instructionTitle.setFont(instructionTitle.getFont().deriveFont(Font.BOLD)); instructionTitle.setAlignmentX(Component.LEFT_ALIGNMENT); - JLabel instructionText = new JLabel("• 在下方输入框中输入标签名称,然后点击“添加”按钮"); + JLabel instructionText = new JLabel(CodePinsBundle.message("tag.editor.instruction.1")); instructionText.setAlignmentX(Component.LEFT_ALIGNMENT); - JLabel instructionText2 = new JLabel("• 双击标签列表中的标签可删除该标签"); + JLabel instructionText2 = new JLabel(CodePinsBundle.message("tag.editor.instruction.2")); instructionText2.setAlignmentX(Component.LEFT_ALIGNMENT); instructionPanel.add(instructionTitle); @@ -101,7 +102,7 @@ public TagEditorDialog(Project project, PinEntry pinEntry) { JPanel currentTagsPanel = new JPanel(new BorderLayout()); currentTagsPanel.setBorder(JBUI.Borders.empty(10, 5, 5, 5)); - JLabel currentTagsLabel = new JLabel("当前标签:"); + JLabel currentTagsLabel = new JLabel(CodePinsBundle.message("tag.editor.current")); currentTagsLabel.setFont(currentTagsLabel.getFont().deriveFont(Font.BOLD)); currentTagsPanel.add(currentTagsLabel, BorderLayout.NORTH); @@ -111,7 +112,7 @@ public TagEditorDialog(Project project, PinEntry pinEntry) { // 添加删除按钮 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - JButton removeButton = new JButton("删除选中标签"); + JButton removeButton = new JButton(CodePinsBundle.message("tag.editor.delete")); removeButton.setIcon(IconLoader.getIcon("/icons/trash.svg", getClass())); removeButton.addActionListener(e -> removeSelectedTags()); buttonPanel.add(removeButton); @@ -124,7 +125,7 @@ public TagEditorDialog(Project project, PinEntry pinEntry) { JBUI.Borders.empty(10, 5, 5, 5) )); - JLabel newTagLabel = new JLabel("添加新标签:"); + JLabel newTagLabel = new JLabel(CodePinsBundle.message("tag.editor.add.new")); newTagLabel.setFont(newTagLabel.getFont().deriveFont(Font.BOLD)); newTagPanel.add(newTagLabel, BorderLayout.NORTH); @@ -133,10 +134,10 @@ public TagEditorDialog(Project project, PinEntry pinEntry) { inputPanel.setBorder(JBUI.Borders.empty(5, 0, 5, 0)); // 设置输入框的提示文本 - newTagField.putClientProperty("JTextField.placeholderText", "输入标签名称,回车或点击添加按钮"); + newTagField.putClientProperty("JTextField.placeholderText", CodePinsBundle.message("tag.editor.add.placeholder")); inputPanel.add(newTagField, BorderLayout.CENTER); - JButton addButton = new JButton("添加"); + JButton addButton = new JButton(CodePinsBundle.message("tag.editor.add.button")); addButton.setIcon(IconLoader.getIcon("/icons/plus.svg", getClass())); addButton.addActionListener(e -> addNewTag()); inputPanel.add(addButton, BorderLayout.EAST); @@ -145,7 +146,7 @@ public TagEditorDialog(Project project, PinEntry pinEntry) { // 建议面板 suggestionsPanel.setBorder(JBUI.Borders.empty(5)); - suggestionsPanel.add(new JLabel("建议标签:"), BorderLayout.NORTH); + suggestionsPanel.add(new JLabel(CodePinsBundle.message("tag.editor.suggestions")), BorderLayout.NORTH); JBScrollPane suggestionsScrollPane = new JBScrollPane(suggestionsList); suggestionsScrollPane.setPreferredSize(new Dimension(380, 100)); diff --git a/src/main/java/cn/ilikexff/codepins/ui/TagFilterPanel.java b/src/main/java/cn/ilikexff/codepins/ui/TagFilterPanel.java index fe857b9..0921161 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/TagFilterPanel.java +++ b/src/main/java/cn/ilikexff/codepins/ui/TagFilterPanel.java @@ -2,12 +2,13 @@ import cn.ilikexff.codepins.core.PinEntry; import cn.ilikexff.codepins.core.PinStorage; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.services.LicenseService; import cn.ilikexff.codepins.utils.IconUtil; import com.intellij.ui.JBColor; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBPanel; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; -import cn.ilikexff.codepins.ui.AnimationUtil; import javax.swing.*; import java.awt.*; @@ -48,14 +49,14 @@ public TagFilterPanel(Consumer> onTagSelectionChanged) { titlePanel.setOpaque(false); JLabel iconLabel = new JLabel(IconUtil.loadIcon("/icons/filter.svg", getClass())); - JLabel titleLabel = new JLabel("标签筛选"); + JLabel titleLabel = new JLabel(CodePinsBundle.message("tag.filter.panel")); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 13f)); titlePanel.add(iconLabel); titlePanel.add(titleLabel); // 清除按钮 - JButton clearButton = new JButton("清除筛选"); + JButton clearButton = new JButton(CodePinsBundle.message("tag.clear.filter")); clearButton.setFont(clearButton.getFont().deriveFont(11f)); clearButton.setBorder(JBUI.Borders.empty(2, 8)); clearButton.setFocusPainted(false); @@ -68,7 +69,7 @@ public TagFilterPanel(Consumer> onTagSelectionChanged) { }); // 添加新建标签按钮 - JButton addTagButton = new JButton("新建标签"); + JButton addTagButton = new JButton(CodePinsBundle.message("tag.create")); addTagButton.setFont(addTagButton.getFont().deriveFont(11f)); addTagButton.setBorder(JBUI.Borders.empty(2, 8)); addTagButton.setFocusPainted(false); @@ -148,7 +149,7 @@ public void refreshTagsView() { Set allTags = PinStorage.getAllTags(); if (allTags.isEmpty()) { - JLabel emptyLabel = new JLabel("暂无标签"); + JLabel emptyLabel = new JLabel(CodePinsBundle.message("tag.empty")); emptyLabel.setForeground(JBColor.GRAY); tagsContainer.add(emptyLabel); } else { @@ -481,7 +482,7 @@ private void openAddTagDialog() { dummyPin ); - dialog.setTitle("添加自定义标签"); + dialog.setTitle(CodePinsBundle.message("tag.create.custom")); if (dialog.showAndGet()) { List newTags = dialog.getTags(); diff --git a/src/main/java/cn/ilikexff/codepins/ui/WatermarkSettingsDialog.java b/src/main/java/cn/ilikexff/codepins/ui/WatermarkSettingsDialog.java index 8d3e4fa..b21f707 100644 --- a/src/main/java/cn/ilikexff/codepins/ui/WatermarkSettingsDialog.java +++ b/src/main/java/cn/ilikexff/codepins/ui/WatermarkSettingsDialog.java @@ -1,9 +1,8 @@ package cn.ilikexff.codepins.ui; -import cn.ilikexff.codepins.services.LicenseService; +import cn.ilikexff.codepins.i18n.CodePinsBundle; import cn.ilikexff.codepins.utils.SocialSharingUtil; import cn.ilikexff.codepins.utils.WatermarkManager; -import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; @@ -43,7 +42,7 @@ public WatermarkSettingsDialog(Project project) { super(project); this.project = project; - setTitle("水印设置"); + setTitle(CodePinsBundle.message("watermark.dialog.title")); setSize(500, 400); init(); } @@ -55,11 +54,11 @@ public WatermarkSettingsDialog(Project project) { // 创建水印类型选择面板 JPanel typePanel = new JPanel(new GridLayout(0, 1)); - typePanel.setBorder(BorderFactory.createTitledBorder("水印类型")); + typePanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("watermark.type"))); // 水印类型选项 - textWatermarkRadio = new JBRadioButton("文本水印"); - noWatermarkRadio = new JBRadioButton("无水印"); + textWatermarkRadio = new JBRadioButton(CodePinsBundle.message("watermark.type.text")); + noWatermarkRadio = new JBRadioButton(CodePinsBundle.message("watermark.type.none")); // 默认选择文本水印 textWatermarkRadio.setSelected(true); @@ -75,27 +74,27 @@ public WatermarkSettingsDialog(Project project) { // 创建文本水印设置面板 textPanel = new JPanel(new GridLayout(0, 2, 10, 10)); - textPanel.setBorder(BorderFactory.createTitledBorder("文本水印设置")); + textPanel.setBorder(BorderFactory.createTitledBorder(CodePinsBundle.message("watermark.text.settings"))); // 文本输入 - textPanel.add(new JBLabel("水印文本:")); + textPanel.add(new JBLabel(CodePinsBundle.message("watermark.text") + ":")); textField = new JTextField("Generated by CodePins - Code Bookmarks"); textPanel.add(textField); // 位置选择 - textPanel.add(new JBLabel("水印位置:")); + textPanel.add(new JBLabel(CodePinsBundle.message("watermark.position") + ":")); positionComboBox = new JComboBox<>(WatermarkManager.WatermarkPosition.values()); positionComboBox.setSelectedItem(WatermarkManager.WatermarkPosition.BOTTOM_RIGHT); textPanel.add(positionComboBox); // 颜色选择 - textPanel.add(new JBLabel("水印颜色:")); + textPanel.add(new JBLabel(CodePinsBundle.message("watermark.color") + ":")); colorPanel = new ColorPanel(); colorPanel.setSelectedColor(new Color(128, 128, 128, 128)); textPanel.add(colorPanel); // 透明度滑块 - textPanel.add(new JBLabel("透明度:")); + textPanel.add(new JBLabel(CodePinsBundle.message("watermark.opacity") + ":")); opacitySlider = new JSlider(0, 100, 50); opacitySlider.setMajorTickSpacing(25); opacitySlider.setMinorTickSpacing(5); @@ -130,15 +129,16 @@ protected void doOKAction() { // 检查是否为付费用户 boolean isPremium = SocialSharingUtil.isPremiumUser(); - // 获取水印类型 - WatermarkManager.WatermarkType type; + // 获取水印类型并保存设置 if (textWatermarkRadio.isSelected()) { - type = WatermarkManager.WatermarkType.TEXT; + // 使用文本水印 + // 这里可以添加保存文本水印设置的代码 } else if (noWatermarkRadio.isSelected() && isPremium) { - type = WatermarkManager.WatermarkType.NONE; + // 无水印 + // 这里可以添加保存无水印设置的代码 } else { // 默认使用文本水印 - type = WatermarkManager.WatermarkType.TEXT; + // 使用默认设置 } // 插件现在完全免费,移除付费功能检查 @@ -149,8 +149,8 @@ protected void doOKAction() { } catch (Exception e) { Messages.showErrorDialog( project, - "保存水印设置失败: " + e.getMessage(), - "设置错误" + CodePinsBundle.message("watermark.save.error", e.getMessage()), + CodePinsBundle.message("watermark.error.title") ); } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 44d1614..cd56079 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -1,7 +1,7 @@ cn.ilikexff.codepins - CodePins - Code Bookmarks + ilikexff 2.0.0 @@ -137,34 +137,44 @@ + - + - + - + - + - + + + + + + + - + - + - + - + - + - + - - + + diff --git a/src/main/resources/icons/codepins_icon.svg b/src/main/resources/icons/codepins_icon.svg deleted file mode 100644 index 49c1655..0000000 Binary files a/src/main/resources/icons/codepins_icon.svg and /dev/null differ diff --git a/src/main/resources/icons/search.svg b/src/main/resources/icons/search.svg index 5618561..ca5d811 100644 --- a/src/main/resources/icons/search.svg +++ b/src/main/resources/icons/search.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/src/main/resources/messages/CodePinsBundle.properties b/src/main/resources/messages/CodePinsBundle.properties index e1fef47..1fbb580 100644 --- a/src/main/resources/messages/CodePinsBundle.properties +++ b/src/main/resources/messages/CodePinsBundle.properties @@ -1,5 +1,317 @@ +# Tooltip texts tooltip.path=Path tooltip.line=Line tooltip.note=Note tooltip.time=Created At -tooltip.author=Author \ No newline at end of file +tooltip.author=Author + +# UI texts - General +ui.app.name=CodePins +ui.app.description=Modern Code Bookmarks Solution +plugin.name=CodePins - Code Bookmarks + +# UI texts - Buttons +button.add=Add +button.edit=Edit +button.delete=Delete +button.cancel=Cancel +button.ok=OK +button.apply=Apply +button.save=Save +button.close=Close +button.refresh=Refresh +button.import=Import +button.export=Export +button.clear=Clear All + +# UI texts - Pin related +pin.add=Add CodePin Here +pin.remove=Remove CodePin +pin.edit=Edit CodePin +pin.block=Code Block Pin +pin.single=Single Line Pin +pin.added=Pin added at line {0} +pin.removed=Pin removed +pin.invalid=Invalid pin +pin.type.block=[Block] +pin.type.single=[Single] + +# UI texts - Tags +tag.add=Add Tag +tag.edit=Edit Tags +tag.remove=Remove Tag +tag.filter=Filter by Tags +tag.clear=Clear Tags +tag.filter.panel=Tag Filter +tag.clear.filter=Clear Filter +tag.create=Create Tag +tag.create.custom=Add Custom Tag +tag.current.label=Current Tags: +tag.refresh=Refresh +tag.current=Current Tags +tag.empty=No Tags + +# UI texts - Tag Editor Dialog +tag.editor.title=Edit Tags +tag.editor.instruction.title=Tag Usage Instructions +tag.editor.instruction.1=1. Enter tag name in the input field below, then press Enter or click the Add button +tag.editor.instruction.2=2. Select a tag in the list, then click the Delete button to remove it +tag.editor.instruction.3=3. Select a tag in the list, then click the Edit button or double-click the tag to edit it +tag.editor.instruction.4=4. No tag limit, the plugin is completely free and open source +tag.editor.current=Current Tags: +tag.editor.delete=Delete Selected Tag +tag.editor.add.new=Add New Tag: +tag.editor.add.placeholder=Enter tag name, press Enter or click Add button +tag.editor.add.button=Add +tag.editor.suggestions=Suggested Tags: + +# UI texts - Import/Export +import.dialog.title=Import Pins +import.dialog.description=Import pins from a file +import.dialog.header=Import Pin Data +import.dialog.instruction=Select a file to import and set import options. +import.mode=Import Mode +import.mode.merge=Merge with existing pins +import.mode.replace=Replace all existing pins +import.file=Import File +import.file.select=Select File +import.file.selected=Selected file: {0} +import.file.none=No file selected +import.file.label=Import File +import.options.label=Import Options +import.warning=Warning: +import.warning.desc=Import operation may overwrite existing pins. Please ensure you have backed up important data. +import.file.chooser.title=Select Import File +import.file.chooser.desc=Select JSON file to import +import.success=Successfully imported {0} pins. +import.success.title=Import Successful + +export.dialog.title=Export Pins +export.dialog.description=Export pins to a file +export.mode=Export Mode +export.mode.all=Export all pins +export.mode.selected=Export selected pins +export.file.select=Select Export Location +export.success=Successfully exported {0} pins to file: +{1} +export.success.title=Export Successful +export.description.html=You can choose to export all pins or only selected pins.
To select specific pins, choose the 'Export selected pins' option, then select the pins to export from the list below. +export.pins.list=Available Pins +export.no.pins=No pins to export. Please select at least one pin for export. +export.error=Export Error + +# UI texts - Notes +note.edit=Edit Note +note.placeholder=Enter note here... +note.add.dialog.title=Add Pin +note.add.dialog.message=Enter pin note (optional): + +# UI texts - Tooltip +tooltip.path=Path +tooltip.line=Line +tooltip.note=Note +tooltip.createdAt=Created At +tooltip.author=Author +tooltip.blockPin=📌 Code Block Pin +tooltip.linePin=📌 Line Pin +tooltip.searchPlaceholder=Search pins (note and path) + +# UI texts - Settings +settings.title=CodePins Settings +settings.general=General Settings +settings.general.preview.height=Preview Window Height: +settings.general.confirm.delete=Confirm when deleting pins + +settings.pin.add=Pin Addition Settings +settings.pin.add.show.note.dialog=Show note and tag dialog when adding pins via quick action +settings.pin.add.show.note.dialog.desc=When enabled, a dialog for entering notes and tags will be shown when adding pins using the text selection floating button + +settings.comment=Comment Directive Settings +settings.comment.show.note.dialog=Show note and tag dialog when adding pins via comment directive +settings.comment.show.note.dialog.desc=When enabled, a dialog for entering notes and tags will be shown when adding pins using comment directives +settings.comment.auto.add.tag=Automatically add "Quick Add" tag +settings.comment.auto.add.tag.desc=When enabled, a "Quick Add" tag will be automatically added when adding pins using comment directives +settings.comment.use.completion.symbol=Use completion symbol +settings.comment.completion.symbol=Completion Symbol: +settings.comment.completion.symbol.desc=When enabled, pin addition will only be triggered when the completion symbol is entered after the comment directive, preventing premature triggering due to auto-save + +settings.shortcuts=Keyboard Shortcuts +settings.shortcuts.info=CodePins provides the following default shortcuts:\n\n- Add Pin: Alt+Shift+P\n- Navigate to Next Pin: Alt+Shift+Right\n- Navigate to Previous Pin: Alt+Shift+Left\n- Toggle Pin Tool Window: Alt+Shift+T\n\nYou can customize these shortcuts in the IDE's 'Settings > Keymap'.\nSearch for "CodePins" to find all related actions. +settings.shortcuts.open=Open Keymap Settings + +settings.support=Support Development +settings.support.free=✓ CodePins is now completely free and open source! +settings.support.thanks=Thank you for using CodePins! If this plugin has been helpful to you, +please consider supporting our development in the following ways: +settings.support.contribute=🤝 Welcome to join open source contribution! +settings.support.contribute.desc=We sincerely invite you to maintain this open source project together:\n• 🐛 Report bugs and suggest improvements\n• 💡 Contribute new features and code optimizations\n• 📖 Improve documentation and user guides\n• 🌍 Help translate to more languages\n• 📢 Recommend CodePins to other developers +settings.support.github=⭐ GitHub +settings.support.issue=🐛 Report Issue +settings.support.donate=☕ Buy Me a Coffee +settings.support.contribute.button=👍 Contribute +settings.general=General Settings +settings.appearance=Appearance +settings.behavior=Behavior +settings.language=Language +settings.language.system=System Default +settings.language.english=English +settings.language.chinese=Chinese +settings.language.change.title=Language Changed +settings.language.change.message=Please restart IDE to apply the new language setting. + +# UI texts - Dialogs +dialog.confirm.title=Confirmation +dialog.confirm.delete=Are you sure you want to delete this pin? +dialog.confirm.clear=Are you sure you want to clear all pins? + +# UI texts - Notifications +notification.success=Success +notification.error=Error +notification.warning=Warning +notification.info=Information + +# UI texts - Search +search.placeholder=Search pins... +search.no.results=No results found + +# UI texts - Status +status.free=You are using CodePins Free Open Source Edition + +# UI texts - Empty State +empty.title=No CodePins Added Yet +empty.description=CodePins help you mark important code locations and quickly return to them anytime.\n\nRight-click on a code line in the editor and select "Add CodePin Here" to add a pin. +empty.help.button=View Help + +# UI texts - Context Menu +context.view.code=View Code Block +context.edit.note=Edit Note +context.edit.tags=Edit Tags +context.copy.pin=Copy Pin +context.delete.pin=Delete Pin +context.share.pin=Share Pin + +# UI texts - Share Dialog +share.dialog.title=Share Pin +share.format=Share Format +share.format.markdown=Markdown +share.format.html=HTML +share.format.json=JSON +share.format.code.only=Code Only +share.format.image=Image +share.format.svg=SVG +share.method=Share Method +share.method.clipboard=Copy to Clipboard +share.method.file=Export to File +share.exclude.metadata=Exclude Metadata (not showing data) +share.show.line.numbers=Show Line Numbers +share.info=Will share {0} pin(s) +share.social=Social Share +share.watermark=Watermark Settings + +# UI texts - Watermark Settings Dialog +watermark.dialog.title=Watermark Settings +watermark.type=Watermark Type +watermark.type.text=Text Watermark +watermark.type.none=No Watermark +watermark.text.settings=Text Watermark Settings +watermark.text=Watermark Text +watermark.position=Watermark Position +watermark.color=Watermark Color +watermark.opacity=Opacity +watermark.save.error=Failed to save watermark settings: {0} +watermark.error.title=Settings Error + +# UI texts - Social Share Dialog +social.share.dialog.title=Share to Social Media +social.share.platform=Select Platform +social.share.platform.list=Share Platform +social.share.link.options=Link Options +social.share.link.expiration=Link Expiration +social.share.link.expiration.future=(Long-term valid, limitation features will be available in future versions) +social.share.password.protection=Password Protection +social.share.password.future=(Future feature) +social.share.info=Share Information +social.share.info.text=Will share {0} pin(s) to social media.\n\nNote: Shared links use GitHub Gist service for storage and are valid long-term.\nCustom expiration time and password protection features will be available in future versions. +social.share.error.select.platform=Please select a social media platform +social.share.error.title=Share Error +social.share.title=CodePins Share +social.share.title.single=CodePins Share: {0} +social.share.title.multiple=CodePins Share: {0} pins + +# UI texts - Toolbar +toolbar.sort=Sort Pins +toolbar.sort.time.desc=By Creation Time (New → Old) +toolbar.sort.time.asc=By Creation Time (Old → New) +toolbar.sort.name.asc=By Name (A → Z) +toolbar.sort.name.desc=By Name (Z → A) +toolbar.sort.path.asc=By File Path (A → Z) +toolbar.sort.path.desc=By File Path (Z → A) +toolbar.share=Share Pins +toolbar.share.desc=Share selected pins +toolbar.delete.multiple=Delete Multiple +toolbar.delete.multiple.desc=Delete selected pins +toolbar.select.prompt=Please select pins first +toolbar.confirm.delete=Are you sure you want to delete {0} selected pins? +toolbar.confirm.delete.title=Confirm Deletion +toolbar.pin.count={0} pins + +# UI texts - Comments +comment.marker.detected=Comment marker detected +comment.marker.processed=Comment marker processed +comment.detection.title=CodePins Comment Detection +comment.detection.scanning=Scanning file for comment markers... +comment.detection.completed=Comment marker detection completed +pin.exists.title=CodePins Pin Exists +pin.exists.range=A pin already exists in this range, not adding duplicate +pin.prepare.title=CodePins Preparing Pin +pin.created.title=CodePins Pin Created +pin.created.success=Pin successfully created: {0} +pin.confirm.note=Confirm or modify pin note: +pin.added.title=CodePins Pin Added +pin.added.from.comment=Automatically added pin from comment marker: {0} +pin.creating.title=CodePins Creating Pin +pin.creating.message=Creating pin... +pin.create.failed.title=CodePins Pin Creation Failed +pin.create.failed.message=Error creating pin: {0} +tag.quick.add=Quick Add +debug.file=File: {0} +debug.offset=Offset: {0}-{1} +debug.note=Note: {0} +debug.is.block=Is Block: {0} + +# UI texts - Actions +action.add.pin=Add CodePin Here +action.add.pin.description=Pin current line for later reference +action.add.pin.cursor=Add Pin at Cursor +action.add.pin.cursor.description=Add a pin at the current cursor position +action.navigate.next=Navigate to Next Pin +action.navigate.next.description=Navigate to the next pin +action.navigate.prev=Navigate to Previous Pin +action.navigate.prev.description=Navigate to the previous pin +action.toggle.window=Toggle CodePins Tool Window +action.toggle.window.description=Show or hide the CodePins tool window +action.detect.markers=Detect Comment Markers +action.detect.markers.description=Detect @pin and @pin-block markers in comments +action.group.name=CodePins +action.group.description=CodePins actions +action.add.pin.title=Add CodePin +action.add.pin.note.prompt=Enter pin note (optional): +action.add.pin.failed.title=Add CodePin Failed +status.pin.added=✅ Pin added +status.pin.add.failed=❌ Pin add failed +pin.add.failed.retry=Failed to add pin, please try again later + +# Tag Editor Dialog +tag.dialog.title=Edit Tags +tag.dialog.instruction=Tag Usage Instructions:
1. Enter tag name in the input field below, then press Enter or click the Add button
2. Select a tag in the list, then click the Delete button to remove it
3. Select a tag in the list, then click the Edit button or double-click the tag to edit it
4. No tag limit, the plugin is completely free and open source +tag.dialog.add=Add +tag.dialog.edit=Edit +tag.dialog.delete=Delete +tag.dialog.existing=Existing Tags: +tag.dialog.new=New Tag: +tag.dialog.input.placeholder=Enter tag name here... +tag.dialog.exists=Tag '{0}' already exists +tag.dialog.exists.title=Duplicate Tag +tag.dialog.empty=No existing tags +tag.dialog.all=All tags have been added +tag.dialog.current=Current tags (click to add): \ No newline at end of file diff --git a/src/main/resources/messages/CodePinsBundle_en.properties b/src/main/resources/messages/CodePinsBundle_en.properties new file mode 100644 index 0000000..1fbb580 --- /dev/null +++ b/src/main/resources/messages/CodePinsBundle_en.properties @@ -0,0 +1,317 @@ +# Tooltip texts +tooltip.path=Path +tooltip.line=Line +tooltip.note=Note +tooltip.time=Created At +tooltip.author=Author + +# UI texts - General +ui.app.name=CodePins +ui.app.description=Modern Code Bookmarks Solution +plugin.name=CodePins - Code Bookmarks + +# UI texts - Buttons +button.add=Add +button.edit=Edit +button.delete=Delete +button.cancel=Cancel +button.ok=OK +button.apply=Apply +button.save=Save +button.close=Close +button.refresh=Refresh +button.import=Import +button.export=Export +button.clear=Clear All + +# UI texts - Pin related +pin.add=Add CodePin Here +pin.remove=Remove CodePin +pin.edit=Edit CodePin +pin.block=Code Block Pin +pin.single=Single Line Pin +pin.added=Pin added at line {0} +pin.removed=Pin removed +pin.invalid=Invalid pin +pin.type.block=[Block] +pin.type.single=[Single] + +# UI texts - Tags +tag.add=Add Tag +tag.edit=Edit Tags +tag.remove=Remove Tag +tag.filter=Filter by Tags +tag.clear=Clear Tags +tag.filter.panel=Tag Filter +tag.clear.filter=Clear Filter +tag.create=Create Tag +tag.create.custom=Add Custom Tag +tag.current.label=Current Tags: +tag.refresh=Refresh +tag.current=Current Tags +tag.empty=No Tags + +# UI texts - Tag Editor Dialog +tag.editor.title=Edit Tags +tag.editor.instruction.title=Tag Usage Instructions +tag.editor.instruction.1=1. Enter tag name in the input field below, then press Enter or click the Add button +tag.editor.instruction.2=2. Select a tag in the list, then click the Delete button to remove it +tag.editor.instruction.3=3. Select a tag in the list, then click the Edit button or double-click the tag to edit it +tag.editor.instruction.4=4. No tag limit, the plugin is completely free and open source +tag.editor.current=Current Tags: +tag.editor.delete=Delete Selected Tag +tag.editor.add.new=Add New Tag: +tag.editor.add.placeholder=Enter tag name, press Enter or click Add button +tag.editor.add.button=Add +tag.editor.suggestions=Suggested Tags: + +# UI texts - Import/Export +import.dialog.title=Import Pins +import.dialog.description=Import pins from a file +import.dialog.header=Import Pin Data +import.dialog.instruction=Select a file to import and set import options. +import.mode=Import Mode +import.mode.merge=Merge with existing pins +import.mode.replace=Replace all existing pins +import.file=Import File +import.file.select=Select File +import.file.selected=Selected file: {0} +import.file.none=No file selected +import.file.label=Import File +import.options.label=Import Options +import.warning=Warning: +import.warning.desc=Import operation may overwrite existing pins. Please ensure you have backed up important data. +import.file.chooser.title=Select Import File +import.file.chooser.desc=Select JSON file to import +import.success=Successfully imported {0} pins. +import.success.title=Import Successful + +export.dialog.title=Export Pins +export.dialog.description=Export pins to a file +export.mode=Export Mode +export.mode.all=Export all pins +export.mode.selected=Export selected pins +export.file.select=Select Export Location +export.success=Successfully exported {0} pins to file: +{1} +export.success.title=Export Successful +export.description.html=You can choose to export all pins or only selected pins.
To select specific pins, choose the 'Export selected pins' option, then select the pins to export from the list below. +export.pins.list=Available Pins +export.no.pins=No pins to export. Please select at least one pin for export. +export.error=Export Error + +# UI texts - Notes +note.edit=Edit Note +note.placeholder=Enter note here... +note.add.dialog.title=Add Pin +note.add.dialog.message=Enter pin note (optional): + +# UI texts - Tooltip +tooltip.path=Path +tooltip.line=Line +tooltip.note=Note +tooltip.createdAt=Created At +tooltip.author=Author +tooltip.blockPin=📌 Code Block Pin +tooltip.linePin=📌 Line Pin +tooltip.searchPlaceholder=Search pins (note and path) + +# UI texts - Settings +settings.title=CodePins Settings +settings.general=General Settings +settings.general.preview.height=Preview Window Height: +settings.general.confirm.delete=Confirm when deleting pins + +settings.pin.add=Pin Addition Settings +settings.pin.add.show.note.dialog=Show note and tag dialog when adding pins via quick action +settings.pin.add.show.note.dialog.desc=When enabled, a dialog for entering notes and tags will be shown when adding pins using the text selection floating button + +settings.comment=Comment Directive Settings +settings.comment.show.note.dialog=Show note and tag dialog when adding pins via comment directive +settings.comment.show.note.dialog.desc=When enabled, a dialog for entering notes and tags will be shown when adding pins using comment directives +settings.comment.auto.add.tag=Automatically add "Quick Add" tag +settings.comment.auto.add.tag.desc=When enabled, a "Quick Add" tag will be automatically added when adding pins using comment directives +settings.comment.use.completion.symbol=Use completion symbol +settings.comment.completion.symbol=Completion Symbol: +settings.comment.completion.symbol.desc=When enabled, pin addition will only be triggered when the completion symbol is entered after the comment directive, preventing premature triggering due to auto-save + +settings.shortcuts=Keyboard Shortcuts +settings.shortcuts.info=CodePins provides the following default shortcuts:\n\n- Add Pin: Alt+Shift+P\n- Navigate to Next Pin: Alt+Shift+Right\n- Navigate to Previous Pin: Alt+Shift+Left\n- Toggle Pin Tool Window: Alt+Shift+T\n\nYou can customize these shortcuts in the IDE's 'Settings > Keymap'.\nSearch for "CodePins" to find all related actions. +settings.shortcuts.open=Open Keymap Settings + +settings.support=Support Development +settings.support.free=✓ CodePins is now completely free and open source! +settings.support.thanks=Thank you for using CodePins! If this plugin has been helpful to you, +please consider supporting our development in the following ways: +settings.support.contribute=🤝 Welcome to join open source contribution! +settings.support.contribute.desc=We sincerely invite you to maintain this open source project together:\n• 🐛 Report bugs and suggest improvements\n• 💡 Contribute new features and code optimizations\n• 📖 Improve documentation and user guides\n• 🌍 Help translate to more languages\n• 📢 Recommend CodePins to other developers +settings.support.github=⭐ GitHub +settings.support.issue=🐛 Report Issue +settings.support.donate=☕ Buy Me a Coffee +settings.support.contribute.button=👍 Contribute +settings.general=General Settings +settings.appearance=Appearance +settings.behavior=Behavior +settings.language=Language +settings.language.system=System Default +settings.language.english=English +settings.language.chinese=Chinese +settings.language.change.title=Language Changed +settings.language.change.message=Please restart IDE to apply the new language setting. + +# UI texts - Dialogs +dialog.confirm.title=Confirmation +dialog.confirm.delete=Are you sure you want to delete this pin? +dialog.confirm.clear=Are you sure you want to clear all pins? + +# UI texts - Notifications +notification.success=Success +notification.error=Error +notification.warning=Warning +notification.info=Information + +# UI texts - Search +search.placeholder=Search pins... +search.no.results=No results found + +# UI texts - Status +status.free=You are using CodePins Free Open Source Edition + +# UI texts - Empty State +empty.title=No CodePins Added Yet +empty.description=CodePins help you mark important code locations and quickly return to them anytime.\n\nRight-click on a code line in the editor and select "Add CodePin Here" to add a pin. +empty.help.button=View Help + +# UI texts - Context Menu +context.view.code=View Code Block +context.edit.note=Edit Note +context.edit.tags=Edit Tags +context.copy.pin=Copy Pin +context.delete.pin=Delete Pin +context.share.pin=Share Pin + +# UI texts - Share Dialog +share.dialog.title=Share Pin +share.format=Share Format +share.format.markdown=Markdown +share.format.html=HTML +share.format.json=JSON +share.format.code.only=Code Only +share.format.image=Image +share.format.svg=SVG +share.method=Share Method +share.method.clipboard=Copy to Clipboard +share.method.file=Export to File +share.exclude.metadata=Exclude Metadata (not showing data) +share.show.line.numbers=Show Line Numbers +share.info=Will share {0} pin(s) +share.social=Social Share +share.watermark=Watermark Settings + +# UI texts - Watermark Settings Dialog +watermark.dialog.title=Watermark Settings +watermark.type=Watermark Type +watermark.type.text=Text Watermark +watermark.type.none=No Watermark +watermark.text.settings=Text Watermark Settings +watermark.text=Watermark Text +watermark.position=Watermark Position +watermark.color=Watermark Color +watermark.opacity=Opacity +watermark.save.error=Failed to save watermark settings: {0} +watermark.error.title=Settings Error + +# UI texts - Social Share Dialog +social.share.dialog.title=Share to Social Media +social.share.platform=Select Platform +social.share.platform.list=Share Platform +social.share.link.options=Link Options +social.share.link.expiration=Link Expiration +social.share.link.expiration.future=(Long-term valid, limitation features will be available in future versions) +social.share.password.protection=Password Protection +social.share.password.future=(Future feature) +social.share.info=Share Information +social.share.info.text=Will share {0} pin(s) to social media.\n\nNote: Shared links use GitHub Gist service for storage and are valid long-term.\nCustom expiration time and password protection features will be available in future versions. +social.share.error.select.platform=Please select a social media platform +social.share.error.title=Share Error +social.share.title=CodePins Share +social.share.title.single=CodePins Share: {0} +social.share.title.multiple=CodePins Share: {0} pins + +# UI texts - Toolbar +toolbar.sort=Sort Pins +toolbar.sort.time.desc=By Creation Time (New → Old) +toolbar.sort.time.asc=By Creation Time (Old → New) +toolbar.sort.name.asc=By Name (A → Z) +toolbar.sort.name.desc=By Name (Z → A) +toolbar.sort.path.asc=By File Path (A → Z) +toolbar.sort.path.desc=By File Path (Z → A) +toolbar.share=Share Pins +toolbar.share.desc=Share selected pins +toolbar.delete.multiple=Delete Multiple +toolbar.delete.multiple.desc=Delete selected pins +toolbar.select.prompt=Please select pins first +toolbar.confirm.delete=Are you sure you want to delete {0} selected pins? +toolbar.confirm.delete.title=Confirm Deletion +toolbar.pin.count={0} pins + +# UI texts - Comments +comment.marker.detected=Comment marker detected +comment.marker.processed=Comment marker processed +comment.detection.title=CodePins Comment Detection +comment.detection.scanning=Scanning file for comment markers... +comment.detection.completed=Comment marker detection completed +pin.exists.title=CodePins Pin Exists +pin.exists.range=A pin already exists in this range, not adding duplicate +pin.prepare.title=CodePins Preparing Pin +pin.created.title=CodePins Pin Created +pin.created.success=Pin successfully created: {0} +pin.confirm.note=Confirm or modify pin note: +pin.added.title=CodePins Pin Added +pin.added.from.comment=Automatically added pin from comment marker: {0} +pin.creating.title=CodePins Creating Pin +pin.creating.message=Creating pin... +pin.create.failed.title=CodePins Pin Creation Failed +pin.create.failed.message=Error creating pin: {0} +tag.quick.add=Quick Add +debug.file=File: {0} +debug.offset=Offset: {0}-{1} +debug.note=Note: {0} +debug.is.block=Is Block: {0} + +# UI texts - Actions +action.add.pin=Add CodePin Here +action.add.pin.description=Pin current line for later reference +action.add.pin.cursor=Add Pin at Cursor +action.add.pin.cursor.description=Add a pin at the current cursor position +action.navigate.next=Navigate to Next Pin +action.navigate.next.description=Navigate to the next pin +action.navigate.prev=Navigate to Previous Pin +action.navigate.prev.description=Navigate to the previous pin +action.toggle.window=Toggle CodePins Tool Window +action.toggle.window.description=Show or hide the CodePins tool window +action.detect.markers=Detect Comment Markers +action.detect.markers.description=Detect @pin and @pin-block markers in comments +action.group.name=CodePins +action.group.description=CodePins actions +action.add.pin.title=Add CodePin +action.add.pin.note.prompt=Enter pin note (optional): +action.add.pin.failed.title=Add CodePin Failed +status.pin.added=✅ Pin added +status.pin.add.failed=❌ Pin add failed +pin.add.failed.retry=Failed to add pin, please try again later + +# Tag Editor Dialog +tag.dialog.title=Edit Tags +tag.dialog.instruction=Tag Usage Instructions:
1. Enter tag name in the input field below, then press Enter or click the Add button
2. Select a tag in the list, then click the Delete button to remove it
3. Select a tag in the list, then click the Edit button or double-click the tag to edit it
4. No tag limit, the plugin is completely free and open source +tag.dialog.add=Add +tag.dialog.edit=Edit +tag.dialog.delete=Delete +tag.dialog.existing=Existing Tags: +tag.dialog.new=New Tag: +tag.dialog.input.placeholder=Enter tag name here... +tag.dialog.exists=Tag '{0}' already exists +tag.dialog.exists.title=Duplicate Tag +tag.dialog.empty=No existing tags +tag.dialog.all=All tags have been added +tag.dialog.current=Current tags (click to add): \ No newline at end of file diff --git a/src/main/resources/messages/CodePinsBundle_zh_CN.properties b/src/main/resources/messages/CodePinsBundle_zh_CN.properties index 94da8f5..46e94db 100644 --- a/src/main/resources/messages/CodePinsBundle_zh_CN.properties +++ b/src/main/resources/messages/CodePinsBundle_zh_CN.properties @@ -1,5 +1,327 @@ -tooltip.path=\u8DEF\u5F84 -tooltip.line=\u884C\u53F7 -tooltip.note=\u5907\u6CE8 -tooltip.time=\u521B\u5EFA\u65F6\u95F4 -tooltip.author=\u521B\u5EFA\u8005 \ No newline at end of file +# 工具提示文本 +tooltip.path=路径 +tooltip.line=行号 +tooltip.note=备注 +tooltip.time=创建时间 +tooltip.author=创建者 + +# UI 文本 - 通用 +ui.app.name=CodePins +ui.app.description=现代代码书签解决方案 +plugin.name=CodePins - 代码图钉 + +# UI 文本 - 按钮 +button.add=添加 +button.edit=编辑 +button.delete=删除 +button.cancel=取消 +button.ok=确定 +button.apply=应用 +button.save=保存 +button.close=关闭 +button.refresh=刷新 +button.import=导入 +button.export=导出 +button.clear=清除全部 + +# UI 文本 - 图钉相关 +pin.add=添加图钉 +pin.remove=移除图钉 +pin.edit=编辑图钉 +pin.block=代码块图钉 +pin.single=单行图钉 +pin.added=已添加图钉到第 {0} 行 +pin.removed=图钉已移除 +pin.invalid=无效图钉 +pin.type.block=[代码块] +pin.type.single=[单行] + +# UI 文本 - 标签 +tag.add=添加标签 +tag.edit=编辑标签 +tag.remove=移除标签 +tag.filter=按标签筛选 +tag.clear=清除标签 +tag.filter.panel=标签筛选 +tag.clear.filter=清除筛选 +tag.create=新建标签 +tag.create.custom=添加自定义标签 +tag.current.label=当前标签: +tag.refresh=刷新 +tag.current=当前标签 +tag.empty=暂无标签 + +# UI 文本 - 标签编辑对话框 +tag.editor.title=编辑标签 +tag.editor.instruction.title=标签使用说明 +tag.editor.instruction.1=1. 在下方输入框中输入标签名称,然后点击“添加”按钮 +tag.editor.instruction.2=2. 选择列表中的标签,然后点击“删除”按钮删除该标签 +tag.editor.instruction.3=3. 选择列表中的标签,然后点击“编辑”按钮或双击标签进行编辑 +tag.editor.instruction.4=4. 无标签限制,插件完全免费并开源 +tag.editor.current=当前标签: +tag.editor.delete=删除选中标签 +tag.editor.add.new=添加新标签: +tag.editor.add.placeholder=输入标签名称,回车或点击添加按钮 +tag.editor.add.button=添加 +tag.editor.suggestions=建议标签: + +# UI 文本 - 导入/导出 +import.dialog.title=导入图钉 +import.dialog.description=从文件导入图钉 +import.dialog.header=导入图钉数据 +import.dialog.instruction=选择要导入的文件,并设置导入选项。 +import.mode=导入模式 +import.mode.merge=与现有图钉合并 +import.mode.replace=替换所有现有图钉 +import.file=导入文件 +import.file.select=选择文件 +import.file.selected=已选择文件:{0} +import.file.none=未选择文件 +import.file.label=导入文件 +import.options.label=导入选项 +import.warning=注意: +import.warning.desc=导入操作可能会覆盖现有图钉。请确保您已备份重要数据。 +import.file.chooser.title=选择导入文件 +import.file.chooser.desc=选择要导入的 JSON 文件 +import.success=成功导入 {0} 个图钉。 +import.success.title=导入成功 + +export.dialog.title=导出图钉 +export.dialog.description=将图钉导出到文件 +export.mode=导出模式 +export.mode.all=导出所有图钉 +export.mode.selected=导出选中图钉 +export.file.select=选择导出位置 +export.success=成功导出 {0} 个图钉到文件: +{1} +export.success.title=导出成功 +export.description.html=您可以选择导出全部图钉或仅导出选中的图钉。
如需选择特定图钉,请选择'仅导出选中的图钉'选项,然后在下方列表中选择要导出的图钉。 +export.pins.list=可用图钉 +export.no.pins=没有图钉可导出。请选择至少一个图钉进行导出。 +export.error=导出错误 + +# UI 文本 - 通知 +notification.copy.success=图钉复制成功! +notification.copy.success.title=复制成功 +notification.restore.success=图钉恢复成功! +notification.restore.success.title=恢复成功 +notification.delete.success=图钉删除成功! +notification.delete.success.title=删除成功 +notification.clear.success=所有图钉清除成功! +notification.clear.success.title=清除成功 + +# UI 文本 - 备注 +note.edit=编辑备注 +note.placeholder=在此输入备注... +note.add.dialog.title=添加图钉 +note.add.dialog.message=请输入图钉备注(可选): + +# UI 文本 - 悬浮窗 +tooltip.path=路径 +tooltip.line=行号 +tooltip.note=备注 +tooltip.createdAt=创建于 +tooltip.author=作者 +tooltip.blockPin=📌 代码块图钉 +tooltip.linePin=📌 单行图钉 +tooltip.searchPlaceholder=搜索图钉(支持备注与路径) + +# UI 文本 - 设置 +settings.title=CodePins 设置 +settings.general=常规设置 +settings.general.preview.height=预览窗口高度: +settings.general.confirm.delete=删除图钉时确认 + +settings.pin.add=图钉添加设置 +settings.pin.add.show.note.dialog=快捷添加图钉时显示备注和标签对话框 +settings.pin.add.show.note.dialog.desc=开启后,使用选择文本浮动按钮添加图钉时,将显示备注和标签对话框 + +settings.comment=注释指令设置 +settings.comment.show.note.dialog=注释指令添加图钉时显示备注和标签对话框 +settings.comment.show.note.dialog.desc=开启后,使用注释指令添加图钉时,将显示备注和标签对话框 +settings.comment.auto.add.tag=自动添加“快捷添加”标签 +settings.comment.auto.add.tag.desc=开启后,使用注释指令添加图钉时,自动添加“快捷添加”标签 +settings.comment.use.completion.symbol=使用完成指令符号 +settings.comment.completion.symbol=完成指令符号: +settings.comment.completion.symbol.desc=开启后,只有在注释指令后输入完成符号时才会触发图钉添加,避免自动保存导致过早触发 + +settings.shortcuts=快捷键设置 +settings.shortcuts.info=CodePins 提供以下默认快捷键:\n\n- 添加图钉: Alt+Shift+P\n- 导航到下一个图钉: Alt+Shift+Right\n- 导航到上一个图钉: Alt+Shift+Left\n- 切换图钉工具窗口: Alt+Shift+T\n\n您可以在 IDE 的'设置 > 键盘快捷键'中自定义这些快捷键。\n搜索 "CodePins" 以找到所有相关操作。 +settings.shortcuts.open=打开键盘快捷键设置 + +settings.support=支持开发 +settings.support.free=✓ CodePins 现在完全免费开源! +settings.support.thanks=感谢您使用 CodePins!如果这个插件对您有帮助, +请考虑通过以下方式支持我们的开发: +settings.support.contribute=🤝 欢迎加入开源贡献! +settings.support.contribute.desc=我们诚挂邀请您一起维护这个开源项目:\n• 🐛 报告 Bug 和提出改进建议\n• 💡 贡献新功能和代码优化\n• 📖 完善文档和使用指南\n• 🌍 帮助翻译到更多语言\n• 📢 向其他开发者推荐 CodePins +settings.support.github=⭐ GitHub +settings.support.issue=🐛 报告问题 +settings.support.donate=☕ 请我喝咖啡 +settings.support.contribute.button=👍 参与贡献 +settings.general=通用设置 +settings.appearance=外观 +settings.behavior=行为 +settings.language=语言 +settings.language.system=系统默认 +settings.language.english=英文 +settings.language.chinese=中文 +settings.language.change.title=语言已更改 +settings.language.change.message=请重启 IDE 以应用新的语言设置。 + +# UI 文本 - 对话框 +dialog.confirm.title=确认 +dialog.confirm.delete=您确定要删除这个图钉吗? +dialog.confirm.clear=您确定要清除所有图钉吗? + +# UI 文本 - 通知 +notification.success=成功 +notification.error=错误 +notification.warning=警告 +notification.info=信息 + +# UI 文本 - 搜索 +search.placeholder=搜索图钉... +search.no.results=未找到结果 + +# UI 文本 - 状态 +status.free=您正在使用 CodePins 免费开源版 + +# UI 文本 - 空状态 +empty.title=还没有添加任何图钉 +empty.description=图钉可以帮助您标记重要的代码位置,\n并随时快速返回查看。\n\n在编辑器中右键点击代码行,\n选择"添加图钉"添加图钉。 +empty.help.button=查看帮助 + +# UI 文本 - 右键菜单 +context.view.code=查看代码块 +context.edit.note=修改备注 +context.edit.tags=编辑标签 +context.copy.pin=复制图钉 +context.delete.pin=删除图钉 +context.share.pin=分享图钉 + +# UI 文本 - 分享对话框 +share.dialog.title=分享图钉 +share.format=分享格式 +share.format.markdown=Markdown +share.format.html=HTML +share.format.json=JSON +share.format.code.only=仅代码 +share.format.image=图片 +share.format.svg=SVG +share.method=分享方式 +share.method.clipboard=复制到剪贴板 +share.method.file=导出到文件 +share.exclude.metadata=仅分享代码内容(不含元数据) +share.show.line.numbers=显示行号 +share.info=将分享 {0} 个图钉 +share.social=社交分享 +share.watermark=水印设置 + +# UI 文本 - 水印设置对话框 +watermark.dialog.title=水印设置 +watermark.type=水印类型 +watermark.type.text=文本水印 +watermark.type.none=无水印 +watermark.text.settings=文本水印设置 +watermark.text=水印文本 +watermark.position=水印位置 +watermark.color=水印颜色 +watermark.opacity=透明度 +watermark.save.error=保存水印设置失败: {0} +watermark.error.title=设置错误 + +# UI 文本 - 社交分享对话框 +social.share.dialog.title=分享到社交媒体 +social.share.platform=选择平台 +social.share.platform.list=分享平台 +social.share.link.options=链接选项 +social.share.link.expiration=链接有效期 +social.share.link.expiration.future=(长期有效,限制功能将在未来版本中提供) +social.share.password.protection=密码保护 +social.share.password.future=(未来功能) +social.share.info=分享信息 +social.share.info.text=将分享 {0} 个图钉到社交媒体。\n\n注意:分享链接使用GitHub Gist服务存储,链接长期有效。\n自定义过期时间和密码保护功能将在未来版本中提供。 +social.share.error.select.platform=请选择一个社交媒体平台 +social.share.error.title=分享错误 +social.share.title=CodePins分享 +social.share.title.single=CodePins分享: {0} +social.share.title.multiple=CodePins分享: {0}个图钉 + +# UI 文本 - 工具栏 +toolbar.sort=排序图钉 +toolbar.sort.time.desc=按创建时间(新→旧) +toolbar.sort.time.asc=按创建时间(旧→新) +toolbar.sort.name.asc=按名称(A→Z) +toolbar.sort.name.desc=按名称(Z→A) +toolbar.sort.path.asc=按文件路径(A→Z) +toolbar.sort.path.desc=按文件路径(Z→A) +toolbar.share=分享图钉 +toolbar.share.desc=分享选中的图钉 +toolbar.delete.multiple=批量删除 +toolbar.delete.multiple.desc=删除选中的图钉 +toolbar.select.prompt=请先选择要操作的图钉 +toolbar.confirm.delete=确定要删除选中的 {0} 个图钉吗? +toolbar.confirm.delete.title=确认删除 +toolbar.pin.count={0} 个图钉 + +# UI 文本 - 注释 +comment.marker.detected=检测到注释标记 +comment.marker.processed=注释标记已处理 +comment.detection.title=CodePins 注释检测 +comment.detection.scanning=正在检测文件中的注释标记... +comment.detection.completed=注释标记检测完成 +pin.exists.title=CodePins 图钉已存在 +pin.exists.range=该范围已有图钉,不重复添加 +pin.prepare.title=CodePins 准备创建图钉 +pin.created.title=CodePins 图钉创建成功 +pin.created.success=图钉已成功创建: {0} +pin.confirm.note=请确认或修改图钉备注: +pin.added.title=CodePins 图钉已添加 +pin.added.from.comment=根据注释指令自动添加了图钉: {0} +pin.creating.title=CodePins 正在创建图钉 +pin.creating.message=正在创建图钉... +pin.create.failed.title=CodePins 图钉创建失败 +pin.create.failed.message=创建图钉时发生错误: {0} +tag.quick.add=快捷添加 +debug.file=文件: {0} +debug.offset=偏移量: {0}-{1} +debug.note=备注: {0} +debug.is.block=是否代码块: {0} + +# UI 文本 - 操作 +action.add.pin=添加图钉 +action.add.pin.description=将当前行添加为图钉 +action.add.pin.cursor=在光标处添加图钉 +action.add.pin.cursor.description=在当前光标位置添加图钉 +action.navigate.next=导航到下一个图钉 +action.navigate.next.description=导航到下一个图钉位置 +action.navigate.prev=导航到上一个图钉 +action.navigate.prev.description=导航到上一个图钉位置 +action.toggle.window=切换图钉工具窗口 +action.toggle.window.description=显示或隐藏图钉工具窗口 +action.detect.markers=检测注释标记 +action.detect.markers.description=检测注释中的 @pin 和 @pin-block 标记 +action.group.name=CodePins +action.group.description=CodePins 操作 +action.add.pin.title=添加图钉 +action.add.pin.note.prompt=请输入图钉备注(可选): +action.add.pin.failed.title=添加图钉失败 +status.pin.added=✅ 图钉已添加 +status.pin.add.failed=❌ 图钉添加失败 +pin.add.failed.retry=添加图钉失败,请稍后重试 + +# 标签编辑对话框 +tag.dialog.title=编辑标签 +tag.dialog.instruction=标签使用说明:
1. 在下方输入框中输入标签名称,然后按回车或点击添加按钮
2. 选中列表中的标签,然后点击删除按钮可删除标签
3. 选中列表中的标签,然后点击编辑按钮或双击标签可编辑标签
4. 标签数量无限制,插件已完全免费开源 +tag.dialog.add=添加 +tag.dialog.edit=编辑 +tag.dialog.delete=删除 +tag.dialog.existing=现有标签: +tag.dialog.new=新标签: +tag.dialog.input.placeholder=在此输入标签名称... +tag.dialog.exists=标签 '{0}' 已存在 +tag.dialog.exists.title=标签重复 +tag.dialog.empty=暂无已有标签 +tag.dialog.all=所有标签已添加 +tag.dialog.current=当前已有标签(点击添加): \ No newline at end of file