diff --git a/core/app/dto/setting.go b/core/app/dto/setting.go index 1b5621005682..8b1e79099fb9 100644 --- a/core/app/dto/setting.go +++ b/core/app/dto/setting.go @@ -19,6 +19,7 @@ type SettingInfo struct { Theme string `json:"theme"` MenuTabs string `json:"menuTabs"` Language string `json:"language"` + DocSource string `json:"docSource"` ServerPort string `json:"serverPort"` SSL string `json:"ssl"` diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go index 1127adf0492a..6a9b2ac00b36 100644 --- a/core/app/service/upgrade.go +++ b/core/app/service/upgrade.go @@ -286,10 +286,22 @@ type noteDetailHelper struct { } func (u *UpgradeService) LoadRelease() ([]dto.ReleasesNotes, error) { + docSource, _ := settingRepo.GetValueByKey("DocSource") + lang, _ := settingRepo.GetValueByKey("Language") var notes []dto.ReleasesNotes - url := "https://docs.1panel.pro/v2/search/search_index.json" - if global.CONF.Base.Edition != "intl" { - url = "https://1panel.cn/docs/v2/search/search_index.json" + url := "https://1panel.cn/docs/v2/search/search_index.json" + useIntlDocs := false + lang = strings.ToLower(strings.TrimSpace(lang)) + if docSource == "withByRegion" { + useIntlDocs = global.CONF.Base.Edition == "intl" + if !useIntlDocs && lang != "zh" { + useIntlDocs = true + } + } else { + useIntlDocs = lang != "zh" + } + if useIntlDocs { + url = "https://docs.1panel.pro/v2/search/search_index.json" } resp, err := req_helper.HandleGet(url) if err != nil { diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go index 10419e26bd28..86d498085ab8 100644 --- a/core/init/migration/migrate.go +++ b/core/init/migration/migrate.go @@ -34,6 +34,7 @@ func Init() { migrations.AddDashboardCarouselSetting, migrations.AddEditionSetting, migrations.UpdateAiLocalModelMenuTitle, + migrations.AddDocSourceSetting, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/core/init/migration/migrations/init.go b/core/init/migration/migrations/init.go index d698d51c2505..d00d7d424f74 100644 --- a/core/init/migration/migrations/init.go +++ b/core/init/migration/migrations/init.go @@ -70,6 +70,9 @@ var InitSetting = &gormigrate.Migration{ if err := tx.Create(&model.Setting{Key: "Language", Value: language}).Error; err != nil { return err } + if err := tx.Create(&model.Setting{Key: "DocSource", Value: "withByRegion"}).Error; err != nil { + return err + } if err := tx.Create(&model.Setting{Key: "SessionTimeout", Value: "86400"}).Error; err != nil { return err } @@ -835,6 +838,23 @@ var AddEditionSetting = &gormigrate.Migration{ }, } +var AddDocSourceSetting = &gormigrate.Migration{ + ID: "20260316-add-doc-source-setting", + Migrate: func(tx *gorm.DB) error { + var setting model.Setting + if err := tx.Where("key = ?", "DocSource").First(&setting).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return tx.Create(&model.Setting{Key: "DocSource", Value: "withByRegion"}).Error + } + return err + } + if setting.Value == "" { + return tx.Model(&model.Setting{}).Where("key = ?", "DocSource").Update("value", "withByRegion").Error + } + return nil + }, +} + var UpdateAiLocalModelMenuTitle = &gormigrate.Migration{ ID: "20260307-update-ai-local-model-menu-title", Migrate: func(tx *gorm.DB) error { diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts index d0a056fcff08..0a3d00fdc440 100644 --- a/frontend/src/api/interface/setting.ts +++ b/frontend/src/api/interface/setting.ts @@ -21,6 +21,7 @@ export namespace Setting { theme: string; menuTabs: string; language: string; + docSource: string; defaultIO: string; defaultNetwork: string; lastCleanTime: string; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 9c72009e5ab3..a7962f13f70d 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1811,6 +1811,10 @@ const message = { light: 'Light', auto: 'Follow System', language: 'Language', + runtimeEnv: 'Runtime environment', + docSource: 'Document source', + withByRegion: 'Follow region (Default)', + withByLang: 'Follow system language', region: 'Region', cn: 'Mainland China', intl: 'Global', @@ -1819,6 +1823,8 @@ const message = { regionHelper2: 'App Store and script library', regionHelper3: 'User manual and related documents', regionHelper4: 'This may affect future downloads and access. Proceed with caution.', + regionTip: 'Region affects the system update source and package download address.', + docSourceTip: 'Document source determines the language used for docs and release note links.', languageHelper: 'By default, it follows the browser language. This parameter takes effect only on the current browser', sessionTimeout: 'Session timeout', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 6c6c391bc67f..7c3e2b8a83c4 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -1842,6 +1842,10 @@ const message = { light: 'Claro', auto: 'Seguir sistema', language: 'Idioma', + runtimeEnv: 'Entorno de ejecución', + docSource: 'Fuente de documentación', + withByRegion: 'Seguir región de operación (Predeterminado)', + withByLang: 'Seguir idioma del sistema', region: 'Región de operación', cn: 'China continental', intl: 'Global', @@ -1850,6 +1854,9 @@ const message = { regionHelper2: 'Tienda de aplicaciones y biblioteca de scripts', regionHelper3: 'Manual de usuario y documentación relacionada', regionHelper4: 'Esto puede afectar futuras descargas y accesos. Proceda con precaución.', + regionTip: + 'La región de operación afecta la fuente de actualización del sistema y la dirección de descarga de paquetes de instalación.', + docSourceTip: 'La fuente de documentación determina el idioma de los enlaces de documentación y registro de actualizaciones.', languageHelper: 'Por defecto sigue el idioma del navegador. Este parámetro solo tiene efecto en el navegador actual', sessionTimeout: 'Tiempo de espera de sesión', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index aa2b65ada4b6..e9d2f713bb74 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -1818,6 +1818,10 @@ const message = { light: 'ライト', auto: 'システムをフォローします', language: '言語', + runtimeEnv: '実行環境', + docSource: 'ドキュメントソース', + withByRegion: '運用リージョンに従う(デフォルト)', + withByLang: 'システム言語に従う', region: '運用リージョン', cn: '中国本土', intl: 'グローバル', @@ -1826,6 +1830,8 @@ const message = { regionHelper2: 'アプリストアおよびスクリプトライブラリ', regionHelper3: 'ユーザーマニュアルおよび関連ドキュメント', regionHelper4: '今後のダウンロードやアクセスに影響する可能性があります。ご注意ください。', + regionTip: '運用リージョンはシステム更新ソースとインストールパッケージのダウンロードアドレスに影響します。', + docSourceTip: 'ドキュメントソースはドキュメントと更新ログの遷移言語を決定します。', languageHelper: 'デフォルトでは、ブラウザ言語に従います。このパラメーターは、現在のブラウザでのみ有効になります', sessionTimeout: 'セッションタイムアウト', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 2ba1008cb71c..1b5c73f9c463 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -1786,6 +1786,10 @@ const message = { light: '라이트', auto: '시스템 따라가기', language: '언어', + runtimeEnv: '실행 환경', + docSource: '문서 출처', + withByRegion: '운영 지역 따름(기본값)', + withByLang: '시스템 언어 따름', region: '운영 지역', cn: '중국 본토', intl: '글로벌', @@ -1794,6 +1798,8 @@ const message = { regionHelper2: '앱 스토어 및 스크립트 라이브러리', regionHelper3: '사용자 매뉴얼 및 관련 문서', regionHelper4: '향후 다운로드 및 접근에 영향을 줄 수 있습니다. 주의하십시오.', + regionTip: '운영 지역은 시스템 업데이트 소스와 설치 패키지 다운로드 주소에 영향을 미칩니다.', + docSourceTip: '문서 출처는 문서와 업데이트 로그의 이동 언어를 결정합니다.', languageHelper: '기본적으로 브라우저 언어를 따릅니다. 이 설정은 현재 브라우저에서만 적용됩니다.', sessionTimeout: '세션 타임아웃', sessionTimeoutError: '최소 세션 타임아웃은 300초입니다.', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 6a38bad94b72..804ca6bd06e0 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -1846,6 +1846,10 @@ const message = { light: 'Terang', auto: 'Ikut Sistem', language: 'Bahasa', + runtimeEnv: 'Persekitaran operasi', + docSource: 'Sumber dokumentasi', + withByRegion: 'Ikut wilayah operasi (Lalai)', + withByLang: 'Ikut bahasa sistem', region: 'Wilayah operasi', cn: 'Tanah Besar China', intl: 'Global', @@ -1855,6 +1859,8 @@ const message = { regionHelper3: 'Manual pengguna dan dokumen berkaitan', regionHelper4: 'Tindakan ini mungkin menjejaskan muat turun dan akses seterusnya. Sila teruskan dengan berhati-hati.', + regionTip: 'Wilayah operasi mempengaruhi sumber kemas kini sistem serta alamat muat turun pakej pemasangan.', + docSourceTip: 'Sumber dokumentasi menentukan bahasa pautan untuk dokumentasi dan log kemas kini.', languageHelper: 'Secara lalai, ia mengikuti bahasa penyemak imbas. Parameter ini hanya berkuat kuasa pada penyemak imbas semasa', sessionTimeout: 'Tempoh tamat sesi', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index e69ac9e48181..239dd5a1beb6 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -1961,6 +1961,10 @@ const message = { light: 'Claro', auto: 'Seguir o sistema', language: 'Idioma', + runtimeEnv: 'Ambiente de execução', + docSource: 'Fonte da documentação', + withByRegion: 'Seguir região de operação (Padrão)', + withByLang: 'Seguir idioma do sistema', region: 'Região de operação', cn: 'China continental', intl: 'Global', @@ -1969,6 +1973,8 @@ const message = { regionHelper2: 'Loja de aplicativos e biblioteca de scripts', regionHelper3: 'Manual do usuário e documentação relacionada', regionHelper4: 'Isso pode afetar downloads e acessos futuros. Prossiga com cautela.', + regionTip: 'A região de operação afeta a fonte de atualização do sistema e o endereço de download dos pacotes de instalação.', + docSourceTip: 'A fonte da documentação determina o idioma de redirecionamento da documentação e do registro de atualizações.', languageHelper: 'Por padrão, segue o idioma do navegador. Este parâmetro tem efeito apenas no navegador atual', sessionTimeout: 'Tempo limite de sessão', sessionTimeoutError: 'O tempo mínimo de sessão é de 300 segundos', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 41c0fa1e9c33..f2ce9de0a918 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -1832,6 +1832,10 @@ const message = { light: 'Светлая', auto: 'Как в системе', language: 'Язык', + runtimeEnv: 'Среда выполнения', + docSource: 'Источник документации', + withByRegion: 'Следовать региону работы (по умолчанию)', + withByLang: 'Следовать языку системы', region: 'Регион работы', cn: 'Материковый Китай', intl: 'Глобальный', @@ -1840,6 +1844,8 @@ const message = { regionHelper2: 'Магазин приложений и библиотека скриптов', regionHelper3: 'Руководство пользователя и сопутствующая документация', regionHelper4: 'Это может повлиять на последующие загрузки и доступ. Действуйте осторожно.', + regionTip: 'Регион работы влияет на источник обновления системы и адрес загрузки установочных пакетов.', + docSourceTip: 'Источник документации определяет язык перехода для документации и журнала обновлений.', languageHelper: 'По умолчанию следует языку браузера. Этот параметр действует только в текущем браузере', sessionTimeout: 'Время сессии', sessionTimeoutError: 'Минимальное время сессии 300 секунд', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 5fb133e7f12f..ad63fd39a167 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -1839,6 +1839,10 @@ const message = { light: 'Açık', auto: 'Sistemi takip et', language: 'Dil', + runtimeEnv: 'Çalışma ortamı', + docSource: 'Doküman kaynağı', + withByRegion: 'Çalışma bölgesini takip et (Varsayılan)', + withByLang: 'Sistem dilini takip et', region: 'Çalışma bölgesi', cn: 'Çin anakarası', intl: 'Küresel', @@ -1847,6 +1851,8 @@ const message = { regionHelper2: 'Uygulama mağazası ve betik kütüphanesi', regionHelper3: 'Kullanım kılavuzu ve ilgili dokümanlar', regionHelper4: 'Bu işlem sonraki indirme ve erişimleri etkileyebilir. Lütfen dikkatli olun.', + regionTip: 'Çalışma bölgesi, sistem güncelleme kaynağını ve kurulum paketi indirme adresini belirgin şekilde etkiler.', + docSourceTip: 'Doküman kaynağı, dokümantasyon ve güncelleme günlüğü bağlantılarının dilini belirler.', languageHelper: 'Varsayılan olarak tarayıcı dilini takip eder. Bu parametre yalnızca geçerli tarayıcıda etkilidir', sessionTimeout: 'Oturum zaman aşımı', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 861190afc032..0c9437ea0ac6 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -1710,6 +1710,10 @@ const message = { light: '亮色', auto: '跟隨系統', language: '系統語言', + runtimeEnv: '運行環境', + docSource: '文件來源', + withByRegion: '跟隨運行區域(預設)', + withByLang: '跟隨系統語言', region: '運行區域', cn: '中國大陸', intl: '全球', @@ -1718,6 +1722,8 @@ const message = { regionHelper2: '應用商店與腳本庫', regionHelper3: '使用手冊及相關文件', regionHelper4: '此操作可能影響後續資源下載與存取,請謹慎操作。', + regionTip: '運行區域影響系統更新源以及安裝包下載地址。', + docSourceTip: '文件來源決定文件與更新日誌的跳轉語言。', languageHelper: '預設跟隨瀏覽器語言,設定後只對目前瀏覽器生效,更換瀏覽器後需要重新設定', sessionTimeout: '逾時時間', sessionTimeoutError: '最小逾時時間為 300 秒', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 69db5b395b93..1f4025596779 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1711,14 +1711,16 @@ const message = { light: '亮色', auto: '跟随系统', language: '系统语言', + docSource: '文档来源', + withByRegion: '跟随运行区域(默认)', + withByLang: '跟随系统语言', + runtimeEnv: '运行环境', region: '运行区域', cn: '中国大陆', intl: '全球', - regionHelper: '更改运行区域后,将调整以下资源来源:', - regionHelper1: '系统安装包', - regionHelper2: '应用商店与脚本库', - regionHelper3: '使用手册及相关文档', - regionHelper4: '该操作可能影响后续资源下载与访问,请谨慎操作。', + regionHelper: '该操作可能影响后续资源下载与访问,是否继续?', + regionTip: '运行区域影响系统更新源以及安装包下载地址。', + docSourceTip: '文档来源决定文档与更新日志的跳转语言。', languageHelper: '默认跟随浏览器语言,设置后只对当前浏览器生效,更换浏览器后需要重新设置', sessionTimeout: '超时时间', sessionTimeoutError: '最小超时时间为 300 秒', diff --git a/frontend/src/store/interface/index.ts b/frontend/src/store/interface/index.ts index 015359a5f740..fd07a07c3725 100644 --- a/frontend/src/store/interface/index.ts +++ b/frontend/src/store/interface/index.ts @@ -53,6 +53,7 @@ export interface GlobalState { isProductPro: boolean; isIntl: boolean; + docWithRegion: boolean; productProExpires: number; isMasterProductPro: boolean; isOffLine: boolean; diff --git a/frontend/src/store/modules/global.ts b/frontend/src/store/modules/global.ts index 846b63557342..b2223e2232b4 100644 --- a/frontend/src/store/modules/global.ts +++ b/frontend/src/store/modules/global.ts @@ -4,6 +4,9 @@ import { GlobalState, ThemeConfigProp } from '../interface'; import { DeviceType } from '@/enums/app'; import i18n, { setActiveLocale } from '@/lang'; +const CN_DOCS_URL = 'https://1panel.cn/docs/v2'; +const INTL_DOCS_URL = 'https://docs.1panel.pro/v2'; + const GlobalStore = defineStore({ id: 'GlobalState', state: (): GlobalState => ({ @@ -47,6 +50,7 @@ const GlobalStore = defineStore({ isProductPro: false, isIntl: false, + docWithRegion: true, productProExpires: 0, isMasterProductPro: false, isOffLine: false, @@ -60,7 +64,14 @@ const GlobalStore = defineStore({ state.themeConfig.theme === 'dark' || (state.themeConfig.theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches), isDarkGoldTheme: (state) => state.themeConfig.primary === '#F0BE96' && state.isProductPro, - docsUrl: (state) => (state.isIntl ? 'https://docs.1panel.pro/v2' : 'https://1panel.cn/docs/v2'), + docsUrl: (state) => { + if (state.docWithRegion) { + return state.isIntl ? INTL_DOCS_URL : CN_DOCS_URL; + } + const lang = state.language.toLowerCase(); + const isChinese = lang === 'zh'; + return isChinese ? CN_DOCS_URL : INTL_DOCS_URL; + }, isMaster: (state) => state.currentNode === 'local', }, actions: { diff --git a/frontend/src/views/setting/panel/edition/index.vue b/frontend/src/views/setting/panel/edition/index.vue new file mode 100644 index 000000000000..af308e7572bf --- /dev/null +++ b/frontend/src/views/setting/panel/edition/index.vue @@ -0,0 +1,120 @@ + + diff --git a/frontend/src/views/setting/panel/index.vue b/frontend/src/views/setting/panel/index.vue index ccfd5a5ee869..823d36d63ccf 100644 --- a/frontend/src/views/setting/panel/index.vue +++ b/frontend/src/views/setting/panel/index.vue @@ -202,15 +202,10 @@ - - - - {{ $t('setting.cn') }} - - - {{ $t('setting.intl') }} - - + + + {{ runtimeEnvLabel() }} + @@ -228,6 +223,7 @@ + @@ -254,6 +250,7 @@ import PanelName from '@/views/setting/panel/name/index.vue'; import SystemIP from '@/views/setting/panel/systemip/index.vue'; import Proxy from '@/views/setting/panel/proxy/index.vue'; import HideMenu from '@/views/setting/panel/hidemenu/index.vue'; +import Edition from '@/views/setting/panel/edition/index.vue'; import { storeToRefs } from 'pinia'; import { getXpackSetting, updateXpackSettingByKey } from '@/utils/xpack'; import { setPrimaryColor } from '@/utils/theme'; @@ -289,6 +286,7 @@ const form = reactive({ themeColor: {} as ThemeColor, menuTabs: '', language: '', + docSource: 'withByRegion', edition: '', complexityVerification: '', developerMode: '', @@ -323,6 +321,7 @@ const hideMenuRef = ref(); const watermarkRef = ref(); const themeColorRef = ref(); const apiInterfaceRef = ref(); +const editionRef = ref(); const unset = ref(i18n.global.t('setting.unSetting')); const languageOptions = ref([ @@ -353,6 +352,7 @@ const search = async () => { form.menuTabs = res.data.menuTabs; form.panelName = res.data.panelName; form.language = res.data.language; + form.docSource = res.data.docSource || 'withByRegion'; form.edition = res.data.edition; form.sessionTimeout = Number(res.data.sessionTimeout); @@ -428,6 +428,16 @@ const onChangeHideMenus = () => { hideMenuRef.value.acceptParams({ hideMenu: form.hideMenu }); }; +const onChangeRegion = () => { + editionRef.value.acceptParams({ edition: form.edition, docSource: form.docSource }); +}; + +const runtimeEnvLabel = () => { + const editionLabel = form.edition === 'cn' ? i18n.global.t('setting.cn') : i18n.global.t('setting.intl'); + const docSourceLabel = i18n.global.t(`setting.${form.docSource || 'withByRegion'}`); + return `${editionLabel} / ${docSourceLabel}`; +}; + const onChangeThemeColor = () => { const themeColor: ThemeColor = JSON.parse(globalStore.themeConfig.themeColor); themeColorRef.value.acceptParams({ themeColor: themeColor, theme: globalStore.themeConfig.theme }); @@ -535,9 +545,6 @@ const onSave = async (key: string, val: any) => { await globalStore.updateLanguage(val); location.reload(); break; - case 'Edition': - globalStore.isIntl = val === 'intl'; - break; } MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); search(); @@ -548,39 +555,6 @@ const onSave = async (key: string, val: any) => { loading.value = false; }; -const onSaveEdition = async () => { - ElMessageBox.confirm( - ` -
-
- ${i18n.global.t('setting.regionHelper')} -
-
    -
  • ${i18n.global.t('setting.regionHelper1')}
  • -
  • ${i18n.global.t('setting.regionHelper2')}
  • -
  • ${i18n.global.t('setting.regionHelper3')}
  • -
-
- ${i18n.global.t('setting.regionHelper4')} -
-
- `, - i18n.global.t('setting.region'), - { - confirmButtonText: i18n.global.t('commons.button.confirm'), - cancelButtonText: i18n.global.t('commons.button.cancel'), - dangerouslyUseHTMLString: true, - }, - ) - .then(async () => { - await onSave('Edition', form.edition); - location.reload(); - }) - .catch(() => { - form.edition = form.edition === 'cn' ? 'intl' : 'cn'; - }); -}; - onMounted(() => { search(); getSystemAvailable();