feat(i18n): enhance locale handling and routing (#996)

* feat(i18n): enhance locale handling and routing

- Implemented dynamic locale aliases in router configuration.
- Added support for preferred locale storage in global state.
- Improved locale resolution logic in router beforeEach guard.
- Created utility functions for locale management and path manipulation.
- Added tests for locale matching and message extraction.
- Updated Header component to allow language selection.
- Refactored getRouterPathWithLang to utilize new locale utilities.
- Updated Vite configuration to support aliasing for vue-i18n.
- Bumped version numbers across various packages to 1.9.0.

* feat(i18n): update version to 1.8.0 and enhance locale handling

- Updated version numbers across all package.json files to 1.8.0.
- Enhanced locale handling in App.vue by centralizing locale configurations.
- Improved Turnstile component to support dynamic language rendering.
- Refactored i18n utilities to include initial locale setup and empty locale messages.
- Updated i18n.ts to utilize the new locale management structure.
- Added naive-locale.ts for better integration with Naive UI's locale handling.
- Adjusted Header.vue to streamline language selection and locale changes.
- Fixed translations in multiple locale files for consistency and accuracy.

* fix(i18n): address review feedback

* feat(i18n): update default locale to English and enhance language handling in components

* fix(i18n): switch locale selector to dropdown

* docs: add topbar language and github order design spec

* fix(i18n): 修复 Header 语言切换器相关问题,恢复为独立控件并调整样式

* Refactor locale handling in router and add locale-guard utility functions

- Improved locale resolution logic in router by introducing utility functions for better readability and maintainability.
- Added `locale-guard.js` to encapsulate locale-related functions such as getting route locale, resolving locale for navigation, and applying locale navigation state.
- Updated JWT synchronization logic to streamline the handling of JWT from query parameters.
- Modified i18n messages test to check for coverage of registered locale message keys instead of extracting English source messages.

* 删除顶部栏语言和GitHub顺序设计文档

* fix: 修复前端设置初始化时未返回 domains 数组导致的 undefined 错误

* refactor(i18n): consolidate locale infrastructure

* fix(i18n): stabilize locale route switching

* fix(i18n): persist default locale selection

* fix(i18n): 修复前端设置初始化时未返回 domains 数组导致的 undefined 错误,统一按空数组兜底处理
feat(i18n): 添加 locale 别名处理,支持默认语言的重定向
test(i18n): 增加对默认语言别名重定向的测试用例

* refactor: replace useAppI18n with useScopedI18n in multiple components for improved localization management

* fix(tests): 移除不必要的 URL 断言以简化 Passkey 测试

* fix(i18n): 更新语言切换逻辑,确保使用当前语言设置进行路由导航

* fix(i18n): 强制路由切换以确保语言切换后正确导航

* refactor(i18n): 优化消息注册和路由本地化逻辑,移除冗余代码

* refactor(i18n): 拆分 API 文件以优化路由管理,更新语言处理逻辑

* fix: align i18n release notes and frontend test script
This commit is contained in:
bhwa233
2026-04-25 13:46:26 +08:00
committed by GitHub
parent 1e50bb0933
commit eb62c37e02
72 changed files with 5577 additions and 1993 deletions

View File

@@ -10,6 +10,7 @@
### Features
- feat: |Frontend| 前端新增 6 国语言支持(`zh` / `en` / `es` / `pt-BR` / `ja` / `de`),默认语言保持为 `zh`;无 locale 前缀路由(如 `/``/user`)默认使用中文渲染,同时会记录浏览器语言作为语言偏好。用户手动切换后会持久化语言偏好,并保持当前页面路径、查询参数与 canonical locale URL 一致
- feat: |API| 新增服务端解析邮件接口 `/api/parsed_mails``/api/parsed_mail/:id`,直接返回 `sender` / `subject` / `text` / `html` / `attachments` 元信息(复用 `commonParseMail`AI agent 侧不再需要引入 MIME 解析器
- feat: |Skill| 新增仓库内置只读 skill `cf-temp-mail-agent-mail``skills/cf-temp-mail-agent-mail/`),让 OpenClaw / Codex / Cursor 等 AI agent 凭用户提供的 Address JWT + API 地址读取邮箱、轮询验证码,绕开创建邮箱时的 Turnstile 人机验证;可通过 `npx degit dreamhunter2333/cloudflare_temp_email/skills/cf-temp-mail-agent-mail` 安装
- docs: |文档| 新增"AI Agent 使用邮箱"文档(`guide/feature/agent-email`),说明 `parsed_mail` API 用法,并在 parsed API 不可用时给出对齐前端的 `mail-parser-wasm` + `postal-mime` 本地解析回退方案

View File

@@ -10,6 +10,7 @@
### Features
- feat: |Frontend| Add six-language frontend support (`zh` / `en` / `es` / `pt-BR` / `ja` / `de`), keep `zh` as the default locale; locale-unprefixed routes (for example `/` and `/user`) render in Chinese by default while still recording browser language as the stored preference. Explicit locale switches are persisted, and the current route, query string, and canonical locale URL stay in sync during switching
- feat: |API| Add server-side parsed-mail endpoints `/api/parsed_mails` and `/api/parsed_mail/:id` that return `sender` / `subject` / `text` / `html` / `attachments` metadata directly (reuses `commonParseMail`), so AI agents no longer need a client-side MIME parser
- feat: |Skill| Bundle a read-only skill `cf-temp-mail-agent-mail` (`skills/cf-temp-mail-agent-mail/`) so AI agents like OpenClaw / Codex / Cursor can consume a mailbox with a user-supplied Address JWT + API base URL — list mails, poll verification codes, etc. — sidestepping the Turnstile challenge required to create a mailbox. Install via `npx degit dreamhunter2333/cloudflare_temp_email/skills/cf-temp-mail-agent-mail`
- docs: |Docs| Add "AI Agent Mailbox Usage" doc (`guide/feature/agent-email`) covering the `parsed_mail` API and a local-parse fallback using `mail-parser-wasm` + `postal-mime` (mirrors the frontend) when parsed endpoints are unavailable

View File

@@ -0,0 +1,64 @@
import { expect, test } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { FRONTEND_URL } from '../../fixtures/test-helpers';
const installLocaleInitScript = async (page: Page, locales: string[], preferredLocale: string | null = null) => {
await page.addInitScript(
({ locales: initialLocales, preferredLocale: initialPreferredLocale }: { locales: string[]; preferredLocale: string | null }) => {
const localeInitStorageKey = '__localeInitDone';
if (!window.sessionStorage.getItem(localeInitStorageKey)) {
window.localStorage.removeItem('preferredLocale');
if (initialPreferredLocale) {
window.localStorage.setItem('preferredLocale', initialPreferredLocale);
}
window.sessionStorage.setItem(localeInitStorageKey, '1');
}
Object.defineProperty(window.navigator, 'language', {
configurable: true,
get: () => initialLocales[0],
});
Object.defineProperty(window.navigator, 'languages', {
configurable: true,
get: () => initialLocales,
});
},
{ locales, preferredLocale },
);
};
const selectLanguage = async (page: Page, selectTrigger: Locator, optionLabel: string) => {
await selectTrigger.click();
const option = page.locator('.n-dropdown-option, .n-dropdown-option-body').filter({ hasText: optionLabel }).first();
await expect(option).toBeVisible();
await option.click();
};
test.describe('Locale switching', () => {
test('keeps default route in Chinese while persisting browser language preference', async ({ page }) => {
await installLocaleInitScript(page, ['es-ES', 'en-US']);
await page.goto(`${FRONTEND_URL}/`);
await expect(page).toHaveURL(`${FRONTEND_URL}/`);
await expect.poll(() => page.evaluate(() => window.localStorage.getItem('preferredLocale'))).toBe('es');
await expect.poll(() => page.evaluate(() => document.documentElement.lang)).toBe('zh');
});
test('mobile drawer switch updates locale route and persisted preference', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 844 });
await installLocaleInitScript(page, ['zh-CN']);
await page.goto(`${FRONTEND_URL}/`);
await page.getByRole('button', { name: /菜单|Menu/i }).click();
const drawerLocaleDropdown = page.locator('.n-drawer').getByRole('button', { name: /中文|English|Español|Português|日本語|Deutsch/ }).first();
await selectLanguage(page, drawerLocaleDropdown, 'Deutsch');
await expect(page).toHaveURL(`${FRONTEND_URL}/de/`);
await expect.poll(() => page.evaluate(() => window.localStorage.getItem('preferredLocale'))).toBe('de');
await expect.poll(() => page.evaluate(() => document.documentElement.lang)).toBe('de');
});
});

View File

@@ -18,7 +18,7 @@
"deploy:preview": "npm run build && wrangler pages deploy ./dist --branch preview",
"deploy": "npm run build && wrangler pages deploy ./dist --branch production",
"deploy:actions": "npm run build && wrangler pages deploy ./dist",
"test": "vitest run",
"test": "vitest run --passWithNoTests",
"test:watch": "vitest"
},
"dependencies": {

View File

@@ -1,6 +1,8 @@
<script setup>
import { darkTheme, NGlobalStyle, zhCN } from 'naive-ui'
import { computed, onMounted } from 'vue'
import {
darkTheme,
} from 'naive-ui'
import { computed, onMounted, watchEffect } from 'vue'
import { useScript } from '@unhead/vue'
import { useI18n } from 'vue-i18n'
import { useGlobalState } from './store'
@@ -8,20 +10,27 @@ import { useIsMobile } from './utils/composables'
import Header from './views/Header.vue';
import Footer from './views/Footer.vue';
import { api } from './api'
import { getNaiveLocaleConfig } from './i18n/naive-locale'
import { DEFAULT_LOCALE, isSupportedLocale } from './i18n/utils'
const {
isDark, loading, useSideMargin, telegramApp, isTelegram
} = useGlobalState()
const adClient = import.meta.env.VITE_GOOGLE_AD_CLIENT;
const adSlot = import.meta.env.VITE_GOOGLE_AD_SLOT;
const { locale } = useI18n({});
const { locale } = useI18n({ useScope: 'global' });
const theme = computed(() => isDark.value ? darkTheme : null)
const localeConfig = computed(() => locale.value == 'zh' ? zhCN : null)
const localeConfig = computed(() => getNaiveLocaleConfig(isSupportedLocale(locale.value) ? locale.value : DEFAULT_LOCALE))
const isMobile = useIsMobile()
const showSideMargin = computed(() => !isMobile.value && useSideMargin.value);
const showAd = computed(() => !isMobile.value && adClient && adSlot);
const gridMaxCols = computed(() => showAd.value ? 8 : 12);
watchEffect(() => {
if (typeof document === 'undefined') return
document.documentElement.lang = isSupportedLocale(locale.value) ? locale.value : DEFAULT_LOCALE
})
// Load Google Ad script at top level (not inside onMounted)
if (showAd.value) {
useScript({
@@ -77,7 +86,7 @@ onMounted(async () => {
</script>
<template>
<n-config-provider :locale="localeConfig" :theme="theme">
<n-config-provider :locale="localeConfig.locale" :date-locale="localeConfig.dateLocale" :theme="theme">
<n-global-style />
<n-spin description="loading..." :show="loading">
<n-notification-provider container-style="margin-top: 60px;">

View File

@@ -1,7 +1,7 @@
<script setup>
import { onMounted, ref, watch } from 'vue'
import { useLocalStorage } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useMessage } from 'naive-ui'
import useClipboard from 'vue-clipboard3'
import { Copy } from '@vicons/fa'
@@ -27,24 +27,7 @@ const {
jwt, settings, userJwt, isTelegram, openSettings, telegramApp
} = useGlobalState()
const { t } = useI18n({
messages: {
en: {
userAddresses: 'User Addresses',
localAddresses: 'Local Addresses',
address: 'Address',
copy: 'Copy',
copied: 'Copied',
},
zh: {
userAddresses: '用户地址',
localAddresses: '本地地址',
address: '地址',
copy: '复制',
copied: '已复制',
}
}
});
const { t } = useScopedI18n('components.AddressSelect')
const addressOptions = ref([])
const addressValue = ref(null)

View File

@@ -1,6 +1,6 @@
<script setup>
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useScopedI18n } from '@/i18n/app';
import { ContentCopyOutlined, LinkRound, CodeRound } from '@vicons/material';
import { useMessage } from 'naive-ui';
import { useGlobalState } from '../store';
@@ -32,30 +32,7 @@ const tagThemeOverrides = computed(() => {
return {}
});
const { t } = useI18n({
messages: {
en: {
authCode: 'Verification Code',
authLink: 'Authentication Link',
serviceLink: 'Service Link',
subscriptionLink: 'Subscription Link',
otherLink: 'Other Link',
copySuccess: 'Copied successfully',
copyFailed: 'Copy failed',
open: 'Open',
},
zh: {
authCode: '验证码',
authLink: '认证链接',
serviceLink: '服务链接',
subscriptionLink: '订阅链接',
otherLink: '其他链接',
copySuccess: '复制成功',
copyFailed: '复制失败',
open: '打开',
}
}
});
const { t } = useScopedI18n('components.AiExtractInfo')
const props = defineProps({
metadata: {

View File

@@ -1,7 +1,7 @@
<script setup>
import { watch, onMounted, ref, onBeforeUnmount, computed } from "vue";
import { useMessage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../store'
import { CloudDownloadRound, ArrowBackIosNewFilled, ArrowForwardIosFilled, InboxRound } from '@vicons/material'
import { useIsMobile } from '../utils/composables'
@@ -138,60 +138,7 @@ const showMultiActionDelete = ref(false)
const multiActionDownloadZip = ref({})
const multiActionDeleteProgress = ref({ percentage: 0, tip: '0/0' })
const { t } = useI18n({
messages: {
en: {
success: 'Success',
autoRefresh: 'Auto Refresh',
refreshAfter: 'Refresh After {msg} Seconds',
refresh: 'Refresh',
attachments: 'Show Attachments',
downloadMail: 'Download Mail',
pleaseSelectMail: "Please select mail",
emptyInbox: "Your inbox is empty",
delete: 'Delete',
deleteMailTip: 'Are you sure you want to delete mail?',
reply: 'Reply',
forwardMail: 'Forward',
showTextMail: 'Show Text Mail',
showHtmlMail: 'Show Html Mail',
saveToS3: 'Save to S3',
multiAction: 'Multi Action',
cancelMultiAction: 'Cancel Multi Action',
selectAll: 'Select All of This Page',
unselectAll: 'Unselect All',
prevMail: 'Previous',
nextMail: 'Next',
keywordQueryTip: 'Filter current page',
query: 'Query',
},
zh: {
success: '成功',
autoRefresh: '自动刷新',
refreshAfter: '{msg}秒后刷新',
refresh: '刷新',
downloadMail: '下载邮件',
attachments: '查看附件',
pleaseSelectMail: "请选择邮件",
emptyInbox: "收件箱为空",
delete: '删除',
deleteMailTip: '确定要删除邮件吗?',
reply: '回复',
forwardMail: '转发',
showTextMail: '显示纯文本邮件',
showHtmlMail: '显示HTML邮件',
saveToS3: '保存到S3',
multiAction: '多选',
cancelMultiAction: '取消多选',
selectAll: '全选本页',
unselectAll: '取消全选',
prevMail: '上一封',
nextMail: '下一封',
keywordQueryTip: '过滤当前页',
query: '查询',
}
}
});
const { t } = useScopedI18n('components.MailBox')
const setupAutoRefresh = async (autoRefresh) => {
// auto refresh every configAutoRefreshInterval seconds

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref } from "vue";
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { CloudDownloadRound, ReplyFilled, ForwardFilled, FullscreenRound } from '@vicons/material'
import ShadowHtmlComponent from "./ShadowHtmlComponent.vue";
import AiExtractInfo from "./AiExtractInfo.vue";
@@ -10,36 +10,7 @@ import { useGlobalState } from '../store';
const { preferShowTextMail, useIframeShowMail, useUTCDate, isDark } = useGlobalState();
const { t } = useI18n({
messages: {
en: {
delete: 'Delete',
deleteMailTip: 'Are you sure you want to delete mail?',
attachments: 'View Attachments',
downloadMail: 'Download Mail',
reply: 'Reply',
forward: 'Forward',
showTextMail: 'Show Text Mail',
showHtmlMail: 'Show HTML Mail',
saveToS3: 'Save to S3',
size: 'Size',
fullscreen: 'Fullscreen',
},
zh: {
delete: '删除',
deleteMailTip: '确定要删除邮件吗?',
attachments: '查看附件',
downloadMail: '下载邮件',
reply: '回复',
forward: '转发',
showTextMail: '显示纯文本邮件',
showHtmlMail: '显示HTML邮件',
saveToS3: '保存到S3',
size: '大小',
fullscreen: '全屏',
}
}
});
const { t } = useScopedI18n('components.MailContentRenderer')
const props = defineProps({
mail: {

View File

@@ -1,7 +1,7 @@
<script setup>
import { watch, onMounted, ref, computed } from "vue";
import { useMessage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../store'
import { useIsMobile } from '../utils/composables'
import { utcToLocalDate } from '../utils';
@@ -46,36 +46,7 @@ const multiActionMode = ref(false)
const showMultiActionDelete = ref(false)
const multiActionDeleteProgress = ref({ percentage: 0, tip: '0/0' })
const { t } = useI18n({
messages: {
en: {
success: 'Success',
refresh: 'Refresh',
showCode: 'Change View Original Code',
pleaseSelectMail: "Please select a mail to view.",
emptySent: "No sent emails",
delete: 'Delete',
deleteMailTip: 'Are you sure you want to delete mail?',
multiAction: 'Multi Action',
cancelMultiAction: 'Cancel Multi Action',
selectAll: 'Select All of This Page',
unselectAll: 'Unselect All',
},
zh: {
success: '成功',
refresh: '刷新',
showCode: '切换查看元数据',
pleaseSelectMail: "请选择一封邮件查看。",
emptySent: "发件箱为空",
delete: '删除',
deleteMailTip: '确定要删除邮件吗?',
multiAction: '多选',
cancelMultiAction: '取消多选',
selectAll: '全选本页',
unselectAll: '取消全选',
}
}
});
const { t } = useScopedI18n('components.SendBox')
watch([page, pageSize], async ([page, pageSize], [oldPage, oldPageSize]) => {
if (page !== oldPage || pageSize !== oldPageSize) {

View File

@@ -1,29 +1,32 @@
<script setup>
import { ref, watch, onMounted } from "vue";
import { useI18n } from 'vue-i18n'
import { ref, watch } from "vue";
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../store'
import { getTurnstileLocale } from '../i18n/locale-registry'
import { DEFAULT_LOCALE, isSupportedLocale } from '../i18n/utils'
const { openSettings, isDark } = useGlobalState()
const cfToken = defineModel('value')
const { locale, t } = useI18n({
messages: {
en: {
refresh: 'Refresh'
},
zh: {
refresh: '刷新'
}
}
});
const { locale, t } = useScopedI18n('components.Turnstile')
const containerId = `cf-turnstile-${Math.random().toString(36).slice(2, 9)}`
const cfTurnstileId = ref("")
const turnstileLoading = ref(false)
let turnstileRenderQueue = Promise.resolve()
const refresh = () => checkCfTurnstile(true)
const refresh = () => rerenderTurnstile()
defineExpose({ refresh })
const rerenderTurnstile = () => {
cfToken.value = "";
turnstileRenderQueue = turnstileRenderQueue
.catch(() => { })
.then(() => checkCfTurnstile(true))
turnstileRenderQueue.catch(() => { })
return turnstileRenderQueue
}
const checkCfTurnstile = async (remove) => {
if (!openSettings.value.cfTurnstileSiteKey) return;
turnstileLoading.value = true;
@@ -41,11 +44,15 @@ const checkCfTurnstile = async (remove) => {
if (remove && cfTurnstileId.value) {
window.turnstile.remove(cfTurnstileId.value);
}
// Cloudflare documents sitekey/theme/language as render-time options and
// exposes remove()/render() for widget lifecycle updates, so recreate the
// widget when any of those inputs change:
// https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/
cfTurnstileId.value = window.turnstile.render(
`#${containerId}`,
{
sitekey: openSettings.value.cfTurnstileSiteKey,
language: locale.value == 'zh' ? 'zh-CN' : 'en-US',
language: getTurnstileLocale(isSupportedLocale(locale.value) ? locale.value : DEFAULT_LOCALE),
theme: isDark.value ? 'dark' : 'light',
callback: function (token) {
cfToken.value = token;
@@ -57,14 +64,7 @@ const checkCfTurnstile = async (remove) => {
}
}
watch(isDark, async (isDark) => {
checkCfTurnstile(true)
}, { immediate: true })
onMounted(() => {
cfToken.value = "";
checkCfTurnstile(true);
})
watch([isDark, locale, () => openSettings.value.cfTurnstileSiteKey], rerenderTurnstile, { immediate: true })
</script>
<template>
@@ -73,7 +73,7 @@ onMounted(() => {
<n-form-item-row>
<n-flex vertical>
<div :id="containerId"></div>
<n-button text @click="checkCfTurnstile(true)">
<n-button text @click="rerenderTurnstile">
{{ t('refresh') }}
</n-button>
</n-flex>

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, ref, h } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import type { DropdownOption } from 'naive-ui'
const props = defineProps({
@@ -24,30 +24,7 @@ const props = defineProps({
// @ts-ignore
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
successTip: 'Success',
test: 'Test',
save: 'Save',
notEnabled: 'Webhook is not enabled for you',
urlMissing: 'URL is required',
enable: 'Enable',
presets: 'Presets',
fillInDemoTip: 'Please modify the URL and other settings to your own',
},
zh: {
successTip: '成功',
test: '测试',
save: '保存',
notEnabled: 'Webhook 未开启,请联系管理员开启',
urlMissing: 'URL 不能为空',
enable: '启用',
presets: '示例模板',
fillInDemoTip: '请修改URL和其他设置为您自己的配置',
}
}
});
const { t } = useScopedI18n('components.WebhookComponent')
class WebhookSettings {
enabled: boolean = false

View File

@@ -1,15 +0,0 @@
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
legacy: false, // you must set `false`, to use Composition API
locale: 'zh', // set locale
fallbackLocale: 'en', // set fallback locale
'en': {
messages: {}
},
'zh': {
messages: {}
}
})
export default i18n;

12
frontend/src/i18n/app.ts Normal file
View File

@@ -0,0 +1,12 @@
import { useI18n } from 'vue-i18n'
const withNamespace = (namespace: string, key: string) => `${namespace}.${key}`
export const useScopedI18n = (namespace: string) => {
const composer = useI18n({ useScope: 'global' })
return {
...composer,
t: ((key: string, ...args: unknown[]) => composer.t(withNamespace(namespace, key), ...(args as []))) as typeof composer.t,
}
}

View File

@@ -0,0 +1,18 @@
import { createI18n } from 'vue-i18n'
import {
FALLBACK_LOCALE,
getInitialLocale,
} from './utils'
import { I18N_MESSAGES } from './messages'
const i18n = createI18n({
legacy: false, // you must set `false`, to use Composition API
locale: getInitialLocale(),
fallbackLocale: FALLBACK_LOCALE,
messages: I18N_MESSAGES,
missingWarn: false,
fallbackWarn: false,
})
export default i18n;

View File

@@ -0,0 +1,107 @@
import {
dateDeDE,
dateEnUS,
dateEsAR,
dateJaJP,
datePtBR,
dateZhCN,
deDE,
enUS,
esAR,
jaJP,
ptBR,
zhCN,
} from 'naive-ui'
import type { NDateLocale, NLocale } from 'naive-ui'
type NaiveLocaleConfig = {
locale: NLocale
dateLocale: NDateLocale
}
type LocaleRegistryEntry = {
locale: string
label: string
browserMatches: string[]
naive: NaiveLocaleConfig
turnstileLocale: string
}
export const LOCALE_REGISTRY = [
{
locale: 'zh',
label: '中文',
browserMatches: ['zh'],
naive: { locale: zhCN, dateLocale: dateZhCN },
turnstileLocale: 'zh-CN',
},
{
locale: 'en',
label: 'English',
browserMatches: ['en'],
naive: { locale: enUS, dateLocale: dateEnUS },
turnstileLocale: 'en',
},
{
locale: 'es',
label: 'Español',
browserMatches: ['es'],
naive: { locale: esAR, dateLocale: dateEsAR },
turnstileLocale: 'es',
},
{
locale: 'pt-BR',
label: 'Português (Brasil)',
browserMatches: ['pt'],
naive: { locale: ptBR, dateLocale: datePtBR },
turnstileLocale: 'pt-BR',
},
{
locale: 'ja',
label: '日本語',
browserMatches: ['ja'],
naive: { locale: jaJP, dateLocale: dateJaJP },
turnstileLocale: 'ja',
},
{
locale: 'de',
label: 'Deutsch',
browserMatches: ['de'],
naive: { locale: deDE, dateLocale: dateDeDE },
turnstileLocale: 'de',
},
] as const satisfies readonly LocaleRegistryEntry[]
export type SupportedLocale = (typeof LOCALE_REGISTRY)[number]['locale']
export const SUPPORTED_LOCALES = LOCALE_REGISTRY.map(({ locale }) => locale) as SupportedLocale[]
const localeRegistryMap = Object.fromEntries(
LOCALE_REGISTRY.map((entry) => [entry.locale, entry]),
) as Record<SupportedLocale, (typeof LOCALE_REGISTRY)[number]>
export const getLocaleRegistryEntry = (locale: SupportedLocale) => {
return localeRegistryMap[locale]
}
export const getLocaleLabel = (locale: SupportedLocale) => {
return getLocaleRegistryEntry(locale).label
}
export const getLocaleOptions = () => {
return LOCALE_REGISTRY.map(({ locale, label }) => ({
label,
value: locale,
key: locale,
}))
}
export const getNaiveLocaleConfig = (locale: SupportedLocale) => {
return getLocaleRegistryEntry(locale).naive
}
export const getTurnstileLocale = (locale: SupportedLocale) => {
return getLocaleRegistryEntry(locale).turnstileLocale
}

View File

@@ -0,0 +1,590 @@
export const deMessages = {
"views.index.SimpleIndex.mailCount": "{current} / {total} E-Mails",
"views.admin.Statistics.activeAddressCount30days": "Aktive Adressanzahl in 30 Tagen",
"views.admin.Statistics.activeAddressCount7days": "Aktive Adressanzahl in 7 Tagen",
"views.Index.about": "Über",
"views.Admin.about": "Über",
"views.Header.accessHeader": "Zugangspasswort",
"views.Admin.account": "Konto",
"views.Index.accountSettings": "Kontoeinstellungen",
"views.Admin.account_settings": "Kontoeinstellungen",
"views.index.SimpleIndex.accountSettings": "Kontoeinstellungen",
"views.index.Attachment.action": "Aktion",
"views.admin.SenderAccess.action": "Aktion",
"views.user.UserSettings.actions": "Aktionen",
"views.user.AddressManagement.actions": "Aktionen",
"views.index.TelegramAddress.actions": "Aktionen",
"views.admin.Account.actions": "Aktionen",
"views.index.LocalAddress.actions": "Aktionen",
"views.admin.UserManagement.actions": "Aktionen",
"views.admin.AccountSettings.actions": "Aktionen",
"views.admin.AccountSettings.add": "Hinzufügen",
"views.admin.Maintenance.customSqlTip": "Füge benutzerdefinierte DELETE-SQL-Anweisungen für die geplante Bereinigung hinzu. Pro Eintrag ist nur eine DELETE-Anweisung erlaubt.",
"views.admin.Maintenance.addCustomSql": "Benutzerdefinierte SQL hinzufügen",
"views.admin.UserOauth2Settings.addOauth2": "Oauth2 hinzufügen",
"components.AddressSelect.address": "Adresse",
"views.user.AddressManagement.address": "Adresse",
"views.index.TelegramAddress.address": "Adresse",
"views.admin.SenderAccess.address": "Adresse",
"views.index.LocalAddress.address": "Adresse",
"views.admin.CreateAccount.address": "Adresse",
"views.admin.AiExtractSettings.allowList": "Adressfreigabeliste (Adresse eingeben und Enter drücken; Wildcards werden unterstützt)",
"views.admin.AccountSettings.send_address_block_list": "Blockierbegriffe für ausgehende E-Mails",
"views.admin.AccountSettings.address_block_list": "Blockierbegriffe für Benutzer (Administrator kann überspringen)",
"views.index.SimpleIndex.addressCopied": "Adresse erfolgreich kopiert",
"views.admin.Statistics.addressCount": "Adressanzahl",
"views.admin.UserManagement.address_count": "Adressanzahl",
"views.User.address_management": "Adressverwaltung",
"views.admin.UserManagement.userAddressManagement": "Adressverwaltung",
"views.index.AddressBar.addressPassword": "Adresspasswort",
"views.admin.CreateAccount.addressPassword": "Adresspasswort",
"views.Admin.adminAccount": "Administrator",
"views.Admin.accessHeader": "Admin-Passwort",
"views.Admin.loginViaPassword": "Administrator mit Passwort anmelden",
"views.admin.AiExtractSettings.title": "KI-E-Mail-Extraktionseinstellungen",
"views.Admin.aiExtractSettings": "KI-Extraktionseinstellungen",
"views.admin.AccountSettings.match_all": "Alle",
"views.admin.AccountSettings.create_address_subdomain_match": "Suffix-Abgleich für Subdomains beim Erstellen von Adressen erlauben",
"views.admin.UserSettings.enableUserRegister": "Benutzerregistrierung erlauben",
"views.admin.AccountSettings.match_any": "Beliebig",
"views.Index.appearance": "Darstellung",
"views.Admin.appearance": "Darstellung",
"views.admin.IpBlacklistSettings.tip_scope": "Gilt für: Adresse erstellen, E-Mail senden, externe Sende-API, Benutzerregistrierung und Code-Prüfung",
"views.index.AccountSettings.clearInboxConfirm": "Möchtest du wirklich alle E-Mails im Posteingang löschen?",
"views.index.AccountSettings.clearSentItemsConfirm": "Möchtest du wirklich alle E-Mails in den gesendeten Elementen löschen?",
"views.admin.Account.multiClearInboxTip": "Möchtest du wirklich den Posteingang der ausgewählten Adressen leeren?",
"views.admin.Account.clearInboxTip": "Möchtest du wirklich den Posteingang dieser E-Mail-Adresse leeren?",
"views.admin.Account.multiClearSentItemsTip": "Möchtest du wirklich die gesendeten Elemente der ausgewählten Adressen leeren?",
"views.admin.Account.clearSentItemsTip": "Möchtest du wirklich die gesendeten Elemente dieser E-Mail-Adresse leeren?",
"views.admin.Account.multiDeleteTip": "Möchtest du wirklich die ausgewählten Adressen löschen?",
"views.index.Attachment.deleteConfirm": "Möchtest du wirklich diesen Anhang löschen?",
"views.admin.Account.deleteTip": "Möchtest du wirklich diese E-Mail löschen?",
"views.admin.SenderAccess.deleteTip": "Möchtest du dies wirklich löschen?",
"views.index.AccountSettings.deleteAccountConfirm": "Möchtest du wirklich dein Konto und alle zugehörigen E-Mails löschen?",
"views.index.AccountSettings.logoutConfirm": "Möchtest du dich wirklich abmelden?",
"components.MailBox.deleteMailTip": "Möchtest du die E-Mail wirklich löschen?",
"components.MailContentRenderer.deleteMailTip": "Möchtest du die E-Mail wirklich löschen?",
"components.SendBox.deleteMailTip": "Möchtest du die E-Mail wirklich löschen?",
"views.admin.AccountSettings.delete_rule_confirm": "Möchtest du diese Regel wirklich löschen?",
"views.admin.UserManagement.deleteUserTip": "Möchtest du diesen Benutzer wirklich löschen?",
"views.Admin.logoutConfirmContent": "Möchtest du dich wirklich aus dem Admin-Bereich abmelden?",
"views.user.UserSettings.logoutConfirm": "Möchtest du dich wirklich abmelden?",
"views.admin.IpBlacklistSettings.asn_blacklist": "ASN-Organisationssperrliste",
"views.admin.IpBlacklistSettings.tip_asn": "ASN-Organisation: Nach ISP/Provider blockieren. Groß-/Kleinschreibung-unabhängiger Textabgleich oder Regex.",
"components.AiExtractInfo.authLink": "Authentifizierungslink",
"views.admin.Maintenance.autoCleanup": "Automatische Bereinigung",
"components.MailBox.autoRefresh": "Automatische Aktualisierung",
"views.common.Appearance.autoRefreshInterval": "Automatisches Aktualisierungsintervall (s)",
"views.Index.auto_reply": "Automatische Antwort",
"views.index.AutoReply.autoReply": "Automatische Antwort",
"views.common.Login.autoGeneratedName": "Automatisch erzeugter Name",
"views.admin.SenderAccess.balance": "Guthaben",
"views.admin.Maintenance.basicCleanup": "Grundbereinigung",
"views.user.AddressManagement.unbindAddressTip": "Wechsle vor dem Trennen zu dieser E-Mail-Adresse und speichere die Zugangsdaten.",
"views.index.TelegramAddress.bind": "Verknüpfen",
"views.index.TelegramAddress.bindAddressSuccess": "Adresse erfolgreich verknüpft",
"views.index.LocalAddress.bindAddressSuccess": "Adresse erfolgreich verknüpft",
"views.User.bind_address": "Mailadresse verknüpfen",
"views.admin.IpBlacklistSettings.enable_tip": "IPs blockieren, die Sperrlistenmustern entsprechen und auf limitierte APIs zugreifen",
"views.admin.AccountSettings.fromBlockList": "Blockierbegriffe für eingehende E-Mails",
"views.admin.AccountSettings.block_receive_unknow_address_email": "Empfang von E-Mails an unbekannte Adressen blockieren",
"views.common.Appearance.bottom": "unten",
"views.admin.IpBlacklistSettings.fingerprint_blacklist": "Browser-Fingerprint-Sperrliste",
"views.admin.IpBlacklistSettings.tip_fingerprint": "Browser-Fingerprint: Sperrt per Fingerprint. Unterstützt exakte Treffer oder Regex.",
"views.admin.AccountSettings.cancel": "Abbrechen",
"components.MailBox.cancelMultiAction": "Mehrfachaktion abbrechen",
"components.SendBox.cancelMultiAction": "Mehrfachaktion abbrechen",
"views.user.AddressManagement.changeMailAddress": "Adresse wechseln",
"views.index.TelegramAddress.changeMailAddress": "Mailadresse ändern",
"views.index.LocalAddress.changeMailAddress": "Mailadresse ändern",
"views.index.AccountSettings.changePassword": "Passwort ändern",
"views.admin.UserManagement.changeRole": "Ändern Rolle",
"components.SendBox.showCode": "Ansicht des Originalcodes umschalten",
"views.admin.Telegram.status": "Status prüfen",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env_note": "Wenn du „Umgebungsvariable folgen“ auswählst, wird die Admin-Überschreibung entfernt und der Zustand auf „nicht gesetzt“ zurückgesetzt. Das tatsächliche Ergebnis wird weiterhin durch die Worker-Umgebung und die Prioritätsregeln bestimmt.",
"views.admin.Maintenance.cleanupNow": "Jetzt bereinigen",
"views.admin.Maintenance.cleanupSuccess": "Bereinigung erfolgreich",
"views.admin.Maintenance.addressCreateLabel": "Adressen löschen, die vor mehr als n Tagen erstellt wurden",
"views.admin.Maintenance.emptyAddressLabel": "Leere Adressen löschen, die älter als n Tage sind",
"views.admin.Maintenance.inactiveAddressLabel": "Inaktive Adressen löschen, die älter als n Tage sind",
"views.admin.Maintenance.mailBoxLabel": "Posteingänge löschen, die älter als n Tage sind",
"views.admin.Maintenance.sendBoxLabel": "Postausgänge löschen, die älter als n Tage sind",
"views.admin.Maintenance.unboundAddressLabel": "Nicht verknüpfte Adressen löschen, die älter als n Tage sind",
"views.admin.Maintenance.mailUnknowLabel": "E-Mails mit unbekanntem Empfänger löschen, die älter als n Tage sind",
"views.index.AccountSettings.clearInbox": "Posteingang leeren",
"views.admin.Account.clearInbox": "Posteingang leeren",
"views.index.AccountSettings.clearSentItems": "Gesendete Elemente leeren",
"views.admin.Account.clearSentItems": "Gesendete Elemente leeren",
"views.Header.title": "Cloudflare Temporäre E-Mail",
"views.admin.DatabaseManager.code_db_version": "Vom Code benötigte DB-Version",
"views.user.UserOauth2Callback.codeNotFound": "Code nicht gefunden",
"views.admin.AccountSettings.config": "Konfiguration",
"views.admin.RoleAddressConfig.roleConfigDesc": "Lege die maximale Anzahl von Adressen pro Benutzerrolle fest. Rollenbasierte Limits haben Vorrang vor globalen Einstellungen. Verwende 0 für unbegrenzt.",
"views.Admin.confirm": "Bestätigen",
"views.Admin.logoutConfirmTitle": "Abmeldung bestätigen",
"views.index.AccountSettings.confirmPassword": "Passwort bestätigen",
"views.index.SendMail.content": "Inhalt",
"views.admin.SendMail.content": "Inhalt",
"views.index.SendMail.contentEmpty": "Der Inhalt ist leer",
"views.admin.SendMail.contentEmpty": "Der Inhalt ist leer",
"components.AddressSelect.copied": "Kopiert",
"components.AiExtractInfo.copySuccess": "Erfolgreich kopiert",
"components.AddressSelect.copy": "Kopieren",
"views.index.SimpleIndex.copyAddress": "Kopieren",
"components.AiExtractInfo.copyFailed": "Kopieren fehlgeschlagen",
"views.Footer.copyright": "Urheberrecht",
"views.Admin.account_create": "Konto erstellen",
"views.admin.CreateAccount.creatNewEmail": "Neue E-Mail erstellen",
"views.common.Login.getNewEmail": "Neue E-Mail erstellen",
"views.user.AddressManagement.create_or_bind": "Erstellen oder verknüpfen",
"views.index.LocalAddress.create_or_bind": "Erstellen oder verknüpfen",
"views.user.UserSettings.createPasskey": "Passkey erstellen",
"views.admin.UserManagement.createUser": "Benutzer erstellen",
"views.user.UserSettings.created_at": "Erstellt am",
"views.admin.Account.created_at": "Erstellt am",
"views.admin.SenderAccess.created_at": "Erstellt am",
"views.admin.UserManagement.created_at": "Erstellt am",
"views.common.Login.credentialLogin": "Mit Zugangsdaten anmelden",
"views.admin.DatabaseManager.current_db_version": "Aktuelle DB-Version",
"views.user.UserBar.currentUser": "Aktuell angemeldeter Benutzer",
"views.admin.UserManagement.roleDonotExist": "Die aktuelle Rolle existiert nicht",
"views.admin.Maintenance.customSqlCleanup": "Benutzerdefinierte SQL-Bereinigung",
"views.admin.AccountSettings.send_mail_daily_limit": "Tageslimit",
"views.admin.AccountSettings.send_mail_daily_limit_invalid": "Das Tageslimit muss eine ganze Zahl größer oder gleich -1 sein",
"views.admin.IpBlacklistSettings.tip_daily_limit": "Tageslimit: Begrenzt die maximale Anzahl von Anfragen pro IP-Adresse und Tag (1-1000000).",
"views.admin.IpBlacklistSettings.daily_request_limit": "Tägliches Anfrage-Limit",
"views.Header.dark": "Dunkel",
"views.Admin.database": "Datenbank",
"views.admin.DatabaseManager.need_initialization_tip": "Die Datenbank muss initialisiert werden. Bitte zuerst initialisieren.",
"views.admin.DatabaseManager.initializationSuccess": "Datenbank erfolgreich initialisiert",
"views.admin.DatabaseManager.migrationSuccess": "Datenbank erfolgreich migriert",
"views.admin.DatabaseManager.need_migration_tip": "Die Datenbank muss migriert werden. Bitte zuerst migrieren.",
"components.MailBox.delete": "Löschen",
"components.MailContentRenderer.delete": "Löschen",
"components.SendBox.delete": "Löschen",
"views.index.Attachment.delete": "Löschen",
"views.admin.Account.delete": "Löschen",
"views.admin.UserOauth2Settings.delete": "Löschen",
"views.admin.SenderAccess.delete": "Löschen",
"views.admin.UserManagement.delete": "Löschen",
"views.admin.AccountSettings.delete_rule": "Löschen",
"views.admin.Maintenance.deleteCustomSql": "Löschen",
"views.index.AccountSettings.deleteAccount": "Konto löschen",
"views.admin.Account.deleteAccount": "Konto löschen",
"views.user.UserSettings.deletePasskey": "Passkey löschen",
"views.admin.AccountSettings.delete_success": "Erfolgreich gelöscht",
"views.admin.UserManagement.deleteUser": "Benutzer löschen",
"views.index.Attachment.deleteSuccess": "Erfolgreich gelöscht",
"views.admin.SenderAccess.disable": "Deaktivieren",
"views.Admin.loginViaDisabledCheck": "Passwortprüfung deaktiviert",
"views.common.Appearance.preferShowTextMail": "Text-Mail standardmäßig anzeigen",
"views.admin.AccountSettings.domain_list": "Domain-Liste (optional)",
"views.admin.AccountSettings.source_patterns_tip": "Die Domain-Liste filtert nach Empfängeradresse, die Quell-Regex nach Absenderadresse. Für die Weiterleitung müssen beide Bedingungen erfüllt sein (UND-Logik). Leer lassen, um den jeweiligen Filter zu überspringen.",
"views.admin.UserManagement.domains": "Domains",
"views.index.Attachment.download": "Herunterladen",
"components.MailBox.downloadMail": "Herunterladen Mail",
"components.MailContentRenderer.downloadMail": "Herunterladen Mail",
"views.admin.Maintenance.sqlNamePlaceholder": "z. B. Alte Logs bereinigen",
"views.admin.Maintenance.sqlPlaceholder": "z. B. DELETE FROM raw_mails WHERE source GLOB '*{'@'}example.com' AND created_at < datetime('now', '-3 day')",
"views.admin.AccountSettings.source_patterns_placeholder": "z. B. gmail.com",
"views.admin.AccountSettings.forwarding_rule_warning": "Jede Regel wird unabhängig ausgeführt. Die Weiterleitungsadresse muss verifiziert sein.",
"views.index.SendMail.edit": "Bearbeiten",
"views.admin.SendMail.edit": "Bearbeiten",
"views.user.UserLogin.email": "E-Mail",
"views.admin.UserManagement.email": "E-Mail",
"views.common.Login.email": "E-Mail",
"views.common.Login.credential": "E-Mail-Adresszugangsdaten",
"views.common.Login.emailPasswordRequired": "E-Mail und Passwort sind erforderlich",
"views.admin.UserSettings.emailCheckRegex": "Regex zur E-Mail-Prüfung (z. B. ^[^.]+{'@'}.+$, um Punkte vor {'@'} zu verbieten)",
"views.admin.AccountSettings.email_forwarding_config": "E-Mail-Weiterleitungskonfiguration",
"views.admin.UserOauth2Settings.userEmailFormat": "E-Mail-Regex-Muster",
"views.Admin.mails": "E-Mails",
"views.index.AutoReply.sourcePrefixPlaceholder": "Leer=alle, Präfixabgleich oder /regex/",
"components.WebhookComponent.enable": "Aktivieren",
"views.admin.UserOauth2Settings.enable": "Aktivieren",
"views.admin.SenderAccess.enable": "Aktivieren",
"views.admin.Telegram.enable": "Aktivieren",
"views.admin.UserSettings.enable": "Aktivieren",
"views.admin.AiExtractSettings.enableAllowList": "Aktivieren Adressfreigabeliste",
"views.admin.Webhook.enableAllowList": "Freigabeliste aktivieren (Webhook-Zugriff auf bestimmte Benutzer beschränken)",
"views.index.AutoReply.enableAutoReply": "Automatische Antwort aktivieren",
"views.admin.Maintenance.cronTip": "Um die Cron-Bereinigung zu aktivieren, konfiguriere [crons] im Worker. Siehe Dokumentation; 0 Tage bedeutet alles löschen.",
"views.admin.IpBlacklistSettings.enable_daily_limit": "Aktivieren Tägliches Anfrage-Limit",
"views.admin.UserSettings.enableEmailCheckRegex": "E-Mail-Prüfregex aktivieren",
"views.admin.UserOauth2Settings.enableEmailFormat": "E-Mail-Format aktivieren",
"views.admin.Telegram.enableGlobalMailPush": "Globalen Mail-Push aktivieren (Telegram-Chat-ID manuell eingeben)",
"views.admin.IpBlacklistSettings.enable_ip_blacklist": "IP-Sperrliste aktivieren",
"views.admin.IpBlacklistSettings.enable_ip_whitelist": "IP-Freigabeliste aktivieren (streng)",
"views.admin.UserOauth2Settings.enableMailAllowList": "Aktivieren E-Mail-Adressfreigabeliste(Manually enterable)",
"views.admin.UserSettings.enableMailAllowList": "Aktivieren E-Mail-Adressfreigabeliste(Manually enterable)",
"views.admin.UserSettings.enableMailVerify": "Mail-Prüfung aktivieren (die Absenderadresse muss im System vorhanden sein, Guthaben haben und normal senden können)",
"views.admin.Telegram.enableTelegramAllowList": "Aktivieren Telegram-Freigabeliste(Manually input Chat ID)",
"views.admin.IpBlacklistSettings.asn_blacklist_placeholder": "ASN-Organisation eingeben (z. B. Google, Amazon)",
"views.admin.IpBlacklistSettings.fingerprint_blacklist_placeholder": "Fingerprint-ID eingeben (z. B. a1b2c3d4e5f6g7h8)",
"views.admin.IpBlacklistSettings.daily_request_limit_placeholder": "Limit eingeben (z. B. 1000)",
"views.admin.IpBlacklistSettings.ip_blacklist_placeholder": "Muster eingeben (z. B. 192.168.1 oder ^10\\.0\\.0\\.5$)",
"views.common.Login.bindUserAddressError": "Fehler beim Verknüpfen der E-Mail-Adresse mit dem Benutzer",
"views.admin.IpBlacklistSettings.ip_whitelist_placeholder": "Exakte IP (z. B. 1.2.3.4) oder verankerte Regex (z. B. ^192\\.168\\.1\\.\\d+$)",
"views.index.SimpleIndex.exitSimpleIndex": "Einfachen Modus verlassen",
"components.MailBox.keywordQueryTip": "Aktuelle Seite filtern",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env": "Umgebungsvariable folgen",
"views.admin.AccountSettings.create_address_subdomain_match_force_disable": "Erzwingt deaktivieren",
"views.admin.AccountSettings.create_address_subdomain_match_force_enable": "Erzwingt aktivieren",
"views.user.UserLogin.forgotPassword": "Passwort vergessen",
"components.MailBox.forwardMail": "Weiterleiten",
"components.MailContentRenderer.forward": "Weiterleiten",
"views.admin.AccountSettings.forward_address": "Weiterleitungsadresse",
"views.admin.AccountSettings.forward_address_required": "Eine Weiterleitungsadresse ist erforderlich",
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
"components.MailContentRenderer.fullscreen": "Vollbild",
"views.common.Login.generateName": "Zufälligen Namen erzeugen",
"views.admin.Telegram.globalMailPushList": "Globale Mail-Push-Chat-ID-Liste",
"views.common.Appearance.globalTabplacement": "Globale Tab-Position",
"views.common.Login.help": "Hilfe",
"views.Header.home": "Startseite",
"views.index.SendMail.html": "HTML",
"views.admin.SendMail.html": "HTML",
"views.admin.UserOauth2Settings.icon": "Icon (SVG, bitte nur aus vertrauenswürdiger Quelle)",
"views.admin.CreateAccount.enablePrefix": "Wenn Präfix aktiviert ist",
"views.common.AdminContact.adminContact": "Wenn du Hilfe brauchst, kontaktiere bitte den Administrator ({msg})",
"views.admin.Telegram.init": "Initialisieren",
"views.admin.DatabaseManager.init": "Datenbank initialisieren",
"views.admin.AccountSettings.regex_invalid": "Ungültiges Regex-Muster",
"views.Admin.ipBlacklistSettings": "IP-Sperrliste",
"views.admin.IpBlacklistSettings.ip_blacklist": "Muster der IP-Sperrliste",
"views.admin.IpBlacklistSettings.title": "Einstellungen der IP-Sperrliste",
"views.admin.IpBlacklistSettings.tip_ip": "IP-Sperrliste: Unterstützt Textabgleich (z. B. \"192.168.1\") oder Regex (z. B. \"^10\\.0\\.0\\.5$\").",
"views.admin.IpBlacklistSettings.whitelist_empty_warning": "Die IP-Freigabeliste ist aktiviert, aber leer. Der Server ignoriert dies, um eine Aussperrung zu vermeiden. Bitte vor dem Aktivieren mindestens einen Eintrag hinzufügen.",
"views.admin.IpBlacklistSettings.ip_whitelist": "IP-Freigabeliste Muster",
"views.admin.IpBlacklistSettings.tip_whitelist": "IP-Freigabeliste: Strenger Freigabemodus. Einfache Einträge müssen EXAKT zur IP passen (keine Teilzeichenfolge). Verwende verankerte Regex (^...$) für Bereiche. Freigegebene IPs überspringen die Sperrlistenprüfung.",
"views.admin.SenderAccess.is_enabled": "Aktiviert",
"views.admin.Account.itemCount": "itemCount",
"views.admin.SenderAccess.itemCount": "itemCount",
"views.admin.UserManagement.itemCount": "itemCount",
"views.user.UserMailBox.addressQueryTip": "Leer lassen, um alle Adressen abzufragen",
"views.admin.Account.addressQueryTip": "Leer lassen, um alle Adressen abzufragen",
"views.admin.Mails.addressQueryTip": "Leer lassen, um alle Adressen abzufragen",
"views.common.Appearance.left": "links",
"views.common.Login.getNewEmailTip2": "Wenn es leer bleibt, wird eine zufällige E-Mail-Adresse erzeugt.",
"views.Header.light": "Hell",
"views.admin.IpBlacklistSettings.enable_daily_limit_tip": "Anzahl der API-Anfragen pro IP-Adresse und Tag begrenzen",
"components.AddressSelect.localAddresses": "Lokale Adressen",
"views.common.Login.bindUserInfo": "Angemeldete Benutzer: Anmeldung ohne verknüpfte E-Mail oder das Erstellen einer neuen Adresse verknüpft sie mit dem aktuellen Benutzer",
"views.user.UserOauth2Callback.logging": "Anmeldung läuft...",
"views.user.UserLogin.login": "Anmelden",
"views.common.Login.login": "Anmelden",
"views.common.Login.loginAndBind": "Anmelden und verknüpfen",
"views.Admin.loginMethod": "Anmeldemethode",
"views.user.UserBar.fetchUserSettingsError": "Das Anmeldepasswort ist ungültig oder das Konto existiert nicht. Es könnte auch ein Netzwerkproblem vorliegen. Bitte später erneut versuchen.",
"views.user.UserLogin.loginWith": "Mit {provider} anmelden",
"views.user.UserLogin.loginWithPasskey": "Mit Passkey anmelden",
"views.user.UserSettings.logout": "Abmelden",
"views.user.BindAddress.logout": "Abmelden",
"views.Admin.logout": "Abmelden",
"views.index.AccountSettings.logout": "Abmelden",
"views.Admin.logoutSuccess": "Erfolgreich abgemeldet",
"views.admin.UserOauth2Settings.mailAllowList": "E-Mail-Adressfreigabeliste",
"views.admin.UserSettings.mailAllowList": "E-Mail-Adressfreigabeliste",
"views.index.SimpleIndex.addressCredential": "E-Mail-Adresszugangsdaten",
"views.index.AddressBar.addressCredential": "E-Mail-Adresszugangsdaten",
"views.admin.Account.addressCredential": "E-Mail-Adresszugangsdaten",
"views.admin.CreateAccount.addressCredential": "E-Mail-Adresszugangsdaten",
"views.index.AddressBar.fetchAddressError": "Die E-Mail-Zugangsdaten sind ungültig oder das Konto existiert nicht. Es kann auch ein Netzwerkproblem sein. Bitte später erneut versuchen.",
"views.Index.mailbox": "Posteingang",
"views.User.user_mail_box_tab": "Posteingang",
"views.user.AddressManagement.mail_count": "E-Mail-Anzahl",
"views.admin.Account.mail_count": "E-Mail-Anzahl",
"views.admin.Statistics.mailCount": "E-Mail-Anzahl",
"views.admin.UserAddressManagement.mail_count": "E-Mail-Anzahl",
"views.index.SimpleIndex.deleteSuccess": "E-Mail erfolgreich gelöscht",
"views.user.UserLogin.cannotForgotPassword": "E-Mail-Verifizierung oder Registrierung ist deaktiviert; das Passwort kann nicht zurückgesetzt werden. Bitte den Administrator kontaktieren.",
"views.Admin.mailWebhook": "Mail-Webhook",
"views.common.Appearance.mailboxSplitSize": "Größe der Mailbox-Aufteilung",
"views.index.SimpleIndex.refreshSuccess": "E-Mails erfolgreich aktualisiert",
"views.Admin.unknow": "E-Mails mit unbekanntem Empfänger",
"views.Admin.maintenance": "Wartung",
"views.index.AddressBar.addressManage": "Verwalten",
"views.admin.AccountSettings.source_match_mode": "Abgleichsmodus",
"views.admin.RoleAddressConfig.maxAddressCount": "Maximale Adressanzahl (0 = unbegrenzt)",
"views.admin.UserSettings.maxAddressCount": "Maximale Anzahl verknüpfbarer E-Mail-Adressen (0 = unbegrenzt)",
"views.Header.menu": "Menü",
"views.admin.DatabaseManager.migration": "Datenbank migrieren",
"views.admin.SenderAccess.modify": "Ändern",
"views.admin.AccountSettings.send_mail_monthly_limit": "Monatslimit",
"views.admin.AccountSettings.send_mail_monthly_limit_invalid": "Das Monatslimit muss eine ganze Zahl größer oder gleich -1 sein",
"components.MailBox.multiAction": "Mehrfachaktion",
"components.SendBox.multiAction": "Mehrfachaktion",
"views.admin.Account.multiClearInbox": "Mehrere Posteingänge leeren",
"views.admin.Account.multiClearSentItems": "Mehrere gesendete Elemente leeren",
"views.admin.Account.multiDelete": "Mehrfach löschen",
"views.user.AddressManagement.name": "Name",
"views.index.AutoReply.name": "Name",
"views.admin.Account.name": "Name",
"views.admin.UserOauth2Settings.name": "Name",
"views.admin.UserAddressManagement.name": "Name",
"views.admin.Maintenance.sqlName": "Name",
"views.index.AccountSettings.newPassword": "Neues Passwort",
"views.admin.Account.newPassword": "Neues Passwort",
"components.MailBox.nextMail": "Weiter",
"views.index.SimpleIndex.nextPage": "Weiter",
"views.admin.AccountSettings.noLimitSendAddressList": "Adressliste ohne Guthabenlimit",
"views.index.SimpleIndex.noMails": "Keine E-Mails gefunden",
"views.admin.RoleAddressConfig.noRolesAvailable": "In der Systemkonfiguration sind keine Rollen verfügbar",
"views.index.SendMail.requestAccessTip": "Noch kein Sendeguthaben vorhanden. Wenn der Administrator ein Standardguthaben aktiviert hat, wird es automatisch zugewiesen; andernfalls Zugriff anfordern oder den Administrator kontaktieren.",
"components.SendBox.emptySent": "Keine gesendeten E-Mails",
"views.admin.RoleAddressConfig.notConfigured": "Nicht konfiguriert (globale Einstellungen verwenden)",
"views.Admin.userOauth2Settings": "OAuth2-Einstellungen",
"views.admin.UserOauth2Settings.oauth2Type": "OAuth2-Typ",
"views.Header.ok": "OK",
"views.Admin.ok": "OK",
"views.index.AddressBar.ok": "OK",
"views.admin.SenderAccess.ok": "OK",
"views.common.Login.ok": "OK",
"views.admin.AccountSettings.create_address_subdomain_match_tip": "Wirkt sich nur auf die Domainvalidierung von /api/new_address und /admin/new_address aus. Beispiel: Wenn aktiviert, kann foo.example.com der Basisdomain example.com entsprechen.",
"components.AiExtractInfo.open": "Öffnen",
"views.index.AddressBar.linkWithAddressCredential": "Automatischen E-Mail-Anmeldelink öffnen",
"views.admin.CreateAccount.linkWithAddressCredential": "Automatischen E-Mail-Anmeldelink öffnen",
"views.index.SendMail.options": "Optionen",
"views.admin.SendMail.options": "Optionen",
"components.AiExtractInfo.otherLink": "Weiterer Link",
"views.user.UserSettings.passkeyCreated": "Passkey erfolgreich erstellt",
"views.user.UserSettings.passkey_name": "Passkey-Name",
"views.user.UserLogin.password": "Passwort",
"views.admin.UserManagement.password": "Passwort",
"views.common.Login.password": "Passwort",
"views.index.AccountSettings.passwordChanged": "Passwort erfolgreich geändert",
"views.common.Login.passwordLogin": "Passwortanmeldung",
"views.admin.Account.passwordResetSuccess": "Passwort erfolgreich zurückgesetzt",
"views.index.AccountSettings.passwordMismatch": "Passwörter stimmen nicht überein",
"views.index.SendMail.successSend": "Bitte den Postausgang prüfen. Falls es fehlschlägt, Guthaben prüfen oder später erneut versuchen.",
"views.admin.SendMail.successSend": "Bitte prüfe deinen Postausgang. Falls es fehlgeschlagen ist, versuche es später erneut.",
"views.user.UserLogin.pleaseCompleteTurnstile": "Bitte Turnstile abschließen",
"views.index.SimpleIndex.addressCredentialTip": "Bitte kopiere die E-Mail-Adresszugangsdaten. Du kannst sie zum Anmelden verwenden.",
"views.index.AddressBar.addressCredentialTip": "Bitte kopiere die E-Mail-Adresszugangsdaten. Du kannst sie verwenden, um dich bei deinem E-Mail-Konto anzumelden.",
"views.admin.Account.addressCredentialTip": "Bitte kopiere die E-Mail-Adresszugangsdaten. Du kannst sie verwenden, um dich bei deinem E-Mail-Konto anzumelden.",
"views.admin.CreateAccount.addressCredentialTip": "Bitte kopiere die E-Mail-Adresszugangsdaten. Du kannst sie verwenden, um dich bei deinem E-Mail-Konto anzumelden.",
"views.Admin.accessTip": "Bitte das Admin-Passwort eingeben",
"views.Header.accessTip": "Bitte das korrekte Zugangspasswort eingeben",
"views.admin.AccountSettings.address_block_list_placeholder": "Bitte die zu blockierenden Schlüsselwörter eingeben",
"views.user.UserSettings.renamePasskeyNamePlaceholder": "Bitte den neuen Passkey-Namen eingeben",
"views.user.UserSettings.passkeyNamePlaceholder": "Bitte den Passkey-Namen eingeben oder leer lassen, um einen zufälligen zu erzeugen",
"views.admin.CreateAccount.fillInAllFields": "Bitte alle Felder ausfüllen",
"views.admin.SendBox.queryTip": "Bitte die abzufragende Adresse eingeben; leer lassen für alle",
"views.user.UserLogin.pleaseInputCode": "Bitte den Code eingeben",
"views.admin.UserManagement.pleaseInput": "Bitte alle erforderlichen Informationen eingeben",
"views.user.UserLogin.pleaseInputEmail": "Bitte die E-Mail eingeben",
"views.user.UserLogin.pleaseInput": "Bitte E-Mail und Passwort eingeben",
"views.admin.Maintenance.tip": "Bitte die Tage eingeben",
"views.common.Login.getNewEmailTip1": "Bitte die gewünschte E-Mail-Adresse eingeben. Erlaubt sind nur:",
"views.common.Login.credentialInput": "Bitte die Zugangsdaten der Mailadresse eingeben",
"views.admin.SenderAccess.modalTip": "Bitte das Sendeguthaben eingeben",
"views.user.UserLogin.pleaseLogin": "Bitte anmelden",
"views.common.Login.pleaseGetNewEmail": "Bitte anmelden oder auf „Neue E-Mail erstellen“ klicken, um eine neue Adresse zu erhalten.",
"components.WebhookComponent.fillInDemoTip": "Bitte URL und weitere Einstellungen an deine Umgebung anpassen",
"components.SendBox.pleaseSelectMail": "Bitte eine E-Mail zum Anzeigen auswählen.",
"views.admin.Account.pleaseSelectAddress": "Bitte eine Adresse auswählen",
"components.MailBox.pleaseSelectMail": "Bitte eine E-Mail auswählen",
"views.admin.UserManagement.prefix": "Präfix",
"components.WebhookComponent.presets": "Voreinstellungen",
"views.index.SendMail.preview": "Vorschau",
"views.admin.SendMail.preview": "Vorschau",
"views.admin.UserOauth2Settings.iconPreview": "Vorschau",
"components.MailBox.prevMail": "Zurück",
"views.index.SimpleIndex.prevPage": "Zurück",
"components.MailBox.query": "Abfragen",
"views.user.UserMailBox.query": "Abfragen",
"views.Index.query": "Abfragen",
"views.admin.SendBox.query": "Abfragen",
"views.admin.Account.query": "Abfragen",
"views.admin.SenderAccess.query": "Abfragen",
"views.admin.UserManagement.query": "Abfragen",
"views.admin.Mails.query": "Abfragen",
"views.Admin.qucickSetup": "Schnelleinrichtung",
"views.index.SendMail.toMailEmpty": "Die Empfängeradresse ist leer",
"views.admin.SendMail.toMailEmpty": "Die Empfängeradresse ist leer",
"views.index.SendMail.toName": "Name und Adresse des Empfängers; Namen leer lassen, um die E-Mail-Adresse zu verwenden",
"views.admin.SendMail.toName": "Name und Adresse des Empfängers; Namen leer lassen, um die E-Mail-Adresse zu verwenden",
"components.MailBox.refresh": "Aktualisieren",
"components.Turnstile.refresh": "Aktualisieren",
"components.SendBox.refresh": "Aktualisieren",
"views.index.SimpleIndex.refreshMails": "Aktualisieren",
"components.MailBox.refreshAfter": "Aktualisieren in {msg} Sekunden",
"views.index.SimpleIndex.refreshAfter": "Aktualisieren in {msg} Sekunden",
"views.admin.AccountSettings.regex_too_long": "Regex-Muster zu lang (max. 200 Zeichen)",
"views.user.UserLogin.register": "Registrieren",
"views.user.UserSettings.renamePasskey": "Passkey umbenennen",
"views.admin.UserOauth2Settings.userEmailReplace": "Ersetzungsvorlage",
"components.MailBox.reply": "Antworten",
"components.MailContentRenderer.reply": "Antworten",
"views.index.SendMail.requestAccess": "Zugriff anfordern",
"views.user.UserLogin.resetPassword": "Zurücksetzen Passwort",
"views.admin.Account.resetPassword": "Zurücksetzen Passwort",
"views.admin.UserManagement.resetPassword": "Zurücksetzen Passwort",
"views.index.SendMail.rich text": "Rich Text",
"views.admin.SendMail.rich text": "Rich Text",
"views.common.Appearance.right": "rechts",
"views.admin.UserManagement.role": "Rolle",
"views.admin.RoleAddressConfig.role": "Rolle",
"views.Admin.roleAddressConfig": "Rollenbasierte Adresskonfiguration",
"views.admin.AccountSettings.rule_index": "Regel",
"views.Index.s3Attachment": "S3-Anhang",
"components.WebhookComponent.save": "Speichern",
"views.index.AutoReply.save": "Speichern",
"views.admin.AiExtractSettings.save": "Speichern",
"views.admin.UserOauth2Settings.save": "Speichern",
"views.admin.IpBlacklistSettings.save": "Speichern",
"views.admin.Telegram.save": "Speichern",
"views.admin.UserSettings.save": "Speichern",
"views.admin.RoleAddressConfig.save": "Speichern",
"views.admin.AccountSettings.save": "Speichern",
"views.admin.Maintenance.save": "Speichern",
"views.admin.Webhook.save": "Speichern",
"views.admin.Maintenance.saveSuccess": "Erfolgreich gespeichert",
"views.admin.UserOauth2Settings.successTip": "Erfolgreich gespeichert",
"views.admin.IpBlacklistSettings.successTip": "Erfolgreich gespeichert",
"views.admin.UserSettings.successTip": "Erfolgreich gespeichert",
"views.admin.AccountSettings.successTip": "Erfolgreich gespeichert",
"components.MailBox.saveToS3": "In S3 speichern",
"components.MailContentRenderer.saveToS3": "In S3 speichern",
"views.Index.saveToS3Success": "In S3 gespeichert",
"components.MailBox.selectAll": "Alles auf dieser Seite auswählen",
"components.SendBox.selectAll": "Alles auf dieser Seite auswählen",
"views.admin.Account.selectAll": "Alles auf dieser Seite auswählen",
"views.admin.AccountSettings.select_domain": "Domain auswählen",
"views.admin.Account.selectedItems": "Ausgewählt",
"views.index.SendMail.send": "Senden",
"views.admin.SendMail.send": "Senden",
"views.Index.sendbox": "Postausgang",
"views.Admin.sendBox": "Postausgang",
"views.user.AddressManagement.send_count": "Sendeanzahl",
"views.admin.Account.send_count": "Sendeanzahl",
"views.admin.UserAddressManagement.send_count": "Sendeanzahl",
"views.Index.sendmail": "E-Mail senden",
"views.Admin.sendMail": "E-Mail senden",
"views.index.SendMail.send_balance": "Verbleibendes Sendeguthaben",
"views.admin.Statistics.sendMailCount": "Anzahl gesendeter E-Mails",
"views.admin.AccountSettings.send_mail_limit": "Sende-Limit",
"views.user.UserLogin.sendVerificationCode": "Bestätigungscode senden",
"views.Admin.senderAccess": "Sender-Zugriffssteuerung",
"views.admin.SendMail.fromMailEmpty": "Die Absenderadresse ist leer",
"views.index.AutoReply.sourcePrefix": "Absenderfilter",
"components.AiExtractInfo.serviceLink": "Servicelink",
"views.index.AutoReply.settings": "Einstellungen",
"views.index.AccountSettings.showAddressCredential": "Adresszugangsdaten anzeigen",
"components.MailBox.attachments": "Anhänge anzeigen",
"components.MailBox.showHtmlMail": "HTML-Mail anzeigen",
"components.MailContentRenderer.showHtmlMail": "HTML-Mail anzeigen",
"views.admin.Account.showCredential": "E-Mail-Adresszugangsdaten anzeigen",
"views.user.UserSettings.showPasskeyList": "Passkey-Liste anzeigen",
"components.MailBox.showTextMail": "Textmail anzeigen",
"components.MailContentRenderer.showTextMail": "Textmail anzeigen",
"views.Index.enterSimpleMode": "Einfacher Modus",
"components.MailContentRenderer.size": "Größe",
"views.admin.Account.source_meta": "Quelle",
"views.admin.AccountSettings.source_patterns": "Regex für Quelladresse (optional)",
"views.admin.Maintenance.sqlStatement": "SQL-Anweisung (nur DELETE)",
"views.user.UserOauth2Callback.stateNotMatch": "State stimmt nicht überein",
"views.Admin.statistics": "Statistiken",
"views.Header.status": "Status",
"views.admin.IpBlacklistSettings.enable_whitelist_tip": "Strenger Modus: Nur IPs aus der Freigabeliste dürfen auf limitierte APIs zugreifen. Alle anderen IPs werden abgewiesen.",
"views.index.AutoReply.subject": "Betreff",
"views.index.SendMail.subject": "Betreff",
"views.admin.SendMail.subject": "Betreff",
"views.index.SendMail.subjectEmpty": "Der Betreff ist leer",
"views.admin.SendMail.subjectEmpty": "Der Betreff ist leer",
"components.AiExtractInfo.subscriptionLink": "Abonnement-Link",
"views.user.AddressManagement.success": "Erfolg",
"views.index.TelegramAddress.success": "Erfolg",
"views.index.LocalAddress.success": "Erfolg",
"views.admin.UserAddressManagement.success": "Erfolg",
"components.WebhookComponent.successTip": "Erfolg",
"components.MailBox.success": "Erfolg",
"components.SendBox.success": "Erfolg",
"views.index.AccountSettings.success": "Erfolg",
"views.index.AutoReply.success": "Erfolg",
"views.admin.AiExtractSettings.successTip": "Erfolg",
"views.admin.Account.success": "Erfolg",
"views.admin.SenderAccess.success": "Erfolg",
"views.admin.UserManagement.success": "Erfolg",
"views.admin.Telegram.successTip": "Erfolg",
"views.admin.RoleAddressConfig.successTip": "Erfolg",
"views.admin.Webhook.successTip": "Erfolg",
"views.admin.CreateAccount.successTip": "Erfolgreich erstellt",
"views.admin.Telegram.globalMailPushListTip": "Unterstützt chat_id von Privat-/Gruppen-/Kanal-Chats. Sende deinem Bot eine Nachricht und rufe dann diesen Link auf, um die chat_id zu sehen: https://api.telegram.org/bot<Ersetze durch dein BOT TOKEN>/getUpdates",
"views.user.AddressManagement.targetUserEmail": "Zielbenutzer-E-Mail",
"views.admin.Telegram.telegramAllowList": "Telegram-Freigabeliste (Chat-ID manuell eingeben)",
"views.Admin.telegram": "Telegram-Bot",
"views.admin.Telegram.miniAppUrl": "Telegram Mini App URL",
"components.WebhookComponent.test": "Test",
"views.index.SendMail.text": "Text",
"views.admin.SendMail.text": "Text",
"views.user.UserSettings.passordTip": "Der Server erhält nur den Hash des Passworts und niemals das Klartext-Passwort. Daher kann er dein Passwort weder sehen noch wiederherstellen. Wenn der Administrator die E-Mail-Verifizierung aktiviert, kannst du es im Inkognito-Modus zurücksetzen.",
"views.index.LocalAddress.tip": "Diese Adressen werden in deinem Browser gespeichert und können verloren gehen, wenn du den Browser-Cache leerst.",
"views.admin.UserOauth2Settings.tip": "Drittanbieter-Login verwendet automatisch die E-Mail-Adresse des Benutzers zur Registrierung eines Kontos (dieselbe E-Mail gilt als dasselbe Konto). Das Konto entspricht dem regulär registrierten Konto, und das Passwort kann auch über „Passwort vergessen“ gesetzt werden.",
"views.admin.AccountSettings.send_mail_limit_tip": "Dies gilt für alle Sendekanäle. Verwende -1 für unbegrenzt und 0, um das Senden zu blockieren.",
"views.admin.AccountSettings.create_address_subdomain_match_note": "Dies unterscheidet sich von RANDOM_SUBDOMAIN_DOMAINS: Dieser Schalter erlaubt API-Aufrufern, benutzerdefinierte Subdomains direkt anzugeben, während die zufällige Subdomain nur bei der Erstellung automatisch erzeugt wird.",
"views.index.SendMail.tooLarge": "Datei zu groß; bitte eine Datei unter 1 MB hochladen.",
"views.admin.SendMail.tooLarge": "Datei zu groß; bitte eine Datei unter 1 MB hochladen.",
"views.common.Appearance.top": "oben",
"views.user.AddressManagement.transferAddress": "Adresse übertragen",
"views.user.AddressManagement.transferAddressTip": "Wenn du diese Adresse an einen anderen Benutzer überträgst, wird sie aus deinem Konto entfernt und in dessen Konto verschoben. Möchtest du die Adresse wirklich übertragen?",
"views.common.Appearance.useSideMargin": "Seitliche Abstände links und rechts auf der Seite aktivieren",
"views.admin.AiExtractSettings.manualInputPrompt": "Eingeben und mit Enter hinzufügen",
"views.admin.UserOauth2Settings.manualInputPrompt": "Eingeben und mit Enter hinzufügen",
"views.admin.Telegram.manualInputPrompt": "Eingeben und mit Enter hinzufügen",
"views.admin.UserSettings.manualInputPrompt": "Eingeben und mit Enter hinzufügen",
"views.admin.AccountSettings.manualInputPrompt": "Eingeben und mit Enter hinzufügen",
"views.admin.Webhook.manualInputPrompt": "Eingeben und mit Enter hinzufügen",
"views.admin.IpBlacklistSettings.manualInputPrompt": "Muster eingeben und Enter drücken, um es hinzuzufügen",
"views.user.AddressManagement.unbindAddress": "Adressverknüpfung lösen",
"views.index.TelegramAddress.unbindMailAddress": "Mailadressverknüpfung lösen",
"views.index.LocalAddress.unbindMailAddress": "Mailadresszugangsdaten lösen",
"components.MailBox.unselectAll": "Auswahl aufheben",
"components.SendBox.unselectAll": "Auswahl aufheben",
"views.admin.Account.unselectAll": "Auswahl aufheben",
"views.admin.Account.updated_at": "Aktualisiert am",
"views.user.UserSettings.updated_at": "Aktualisiert am",
"components.WebhookComponent.urlMissing": "URL ist erforderlich",
"views.common.Appearance.useIframeShowMail": "HTML-Mail per iframe anzeigen",
"views.admin.CreateAccount.enableRandomSubdomain": "Zufällige Subdomain verwenden",
"views.common.Login.enableRandomSubdomain": "Zufällige Subdomain verwenden",
"views.admin.UserOauth2Settings.userEmailFormatTip": "Regex verwenden, um die E-Mail umzuwandeln. Beispiel: ^(.+)@old\\.com$ mit $1@new.com",
"views.common.Appearance.useSimpleIndex": "Einfachen Index verwenden",
"views.common.Appearance.useUTCDate": "UTC-Datum verwenden",
"views.Header.user": "Benutzer",
"views.Admin.user": "Benutzer",
"components.AddressSelect.userAddresses": "Benutzeradressen",
"views.Admin.loginViaUserAdmin": "Benutzer-Admin-Berechtigung",
"views.admin.Statistics.userCount": "Benutzeranzahl",
"views.admin.UserManagement.user_email": "Benutzer-E-Mail",
"views.index.AddressBar.userLogin": "Benutzeranmeldung",
"views.Admin.user_management": "Benutzerverwaltung",
"views.Admin.user_settings": "Benutzereinstellungen",
"views.User.user_settings": "Benutzereinstellungen",
"components.AiExtractInfo.authCode": "Bestätigungscode",
"views.user.UserLogin.verifyCode": "Bestätigungscode",
"views.user.UserLogin.verifyCodeSent": "Bestätigungscode gesendet, läuft ab in {timeout} Sekunden",
"views.admin.AccountSettings.verified_address_list": "Liste verifizierter Adressen (kann E-Mails über die interne cf-API senden)",
"views.admin.UserSettings.verifyMailSender": "Mail-Absender verifizieren",
"components.MailContentRenderer.attachments": "Anhänge anzeigen",
"views.admin.Account.viewMails": "E-Mails anzeigen",
"views.admin.Account.viewSendBox": "Postausgang anzeigen",
"views.user.UserLogin.waitforVerifyCode": "Warten {timeout} Sekunden",
"views.admin.Webhook.webhookAllowList": "Webhook-Freigabeliste (erlaubte Mailadresse eingeben und Enter drücken)",
"views.admin.Webhook.notEnabled": "Webhook ist nicht aktiviert",
"components.WebhookComponent.notEnabled": "Webhook ist für dich nicht aktiviert",
"views.Index.webhookSettings": "Webhook-Einstellungen",
"views.Admin.webhookSettings": "Webhook-Einstellungen",
"views.admin.AiExtractSettings.disabledTip": "Wenn deaktiviert, verarbeitet die KI-Extraktion alle E-Mail-Adressen",
"views.admin.AiExtractSettings.enableAllowListTip": "Wenn aktiviert, verarbeitet die KI-Extraktion nur E-Mails an Adressen auf der Freigabeliste",
"views.admin.CreateAccount.randomSubdomainTip": "Wenn aktiviert, verwendet die erstellte Adresse eine zufällige Subdomain. Subdomain-Adressen werden nur für den Empfang empfohlen.",
"views.common.Login.randomSubdomainTip": "Wenn aktiviert, verwendet die erstellte Adresse eine zufällige Subdomain. Subdomain-Adressen werden nur für den Empfang empfohlen.",
"views.admin.AiExtractSettings.allowListTip": "Der Platzhalter * passt auf beliebige Zeichen; z. B. passt *{'@'}example.com auf alle Adressen der Domain example.com",
"views.Admin.workerconfig": "Worker-Konfiguration",
"views.admin.AccountSettings.create_address_subdomain_match_env_locked": "Die Worker-Umgebungsvariable ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH ist derzeit false. Der gespeicherte Admin-Schalter kann geändert werden, wird aber erst wirksam, wenn die Umgebungsvariable aktiviert oder entfernt wird.",
"views.common.Login.getNewEmailTip3": "Du kannst eine Domain aus der Dropdown-Liste auswählen.",
"views.admin.AccountSettings.tip": "Die folgenden Mehrfachauswahlwerte können manuell eingegeben und mit Enter hinzugefügt werden",
"components.MailBox.emptyInbox": "Dein Posteingang ist leer",
"views.index.SendMail.fromName": "Dein Name und deine Adresse; Namen leer lassen, um die E-Mail-Adresse zu verwenden",
"views.admin.SendMail.fromName": "Dein Name und deine Adresse; Namen leer lassen, um die E-Mail-Adresse zu verwenden"
}

View File

@@ -0,0 +1,590 @@
export const esMessages = {
"views.index.SimpleIndex.mailCount": "{current} / {total} correos",
"views.admin.Statistics.activeAddressCount30days": "Cantidad de direcciones activas en 30 días",
"views.admin.Statistics.activeAddressCount7days": "Cantidad de direcciones activas en 7 días",
"views.Index.about": "Acerca de",
"views.Admin.about": "Acerca de",
"views.Header.accessHeader": "Contraseña de acceso",
"views.Admin.account": "Cuenta",
"views.Index.accountSettings": "Configuración de la cuenta",
"views.Admin.account_settings": "Configuración de la cuenta",
"views.index.SimpleIndex.accountSettings": "Configuración de la cuenta",
"views.index.Attachment.action": "Acción",
"views.admin.SenderAccess.action": "Acción",
"views.user.UserSettings.actions": "Acciones",
"views.user.AddressManagement.actions": "Acciones",
"views.index.TelegramAddress.actions": "Acciones",
"views.admin.Account.actions": "Acciones",
"views.index.LocalAddress.actions": "Acciones",
"views.admin.UserManagement.actions": "Acciones",
"views.admin.AccountSettings.actions": "Acciones",
"views.admin.AccountSettings.add": "Añadir",
"views.admin.Maintenance.customSqlTip": "Añade sentencias SQL DELETE personalizadas para la limpieza programada. Solo se permite una sentencia DELETE por entrada.",
"views.admin.Maintenance.addCustomSql": "Añadir SQL personalizada",
"views.admin.UserOauth2Settings.addOauth2": "Añadir OAuth2",
"components.AddressSelect.address": "Dirección",
"views.user.AddressManagement.address": "Dirección",
"views.index.TelegramAddress.address": "Dirección",
"views.admin.SenderAccess.address": "Dirección",
"views.index.LocalAddress.address": "Dirección",
"views.admin.CreateAccount.address": "Dirección",
"views.admin.AiExtractSettings.allowList": "Lista blanca de direcciones (introduce una dirección y pulsa Enter; admite comodines)",
"views.admin.AccountSettings.send_address_block_list": "Palabras clave bloqueadas para enviar correo",
"views.admin.AccountSettings.address_block_list": "Palabras clave bloqueadas para usuarios (el administrador puede omitirlo)",
"views.index.SimpleIndex.addressCopied": "Dirección copiada correctamente",
"views.admin.Statistics.addressCount": "Cantidad de direcciones",
"views.admin.UserManagement.address_count": "Cantidad de direcciones",
"views.User.address_management": "Gestión de direcciones",
"views.admin.UserManagement.userAddressManagement": "Gestión de direcciones",
"views.index.AddressBar.addressPassword": "Contraseña de la dirección",
"views.admin.CreateAccount.addressPassword": "Contraseña de la dirección",
"views.Admin.adminAccount": "Administrador",
"views.Admin.accessHeader": "Contraseña de administrador",
"views.Admin.loginViaPassword": "Inicio de sesión de administrador con contraseña",
"views.admin.AiExtractSettings.title": "Configuración de extracción de correo con IA",
"views.Admin.aiExtractSettings": "Configuración de extracción con IA",
"views.admin.AccountSettings.match_all": "Todos",
"views.admin.AccountSettings.create_address_subdomain_match": "Permitir coincidencia por sufijo de subdominio al crear direcciones",
"views.admin.UserSettings.enableUserRegister": "Permitir registro de usuarios",
"views.admin.AccountSettings.match_any": "Cualquiera",
"views.Index.appearance": "Apariencia",
"views.Admin.appearance": "Apariencia",
"views.admin.IpBlacklistSettings.tip_scope": "Se aplica a: crear dirección, enviar correo, API externa de envío, registro de usuario y verificación de código",
"views.index.AccountSettings.clearInboxConfirm": "¿Seguro que quieres vaciar todos los correos de tu bandeja de entrada?",
"views.index.AccountSettings.clearSentItemsConfirm": "¿Seguro que quieres vaciar todos los correos enviados?",
"views.admin.Account.multiClearInboxTip": "¿Seguro que quieres vaciar la bandeja de entrada de las direcciones seleccionadas?",
"views.admin.Account.clearInboxTip": "¿Seguro que quieres vaciar la bandeja de entrada de este correo?",
"views.admin.Account.multiClearSentItemsTip": "¿Seguro que quieres vaciar los enviados de las direcciones seleccionadas?",
"views.admin.Account.clearSentItemsTip": "¿Seguro que quieres vaciar los enviados de este correo?",
"views.admin.Account.multiDeleteTip": "¿Seguro que quieres eliminar las direcciones seleccionadas?",
"views.index.Attachment.deleteConfirm": "¿Seguro que quieres eliminar este adjunto?",
"views.admin.Account.deleteTip": "¿Seguro que quieres eliminar este correo?",
"views.admin.SenderAccess.deleteTip": "¿Seguro que quieres eliminar esto?",
"views.index.AccountSettings.deleteAccountConfirm": "¿Seguro que quieres eliminar tu cuenta y todos sus correos?",
"views.index.AccountSettings.logoutConfirm": "¿Seguro que quieres cerrar sesión?",
"components.MailBox.deleteMailTip": "¿Seguro que quieres eliminar el correo?",
"components.MailContentRenderer.deleteMailTip": "¿Seguro que quieres eliminar el correo?",
"components.SendBox.deleteMailTip": "¿Seguro que quieres eliminar el correo?",
"views.admin.AccountSettings.delete_rule_confirm": "¿Seguro que quieres eliminar esta regla?",
"views.admin.UserManagement.deleteUserTip": "¿Seguro que quieres eliminar este usuario?",
"views.Admin.logoutConfirmContent": "¿Seguro que quieres salir del panel de administración?",
"views.user.UserSettings.logoutConfirm": "¿Seguro que quieres cerrar sesión?",
"views.admin.IpBlacklistSettings.asn_blacklist": "Lista negra de organizaciones ASN",
"views.admin.IpBlacklistSettings.tip_asn": "Organización ASN: bloquear por ISP/proveedor. Coincidencia de texto sin distinción de mayúsculas o regex.",
"components.AiExtractInfo.authLink": "Enlace de autenticación",
"views.admin.Maintenance.autoCleanup": "Limpieza automática",
"components.MailBox.autoRefresh": "Actualización automática",
"views.common.Appearance.autoRefreshInterval": "Intervalo de actualización automática (s)",
"views.Index.auto_reply": "Respuesta automática",
"views.index.AutoReply.autoReply": "Respuesta automática",
"views.common.Login.autoGeneratedName": "Nombre generado automáticamente",
"views.admin.SenderAccess.balance": "Saldo",
"views.admin.Maintenance.basicCleanup": "Limpieza básica",
"views.user.AddressManagement.unbindAddressTip": "Antes de desvincular, cambia a esta dirección y guarda la credencial del correo.",
"views.index.TelegramAddress.bind": "Vincular",
"views.index.TelegramAddress.bindAddressSuccess": "Dirección vinculada correctamente",
"views.index.LocalAddress.bindAddressSuccess": "Dirección vinculada correctamente",
"views.User.bind_address": "Vincular dirección de correo",
"views.admin.IpBlacklistSettings.enable_tip": "Bloquear IP que coincidan con la lista negra para las API limitadas",
"views.admin.AccountSettings.fromBlockList": "Palabras bloqueadas para recibir correo",
"views.admin.AccountSettings.block_receive_unknow_address_email": "Bloquear correos para direcciones desconocidas",
"views.common.Appearance.bottom": "abajo",
"views.admin.IpBlacklistSettings.fingerprint_blacklist": "Lista negra de huella del navegador",
"views.admin.IpBlacklistSettings.tip_fingerprint": "Huella del navegador: bloquea por huella. Admite coincidencia exacta o regex.",
"views.admin.AccountSettings.cancel": "Cancelar",
"components.MailBox.cancelMultiAction": "Cancelar selección múltiple",
"components.SendBox.cancelMultiAction": "Cancelar selección múltiple",
"views.user.AddressManagement.changeMailAddress": "Cambiar dirección",
"views.index.TelegramAddress.changeMailAddress": "Cambiar dirección de correo",
"views.index.LocalAddress.changeMailAddress": "Cambiar dirección de correo",
"views.index.AccountSettings.changePassword": "Cambiar contraseña",
"views.admin.UserManagement.changeRole": "Cambiar Rol",
"components.SendBox.showCode": "Cambiar vista del código original",
"views.admin.Telegram.status": "Ver estado",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env_note": "Elegir \"Seguir variable de entorno\" elimina la anulación del administrador y vuelve al estado sin definir. El resultado efectivo sigue dependiendo de las variables del Worker y de las reglas de prioridad.",
"views.admin.Maintenance.cleanupNow": "Limpiar ahora",
"views.admin.Maintenance.cleanupSuccess": "Limpieza completada",
"views.admin.Maintenance.addressCreateLabel": "Limpiar las direcciones creadas hace más de n días",
"views.admin.Maintenance.emptyAddressLabel": "Limpiar las direcciones vacías de hace más de n días",
"views.admin.Maintenance.inactiveAddressLabel": "Limpiar las direcciones inactivas de hace más de n días",
"views.admin.Maintenance.mailBoxLabel": "Limpiar la bandeja de entrada de hace más de n días",
"views.admin.Maintenance.sendBoxLabel": "Limpiar la bandeja de salida de hace más de n días",
"views.admin.Maintenance.unboundAddressLabel": "Limpiar las direcciones no vinculadas de hace más de n días",
"views.admin.Maintenance.mailUnknowLabel": "Limpiar los correos con destinatario desconocido de hace más de n días",
"views.index.AccountSettings.clearInbox": "Vaciar bandeja de entrada",
"views.admin.Account.clearInbox": "Vaciar bandeja de entrada",
"views.index.AccountSettings.clearSentItems": "Vaciar enviados",
"views.admin.Account.clearSentItems": "Vaciar enviados",
"views.Header.title": "Cloudflare Correo Temporal",
"views.admin.DatabaseManager.code_db_version": "Versión de BD requerida por el código",
"views.user.UserOauth2Callback.codeNotFound": "código no encontrado",
"views.admin.AccountSettings.config": "Configuración",
"views.admin.RoleAddressConfig.roleConfigDesc": "Configura el número máximo de direcciones para cada rol. Los límites por rol tienen prioridad sobre la configuración global. Usa 0 para ilimitado.",
"views.Admin.confirm": "Confirmar",
"views.Admin.logoutConfirmTitle": "Confirmar cierre de sesión",
"views.index.AccountSettings.confirmPassword": "Confirmar contraseña",
"views.index.SendMail.content": "Contenido",
"views.admin.SendMail.content": "Contenido",
"views.index.SendMail.contentEmpty": "El contenido está vacío",
"views.admin.SendMail.contentEmpty": "El contenido está vacío",
"components.AddressSelect.copied": "Copiado",
"components.AiExtractInfo.copySuccess": "Copiado correctamente",
"components.AddressSelect.copy": "Copiar",
"views.index.SimpleIndex.copyAddress": "Copiar",
"components.AiExtractInfo.copyFailed": "Error al copiar",
"views.Footer.copyright": "Derechos de autor",
"views.Admin.account_create": "Crear cuenta",
"views.admin.CreateAccount.creatNewEmail": "Crear nuevo correo",
"views.common.Login.getNewEmail": "Crear nuevo correo",
"views.user.AddressManagement.create_or_bind": "Crear o vincular",
"views.index.LocalAddress.create_or_bind": "Crear o vincular",
"views.user.UserSettings.createPasskey": "Crear passkey",
"views.admin.UserManagement.createUser": "Crear usuario",
"views.user.UserSettings.created_at": "Creado el",
"views.admin.Account.created_at": "Creado el",
"views.admin.SenderAccess.created_at": "Creado el",
"views.admin.UserManagement.created_at": "Creado el",
"views.common.Login.credentialLogin": "Inicio de sesión con credencial",
"views.admin.DatabaseManager.current_db_version": "Versión actual de la BD",
"views.user.UserBar.currentUser": "Usuario actual",
"views.admin.UserManagement.roleDonotExist": "El rol actual no existe",
"views.admin.Maintenance.customSqlCleanup": "Limpieza SQL personalizada",
"views.admin.AccountSettings.send_mail_daily_limit": "Límite diario",
"views.admin.AccountSettings.send_mail_daily_limit_invalid": "El límite diario debe ser un entero mayor o igual que -1",
"views.admin.IpBlacklistSettings.tip_daily_limit": "Límite diario: restringe el número máximo de solicitudes por IP al día (1-1000000).",
"views.admin.IpBlacklistSettings.daily_request_limit": "Límite diario de solicitudes",
"views.Header.dark": "Oscuro",
"views.Admin.database": "Base de datos",
"views.admin.DatabaseManager.need_initialization_tip": "Es necesario inicializar la base de datos. Inicialízala primero.",
"views.admin.DatabaseManager.initializationSuccess": "Base de datos inicializada correctamente",
"views.admin.DatabaseManager.migrationSuccess": "Base de datos migrada correctamente",
"views.admin.DatabaseManager.need_migration_tip": "Es necesario migrar la base de datos. Realiza la migración primero.",
"components.MailBox.delete": "Eliminar",
"components.MailContentRenderer.delete": "Eliminar",
"components.SendBox.delete": "Eliminar",
"views.index.Attachment.delete": "Eliminar",
"views.admin.Account.delete": "Eliminar",
"views.admin.UserOauth2Settings.delete": "Eliminar",
"views.admin.SenderAccess.delete": "Eliminar",
"views.admin.UserManagement.delete": "Eliminar",
"views.admin.AccountSettings.delete_rule": "Eliminar",
"views.admin.Maintenance.deleteCustomSql": "Eliminar",
"views.index.AccountSettings.deleteAccount": "Eliminar cuenta",
"views.admin.Account.deleteAccount": "Eliminar cuenta",
"views.user.UserSettings.deletePasskey": "Eliminar passkey",
"views.admin.AccountSettings.delete_success": "Eliminado correctamente",
"views.admin.UserManagement.deleteUser": "Eliminar usuario",
"views.index.Attachment.deleteSuccess": "Eliminado correctamente",
"views.admin.SenderAccess.disable": "Deshabilitar",
"views.Admin.loginViaDisabledCheck": "Comprobación de contraseña deshabilitada",
"views.common.Appearance.preferShowTextMail": "Mostrar correo en texto por defecto",
"views.admin.AccountSettings.domain_list": "Lista de dominios (opcional)",
"views.admin.AccountSettings.source_patterns_tip": "La lista de dominios filtra por destinatario y la regex de origen por remitente. Ambas condiciones deben cumplirse para reenviar (lógica AND). Deja alguna vacía para omitirla.",
"views.admin.UserManagement.domains": "Dominios",
"views.index.Attachment.download": "Descargar",
"components.MailBox.downloadMail": "Descargar Correo",
"components.MailContentRenderer.downloadMail": "Descargar Correo",
"views.admin.Maintenance.sqlNamePlaceholder": "p. ej., limpiar registros antiguos",
"views.admin.Maintenance.sqlPlaceholder": "p. ej., DELETE FROM raw_mails WHERE source GLOB '*{'@'}example.com' AND created_at < datetime('now', '-3 day')",
"views.admin.AccountSettings.source_patterns_placeholder": "p. ej., gmail.com",
"views.admin.AccountSettings.forwarding_rule_warning": "Cada regla se ejecuta de forma independiente. La dirección de reenvío debe estar verificada.",
"views.index.SendMail.edit": "Editar",
"views.admin.SendMail.edit": "Editar",
"views.user.UserLogin.email": "Correo electrónico",
"views.admin.UserManagement.email": "Correo electrónico",
"views.common.Login.email": "Correo electrónico",
"views.common.Login.credential": "Credencial de dirección de correo",
"views.common.Login.emailPasswordRequired": "El correo y la contraseña son obligatorios",
"views.admin.UserSettings.emailCheckRegex": "Regex de verificación de correo (p. ej., ^[^.]+{'@'}.+$ para impedir puntos antes de {'@'})",
"views.admin.AccountSettings.email_forwarding_config": "Configuración de reenvío de correo",
"views.admin.UserOauth2Settings.userEmailFormat": "Patrón regex del correo",
"views.Admin.mails": "Correos",
"views.index.AutoReply.sourcePrefixPlaceholder": "Vacío=todos, prefijo o /regex/",
"components.WebhookComponent.enable": "Habilitar",
"views.admin.UserOauth2Settings.enable": "Habilitar",
"views.admin.SenderAccess.enable": "Habilitar",
"views.admin.Telegram.enable": "Habilitar",
"views.admin.UserSettings.enable": "Habilitar",
"views.admin.AiExtractSettings.enableAllowList": "Habilitar Lista blanca de direcciones",
"views.admin.Webhook.enableAllowList": "Habilitar lista de permitidos (restringe el acceso del webhook a usuarios específicos)",
"views.index.AutoReply.enableAutoReply": "Habilitar respuesta automática",
"views.admin.Maintenance.cronTip": "Para activar la limpieza por cron, configura [crons] en el worker. Consulta la documentación; 0 días significa limpiar todo.",
"views.admin.IpBlacklistSettings.enable_daily_limit": "Habilitar Límite diario de solicitudes",
"views.admin.UserSettings.enableEmailCheckRegex": "Habilitar regex de verificación de correo",
"views.admin.UserOauth2Settings.enableEmailFormat": "Habilitar formato de correo",
"views.admin.Telegram.enableGlobalMailPush": "Habilitar envío global de correos (introduce manualmente el Chat ID de Telegram)",
"views.admin.IpBlacklistSettings.enable_ip_blacklist": "Habilitar Lista negra de IP",
"views.admin.IpBlacklistSettings.enable_ip_whitelist": "Habilitar Lista blanca de IP (estricto)",
"views.admin.UserOauth2Settings.enableMailAllowList": "Habilitar lista blanca de direcciones de correo (editable manualmente)",
"views.admin.UserSettings.enableMailAllowList": "Habilitar lista blanca de direcciones de correo (editable manualmente)",
"views.admin.UserSettings.enableMailVerify": "Habilitar verificación de correo (la dirección remitente debe existir en el sistema, tener saldo y poder enviar normalmente)",
"views.admin.Telegram.enableTelegramAllowList": "Habilitar lista blanca de Telegram (introduce manualmente el Chat ID)",
"views.admin.IpBlacklistSettings.asn_blacklist_placeholder": "Introduce organización ASN (p. ej., Google, Amazon)",
"views.admin.IpBlacklistSettings.fingerprint_blacklist_placeholder": "Introduce el ID de huella (p. ej., a1b2c3d4e5f6g7h8)",
"views.admin.IpBlacklistSettings.daily_request_limit_placeholder": "Introduce el límite (p. ej., 1000)",
"views.admin.IpBlacklistSettings.ip_blacklist_placeholder": "Introduce el patrón (p. ej., 192.168.1 o ^10\\.0\\.0\\.5$)",
"views.common.Login.bindUserAddressError": "Error al vincular la dirección al usuario",
"views.admin.IpBlacklistSettings.ip_whitelist_placeholder": "IP exacta (p. ej., 1.2.3.4) o regex anclada (p. ej., ^192\\.168\\.1\\.\\d+$)",
"views.index.SimpleIndex.exitSimpleIndex": "Salir del modo simple",
"components.MailBox.keywordQueryTip": "Filtrar página actual",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env": "Seguir variable de entorno",
"views.admin.AccountSettings.create_address_subdomain_match_force_disable": "Forzar deshabilitación",
"views.admin.AccountSettings.create_address_subdomain_match_force_enable": "Forzar habilitación",
"views.user.UserLogin.forgotPassword": "Olvidé mi contraseña",
"components.MailBox.forwardMail": "Reenviar",
"components.MailContentRenderer.forward": "Reenviar",
"views.admin.AccountSettings.forward_address": "Dirección de reenvío",
"views.admin.AccountSettings.forward_address_required": "La dirección de reenvío es obligatoria",
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
"components.MailContentRenderer.fullscreen": "Pantalla completa",
"views.common.Login.generateName": "Generar nombre aleatorio",
"views.admin.Telegram.globalMailPushList": "Lista global de chat ID para envío de correos",
"views.common.Appearance.globalTabplacement": "Posición global de pestañas",
"views.common.Login.help": "Ayuda",
"views.Header.home": "Inicio",
"views.index.SendMail.html": "HTML",
"views.admin.SendMail.html": "HTML",
"views.admin.UserOauth2Settings.icon": "Icono (SVG, asegúrate de que la fuente sea fiable)",
"views.admin.CreateAccount.enablePrefix": "Si se habilita el prefijo",
"views.common.AdminContact.adminContact": "Si necesitas ayuda, contacta con el administrador ({msg})",
"views.admin.Telegram.init": "Inicializar",
"views.admin.DatabaseManager.init": "Inicializar base de datos",
"views.admin.AccountSettings.regex_invalid": "Patrón regex no válido",
"views.Admin.ipBlacklistSettings": "Lista negra de IP",
"views.admin.IpBlacklistSettings.ip_blacklist": "Patrones de lista negra de IP",
"views.admin.IpBlacklistSettings.title": "Configuración de lista negra de IP",
"views.admin.IpBlacklistSettings.tip_ip": "Lista negra de IP: admite coincidencia por texto (p. ej., \"192.168.1\") o por regex (p. ej., \"^10\\.0\\.0\\.5$\").",
"views.admin.IpBlacklistSettings.whitelist_empty_warning": "La lista blanca de IP está activada, pero vacía. El servidor la ignorará para evitar bloquear el acceso. Añade al menos una entrada antes de activarla.",
"views.admin.IpBlacklistSettings.ip_whitelist": "Lista blanca de IP Patrones",
"views.admin.IpBlacklistSettings.tip_whitelist": "Lista blanca de IP: modo estricto. Las entradas simples deben coincidir exactamente con la IP (sin subcadenas). Usa regex ancladas (^...$) para rangos. Las IP permitidas omiten la comprobación de la lista negra.",
"views.admin.SenderAccess.is_enabled": "Habilitado",
"views.admin.Account.itemCount": "itemCount",
"views.admin.SenderAccess.itemCount": "itemCount",
"views.admin.UserManagement.itemCount": "itemCount",
"views.user.UserMailBox.addressQueryTip": "Déjalo vacío para consultar todas las direcciones",
"views.admin.Account.addressQueryTip": "Déjalo vacío para consultar todas las direcciones",
"views.admin.Mails.addressQueryTip": "Déjalo vacío para consultar todas las direcciones",
"views.common.Appearance.left": "izquierda",
"views.common.Login.getNewEmailTip2": "Si lo dejas vacío, se generará una dirección aleatoria.",
"views.Header.light": "Claro",
"views.admin.IpBlacklistSettings.enable_daily_limit_tip": "Limitar el número de solicitudes API por IP y día",
"components.AddressSelect.localAddresses": "Direcciones locales",
"views.common.Login.bindUserInfo": "Si ya has iniciado sesión, al entrar sin vincular correo o crear uno nuevo se vinculará al usuario actual",
"views.user.UserOauth2Callback.logging": "Iniciando sesión...",
"views.user.UserLogin.login": "Iniciar sesión",
"views.common.Login.login": "Iniciar sesión",
"views.common.Login.loginAndBind": "Iniciar sesión y vincular",
"views.Admin.loginMethod": "Método de inicio de sesión",
"views.user.UserBar.fetchUserSettingsError": "La contraseña de inicio de sesión es incorrecta o la cuenta no existe; también puede tratarse de un problema de red. Inténtalo de nuevo más tarde.",
"views.user.UserLogin.loginWith": "Iniciar sesión con {provider}",
"views.user.UserLogin.loginWithPasskey": "Iniciar sesión con passkey",
"views.user.UserSettings.logout": "Cerrar sesión",
"views.user.BindAddress.logout": "Cerrar sesión",
"views.Admin.logout": "Cerrar sesión",
"views.index.AccountSettings.logout": "Cerrar sesión",
"views.Admin.logoutSuccess": "Sesión cerrada correctamente",
"views.admin.UserOauth2Settings.mailAllowList": "Lista blanca de direcciones de correo",
"views.admin.UserSettings.mailAllowList": "Lista blanca de direcciones de correo",
"views.index.SimpleIndex.addressCredential": "Credencial de dirección de correo",
"views.index.AddressBar.addressCredential": "Credencial de dirección de correo",
"views.admin.Account.addressCredential": "Credencial de dirección de correo",
"views.admin.CreateAccount.addressCredential": "Credencial de dirección de correo",
"views.index.AddressBar.fetchAddressError": "La credencial del correo no es válida o la cuenta no existe; también puede ser un problema de red. Inténtalo más tarde.",
"views.Index.mailbox": "Bandeja de entrada",
"views.User.user_mail_box_tab": "Bandeja de entrada",
"views.user.AddressManagement.mail_count": "Cantidad de correos",
"views.admin.Account.mail_count": "Cantidad de correos",
"views.admin.Statistics.mailCount": "Cantidad de correos",
"views.admin.UserAddressManagement.mail_count": "Cantidad de correos",
"views.index.SimpleIndex.deleteSuccess": "Correo eliminado correctamente",
"views.user.UserLogin.cannotForgotPassword": "La verificación por correo o el registro está desactivado; no se puede restablecer la contraseña. Contacta con el administrador.",
"views.Admin.mailWebhook": "Webhook de correo",
"views.common.Appearance.mailboxSplitSize": "Tamaño de división del buzón",
"views.index.SimpleIndex.refreshSuccess": "Correos actualizados correctamente",
"views.Admin.unknow": "Correos con destinatario desconocido",
"views.Admin.maintenance": "Mantenimiento",
"views.index.AddressBar.addressManage": "Gestionar",
"views.admin.AccountSettings.source_match_mode": "Modo de coincidencia",
"views.admin.RoleAddressConfig.maxAddressCount": "Cantidad máxima de direcciones (0 = ilimitado)",
"views.admin.UserSettings.maxAddressCount": "Número máximo de direcciones que se pueden vincular (0 = ilimitado)",
"views.Header.menu": "Menú",
"views.admin.DatabaseManager.migration": "Migrar base de datos",
"views.admin.SenderAccess.modify": "Modificar",
"views.admin.AccountSettings.send_mail_monthly_limit": "Límite mensual",
"views.admin.AccountSettings.send_mail_monthly_limit_invalid": "El límite mensual debe ser un entero mayor o igual que -1",
"components.MailBox.multiAction": "Acción múltiple",
"components.SendBox.multiAction": "Acción múltiple",
"views.admin.Account.multiClearInbox": "Vaciar varias bandejas",
"views.admin.Account.multiClearSentItems": "Vaciar varios enviados",
"views.admin.Account.multiDelete": "Eliminar en lote",
"views.user.AddressManagement.name": "Nombre",
"views.index.AutoReply.name": "Nombre",
"views.admin.Account.name": "Nombre",
"views.admin.UserOauth2Settings.name": "Nombre",
"views.admin.UserAddressManagement.name": "Nombre",
"views.admin.Maintenance.sqlName": "Nombre",
"views.index.AccountSettings.newPassword": "Nueva contraseña",
"views.admin.Account.newPassword": "Nueva contraseña",
"components.MailBox.nextMail": "Siguiente",
"views.index.SimpleIndex.nextPage": "Siguiente",
"views.admin.AccountSettings.noLimitSendAddressList": "Lista de direcciones sin límite de saldo",
"views.index.SimpleIndex.noMails": "No se encontraron correos",
"views.admin.RoleAddressConfig.noRolesAvailable": "No hay roles disponibles en la configuración del sistema",
"views.index.SendMail.requestAccessTip": "Todavía no hay saldo de envío. Si el administrador activó un saldo por defecto, se asignará automáticamente; si no, solicita acceso o contacta con el administrador.",
"components.SendBox.emptySent": "No hay correos enviados",
"views.admin.RoleAddressConfig.notConfigured": "No configurado (usar configuración global)",
"views.Admin.userOauth2Settings": "Configuración de OAuth2",
"views.admin.UserOauth2Settings.oauth2Type": "Tipo de OAuth2",
"views.Header.ok": "Aceptar",
"views.Admin.ok": "Aceptar",
"views.index.AddressBar.ok": "Aceptar",
"views.admin.SenderAccess.ok": "Aceptar",
"views.common.Login.ok": "Aceptar",
"views.admin.AccountSettings.create_address_subdomain_match_tip": "Solo afecta la validación de dominio en /api/new_address y /admin/new_address. Ejemplo: activado, foo.example.com puede coincidir con example.com.",
"components.AiExtractInfo.open": "Abrir",
"views.index.AddressBar.linkWithAddressCredential": "Abrir enlace de inicio de sesión automático por correo",
"views.admin.CreateAccount.linkWithAddressCredential": "Abrir enlace de inicio de sesión automático por correo",
"views.index.SendMail.options": "Opciones",
"views.admin.SendMail.options": "Opciones",
"components.AiExtractInfo.otherLink": "Otro enlace",
"views.user.UserSettings.passkeyCreated": "Passkey creada correctamente",
"views.user.UserSettings.passkey_name": "Nombre de la passkey",
"views.user.UserLogin.password": "Contraseña",
"views.admin.UserManagement.password": "Contraseña",
"views.common.Login.password": "Contraseña",
"views.index.AccountSettings.passwordChanged": "Contraseña cambiada correctamente",
"views.common.Login.passwordLogin": "Inicio de sesión con contraseña",
"views.admin.Account.passwordResetSuccess": "Contraseña restablecida correctamente",
"views.index.AccountSettings.passwordMismatch": "Las contraseñas no coinciden",
"views.index.SendMail.successSend": "Revisa la bandeja de salida. Si falla, comprueba tu saldo o inténtalo más tarde.",
"views.admin.SendMail.successSend": "Revisa la bandeja de salida. Si falla, inténtalo de nuevo más tarde.",
"views.user.UserLogin.pleaseCompleteTurnstile": "Completa el turnstile",
"views.index.SimpleIndex.addressCredentialTip": "Copia la credencial de la dirección de correo; podrás usarla para iniciar sesión.",
"views.index.AddressBar.addressCredentialTip": "Copia la credencial de la dirección de correo; podrás usarla para iniciar sesión en tu cuenta de correo.",
"views.admin.Account.addressCredentialTip": "Copia la credencial de la dirección de correo; podrás usarla para iniciar sesión en tu cuenta de correo.",
"views.admin.CreateAccount.addressCredentialTip": "Copia la credencial de la dirección de correo; podrás usarla para iniciar sesión en tu cuenta de correo.",
"views.Admin.accessTip": "Introduce la contraseña de administrador",
"views.Header.accessTip": "Introduce la contraseña de acceso correcta",
"views.admin.AccountSettings.address_block_list_placeholder": "Introduce las palabras clave que quieres bloquear",
"views.user.UserSettings.renamePasskeyNamePlaceholder": "Introduce el nuevo nombre de la passkey",
"views.user.UserSettings.passkeyNamePlaceholder": "Introduce el nombre de la passkey o déjalo vacío para generarlo aleatoriamente",
"views.admin.CreateAccount.fillInAllFields": "Rellena todos los campos",
"views.admin.SendBox.queryTip": "Introduce la dirección a consultar; vacío para todas",
"views.user.UserLogin.pleaseInputCode": "Introduce el código",
"views.admin.UserManagement.pleaseInput": "Introduce la información completa",
"views.user.UserLogin.pleaseInputEmail": "Introduce el correo",
"views.user.UserLogin.pleaseInput": "Introduce el correo y la contraseña",
"views.admin.Maintenance.tip": "Introduce los días",
"views.common.Login.getNewEmailTip1": "Introduce el correo que quieres usar. Solo se permite: ",
"views.common.Login.credentialInput": "Introduce la credencial de la dirección de correo",
"views.admin.SenderAccess.modalTip": "Introduce el saldo de envío",
"views.user.UserLogin.pleaseLogin": "Inicia sesión",
"views.common.Login.pleaseGetNewEmail": "Inicia sesión o pulsa \"Crear nuevo correo\" para obtener una nueva dirección.",
"components.WebhookComponent.fillInDemoTip": "Modifica la URL y el resto de ajustes por los tuyos",
"components.SendBox.pleaseSelectMail": "Selecciona un correo para verlo.",
"views.admin.Account.pleaseSelectAddress": "Selecciona una dirección",
"components.MailBox.pleaseSelectMail": "Selecciona un correo",
"views.admin.UserManagement.prefix": "Prefijo",
"components.WebhookComponent.presets": "Plantillas",
"views.index.SendMail.preview": "Vista previa",
"views.admin.SendMail.preview": "Vista previa",
"views.admin.UserOauth2Settings.iconPreview": "Vista previa",
"components.MailBox.prevMail": "Anterior",
"views.index.SimpleIndex.prevPage": "Anterior",
"components.MailBox.query": "Consultar",
"views.user.UserMailBox.query": "Consultar",
"views.Index.query": "Consultar",
"views.admin.SendBox.query": "Consultar",
"views.admin.Account.query": "Consultar",
"views.admin.SenderAccess.query": "Consultar",
"views.admin.UserManagement.query": "Consultar",
"views.admin.Mails.query": "Consultar",
"views.Admin.qucickSetup": "Configuración rápida",
"views.index.SendMail.toMailEmpty": "La dirección del destinatario está vacía",
"views.admin.SendMail.toMailEmpty": "La dirección del destinatario está vacía",
"views.index.SendMail.toName": "Nombre y dirección del destinatario; deja el nombre vacío para usar el correo",
"views.admin.SendMail.toName": "Nombre y dirección del destinatario; deja el nombre vacío para usar el correo",
"components.MailBox.refresh": "Actualizar",
"components.Turnstile.refresh": "Actualizar",
"components.SendBox.refresh": "Actualizar",
"views.index.SimpleIndex.refreshMails": "Actualizar",
"components.MailBox.refreshAfter": "Actualizar en {msg} segundos",
"views.index.SimpleIndex.refreshAfter": "Actualizar en {msg} segundos",
"views.admin.AccountSettings.regex_too_long": "El patrón regex es demasiado largo (máx. 200 caracteres)",
"views.user.UserLogin.register": "Registrarse",
"views.user.UserSettings.renamePasskey": "Renombrar passkey",
"views.admin.UserOauth2Settings.userEmailReplace": "Plantilla de reemplazo",
"components.MailBox.reply": "Responder",
"components.MailContentRenderer.reply": "Responder",
"views.index.SendMail.requestAccess": "Solicitar acceso",
"views.user.UserLogin.resetPassword": "Restablecer Contraseña",
"views.admin.Account.resetPassword": "Restablecer Contraseña",
"views.admin.UserManagement.resetPassword": "Restablecer Contraseña",
"views.index.SendMail.rich text": "Texto enriquecido",
"views.admin.SendMail.rich text": "Texto enriquecido",
"views.common.Appearance.right": "derecha",
"views.admin.UserManagement.role": "Rol",
"views.admin.RoleAddressConfig.role": "Rol",
"views.Admin.roleAddressConfig": "Configuración de direcciones por rol",
"views.admin.AccountSettings.rule_index": "Regla",
"views.Index.s3Attachment": "Adjunto S3",
"components.WebhookComponent.save": "Guardar",
"views.index.AutoReply.save": "Guardar",
"views.admin.AiExtractSettings.save": "Guardar",
"views.admin.UserOauth2Settings.save": "Guardar",
"views.admin.IpBlacklistSettings.save": "Guardar",
"views.admin.Telegram.save": "Guardar",
"views.admin.UserSettings.save": "Guardar",
"views.admin.RoleAddressConfig.save": "Guardar",
"views.admin.AccountSettings.save": "Guardar",
"views.admin.Maintenance.save": "Guardar",
"views.admin.Webhook.save": "Guardar",
"views.admin.Maintenance.saveSuccess": "Guardado correctamente",
"views.admin.UserOauth2Settings.successTip": "Guardado correcto",
"views.admin.IpBlacklistSettings.successTip": "Guardado correcto",
"views.admin.UserSettings.successTip": "Guardado correcto",
"views.admin.AccountSettings.successTip": "Guardado correcto",
"components.MailBox.saveToS3": "Guardar en S3",
"components.MailContentRenderer.saveToS3": "Guardar en S3",
"views.Index.saveToS3Success": "guardado en S3 correctamente",
"components.MailBox.selectAll": "Seleccionar toda esta página",
"components.SendBox.selectAll": "Seleccionar toda esta página",
"views.admin.Account.selectAll": "Seleccionar toda esta página",
"views.admin.AccountSettings.select_domain": "Seleccionar dominio",
"views.admin.Account.selectedItems": "Seleccionado",
"views.index.SendMail.send": "Enviar",
"views.admin.SendMail.send": "Enviar",
"views.Index.sendbox": "Bandeja de salida",
"views.Admin.sendBox": "Bandeja de salida",
"views.user.AddressManagement.send_count": "Cantidad enviada",
"views.admin.Account.send_count": "Cantidad enviada",
"views.admin.UserAddressManagement.send_count": "Cantidad enviada",
"views.Index.sendmail": "Enviar correo",
"views.Admin.sendMail": "Enviar correo",
"views.index.SendMail.send_balance": "Saldo restante de envío",
"views.admin.Statistics.sendMailCount": "Cantidad de correos enviados",
"views.admin.AccountSettings.send_mail_limit": "Límite de envío",
"views.user.UserLogin.sendVerificationCode": "Enviar código de verificación",
"views.Admin.senderAccess": "Control de permisos de envío",
"views.admin.SendMail.fromMailEmpty": "La dirección del remitente está vacía",
"views.index.AutoReply.sourcePrefix": "Filtro de remitente",
"components.AiExtractInfo.serviceLink": "Enlace del servicio",
"views.index.AutoReply.settings": "Configuración",
"views.index.AccountSettings.showAddressCredential": "Mostrar credencial de la dirección",
"components.MailBox.attachments": "Mostrar adjuntos",
"components.MailBox.showHtmlMail": "Mostrar correo HTML",
"components.MailContentRenderer.showHtmlMail": "Mostrar correo HTML",
"views.admin.Account.showCredential": "Mostrar credencial de la dirección de correo",
"views.user.UserSettings.showPasskeyList": "Mostrar lista de passkeys",
"components.MailBox.showTextMail": "Mostrar correo en texto",
"components.MailContentRenderer.showTextMail": "Mostrar correo en texto",
"views.Index.enterSimpleMode": "Modo simple",
"components.MailContentRenderer.size": "Tamaño",
"views.admin.Account.source_meta": "Origen",
"views.admin.AccountSettings.source_patterns": "Regex de dirección de origen (opcional)",
"views.admin.Maintenance.sqlStatement": "Sentencia SQL (solo DELETE)",
"views.user.UserOauth2Callback.stateNotMatch": "el estado no coincide",
"views.Admin.statistics": "Estadísticas",
"views.Header.status": "Estado",
"views.admin.IpBlacklistSettings.enable_whitelist_tip": "Modo estricto: solo las IP que coincidan con la lista blanca podrán acceder a las API con límite. Todas las demás serán rechazadas.",
"views.index.AutoReply.subject": "Asunto",
"views.index.SendMail.subject": "Asunto",
"views.admin.SendMail.subject": "Asunto",
"views.index.SendMail.subjectEmpty": "El asunto está vacío",
"views.admin.SendMail.subjectEmpty": "El asunto está vacío",
"components.AiExtractInfo.subscriptionLink": "Enlace de suscripción",
"views.user.AddressManagement.success": "éxito",
"views.index.TelegramAddress.success": "éxito",
"views.index.LocalAddress.success": "éxito",
"views.admin.UserAddressManagement.success": "éxito",
"components.WebhookComponent.successTip": "Correcto",
"components.MailBox.success": "Correcto",
"components.SendBox.success": "Correcto",
"views.index.AccountSettings.success": "Correcto",
"views.index.AutoReply.success": "Correcto",
"views.admin.AiExtractSettings.successTip": "Correcto",
"views.admin.Account.success": "Correcto",
"views.admin.SenderAccess.success": "Correcto",
"views.admin.UserManagement.success": "Correcto",
"views.admin.Telegram.successTip": "Correcto",
"views.admin.RoleAddressConfig.successTip": "Correcto",
"views.admin.Webhook.successTip": "Correcto",
"views.admin.CreateAccount.successTip": "Creado correctamente",
"views.admin.Telegram.globalMailPushListTip": "Admite chat_id de chat privado/grupo/canal. Envía un mensaje al bot y visita este enlace para ver el chat_id: https://api.telegram.org/bot<Reemplaza con tu BOT TOKEN>/getUpdates",
"views.user.AddressManagement.targetUserEmail": "Correo del usuario objetivo",
"views.admin.Telegram.telegramAllowList": "Lista blanca de Telegram (introduce manualmente el Chat ID)",
"views.Admin.telegram": "Bot de Telegram",
"views.admin.Telegram.miniAppUrl": "URL de la Mini App de Telegram",
"components.WebhookComponent.test": "Prueba",
"views.index.SendMail.text": "Texto",
"views.admin.SendMail.text": "Texto",
"views.user.UserSettings.passordTip": "El servidor solo recibe el hash de la contraseña, no la contraseña en texto claro, por lo que no puede verla ni recuperarla. Si el administrador activa la verificación por correo, puedes restablecerla en modo incógnito.",
"views.index.LocalAddress.tip": "Estas direcciones se guardan en tu navegador y podrían perderse si borras la caché.",
"views.admin.UserOauth2Settings.tip": "El inicio de sesión de terceros usará automáticamente el correo del usuario para registrar una cuenta (el mismo correo se considera la misma cuenta). También puedes establecer la contraseña con “olvidé mi contraseña”.",
"views.admin.AccountSettings.send_mail_limit_tip": "Se aplica a todos los canales de envío. Usa -1 para ilimitado y 0 para bloquear el envío.",
"views.admin.AccountSettings.create_address_subdomain_match_note": "Esto es diferente de RANDOM_SUBDOMAIN_DOMAINS: este interruptor permite indicar subdominios personalizados; el subdominio aleatorio solo genera uno al crear.",
"views.index.SendMail.tooLarge": "Archivo demasiado grande; sube uno de menos de 1 MB.",
"views.admin.SendMail.tooLarge": "Archivo demasiado grande; sube uno de menos de 1 MB.",
"views.common.Appearance.top": "arriba",
"views.user.AddressManagement.transferAddress": "Transferir dirección",
"views.user.AddressManagement.transferAddressTip": "Transferir esta dirección a otro usuario la quitará de tu cuenta y la moverá a la suya. ¿Seguro que quieres transferirla?",
"views.common.Appearance.useSideMargin": "Activar márgenes laterales a la izquierda y a la derecha de la página",
"views.admin.AiExtractSettings.manualInputPrompt": "Escribe y pulsa Enter para añadir",
"views.admin.UserOauth2Settings.manualInputPrompt": "Escribe y pulsa Enter para añadir",
"views.admin.Telegram.manualInputPrompt": "Escribe y pulsa Enter para añadir",
"views.admin.UserSettings.manualInputPrompt": "Escribe y pulsa Enter para añadir",
"views.admin.AccountSettings.manualInputPrompt": "Escribe y pulsa Enter para añadir",
"views.admin.Webhook.manualInputPrompt": "Escribe y pulsa Enter para añadir",
"views.admin.IpBlacklistSettings.manualInputPrompt": "Escribe el patrón y pulsa Enter para añadir",
"views.user.AddressManagement.unbindAddress": "Desvincular dirección",
"views.index.TelegramAddress.unbindMailAddress": "Desvincular dirección de correo",
"views.index.LocalAddress.unbindMailAddress": "Desvincular credencial de dirección de correo",
"components.MailBox.unselectAll": "Deseleccionar todo",
"components.SendBox.unselectAll": "Deseleccionar todo",
"views.admin.Account.unselectAll": "Deseleccionar todo",
"views.admin.Account.updated_at": "Actualizado el",
"views.user.UserSettings.updated_at": "Actualizado el",
"components.WebhookComponent.urlMissing": "La URL es obligatoria",
"views.common.Appearance.useIframeShowMail": "Usar iframe para mostrar correo HTML",
"views.admin.CreateAccount.enableRandomSubdomain": "Usar subdominio aleatorio",
"views.common.Login.enableRandomSubdomain": "Usar subdominio aleatorio",
"views.admin.UserOauth2Settings.userEmailFormatTip": "Usa regex para transformar el correo. Ejemplo: ^(.+)@old\\.com$ con $1@new.com",
"views.common.Appearance.useSimpleIndex": "Usar índice simple",
"views.common.Appearance.useUTCDate": "Usar fecha UTC",
"views.Header.user": "Usuario",
"views.Admin.user": "Usuario",
"components.AddressSelect.userAddresses": "Direcciones del usuario",
"views.Admin.loginViaUserAdmin": "Permiso de administrador del usuario",
"views.admin.Statistics.userCount": "Cantidad de usuarios",
"views.admin.UserManagement.user_email": "Correo del usuario",
"views.index.AddressBar.userLogin": "Inicio de sesión de usuario",
"views.Admin.user_management": "Gestión de usuarios",
"views.Admin.user_settings": "Configuración de usuario",
"views.User.user_settings": "Configuración de usuario",
"components.AiExtractInfo.authCode": "Código de verificación",
"views.user.UserLogin.verifyCode": "Código de verificación",
"views.user.UserLogin.verifyCodeSent": "Código de verificación enviado, expira en {timeout} segundos",
"views.admin.AccountSettings.verified_address_list": "Lista de direcciones verificadas (puede enviar correo mediante la API interna de cf)",
"views.admin.UserSettings.verifyMailSender": "Verificar remitente de correo",
"components.MailContentRenderer.attachments": "Ver adjuntos",
"views.admin.Account.viewMails": "Ver correos",
"views.admin.Account.viewSendBox": "Ver bandeja de salida",
"views.user.UserLogin.waitforVerifyCode": "Espera {timeout} segundos",
"views.admin.Webhook.webhookAllowList": "Lista de permitidos de webhook (introduce el correo autorizado y pulsa Enter)",
"views.admin.Webhook.notEnabled": "El webhook no está habilitado",
"components.WebhookComponent.notEnabled": "El webhook no está habilitado para ti",
"views.Index.webhookSettings": "Configuración de webhook",
"views.Admin.webhookSettings": "Configuración de webhook",
"views.admin.AiExtractSettings.disabledTip": "Si está desactivado, la extracción IA procesará todas las direcciones",
"views.admin.AiExtractSettings.enableAllowListTip": "Si está activado, la extracción IA solo procesará correos enviados a direcciones permitidas",
"views.admin.CreateAccount.randomSubdomainTip": "Si está activado, la dirección creada usará un subdominio aleatorio. Se recomienda usarlo solo para recibir.",
"views.common.Login.randomSubdomainTip": "Si está activado, la dirección creada usará un subdominio aleatorio. Se recomienda usarlo solo para recibir.",
"views.admin.AiExtractSettings.allowListTip": "El comodín * coincide con cualquier carácter; p. ej., *{'@'}example.com coincide con todas las direcciones del dominio example.com",
"views.Admin.workerconfig": "Configuración del Worker",
"views.admin.AccountSettings.create_address_subdomain_match_env_locked": "La variable ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH está en false. Puedes guardar el interruptor, pero no tendrá efecto hasta habilitar o quitar la variable.",
"views.common.Login.getNewEmailTip3": "Puedes elegir un dominio en la lista desplegable.",
"views.admin.AccountSettings.tip": "Puedes introducir manualmente los siguientes valores y pulsar Enter para añadirlos",
"components.MailBox.emptyInbox": "Tu bandeja de entrada está vacía",
"views.index.SendMail.fromName": "Tu nombre y dirección; deja el nombre vacío para usar el correo",
"views.admin.SendMail.fromName": "Tu nombre y dirección; deja el nombre vacío para usar el correo"
}

View File

@@ -0,0 +1,590 @@
export const jaMessages = {
"views.index.SimpleIndex.mailCount": "{current} / {total} 件のメール",
"views.admin.Statistics.activeAddressCount30days": "30日間のアクティブなアドレス数",
"views.admin.Statistics.activeAddressCount7days": "7日間のアクティブなアドレス数",
"views.Index.about": "概要",
"views.Admin.about": "概要",
"views.Header.accessHeader": "アクセス用パスワード",
"views.Admin.account": "アカウント",
"views.Index.accountSettings": "アカウント設定",
"views.Admin.account_settings": "アカウント設定",
"views.index.SimpleIndex.accountSettings": "アカウント設定",
"views.index.Attachment.action": "操作",
"views.admin.SenderAccess.action": "操作",
"views.user.UserSettings.actions": "操作",
"views.user.AddressManagement.actions": "操作",
"views.index.TelegramAddress.actions": "操作",
"views.admin.Account.actions": "操作",
"views.index.LocalAddress.actions": "操作",
"views.admin.UserManagement.actions": "操作",
"views.admin.AccountSettings.actions": "操作",
"views.admin.AccountSettings.add": "追加",
"views.admin.Maintenance.customSqlTip": "定期クリーンアップ用にカスタム DELETE SQL を追加します。各項目には 1 つの DELETE 文のみ使用できます。",
"views.admin.Maintenance.addCustomSql": "カスタム SQL を追加",
"views.admin.UserOauth2Settings.addOauth2": "Oauth2 を追加",
"components.AddressSelect.address": "アドレス",
"views.user.AddressManagement.address": "アドレス",
"views.index.TelegramAddress.address": "アドレス",
"views.admin.SenderAccess.address": "アドレス",
"views.index.LocalAddress.address": "アドレス",
"views.admin.CreateAccount.address": "アドレス",
"views.admin.AiExtractSettings.allowList": "アドレス許可リスト (アドレスを入力して Enter。ワイルドカード対応)",
"views.admin.AccountSettings.send_address_block_list": "送信メール用のブロックキーワード",
"views.admin.AccountSettings.address_block_list": "ユーザー向けブロックキーワード (管理者はスキップ可能)",
"views.index.SimpleIndex.addressCopied": "アドレスをコピーしました",
"views.admin.Statistics.addressCount": "アドレス数",
"views.admin.UserManagement.address_count": "アドレス数",
"views.User.address_management": "アドレス管理",
"views.admin.UserManagement.userAddressManagement": "アドレス管理",
"views.index.AddressBar.addressPassword": "アドレスパスワード",
"views.admin.CreateAccount.addressPassword": "アドレスパスワード",
"views.Admin.adminAccount": "管理者",
"views.Admin.accessHeader": "管理者パスワード",
"views.Admin.loginViaPassword": "管理者パスワードでログイン",
"views.admin.AiExtractSettings.title": "AI メール抽出設定",
"views.Admin.aiExtractSettings": "AI抽出設定",
"views.admin.AccountSettings.match_all": "すべて",
"views.admin.AccountSettings.create_address_subdomain_match": "アドレス作成時にサブドメイン接尾辞一致を許可",
"views.admin.UserSettings.enableUserRegister": "ユーザー登録を許可",
"views.admin.AccountSettings.match_any": "いずれか",
"views.Index.appearance": "表示",
"views.Admin.appearance": "表示",
"views.admin.IpBlacklistSettings.tip_scope": "対象: アドレス作成、メール送信、外部送信 API、ユーザー登録、コード検証",
"views.index.AccountSettings.clearInboxConfirm": "受信箱のすべてのメールを削除してもよろしいですか?",
"views.index.AccountSettings.clearSentItemsConfirm": "送信済みメールをすべて削除してもよろしいですか?",
"views.admin.Account.multiClearInboxTip": "選択したアドレスの受信箱を削除してもよろしいですか?",
"views.admin.Account.clearInboxTip": "このメールアドレスの受信箱を削除してもよろしいですか?",
"views.admin.Account.multiClearSentItemsTip": "選択したアドレスの送信済みを削除してもよろしいですか?",
"views.admin.Account.clearSentItemsTip": "このメールアドレスの送信済みを削除してもよろしいですか?",
"views.admin.Account.multiDeleteTip": "選択したアドレスを削除してもよろしいですか?",
"views.index.Attachment.deleteConfirm": "この添付ファイルを削除してもよろしいですか?",
"views.admin.Account.deleteTip": "このメールを削除してもよろしいですか?",
"views.admin.SenderAccess.deleteTip": "これを削除してもよろしいですか?",
"views.index.AccountSettings.deleteAccountConfirm": "このアカウントと関連メールをすべて削除してもよろしいですか?",
"views.index.AccountSettings.logoutConfirm": "ログアウトしてもよろしいですか?",
"components.MailBox.deleteMailTip": "メールを削除してもよろしいですか?",
"components.MailContentRenderer.deleteMailTip": "メールを削除してもよろしいですか?",
"components.SendBox.deleteMailTip": "メールを削除してもよろしいですか?",
"views.admin.AccountSettings.delete_rule_confirm": "このルールを削除してもよろしいですか?",
"views.admin.UserManagement.deleteUserTip": "このユーザーを削除してもよろしいですか?",
"views.Admin.logoutConfirmContent": "管理画面からログアウトしてもよろしいですか?",
"views.user.UserSettings.logoutConfirm": "ログアウトしてもよろしいですか?",
"views.admin.IpBlacklistSettings.asn_blacklist": "ASN組織ブラックリスト",
"views.admin.IpBlacklistSettings.tip_asn": "ASN 組織: ISP/プロバイダ単位でブロックします。大文字小文字を区別しないテキスト一致または正規表現に対応します。",
"components.AiExtractInfo.authLink": "認証リンク",
"views.admin.Maintenance.autoCleanup": "自動クリーンアップ",
"components.MailBox.autoRefresh": "自動更新",
"views.common.Appearance.autoRefreshInterval": "自動更新間隔 (秒)",
"views.Index.auto_reply": "自動返信",
"views.index.AutoReply.autoReply": "自動返信",
"views.common.Login.autoGeneratedName": "自動生成名",
"views.admin.SenderAccess.balance": "残高",
"views.admin.Maintenance.basicCleanup": "基本クリーンアップ",
"views.user.AddressManagement.unbindAddressTip": "解除前にこのアドレスへ切り替え、メール資格情報を保存してください。",
"views.index.TelegramAddress.bind": "紐付け",
"views.index.TelegramAddress.bindAddressSuccess": "アドレスの紐付けに成功しました",
"views.index.LocalAddress.bindAddressSuccess": "アドレスの紐付けに成功しました",
"views.User.bind_address": "メールアドレスを紐付け",
"views.admin.IpBlacklistSettings.enable_tip": "ブラックリストに一致するIPによるレート制限APIへのアクセスをブロック",
"views.admin.AccountSettings.fromBlockList": "受信メールのブロックキーワード",
"views.admin.AccountSettings.block_receive_unknow_address_email": "未知アドレス宛メールの受信をブロック",
"views.common.Appearance.bottom": "下",
"views.admin.IpBlacklistSettings.fingerprint_blacklist": "ブラウザ指紋ブラックリスト",
"views.admin.IpBlacklistSettings.tip_fingerprint": "ブラウザ指紋: 指紋でブロックします。完全一致または正規表現に対応します。",
"views.admin.AccountSettings.cancel": "キャンセル",
"components.MailBox.cancelMultiAction": "複数選択をキャンセル",
"components.SendBox.cancelMultiAction": "複数選択をキャンセル",
"views.user.AddressManagement.changeMailAddress": "アドレスを変更",
"views.index.TelegramAddress.changeMailAddress": "メールアドレスを変更",
"views.index.LocalAddress.changeMailAddress": "メールアドレスを変更",
"views.index.AccountSettings.changePassword": "パスワードを変更",
"views.admin.UserManagement.changeRole": "ロールを変更",
"components.SendBox.showCode": "元のコード表示を切り替え",
"views.admin.Telegram.status": "ステータスを確認",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env_note": "「環境変数に従う」を選ぶと、管理者による上書きが解除され、未設定状態に戻ります。実際の動作は引き続き Worker の環境変数と優先順位ルールで決まります。",
"views.admin.Maintenance.cleanupNow": "今すぐクリーンアップ",
"views.admin.Maintenance.cleanupSuccess": "クリーンアップ成功",
"views.admin.Maintenance.addressCreateLabel": "n 日より前に作成されたアドレスを削除",
"views.admin.Maintenance.emptyAddressLabel": "n 日より前の空アドレスを削除",
"views.admin.Maintenance.inactiveAddressLabel": "n 日より前の非アクティブなアドレスを削除",
"views.admin.Maintenance.mailBoxLabel": "n 日より前の受信箱を削除",
"views.admin.Maintenance.sendBoxLabel": "n 日より前の送信箱を削除",
"views.admin.Maintenance.unboundAddressLabel": "n 日より前の未紐付けアドレスを削除",
"views.admin.Maintenance.mailUnknowLabel": "n 日より前の受信者不明メールを削除",
"views.index.AccountSettings.clearInbox": "受信箱を削除",
"views.admin.Account.clearInbox": "受信箱を削除",
"views.index.AccountSettings.clearSentItems": "送信済みを削除",
"views.admin.Account.clearSentItems": "送信済みを削除",
"views.Header.title": "Cloudflare 一時メール",
"views.admin.DatabaseManager.code_db_version": "コードが必要とするDBバージョン",
"views.user.UserOauth2Callback.codeNotFound": "コードが見つかりません",
"views.admin.AccountSettings.config": "設定",
"views.admin.RoleAddressConfig.roleConfigDesc": "各ユーザーロールごとの最大アドレス数を設定します。ロール別制限は全体設定より優先されます。0 は無制限です。",
"views.Admin.confirm": "確認",
"views.Admin.logoutConfirmTitle": "ログアウトを確認",
"views.index.AccountSettings.confirmPassword": "パスワードを確認",
"views.index.SendMail.content": "内容",
"views.admin.SendMail.content": "内容",
"views.index.SendMail.contentEmpty": "内容が空です",
"views.admin.SendMail.contentEmpty": "内容が空です",
"components.AddressSelect.copied": "コピーしました",
"components.AiExtractInfo.copySuccess": "正常にコピーしました",
"components.AddressSelect.copy": "コピー",
"views.index.SimpleIndex.copyAddress": "コピー",
"components.AiExtractInfo.copyFailed": "コピーに失敗しました",
"views.Footer.copyright": "著作権",
"views.Admin.account_create": "アカウントを作成",
"views.admin.CreateAccount.creatNewEmail": "新しいメールを作成",
"views.common.Login.getNewEmail": "新しいメールを作成",
"views.user.AddressManagement.create_or_bind": "作成または紐付け",
"views.index.LocalAddress.create_or_bind": "作成または紐付け",
"views.user.UserSettings.createPasskey": "パスキーを作成",
"views.admin.UserManagement.createUser": "ユーザーを作成",
"views.user.UserSettings.created_at": "作成日時",
"views.admin.Account.created_at": "作成日時",
"views.admin.SenderAccess.created_at": "作成日時",
"views.admin.UserManagement.created_at": "作成日時",
"views.common.Login.credentialLogin": "資格情報でログイン",
"views.admin.DatabaseManager.current_db_version": "現在のDBバージョン",
"views.user.UserBar.currentUser": "現在のログインユーザー",
"views.admin.UserManagement.roleDonotExist": "現在のロールは存在しません",
"views.admin.Maintenance.customSqlCleanup": "カスタムSQLクリーンアップ",
"views.admin.AccountSettings.send_mail_daily_limit": "日次上限",
"views.admin.AccountSettings.send_mail_daily_limit_invalid": "日次上限は -1 以上の整数である必要があります",
"views.admin.IpBlacklistSettings.tip_daily_limit": "日次上限: IP ごとの 1 日あたり最大リクエスト数を制限します (1-1000000)。",
"views.admin.IpBlacklistSettings.daily_request_limit": "1日のリクエスト上限",
"views.Header.dark": "ダーク",
"views.Admin.database": "データベース",
"views.admin.DatabaseManager.need_initialization_tip": "データベースの初期化が必要です。先に初期化してください。",
"views.admin.DatabaseManager.initializationSuccess": "データベースを初期化しました",
"views.admin.DatabaseManager.migrationSuccess": "データベースを移行しました",
"views.admin.DatabaseManager.need_migration_tip": "データベースの移行が必要です。先に移行してください。",
"components.MailBox.delete": "削除",
"components.MailContentRenderer.delete": "削除",
"components.SendBox.delete": "削除",
"views.index.Attachment.delete": "削除",
"views.admin.Account.delete": "削除",
"views.admin.UserOauth2Settings.delete": "削除",
"views.admin.SenderAccess.delete": "削除",
"views.admin.UserManagement.delete": "削除",
"views.admin.AccountSettings.delete_rule": "削除",
"views.admin.Maintenance.deleteCustomSql": "削除",
"views.index.AccountSettings.deleteAccount": "アカウントを削除",
"views.admin.Account.deleteAccount": "アカウントを削除",
"views.user.UserSettings.deletePasskey": "Passkeyを削除",
"views.admin.AccountSettings.delete_success": "削除しました",
"views.admin.UserManagement.deleteUser": "ユーザーを削除",
"views.index.Attachment.deleteSuccess": "正常に削除しました",
"views.admin.SenderAccess.disable": "無効化",
"views.Admin.loginViaDisabledCheck": "パスワードチェックを無効化",
"views.common.Appearance.preferShowTextMail": "既定でテキストメールを表示",
"views.admin.AccountSettings.domain_list": "ドメイン一覧(任意)",
"views.admin.AccountSettings.source_patterns_tip": "ドメイン一覧は受信先アドレスで、送信元正規表現は送信者アドレスでフィルタします。転送には両方の条件を満たす必要がありますAND。どちらかを空欄にするとその条件は無視されます。",
"views.admin.UserManagement.domains": "ドメイン",
"views.index.Attachment.download": "ダウンロード",
"components.MailBox.downloadMail": "メールをダウンロード",
"components.MailContentRenderer.downloadMail": "メールをダウンロード",
"views.admin.Maintenance.sqlNamePlaceholder": "例: 古いログを削除",
"views.admin.Maintenance.sqlPlaceholder": "例: DELETE FROM raw_mails WHERE source GLOB '*{'@'}example.com' AND created_at < datetime('now', '-3 day')",
"views.admin.AccountSettings.source_patterns_placeholder": "例: gmail.com",
"views.admin.AccountSettings.forwarding_rule_warning": "各ルールは個別に実行されます。転送先アドレスは検証済みである必要があります。",
"views.index.SendMail.edit": "編集",
"views.admin.SendMail.edit": "編集",
"views.user.UserLogin.email": "メール",
"views.admin.UserManagement.email": "メール",
"views.common.Login.email": "メール",
"views.common.Login.credential": "メールアドレス資格情報",
"views.common.Login.emailPasswordRequired": "メールアドレスとパスワードは必須です",
"views.admin.UserSettings.emailCheckRegex": "メール検査用正規表現 (例: ^[^.]+{'@'}.+$ で {'@'} の前のドットを禁止)",
"views.admin.AccountSettings.email_forwarding_config": "メール転送設定",
"views.admin.UserOauth2Settings.userEmailFormat": "メール正規表現パターン",
"views.Admin.mails": "メール",
"views.index.AutoReply.sourcePrefixPlaceholder": "空欄=すべて、前方一致、または /regex/",
"components.WebhookComponent.enable": "有効化",
"views.admin.UserOauth2Settings.enable": "有効化",
"views.admin.SenderAccess.enable": "有効化",
"views.admin.Telegram.enable": "有効化",
"views.admin.UserSettings.enable": "有効化",
"views.admin.AiExtractSettings.enableAllowList": "アドレス許可リストを有効化",
"views.admin.Webhook.enableAllowList": "許可リストを有効化 (Webhook へのアクセスを特定ユーザーに制限)",
"views.index.AutoReply.enableAutoReply": "自動返信を有効化",
"views.admin.Maintenance.cronTip": "cron クリーンアップを有効にするには worker の [crons] を設定してください。詳細はドキュメントを参照してください。0 日はすべて削除を意味します。",
"views.admin.IpBlacklistSettings.enable_daily_limit": "1日のリクエスト上限を有効化",
"views.admin.UserSettings.enableEmailCheckRegex": "メール検査用正規表現を有効化",
"views.admin.UserOauth2Settings.enableEmailFormat": "メール形式チェックを有効化",
"views.admin.Telegram.enableGlobalMailPush": "グローバルメール通知を有効化 (Telegram の Chat ID を手動入力)",
"views.admin.IpBlacklistSettings.enable_ip_blacklist": "IPブラックリストを有効化",
"views.admin.IpBlacklistSettings.enable_ip_whitelist": "IPホワイトリストを有効化厳格",
"views.admin.UserOauth2Settings.enableMailAllowList": "メールアドレス許可リストを有効化 (手動入力可)",
"views.admin.UserSettings.enableMailAllowList": "メールアドレス許可リストを有効化 (手動入力可)",
"views.admin.UserSettings.enableMailVerify": "メール検証を有効化 (送信元アドレスはシステム内に存在し、残高があり、正常に送信できる必要があります)",
"views.admin.Telegram.enableTelegramAllowList": "Telegram 許可リストを有効化 (Chat ID を手動入力)",
"views.admin.IpBlacklistSettings.asn_blacklist_placeholder": "ASN組織を入力してください例: Google, Amazon",
"views.admin.IpBlacklistSettings.fingerprint_blacklist_placeholder": "フィンガープリントIDを入力してください例: a1b2c3d4e5f6g7h8",
"views.admin.IpBlacklistSettings.daily_request_limit_placeholder": "上限を入力してください(例: 1000",
"views.admin.IpBlacklistSettings.ip_blacklist_placeholder": "パターンを入力してください(例: 192.168.1 または ^10\\.0\\.0\\.5$",
"views.common.Login.bindUserAddressError": "メールアドレスをユーザーに紐付ける際にエラーが発生しました",
"views.admin.IpBlacklistSettings.ip_whitelist_placeholder": "完全一致IP例: 1.2.3.4)またはアンカー付き正規表現(例: ^192\\.168\\.1\\.\\d+$",
"views.index.SimpleIndex.exitSimpleIndex": "シンプルモードを終了",
"components.MailBox.keywordQueryTip": "現在のページを絞り込む",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env": "環境変数に従う",
"views.admin.AccountSettings.create_address_subdomain_match_force_disable": "強制的に無効化",
"views.admin.AccountSettings.create_address_subdomain_match_force_enable": "強制的に有効化",
"views.user.UserLogin.forgotPassword": "パスワードを忘れた",
"components.MailBox.forwardMail": "転送",
"components.MailContentRenderer.forward": "転送",
"views.admin.AccountSettings.forward_address": "転送先アドレス",
"views.admin.AccountSettings.forward_address_required": "転送先アドレスは必須です",
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
"components.MailContentRenderer.fullscreen": "全画面",
"views.common.Login.generateName": "ランダム名を生成",
"views.admin.Telegram.globalMailPushList": "グローバルメール通知 Chat ID 一覧",
"views.common.Appearance.globalTabplacement": "全体タブ位置",
"views.common.Login.help": "ヘルプ",
"views.Header.home": "ホーム",
"views.index.SendMail.html": "HTML",
"views.admin.SendMail.html": "HTML",
"views.admin.UserOauth2Settings.icon": "アイコン (SVG、信頼できるソースのみ使用してください)",
"views.admin.CreateAccount.enablePrefix": "プレフィックスを有効化する場合",
"views.common.AdminContact.adminContact": "支援が必要な場合は管理者 ({msg}) に連絡してください",
"views.admin.Telegram.init": "初期化",
"views.admin.DatabaseManager.init": "データベースを初期化",
"views.admin.AccountSettings.regex_invalid": "無効な正規表現です",
"views.Admin.ipBlacklistSettings": "IPブラックリスト",
"views.admin.IpBlacklistSettings.ip_blacklist": "IPブラックリスト パターン",
"views.admin.IpBlacklistSettings.title": "IPブラックリスト 設定",
"views.admin.IpBlacklistSettings.tip_ip": "IP ブラックリスト: テキスト一致 (例: \"192.168.1\") または正規表現 (例: \"^10\\.0\\.0\\.5$\") に対応します。",
"views.admin.IpBlacklistSettings.whitelist_empty_warning": "IP ホワイトリストは有効ですが一覧が空です。ロックアウト防止のためサーバーはこの設定を無視します。有効化する前に少なくとも 1 件追加してください。",
"views.admin.IpBlacklistSettings.ip_whitelist": "IPホワイトリスト パターン",
"views.admin.IpBlacklistSettings.tip_whitelist": "IP ホワイトリスト: 厳格な許可リストです。通常の項目は完全一致の IP である必要があります (部分一致不可)。範囲指定にはアンカー付き正規表現 (^...$) を使用してください。許可された IP はブラックリスト判定をスキップします。",
"views.admin.SenderAccess.is_enabled": "有効",
"views.admin.Account.itemCount": "itemCount",
"views.admin.SenderAccess.itemCount": "itemCount",
"views.admin.UserManagement.itemCount": "itemCount",
"views.user.UserMailBox.addressQueryTip": "空欄で全アドレスを検索します",
"views.admin.Account.addressQueryTip": "空欄で全アドレスを検索します",
"views.admin.Mails.addressQueryTip": "空欄で全アドレスを検索します",
"views.common.Appearance.left": "左",
"views.common.Login.getNewEmailTip2": "空欄のままにするとランダムなメールアドレスが生成されます。",
"views.Header.light": "ライト",
"views.admin.IpBlacklistSettings.enable_daily_limit_tip": "IP ごとの 1 日あたり API リクエスト数を制限",
"components.AddressSelect.localAddresses": "ローカルアドレス",
"views.common.Login.bindUserInfo": "ログイン済みユーザーは、未紐付けのままログインまたは新規作成すると現在のユーザーに紐付きます",
"views.user.UserOauth2Callback.logging": "ログイン中...",
"views.user.UserLogin.login": "ログイン",
"views.common.Login.login": "ログイン",
"views.common.Login.loginAndBind": "ログインして紐付け",
"views.Admin.loginMethod": "ログイン方法",
"views.user.UserBar.fetchUserSettingsError": "ログイン用パスワードが無効か、アカウントが存在しません。ネットワーク接続の問題の可能性もあるため、後でもう一度お試しください。",
"views.user.UserLogin.loginWith": "{provider}でログイン",
"views.user.UserLogin.loginWithPasskey": "パスキーでログイン",
"views.user.UserSettings.logout": "ログアウト",
"views.user.BindAddress.logout": "ログアウト",
"views.Admin.logout": "ログアウト",
"views.index.AccountSettings.logout": "ログアウト",
"views.Admin.logoutSuccess": "ログアウトしました",
"views.admin.UserOauth2Settings.mailAllowList": "メールアドレス許可リスト",
"views.admin.UserSettings.mailAllowList": "メールアドレス許可リスト",
"views.index.SimpleIndex.addressCredential": "メールアドレス資格情報",
"views.index.AddressBar.addressCredential": "メールアドレス資格情報",
"views.admin.Account.addressCredential": "メールアドレス資格情報",
"views.admin.CreateAccount.addressCredential": "メールアドレス資格情報",
"views.index.AddressBar.fetchAddressError": "メール資格情報が無効かアカウントが存在しません。ネットワークの問題の可能性もあります。後でもう一度お試しください。",
"views.Index.mailbox": "受信箱",
"views.User.user_mail_box_tab": "受信箱",
"views.user.AddressManagement.mail_count": "メール数",
"views.admin.Account.mail_count": "メール数",
"views.admin.Statistics.mailCount": "メール数",
"views.admin.UserAddressManagement.mail_count": "メール数",
"views.index.SimpleIndex.deleteSuccess": "メールを削除しました",
"views.user.UserLogin.cannotForgotPassword": "メール検証または登録が無効のため、パスワードを再設定できません。管理者へ連絡してください。",
"views.Admin.mailWebhook": "メールWebhook",
"views.common.Appearance.mailboxSplitSize": "メールボックス分割サイズ",
"views.index.SimpleIndex.refreshSuccess": "メールを更新しました",
"views.Admin.unknow": "受信者不明のメール",
"views.Admin.maintenance": "メンテナンス",
"views.index.AddressBar.addressManage": "管理",
"views.admin.AccountSettings.source_match_mode": "一致モード",
"views.admin.RoleAddressConfig.maxAddressCount": "最大アドレス数 (0 = 無制限)",
"views.admin.UserSettings.maxAddressCount": "紐付け可能なメールアドレスの最大数0 = 無制限)",
"views.Header.menu": "メニュー",
"views.admin.DatabaseManager.migration": "データベースを移行",
"views.admin.SenderAccess.modify": "変更",
"views.admin.AccountSettings.send_mail_monthly_limit": "月次上限",
"views.admin.AccountSettings.send_mail_monthly_limit_invalid": "月次上限は -1 以上の整数である必要があります",
"components.MailBox.multiAction": "複数操作",
"components.SendBox.multiAction": "複数操作",
"views.admin.Account.multiClearInbox": "受信箱を一括削除",
"views.admin.Account.multiClearSentItems": "送信済みを一括削除",
"views.admin.Account.multiDelete": "一括削除",
"views.user.AddressManagement.name": "名前",
"views.index.AutoReply.name": "名前",
"views.admin.Account.name": "名前",
"views.admin.UserOauth2Settings.name": "名前",
"views.admin.UserAddressManagement.name": "名前",
"views.admin.Maintenance.sqlName": "名前",
"views.index.AccountSettings.newPassword": "新しいパスワード",
"views.admin.Account.newPassword": "新しいパスワード",
"components.MailBox.nextMail": "次へ",
"views.index.SimpleIndex.nextPage": "次へ",
"views.admin.AccountSettings.noLimitSendAddressList": "残高無制限の送信アドレス一覧",
"views.index.SimpleIndex.noMails": "メールが見つかりません",
"views.admin.RoleAddressConfig.noRolesAvailable": "システム設定に利用可能なロールがありません",
"views.index.SendMail.requestAccessTip": "まだ送信残高がありません。管理者がデフォルト残高を有効にしていれば自動付与されます。そうでない場合は権限申請または管理者へ連絡してください。",
"components.SendBox.emptySent": "送信済みメールはありません",
"views.admin.RoleAddressConfig.notConfigured": "未設定 (全体設定を使用)",
"views.Admin.userOauth2Settings": "OAuth2設定",
"views.admin.UserOauth2Settings.oauth2Type": "OAuth2タイプ",
"views.Header.ok": "OK",
"views.Admin.ok": "OK",
"views.index.AddressBar.ok": "OK",
"views.admin.SenderAccess.ok": "OK",
"views.common.Login.ok": "OK",
"views.admin.AccountSettings.create_address_subdomain_match_tip": "/api/new_address と /admin/new_address のドメイン検証にのみ影響します。例: 有効時は foo.example.com が example.com に一致できます。",
"components.AiExtractInfo.open": "開く",
"views.index.AddressBar.linkWithAddressCredential": "メールの自動ログインリンクを開く",
"views.admin.CreateAccount.linkWithAddressCredential": "メールの自動ログインリンクを開く",
"views.index.SendMail.options": "オプション",
"views.admin.SendMail.options": "オプション",
"components.AiExtractInfo.otherLink": "その他のリンク",
"views.user.UserSettings.passkeyCreated": "Passkey を作成しました",
"views.user.UserSettings.passkey_name": "Passkey 名",
"views.user.UserLogin.password": "パスワード",
"views.admin.UserManagement.password": "パスワード",
"views.common.Login.password": "パスワード",
"views.index.AccountSettings.passwordChanged": "パスワードを変更しました",
"views.common.Login.passwordLogin": "パスワードログイン",
"views.admin.Account.passwordResetSuccess": "パスワードをリセットしました",
"views.index.AccountSettings.passwordMismatch": "パスワードが一致しません",
"views.index.SendMail.successSend": "送信箱を確認してください。失敗した場合は残高を確認するか、後でもう一度お試しください。",
"views.admin.SendMail.successSend": "送信箱を確認してください。失敗した場合は、しばらくしてから再試行してください。",
"views.user.UserLogin.pleaseCompleteTurnstile": "Turnstile を完了してください",
"views.index.SimpleIndex.addressCredentialTip": "メールアドレス資格情報をコピーしてください。この情報でログインできます。",
"views.index.AddressBar.addressCredentialTip": "メールアドレス資格情報をコピーしてください。この情報でメールアカウントにログインできます。",
"views.admin.Account.addressCredentialTip": "メールアドレス資格情報をコピーしてください。この情報でメールアカウントにログインできます。",
"views.admin.CreateAccount.addressCredentialTip": "メールアドレス資格情報をコピーしてください。この情報でメールアカウントにログインできます。",
"views.Admin.accessTip": "管理者パスワードを入力してください",
"views.Header.accessTip": "正しいアクセス用パスワードを入力してください",
"views.admin.AccountSettings.address_block_list_placeholder": "ブロックしたいキーワードを入力してください",
"views.user.UserSettings.renamePasskeyNamePlaceholder": "新しい Passkey 名を入力してください",
"views.user.UserSettings.passkeyNamePlaceholder": "Passkey 名を入力するか、空欄でランダム生成してください",
"views.admin.CreateAccount.fillInAllFields": "すべての項目を入力してください",
"views.admin.SendBox.queryTip": "検索するアドレスを入力してください。空欄で全件検索します",
"views.user.UserLogin.pleaseInputCode": "コードを入力してください",
"views.admin.UserManagement.pleaseInput": "必要な情報をすべて入力してください",
"views.user.UserLogin.pleaseInputEmail": "メールを入力してください",
"views.user.UserLogin.pleaseInput": "メールアドレスとパスワードを入力してください",
"views.admin.Maintenance.tip": "日数を入力してください",
"views.common.Login.getNewEmailTip1": "使用したいメールアドレスを入力してください。使用可能: ",
"views.common.Login.credentialInput": "メールアドレス資格情報を入力してください",
"views.admin.SenderAccess.modalTip": "送信残高を入力してください",
"views.user.UserLogin.pleaseLogin": "ログインしてください",
"views.common.Login.pleaseGetNewEmail": "ログインするか、「新しいメールを作成」をクリックして新しいアドレスを取得してください。",
"components.WebhookComponent.fillInDemoTip": "URL とその他設定を自分のものに変更してください",
"components.SendBox.pleaseSelectMail": "表示するメールを選択してください。",
"views.admin.Account.pleaseSelectAddress": "アドレスを選択してください",
"components.MailBox.pleaseSelectMail": "メールを選択してください",
"views.admin.UserManagement.prefix": "プレフィックス",
"components.WebhookComponent.presets": "プリセット",
"views.index.SendMail.preview": "プレビュー",
"views.admin.SendMail.preview": "プレビュー",
"views.admin.UserOauth2Settings.iconPreview": "プレビュー",
"components.MailBox.prevMail": "前へ",
"views.index.SimpleIndex.prevPage": "前へ",
"components.MailBox.query": "検索",
"views.user.UserMailBox.query": "検索",
"views.Index.query": "検索",
"views.admin.SendBox.query": "検索",
"views.admin.Account.query": "検索",
"views.admin.SenderAccess.query": "検索",
"views.admin.UserManagement.query": "検索",
"views.admin.Mails.query": "検索",
"views.Admin.qucickSetup": "クイック設定",
"views.index.SendMail.toMailEmpty": "受信者アドレスが空です",
"views.admin.SendMail.toMailEmpty": "受信者アドレスが空です",
"views.index.SendMail.toName": "受信者名とアドレス。名前を空欄にするとメールアドレスを使用します",
"views.admin.SendMail.toName": "受信者名とアドレス。名前を空欄にするとメールアドレスを使用します",
"components.MailBox.refresh": "更新",
"components.Turnstile.refresh": "更新",
"components.SendBox.refresh": "更新",
"views.index.SimpleIndex.refreshMails": "更新",
"components.MailBox.refreshAfter": "あと {msg} 秒で更新",
"views.index.SimpleIndex.refreshAfter": "あと {msg} 秒で更新",
"views.admin.AccountSettings.regex_too_long": "正規表現が長すぎます(最大 200 文字)",
"views.user.UserLogin.register": "登録",
"views.user.UserSettings.renamePasskey": "Passkeyを名前変更",
"views.admin.UserOauth2Settings.userEmailReplace": "置換テンプレート",
"components.MailBox.reply": "返信",
"components.MailContentRenderer.reply": "返信",
"views.index.SendMail.requestAccess": "アクセスを申請",
"views.user.UserLogin.resetPassword": "パスワードをリセット",
"views.admin.Account.resetPassword": "パスワードをリセット",
"views.admin.UserManagement.resetPassword": "パスワードをリセット",
"views.index.SendMail.rich text": "リッチテキスト",
"views.admin.SendMail.rich text": "リッチテキスト",
"views.common.Appearance.right": "右",
"views.admin.UserManagement.role": "ロール",
"views.admin.RoleAddressConfig.role": "ロール",
"views.Admin.roleAddressConfig": "ロール別アドレス設定",
"views.admin.AccountSettings.rule_index": "ルール",
"views.Index.s3Attachment": "S3添付ファイル",
"components.WebhookComponent.save": "保存",
"views.index.AutoReply.save": "保存",
"views.admin.AiExtractSettings.save": "保存",
"views.admin.UserOauth2Settings.save": "保存",
"views.admin.IpBlacklistSettings.save": "保存",
"views.admin.Telegram.save": "保存",
"views.admin.UserSettings.save": "保存",
"views.admin.RoleAddressConfig.save": "保存",
"views.admin.AccountSettings.save": "保存",
"views.admin.Maintenance.save": "保存",
"views.admin.Webhook.save": "保存",
"views.admin.Maintenance.saveSuccess": "保存しました",
"views.admin.UserOauth2Settings.successTip": "保存しました",
"views.admin.IpBlacklistSettings.successTip": "保存しました",
"views.admin.UserSettings.successTip": "保存しました",
"views.admin.AccountSettings.successTip": "保存しました",
"components.MailBox.saveToS3": "S3 に保存",
"components.MailContentRenderer.saveToS3": "S3 に保存",
"views.Index.saveToS3Success": "S3 への保存に成功しました",
"components.MailBox.selectAll": "このページをすべて選択",
"components.SendBox.selectAll": "このページをすべて選択",
"views.admin.Account.selectAll": "このページをすべて選択",
"views.admin.AccountSettings.select_domain": "ドメインを選択",
"views.admin.Account.selectedItems": "選択済み",
"views.index.SendMail.send": "送信",
"views.admin.SendMail.send": "送信",
"views.Index.sendbox": "送信箱",
"views.Admin.sendBox": "送信箱",
"views.user.AddressManagement.send_count": "送信数",
"views.admin.Account.send_count": "送信数",
"views.admin.UserAddressManagement.send_count": "送信数",
"views.Index.sendmail": "メール送信",
"views.Admin.sendMail": "メール送信",
"views.index.SendMail.send_balance": "残り送信枠",
"views.admin.Statistics.sendMailCount": "送信メール数",
"views.admin.AccountSettings.send_mail_limit": "送信上限",
"views.user.UserLogin.sendVerificationCode": "認証コードを送信",
"views.Admin.senderAccess": "送信権限管理",
"views.admin.SendMail.fromMailEmpty": "送信元アドレスが空です",
"views.index.AutoReply.sourcePrefix": "送信者フィルター",
"components.AiExtractInfo.serviceLink": "サービスリンク",
"views.index.AutoReply.settings": "設定",
"views.index.AccountSettings.showAddressCredential": "アドレス資格情報を表示",
"components.MailBox.attachments": "添付ファイルを表示",
"components.MailBox.showHtmlMail": "HTMLメールを表示",
"components.MailContentRenderer.showHtmlMail": "HTMLメールを表示",
"views.admin.Account.showCredential": "メールアドレス資格情報を表示",
"views.user.UserSettings.showPasskeyList": "Passkey 一覧を表示",
"components.MailBox.showTextMail": "テキストメールを表示",
"components.MailContentRenderer.showTextMail": "テキストメールを表示",
"views.Index.enterSimpleMode": "シンプルモード",
"components.MailContentRenderer.size": "サイズ",
"views.admin.Account.source_meta": "送信元",
"views.admin.AccountSettings.source_patterns": "送信元アドレス正規表現 (任意)",
"views.admin.Maintenance.sqlStatement": "SQL文 (DELETEのみ)",
"views.user.UserOauth2Callback.stateNotMatch": "state が一致しません",
"views.Admin.statistics": "統計",
"views.Header.status": "ステータス",
"views.admin.IpBlacklistSettings.enable_whitelist_tip": "厳格モード: ホワイトリストに一致する IP のみがレート制限対象 API にアクセスできます。それ以外の IP はすべて拒否されます。",
"views.index.AutoReply.subject": "件名",
"views.index.SendMail.subject": "件名",
"views.admin.SendMail.subject": "件名",
"views.index.SendMail.subjectEmpty": "件名が空です",
"views.admin.SendMail.subjectEmpty": "件名が空です",
"components.AiExtractInfo.subscriptionLink": "購読リンク",
"views.user.AddressManagement.success": "成功",
"views.index.TelegramAddress.success": "成功",
"views.index.LocalAddress.success": "成功",
"views.admin.UserAddressManagement.success": "成功",
"components.WebhookComponent.successTip": "成功",
"components.MailBox.success": "成功",
"components.SendBox.success": "成功",
"views.index.AccountSettings.success": "成功",
"views.index.AutoReply.success": "成功",
"views.admin.AiExtractSettings.successTip": "成功",
"views.admin.Account.success": "成功",
"views.admin.SenderAccess.success": "成功",
"views.admin.UserManagement.success": "成功",
"views.admin.Telegram.successTip": "成功",
"views.admin.RoleAddressConfig.successTip": "成功",
"views.admin.Webhook.successTip": "成功",
"views.admin.CreateAccount.successTip": "作成に成功",
"views.admin.Telegram.globalMailPushListTip": "個人/グループ/チャンネルの chat_id をサポートします。ボットへメッセージ送信後、このリンクで chat_id を確認できます: https://api.telegram.org/bot<あなたの BOT TOKEN に置換>/getUpdates",
"views.user.AddressManagement.targetUserEmail": "対象ユーザーのメール",
"views.admin.Telegram.telegramAllowList": "Telegram 許可リスト (Chat ID を手動入力)",
"views.Admin.telegram": "Telegramボット",
"views.admin.Telegram.miniAppUrl": "Telegram Mini App URL",
"components.WebhookComponent.test": "テスト",
"views.index.SendMail.text": "テキスト",
"views.admin.SendMail.text": "テキスト",
"views.user.UserSettings.passordTip": "サーバーはパスワードのハッシュ値のみ受け取り、平文は受け取りません。そのためパスワードを閲覧・復元できません。管理者がメール検証を有効にしている場合はシークレットモードで再設定できます。",
"views.index.LocalAddress.tip": "これらのアドレスはブラウザに保存されており、キャッシュを消すと失われる可能性があります。",
"views.admin.UserOauth2Settings.tip": "サードパーティログインではユーザーのメールアドレスで自動的にアカウント登録されます(同じメールは同一アカウントとして扱われます)。「パスワードを忘れた」からパスワード設定も可能です。",
"views.admin.AccountSettings.send_mail_limit_tip": "すべての送信チャネルに適用されます。-1 は無制限、0 は送信禁止です。",
"views.admin.AccountSettings.create_address_subdomain_match_note": "これは RANDOM_SUBDOMAIN_DOMAINS と異なります。この設定では API 呼び出し側が独自サブドメインを指定でき、ランダムサブドメインは作成時に自動生成されるだけです。",
"views.index.SendMail.tooLarge": "ファイルが大きすぎます。1MB 未満のファイルをアップロードしてください。",
"views.admin.SendMail.tooLarge": "ファイルが大きすぎます。1MB 未満のファイルをアップロードしてください。",
"views.common.Appearance.top": "上",
"views.user.AddressManagement.transferAddress": "アドレスを移行",
"views.user.AddressManagement.transferAddressTip": "別のユーザーへアドレスを移行すると、そのアドレスはあなたのアカウントから削除され、相手のアカウントへ移されます。本当に移行しますか?",
"views.common.Appearance.useSideMargin": "ページ左右の余白を有効にする",
"views.admin.AiExtractSettings.manualInputPrompt": "入力して Enter で追加",
"views.admin.UserOauth2Settings.manualInputPrompt": "入力して Enter で追加",
"views.admin.Telegram.manualInputPrompt": "入力して Enter で追加",
"views.admin.UserSettings.manualInputPrompt": "入力して Enter で追加",
"views.admin.AccountSettings.manualInputPrompt": "入力して Enter で追加",
"views.admin.Webhook.manualInputPrompt": "入力して Enter で追加",
"views.admin.IpBlacklistSettings.manualInputPrompt": "パターンを入力して Enter で追加",
"views.user.AddressManagement.unbindAddress": "アドレスの紐付けを解除",
"views.index.TelegramAddress.unbindMailAddress": "メールアドレスの紐付けを解除",
"views.index.LocalAddress.unbindMailAddress": "メールアドレス資格情報の紐付けを解除",
"components.MailBox.unselectAll": "すべての選択を解除",
"components.SendBox.unselectAll": "すべての選択を解除",
"views.admin.Account.unselectAll": "すべての選択を解除",
"views.admin.Account.updated_at": "更新日時",
"views.user.UserSettings.updated_at": "更新日時",
"components.WebhookComponent.urlMissing": "URL は必須です",
"views.common.Appearance.useIframeShowMail": "iframe で HTML メールを表示",
"views.admin.CreateAccount.enableRandomSubdomain": "ランダムサブドメインを使用",
"views.common.Login.enableRandomSubdomain": "ランダムサブドメインを使用",
"views.admin.UserOauth2Settings.userEmailFormatTip": "正規表現でメールを変換します。例: ^(.+)@old\\.com$ と $1@new.com",
"views.common.Appearance.useSimpleIndex": "シンプルインデックスを使う",
"views.common.Appearance.useUTCDate": "UTC 日時を使う",
"views.Header.user": "ユーザー",
"views.Admin.user": "ユーザー",
"components.AddressSelect.userAddresses": "ユーザーのアドレス",
"views.Admin.loginViaUserAdmin": "ユーザー管理者権限",
"views.admin.Statistics.userCount": "ユーザー数",
"views.admin.UserManagement.user_email": "ユーザーメール",
"views.index.AddressBar.userLogin": "ユーザーログイン",
"views.Admin.user_management": "ユーザー管理",
"views.Admin.user_settings": "ユーザー設定",
"views.User.user_settings": "ユーザー設定",
"components.AiExtractInfo.authCode": "認証コード",
"views.user.UserLogin.verifyCode": "認証コード",
"views.user.UserLogin.verifyCodeSent": "認証コードを送信しました, 有効期限 {timeout} 秒",
"views.admin.AccountSettings.verified_address_list": "検証済みアドレス一覧 (cf internal api でメール送信可能)",
"views.admin.UserSettings.verifyMailSender": "メール送信元を検証",
"components.MailContentRenderer.attachments": "添付ファイルを表示",
"views.admin.Account.viewMails": "メールを表示",
"views.admin.Account.viewSendBox": "送信箱を表示",
"views.user.UserLogin.waitforVerifyCode": "待機 {timeout} 秒",
"views.admin.Webhook.webhookAllowList": "Webhook 許可リスト (利用を許可するメールアドレスを入力して Enter)",
"views.admin.Webhook.notEnabled": "Webhook は有効ではありません",
"components.WebhookComponent.notEnabled": "あなたには Webhook が有効ではありません",
"views.Index.webhookSettings": "Webhook設定",
"views.Admin.webhookSettings": "Webhook設定",
"views.admin.AiExtractSettings.disabledTip": "無効時は AI 抽出がすべてのメールアドレスを処理します",
"views.admin.AiExtractSettings.enableAllowListTip": "有効時は AI 抽出は許可リストのアドレス宛メールのみ処理します",
"views.admin.CreateAccount.randomSubdomainTip": "有効時は作成されるアドレスがランダムなサブドメインを使用します。サブドメインアドレスは受信専用として推奨されます。",
"views.common.Login.randomSubdomainTip": "有効時は作成されるアドレスがランダムなサブドメインを使用します。サブドメインアドレスは受信専用として推奨されます。",
"views.admin.AiExtractSettings.allowListTip": "ワイルドカード * は任意の文字に一致します。例: *{'@'}example.com は example.com ドメイン配下のすべてのアドレスに一致します",
"views.Admin.workerconfig": "Worker設定",
"views.admin.AccountSettings.create_address_subdomain_match_env_locked": "Worker 環境変数 ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH は現在 false です。管理画面のスイッチは保存できますが、env を有効化または削除するまで反映されません。",
"views.common.Login.getNewEmailTip3": "ドロップダウン一覧からドメインを選択できます。",
"views.admin.AccountSettings.tip": "以下の複数選択項目は手動入力して Enter で追加できます",
"components.MailBox.emptyInbox": "受信箱は空です",
"views.index.SendMail.fromName": "あなたの名前とアドレス。名前を空欄にするとメールアドレスを使用します",
"views.admin.SendMail.fromName": "あなたの名前とアドレス。名前を空欄にするとメールアドレスを使用します"
}

View File

@@ -0,0 +1,590 @@
export const ptBRMessages = {
"views.index.SimpleIndex.mailCount": "{current} / {total} e-mails",
"views.admin.Statistics.activeAddressCount30days": "Quantidade de endereços ativos em 30 dias",
"views.admin.Statistics.activeAddressCount7days": "Quantidade de endereços ativos em 7 dias",
"views.Index.about": "Sobre",
"views.Admin.about": "Sobre",
"views.Header.accessHeader": "Senha de acesso",
"views.Admin.account": "Conta",
"views.Index.accountSettings": "Configurações da conta",
"views.Admin.account_settings": "Configurações da conta",
"views.index.SimpleIndex.accountSettings": "Configurações da conta",
"views.index.Attachment.action": "Ação",
"views.admin.SenderAccess.action": "Ação",
"views.user.UserSettings.actions": "Ações",
"views.user.AddressManagement.actions": "Ações",
"views.index.TelegramAddress.actions": "Ações",
"views.admin.Account.actions": "Ações",
"views.index.LocalAddress.actions": "Ações",
"views.admin.UserManagement.actions": "Ações",
"views.admin.AccountSettings.actions": "Ações",
"views.admin.AccountSettings.add": "Adicionar",
"views.admin.Maintenance.customSqlTip": "Adicione instruções SQL DELETE personalizadas para a limpeza agendada. Apenas uma instrução DELETE é permitida por item.",
"views.admin.Maintenance.addCustomSql": "Adicionar SQL personalizada",
"views.admin.UserOauth2Settings.addOauth2": "Adicionar OAuth2",
"components.AddressSelect.address": "Endereço",
"views.user.AddressManagement.address": "Endereço",
"views.index.TelegramAddress.address": "Endereço",
"views.admin.SenderAccess.address": "Endereço",
"views.index.LocalAddress.address": "Endereço",
"views.admin.CreateAccount.address": "Endereço",
"views.admin.AiExtractSettings.allowList": "Lista branca de endereços (digite o endereço e pressione Enter; suporta curingas)",
"views.admin.AccountSettings.send_address_block_list": "Palavras-chave bloqueadas para envio de e-mail",
"views.admin.AccountSettings.address_block_list": "Palavras-chave bloqueadas para usuários (o administrador pode ignorar)",
"views.index.SimpleIndex.addressCopied": "Endereço copiado com sucesso",
"views.admin.Statistics.addressCount": "Quantidade de endereços",
"views.admin.UserManagement.address_count": "Quantidade de endereços",
"views.User.address_management": "Gerenciamento de endereços",
"views.admin.UserManagement.userAddressManagement": "Gerenciamento de endereços",
"views.index.AddressBar.addressPassword": "Senha do endereço",
"views.admin.CreateAccount.addressPassword": "Senha do endereço",
"views.Admin.adminAccount": "Administrador",
"views.Admin.accessHeader": "Senha de administrador",
"views.Admin.loginViaPassword": "Entrar como administrador com senha",
"views.admin.AiExtractSettings.title": "Configurações de extração de e-mail por IA",
"views.Admin.aiExtractSettings": "Configurações de extração por IA",
"views.admin.AccountSettings.match_all": "Todos",
"views.admin.AccountSettings.create_address_subdomain_match": "Permitir correspondência por sufixo de subdomínio ao criar endereços",
"views.admin.UserSettings.enableUserRegister": "Permitir cadastro de usuários",
"views.admin.AccountSettings.match_any": "Qualquer",
"views.Index.appearance": "Aparência",
"views.Admin.appearance": "Aparência",
"views.admin.IpBlacklistSettings.tip_scope": "Aplica-se a: criar endereço, enviar e-mail, API externa de envio, cadastro de usuário e verificação de código",
"views.index.AccountSettings.clearInboxConfirm": "Tem certeza de que deseja limpar todos os e-mails da sua caixa de entrada?",
"views.index.AccountSettings.clearSentItemsConfirm": "Tem certeza de que deseja limpar todos os e-mails enviados?",
"views.admin.Account.multiClearInboxTip": "Tem certeza de que deseja limpar a caixa de entrada dos endereços selecionados?",
"views.admin.Account.clearInboxTip": "Tem certeza de que deseja limpar a caixa de entrada deste e-mail?",
"views.admin.Account.multiClearSentItemsTip": "Tem certeza de que deseja limpar os itens enviados dos endereços selecionados?",
"views.admin.Account.clearSentItemsTip": "Tem certeza de que deseja limpar os itens enviados deste e-mail?",
"views.admin.Account.multiDeleteTip": "Tem certeza de que deseja excluir os endereços selecionados?",
"views.index.Attachment.deleteConfirm": "Tem certeza de que deseja excluir este anexo?",
"views.admin.Account.deleteTip": "Tem certeza de que deseja excluir este e-mail?",
"views.admin.SenderAccess.deleteTip": "Tem certeza de que deseja excluir isto?",
"views.index.AccountSettings.deleteAccountConfirm": "Tem certeza de que deseja excluir sua conta e todos os e-mails dela?",
"views.index.AccountSettings.logoutConfirm": "Tem certeza de que deseja sair?",
"components.MailBox.deleteMailTip": "Tem certeza de que deseja excluir o e-mail?",
"components.MailContentRenderer.deleteMailTip": "Tem certeza de que deseja excluir o e-mail?",
"components.SendBox.deleteMailTip": "Tem certeza de que deseja excluir o e-mail?",
"views.admin.AccountSettings.delete_rule_confirm": "Tem certeza de que deseja excluir esta regra?",
"views.admin.UserManagement.deleteUserTip": "Tem certeza de que deseja excluir este usuário?",
"views.Admin.logoutConfirmContent": "Tem certeza de que deseja sair do painel de administração?",
"views.user.UserSettings.logoutConfirm": "Tem certeza de que deseja sair?",
"views.admin.IpBlacklistSettings.asn_blacklist": "Lista negra de organizações ASN",
"views.admin.IpBlacklistSettings.tip_asn": "Organização ASN: bloquear por ISP/provedor. Correspondência de texto sem diferenciar maiúsculas ou regex.",
"components.AiExtractInfo.authLink": "Link de autenticação",
"views.admin.Maintenance.autoCleanup": "Limpeza automática",
"components.MailBox.autoRefresh": "Atualização automática",
"views.common.Appearance.autoRefreshInterval": "Intervalo de atualização automática (s)",
"views.Index.auto_reply": "Resposta automática",
"views.index.AutoReply.autoReply": "Resposta automática",
"views.common.Login.autoGeneratedName": "Nome gerado automaticamente",
"views.admin.SenderAccess.balance": "Saldo",
"views.admin.Maintenance.basicCleanup": "Limpeza básica",
"views.user.AddressManagement.unbindAddressTip": "Antes de desvincular, mude para este endereço e salve a credencial do e-mail.",
"views.index.TelegramAddress.bind": "Vincular",
"views.index.TelegramAddress.bindAddressSuccess": "Endereço vinculado com sucesso",
"views.index.LocalAddress.bindAddressSuccess": "Endereço vinculado com sucesso",
"views.User.bind_address": "Vincular endereço de e-mail",
"views.admin.IpBlacklistSettings.enable_tip": "Bloquear IPs que correspondam à lista negra nas APIs com limite",
"views.admin.AccountSettings.fromBlockList": "Palavras bloqueadas para receber e-mail",
"views.admin.AccountSettings.block_receive_unknow_address_email": "Bloquear recebimento de e-mails para endereços desconhecidos",
"views.common.Appearance.bottom": "base",
"views.admin.IpBlacklistSettings.fingerprint_blacklist": "Lista negra de impressão digital do navegador",
"views.admin.IpBlacklistSettings.tip_fingerprint": "Impressão digital do navegador: bloqueia por impressão. Suporta correspondência exata ou regex.",
"views.admin.AccountSettings.cancel": "Cancelar",
"components.MailBox.cancelMultiAction": "Cancelar múltipla seleção",
"components.SendBox.cancelMultiAction": "Cancelar múltipla seleção",
"views.user.AddressManagement.changeMailAddress": "Alterar endereço",
"views.index.TelegramAddress.changeMailAddress": "Alterar endereço de e-mail",
"views.index.LocalAddress.changeMailAddress": "Alterar endereço de e-mail",
"views.index.AccountSettings.changePassword": "Alterar senha",
"views.admin.UserManagement.changeRole": "Alterar Função",
"components.SendBox.showCode": "Alternar visualização do código original",
"views.admin.Telegram.status": "Ver status",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env_note": "Escolher \"Seguir variável de ambiente\" remove a sobrescrita do administrador e retorna ao estado não definido. O resultado efetivo continua sendo controlado pelas variáveis do Worker e pelas regras de precedência.",
"views.admin.Maintenance.cleanupNow": "Limpar agora",
"views.admin.Maintenance.cleanupSuccess": "Limpeza concluída",
"views.admin.Maintenance.addressCreateLabel": "Limpar os endereços criados há mais de n dias",
"views.admin.Maintenance.emptyAddressLabel": "Limpar os endereços vazios de mais de n dias",
"views.admin.Maintenance.inactiveAddressLabel": "Limpar os endereços inativos de mais de n dias",
"views.admin.Maintenance.mailBoxLabel": "Limpar a caixa de entrada de mais de n dias",
"views.admin.Maintenance.sendBoxLabel": "Limpar a caixa de saída de mais de n dias",
"views.admin.Maintenance.unboundAddressLabel": "Limpar os endereços desvinculados de mais de n dias",
"views.admin.Maintenance.mailUnknowLabel": "Limpar os e-mails com destinatário desconhecido de mais de n dias",
"views.index.AccountSettings.clearInbox": "Limpar caixa de entrada",
"views.admin.Account.clearInbox": "Limpar caixa de entrada",
"views.index.AccountSettings.clearSentItems": "Limpar itens enviados",
"views.admin.Account.clearSentItems": "Limpar itens enviados",
"views.Header.title": "Cloudflare E-mail Temporário",
"views.admin.DatabaseManager.code_db_version": "Versão do banco exigida pelo código",
"views.user.UserOauth2Callback.codeNotFound": "código não encontrado",
"views.admin.AccountSettings.config": "Configuração",
"views.admin.RoleAddressConfig.roleConfigDesc": "Configure a quantidade máxima de endereços para cada função. Os limites por função têm prioridade sobre a configuração global. Use 0 para ilimitado.",
"views.Admin.confirm": "Confirmar",
"views.Admin.logoutConfirmTitle": "Confirmar saída",
"views.index.AccountSettings.confirmPassword": "Confirmar senha",
"views.index.SendMail.content": "Conteúdo",
"views.admin.SendMail.content": "Conteúdo",
"views.index.SendMail.contentEmpty": "O conteúdo está vazio",
"views.admin.SendMail.contentEmpty": "O conteúdo está vazio",
"components.AddressSelect.copied": "Copiado",
"components.AiExtractInfo.copySuccess": "Copiado com sucesso",
"components.AddressSelect.copy": "Copiar",
"views.index.SimpleIndex.copyAddress": "Copiar",
"components.AiExtractInfo.copyFailed": "Falha ao copiar",
"views.Footer.copyright": "Direitos autorais",
"views.Admin.account_create": "Criar conta",
"views.admin.CreateAccount.creatNewEmail": "Criar novo e-mail",
"views.common.Login.getNewEmail": "Criar novo e-mail",
"views.user.AddressManagement.create_or_bind": "Criar ou vincular",
"views.index.LocalAddress.create_or_bind": "Criar ou vincular",
"views.user.UserSettings.createPasskey": "Criar passkey",
"views.admin.UserManagement.createUser": "Criar usuário",
"views.user.UserSettings.created_at": "Criado em",
"views.admin.Account.created_at": "Criado em",
"views.admin.SenderAccess.created_at": "Criado em",
"views.admin.UserManagement.created_at": "Criado em",
"views.common.Login.credentialLogin": "Login com credencial",
"views.admin.DatabaseManager.current_db_version": "Versão atual do banco",
"views.user.UserBar.currentUser": "Usuário atual",
"views.admin.UserManagement.roleDonotExist": "A função atual não existe",
"views.admin.Maintenance.customSqlCleanup": "Limpeza SQL personalizada",
"views.admin.AccountSettings.send_mail_daily_limit": "Limite diário",
"views.admin.AccountSettings.send_mail_daily_limit_invalid": "O limite diário deve ser um inteiro maior ou igual a -1",
"views.admin.IpBlacklistSettings.tip_daily_limit": "Limite diário: restringe o número máximo de requisições por IP por dia (1-1000000).",
"views.admin.IpBlacklistSettings.daily_request_limit": "Limite diário de solicitações",
"views.Header.dark": "Escuro",
"views.Admin.database": "Banco de dados",
"views.admin.DatabaseManager.need_initialization_tip": "É necessário inicializar o banco de dados. Faça a inicialização primeiro.",
"views.admin.DatabaseManager.initializationSuccess": "Banco de dados inicializado com sucesso",
"views.admin.DatabaseManager.migrationSuccess": "Banco de dados migrado com sucesso",
"views.admin.DatabaseManager.need_migration_tip": "É necessário migrar o banco de dados. Execute a migração primeiro.",
"components.MailBox.delete": "Excluir",
"components.MailContentRenderer.delete": "Excluir",
"components.SendBox.delete": "Excluir",
"views.index.Attachment.delete": "Excluir",
"views.admin.Account.delete": "Excluir",
"views.admin.UserOauth2Settings.delete": "Excluir",
"views.admin.SenderAccess.delete": "Excluir",
"views.admin.UserManagement.delete": "Excluir",
"views.admin.AccountSettings.delete_rule": "Excluir",
"views.admin.Maintenance.deleteCustomSql": "Excluir",
"views.index.AccountSettings.deleteAccount": "Excluir conta",
"views.admin.Account.deleteAccount": "Excluir conta",
"views.user.UserSettings.deletePasskey": "Excluir passkey",
"views.admin.AccountSettings.delete_success": "Excluído com sucesso",
"views.admin.UserManagement.deleteUser": "Excluir usuário",
"views.index.Attachment.deleteSuccess": "Excluído com sucesso",
"views.admin.SenderAccess.disable": "Desativar",
"views.Admin.loginViaDisabledCheck": "Verificação de senha desativada",
"views.common.Appearance.preferShowTextMail": "Exibir e-mail em texto por padrão",
"views.admin.AccountSettings.domain_list": "Lista de domínios (opcional)",
"views.admin.AccountSettings.source_patterns_tip": "A lista de domínios filtra pelo destinatário e o regex de origem filtra pelo remetente. Ambas as condições precisam corresponder para encaminhar (lógica AND). Deixe qualquer uma vazia para ignorá-la.",
"views.admin.UserManagement.domains": "Domínios",
"views.index.Attachment.download": "Baixar",
"components.MailBox.downloadMail": "Baixar E-mail",
"components.MailContentRenderer.downloadMail": "Baixar E-mail",
"views.admin.Maintenance.sqlNamePlaceholder": "ex.: limpar logs antigos",
"views.admin.Maintenance.sqlPlaceholder": "ex.: DELETE FROM raw_mails WHERE source GLOB '*{'@'}example.com' AND created_at < datetime('now', '-3 day')",
"views.admin.AccountSettings.source_patterns_placeholder": "ex.: gmail.com",
"views.admin.AccountSettings.forwarding_rule_warning": "Cada regra é executada de forma independente. O endereço de encaminhamento deve ser verificado.",
"views.index.SendMail.edit": "Editar",
"views.admin.SendMail.edit": "Editar",
"views.user.UserLogin.email": "E-mail",
"views.admin.UserManagement.email": "E-mail",
"views.common.Login.email": "E-mail",
"views.common.Login.credential": "Credencial do endereço de e-mail",
"views.common.Login.emailPasswordRequired": "E-mail e senha são obrigatórios",
"views.admin.UserSettings.emailCheckRegex": "Regex de verificação de e-mail (ex.: ^[^.]+{'@'}.+$ para impedir pontos antes de {'@'})",
"views.admin.AccountSettings.email_forwarding_config": "Configuração de encaminhamento de e-mail",
"views.admin.UserOauth2Settings.userEmailFormat": "Padrão regex de e-mail",
"views.Admin.mails": "E-mails",
"views.index.AutoReply.sourcePrefixPlaceholder": "Vazio=todos, prefixo ou /regex/",
"components.WebhookComponent.enable": "Ativar",
"views.admin.UserOauth2Settings.enable": "Ativar",
"views.admin.SenderAccess.enable": "Ativar",
"views.admin.Telegram.enable": "Ativar",
"views.admin.UserSettings.enable": "Ativar",
"views.admin.AiExtractSettings.enableAllowList": "Ativar Lista branca de endereços",
"views.admin.Webhook.enableAllowList": "Ativar lista de permissão (restringir o acesso ao webhook a usuários específicos)",
"views.index.AutoReply.enableAutoReply": "Ativar resposta automática",
"views.admin.Maintenance.cronTip": "Para ativar a limpeza por cron, configure [crons] no worker. Consulte a documentação; 0 dias significa limpar tudo.",
"views.admin.IpBlacklistSettings.enable_daily_limit": "Ativar limite diário de solicitações",
"views.admin.UserSettings.enableEmailCheckRegex": "Ativar regex de verificação de e-mail",
"views.admin.UserOauth2Settings.enableEmailFormat": "Ativar formato de e-mail",
"views.admin.Telegram.enableGlobalMailPush": "Ativar envio global de e-mails (informar manualmente o Chat ID do Telegram)",
"views.admin.IpBlacklistSettings.enable_ip_blacklist": "Ativar Lista negra de IP",
"views.admin.IpBlacklistSettings.enable_ip_whitelist": "Ativar Lista branca de IP (estrito)",
"views.admin.UserOauth2Settings.enableMailAllowList": "Ativar lista branca de endereços de e-mail (editável manualmente)",
"views.admin.UserSettings.enableMailAllowList": "Ativar lista branca de endereços de e-mail (editável manualmente)",
"views.admin.UserSettings.enableMailVerify": "Habilitar verificação de e-mail (o endereço remetente deve existir no sistema, ter saldo e poder enviar normalmente)",
"views.admin.Telegram.enableTelegramAllowList": "Ativar lista branca do Telegram (informar manualmente o Chat ID)",
"views.admin.IpBlacklistSettings.asn_blacklist_placeholder": "Informe a organização ASN (ex.: Google, Amazon)",
"views.admin.IpBlacklistSettings.fingerprint_blacklist_placeholder": "Informe o ID da impressão (ex.: a1b2c3d4e5f6g7h8)",
"views.admin.IpBlacklistSettings.daily_request_limit_placeholder": "Informe o limite (ex.: 1000)",
"views.admin.IpBlacklistSettings.ip_blacklist_placeholder": "Informe o padrão (ex.: 192.168.1 ou ^10\\.0\\.0\\.5$)",
"views.common.Login.bindUserAddressError": "Erro ao vincular o endereço ao usuário",
"views.admin.IpBlacklistSettings.ip_whitelist_placeholder": "IP exato (ex.: 1.2.3.4) ou regex ancorada (ex.: ^192\\.168\\.1\\.\\d+$)",
"views.index.SimpleIndex.exitSimpleIndex": "Sair do modo simples",
"components.MailBox.keywordQueryTip": "Filtrar página atual",
"views.admin.AccountSettings.create_address_subdomain_match_follow_env": "Seguir variável de ambiente",
"views.admin.AccountSettings.create_address_subdomain_match_force_disable": "Forçar desativação",
"views.admin.AccountSettings.create_address_subdomain_match_force_enable": "Forçar ativação",
"views.user.UserLogin.forgotPassword": "Esqueci a senha",
"components.MailBox.forwardMail": "Encaminhar",
"components.MailContentRenderer.forward": "Encaminhar",
"views.admin.AccountSettings.forward_address": "Endereço de encaminhamento",
"views.admin.AccountSettings.forward_address_required": "O endereço de encaminhamento é obrigatório",
"views.admin.AccountSettings.forward_placeholder": "forward@example.com",
"components.MailContentRenderer.fullscreen": "Tela cheia",
"views.common.Login.generateName": "Gerar nome aleatório",
"views.admin.Telegram.globalMailPushList": "Lista global de chat ID para envio de e-mails",
"views.common.Appearance.globalTabplacement": "Posição global das abas",
"views.common.Login.help": "Ajuda",
"views.Header.home": "Início",
"views.index.SendMail.html": "HTML",
"views.admin.SendMail.html": "HTML",
"views.admin.UserOauth2Settings.icon": "Ícone (SVG, use apenas fonte confiável)",
"views.admin.CreateAccount.enablePrefix": "Se ativar o prefixo",
"views.common.AdminContact.adminContact": "Se precisar de ajuda, entre em contato com o administrador ({msg})",
"views.admin.Telegram.init": "Inicializar",
"views.admin.DatabaseManager.init": "Inicializar banco de dados",
"views.admin.AccountSettings.regex_invalid": "Padrão regex inválido",
"views.Admin.ipBlacklistSettings": "Lista negra de IP",
"views.admin.IpBlacklistSettings.ip_blacklist": "Padrões da lista negra de IP",
"views.admin.IpBlacklistSettings.title": "Configurações da lista negra de IP",
"views.admin.IpBlacklistSettings.tip_ip": "Lista negra de IP: aceita correspondência por texto (ex.: \"192.168.1\") ou regex (ex.: \"^10\\.0\\.0\\.5$\").",
"views.admin.IpBlacklistSettings.whitelist_empty_warning": "A lista branca de IP está ativada, mas está vazia. O servidor irá ignorá-la para evitar bloqueio de acesso. Adicione pelo menos uma entrada antes de ativar.",
"views.admin.IpBlacklistSettings.ip_whitelist": "Lista branca de IP Padrões",
"views.admin.IpBlacklistSettings.tip_whitelist": "Lista branca de IP: modo estrito. Entradas simples devem corresponder exatamente ao IP (sem substring). Use regex ancorada (^...$) para intervalos. IPs permitidos ignoram a verificação da lista negra.",
"views.admin.SenderAccess.is_enabled": "Ativado",
"views.admin.Account.itemCount": "itemCount",
"views.admin.SenderAccess.itemCount": "itemCount",
"views.admin.UserManagement.itemCount": "itemCount",
"views.user.UserMailBox.addressQueryTip": "Deixe em branco para consultar todos os endereços",
"views.admin.Account.addressQueryTip": "Deixe em branco para consultar todos os endereços",
"views.admin.Mails.addressQueryTip": "Deixe em branco para consultar todos os endereços",
"views.common.Appearance.left": "esquerda",
"views.common.Login.getNewEmailTip2": "Deixar em branco gerará um endereço de e-mail aleatório.",
"views.Header.light": "Claro",
"views.admin.IpBlacklistSettings.enable_daily_limit_tip": "Limitar o número de requisições de API por IP por dia",
"components.AddressSelect.localAddresses": "Endereços locais",
"views.common.Login.bindUserInfo": "Usuário logado: entrar sem vincular e-mail ou criar um novo endereço vinculará ao usuário atual",
"views.user.UserOauth2Callback.logging": "Entrando...",
"views.user.UserLogin.login": "Entrar",
"views.common.Login.login": "Entrar",
"views.common.Login.loginAndBind": "Entrar e vincular",
"views.Admin.loginMethod": "Método de login",
"views.user.UserBar.fetchUserSettingsError": "A senha de login é inválida ou a conta não existe; também pode ser um problema de conexão. Tente novamente mais tarde.",
"views.user.UserLogin.loginWith": "Entrar com {provider}",
"views.user.UserLogin.loginWithPasskey": "Entrar com passkey",
"views.user.UserSettings.logout": "Sair",
"views.user.BindAddress.logout": "Sair",
"views.Admin.logout": "Sair",
"views.index.AccountSettings.logout": "Sair",
"views.Admin.logoutSuccess": "Logout realizado com sucesso",
"views.admin.UserOauth2Settings.mailAllowList": "Lista branca de endereços de e-mail",
"views.admin.UserSettings.mailAllowList": "Lista branca de endereços de e-mail",
"views.index.SimpleIndex.addressCredential": "Credencial do endereço de e-mail",
"views.index.AddressBar.addressCredential": "Credencial do endereço de e-mail",
"views.admin.Account.addressCredential": "Credencial do endereço de e-mail",
"views.admin.CreateAccount.addressCredential": "Credencial do endereço de e-mail",
"views.index.AddressBar.fetchAddressError": "A credencial do e-mail é inválida ou a conta não existe; também pode ser um problema de rede. Tente novamente mais tarde.",
"views.Index.mailbox": "Caixa de entrada",
"views.User.user_mail_box_tab": "Caixa de entrada",
"views.user.AddressManagement.mail_count": "Quantidade de e-mails",
"views.admin.Account.mail_count": "Quantidade de e-mails",
"views.admin.Statistics.mailCount": "Quantidade de e-mails",
"views.admin.UserAddressManagement.mail_count": "Quantidade de e-mails",
"views.index.SimpleIndex.deleteSuccess": "E-mail excluído com sucesso",
"views.user.UserLogin.cannotForgotPassword": "A verificação por e-mail ou o registro está desativado; não é possível redefinir a senha. Entre em contato com o administrador.",
"views.Admin.mailWebhook": "Webhook de e-mail",
"views.common.Appearance.mailboxSplitSize": "Tamanho da divisão da caixa de correio",
"views.index.SimpleIndex.refreshSuccess": "E-mails atualizados com sucesso",
"views.Admin.unknow": "E-mails com destinatário desconhecido",
"views.Admin.maintenance": "Manutenção",
"views.index.AddressBar.addressManage": "Gerenciar",
"views.admin.AccountSettings.source_match_mode": "Modo de correspondência",
"views.admin.RoleAddressConfig.maxAddressCount": "Quantidade máxima de endereços (0 = ilimitado)",
"views.admin.UserSettings.maxAddressCount": "Número máximo de endereços que podem ser vinculados (0 = ilimitado)",
"views.Header.menu": "Menu",
"views.admin.DatabaseManager.migration": "Migrar banco de dados",
"views.admin.SenderAccess.modify": "Modificar",
"views.admin.AccountSettings.send_mail_monthly_limit": "Limite mensal",
"views.admin.AccountSettings.send_mail_monthly_limit_invalid": "O limite mensal deve ser um inteiro maior ou igual a -1",
"components.MailBox.multiAction": "Ação múltipla",
"components.SendBox.multiAction": "Ação múltipla",
"views.admin.Account.multiClearInbox": "Limpar várias caixas de entrada",
"views.admin.Account.multiClearSentItems": "Limpar vários itens enviados",
"views.admin.Account.multiDelete": "Excluir em lote",
"views.user.AddressManagement.name": "Nome",
"views.index.AutoReply.name": "Nome",
"views.admin.Account.name": "Nome",
"views.admin.UserOauth2Settings.name": "Nome",
"views.admin.UserAddressManagement.name": "Nome",
"views.admin.Maintenance.sqlName": "Nome",
"views.index.AccountSettings.newPassword": "Nova senha",
"views.admin.Account.newPassword": "Nova senha",
"components.MailBox.nextMail": "Próximo",
"views.index.SimpleIndex.nextPage": "Próximo",
"views.admin.AccountSettings.noLimitSendAddressList": "Lista de endereços sem limite de saldo",
"views.index.SimpleIndex.noMails": "Nenhum e-mail encontrado",
"views.admin.RoleAddressConfig.noRolesAvailable": "Nenhuma função disponível na configuração do sistema",
"views.index.SendMail.requestAccessTip": "Ainda não há saldo de envio. Se o administrador ativou um saldo padrão, ele será atribuído automaticamente; caso contrário, solicite acesso ou fale com o administrador.",
"components.SendBox.emptySent": "Nenhum e-mail enviado",
"views.admin.RoleAddressConfig.notConfigured": "Não configurado (usar configurações globais)",
"views.Admin.userOauth2Settings": "Configurações de OAuth2",
"views.admin.UserOauth2Settings.oauth2Type": "Tipo de OAuth2",
"views.Header.ok": "OK",
"views.Admin.ok": "OK",
"views.index.AddressBar.ok": "OK",
"views.admin.SenderAccess.ok": "OK",
"views.common.Login.ok": "OK",
"views.admin.AccountSettings.create_address_subdomain_match_tip": "Afeta apenas a validação de domínio em /api/new_address e /admin/new_address. Exemplo: com isso ativado, foo.example.com pode corresponder a example.com.",
"components.AiExtractInfo.open": "Abrir",
"views.index.AddressBar.linkWithAddressCredential": "Abrir link de login automático por e-mail",
"views.admin.CreateAccount.linkWithAddressCredential": "Abrir link de login automático por e-mail",
"views.index.SendMail.options": "Opções",
"views.admin.SendMail.options": "Opções",
"components.AiExtractInfo.otherLink": "Outro link",
"views.user.UserSettings.passkeyCreated": "Passkey criada com sucesso",
"views.user.UserSettings.passkey_name": "Nome da passkey",
"views.user.UserLogin.password": "Senha",
"views.admin.UserManagement.password": "Senha",
"views.common.Login.password": "Senha",
"views.index.AccountSettings.passwordChanged": "Senha alterada com sucesso",
"views.common.Login.passwordLogin": "Login com senha",
"views.admin.Account.passwordResetSuccess": "Senha redefinida com sucesso",
"views.index.AccountSettings.passwordMismatch": "As senhas não coincidem",
"views.index.SendMail.successSend": "Verifique a caixa de saída. Se falhar, confira seu saldo ou tente novamente mais tarde.",
"views.admin.SendMail.successSend": "Verifique sua caixa de saída. Se falhar, tente novamente mais tarde.",
"views.user.UserLogin.pleaseCompleteTurnstile": "Conclua o turnstile",
"views.index.SimpleIndex.addressCredentialTip": "Copie a credencial do endereço de e-mail; você poderá usá-la para fazer login.",
"views.index.AddressBar.addressCredentialTip": "Copie a credencial do endereço de e-mail; você poderá usá-la para entrar na sua conta de e-mail.",
"views.admin.Account.addressCredentialTip": "Copie a credencial do endereço de e-mail; você poderá usá-la para entrar na sua conta de e-mail.",
"views.admin.CreateAccount.addressCredentialTip": "Copie a credencial do endereço de e-mail; você poderá usá-la para entrar na sua conta de e-mail.",
"views.Admin.accessTip": "Digite a senha de administrador",
"views.Header.accessTip": "Informe a senha de acesso correta",
"views.admin.AccountSettings.address_block_list_placeholder": "Informe as palavras-chave que deseja bloquear",
"views.user.UserSettings.renamePasskeyNamePlaceholder": "Informe o novo nome da passkey",
"views.user.UserSettings.passkeyNamePlaceholder": "Informe o nome da passkey ou deixe em branco para gerar um aleatório",
"views.admin.CreateAccount.fillInAllFields": "Preencha todos os campos",
"views.admin.SendBox.queryTip": "Informe o endereço para consulta; deixe em branco para consultar todos",
"views.user.UserLogin.pleaseInputCode": "Informe o código",
"views.admin.UserManagement.pleaseInput": "Informe todas as informações",
"views.user.UserLogin.pleaseInputEmail": "Informe o e-mail",
"views.user.UserLogin.pleaseInput": "Informe e-mail e senha",
"views.admin.Maintenance.tip": "Informe os dias",
"views.common.Login.getNewEmailTip1": "Informe o e-mail que deseja usar. Permitido apenas: ",
"views.common.Login.credentialInput": "Digite a credencial do endereço de e-mail",
"views.admin.SenderAccess.modalTip": "Informe o saldo de envio",
"views.user.UserLogin.pleaseLogin": "Faça login",
"views.common.Login.pleaseGetNewEmail": "Faça login ou clique em \"Criar novo e-mail\" para obter um novo endereço.",
"components.WebhookComponent.fillInDemoTip": "Altere a URL e as demais configurações para as suas",
"components.SendBox.pleaseSelectMail": "Selecione um e-mail para visualizar.",
"views.admin.Account.pleaseSelectAddress": "Selecione um endereço",
"components.MailBox.pleaseSelectMail": "Selecione um e-mail",
"views.admin.UserManagement.prefix": "Prefixo",
"components.WebhookComponent.presets": "Modelos",
"views.index.SendMail.preview": "Pré-visualização",
"views.admin.SendMail.preview": "Pré-visualização",
"views.admin.UserOauth2Settings.iconPreview": "Pré-visualização",
"components.MailBox.prevMail": "Anterior",
"views.index.SimpleIndex.prevPage": "Anterior",
"components.MailBox.query": "Consultar",
"views.user.UserMailBox.query": "Consultar",
"views.Index.query": "Consultar",
"views.admin.SendBox.query": "Consultar",
"views.admin.Account.query": "Consultar",
"views.admin.SenderAccess.query": "Consultar",
"views.admin.UserManagement.query": "Consultar",
"views.admin.Mails.query": "Consultar",
"views.Admin.qucickSetup": "Configuração rápida",
"views.index.SendMail.toMailEmpty": "O endereço do destinatário está vazio",
"views.admin.SendMail.toMailEmpty": "O endereço do destinatário está vazio",
"views.index.SendMail.toName": "Nome e endereço do destinatário; deixe o nome em branco para usar o e-mail",
"views.admin.SendMail.toName": "Nome e endereço do destinatário; deixe o nome em branco para usar o e-mail",
"components.MailBox.refresh": "Atualizar",
"components.Turnstile.refresh": "Atualizar",
"components.SendBox.refresh": "Atualizar",
"views.index.SimpleIndex.refreshMails": "Atualizar",
"components.MailBox.refreshAfter": "Atualizar em {msg} segundos",
"views.index.SimpleIndex.refreshAfter": "Atualizar em {msg} segundos",
"views.admin.AccountSettings.regex_too_long": "O padrão regex é muito longo (máx. 200 caracteres)",
"views.user.UserLogin.register": "Registrar",
"views.user.UserSettings.renamePasskey": "Renomear passkey",
"views.admin.UserOauth2Settings.userEmailReplace": "Modelo de substituição",
"components.MailBox.reply": "Responder",
"components.MailContentRenderer.reply": "Responder",
"views.index.SendMail.requestAccess": "Solicitar acesso",
"views.user.UserLogin.resetPassword": "Redefinir Senha",
"views.admin.Account.resetPassword": "Redefinir Senha",
"views.admin.UserManagement.resetPassword": "Redefinir Senha",
"views.index.SendMail.rich text": "Texto rico",
"views.admin.SendMail.rich text": "Texto rico",
"views.common.Appearance.right": "direita",
"views.admin.UserManagement.role": "Função",
"views.admin.RoleAddressConfig.role": "Função",
"views.Admin.roleAddressConfig": "Configuração de endereço por função",
"views.admin.AccountSettings.rule_index": "Regra",
"views.Index.s3Attachment": "Anexo S3",
"components.WebhookComponent.save": "Salvar",
"views.index.AutoReply.save": "Salvar",
"views.admin.AiExtractSettings.save": "Salvar",
"views.admin.UserOauth2Settings.save": "Salvar",
"views.admin.IpBlacklistSettings.save": "Salvar",
"views.admin.Telegram.save": "Salvar",
"views.admin.UserSettings.save": "Salvar",
"views.admin.RoleAddressConfig.save": "Salvar",
"views.admin.AccountSettings.save": "Salvar",
"views.admin.Maintenance.save": "Salvar",
"views.admin.Webhook.save": "Salvar",
"views.admin.Maintenance.saveSuccess": "Salvo com sucesso",
"views.admin.UserOauth2Settings.successTip": "Salvo com sucesso",
"views.admin.IpBlacklistSettings.successTip": "Salvo com sucesso",
"views.admin.UserSettings.successTip": "Salvo com sucesso",
"views.admin.AccountSettings.successTip": "Salvo com sucesso",
"components.MailBox.saveToS3": "Salvar no S3",
"components.MailContentRenderer.saveToS3": "Salvar no S3",
"views.Index.saveToS3Success": "salvo no S3 com sucesso",
"components.MailBox.selectAll": "Selecionar toda esta página",
"components.SendBox.selectAll": "Selecionar toda esta página",
"views.admin.Account.selectAll": "Selecionar toda esta página",
"views.admin.AccountSettings.select_domain": "Selecionar domínio",
"views.admin.Account.selectedItems": "Selecionado",
"views.index.SendMail.send": "Enviar",
"views.admin.SendMail.send": "Enviar",
"views.Index.sendbox": "Caixa de saída",
"views.Admin.sendBox": "Caixa de saída",
"views.user.AddressManagement.send_count": "Quantidade enviada",
"views.admin.Account.send_count": "Quantidade enviada",
"views.admin.UserAddressManagement.send_count": "Quantidade enviada",
"views.Index.sendmail": "Enviar e-mail",
"views.Admin.sendMail": "Enviar e-mail",
"views.index.SendMail.send_balance": "Saldo restante de envio",
"views.admin.Statistics.sendMailCount": "Quantidade de e-mails enviados",
"views.admin.AccountSettings.send_mail_limit": "Limite de envio",
"views.user.UserLogin.sendVerificationCode": "Enviar código de verificação",
"views.Admin.senderAccess": "Controle de acesso de envio",
"views.admin.SendMail.fromMailEmpty": "O endereço do remetente está vazio",
"views.index.AutoReply.sourcePrefix": "Filtro de remetente",
"components.AiExtractInfo.serviceLink": "Link do serviço",
"views.index.AutoReply.settings": "Configurações",
"views.index.AccountSettings.showAddressCredential": "Mostrar credencial do endereço",
"components.MailBox.attachments": "Mostrar anexos",
"components.MailBox.showHtmlMail": "Mostrar e-mail HTML",
"components.MailContentRenderer.showHtmlMail": "Mostrar e-mail HTML",
"views.admin.Account.showCredential": "Mostrar credencial do endereço de e-mail",
"views.user.UserSettings.showPasskeyList": "Mostrar lista de passkeys",
"components.MailBox.showTextMail": "Mostrar e-mail em texto",
"components.MailContentRenderer.showTextMail": "Mostrar e-mail em texto",
"views.Index.enterSimpleMode": "Modo simples",
"components.MailContentRenderer.size": "Tamanho",
"views.admin.Account.source_meta": "Origem",
"views.admin.AccountSettings.source_patterns": "Regex do endereço de origem (opcional)",
"views.admin.Maintenance.sqlStatement": "Comando SQL (somente DELETE)",
"views.user.UserOauth2Callback.stateNotMatch": "estado não corresponde",
"views.Admin.statistics": "Estatísticas",
"views.Header.status": "Status",
"views.admin.IpBlacklistSettings.enable_whitelist_tip": "Modo estrito: apenas IPs que corresponderem à lista branca poderão acessar APIs com limite. Todos os outros IPs serão negados.",
"views.index.AutoReply.subject": "Assunto",
"views.index.SendMail.subject": "Assunto",
"views.admin.SendMail.subject": "Assunto",
"views.index.SendMail.subjectEmpty": "O assunto está vazio",
"views.admin.SendMail.subjectEmpty": "O assunto está vazio",
"components.AiExtractInfo.subscriptionLink": "Link de assinatura",
"views.user.AddressManagement.success": "sucesso",
"views.index.TelegramAddress.success": "sucesso",
"views.index.LocalAddress.success": "sucesso",
"views.admin.UserAddressManagement.success": "sucesso",
"components.WebhookComponent.successTip": "Sucesso",
"components.MailBox.success": "Sucesso",
"components.SendBox.success": "Sucesso",
"views.index.AccountSettings.success": "Sucesso",
"views.index.AutoReply.success": "Sucesso",
"views.admin.AiExtractSettings.successTip": "Sucesso",
"views.admin.Account.success": "Sucesso",
"views.admin.SenderAccess.success": "Sucesso",
"views.admin.UserManagement.success": "Sucesso",
"views.admin.Telegram.successTip": "Sucesso",
"views.admin.RoleAddressConfig.successTip": "Sucesso",
"views.admin.Webhook.successTip": "Sucesso",
"views.admin.CreateAccount.successTip": "Criado com sucesso",
"views.admin.Telegram.globalMailPushListTip": "Suporta chat_id de conversa privada/grupo/canal. Envie uma mensagem ao bot e acesse este link para ver o chat_id: https://api.telegram.org/bot<Substitua pelo seu BOT TOKEN>/getUpdates",
"views.user.AddressManagement.targetUserEmail": "E-mail do usuário de destino",
"views.admin.Telegram.telegramAllowList": "Lista branca do Telegram (informar manualmente o Chat ID)",
"views.Admin.telegram": "Bot do Telegram",
"views.admin.Telegram.miniAppUrl": "URL do Mini App do Telegram",
"components.WebhookComponent.test": "Teste",
"views.index.SendMail.text": "Texto",
"views.admin.SendMail.text": "Texto",
"views.user.UserSettings.passordTip": "O servidor recebe apenas o hash da senha, nunca a senha em texto puro, portanto não pode vê-la nem recuperá-la. Se o administrador ativar a verificação por e-mail, você poderá redefini-la no modo anônimo.",
"views.index.LocalAddress.tip": "Esses endereços ficam armazenados no navegador e podem ser perdidos se você limpar o cache.",
"views.admin.UserOauth2Settings.tip": "O login de terceiros usará automaticamente o e-mail do usuário para registrar uma conta (o mesmo e-mail será considerado a mesma conta). Você também pode definir a senha via “esqueci minha senha”.",
"views.admin.AccountSettings.send_mail_limit_tip": "Aplica-se a todos os canais de envio. Use -1 para ilimitado e 0 para bloquear o envio.",
"views.admin.AccountSettings.create_address_subdomain_match_note": "Isso é diferente de RANDOM_SUBDOMAIN_DOMAINS: esta opção permite informar subdomínios personalizados; o subdomínio aleatório apenas gera um durante a criação.",
"views.index.SendMail.tooLarge": "Arquivo muito grande; envie um arquivo menor que 1 MB.",
"views.admin.SendMail.tooLarge": "Arquivo muito grande; envie um arquivo menor que 1 MB.",
"views.common.Appearance.top": "topo",
"views.user.AddressManagement.transferAddress": "Transferir endereço",
"views.user.AddressManagement.transferAddressTip": "Transferir este endereço para outro usuário irá removê-lo da sua conta e movê-lo para a conta dele. Tem certeza de que deseja transferi-lo?",
"views.common.Appearance.useSideMargin": "Ativar margens laterais à esquerda e à direita da página",
"views.admin.AiExtractSettings.manualInputPrompt": "Digite e pressione Enter para adicionar",
"views.admin.UserOauth2Settings.manualInputPrompt": "Digite e pressione Enter para adicionar",
"views.admin.Telegram.manualInputPrompt": "Digite e pressione Enter para adicionar",
"views.admin.UserSettings.manualInputPrompt": "Digite e pressione Enter para adicionar",
"views.admin.AccountSettings.manualInputPrompt": "Digite e pressione Enter para adicionar",
"views.admin.Webhook.manualInputPrompt": "Digite e pressione Enter para adicionar",
"views.admin.IpBlacklistSettings.manualInputPrompt": "Digite o padrão e pressione Enter para adicionar",
"views.user.AddressManagement.unbindAddress": "Desvincular endereço",
"views.index.TelegramAddress.unbindMailAddress": "Desvincular endereço de e-mail",
"views.index.LocalAddress.unbindMailAddress": "Desvincular credencial do endereço de e-mail",
"components.MailBox.unselectAll": "Desmarcar tudo",
"components.SendBox.unselectAll": "Desmarcar tudo",
"views.admin.Account.unselectAll": "Desmarcar tudo",
"views.admin.Account.updated_at": "Atualizado em",
"views.user.UserSettings.updated_at": "Atualizado em",
"components.WebhookComponent.urlMissing": "URL é obrigatória",
"views.common.Appearance.useIframeShowMail": "Usar iframe para mostrar e-mail HTML",
"views.admin.CreateAccount.enableRandomSubdomain": "Usar subdomínio aleatório",
"views.common.Login.enableRandomSubdomain": "Usar subdomínio aleatório",
"views.admin.UserOauth2Settings.userEmailFormatTip": "Use regex para transformar o e-mail. Exemplo: ^(.+)@old\\.com$ com $1@new.com",
"views.common.Appearance.useSimpleIndex": "Usar índice simples",
"views.common.Appearance.useUTCDate": "Usar data UTC",
"views.Header.user": "Usuário",
"views.Admin.user": "Usuário",
"components.AddressSelect.userAddresses": "Endereços do usuário",
"views.Admin.loginViaUserAdmin": "Permissão de administrador do usuário",
"views.admin.Statistics.userCount": "Quantidade de usuários",
"views.admin.UserManagement.user_email": "E-mail do usuário",
"views.index.AddressBar.userLogin": "Login do usuário",
"views.Admin.user_management": "Gerenciamento de usuários",
"views.Admin.user_settings": "Configurações do usuário",
"views.User.user_settings": "Configurações do usuário",
"components.AiExtractInfo.authCode": "Código de verificação",
"views.user.UserLogin.verifyCode": "Código de verificação",
"views.user.UserLogin.verifyCodeSent": "Código de verificação enviado, expira em {timeout} segundos",
"views.admin.AccountSettings.verified_address_list": "Lista de endereços verificados (pode enviar e-mail pela API interna do cf)",
"views.admin.UserSettings.verifyMailSender": "Verificar remetente do e-mail",
"components.MailContentRenderer.attachments": "Ver anexos",
"views.admin.Account.viewMails": "Ver e-mails",
"views.admin.Account.viewSendBox": "Ver caixa de saída",
"views.user.UserLogin.waitforVerifyCode": "Aguarde {timeout} segundos",
"views.admin.Webhook.webhookAllowList": "Lista de permissão do webhook (digite o e-mail autorizado e pressione Enter)",
"views.admin.Webhook.notEnabled": "O webhook não está habilitado",
"components.WebhookComponent.notEnabled": "O webhook não está habilitado para você",
"views.Index.webhookSettings": "Configurações de webhook",
"views.Admin.webhookSettings": "Configurações de webhook",
"views.admin.AiExtractSettings.disabledTip": "Quando desativado, a extração por IA processará todos os endereços",
"views.admin.AiExtractSettings.enableAllowListTip": "Quando ativado, a extração por IA só processará e-mails enviados aos endereços permitidos",
"views.admin.CreateAccount.randomSubdomainTip": "Quando ativado, o endereço criado usará um subdomínio aleatório. Endereços com subdomínio são recomendados apenas para recebimento.",
"views.common.Login.randomSubdomainTip": "Quando ativado, o endereço criado usará um subdomínio aleatório. Endereços com subdomínio são recomendados apenas para recebimento.",
"views.admin.AiExtractSettings.allowListTip": "O curinga * corresponde a quaisquer caracteres; ex.: *{'@'}example.com corresponde a todos os endereços do domínio example.com",
"views.Admin.workerconfig": "Configuração do Worker",
"views.admin.AccountSettings.create_address_subdomain_match_env_locked": "A variável ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH está em false. O botão salvo pode ser alterado, mas só terá efeito quando a variável for ativada ou removida.",
"views.common.Login.getNewEmailTip3": "Você pode escolher um domínio na lista suspensa.",
"views.admin.AccountSettings.tip": "Você pode inserir manualmente os seguintes valores e pressionar Enter para adicioná-los",
"components.MailBox.emptyInbox": "Sua caixa de entrada está vazia",
"views.index.SendMail.fromName": "Seu nome e endereço; deixe o nome em branco para usar o e-mail",
"views.admin.SendMail.fromName": "Seu nome e endereço; deixe o nome em branco para usar o e-mail"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
import { MESSAGE_REGISTRY, getMessageSource } from './message-registry'
import type { MessageKey, MessageNamespace } from './message-registry'
import { deMessages } from './locales/source/de'
import { esMessages } from './locales/source/es'
import { jaMessages } from './locales/source/ja'
import { ptBRMessages } from './locales/source/ptBR'
import type { SupportedLocale } from './locale-registry'
type LocaleTree = Record<string, unknown>
type SourceLocale = Extract<SupportedLocale, 'en' | 'zh'>
type AdditionalLocale = Exclude<SupportedLocale, SourceLocale>
const additionalLocaleSources: Record<AdditionalLocale, Record<string, string>> = {
es: esMessages,
'pt-BR': ptBRMessages,
ja: jaMessages,
de: deMessages,
}
const setNestedValue = (target: LocaleTree, path: string, value: unknown) => {
const segments = path.split('.')
let current: LocaleTree = target
for (const segment of segments.slice(0, -1)) {
const existing = current[segment]
if (typeof existing === 'object' && existing !== null && !Array.isArray(existing)) {
current = existing as LocaleTree
continue
}
current[segment] = {}
current = current[segment] as LocaleTree
}
current[segments.at(-1) as string] = value
}
const buildSourceLocaleMessages = (locale: SourceLocale) => {
const messages: LocaleTree = {}
for (const namespace of Object.keys(MESSAGE_REGISTRY) as MessageNamespace[]) {
const keys = Object.keys(MESSAGE_REGISTRY[namespace]) as MessageKey<typeof namespace>[]
for (const key of keys) {
const message = getMessageSource(namespace, key, locale)
if (message === undefined) continue
setNestedValue(messages, `${namespace}.${key}`, message)
}
}
return messages
}
const buildAdditionalLocaleMessages = (locale: AdditionalLocale) => {
const messages: LocaleTree = {}
for (const [key, value] of Object.entries(additionalLocaleSources[locale])) {
setNestedValue(messages, key, value)
}
return messages
}
export const I18N_MESSAGES: Record<SupportedLocale, LocaleTree> = {
zh: buildSourceLocaleMessages('zh'),
en: buildSourceLocaleMessages('en'),
es: buildAdditionalLocaleMessages('es'),
'pt-BR': buildAdditionalLocaleMessages('pt-BR'),
ja: buildAdditionalLocaleMessages('ja'),
de: buildAdditionalLocaleMessages('de'),
}
export const getLocalizedMessage = (
locale: AdditionalLocale,
namespace: MessageNamespace,
key: string,
) => {
return additionalLocaleSources[locale][`${namespace}.${key}`]
}

View File

@@ -0,0 +1,13 @@
import type { NDateLocale, NLocale } from 'naive-ui'
import { getNaiveLocaleConfig as getRegistryNaiveLocaleConfig } from './locale-registry'
import type { SupportedLocale } from './locale-registry'
export type NaiveLocaleConfig = {
locale: NLocale
dateLocale: NDateLocale
}
export const getNaiveLocaleConfig = (locale: SupportedLocale): NaiveLocaleConfig => {
return getRegistryNaiveLocaleConfig(locale)
}

138
frontend/src/i18n/utils.ts Normal file
View File

@@ -0,0 +1,138 @@
import { LOCALE_REGISTRY, SUPPORTED_LOCALES } from './locale-registry'
export { SUPPORTED_LOCALES } from './locale-registry'
export type { SupportedLocale } from './locale-registry'
import type { SupportedLocale } from './locale-registry'
export const DEFAULT_LOCALE: SupportedLocale = 'zh'
export const FALLBACK_LOCALE: SupportedLocale = 'zh'
export const PREFERRED_LOCALE_STORAGE_KEY = 'preferredLocale'
export const EMPTY_LOCALE_MESSAGES = Object.fromEntries(
SUPPORTED_LOCALES.map((supportedLocale) => [supportedLocale, {}]),
) as Record<SupportedLocale, Record<string, never>>
export const isSupportedLocale = (locale: unknown): locale is SupportedLocale => {
return typeof locale === 'string' && SUPPORTED_LOCALES.includes(locale as SupportedLocale)
}
export const resolveSupportedLocale = (locale: string | null | undefined): SupportedLocale | null => {
if (!locale) return null
const normalizedLocale = locale.trim().toLowerCase()
for (const supportedLocale of SUPPORTED_LOCALES) {
if (supportedLocale.toLowerCase() === normalizedLocale) {
return supportedLocale
}
}
return null
}
export const matchSupportedLocale = (locale: string | null | undefined): SupportedLocale | null => {
if (!locale) return null
const normalizedLocale = locale.trim().toLowerCase()
for (const entry of LOCALE_REGISTRY) {
if (entry.browserMatches.some((prefix) => normalizedLocale === prefix || normalizedLocale.startsWith(`${prefix}-`))) {
return entry.locale
}
}
return null
}
export const getBrowserLocales = (): string[] => {
if (typeof navigator === 'undefined') return []
const locales = Array.isArray(navigator.languages) && navigator.languages.length > 0
? navigator.languages
: [navigator.language]
return locales.filter(Boolean)
}
export const getStoredLocale = (): SupportedLocale | '' => {
if (typeof window === 'undefined') return ''
const locale = window.localStorage.getItem(PREFERRED_LOCALE_STORAGE_KEY)
return isSupportedLocale(locale) ? locale : ''
}
export const getPreferredLocale = (
storedLocale: string | null | undefined,
browserLocales: string[] = [],
): SupportedLocale => {
if (isSupportedLocale(storedLocale)) return storedLocale
for (const browserLocale of browserLocales) {
const matchedLocale = matchSupportedLocale(browserLocale)
if (matchedLocale) return matchedLocale
}
return FALLBACK_LOCALE
}
export const getInitialLocale = () => DEFAULT_LOCALE
const splitPathSuffix = (fullPath: string) => {
const match = fullPath.match(/^([^?#]*)(.*)$/)
return {
path: match?.[1] || '/',
suffix: match?.[2] || '',
}
}
export const stripLocaleFromPath = (path: string): string => {
if (!path || path === '/') return '/'
const pathLocale = resolveSupportedLocale(path.split('/')[1])
if (!pathLocale) {
return path
}
const localePrefix = `/${path.split('/')[1]}`
if (path === localePrefix || path === `${localePrefix}/`) {
return '/'
}
if (path.startsWith(`${localePrefix}/`)) {
return path.slice(localePrefix.length) || '/'
}
return path
}
export const getPathWithLocale = (path: string, locale: SupportedLocale): string => {
const normalizedPath = path.startsWith('/') ? path : `/${path}`
const basePath = stripLocaleFromPath(normalizedPath)
if (locale === DEFAULT_LOCALE) {
return basePath
}
if (basePath === '/') {
return `/${locale}/`
}
return `/${locale}${basePath}`
}
export const replaceLocaleInFullPath = (fullPath: string, locale: SupportedLocale): string => {
const { path, suffix } = splitPathSuffix(fullPath)
return `${getPathWithLocale(path, locale)}${suffix}`
}
const getLocaleAliasPath = (path: string, locale: SupportedLocale): string => {
const normalizedPath = path.startsWith('/') ? path : `/${path}`
const basePath = stripLocaleFromPath(normalizedPath)
if (locale === DEFAULT_LOCALE) {
if (basePath === '/') {
return `/${locale}/`
}
return `/${locale}${basePath}`
}
return getPathWithLocale(basePath, locale)
}

View File

@@ -4,35 +4,42 @@ import User from '../views/User.vue'
import UserOauth2Callback from '../views/user/UserOauth2Callback.vue'
import i18n from '../i18n'
import { useGlobalState } from '../store'
import {
DEFAULT_LOCALE,
getBrowserLocales,
getPreferredLocale,
replaceLocaleInFullPath,
resolveSupportedLocale,
} from '../i18n/utils'
const { jwt } = useGlobalState()
const { jwt, preferredLocale } = useGlobalState()
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
alias: "/:lang/",
alias: '/:lang/',
component: Index
},
{
path: '/user',
alias: "/:lang/user",
alias: '/:lang/user',
component: User
},
{
path: '/user/oauth2/callback',
alias: "/:lang/user/oauth2/callback",
alias: '/:lang/user/oauth2/callback',
component: UserOauth2Callback
},
{
path: '/admin',
alias: "/:lang/admin",
alias: '/:lang/admin',
component: () => import('../views/Admin.vue')
},
{
path: '/telegram_mail',
alias: "/:lang/telegram_mail",
alias: '/:lang/telegram_mail',
component: () => import('../views/telegram/Mail.vue')
},
{
@@ -43,17 +50,32 @@ const router = createRouter({
]
});
router.beforeEach((to, from, next) => {
if (to.params.lang && ['en', 'zh'].includes(to.params.lang)) {
i18n.global.locale.value = to.params.lang
} else {
i18n.global.locale.value = 'zh'
const routeLocale = resolveSupportedLocale(to.path.split('/')[1])
const resolvedLocale = routeLocale || DEFAULT_LOCALE
i18n.global.locale.value = resolvedLocale
if (routeLocale) {
preferredLocale.value = routeLocale
} else if (!preferredLocale.value) {
preferredLocale.value = getPreferredLocale('', getBrowserLocales())
}
// check if query parameter has jwt, set it to store
if (to.query.jwt) {
jwt.value = to.query.jwt;
jwt.value = to.query.jwt
}
if (routeLocale) {
const canonicalRoutePath = replaceLocaleInFullPath(to.fullPath, routeLocale)
if (canonicalRoutePath !== to.fullPath) {
return next(canonicalRoutePath)
}
}
if (routeLocale === DEFAULT_LOCALE) {
return next(replaceLocaleInFullPath(to.fullPath, DEFAULT_LOCALE))
}
next()
});

View File

@@ -77,6 +77,7 @@ export const useGlobalState = createGlobalState(
const useIframeShowMail = useStorage('useIframeShowMail', false);
const preferShowTextMail = useStorage('preferShowTextMail', false);
const userJwt = useStorage('userJwt', '');
const preferredLocale = useStorage('preferredLocale', '');
const userTab = useSessionStorage('userTab', 'address_management');
const indexTab = useSessionStorage('indexTab', 'mailbox');
const globalTabplacement = useStorage('globalTabplacement', 'top');
@@ -148,6 +149,7 @@ export const useGlobalState = createGlobalState(
useIframeShowMail,
preferShowTextMail,
userJwt,
preferredLocale,
userTab,
indexTab,
userOpenSettings,

View File

@@ -1,265 +0,0 @@
// @vitest-environment jsdom
import { describe, it, expect } from 'vitest'
import { buildReplyModel, buildForwardModel } from '../mail-actions'
describe('buildReplyModel', () => {
it('uses HTML content in blockquote when message is present', () => {
const mail = {
source: 'Alice <alice@example.com>',
originalSource: 'alice@example.com',
subject: 'Hello',
message: '<p>HTML body</p>',
text: 'Plain body',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).toBe(
'<p><br></p><blockquote><p>HTML body</p></blockquote><p><br></p>'
)
expect(result.contentType).toBe('html')
})
it('falls back to plain text when message is empty string', () => {
const mail = {
source: 'bob@example.com',
originalSource: 'bob@example.com',
subject: 'Hi',
message: '',
text: 'Plain text fallback',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).toBe(
'<p><br></p><blockquote>Plain text fallback</blockquote><p><br></p>'
)
expect(result.contentType).toBe('rich')
})
it('falls back to plain text when message is null', () => {
const mail = {
source: 'carol@example.com',
originalSource: 'carol@example.com',
subject: 'Test',
message: null,
text: 'Fallback text',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).toBe(
'<p><br></p><blockquote>Fallback text</blockquote><p><br></p>'
)
})
it('returns empty content when both message and text are empty', () => {
const mail = {
source: 'dave@example.com',
originalSource: 'dave@example.com',
subject: 'Empty',
message: '',
text: '',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).toBe('')
})
it('returns empty content when both message and text are null', () => {
const mail = {
source: 'eve@example.com',
originalSource: 'eve@example.com',
subject: 'Null',
message: null,
text: null,
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).toBe('')
})
it('parses "Name <email>" format for sender', () => {
const mail = {
source: 'Alice Smith <alice@example.com>',
originalSource: 'alice@example.com',
subject: 'Test',
message: '<p>body</p>',
text: '',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.toName).toBe('Alice Smith')
expect(result.toMail).toBe('alice@example.com')
})
it('uses originalSource as toMail when source is plain email', () => {
const mail = {
source: 'plain@example.com',
originalSource: 'plain@example.com',
subject: 'Test',
message: '',
text: 'body',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.toName).toBe('')
expect(result.toMail).toBe('plain@example.com')
})
it('defaults toMail to empty string when originalSource is null', () => {
const mail = {
source: 'plain@example.com',
originalSource: null,
subject: 'Test',
message: '',
text: 'body',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.toMail).toBe('')
})
it('formats subject with reply label', () => {
const mail = {
source: 'test@example.com',
originalSource: 'test@example.com',
subject: 'Original Subject',
message: '',
text: '',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.subject).toBe('Reply: Original Subject')
})
it('uses html contentType for HTML email reply', () => {
const mail = {
source: 'test@example.com',
originalSource: 'test@example.com',
subject: 'Test',
message: '<p>html</p>',
text: 'plain',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.contentType).toBe('html')
})
it('uses rich contentType for plain text email reply', () => {
const mail = {
source: 'test@example.com',
originalSource: 'test@example.com',
subject: 'Test',
message: '',
text: 'plain',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.contentType).toBe('rich')
})
it('strips script tags from HTML reply content (XSS)', () => {
const mail = {
source: 'attacker@example.com',
originalSource: 'attacker@example.com',
subject: 'XSS',
message: '<p>Hello</p><script>alert("xss")</script><p>World</p>',
text: '',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).not.toContain('<script>')
expect(result.content).toContain('<p>Hello</p>')
expect(result.content).toContain('<p>World</p>')
})
it('strips event handlers from HTML reply content (XSS)', () => {
const mail = {
source: 'attacker@example.com',
originalSource: 'attacker@example.com',
subject: 'XSS',
message: '<img src=x onerror="alert(1)"><p>Text</p>',
text: '',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).not.toContain('onerror')
expect(result.content).toContain('<p>Text</p>')
})
it('escapes HTML chars in plain text reply content', () => {
const mail = {
source: 'user@example.com',
originalSource: 'user@example.com',
subject: 'Test',
message: '',
text: 'a < b & c > d',
}
const result = buildReplyModel(mail, 'Reply')
expect(result.content).toContain('a &lt; b &amp; c &gt; d')
expect(result.content).not.toContain('a < b')
})
})
describe('buildForwardModel', () => {
it('uses html contentType when message is present', () => {
const mail = {
subject: 'FW Test',
message: '<p>HTML content</p>',
text: 'Plain content',
}
const result = buildForwardModel(mail, 'Forward')
expect(result.contentType).toBe('html')
expect(result.content).toBe('<p>HTML content</p>')
})
it('uses text contentType when message is empty', () => {
const mail = {
subject: 'FW Test',
message: '',
text: 'Plain text only',
}
const result = buildForwardModel(mail, 'Forward')
expect(result.contentType).toBe('text')
expect(result.content).toBe('Plain text only')
})
it('uses text contentType when message is null', () => {
const mail = {
subject: 'FW Test',
message: null,
text: 'Fallback text',
}
const result = buildForwardModel(mail, 'Forward')
expect(result.contentType).toBe('text')
expect(result.content).toBe('Fallback text')
})
it('formats subject with forward label', () => {
const mail = {
subject: 'Original',
message: '',
text: '',
}
const result = buildForwardModel(mail, 'Forward')
expect(result.subject).toBe('Forward: Original')
})
it('strips script tags from HTML forward content (XSS)', () => {
const mail = {
subject: 'XSS Test',
message: '<div>Safe</div><script>alert("xss")</script>',
text: '',
}
const result = buildForwardModel(mail, 'Forward')
expect(result.content).not.toContain('<script>')
expect(result.content).toContain('<div>Safe</div>')
})
it('strips event handlers from HTML forward content (XSS)', () => {
const mail = {
subject: 'XSS Test',
message: '<img src=x onerror="alert(1)"><b>Bold</b>',
text: '',
}
const result = buildForwardModel(mail, 'Forward')
expect(result.content).not.toContain('onerror')
expect(result.content).toContain('<b>Bold</b>')
})
it('escapes special chars in plain text forward content', () => {
const mail = {
subject: 'FW Text',
message: '',
text: 'a < b & c > d',
}
const result = buildForwardModel(mail, 'Forward')
expect(result.contentType).toBe('text')
expect(result.content).toBe('a &lt; b &amp; c &gt; d')
})
})

View File

@@ -1,3 +1,5 @@
import { getPathWithLocale } from '../i18n/utils'
export const hashPassword = async (password: string) => {
// user crypto to hash password
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(password));
@@ -6,10 +8,15 @@ export const hashPassword = async (password: string) => {
}
export const getRouterPathWithLang = (path: string, lang: string) => {
if (!lang || lang === 'zh') {
return path;
}
return `/${lang}${path}`;
const normalizedLang = lang === 'en'
|| lang === 'es'
|| lang === 'pt-BR'
|| lang === 'ja'
|| lang === 'de'
? lang
: 'zh';
return getPathWithLocale(path, normalizedLang);
}
export const utcToLocalDate = (utcDate: string, useUTCDate: boolean) => {

View File

@@ -1,6 +1,6 @@
<script setup>
import { computed, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router'
import { useGlobalState } from '../store'
@@ -78,90 +78,7 @@ const handleLogout = async () => {
await router.push(getRouterPathWithLang('/', locale.value));
}
const { t, locale } = useI18n({
messages: {
en: {
accessHeader: 'Admin Password',
accessTip: 'Please enter the admin password',
mails: 'Emails',
sendMail: 'Send Mail',
qucickSetup: 'Quick Setup',
account: 'Account',
account_create: 'Create Account',
account_settings: 'Account Settings',
user: 'User',
user_management: 'User Management',
user_settings: 'User Settings',
userOauth2Settings: 'Oauth2 Settings',
roleAddressConfig: 'Role Address Config',
unknow: 'Mails with unknow receiver',
senderAccess: 'Sender Access Control',
sendBox: 'Send Box',
telegram: 'Telegram Bot',
webhookSettings: 'Webhook Settings',
statistics: 'Statistics',
maintenance: 'Maintenance',
database: 'Database',
workerconfig: 'Worker Config',
ipBlacklistSettings: 'IP Blacklist',
aiExtractSettings: 'AI Extract Settings',
appearance: 'Appearance',
about: 'About',
ok: 'OK',
mailWebhook: 'Mail Webhook',
adminAccount: 'Admin',
loginMethod: 'Login Method',
loginViaPassword: 'Admin Password Login',
loginViaUserAdmin: 'User Admin Permission',
loginViaDisabledCheck: 'Disabled Password Check',
logout: 'Logout',
logoutConfirmTitle: 'Confirm Logout',
logoutConfirmContent: 'Are you sure you want to logout from admin panel?',
confirm: 'Confirm',
logoutSuccess: 'Logout successful',
},
zh: {
accessHeader: 'Admin 密码',
accessTip: '请输入 Admin 密码',
mails: '邮件',
sendMail: '发送邮件',
qucickSetup: '快速设置',
account: '账号',
account_create: '创建账号',
account_settings: '账号设置',
user: '用户',
user_management: '用户管理',
user_settings: '用户设置',
userOauth2Settings: 'Oauth2 设置',
roleAddressConfig: '角色地址配置',
unknow: '无收件人邮件',
senderAccess: '发件权限控制',
sendBox: '发件箱',
telegram: '电报机器人',
webhookSettings: 'Webhook 设置',
statistics: '统计',
maintenance: '维护',
database: '数据库',
workerconfig: 'Worker 配置',
ipBlacklistSettings: 'IP 黑名单',
aiExtractSettings: 'AI 提取设置',
appearance: '外观',
about: '关于',
ok: '确定',
mailWebhook: '邮件 Webhook',
adminAccount: '管理员',
loginMethod: '登录方式',
loginViaPassword: 'Admin 密码登录',
loginViaUserAdmin: '用户管理员权限',
loginViaDisabledCheck: '已禁用密码检查',
logout: '退出登录',
logoutConfirmTitle: '确认退出',
logoutConfirmContent: '确定要退出管理员面板吗?',
confirm: '确认',
logoutSuccess: '退出成功',
}
}
});
const { t, locale } = useScopedI18n('views.Admin')
const showAdminPasswordModal = computed(() => !showAdminPage.value || showAdminAuth.value)
const tmpAdminAuth = ref('')

View File

@@ -1,19 +1,10 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../store'
const { openSettings } = useGlobalState()
const { t } = useI18n({
messages: {
en: {
copyright: "Copyright"
},
zh: {
copyright: "版权所有"
}
}
});
const { t } = useScopedI18n('views.Footer')
</script>

View File

@@ -1,26 +1,30 @@
<script setup>
import { ref, h, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useHead } from '@unhead/vue'
import { useRoute, useRouter, RouterLink } from 'vue-router'
import { useIsMobile } from '../utils/composables'
import {
DarkModeFilled, LightModeFilled, MenuFilled,
AdminPanelSettingsFilled, MonitorHeartFilled
AdminPanelSettingsFilled, MonitorHeartFilled,
KeyboardArrowDownOutlined
} from '@vicons/material'
import { GithubAlt, Language, User, Home } from '@vicons/fa'
import { useGlobalState } from '../store'
import { api } from '../api'
import { getRouterPathWithLang, hashPassword } from '../utils'
import { DEFAULT_LOCALE, isSupportedLocale, replaceLocaleInFullPath } from '../i18n/utils'
import { getLocaleLabel, SUPPORTED_LOCALES } from '../i18n/locale-registry'
import Turnstile from '../components/Turnstile.vue'
import { NButton, NIcon } from 'naive-ui'
const message = useMessage()
const notification = useNotification()
const {
toggleDark, isDark, isTelegram, showAdminPage,
showAuth, auth, loading, openSettings, userSettings
showAuth, auth, loading, openSettings, preferredLocale, userSettings
} = useGlobalState()
const route = useRoute()
const router = useRouter()
@@ -52,43 +56,52 @@ const authFunc = async () => {
}
}
const changeLocale = async (lang) => {
if (lang == 'zh') {
await router.push(route.fullPath.replace('/en', ''));
} else {
await router.push(`/${lang}${route.fullPath}`);
}
}
const languageOptions = SUPPORTED_LOCALES.map((locale) => ({
label: getLocaleLabel(locale),
value: locale,
key: locale,
}))
const { locale, t } = useI18n({
messages: {
en: {
title: 'Cloudflare Temp Email',
dark: 'Dark',
light: 'Light',
accessHeader: 'Access Password',
accessTip: 'Please enter the correct access password',
home: 'Home',
menu: 'Menu',
user: 'User',
status: 'Status',
ok: 'OK',
},
zh: {
title: 'Cloudflare 临时邮件',
dark: '暗色',
light: '亮色',
accessHeader: '访问密码',
accessTip: '请输入站点访问密码',
home: '主页',
menu: '菜单',
user: '用户',
status: '状态',
ok: '确定',
}
}
const currentLocaleLabel = computed(() => {
return languageOptions.find(opt => opt.value === locale.value)?.label || locale.value;
});
const { t, locale } = useScopedI18n('views.Header')
const changeLocale = async (lang) => {
if (!isSupportedLocale(lang)) {
return;
}
const currentFullPath = route.fullPath;
const targetFullPath = replaceLocaleInFullPath(currentFullPath, lang);
if (lang === locale.value && targetFullPath === currentFullPath) {
showMobileMenu.value = false;
return;
}
if (lang === DEFAULT_LOCALE) {
preferredLocale.value = DEFAULT_LOCALE;
}
let localeSwitched = false;
try {
await router.push({ path: targetFullPath, force: true });
localeSwitched = router.currentRoute.value.fullPath === targetFullPath;
if (!localeSwitched) {
await router.replace({ path: targetFullPath, force: true });
localeSwitched = router.currentRoute.value.fullPath === targetFullPath;
}
} catch (error) {
console.error('Failed to switch locale', error);
} finally {
showMobileMenu.value = false;
}
if (localeSwitched) preferredLocale.value = lang;
}
const version = import.meta.env.PACKAGE_VERSION ? `v${import.meta.env.PACKAGE_VERSION}` : "";
const menuOptions = computed(() => [
@@ -172,27 +185,6 @@ const menuOptions = computed(() => [
),
key: "theme"
},
{
label: () => h(
NButton,
{
text: true,
size: "small",
style: "width: 100%",
onClick: async () => {
locale.value == 'zh' ? await changeLocale('en') : await changeLocale('zh');
showMobileMenu.value = false;
}
},
{
default: () => locale.value == 'zh' ? "English" : "中文",
icon: () => h(
NIcon, { component: Language }
)
}
),
key: "lang"
},
{
label: () => h(
NButton,
@@ -211,25 +203,6 @@ const menuOptions = computed(() => [
),
show: !!openSettings.value?.statusUrl,
key: "status"
},
{
label: () => h(
NButton,
{
text: true,
size: "small",
style: "width: 100%",
tag: "a",
target: "_blank",
href: "https://github.com/dreamhunter2333/cloudflare_temp_email",
},
{
default: () => version || "Github",
icon: () => h(NIcon, { component: GithubAlt })
}
),
show: openSettings.value?.showGithub,
key: "github"
}
]);
@@ -279,7 +252,7 @@ onMounted(async () => {
</div>
</template>
<template #extra>
<n-space>
<n-space align="center" class="header-extra">
<n-menu v-if="!isMobile" mode="horizontal" :options="menuOptions" responsive />
<n-button v-else :text="true" @click="showMobileMenu = !showMobileMenu" style="margin-right: 10px;">
<template #icon>
@@ -287,12 +260,58 @@ onMounted(async () => {
</template>
{{ t('menu') }}
</n-button>
<n-dropdown :options="languageOptions" @select="changeLocale" trigger="click" class="header-locale-dropdown">
<n-button text size="small" class="header-locale-button" style="padding: 0 10px;">
<template #icon>
<n-icon :component="Language" />
</template>
{{ currentLocaleLabel }}
<n-icon :component="KeyboardArrowDownOutlined" style="margin-left: 4px;" />
</n-button>
</n-dropdown>
<n-button
v-if="openSettings.showGithub"
text
size="small"
class="header-version-button"
tag="a"
target="_blank"
href="https://github.com/dreamhunter2333/cloudflare_temp_email"
>
<template #icon>
<n-icon :component="GithubAlt" />
</template>
{{ version || 'Github' }}
</n-button>
</n-space>
</template>
</n-page-header>
<n-drawer v-model:show="showMobileMenu" placement="top" style="height: 100vh;">
<n-drawer-content :title="t('menu')" closable>
<n-menu :options="menuOptions" />
<n-dropdown :options="languageOptions" @select="changeLocale" trigger="click" class="header-locale-dropdown">
<n-button text class="header-locale-button" style="margin-top: 12px;">
<template #icon>
<n-icon :component="Language" />
</template>
{{ currentLocaleLabel }}
<n-icon :component="KeyboardArrowDownOutlined" style="margin-left: 4px;" />
</n-button>
</n-dropdown>
<n-button
v-if="openSettings.showGithub"
text
class="header-version-button"
style="margin-top: 12px;"
tag="a"
target="_blank"
href="https://github.com/dreamhunter2333/cloudflare_temp_email"
>
<template #icon>
<n-icon :component="GithubAlt" />
</template>
{{ version || 'Github' }}
</n-button>
</n-drawer-content>
</n-drawer>
<n-modal v-model:show="showAuth" :closable="false" :closeOnEsc="false" :maskClosable="false" preset="dialog"
@@ -316,6 +335,40 @@ onMounted(async () => {
justify-content: space-between;
}
.header-extra {
align-items: center;
}
.header-extra :deep(.n-space-item) {
display: flex;
align-items: center;
}
.header-locale-button {
display: inline-flex;
align-items: center;
}
.header-locale-button :deep(.n-button__content) {
display: inline-flex;
align-items: center;
}
.header-locale-button :deep(.n-icon) {
display: inline-flex;
align-items: center;
}
.header-version-button {
display: inline-flex;
align-items: center;
}
.header-version-button :deep(.n-button__content) {
display: inline-flex;
align-items: center;
}
.n-alert {
margin-top: 10px;
margin-bottom: 10px;

View File

@@ -1,6 +1,6 @@
<script setup>
import { defineAsyncComponent, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRoute } from 'vue-router'
import { useGlobalState } from '../store'
@@ -30,38 +30,7 @@ const SendMail = defineAsyncComponent(() => {
.finally(() => loading.value = false);
});
const { t } = useI18n({
messages: {
en: {
mailbox: 'Mail Box',
sendbox: 'Send Box',
sendmail: 'Send Mail',
auto_reply: 'Auto Reply',
accountSettings: 'Account Settings',
appearance: 'Appearance',
about: 'About',
s3Attachment: 'S3 Attachment',
saveToS3Success: 'save to s3 success',
webhookSettings: 'Webhook Settings',
query: 'Query',
enterSimpleMode: 'Simple Mode',
},
zh: {
mailbox: '收件箱',
sendbox: '发件箱',
sendmail: '发送邮件',
auto_reply: '自动回复',
accountSettings: '账户',
appearance: '外观',
about: '关于',
s3Attachment: 'S3附件',
saveToS3Success: '保存到s3成功',
webhookSettings: 'Webhook 设置',
query: '查询',
enterSimpleMode: '极简模式',
}
}
});
const { t } = useScopedI18n('views.Index')
const fetchMailData = async (limit, offset) => {
if (mailIdQuery.value > 0) {

View File

@@ -1,5 +1,5 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../store'
@@ -13,22 +13,7 @@ const {
userTab, globalTabplacement, userSettings
} = useGlobalState()
const { t } = useI18n({
messages: {
en: {
address_management: 'Address Management',
user_mail_box_tab: 'Mail Box',
user_settings: 'User Settings',
bind_address: 'Bind Mail Address',
},
zh: {
address_management: '地址管理',
user_mail_box_tab: '收件箱',
user_settings: '用户设置',
bind_address: '绑定邮箱地址',
}
}
});
const { t } = useScopedI18n('views.User')
</script>

View File

@@ -1,7 +1,7 @@
<script setup>
import { ref, h, onMounted, watch, computed } from 'vue';
import { NBadge, useMessage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
import { api } from '../../api'
@@ -14,86 +14,7 @@ const {
} = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
name: 'Name',
created_at: 'Created At',
updated_at: 'Update At',
mail_count: 'Mail Count',
send_count: 'Send Count',
source_meta: 'Source',
showCredential: 'Show Mail Address Credential',
addressCredential: 'Mail Address Credential',
addressCredentialTip: 'Please copy the Mail Address Credential and you can use it to login to your email account.',
delete: 'Delete',
deleteTip: 'Are you sure to delete this email?',
deleteAccount: 'Delete Account',
viewMails: 'View Mails',
viewSendBox: 'View SendBox',
itemCount: 'itemCount',
query: 'Query',
addressQueryTip: 'Leave blank to query all addresses',
clearInbox: 'Clear Inbox',
clearSentItems: 'Clear Sent Items',
clearInboxTip: 'Are you sure to clear inbox for this email?',
clearSentItemsTip: 'Are you sure to clear sent items for this email?',
actions: 'Actions',
success: 'Success',
resetPassword: 'Reset Password',
newPassword: 'New Password',
passwordResetSuccess: 'Password reset successfully',
selectAll: 'Select All of This Page',
unselectAll: 'Unselect All',
pleaseSelectAddress: 'Please select address',
selectedItems: 'Selected',
multiDelete: 'Multi Delete',
multiDeleteTip: 'Are you sure to delete selected addresses?',
multiClearInbox: 'Multi Clear Inbox',
multiClearInboxTip: 'Are you sure to clear inbox for selected addresses?',
multiClearSentItems: 'Multi Clear Sent Items',
multiClearSentItemsTip: 'Are you sure to clear sent items for selected addresses?',
},
zh: {
name: '名称',
created_at: '创建时间',
updated_at: '更新时间',
mail_count: '邮件数量',
send_count: '发送数量',
source_meta: '来源',
showCredential: '查看邮箱地址凭证',
addressCredential: '邮箱地址凭证',
addressCredentialTip: '请复制邮箱地址凭证,你可以使用它登录你的邮箱。',
delete: '删除',
deleteTip: '确定要删除这个邮箱吗?',
deleteAccount: '删除邮箱',
viewMails: '查看邮件',
viewSendBox: '查看发件箱',
itemCount: '总数',
query: '查询',
addressQueryTip: '留空查询所有地址',
clearInbox: '清空收件箱',
clearSentItems: '清空发件箱',
clearInboxTip: '确定要清空这个邮箱的收件箱吗?',
clearSentItemsTip: '确定要清空这个邮箱的发件箱吗?',
actions: '操作',
success: '成功',
resetPassword: '重置密码',
newPassword: '新密码',
passwordResetSuccess: '密码重置成功',
selectAll: '全选本页',
unselectAll: '取消全选',
pleaseSelectAddress: '请选择地址',
selectedItems: '已选择',
multiDelete: '批量删除',
multiDeleteTip: '确定要删除选中的邮箱吗?',
multiClearInbox: '批量清空收件箱',
multiClearInboxTip: '确定要清空选中邮箱的收件箱吗?',
multiClearSentItems: '批量清空发件箱',
multiClearSentItemsTip: '确定要清空选中邮箱的发件箱吗?',
}
}
});
const { t } = useScopedI18n('views.admin.Account')
const showEmailCredential = ref(false)
const curEmailCredential = ref("")

View File

@@ -1,6 +1,6 @@
<script setup>
import { computed, onMounted, ref, h } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { NButton, NPopconfirm, NInput, NSelect, NRadioGroup, NRadio } from 'naive-ui'
import { useGlobalState } from '../../store'
@@ -9,110 +9,7 @@ import { api } from '../../api'
const { loading, openSettings } = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
tip: 'You can manually input the following multiple select input and enter',
manualInputPrompt: 'Type and press Enter to add',
save: 'Save',
successTip: 'Save Success',
address_block_list: 'Address Block Keywords for Users(Admin can skip)',
address_block_list_placeholder: 'Please enter the keywords you want to block',
send_address_block_list: 'Address Block Keywords for send email',
noLimitSendAddressList: 'No Balance Limit Send Address List',
verified_address_list: 'Verified Address List(Can send email by cf internal api)',
send_mail_limit: 'Send Mail Limit',
send_mail_limit_tip: 'This applies to all send channels. Use -1 for unlimited and 0 to block sending.',
send_mail_daily_limit: 'Daily Limit',
send_mail_monthly_limit: 'Monthly Limit',
send_mail_daily_limit_invalid: 'Daily limit must be an integer greater than or equal to -1',
send_mail_monthly_limit_invalid: 'Monthly limit must be an integer greater than or equal to -1',
fromBlockList: 'Block Keywords for receive email',
block_receive_unknow_address_email: 'Block receive unknow address email',
email_forwarding_config: 'Email Forwarding Configuration',
domain_list: 'Domain List (Optional)',
forward_address: 'Forward Address',
actions: 'Actions',
select_domain: 'Select Domain',
forward_placeholder: 'forward@example.com',
delete_rule: 'Delete',
delete_rule_confirm: 'Are you sure you want to delete this rule?',
delete_success: 'Delete Success',
forwarding_rule_warning: 'Each rule will run independently. Forward address needs to be a verified address.',
add: 'Add',
cancel: 'Cancel',
config: 'Config',
source_patterns: 'Source Address Regex (Optional)',
source_patterns_placeholder: 'e.g. gmail.com',
source_match_mode: 'Match Mode',
match_any: 'Any',
match_all: 'All',
source_patterns_tip: 'Domain list filters by recipient address, source regex filters by sender address. Both conditions must match for forwarding (AND logic). Leave either empty to skip that filter.',
regex_too_long: 'Regex pattern too long (max 200 characters)',
regex_invalid: 'Invalid regex pattern',
forward_address_required: 'Forward address is required',
rule_index: 'Rule',
create_address_subdomain_match: 'Allow Subdomain Suffix Match When Creating Address',
create_address_subdomain_match_tip: 'Only affects /api/new_address and /admin/new_address domain validation. Example: when enabled, foo.example.com can match configured base domain example.com.',
create_address_subdomain_match_note: 'This is different from RANDOM_SUBDOMAIN_DOMAINS: this switch allows API callers to specify custom subdomains directly, while random subdomain only auto-generates one during creation.',
create_address_subdomain_match_follow_env: 'Follow Environment Variable',
create_address_subdomain_match_force_enable: 'Force Enable',
create_address_subdomain_match_force_disable: 'Force Disable',
create_address_subdomain_match_follow_env_note: 'Choosing "Follow Environment Variable" clears the admin override and returns to the unset state. The effective result is still controlled by the Worker env and the precedence rules.',
create_address_subdomain_match_env_locked: 'Worker env ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH is currently false. The saved admin switch can be modified, but it will not take effect until env is enabled or removed.',
},
zh: {
tip: '您可以手动输入以下多选输入框, 回车增加',
manualInputPrompt: '输入后按回车键添加',
save: '保存',
successTip: '保存成功',
address_block_list: '邮件地址屏蔽关键词(管理员可跳过检查)',
address_block_list_placeholder: '请输入您想要屏蔽的关键词',
send_address_block_list: '发送邮件地址屏蔽关键词',
noLimitSendAddressList: '无余额限制发送地址列表',
verified_address_list: '已验证地址列表(可通过 cf 内部 api 发送邮件)',
send_mail_limit: '发信额度',
send_mail_limit_tip: '对全部发信渠道生效。-1 表示无限0 表示禁止发送。',
send_mail_daily_limit: '每日额度',
send_mail_monthly_limit: '每月额度',
send_mail_daily_limit_invalid: '每日额度必须是大于等于 -1 的整数',
send_mail_monthly_limit_invalid: '每月额度必须是大于等于 -1 的整数',
fromBlockList: '接收邮件地址屏蔽关键词',
block_receive_unknow_address_email: '禁止接收未知地址邮件',
email_forwarding_config: '邮件转发配置',
domain_list: '域名列表(可选)',
forward_address: '转发地址',
actions: '操作',
select_domain: '选择域名',
forward_placeholder: 'forward@example.com',
delete_rule: '删除',
delete_rule_confirm: '确定要删除这条规则吗?',
delete_success: '删除成功',
forwarding_rule_warning: '每条规则独立运行,转发地址需要为已验证的地址。',
add: '添加',
cancel: '取消',
config: '配置',
source_patterns: '来源地址正则(可选)',
source_patterns_placeholder: '例如: gmail.com',
source_match_mode: '匹配模式',
match_any: '任一',
match_all: '全部',
source_patterns_tip: '域名列表按收件地址过滤来源正则按发件地址过滤两者均为可选。同时配置时需同时满足AND 逻辑),留空则跳过该条件。',
regex_too_long: '正则表达式过长最大200字符',
regex_invalid: '无效的正则表达式',
forward_address_required: '转发地址不能为空',
rule_index: '规则',
create_address_subdomain_match: '创建邮箱时允许子域名后缀匹配',
create_address_subdomain_match_tip: '仅影响 /api/new_address 和 /admin/new_address 的域名校验。例如开启后foo.example.com 可以匹配已配置的基础域名 example.com。',
create_address_subdomain_match_note: '这与 RANDOM_SUBDOMAIN_DOMAINS 不同:这里允许 API 调用方直接指定自定义子域名;随机子域名功能只是在创建时自动补一个随机子域名。',
create_address_subdomain_match_follow_env: '跟随环境变量',
create_address_subdomain_match_force_enable: '强制开启',
create_address_subdomain_match_force_disable: '强制关闭',
create_address_subdomain_match_follow_env_note: '选择“跟随环境变量”会清空后台覆盖,恢复为未设置状态;最终是否开启仍由 Worker env 和优先级规则决定。',
create_address_subdomain_match_env_locked: '当前 Worker 环境变量 ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH 为 false。后台开关仍可保存但在 env 打开或移除前不会生效。',
}
}
});
const { t } = useScopedI18n('views.admin.AccountSettings')
const addressBlockList = ref([])
const sendAddressBlockList = ref([])

View File

@@ -1,38 +1,13 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useMessage } from 'naive-ui'
// @ts-ignore
import { api } from '../../api'
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
title: 'AI Email Extraction Settings',
successTip: 'Success',
save: 'Save',
enableAllowList: 'Enable Address Allowlist',
enableAllowListTip: 'When enabled, AI extraction will only process emails sent to addresses in the allowlist',
allowList: 'Address Allowlist (Enter address and press Enter, wildcards supported)',
allowListTip: "Wildcard * matches any characters, e.g. *{'@'}example.com matches all addresses under example.com domain",
manualInputPrompt: 'Type and press Enter to add',
disabledTip: 'When disabled, AI extraction will process all email addresses',
},
zh: {
title: 'AI 邮件提取设置',
successTip: '成功',
save: '保存',
enableAllowList: '启用地址白名单',
enableAllowListTip: '启用后AI 提取功能仅对白名单中的邮箱地址生效',
allowList: '地址白名单 (请输入地址并回车,支持通配符)',
allowListTip: "通配符 * 可匹配任意字符,如 *{'@'}example.com 可匹配 example.com 域名下的所有地址",
manualInputPrompt: '输入后按回车键添加',
disabledTip: '未启用时,所有邮箱地址都可使用 AI 提取功能',
}
}
});
const { t } = useScopedI18n('views.admin.AiExtractSettings')
type AiExtractSettings = {
enableAllowList: boolean

View File

@@ -1,6 +1,6 @@
<script setup>
import { computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
import { api } from '../../api'
@@ -10,36 +10,7 @@ const {
} = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
address: 'Address',
enablePrefix: 'If enable Prefix',
creatNewEmail: 'Create New Email',
fillInAllFields: 'Please fill in all fields',
successTip: 'Success Created',
addressCredential: 'Mail Address Credential',
addressCredentialTip: 'Please copy the Mail Address Credential and you can use it to login to your email account.',
addressPassword: 'Address Password',
linkWithAddressCredential: 'Open to auto login email link',
enableRandomSubdomain: 'Use Random Subdomain',
randomSubdomainTip: 'When enabled, the created address will use a random subdomain. Subdomain addresses are recommended for receiving only.',
},
zh: {
address: '地址',
enablePrefix: '是否启用前缀',
creatNewEmail: '创建新邮箱',
fillInAllFields: '请填写完整信息',
successTip: '创建成功',
addressCredential: '邮箱地址凭证',
addressCredentialTip: '请复制邮箱地址凭证,你可以使用它登录你的邮箱。',
addressPassword: '地址密码',
linkWithAddressCredential: '打开即可自动登录邮箱的链接',
enableRandomSubdomain: '启用随机子域名',
randomSubdomainTip: '启用后,创建出来的地址会自动挂在随机子域名下。子域名地址更建议仅用于收件。',
}
}
});
const { t } = useScopedI18n('views.admin.CreateAccount')
const enablePrefix = ref(true)
const enableRandomSubdomain = ref(false)

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { CleaningServicesFilled } from '@vicons/material'
import { api } from '../../api'
@@ -14,30 +14,7 @@ const dbVersionData = ref({
code_db_version: ''
})
const { t } = useI18n({
messages: {
en: {
need_initialization_tip: 'Database initialization is required. Please initialize the database.',
need_migration_tip: 'Database migration is required. Please migrate the database.',
current_db_version: 'Current DB Version',
code_db_version: 'Code Needed DB Version',
init: 'Initialize Database',
migration: 'Migrate Database',
initializationSuccess: 'Database initialized successfully',
migrationSuccess: 'Database migrated successfully',
},
zh: {
need_initialization_tip: '需要初始化数据库,请初始化数据库',
need_migration_tip: '需要迁移数据库,请迁移数据库',
current_db_version: '当前数据库版本',
code_db_version: '需要的数据库版本',
init: '初始化数据库',
migration: '升级数据库 Schema',
initializationSuccess: '数据库初始化成功',
migrationSuccess: '数据库升级成功',
}
}
});
const { t } = useScopedI18n('views.admin.DatabaseManager')
const fetchData = async () => {
try {

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
import { api } from '../../api'
@@ -8,68 +8,7 @@ import { api } from '../../api'
const { loading } = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
title: 'IP Blacklist Settings',
manualInputPrompt: 'Type pattern and press Enter to add',
save: 'Save',
successTip: 'Save Success',
enable_ip_blacklist: 'Enable IP Blacklist',
enable_tip: 'Block IPs matching blacklist patterns from accessing rate-limited APIs',
enable_ip_whitelist: 'Enable IP Whitelist (Strict)',
enable_whitelist_tip: 'Strict mode: ONLY IPs matching the whitelist can access rate-limited APIs. All other IPs will be denied.',
ip_whitelist: 'IP Whitelist Patterns',
ip_whitelist_placeholder: 'Exact IP (e.g., 1.2.3.4) or anchored regex (e.g., ^192\\.168\\.1\\.\\d+$)',
tip_whitelist: 'IP Whitelist: Strict allowlist — plain entries must be EXACT IP matches (no substring). Use anchored regex (^...$) for ranges. Whitelisted IPs skip blacklist checks.',
whitelist_empty_warning: 'IP whitelist is enabled but the list is empty. This is ignored by the server to prevent lockout. Please add at least one entry before enabling.',
ip_blacklist: 'IP Blacklist Patterns',
ip_blacklist_placeholder: 'Enter pattern (e.g., 192.168.1 or ^10\\.0\\.0\\.5$)',
asn_blacklist: 'ASN Organization Blacklist',
asn_blacklist_placeholder: 'Enter ASN organization (e.g., Google, Amazon)',
fingerprint_blacklist: 'Browser Fingerprint Blacklist',
fingerprint_blacklist_placeholder: 'Enter fingerprint ID (e.g., a1b2c3d4e5f6g7h8)',
tip_ip: 'IP Blacklist: Supports text matching (e.g., "192.168.1") or regex (e.g., "^10\\.0\\.0\\.5$").',
tip_asn: 'ASN Organization: Block by ISP/provider. Case-insensitive text matching or regex.',
tip_fingerprint: 'Browser Fingerprint: Block by browser fingerprint. Supports exact matching or regex patterns.',
tip_daily_limit: 'Daily Limit: Restrict the maximum number of requests per IP address per day (1-1000000).',
tip_scope: 'Applies to: Create Address, Send Mail, External Send Mail API, User Registration, Verify Code',
enable_daily_limit: 'Enable Daily Request Limit',
enable_daily_limit_tip: 'Limit the number of API requests per IP address per day',
daily_request_limit: 'Daily Request Limit',
daily_request_limit_placeholder: 'Enter limit (e.g., 1000)',
},
zh: {
title: 'IP 黑名单设置',
manualInputPrompt: '输入匹配模式后按回车键添加',
save: '保存',
successTip: '保存成功',
enable_ip_blacklist: '启用 IP 黑名单',
enable_tip: '阻止匹配黑名单的 IP 访问限流 API',
enable_ip_whitelist: '启用 IP 白名单(严格模式)',
enable_whitelist_tip: '严格模式:仅允许匹配白名单的 IP 访问限流 API其他所有 IP 将被拒绝',
ip_whitelist: 'IP 白名单匹配模式',
ip_whitelist_placeholder: '精确 IP(如 1.2.3.4)或锚定正则(如 ^192\\.168\\.1\\.\\d+$)',
tip_whitelist: 'IP 白名单: 严格放行名单——纯文本必须是精确 IP(不支持子串匹配), 批量放行请用锚定正则 ^...$. 命中白名单的 IP 将跳过黑名单检查.',
whitelist_empty_warning: 'IP 白名单已启用但列表为空,服务端将忽略该开关以防止锁死。请先添加至少一条白名单条目再启用。',
ip_blacklist: 'IP 黑名单匹配模式',
ip_blacklist_placeholder: '输入匹配模式例如192.168.1 或 ^10\\.0\\.0\\.5$',
asn_blacklist: 'ASN 组织(运营商)黑名单',
asn_blacklist_placeholder: '输入 ASN 组织名称例如Google, Amazon',
fingerprint_blacklist: '浏览器指纹黑名单',
fingerprint_blacklist_placeholder: '输入指纹 ID例如a1b2c3d4e5f6g7h8',
tip_ip: 'IP 黑名单:支持文本匹配(如 "192.168.1")或正则表达式(如 "^10\\.0\\.0\\.5$")。',
tip_asn: 'ASN 组织:根据运营商/ISP 拉黑。支持不区分大小写的文本匹配或正则表达式。',
tip_fingerprint: '浏览器指纹:根据浏览器指纹拉黑。支持完全匹配或正则表达式。',
tip_daily_limit: '每日限流:限制单个 IP 地址每天最多请求次数1-1000000。',
tip_scope: '作用范围:创建邮箱地址、发送邮件、外部发送邮件 API、用户注册、验证码验证',
enable_daily_limit: '启用每日请求限流',
enable_daily_limit_tip: '限制每个 IP 地址每天的 API 请求次数',
daily_request_limit: '每日请求次数上限',
daily_request_limit_placeholder: '输入限制次数例如1000',
}
}
});
const { t } = useScopedI18n('views.admin.IpBlacklistSettings')
const enabled = ref(false)
const ipBlacklist = ref([])

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted, watch } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
import { api } from '../../api'
@@ -8,18 +8,7 @@ import MailBox from '../../components/MailBox.vue';
const { adminMailTabAddress } = useGlobalState()
const { t } = useI18n({
messages: {
en: {
addressQueryTip: 'Leave blank to query all addresses',
query: 'Query',
},
zh: {
addressQueryTip: '留空查询所有地址',
query: '查询',
}
}
});
const { t } = useScopedI18n('views.admin.Mails')
const mailBoxKey = ref("")

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { CleaningServicesFilled, AddFilled, DeleteFilled } from '@vicons/material'
import { useGlobalState } from '../../store'
@@ -26,60 +26,7 @@ const cleanupModel = ref({
customSqlCleanupList: []
})
const { t } = useI18n({
messages: {
en: {
tip: 'Please input the days',
mailBoxLabel: 'Cleanup the inbox before n days',
mailUnknowLabel: "Cleanup the unknow mail before n days",
sendBoxLabel: "Cleanup the sendbox before n days",
addressCreateLabel: "Cleanup the address created before n days",
inactiveAddressLabel: "Cleanup the inactive address before n days",
unboundAddressLabel: "Cleanup the unbound address before n days",
emptyAddressLabel: "Cleanup the empty address before n days",
cleanupNow: "Cleanup now",
autoCleanup: "Auto cleanup",
cleanupSuccess: "Cleanup success",
saveSuccess: "Save success",
save: "Save",
cronTip: "Enable cron cleanup, need to configure [crons] in worker, please refer to the document, setting 0 days means clear all",
basicCleanup: "Basic Cleanup",
customSqlCleanup: "Custom SQL Cleanup",
customSqlTip: "Add custom DELETE SQL statements for scheduled cleanup. Only single DELETE statement is allowed per entry.",
addCustomSql: "Add Custom SQL",
sqlName: "Name",
sqlStatement: "SQL Statement (DELETE only)",
sqlNamePlaceholder: "e.g. Clean old logs",
sqlPlaceholder: "e.g. DELETE FROM raw_mails WHERE source GLOB '*{'@'}example.com' AND created_at < datetime('now', '-3 day')",
deleteCustomSql: "Delete",
},
zh: {
tip: '请输入天数',
mailBoxLabel: '清理 n 天前的收件箱',
mailUnknowLabel: "清理 n 天前的无收件人邮件",
sendBoxLabel: "清理 n 天前的发件箱",
addressCreateLabel: "清理 n 天前创建的地址",
inactiveAddressLabel: "清理 n 天前的未活跃地址",
unboundAddressLabel: "清理 n 天前的未绑定用户地址",
emptyAddressLabel: "清理 n 天前空邮件的邮箱地址",
autoCleanup: "自动清理",
cleanupSuccess: "清理成功",
saveSuccess: "保存成功",
cleanupNow: "立即清理",
save: "保存",
cronTip: "启用定时清理, 需在 worker 配置 [crons] 参数, 请参考文档, 配置为 0 天表示全部清空",
basicCleanup: "基础清理",
customSqlCleanup: "自定义 SQL 清理",
customSqlTip: "添加自定义 DELETE SQL 语句进行定时清理。每条记录仅允许单条 DELETE 语句。",
addCustomSql: "添加自定义 SQL",
sqlName: "名称",
sqlStatement: "SQL 语句 (仅限 DELETE)",
sqlNamePlaceholder: "例如: 清理旧日志",
sqlPlaceholder: "例如: DELETE FROM raw_mails WHERE source GLOB '*{'@'}example.com' AND created_at < datetime('now', '-3 day')",
deleteCustomSql: "删除",
}
}
});
const { t } = useScopedI18n('views.admin.Maintenance')
const cleanup = async (cleanType, cleanDays) => {
try {

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted, h } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { NInputNumber, NTag, NSpace, NButton } from 'naive-ui';
import { useGlobalState } from '../../store'
@@ -9,28 +9,7 @@ import { api } from '../../api'
const { loading } = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
role: 'Role',
maxAddressCount: 'Max Address Count (0 = Unlimited)',
save: 'Save',
successTip: 'Success',
noRolesAvailable: 'No roles available in system config',
roleConfigDesc: 'Configure maximum address count for each user role. Role-based limits take priority over global settings. Set 0 for unlimited.',
notConfigured: 'Not Configured (Use Global Settings)',
},
zh: {
role: '角色',
maxAddressCount: '最大地址数量0 为不限制)',
save: '保存',
successTip: '成功',
noRolesAvailable: '系统配置中没有可用的角色',
roleConfigDesc: '为每个用户角色配置最大地址数量。角色配置优先于全局设置。设置为 0 表示不限制。',
notConfigured: '未配置(使用全局设置)',
}
}
});
const { t } = useScopedI18n('views.admin.RoleAddressConfig')
const systemRoles = ref([])
const tableData = ref([])

View File

@@ -1,5 +1,5 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
import { api } from '../../api'
@@ -7,18 +7,7 @@ import SendBox from '../../components/SendBox.vue';
const { adminSendBoxTabAddress } = useGlobalState()
const { t } = useI18n({
messages: {
en: {
query: 'Query',
queryTip: 'Please input address to query, leave blank to query all',
},
zh: {
query: '查询',
queryTip: '请输入地址查询, 留空则查询所有',
}
}
});
const { t } = useScopedI18n('views.admin.SendBox')
const fetchData = async (limit, offset) => {
adminSendBoxTabAddress.value = adminSendBoxTabAddress.value.trim();

View File

@@ -1,7 +1,7 @@
<script setup>
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { onBeforeUnmount, ref, shallowRef } from 'vue'
import { useSessionStorage } from '@vueuse/core'
import { api } from '../../api'
@@ -21,49 +21,7 @@ const sendMailModel = useSessionStorage('sendMailByAdminModel', {
content: "",
});
const { t } = useI18n({
locale: 'zh',
messages: {
en: {
successSend: 'Please check your sendbox. If failed, please try again later.',
fromName: 'Your Name and Address, leave Name blank to use email address',
toName: 'Recipient Name and Address, leave Name blank to use email address',
subject: 'Subject',
options: 'Options',
edit: 'Edit',
preview: 'Preview',
content: 'Content',
send: 'Send',
fromMailEmpty: 'Sender address is empty',
subjectEmpty: 'Subject is empty',
toMailEmpty: 'Recipient address is empty',
contentEmpty: 'Content is empty',
text: 'Text',
html: 'HTML',
'rich text': 'Rich Text',
tooLarge: 'Too large file, please upload file less than 1MB.',
},
zh: {
successSend: '请查看您的发件箱, 如果失败, 请检查稍后重试。',
fromName: '你的名称和地址,名称不填写则使用邮箱地址',
toName: '收件人名称和地址,名称不填写则使用邮箱地址',
subject: '主题',
options: '选项',
edit: '编辑',
preview: '预览',
content: '内容',
send: '发送',
fromMailEmpty: '发件人地址不能为空',
subjectEmpty: '主题不能为空',
toMailEmpty: '收件人地址不能为空',
contentEmpty: '内容不能为空',
text: '文本',
html: 'HTML',
'rich text': '富文本',
tooLarge: '文件过大, 请上传小于1MB的文件。',
}
}
});
const { t } = useScopedI18n('views.admin.SendMail')
const contentTypes = [
{ label: t('text'), value: 'text' },

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, h, onMounted, watch } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
import { api } from '../../api'
@@ -8,44 +8,7 @@ import { api } from '../../api'
const { loading } = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
address: 'Address',
success: 'Success',
is_enabled: 'Is Enabled',
enable: 'Enable',
disable: 'Disable',
modify: 'Modify',
delete: 'Delete',
deleteTip: 'Are you sure to delete this?',
created_at: 'Created At',
action: 'Action',
itemCount: 'itemCount',
modalTip: 'Please input the sender balance',
balance: 'Balance',
query: 'Query',
ok: 'OK'
},
zh: {
address: '地址',
success: '成功',
is_enabled: '是否启用',
enable: '启用',
disable: '禁用',
modify: '修改',
delete: '删除',
deleteTip: '确定删除吗?',
created_at: '创建时间',
action: '操作',
itemCount: '总数',
modalTip: '请输入发件额度',
balance: '余额',
query: '查询',
ok: '确定'
}
}
});
const { t } = useScopedI18n('views.admin.SenderAccess')
const data = ref([])
const count = ref(0)
const page = ref(1)

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, h, onMounted, watch } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { User, UserCheck, MailBulk } from '@vicons/fa'
import { SendOutlined } from '@vicons/material'
@@ -8,26 +8,7 @@ import { api } from '../../api'
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
userCount: 'User Count',
addressCount: 'Address Count',
activeAddressCount7days: '7 days Active Address Count',
activeAddressCount30days: '30 days Active Address Count',
mailCount: 'Mail Count',
sendMailCount: 'Send Mail Count'
},
zh: {
userCount: '用户总数',
addressCount: '邮箱地址总数',
activeAddressCount7days: '7天活跃邮箱地址总数',
activeAddressCount30days: '30天活跃邮箱地址总数',
mailCount: '邮件总数',
sendMailCount: '发送邮件总数'
}
}
});
const { t } = useScopedI18n('views.admin.Statistics')
const statistics = ref({
addressCount: 0,

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
// @ts-ignore
import { useGlobalState } from '../../store'
@@ -9,38 +9,7 @@ import { api } from '../../api'
// @ts-ignore
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
init: 'Init',
successTip: 'Success',
status: 'Check Status',
enableTelegramAllowList: 'Enable Telegram Allow List(Manually input Chat ID)',
enable: 'Enable',
telegramAllowList: 'Telegram Allow List(Manually input telegram Chat ID)',
manualInputPrompt: 'Type and press Enter to add',
save: 'Save',
miniAppUrl: 'Telegram Mini App URL',
enableGlobalMailPush: 'Enable Global Mail Push(Manually input telegram Chat ID)',
globalMailPushList: 'Global Mail Push Chat ID List',
globalMailPushListTip: 'Support chat_id of private chat/group/channel. You can send a message to your bot, then visit this link to see chat_id, https://api.telegram.org/bot<Replace with your BOT TOKEN>/getUpdates',
},
zh: {
init: '初始化',
successTip: '成功',
status: '查看状态',
enableTelegramAllowList: '启用 Telegram 白名单(手动输入 Chat ID, 回车增加)',
enable: '启用',
telegramAllowList: 'Telegram 白名单(手动输入 Chat ID, 回车增加)',
manualInputPrompt: '输入后按回车键添加',
save: '保存',
miniAppUrl: '电报小程序 URL(请输入你部署的电报小程序网页地址)',
enableGlobalMailPush: '启用全局邮件推送(手动输入邮箱管理员的 telegram Chat ID, 回车增加)',
globalMailPushList: '全局邮件推送 Chat ID 列表',
globalMailPushListTip: '支持对话/群组/频道的 Chat ID, 您可以发送一条消息给您的机器人,然后访问此链接来查看 chat_id, https://api.telegram.org/bot<这里替换成您的 BOT TOKEN>/getUpdates',
}
}
});
const { t } = useScopedI18n('views.admin.Telegram')
const status = ref({
fetched: false,

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, h, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { NBadge } from 'naive-ui'
import { api } from '../../api'
@@ -14,22 +14,7 @@ const props = defineProps({
const message = useMessage()
const { locale, t } = useI18n({
messages: {
en: {
success: 'success',
name: 'Name',
mail_count: 'Mail Count',
send_count: 'Send Count',
},
zh: {
success: '成功',
name: '名称',
mail_count: '邮件数量',
send_count: '发送数量',
}
}
});
const { locale, t } = useScopedI18n('views.admin.UserAddressManagement')
const data = ref([])

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, h, onMounted, watch, computed } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { NMenu, NButton, NBadge, NTag } from 'naive-ui';
import { MenuFilled } from '@vicons/material'
@@ -13,56 +13,7 @@ import UserAddressManagement from './UserAddressManagement.vue'
const { loading, openSettings } = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
success: 'Success',
user_email: 'User Email',
role: 'Role',
address_count: 'Address Count',
created_at: 'Created At',
actions: 'Actions',
query: 'Query',
itemCount: 'itemCount',
deleteUser: 'Delete User',
delete: 'Delete',
deleteUserTip: 'Are you sure you want to delete this user?',
resetPassword: 'Reset Password',
pleaseInput: 'Please input complete information',
createUser: 'Create User',
email: 'Email',
password: 'Password',
changeRole: 'Change Role',
prefix: 'Prefix',
domains: 'Domains',
roleDonotExist: 'Current Role does not exist',
userAddressManagement: 'Address Management',
},
zh: {
success: '成功',
user_email: '用户邮箱',
role: '角色',
address_count: '地址数量',
created_at: '创建时间',
actions: '操作',
query: '查询',
itemCount: '总数',
deleteUser: '删除用户',
delete: '删除',
deleteUserTip: '确定要删除此用户吗?',
resetPassword: '重置密码',
pleaseInput: '请输入完整信息',
createUser: '创建用户',
email: '邮箱',
password: '密码',
changeRole: '更改角色',
prefix: '前缀',
domains: '域名',
roleDonotExist: '当前角色不存在',
userAddressManagement: '地址管理',
}
}
});
const { t } = useScopedI18n('views.admin.UserManagement')
const data = ref([])
const count = ref(0)
const page = ref(1)

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
// @ts-ignore
import { useGlobalState } from '../../store'
@@ -13,48 +13,7 @@ const { loading } = useGlobalState()
// @ts-ignore
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
save: 'Save',
delete: 'Delete',
successTip: 'Save Success',
enable: 'Enable',
enableMailAllowList: 'Enable Mail Address Allow List(Manually enterable)',
manualInputPrompt: 'Type and press Enter to add',
mailAllowList: 'Mail Address Allow List',
addOauth2: 'Add Oauth2',
name: 'Name',
icon: 'Icon (SVG, please ensure trusted source)',
iconPreview: 'Preview',
oauth2Type: 'Oauth2 Type',
enableEmailFormat: 'Enable Email Format',
userEmailFormat: 'Email Regex Pattern',
userEmailReplace: 'Replace Template',
userEmailFormatTip: 'Use regex to transform email. Example: ^(.+)@old\\.com$ with $1@new.com',
tip: 'Third-party login will automatically use the user\'s email to register an account (the same email will be regarded as the same account), this account is the same as the registered account, and you can also set the password through the forget password',
},
zh: {
save: '保存',
delete: '删除',
successTip: '保存成功',
enable: '启用',
enableMailAllowList: '启用邮件地址白名单(可手动输入, 回车增加)',
manualInputPrompt: '输入后按回车键添加',
mailAllowList: '邮件地址白名单',
addOauth2: '添加 Oauth2',
name: '名称',
icon: '图标 (SVG, 请确保来源可信)',
iconPreview: '预览',
oauth2Type: 'Oauth2 类型',
enableEmailFormat: '启用邮箱格式转换',
userEmailFormat: '邮箱正则表达式',
userEmailReplace: '替换模板',
userEmailFormatTip: '使用正则转换邮箱。示例: ^(.+)@old\\.com$ 配合 $1@new.com',
tip: '第三方登录会自动使用用户邮箱注册账号(邮箱相同将视为同一账号), 此账号和注册的账号相同, 也可以通过忘记密码设置密码',
}
}
});
const { t } = useScopedI18n('views.admin.UserOauth2Settings')
const OAUTH2_ICONS: Record<string, string> = {
github: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>',

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
import { api } from '../../api'
@@ -8,38 +8,7 @@ import { api } from '../../api'
const { loading } = useGlobalState()
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
save: 'Save',
successTip: 'Save Success',
enable: 'Enable',
enableUserRegister: 'Allow User Register',
enableMailVerify: 'Enable Mail Verify (Send address must be an address in the system with a balance and can send mail normally)',
verifyMailSender: 'Verify Mail Sender',
enableMailAllowList: 'Enable Mail Address Allow List(Manually enterable)',
manualInputPrompt: 'Type and press Enter to add',
mailAllowList: 'Mail Address Allow List',
maxAddressCount: 'Maximum number of email addresses that can be binded (0 = Unlimited)',
emailCheckRegex: "Email Check Regex (e.g. ^[^.]+{'@'}.+$ to disallow dots before {'@'})",
enableEmailCheckRegex: 'Enable Email Check Regex',
},
zh: {
save: '保存',
successTip: '保存成功',
enable: '启用',
enableUserRegister: "允许用户注册",
enableMailVerify: '启用邮件验证(发送地址必须是系统中能有余额且能正常发送邮件的地址)',
verifyMailSender: '验证邮件发送地址',
enableMailAllowList: '启用邮件地址白名单(可手动输入, 回车增加)',
manualInputPrompt: '输入后按回车键添加',
mailAllowList: '邮件地址白名单',
maxAddressCount: '可绑定最大邮箱地址数量0 为不限制)',
emailCheckRegex: "邮箱正则校验 (例如 ^[^.]+{'@'}.+$ 禁止{'@'}前面有.)",
enableEmailCheckRegex: '启用邮箱正则校验',
}
}
});
const { t } = useScopedI18n('views.admin.UserSettings')
const commonMail = [
"gmail.com", "163.com", "126.com", "qq.com", "outlook.com", "hotmail.com",

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
// @ts-ignore
import { useGlobalState } from '../../store'
@@ -9,26 +9,7 @@ import { api } from '../../api'
// @ts-ignore
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
successTip: 'Success',
enableAllowList: 'Enable Allow List (Restrict webhook access to specific users)',
webhookAllowList: 'Webhook Allow List(Enter the mail address that is allowed to use webhook and enter)',
manualInputPrompt: 'Type and press Enter to add',
save: 'Save',
notEnabled: 'Webhook is not enabled',
},
zh: {
successTip: '成功',
enableAllowList: '启用白名单 (限制 webhook 访问权限,只有白名单中的用户可以使用)',
webhookAllowList: 'Webhook 白名单(请输入允许使用webhook 的邮箱地址, 回车增加)',
manualInputPrompt: '输入后按回车键添加',
save: '保存',
notEnabled: 'Webhook 未开启',
}
}
});
const { t } = useScopedI18n('views.admin.Webhook')
class WebhookSettings {
enableAllowList: boolean;

View File

@@ -1,6 +1,5 @@
<script setup>
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'
import { useGlobalState } from '../../store'
import { api } from '../../api'

View File

@@ -1,18 +1,9 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useGlobalState } from '../../store'
const { openSettings } = useGlobalState()
const { t } = useI18n({
messages: {
en: {
adminContact: 'If you need help, please contact the administrator ({msg})',
},
zh: {
adminContact: '如果你需要帮助,请联系管理员 ({msg})',
}
}
});
const { t } = useScopedI18n('views.common.AdminContact')
</script>
<template>

View File

@@ -1,5 +1,5 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useIsMobile } from '../../utils/composables'
import { useGlobalState } from '../../store'
@@ -16,38 +16,7 @@ const {
} = useGlobalState()
const isMobile = useIsMobile()
const { t } = useI18n({
messages: {
en: {
useSimpleIndex: 'Use Simple Index',
mailboxSplitSize: 'Mailbox Split Size',
useIframeShowMail: 'Use iframe Show HTML Mail',
preferShowTextMail: 'Display text Mail by default',
useSideMargin: 'Turn on the side margins on the left and right sides of the page',
globalTabplacement: 'Global Tab Placement',
left: 'left',
top: 'top',
right: 'right',
bottom: 'bottom',
useUTCDate: 'Use UTC Date',
autoRefreshInterval: 'Auto Refresh Interval(Sec)',
},
zh: {
useSimpleIndex: '使用极简主页',
mailboxSplitSize: '邮箱界面分栏大小',
preferShowTextMail: '默认以文本显示邮件',
useIframeShowMail: '使用iframe显示HTML邮件',
globalTabplacement: '全局选项卡位置',
useSideMargin: '开启页面左右两侧侧边距',
left: '左侧',
top: '顶部',
right: '右侧',
bottom: '底部',
useUTCDate: '使用 UTC 时间',
autoRefreshInterval: '自动刷新间隔(秒)',
}
}
});
const { t } = useScopedI18n('views.common.Appearance')
</script>
<template>

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router'
import { NewLabelOutlined, EmailOutlined } from '@vicons/material'
@@ -120,58 +120,7 @@ const login = async () => {
}
}
const { locale, t } = useI18n({
messages: {
en: {
login: 'Login',
loginAndBind: 'Login and Bind',
pleaseGetNewEmail: 'Please login or click "Get New Email" button to get a new email address',
getNewEmail: 'Create New Email',
getNewEmailTip1: 'Please input the email you want to use. only allow: ',
getNewEmailTip2: 'Levaing it blank will generate a random email address.',
getNewEmailTip3: 'You can choose a domain from the dropdown list.',
credential: 'Email Address Credential',
ok: 'OK',
generateName: 'Generate Fake Name',
help: 'Help',
credentialInput: 'Please input the Mail Address Credential',
bindUserInfo: 'Logged in user, login without binding email or create new email address will bind to current user',
bindUserAddressError: 'Error when bind email address to user',
autoGeneratedName: 'Auto-generated name',
passwordLogin: 'Password Login',
credentialLogin: 'Credential Login',
email: 'Email',
password: 'Password',
emailPasswordRequired: 'Email and password are required',
enableRandomSubdomain: 'Use Random Subdomain',
randomSubdomainTip: 'When enabled, the created address will use a random subdomain. Subdomain addresses are recommended for receiving only.',
},
zh: {
login: '登录',
loginAndBind: '登录并绑定',
pleaseGetNewEmail: '请"登录"或点击 "注册新邮箱" 按钮来获取一个新的邮箱地址',
getNewEmail: '创建新邮箱',
getNewEmailTip1: '请输入你想要使用的邮箱地址, 只允许: ',
getNewEmailTip2: '留空将会生成一个随机的邮箱地址。',
getNewEmailTip3: '你可以从下拉列表中选择一个域名。',
credential: '邮箱地址凭据',
ok: '确定',
generateName: '生成随机名字',
help: '帮助',
credentialInput: '请输入邮箱地址凭据',
bindUserInfo: '已登录用户, 登录未绑定邮箱或创建新邮箱地址将绑定到当前用户',
bindUserAddressError: '绑定邮箱地址到用户时错误',
autoGeneratedName: '自动生成名称',
passwordLogin: '密码登录',
credentialLogin: '凭据登录',
email: '邮箱',
password: '密码',
emailPasswordRequired: '邮箱和密码不能为空',
enableRandomSubdomain: '启用随机子域名',
randomSubdomainTip: '启用后,创建出来的地址会自动挂在随机子域名下。子域名地址更建议仅用于收件。',
}
}
});
const { locale, t } = useScopedI18n('views.common.Login')
const loginAndBindTag = computed(() => {
if (userSettings.value.user_email) {

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router'
import { useGlobalState } from '../../store'
@@ -21,46 +21,7 @@ const showClearSentItems = ref(false)
const showChangePassword = ref(false)
const newPassword = ref('')
const confirmPassword = ref('')
const { locale, t } = useI18n({
messages: {
en: {
logout: "Logout",
deleteAccount: "Delete Account",
showAddressCredential: 'Show Address Credential',
logoutConfirm: 'Are you sure to logout?',
deleteAccount: "Delete Account",
deleteAccountConfirm: "Are you sure to delete your account and all emails for this account?",
clearInbox: "Clear Inbox",
clearSentItems: "Clear Sent Items",
clearInboxConfirm: "Are you sure to clear all emails in your inbox?",
clearSentItemsConfirm: "Are you sure to clear all emails in your sent items?",
success: "Success",
changePassword: "Change Password",
newPassword: "New Password",
confirmPassword: "Confirm Password",
passwordMismatch: "Passwords do not match",
passwordChanged: "Password changed successfully",
},
zh: {
logout: '退出登录',
deleteAccount: "删除账户",
showAddressCredential: '查看邮箱地址凭证',
logoutConfirm: '确定要退出登录吗?',
deleteAccount: "删除账户",
deleteAccountConfirm: "确定要删除你的账户和其中的所有邮件吗?",
clearInbox: "清空收件箱",
clearSentItems: "清空发件箱",
clearInboxConfirm: "确定要清空你收件箱中的所有邮件吗?",
clearSentItemsConfirm: "确定要清空你发件箱中的所有邮件吗?",
success: "成功",
changePassword: "修改密码",
newPassword: "新密码",
confirmPassword: "确认密码",
passwordMismatch: "密码不匹配",
passwordChanged: "密码修改成功",
}
}
});
const { locale, t } = useScopedI18n('views.index.AccountSettings')
const logout = async () => {
jwt.value = '';

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router'
import { User, ExchangeAlt } from '@vicons/fa'
@@ -20,30 +20,7 @@ const {
isTelegram, addressPassword
} = useGlobalState()
const { locale, t } = useI18n({
messages: {
en: {
ok: 'OK',
fetchAddressError: 'Mail address credential is invalid or account not exist, it may be network connection issue, please try again later.',
addressCredential: 'Mail Address Credential',
linkWithAddressCredential: 'Open to auto login email link',
addressCredentialTip: 'Please copy the Mail Address Credential and you can use it to login to your email account.',
addressPassword: 'Address Password',
userLogin: 'User Login',
addressManage: 'Manage',
},
zh: {
ok: '确定',
fetchAddressError: '邮箱地址凭证无效或邮箱地址不存在,也可能是网络连接异常,请稍后再尝试。',
addressCredential: '邮箱地址凭证',
linkWithAddressCredential: '打开即可自动登录邮箱的链接',
addressCredentialTip: '请复制邮箱地址凭证,你可以使用它登录你的邮箱。',
addressPassword: '地址密码',
userLogin: '用户登录',
addressManage: '地址管理',
}
}
});
const { locale, t } = useScopedI18n('views.index.AddressBar')
const showAddressManage = ref(false)

View File

@@ -1,30 +1,13 @@
<script setup>
import { ref, h, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { api } from '../../api'
import { NPopconfirm } from 'naive-ui';
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
download: 'Download',
action: 'Action',
delete: 'Delete',
deleteConfirm: 'Are you sure to delete this attachment?',
deleteSuccess: 'Deleted successfully',
},
zh: {
download: '下载',
action: '操作',
delete: '删除',
deleteConfirm: '确定要删除此附件吗?',
deleteSuccess: '删除成功',
}
}
});
const { t } = useScopedI18n('views.index.Attachment')
const data = ref([])
const showDownload = ref(false)
const curRow = ref({})

View File

@@ -1,5 +1,5 @@
<script setup>
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { onMounted, ref } from 'vue'
import { useGlobalState } from '../../store'
@@ -15,33 +15,7 @@ const name = ref("")
const { settings } = useGlobalState()
const { t } = useI18n({
locale: 'zh',
messages: {
en: {
success: 'Success',
settings: 'Settings',
sourcePrefix: 'Sender Filter',
sourcePrefixPlaceholder: 'Empty=all, prefix match, or /regex/',
name: 'Name',
enableAutoReply: 'Enable Auto Reply',
subject: 'Subject',
autoReply: 'Auto Reply',
save: 'Save',
},
zh: {
success: '成功',
settings: '设置',
sourcePrefix: '发件人过滤',
sourcePrefixPlaceholder: '留空=全部匹配,前缀匹配,或 /正则/',
name: '名称',
enableAutoReply: '启用自动回复',
subject: '主题',
autoReply: '自动回复',
save: '保存',
}
}
});
const { t } = useScopedI18n('views.index.AutoReply')
const fetchData = async () => {
try {

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, h, computed } from 'vue';
import { useLocalStorage } from '@vueuse/core';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { NPopconfirm, NButton } from 'naive-ui'
// @ts-ignore
@@ -13,30 +13,7 @@ const { jwt } = useGlobalState()
// @ts-ignore
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
tip: 'These addresses are stored in your browser, maybe loss if you clear the browser cache.',
success: 'success',
address: 'Address',
actions: 'Actions',
changeMailAddress: 'Change Mail Address',
unbindMailAddress: 'Unbind Mail Address credential',
create_or_bind: 'Create or Bind',
bindAddressSuccess: 'Bind Address Success',
},
zh: {
tip: '这些地址存储在您的浏览器中,如果您清除浏览器缓存,可能会丢失。',
success: '成功',
address: '地址',
actions: '操作',
changeMailAddress: '切换邮箱地址',
unbindMailAddress: '解绑邮箱地址',
create_or_bind: '创建或绑定',
bindAddressSuccess: '绑定地址成功',
}
}
});
const { t } = useScopedI18n('views.index.LocalAddress')
const tabValue = ref('address')
const localAddressCache = useLocalStorage("LocalAddressCache", []);

View File

@@ -1,7 +1,7 @@
<script setup>
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { onMounted, onBeforeUnmount, ref, shallowRef } from 'vue'
import AdminContact from '../common/AdminContact.vue'
@@ -16,53 +16,7 @@ const sending = ref(false)
const { settings, sendMailModel, indexTab, userSettings } = useGlobalState()
const { t } = useI18n({
locale: 'zh',
messages: {
en: {
successSend: 'Please check your sendbox. If failed, please check your balance or try again later.',
fromName: 'Your Name and Address, leave Name blank to use email address',
toName: 'Recipient Name and Address, leave Name blank to use email address',
subject: 'Subject',
options: 'Options',
edit: 'Edit',
preview: 'Preview',
content: 'Content',
send: 'Send',
subjectEmpty: 'Subject is empty',
toMailEmpty: 'Recipient address is empty',
contentEmpty: 'Content is empty',
requestAccess: 'Request Access',
requestAccessTip: 'No send balance yet. If your admin enabled a default balance it should be assigned automatically; otherwise request access or contact the admin.',
send_balance: 'Send Mail Balance Left',
text: 'Text',
html: 'HTML',
'rich text': 'Rich Text',
tooLarge: 'Too large file, please upload file less than 1MB.',
},
zh: {
successSend: '请查看您的发件箱, 如果失败, 请检查您的余额或稍后重试。',
fromName: '你的名称和地址,名称不填写则使用邮箱地址',
toName: '收件人名称和地址,名称不填写则使用邮箱地址',
subject: '主题',
options: '选项',
edit: '编辑',
preview: '预览',
content: '内容',
send: '发送',
subjectEmpty: '主题不能为空',
toMailEmpty: '收件人地址不能为空',
contentEmpty: '内容不能为空',
requestAccess: '申请权限',
requestAccessTip: '当前还没有可用的发信额度。如果管理员启用了默认额度,会自动发放;否则请申请权限或联系管理员处理。',
send_balance: '剩余发送邮件额度',
text: '文本',
html: 'HTML',
'rich text': '富文本',
tooLarge: '文件过大, 请上传小于1MB的文件。',
}
}
});
const { t } = useScopedI18n('views.index.SendMail')
const contentTypes = [
{ label: t('text'), value: 'text' },

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted, computed, watch, onBeforeUnmount } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useMessage } from 'naive-ui'
import {
ExitToAppFilled,
@@ -30,42 +30,7 @@ const showAccountSettingsCard = ref(false)
const currentAutoRefreshInterval = ref(60)
const timer = ref(null)
const { t } = useI18n({
messages: {
en: {
exitSimpleIndex: 'Exit Simple',
copyAddress: 'Copy',
addressCopied: 'Address copied successfully',
refreshMails: 'Refresh',
noMails: 'No mails found',
prevPage: 'Previous',
nextPage: 'Next',
refreshSuccess: 'Mails refreshed successfully',
mailCount: '{current} / {total} emails',
accountSettings: "Account Settings",
addressCredential: 'Mail Address Credential',
addressCredentialTip: 'Please copy the Mail Address Credential and you can use it to login',
deleteSuccess: 'Mail deleted successfully',
refreshAfter: 'Refresh After {msg} Seconds',
},
zh: {
exitSimpleIndex: '退出极简',
copyAddress: '复制',
addressCopied: '地址复制成功',
refreshMails: '刷新',
noMails: '暂无邮件',
prevPage: '上一页',
nextPage: '下一页',
refreshSuccess: '邮件刷新成功',
mailCount: '{current} / {total} 封邮件',
accountSettings: "账户设置",
addressCredential: '邮箱地址凭证',
addressCredentialTip: '请复制邮箱地址凭证,你可以使用它登录你的邮箱。',
deleteSuccess: '邮件删除成功',
refreshAfter: '{msg}秒后刷新',
}
}
})
const { t } = useScopedI18n('views.index.SimpleIndex')
// 复制地址
const copyAddress = async () => {

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, h, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { NPopconfirm, NButton } from 'naive-ui'
// @ts-ignore
@@ -14,28 +14,7 @@ const { jwt, telegramApp } = useGlobalState()
// @ts-ignore
const message = useMessage()
const { t } = useI18n({
messages: {
en: {
success: 'success',
address: 'Address',
actions: 'Actions',
changeMailAddress: 'Change Mail Address',
unbindMailAddress: 'Unbind Mail Address',
bind: 'Bind',
bindAddressSuccess: 'Bind Address Success',
},
zh: {
success: '成功',
address: '地址',
actions: '操作',
changeMailAddress: '切换邮箱地址',
unbindMailAddress: '解绑邮箱地址',
bind: '绑定',
bindAddressSuccess: '绑定地址成功',
}
}
});
const { t } = useScopedI18n('views.index.TelegramAddress')
const data = ref([]);

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, h, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router';
import { NBadge, NPopconfirm, NButton } from 'naive-ui'
@@ -14,40 +14,7 @@ const { jwt } = useGlobalState()
const message = useMessage()
const router = useRouter()
const { locale, t } = useI18n({
messages: {
en: {
success: 'success',
name: 'Name',
mail_count: 'Mail Count',
send_count: 'Send Count',
actions: 'Actions',
changeMailAddress: 'Change Address',
unbindAddress: 'Unbind Address',
unbindAddressTip: 'Before unbinding, please switch to this email address and save the email address credential.',
transferAddress: 'Transfer Address',
targetUserEmail: 'Target User Email',
transferAddressTip: 'Transfer address to another user will remove the address from your account and transfer it to another user. Are you sure to transfer the address?',
address: 'Address',
create_or_bind: 'Create or Bind',
},
zh: {
success: '成功',
name: '名称',
mail_count: '邮件数量',
send_count: '发送数量',
actions: '操作',
changeMailAddress: '切换地址',
unbindAddress: '解绑地址',
unbindAddressTip: '解绑前请切换到此邮箱地址并保存邮箱地址凭证。',
transferAddress: '转移地址',
targetUserEmail: '目标用户邮箱',
transferAddressTip: '转移地址到其他用户将会从你的账户中移除此地址并转移给其他用户。确定要转移地址吗?',
address: '地址',
create_or_bind: '创建或绑定',
}
}
});
const { locale, t } = useScopedI18n('views.user.AddressManagement')
const data = ref([])
const showTranferAddress = ref(false)

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router'
import { useGlobalState } from '../../store'
@@ -8,16 +8,7 @@ import Login from '../common/Login.vue'
const { userJwt, userSettings, } = useGlobalState()
const { t } = useI18n({
messages: {
en: {
logout: 'Logout',
},
zh: {
logout: '退出登录',
}
}
});
const { t } = useScopedI18n('views.user.BindAddress')
const fetchData = async () => {
}

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRouter } from 'vue-router'
import { useGlobalState } from '../../store'
@@ -14,19 +14,7 @@ const {
userSettings, userJwt, userOpenSettings
} = useGlobalState()
const { t } = useI18n({
messages: {
en: {
currentUser: 'Current Login User',
fetchUserSettingsError: 'Login password is invalid or account not exist, it may be network connection issue, please try again later.',
},
zh: {
currentUser: '当前登录用户',
fetchUserSettingsError: '登录信息已过期或账号不存在,也可能是网络连接异常,请稍后再尝试。',
}
}
});
const { t } = useScopedI18n('views.user.UserBar')
onMounted(async () => {

View File

@@ -1,7 +1,7 @@
<script setup>
import { useMessage } from 'naive-ui'
import { onMounted, ref } from "vue";
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { KeyFilled } from '@vicons/material'
import { api } from '../../api';
@@ -17,50 +17,7 @@ const {
} = useGlobalState()
const message = useMessage();
const { t } = useI18n({
messages: {
en: {
login: 'Login',
register: 'Register',
email: 'Email',
password: 'Password',
verifyCode: 'Verification Code',
verifyCodeSent: 'Verification Code Sent, expires in {timeout} seconds',
waitforVerifyCode: 'Wait for {timeout} seconds',
sendVerificationCode: 'Send Verification Code',
forgotPassword: 'Forgot Password',
cannotForgotPassword: 'Mail verification is disabled or register is disabled, cannot reset password, please contact administrator',
resetPassword: 'Reset Password',
pleaseInput: 'Please input email and password',
pleaseInputEmail: 'Please input email',
pleaseInputCode: 'Please input code',
pleaseCompleteTurnstile: 'Please complete turnstile',
pleaseLogin: 'Please login',
loginWithPasskey: 'Login with Passkey',
loginWith: 'Login with {provider}',
},
zh: {
login: '登录',
register: '注册',
email: '邮箱',
password: '密码',
verifyCode: '验证码',
sendVerificationCode: '发送验证码',
verifyCodeSent: '验证码已发送, {timeout} 秒后失效',
waitforVerifyCode: '等待{timeout}秒',
forgotPassword: '忘记密码',
cannotForgotPassword: '未开启邮箱验证或未开启注册功能,无法重置密码,请联系管理员',
resetPassword: '重置密码',
pleaseInput: '请输入邮箱和密码',
pleaseInputEmail: '请输入邮箱',
pleaseInputCode: '请输入验证码',
pleaseCompleteTurnstile: '请完成人机验证',
pleaseLogin: '请登录',
loginWithPasskey: '使用 Passkey 登录',
loginWith: '使用 {provider} 登录',
}
}
});
const { t } = useScopedI18n('views.user.UserLogin')
const tabValue = ref("signin");
const showModal = ref(false);

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { api } from '../../api'
import { useGlobalState } from '../../store'
@@ -9,18 +9,7 @@ import MailBox from '../../components/MailBox.vue';
const message = useMessage()
const { openSettings } = useGlobalState()
const { t } = useI18n({
messages: {
en: {
addressQueryTip: 'Leave blank to query all addresses',
query: 'Query',
},
zh: {
addressQueryTip: '留空查询所有地址',
query: '查询',
}
}
});
const { t } = useScopedI18n('views.user.UserMailBox')
const mailBoxKey = ref("")
const addressFilter = ref();

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { useRoute, useRouter } from 'vue-router';
import { useGlobalState } from '../../store'
@@ -14,20 +14,7 @@ const message = useMessage();
const route = useRoute()
const router = useRouter()
const errorInfo = ref('')
const { t } = useI18n({
messages: {
en: {
logging: 'Logging in...',
stateNotMatch: 'state not match',
codeNotFound: 'code not found',
},
zh: {
logging: '登录中...',
stateNotMatch: 'state 不匹配',
codeNotFound: '未找到授权码',
}
}
});
const { t } = useScopedI18n('views.user.UserOauth2Callback')
onMounted(async () => {
try {

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useScopedI18n } from '@/i18n/app'
import { startRegistration } from '@simplewebauthn/browser';
import { NButton, NPopconfirm } from 'naive-ui'
@@ -18,44 +18,7 @@ const showRenamePasskey = ref(false)
const currentPasskeyId = ref(null)
const currentPasskeyName = ref('')
const { t } = useI18n({
messages: {
en: {
logout: 'Logout',
logoutConfirm: 'Are you sure you want to logout?',
passordTip: 'The server will only receive the hash value of the password, and will not receive the plaintext password, so it cannot view or retrieve your password. If the administrator enables email verification, you can reset the password in incognito mode',
createPasskey: 'Create Passkey',
showPasskeyList: 'Show Passkey List',
passkeyCreated: 'Passkey created successfully',
passkeyNamePlaceholder: 'Please enter the passkey name or leave it empty to generate a random one',
renamePasskey: 'Rename Passkey',
deletePasskey: 'Delete Passkey',
passkey_name: 'Passkey Name',
created_at: 'Created At',
updated_at: 'Updated At',
actions: 'Actions',
renamePasskey: 'Rename Passkey',
renamePasskeyNamePlaceholder: 'Please enter the new passkey name',
},
zh: {
logout: '退出登录',
logoutConfirm: '确定要退出登录吗?',
passordTip: '服务器只会接收到密码的哈希值,不会接收到明文密码,因此无法查看或者找回您的密码, 如果管理员启用了邮件验证您可以在无痕模式重置密码',
createPasskey: '创建 Passkey',
showPasskeyList: '查看 Passkey 列表',
passkeyCreated: 'Passkey 创建成功',
passkeyNamePlaceholder: '请输入 Passkey 名称或者留空自动生成',
renamePasskey: '重命名 Passkey',
deletePasskey: '删除 Passkey',
passkey_name: 'Passkey 名称',
created_at: '创建时间',
updated_at: '更新时间',
actions: '操作',
renamePasskey: '重命名 Passkey',
renamePasskeyNamePlaceholder: '请输入新的 Passkey 名称',
}
}
});
const { t } = useScopedI18n('views.user.UserSettings')
const logout = async () => {

View File

@@ -61,9 +61,12 @@ export default defineConfig({
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
alias: [
{
find: '@',
replacement: fileURLToPath(new URL('./src', import.meta.url))
}
]
},
define: {
'import.meta.env.PACKAGE_VERSION': JSON.stringify(process.env.npm_package_version),