-
-
Notifications
You must be signed in to change notification settings - Fork 678
Feature: i18n for AstrBot #1817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
## 审查者指南
此 PR 通过配置插件、将所有硬编码的 UI 字符串替换为翻译调用、使用 useI18n 将关键组件(特别是 ProviderPage)重构为 Composition API,并添加具有持久性和自动检测的运行时语言选择,从而在 AstrBot 仪表板中集成 vue-i18n 支持。
#### 初始语言设置的顺序图
```mermaid
sequenceDiagram
actor UserBrowser as "用户/浏览器"
participant AppVue as "App.vue"
participant localStorageApi as "localStorage"
participant i18nInstance as "i18n 实例"
AppVue->>localStorageApi: getItem('preferred-language')
localStorageApi-->>AppVue: savedLanguage
alt savedLanguage 有效
AppVue->>i18nInstance: locale.value = savedLanguage
AppVue->>localStorageApi: setItem('preferred-language', savedLanguage)
else 没有有效的 savedLanguage 或新的应用程序版本
AppVue->>UserBrowser: navigator.language
UserBrowser-->>AppVue: browserLanguage
AppVue->>AppVue: 确定区域设置(来自 browserLanguage 或回退 'zh-CN')
AppVue->>i18nInstance: locale.value = determinedLocale
AppVue->>localStorageApi: setItem('preferred-language', determinedLocale)
end 用户更改语言的顺序图sequenceDiagram
actor User
participant PageVue as "Settings.vue / LoginPage.vue"
participant localStorageApi as "localStorage"
participant i18nInstance as "i18n 实例 (通过 useI18n)"
User->>PageVue: 选择新语言 (例如,'en-US')
PageVue->>PageVue: 用户操作调用 changeLanguage('en-US')
PageVue->>i18nInstance: locale.value = 'en-US'
PageVue->>localStorageApi: setItem('preferred-language', 'en-US')
i18nInstance-->>PageVue: UI 使用新的翻译重新渲染
i18n 初始化和设置的类图classDiagram
class MainTs {
<<TypeScript Module>>
+app: VueApp
+main() void
}
class I18nIndexTs {
<<TypeScript Module>>
+i18n: I18nInstance
+createI18n(options): I18nInstance
}
class VueApp {
<<Vue Application Instance>>
+use(plugin: Plugin): VueApp
+mount(rootContainer: string): void
}
class I18nInstance {
<<vue-i18n Instance>>
+legacy: false
+locale: string
+fallbackLocale: string
+messages: object
+t(key: string): string
}
class LocaleJson {
<<JSON Resource>>
+ "key": "value"
}
MainTs ..> I18nIndexTs : imports i18n (instance)
MainTs ..> VueApp : app.use(i18n)
I18nIndexTs ..> I18nInstance : creates & exports
I18nInstance ..> LocaleJson : loads messages (e.g. en-US.json, zh-CN.json)
App.vue 的更新类图 (i18n 逻辑)classDiagram
class AppVue {
<<VueComponent>>
#setup()
+locale: Ref~string~ (from useI18n)
+onMounted() void
-setupInitialLanguage() void
}
AppVue ..> i18nHook : uses
AppVue ..> localStorageApi : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+locale: WritableComputedRef~string~
+t(key: string): string
}
class localStorageApi {
<<Browser API>>
+getItem(key: string): string
+setItem(key: string, value: string): void
}
Settings.vue 的更新类图 (i18n)classDiagram
class SettingsVue {
<<VueComponent>>
#setup()
+locale: Ref~string~ (from useI18n)
+languages: LanguageOption[]
+selectedGitHubProxy: Ref~string~
+t(key: string): string (from useI18n)
+changeLanguage(lang: string): void
+onMounted(): void
}
class LanguageOption {
+value: string
+label: string
}
SettingsVue ..> i18nHook : uses
SettingsVue ..> localStorageApi : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+locale: WritableComputedRef~string~
+t(key: string): string
}
class localStorageApi {
<<Browser API>>
+getItem(key: string): string
+setItem(key: string, value: string): void
}
LoginPage.vue 的更新类图 (i18n)classDiagram
class LoginPageVue {
<<VueComponent>>
#setup()
+locale: Ref~string~ (from useI18n)
+languages: LanguageOption[]
+t(key: string): string (from useI18n)
+changeLanguage(lang: string): void
+onMounted(): void
}
class LanguageOption {
+value: string
+title: string
}
LoginPageVue ..> i18nHook : uses
LoginPageVue ..> localStorageApi : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+locale: WritableComputedRef~string~
+t(key: string): string
}
class localStorageApi {
<<Browser API>>
+getItem(key: string): string
+setItem(key: string, value: string): void
}
ProviderPage.vue 的更新类图 (Composition API & i18n 重构)classDiagram
class ProviderPageVue {
<<VueComponent>>
#setup()
+t(key: string): string (from useI18n)
+config_data: Ref~object~
+metadata: Ref~object~
+activeProviderTab: Ref~string~
+filteredProviders: ComputedRef~object[]~
+getTemplatesByType(type: string): object
+getTabTypeName(tabType: string): string
+selectProviderTemplate(name: string): void
+newProvider(): void
+onMounted(): void
// ... other reactive properties and methods from setup()
}
ProviderPageVue ..> i18nHook : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+t(key: string): string
}
note for ProviderPageVue "从 Options API 重构为 Composition API。\n所有 data、computed、methods 现在都是 setup() 的一部分。\n使用 useI18n() 进行翻译。"
文件级别的更改
提示和命令与 Sourcery 互动
自定义您的体验访问您的 仪表板 以:
获取帮助Original review guide in EnglishReviewer's GuideThis PR integrates vue-i18n support across the AstrBot dashboard by configuring the plugin, replacing all hardcoded UI strings with translation calls, refactoring key components (notably ProviderPage) to the Composition API using useI18n, and adding runtime language selection with persistence and automatic detection. Sequence Diagram for Initial Language SetupsequenceDiagram
actor UserBrowser as "User/Browser"
participant AppVue as "App.vue"
participant localStorageApi as "localStorage"
participant i18nInstance as "i18n Instance"
AppVue->>localStorageApi: getItem('preferred-language')
localStorageApi-->>AppVue: savedLanguage
alt savedLanguage is valid
AppVue->>i18nInstance: locale.value = savedLanguage
AppVue->>localStorageApi: setItem('preferred-language', savedLanguage)
else no valid savedLanguage or new app version
AppVue->>UserBrowser: navigator.language
UserBrowser-->>AppVue: browserLanguage
AppVue->>AppVue: Determine locale (from browserLanguage or fallback 'zh-CN')
AppVue->>i18nInstance: locale.value = determinedLocale
AppVue->>localStorageApi: setItem('preferred-language', determinedLocale)
end
Sequence Diagram for User Changing LanguagesequenceDiagram
actor User
participant PageVue as "Settings.vue / LoginPage.vue"
participant localStorageApi as "localStorage"
participant i18nInstance as "i18n Instance (via useI18n)"
User->>PageVue: Selects new language (e.g., 'en-US')
PageVue->>PageVue: User action calls changeLanguage('en-US')
PageVue->>i18nInstance: locale.value = 'en-US'
PageVue->>localStorageApi: setItem('preferred-language', 'en-US')
i18nInstance-->>PageVue: UI re-renders with new translations
Class Diagram for i18n Initialization and SetupclassDiagram
class MainTs {
<<TypeScript Module>>
+app: VueApp
+main() void
}
class I18nIndexTs {
<<TypeScript Module>>
+i18n: I18nInstance
+createI18n(options): I18nInstance
}
class VueApp {
<<Vue Application Instance>>
+use(plugin: Plugin): VueApp
+mount(rootContainer: string): void
}
class I18nInstance {
<<vue-i18n Instance>>
+legacy: false
+locale: string
+fallbackLocale: string
+messages: object
+t(key: string): string
}
class LocaleJson {
<<JSON Resource>>
+ "key": "value"
}
MainTs ..> I18nIndexTs : imports i18n (instance)
MainTs ..> VueApp : app.use(i18n)
I18nIndexTs ..> I18nInstance : creates & exports
I18nInstance ..> LocaleJson : loads messages (e.g. en-US.json, zh-CN.json)
Updated Class Diagram for App.vue (i18n Logic)classDiagram
class AppVue {
<<VueComponent>>
#setup()
+locale: Ref~string~ (from useI18n)
+onMounted() void
-setupInitialLanguage() void
}
AppVue ..> i18nHook : uses
AppVue ..> localStorageApi : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+locale: WritableComputedRef~string~
+t(key: string): string
}
class localStorageApi {
<<Browser API>>
+getItem(key: string): string
+setItem(key: string, value: string): void
}
Updated Class Diagram for Settings.vue (i18n)classDiagram
class SettingsVue {
<<VueComponent>>
#setup()
+locale: Ref~string~ (from useI18n)
+languages: LanguageOption[]
+selectedGitHubProxy: Ref~string~
+t(key: string): string (from useI18n)
+changeLanguage(lang: string): void
+onMounted(): void
}
class LanguageOption {
+value: string
+label: string
}
SettingsVue ..> i18nHook : uses
SettingsVue ..> localStorageApi : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+locale: WritableComputedRef~string~
+t(key: string): string
}
class localStorageApi {
<<Browser API>>
+getItem(key: string): string
+setItem(key: string, value: string): void
}
Updated Class Diagram for LoginPage.vue (i18n)classDiagram
class LoginPageVue {
<<VueComponent>>
#setup()
+locale: Ref~string~ (from useI18n)
+languages: LanguageOption[]
+t(key: string): string (from useI18n)
+changeLanguage(lang: string): void
+onMounted(): void
}
class LanguageOption {
+value: string
+title: string
}
LoginPageVue ..> i18nHook : uses
LoginPageVue ..> localStorageApi : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+locale: WritableComputedRef~string~
+t(key: string): string
}
class localStorageApi {
<<Browser API>>
+getItem(key: string): string
+setItem(key: string, value: string): void
}
Updated Class Diagram for ProviderPage.vue (Composition API & i18n Refactor)classDiagram
class ProviderPageVue {
<<VueComponent>>
#setup()
+t(key: string): string (from useI18n)
+config_data: Ref~object~
+metadata: Ref~object~
+activeProviderTab: Ref~string~
+filteredProviders: ComputedRef~object[]~
+getTemplatesByType(type: string): object
+getTabTypeName(tabType: string): string
+selectProviderTemplate(name: string): void
+newProvider(): void
+onMounted(): void
// ... other reactive properties and methods from setup()
}
ProviderPageVue ..> i18nHook : uses
class i18nHook {
<<vue-i18n Composition API Hook>>
+t(key: string): string
}
note for ProviderPageVue "Refactored from Options API to Composition API.\nAll data, computed, methods are now part of setup().\nUses useI18n() for translations."
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
嘿 @Soulter - 我已经查看了你的更改,它们看起来很棒!
AI 代理的提示
请解决此代码审查中的评论:
## 单独的评论
### 评论 1
<location> `dashboard/src/views/ProviderPage.vue:416` </location>
<code_context>
+ return filtered;
+};
+
+const getProviderIcon = (type) => {
+ const icons = {
+ 'OpenAI': 'https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/openai.svg',
</code_context>
<issue_to_address>
图标选择逻辑可能无法稳健地处理模糊或相似的提供商名称。
使用 `type.startsWith(key)` 可能会在提供商名称重叠时导致不正确的图标分配。为了提高准确性,请使用更严格的匹配或按降序长度对键进行排序,以优先考虑更具体的匹配。
</issue_to_address>
### 评论 2
<location> `dashboard/src/views/ProviderPage.vue:561` </location>
<code_context>
-}
+};
+
+const deleteProvider = (provider) => {
+ if (confirm(`确定要删除服务提供商 ${provider.id} 吗?`)) {
+ axios.post('/api/config/provider/delete', { id: provider.id }).then((res) => {
</code_context>
<issue_to_address>
直接使用 `confirm` 进行用户提示可能与 UI/UX 不一致。
考虑将浏览器的 `confirm` 对话框替换为 Vuetify 对话框,以获得更一致和易于访问的用户体验。
</issue_to_address>
### 评论 3
<location> `dashboard/src/views/authentication/authForms/AuthLogin.vue:54` </location>
<code_context>
+ :append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'" :type="show1 ? 'text' : 'password'"
+ @click:append="show1 = !show1" class="pwd-input" prepend-inner-icon="mdi-lock" :disabled="loading"></v-text-field>
+
+ <v-btn color="secondary" :loading="isSubmitting || loading" block class="login-btn mt-8" variant="flat" size="large"
+ :disabled="valid" type="submit" elevation="2">
+ <span class="login-btn-text">{{ t('auth.login') }}</span>
</code_context>
<issue_to_address>
登录按钮在 `valid` 时禁用,这可能无法反映表单的有效性。
当表单有效时,应启用该按钮。请反转逻辑或使用正确的验证状态进行 `:disabled` 绑定。
</issue_to_address>
### 评论 4
<location> `dashboard/src/views/Settings.vue:58` </location>
<code_context>
- }
- },
- mounted() {
- this.selectedGitHubProxy = localStorage.getItem('selectedGitHubProxy') || "";
+ components: {
+ WaitingForRestart,
</code_context>
<issue_to_address>
没有对用户提供的 GitHub 代理 URL 进行验证。
验证用户输入的 GitHub 代理 URL 可以防止格式错误的条目和潜在的安全风险。建议强制执行正确的 URL 格式并限制为 HTTPS。
建议的实施方式:
```
components: {
WaitingForRestart,
},
data() {
return {
selectedGitHubProxy: localStorage.getItem('selectedGitHubProxy') || "",
githubProxyUrlError: "",
};
},
methods: {
validateGitHubProxyUrl(url) {
// Only allow HTTPS URLs, basic validation
try {
const parsed = new URL(url);
if (parsed.protocol !== "https:") {
return "URL must start with https://";
}
return "";
} catch (e) {
return "Invalid URL format";
}
},
onGitHubProxyUrlChange() {
this.githubProxyUrlError = this.validateGitHubProxyUrl(this.selectedGitHubProxy);
if (!this.githubProxyUrlError) {
localStorage.setItem('selectedGitHubProxy', this.selectedGitHubProxy);
}
},
},
```
```
this.selectedGitHubProxy = localStorage.getItem('selectedGitHubProxy') || "";
```
您还需要更新 `Settings.vue` 的模板部分,以:
- 将 GitHub 代理 URL 的输入绑定到 `selectedGitHubProxy`。
- 使用 `@change` 或 `@input` 调用 `onGitHubProxyUrlChange`。
- 将 `githubProxyUrlError` 显示为向用户的验证消息。
示例(放置在模板中):
```vue
<input
v-model="selectedGitHubProxy"
@change="onGitHubProxyUrlChange"
placeholder="Enter GitHub Proxy URL (https://...)"
/>
<div v-if="githubProxyUrlError" class="error-message">{{ githubProxyUrlError }}</div>
```
您可能需要调整模板代码以适应您的 UI 框架和样式。
</issue_to_address>
帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用反馈来改进您的评论。
Original comment in English
Hey @Soulter - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `dashboard/src/views/ProviderPage.vue:416` </location>
<code_context>
+ return filtered;
+};
+
+const getProviderIcon = (type) => {
+ const icons = {
+ 'OpenAI': 'https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/openai.svg',
</code_context>
<issue_to_address>
Icon selection logic may not handle ambiguous or similar provider names robustly.
Using `type.startsWith(key)` may cause incorrect icon assignment when provider names overlap. To improve accuracy, use stricter matching or sort keys by descending length to prioritize more specific matches.
</issue_to_address>
### Comment 2
<location> `dashboard/src/views/ProviderPage.vue:561` </location>
<code_context>
-}
+};
+
+const deleteProvider = (provider) => {
+ if (confirm(`确定要删除服务提供商 ${provider.id} 吗?`)) {
+ axios.post('/api/config/provider/delete', { id: provider.id }).then((res) => {
</code_context>
<issue_to_address>
Direct use of `confirm` for user prompts may not be consistent with UI/UX.
Consider replacing the browser's `confirm` dialog with a Vuetify dialog for a more consistent and accessible user experience.
</issue_to_address>
### Comment 3
<location> `dashboard/src/views/authentication/authForms/AuthLogin.vue:54` </location>
<code_context>
+ :append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'" :type="show1 ? 'text' : 'password'"
+ @click:append="show1 = !show1" class="pwd-input" prepend-inner-icon="mdi-lock" :disabled="loading"></v-text-field>
+
+ <v-btn color="secondary" :loading="isSubmitting || loading" block class="login-btn mt-8" variant="flat" size="large"
+ :disabled="valid" type="submit" elevation="2">
+ <span class="login-btn-text">{{ t('auth.login') }}</span>
</code_context>
<issue_to_address>
Login button disables on `valid`, which may not reflect form validity.
The button should be enabled when the form is valid. Please invert the logic or use the correct validation state for the `:disabled` binding.
</issue_to_address>
### Comment 4
<location> `dashboard/src/views/Settings.vue:58` </location>
<code_context>
- }
- },
- mounted() {
- this.selectedGitHubProxy = localStorage.getItem('selectedGitHubProxy') || "";
+ components: {
+ WaitingForRestart,
</code_context>
<issue_to_address>
No validation for user-provided GitHub proxy URLs.
Validating user input for GitHub proxy URLs can prevent malformed entries and potential security risks. Recommend enforcing proper URL format and restricting to HTTPS.
Suggested implementation:
```
components: {
WaitingForRestart,
},
data() {
return {
selectedGitHubProxy: localStorage.getItem('selectedGitHubProxy') || "",
githubProxyUrlError: "",
};
},
methods: {
validateGitHubProxyUrl(url) {
// Only allow HTTPS URLs, basic validation
try {
const parsed = new URL(url);
if (parsed.protocol !== "https:") {
return "URL must start with https://";
}
return "";
} catch (e) {
return "Invalid URL format";
}
},
onGitHubProxyUrlChange() {
this.githubProxyUrlError = this.validateGitHubProxyUrl(this.selectedGitHubProxy);
if (!this.githubProxyUrlError) {
localStorage.setItem('selectedGitHubProxy', this.selectedGitHubProxy);
}
},
},
```
```
this.selectedGitHubProxy = localStorage.getItem('selectedGitHubProxy') || "";
```
You will also need to update the template section of `Settings.vue` to:
- Bind the input for the GitHub proxy URL to `selectedGitHubProxy`.
- Use `@change` or `@input` to call `onGitHubProxyUrlChange`.
- Display `githubProxyUrlError` as a validation message to the user.
Example (to be placed in the template):
```vue
<input
v-model="selectedGitHubProxy"
@change="onGitHubProxyUrlChange"
placeholder="Enter GitHub Proxy URL (https://...)"
/>
<div v-if="githubProxyUrlError" class="error-message">{{ githubProxyUrlError }}</div>
```
You may need to adjust the template code to fit your UI framework and style.
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
return filtered; | ||
}; | ||
|
||
const getProviderIcon = (type) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): 图标选择逻辑可能无法稳健地处理模糊或相似的提供商名称。
使用 type.startsWith(key)
可能会在提供商名称重叠时导致不正确的图标分配。为了提高准确性,请使用更严格的匹配或按降序长度对键进行排序,以优先考虑更具体的匹配。
Original comment in English
suggestion (bug_risk): Icon selection logic may not handle ambiguous or similar provider names robustly.
Using type.startsWith(key)
may cause incorrect icon assignment when provider names overlap. To improve accuracy, use stricter matching or sort keys by descending length to prioritize more specific matches.
} | ||
}; | ||
|
||
const deleteProvider = (provider) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: 直接使用 confirm
进行用户提示可能与 UI/UX 不一致。
考虑将浏览器的 confirm
对话框替换为 Vuetify 对话框,以获得更一致和易于访问的用户体验。
Original comment in English
suggestion: Direct use of confirm
for user prompts may not be consistent with UI/UX.
Consider replacing the browser's confirm
dialog with a Vuetify dialog for a more consistent and accessible user experience.
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'" :type="show1 ? 'text' : 'password'" | ||
@click:append="show1 = !show1" class="pwd-input" prepend-inner-icon="mdi-lock" :disabled="loading"></v-text-field> | ||
|
||
<v-btn color="secondary" :loading="isSubmitting || loading" block class="login-btn mt-8" variant="flat" size="large" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): 登录按钮在 valid
时禁用,这可能无法反映表单的有效性。
当表单有效时,应启用该按钮。请反转逻辑或使用正确的验证状态进行 :disabled
绑定。
Original comment in English
issue (bug_risk): Login button disables on valid
, which may not reflect form validity.
The button should be enabled when the form is valid. Please invert the logic or use the correct validation state for the :disabled
binding.
<v-icon>mdi-translate</v-icon> | ||
</template> | ||
</v-list-item> | ||
</template> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 suggestion (security): 没有对用户提供的 GitHub 代理 URL 进行验证。
验证用户输入的 GitHub 代理 URL 可以防止格式错误的条目和潜在的安全风险。建议强制执行正确的 URL 格式并限制为 HTTPS。
建议的实施方式:
components: {
WaitingForRestart,
},
data() {
return {
selectedGitHubProxy: localStorage.getItem('selectedGitHubProxy') || "",
githubProxyUrlError: "",
};
},
methods: {
validateGitHubProxyUrl(url) {
// Only allow HTTPS URLs, basic validation
try {
const parsed = new URL(url);
if (parsed.protocol !== "https:") {
return "URL must start with https://";
}
return "";
} catch (e) {
return "Invalid URL format";
}
},
onGitHubProxyUrlChange() {
this.githubProxyUrlError = this.validateGitHubProxyUrl(this.selectedGitHubProxy);
if (!this.githubProxyUrlError) {
localStorage.setItem('selectedGitHubProxy', this.selectedGitHubProxy);
}
},
},
this.selectedGitHubProxy = localStorage.getItem('selectedGitHubProxy') || "";
您还需要更新 Settings.vue
的模板部分,以:
- 将 GitHub 代理 URL 的输入绑定到
selectedGitHubProxy
。 - 使用
@change
或@input
调用onGitHubProxyUrlChange
。 - 将
githubProxyUrlError
显示为向用户的验证消息。
示例(放置在模板中):
<input
v-model="selectedGitHubProxy"
@change="onGitHubProxyUrlChange"
placeholder="Enter GitHub Proxy URL (https://...)"
/>
<div v-if="githubProxyUrlError" class="error-message">{{ githubProxyUrlError }}</div>
您可能需要调整模板代码以适应您的 UI 框架和样式。
Original comment in English
🚨 suggestion (security): No validation for user-provided GitHub proxy URLs.
Validating user input for GitHub proxy URLs can prevent malformed entries and potential security risks. Recommend enforcing proper URL format and restricting to HTTPS.
Suggested implementation:
components: {
WaitingForRestart,
},
data() {
return {
selectedGitHubProxy: localStorage.getItem('selectedGitHubProxy') || "",
githubProxyUrlError: "",
};
},
methods: {
validateGitHubProxyUrl(url) {
// Only allow HTTPS URLs, basic validation
try {
const parsed = new URL(url);
if (parsed.protocol !== "https:") {
return "URL must start with https://";
}
return "";
} catch (e) {
return "Invalid URL format";
}
},
onGitHubProxyUrlChange() {
this.githubProxyUrlError = this.validateGitHubProxyUrl(this.selectedGitHubProxy);
if (!this.githubProxyUrlError) {
localStorage.setItem('selectedGitHubProxy', this.selectedGitHubProxy);
}
},
},
this.selectedGitHubProxy = localStorage.getItem('selectedGitHubProxy') || "";
You will also need to update the template section of Settings.vue
to:
- Bind the input for the GitHub proxy URL to
selectedGitHubProxy
. - Use
@change
or@input
to callonGitHubProxyUrlChange
. - Display
githubProxyUrlError
as a validation message to the user.
Example (to be placed in the template):
<input
v-model="selectedGitHubProxy"
@change="onGitHubProxyUrlChange"
placeholder="Enter GitHub Proxy URL (https://...)"
/>
<div v-if="githubProxyUrlError" class="error-message">{{ githubProxyUrlError }}</div>
You may need to adjust the template code to fit your UI framework and style.
感觉单体翻译文件(zh-CN.json 和 en-US.json)不利于后期维护。需要分结构出来,不过我也在做这个 #1822 |
Closed due to a better solution #1822 |
Motivation
This PR aims to make AstrBot more international.
Check
requirements.txt
和pyproject.toml
文件相应位置。好的,这是将 pull request 总结翻译成中文的结果:
Sourcery 总结
通过添加 vue-i18n 支持、将硬编码文本替换为翻译键以及提供运行时语言切换和持久性,在 AstrBot 仪表板中集成国际化。
新特性:
增强功能:
构建:
Original summary in English
好的,这是翻译成中文的 pull request 总结:
Sourcery 总结
将国际化集成到 AstrBot 仪表板中,通过添加 vue-i18n 支持、翻译所有 UI 文本,并启用具有持久性和回退处理的运行时语言选择。
新特性:
增强功能:
构建:
Original summary in English
Summary by Sourcery
Integrate internationalization into the AstrBot dashboard by adding vue-i18n support, translating all UI text, and enabling runtime language selection with persistence and fallback handling
New Features:
Enhancements:
Build: