docs/TRANSLATION.md
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) — a merged, real-world example of adding a brand-new locale (Spanish
es-ES).
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.
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:
// 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.
Open frontend/src/lang/index.ts and add your locale to LOCALE_LOADERS:
// frontend/src/lang/index.ts
const LOCALE_LOADERS: Record<string, LocaleLoader> = {
// existing entries …
'<locale>': () => import('./modules/<locale>'),
};
frontend/src/lang/fu.ts contains translations for custom FU table/steps components. Add a new entry for your locale:
// frontend/src/lang/fu.ts
const fuLocales: Record<string, FuLocaleMessage> = {
// existing entries …
'<locale>': {
fu: {
table: {
more: '...',
custom_table_rows: '...',
},
steps: {
cancel: '...',
prev: '...',
next: '...',
finish: '...',
},
},
},
};
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:
// 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:
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/.
Open frontend/src/views/login/components/login-form.vue and add your locale label to languageLabelMap:
const languageLabelMap: Record<string, string> = {
// existing entries …
'<locale>': '<Native language name>',
};
Open frontend/src/views/setting/panel/index.vue and add an option to languageOptions:
const languageOptions = ref([
// existing entries …
{ value: '<locale>', label: '<Native language name>' },
]);
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:
ErrInvalidParams: "Invalid request parameters: {{ .detail }}"
ErrRecordExist: "Record already exists"
# …
Add your locale key and file path in both i18n registries:
core/i18n/i18n.go
var langFiles = map[string]string{
// existing entries …
"<locale>": "lang/<locale>.yaml",
}
agent/i18n/i18n.go
var langFiles = map[string]string{
// existing entries …
"<locale>": "lang/<locale>.yaml",
}
Before opening a PR, verify the following:
frontend/src/lang/modules/<locale>.ts created and all strings translatedfrontend/src/lang/index.ts (LOCALE_LOADERS)frontend/src/lang/fu.tsfrontend/src/App.vuelanguageLabelMap in frontend/src/views/login/components/login-form.vuelanguageOptions in frontend/src/views/setting/panel/index.vuecore/i18n/lang/<locale>.yaml createdagent/i18n/lang/<locale>.yaml createdcore/i18n/i18n.go (langFiles)agent/i18n/i18n.go (langFiles)Use BCP 47 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).
feat(i18n): add <language name> (<locale>) locale support
Example: feat(i18n): add German (de-DE) locale support