mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
docs: add translation contribution guide (#12505)
* docs: add translation contribution guide with reference PR and checklist Agent-Logs-Url: https://github.com/1Panel-dev/1Panel/sessions/e6a41d10-557e-4ca1-a78b-18888a9f17be Co-authored-by: wan92hen <27671436+wan92hen@users.noreply.github.com> * docs: clarify frontend vs backend locale file naming conventions Agent-Logs-Url: https://github.com/1Panel-dev/1Panel/sessions/e6a41d10-557e-4ca1-a78b-18888a9f17be Co-authored-by: wan92hen <27671436+wan92hen@users.noreply.github.com> * docs: update reference PR to #10352 (merged Spanish locale) Agent-Logs-Url: https://github.com/1Panel-dev/1Panel/sessions/2b14f230-892a-412e-baed-07e67614d1e9 Co-authored-by: wan92hen <27671436+wan92hen@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: wan92hen <27671436+wan92hen@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,10 @@ Please submit a PR broken down into small changes bit by bit. A PR consisting of
|
||||
|
||||
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
|
||||
|
||||
## Add a new translation
|
||||
|
||||
If you'd like to help translate 1Panel into a new language, please read the [Translation Contribution Guide](docs/TRANSLATION.md). It lists every file you need to create or modify, along with a reference PR you can use as a concrete example.
|
||||
|
||||
## Report issues
|
||||
It is a great way to contribute by reporting an issue. Well-written and complete bug reports are always welcome! Please open an issue and follow the template to fill in required information.
|
||||
|
||||
|
||||
235
docs/TRANSLATION.md
Normal file
235
docs/TRANSLATION.md
Normal file
@@ -0,0 +1,235 @@
|
||||
# Translation Contribution Guide
|
||||
|
||||
Thank you for your interest in helping translate 1Panel into a new language! This guide walks through every file you need to create or modify to add full language support across the frontend and backend.
|
||||
|
||||
> **Reference PR:** [WIP: Dev v2 spanish (#10352)](https://github.com/1Panel-dev/1Panel/pull/10352) — a merged, real-world example of adding a brand-new locale (Spanish `es-ES`).
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
1Panel's i18n system spans two layers:
|
||||
|
||||
| Layer | Technology | Translation format |
|
||||
|---|---|---|
|
||||
| **Frontend** (Vue 3) | `vue-i18n` + Element Plus | TypeScript (`.ts`) |
|
||||
| **Backend** (Go) | `go-i18n` / `nicksnyder/go-i18n` | YAML (`.yaml`) |
|
||||
|
||||
Adding a new language requires changes in **both** layers, as well as registering the locale in a few selector components.
|
||||
|
||||
---
|
||||
|
||||
## Step 1 – Frontend translation file
|
||||
|
||||
Copy the reference English file and translate all values.
|
||||
|
||||
```
|
||||
frontend/src/lang/modules/en.ts → frontend/src/lang/modules/<locale>.ts
|
||||
```
|
||||
|
||||
Replace every English string with its translation. Keep all keys, nested objects, and the `getFuLocaleMessage` call at the bottom unchanged:
|
||||
|
||||
```ts
|
||||
// frontend/src/lang/modules/<locale>.ts
|
||||
import { getFuLocaleMessage } from '@/lang/fu';
|
||||
|
||||
const message = {
|
||||
commons: {
|
||||
// ... translated strings
|
||||
},
|
||||
// ...
|
||||
};
|
||||
|
||||
export default {
|
||||
...getFuLocaleMessage('<locale>'),
|
||||
...message,
|
||||
};
|
||||
```
|
||||
|
||||
> **Tip:** The file is ~4 400 lines. Using a translation tool for a first pass is fine, but please review the output for accuracy and context.
|
||||
|
||||
---
|
||||
|
||||
## Step 2 – Register the locale loader
|
||||
|
||||
Open `frontend/src/lang/index.ts` and add your locale to `LOCALE_LOADERS`:
|
||||
|
||||
```ts
|
||||
// frontend/src/lang/index.ts
|
||||
const LOCALE_LOADERS: Record<string, LocaleLoader> = {
|
||||
// existing entries …
|
||||
'<locale>': () => import('./modules/<locale>'),
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3 – FU component translations
|
||||
|
||||
`frontend/src/lang/fu.ts` contains translations for custom FU table/steps components. Add a new entry for your locale:
|
||||
|
||||
```ts
|
||||
// frontend/src/lang/fu.ts
|
||||
const fuLocales: Record<string, FuLocaleMessage> = {
|
||||
// existing entries …
|
||||
'<locale>': {
|
||||
fu: {
|
||||
table: {
|
||||
more: '...',
|
||||
custom_table_rows: '...',
|
||||
},
|
||||
steps: {
|
||||
cancel: '...',
|
||||
prev: '...',
|
||||
next: '...',
|
||||
finish: '...',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4 – Element Plus component locale
|
||||
|
||||
Element Plus ships its own locale strings (used in date pickers, pagination, etc.). Import the matching locale pack and wire it into `frontend/src/App.vue`:
|
||||
|
||||
```ts
|
||||
// frontend/src/App.vue — import section
|
||||
import <varName> from 'element-plus/es/locale/lang/<ep-locale-code>';
|
||||
```
|
||||
|
||||
Then add a branch in the `i18nLocale` computed property:
|
||||
|
||||
```ts
|
||||
const i18nLocale = computed(() => {
|
||||
// existing branches …
|
||||
if (globalStore.language === '<locale>') return <varName>;
|
||||
return zhCn; // fallback unchanged
|
||||
});
|
||||
```
|
||||
|
||||
You can find all available Element Plus locale codes in `node_modules/element-plus/es/locale/lang/`.
|
||||
|
||||
---
|
||||
|
||||
## Step 5 – Login page language selector
|
||||
|
||||
Open `frontend/src/views/login/components/login-form.vue` and add your locale label to `languageLabelMap`:
|
||||
|
||||
```ts
|
||||
const languageLabelMap: Record<string, string> = {
|
||||
// existing entries …
|
||||
'<locale>': '<Native language name>',
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6 – Panel settings language selector
|
||||
|
||||
Open `frontend/src/views/setting/panel/index.vue` and add an option to `languageOptions`:
|
||||
|
||||
```ts
|
||||
const languageOptions = ref([
|
||||
// existing entries …
|
||||
{ value: '<locale>', label: '<Native language name>' },
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 7 – Backend translation files (core & agent)
|
||||
|
||||
The backend has **two** independent Go modules, each with its own YAML translation file. Copy the English reference and translate:
|
||||
|
||||
```
|
||||
core/i18n/lang/en.yaml → core/i18n/lang/<locale>.yaml (~264 lines)
|
||||
agent/i18n/lang/en.yaml → agent/i18n/lang/<locale>.yaml (~592 lines)
|
||||
```
|
||||
|
||||
YAML format example:
|
||||
|
||||
```yaml
|
||||
ErrInvalidParams: "Invalid request parameters: {{ .detail }}"
|
||||
ErrRecordExist: "Record already exists"
|
||||
# …
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 8 – Register backend locale in Go
|
||||
|
||||
Add your locale key and file path in both i18n registries:
|
||||
|
||||
**`core/i18n/i18n.go`**
|
||||
```go
|
||||
var langFiles = map[string]string{
|
||||
// existing entries …
|
||||
"<locale>": "lang/<locale>.yaml",
|
||||
}
|
||||
```
|
||||
|
||||
**`agent/i18n/i18n.go`**
|
||||
```go
|
||||
var langFiles = map[string]string{
|
||||
// existing entries …
|
||||
"<locale>": "lang/<locale>.yaml",
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist
|
||||
|
||||
Before opening a PR, verify the following:
|
||||
|
||||
- [ ] `frontend/src/lang/modules/<locale>.ts` created and all strings translated
|
||||
- [ ] Locale registered in `frontend/src/lang/index.ts` (`LOCALE_LOADERS`)
|
||||
- [ ] Locale entry added to `frontend/src/lang/fu.ts`
|
||||
- [ ] Element Plus locale imported and mapped in `frontend/src/App.vue`
|
||||
- [ ] Locale label added to `languageLabelMap` in `frontend/src/views/login/components/login-form.vue`
|
||||
- [ ] Locale option added to `languageOptions` in `frontend/src/views/setting/panel/index.vue`
|
||||
- [ ] `core/i18n/lang/<locale>.yaml` created
|
||||
- [ ] `agent/i18n/lang/<locale>.yaml` created
|
||||
- [ ] Locale registered in `core/i18n/i18n.go` (`langFiles`)
|
||||
- [ ] Locale registered in `agent/i18n/i18n.go` (`langFiles`)
|
||||
- [ ] Manually verified: new locale appears in the login language dropdown
|
||||
- [ ] Manually verified: new locale appears in **Settings → Panel → Language**
|
||||
- [ ] Manually verified: UI renders correctly after switching to the new locale
|
||||
|
||||
---
|
||||
|
||||
## Locale code conventions
|
||||
|
||||
Use [BCP 47](https://www.ietf.org/rfc/bcp/bcp47.txt) locale codes. Examples used in this project:
|
||||
|
||||
| Language | Locale code |
|
||||
|---|---|
|
||||
| Simplified Chinese | `zh` |
|
||||
| Traditional Chinese | `zh-Hant` |
|
||||
| English | `en` |
|
||||
| Japanese | `ja` |
|
||||
| Korean | `ko` |
|
||||
| Russian | `ru` |
|
||||
| Malay | `ms` |
|
||||
| Turkish | `tr` |
|
||||
| Brazilian Portuguese | `pt-BR` |
|
||||
| Spanish (Spain) | `es-ES` |
|
||||
|
||||
Use lowercase for simple codes (`ja`, `ko`) and the standard BCP 47 casing for regional variants (`pt-BR`, `es-ES`, `zh-Hant`).
|
||||
|
||||
**Frontend TypeScript file names** use all-lowercase with hyphens preserved exactly as they appear in the existing `frontend/src/lang/modules/` directory (e.g. `pt-br.ts`, `es-es.ts`, `zh-hant.ts`).
|
||||
|
||||
**Backend YAML file names** mirror the locale code exactly, preserving the original casing (e.g. `pt-BR.yaml`, `es-ES.yaml`, `zh-Hant.yaml`).
|
||||
|
||||
---
|
||||
|
||||
## PR title convention
|
||||
|
||||
```
|
||||
feat(i18n): add <language name> (<locale>) locale support
|
||||
```
|
||||
|
||||
Example: `feat(i18n): add German (de-DE) locale support`
|
||||
Reference in New Issue
Block a user