应用市场菜单增加多语言支持

This commit is contained in:
jingrow 2025-12-29 05:06:03 +08:00
parent fc4152cf19
commit 2a350e93ce
22 changed files with 531 additions and 339 deletions

View File

@ -28,7 +28,7 @@
v-if="rows.length === 0"
class="text-center text-sm leading-10 text-gray-500"
>
未找到结果
{{ $t('No results found') }}
</div>
<ListRow v-for="(row, i) in rows" :row="row" :key="row.name">
<template v-slot="{ column, item }">

View File

@ -1,23 +1,23 @@
<template>
<Card
class="md:col-span-2"
title="App Descriptions"
subtitle="Details about your app"
:title="$t('App Descriptions')"
:subtitle="$t('Details about your app')"
>
<div class="divide-y" v-if="app">
<ListItem title="Summary" :description="$sanitize(app.description)">
<ListItem :title="$t('Summary')" :description="$sanitize(app.description)">
<template #actions>
<Button icon-left="edit" @click="showEditSummaryDialog = true">
Edit
{{ $t('Edit') }}
</Button>
</template>
</ListItem>
<Dialog
:options="{
title: 'Update App Summary',
title: $t('Update App Summary'),
actions: [
{
label: 'Save Changes',
label: $t('Save Changes'),
variant: 'solid',
loading: $resources.updateAppSummary.loading,
onClick: () => $resources.updateAppSummary.submit()
@ -28,7 +28,7 @@
>
<template v-slot:body-content>
<FormControl
label="Summary of the app"
:label="$t('Summary of the app')"
type="textarea"
v-model="app.description"
/>
@ -39,10 +39,10 @@
</template>
</Dialog>
<div class="py-3">
<ListItem title="Long Description">
<ListItem :title="$t('Long Description')">
<template #actions>
<Button icon-left="edit" @click="showEditDescriptionDialog = true">
Edit
{{ $t('Edit') }}
</Button>
</template>
</ListItem>
@ -53,11 +53,11 @@
></div>
<Dialog
:options="{
title: 'Update App Description',
title: $t('Update App Description'),
size: '5xl',
actions: [
{
label: 'Save Changes',
label: $t('Save Changes'),
variant: 'solid',
loading: $resources.updateAppDescription.loading,
onClick: () => $resources.updateAppDescription.submit()
@ -91,7 +91,7 @@
:loading="$resources.fetchReadme.loading"
@click="$resources.fetchReadme.submit()"
>
Fetch Readme
{{ $t('Fetch Readme') }}
</Button>
</template>
</Card>
@ -122,7 +122,7 @@ export default {
summary: description
},
onSuccess() {
this.notifySuccess('App Summary Updated!');
this.notifySuccess(this.$t('App Summary Updated!'));
this.showEditSummaryDialog = false;
}
};
@ -136,7 +136,7 @@ export default {
description: long_description
},
onSuccess() {
this.notifySuccess('App Description Updated!');
this.notifySuccess(this.$t('App Description Updated!'));
this.showEditDescriptionDialog = false;
}
};
@ -147,8 +147,8 @@ export default {
params: { name: this.app.name },
onSuccess() {
notify({
title: 'Successfully fetched latest readme',
message: 'Long description updated!',
title: this.$t('Successfully fetched latest readme'),
message: this.$t('Long description updated!'),
icon: 'check',
color: 'green'
});

View File

@ -3,18 +3,18 @@
<div>
<div class="flex justify-between border-b pb-4">
<div>
<h2 class="text-lg font-medium text-gray-900">应用资料</h2>
<h2 class="text-lg font-medium text-gray-900">{{ $t('App Profile') }}</h2>
<p class="mt-1 text-sm leading-6 text-gray-600">
这些信息将公开显示在市场上请确保输入正确的信息没有损坏的链接和图片
{{ $t('This information will be publicly displayed on the marketplace. Please ensure you enter correct information, with no broken links and images.') }}
</p>
</div>
<Button :variant="editing ? 'solid' : 'subtle'" @click="updateListing"
>保存</Button
>{{ $t('Save') }}</Button
>
</div>
<div class="grid grid-cols-1 gap-x-5 border-b py-6 md:grid-cols-2">
<div class="border-r pr-6">
<span class="text-base font-medium">资料</span>
<span class="text-base font-medium">{{ $t('Profile') }}</span>
<div class="group relative my-4 flex">
<div class="flex flex-col">
<Avatar
@ -25,7 +25,7 @@
/>
</div>
<FileUploader
@success="() => imageAddSuccess('资料照片已更新')"
@success="() => imageAddSuccess($t('Profile photo updated'))"
@failure="imageAddFailure"
fileTypes="image/*"
:upload-args="{
@ -44,7 +44,7 @@
:class="{ 'opacity-50': uploading }"
>
<span v-if="uploading">{{ progress }}%</span>
<span v-else>编辑</span>
<span v-else>{{ $t('Edit') }}</span>
</button>
</div>
</template>
@ -53,18 +53,18 @@
<div class="pb-8 sm:col-span-4">
<FormControl
class="mt-4"
label="标题"
:label="$t('Title')"
type="text"
@input="editing = true"
v-model="marketplaceApp.title"
/>
</div>
<div class="sm:col-span-4">
<span class="text-base font-medium">链接</span>
<span class="text-base font-medium">{{ $t('Links') }}</span>
<div>
<FormControl
class="mt-4"
label="文档"
:label="$t('Documentation')"
type="text"
@blur="validateLink('documentation')"
@input="editing = true"
@ -72,7 +72,7 @@
/>
<FormControl
class="mt-4"
label="网站"
:label="$t('Website')"
type="text"
@blur="validateLink('website')"
@input="editing = true"
@ -80,7 +80,7 @@
/>
<FormControl
class="mt-4"
label="支持"
:label="$t('Support')"
type="text"
@blur="validateLink('support')"
@input="editing = true"
@ -88,7 +88,7 @@
/>
<FormControl
class="mt-4"
label="服务条款"
:label="$t('Terms of Service')"
type="text"
@blur="validateLink('terms_of_service')"
@input="editing = true"
@ -96,7 +96,7 @@
/>
<FormControl
class="mt-4"
label="隐私政策"
:label="$t('Privacy Policy')"
type="text"
@blur="validateLink('privacy_policy')"
@input="editing = true"
@ -107,10 +107,10 @@
</div>
<div class="hidden md:block">
<div class="flex w-full">
<span class="text-base font-medium">截图和视频</span>
<span class="text-base font-medium">{{ $t('Screenshots and Videos') }}</span>
<FileUploader
class="ml-auto"
@success="() => imageAddSuccess('已添加截图')"
@success="() => imageAddSuccess($t('Screenshot added'))"
@failure="imageAddFailure"
fileTypes="image/*"
:upload-args="{
@ -126,7 +126,7 @@
:loading="uploading"
@click="openFileSelector()"
icon-left="plus"
label="添加"
:label="$t('Add')"
>
</Button>
</template>
@ -152,16 +152,16 @@
</div>
</div>
<div class="mt-6">
<span class="text-base font-medium">描述</span>
<span class="text-base font-medium">{{ $t('Description') }}</span>
<FormControl
class="mt-4"
label="摘要"
:label="$t('Summary')"
type="textarea"
@input="editing = true"
v-model="marketplaceApp.description"
/>
<div class="mt-4">
<span class="text-xs text-gray-600">描述</span>
<span class="text-xs text-gray-600">{{ $t('Description') }}</span>
<TextEditor
class="mt-1 block w-full rounded border border-gray-100 bg-gray-100 px-2 py-1.5 text-base text-gray-800 placeholder-gray-500 transition-colors hover:border-gray-200 hover:bg-gray-200 focus:border-gray-500 focus:bg-white focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-gray-400"
ref="textEditor"
@ -258,21 +258,21 @@ export default {
toast.promise(this.$resources.updateListing.submit(), {
success: () => {
this.editing = false;
return '更新成功';
return this.$t('Listing updated successfully');
},
loading: '正在更新列表...',
loading: this.$t('Updating listing...'),
error: (err) => {
return err.messages?.length
? err.messages.join('\n')
: err.message || '更新列表失败';
: err.message || this.$t('Failed to update listing');
},
});
},
dropdownOptions(image) {
return [
{ label: '查看', onClick: () => window.open(image) },
{ label: this.$t('View'), onClick: () => window.open(image) },
{
label: '删除',
label: this.$t('Delete'),
onClick: () => {
toast.promise(
this.$resources.removeScreenshot.submit({
@ -280,15 +280,15 @@ onClick: () => {
file: image,
}),
{
loading: '正在删除截图...',
loading: this.$t('Deleting screenshot...'),
success: () => {
this.$resources.listingData.reload();
return '截图删除成功';
return this.$t('Screenshot deleted successfully');
},
error: (err) => {
return err.messages?.length
? err.messages.join('\n')
: err.message || '删除截图失败';
: err.message || this.$t('Failed to delete screenshot');
},
},
);

View File

@ -1,5 +1,5 @@
<template>
<Card title="App Profile" subtitle="Your app's primary profile">
<Card :title="$t('App Profile')" :subtitle="$t('Your app's primary profile')">
<div class="flex items-center border-b pb-6">
<div class="group relative">
<Avatar
@ -26,12 +26,12 @@
:class="{ 'opacity-50': uploading }"
>
<span v-if="uploading">{{ progress }}%</span>
<span v-else>Edit</span>
<span v-else>{{ $t('Edit') }}</span>
</button>
<button
class="absolute bottom-0 left-0 grid w-full place-items-center rounded-md bg-gray-900 text-xs font-semibold text-white text-opacity-70 opacity-80 group-hover:opacity-0"
>
<span>Edit</span>
<span>{{ $t('Edit') }}</span>
</button>
</div>
</template>
@ -45,12 +45,12 @@
</div>
<div class="ml-auto">
<Button icon-left="edit" @click="showAppProfileEditDialog = true">
Edit
{{ $t('Edit') }}
</Button>
</div>
</div>
<div class="mt-8 flex justify-between">
<p class="text-lg font-semibold">Published Versions</p>
<p class="text-lg font-semibold">{{ $t('Published Versions') }}</p>
<Button
icon-left="plus"
@click="
@ -59,7 +59,7 @@
}
"
>
Add
{{ $t('Add') }}
</Button>
</div>
<div class="divide-y" v-if="app">
@ -83,10 +83,10 @@
<Dialog
:options="{
title: 'Update App Title',
title: $t('Update App Title'),
actions: [
{
label: 'Save Changes',
label: $t('Save Changes'),
variant: 'solid',
loading: $resources.updateAppTitle.loading,
onClick: () => $resources.updateAppTitle.submit()
@ -96,7 +96,7 @@
v-model="showAppProfileEditDialog"
>
<template v-slot:body-content>
<FormControl label="App Title" v-model="app.title" />
<FormControl :label="$t('App Title')" v-model="app.title" />
<ErrorMessage class="mt-4" :message="$resources.updateAppTitle.error" />
</template>
@ -206,7 +206,7 @@ export default {
dropdownItems(source) {
return [
{
label: 'Change Branch',
label: this.$t('Change Branch'),
onClick: () => {
this.selectedSource = source.source;
this.selectedVersion = source.version;
@ -215,7 +215,7 @@ export default {
}
},
{
label: 'Remove',
label: this.$t('Remove'),
onClick: () => {
this.$resources.removeVersion.submit({
name: this.app.name,
@ -227,7 +227,7 @@ export default {
},
notifySuccess() {
notify({
title: 'App Profile Updated!',
title: this.$t('App Profile Updated!'),
icon: 'check',
color: 'green'
});

View File

@ -128,7 +128,7 @@ export default {
disabled: enforce2FA,
},
{
name: '应用市场',
name: this.$t('Marketplace'),
icon: () => h(App),
route: '/apps',
isActive: routeName.startsWith('Marketplace'),

View File

@ -427,7 +427,7 @@ this.$socket.emit('pagetype_unsubscribe', pagetype);
);
},
emptyStateMessage() {
return this.options.emptyStateMessage || '未找到结果';
return this.options.emptyStateMessage || this.$t('No results found');
},
banner() {
if (this.options.banner) {

View File

@ -2,14 +2,14 @@
<Dialog
v-model="show"
:options="{
title: '应用发布前需要完成的步骤',
title: $t('Steps to complete before publishing your app'),
size: '2xl'
}"
>
<template #body-content>
<div v-if="appDoc.pg.review_stage === 'Ready for Review'">
<p class="text-p-base text-gray-700">
您的应用已发送给我们的团队进行审核请等待审核完成
{{ $t('Your app has been sent to our team for review. Please wait for the review to complete.') }}
</p>
</div>
<ObjectList v-else :options="listOptions" />
@ -18,7 +18,7 @@
<Button
class="w-full"
variant="solid"
label="标记应用为待审核"
:label="$t('Mark app as ready for review')"
:loading="appDoc.markAppReadyForReview.loading"
:disabled="$resources.reviewSteps.data.some(step => !step.completed)"
@click="appDoc.markAppReadyForReview.submit"
@ -64,11 +64,11 @@ export default {
hideControls: true,
columns: [
{
label: '步骤',
label: this.$t('Step'),
fieldname: 'step'
},
{
label: '已完成',
label: this.$t('Completed'),
fieldname: 'completed',
type: 'Icon',
width: 0.3,
@ -87,7 +87,7 @@ export default {
route += row.step.includes('Publish') ? 'versions' : 'listing';
return {
label: '查看',
label: this.$t('View'),
variant: 'ghost',
route
};

View File

@ -2,10 +2,10 @@
<Dialog
v-if="source"
:options="{
title: '更改 ' + app + ' 的 ' + version + ' 分支',
title: $t('Change {app} {version} branch', { app, version }),
actions: [
{
label: '更改分支',
label: this.$t('Change Branch'),
variant: 'solid',
loading: $resources.changeBranch.loading,
onClick: () => changeBranch()
@ -70,8 +70,8 @@ export default {
to_branch: this.selectedBranch
},
validate() {
if (this.selectedBranch == this.app.branch) {
throw new DashboardError('请选择一个不同的分支');
if (this.selectedBranch == this.activeBranch) {
throw new DashboardError(this.$t('Please select a different branch'));
}
}
};
@ -80,11 +80,11 @@ export default {
methods: {
changeBranch() {
toast.promise(this.$resources.changeBranch.submit(), {
loading: '正在更新版本的分支...',
loading: this.$t('Updating version branch...'),
success: () => {
this.show = false;
this.$emit('branch-changed');
return '分支更改成功';
return this.$t('Branch changed successfully');
},
error: e => getToastErrorMessage(e)
});

View File

@ -3,7 +3,7 @@
<div class="col-span-2 mb-5 rounded-md border">
<div class="grid grid-cols-2 lg:grid-cols-5">
<div class="border-b border-r p-5 lg:border-b-0">
<div class="text-base text-gray-700">总安装量</div>
<div class="text-base text-gray-700">{{ $t('Total Installs') }}</div>
<div class="mt-2 flex items-start justify-between">
<div>
<div class="leading-4">
@ -16,7 +16,7 @@
</div>
<div class="border-b border-r p-5 lg:border-b-0">
<div class="text-base text-gray-700">激活站点</div>
<div class="text-base text-gray-700">{{ $t('Active Sites') }}</div>
<div class="mt-2 flex items-start justify-between">
<div>
<div class="leading-4">
@ -29,7 +29,7 @@
</div>
<div class="border-b border-r p-5 lg:border-b-0">
<div class="text-base text-gray-700">激活站点分组</div>
<div class="text-base text-gray-700">{{ $t('Active Benches') }}</div>
<div class="mt-2 flex items-start justify-between">
<div>
<div class="leading-4">
@ -42,7 +42,7 @@
</div>
<div class="border-b border-r p-5 lg:border-b-0">
<div class="text-base text-gray-700">每周安装量</div>
<div class="text-base text-gray-700">{{ $t('Weekly Installs') }}</div>
<div class="mt-2 flex items-start justify-between">
<div>
<div class="leading-4">
@ -55,7 +55,7 @@
</div>
<div class="border-b border-r p-5 lg:border-b-0">
<div class="text-base text-gray-700">总收入</div>
<div class="text-base text-gray-700">{{ $t('Total Earnings') }}</div>
<div class="mt-2 flex items-start justify-between">
<div>
<div class="leading-4">
@ -74,7 +74,7 @@
class="grid grid-cols-1 gap-5 lg:grid-cols-2"
>
<LineChart
title="页面浏览量"
:title="$t('Pageviews')"
type="time"
key="Page Views"
:data="
@ -88,7 +88,7 @@
:error="$resources.plausible_analytics.error"
/>
<LineChart
title="独立访客"
:title="$t('Unique Visitors')"
type="time"
key="Unique Visitors"
:data="
@ -105,7 +105,7 @@
<LineChart
v-if="$resources.plausible_analytics.data"
class="mt-5"
title="每周安装量"
:title="$t('Weekly Installs')"
type="time"
key="Weekly Installs"
:data="

View File

@ -1,11 +1,11 @@
<template>
<Dialog
:options="{
title: '添加新的应用市场应用',
title: $t('Add New Marketplace App'),
size: 'xl',
actions: [
{
label: '添加应用',
label: $t('Add App'),
variant: 'solid',
disabled: (!appValidated && !selectedVersion) || !this.app.is_public,
onClick: addApp
@ -29,7 +29,7 @@
v-if="selectedBranch"
class="mt-4"
type="autocomplete"
label="选择版本"
:label="$t('Select Version')"
:options="{ pagetype: 'Jingrow Version', filters: { public: 1 } }"
v-model="selectedVersion"
/>
@ -39,7 +39,7 @@
class="flex text-base text-gray-700"
>
<LoadingIndicator class="mr-2 w-4" />
正在验证应用...
{{ $t('Validating app...') }}
</div>
<div v-if="appValidated" class="flex text-base text-gray-700">
<div v-if="this.app.is_public === true" class="flex gap-1">
@ -48,7 +48,7 @@
name="check"
:stroke-width="3"
/>
找到 {{ this.app.title }} ({{ this.app.name }})
{{ $t('Found {title} ({name})', { title: this.app.title, name: this.app.name }) }}
</div>
<div v-else-if="this.app.is_public === false">
<div class="flex text-base text-gray-700 gap-1">
@ -56,12 +56,12 @@
class="w-4 p-0.5 text-white rounded bg-red-500"
name="x"
/>
Github 仓库是私有的
{{ $t('Github repository is private.') }}
<Link
href="https://jingrow.com/marketplace/terms"
class="font-medium"
>
条款和政策
{{ $t('Terms and Policies') }}
</Link>
</div>
</div>
@ -140,14 +140,14 @@ export default {
methods: {
addApp() {
toast.promise(this.$resources.addApp.submit(), {
loading: '正在添加新应用...',
loading: this.$t('Adding new app...'),
success: () => {
this.show = false;
this.$router.push({
name: 'Marketplace App Detail Listing',
params: { name: this.app.name }
});
return '新应用已添加';
return this.$t('New app added');
},
error: e => getToastErrorMessage(e)
});

View File

@ -7,6 +7,7 @@ import { duration, planTitle, userCurrency } from '../utils/format';
import { trialDays } from '../utils/site';
import { getJobsTab } from './common/jobs';
import { tagTab } from './common/tags';
import { t } from '../utils/i18n';
export default {
pagetype: 'Jsite Domain',
@ -19,7 +20,7 @@ export default {
},
list: {
route: '/domains',
title: '域名',
title: t('Domains'),
fields: [
'name',
'domain',
@ -39,12 +40,12 @@ export default {
return [
{
type: 'select',
label: '状态',
label: t('Status'),
fieldname: 'status',
options: [
{ label: '', value: '' },
{ label: '正常', value: 'ok' },
{ label: '锁定', value: 'clienthold' }
{ label: t('Normal'), value: 'ok' },
{ label: t('Locked'), value: 'clienthold' }
]
}
];
@ -53,7 +54,7 @@ export default {
searchField: 'domain',
columns: [
{
label: '域名',
label: t('Domain'),
fieldname: 'domain',
width: 2,
class: 'font-medium',
@ -62,20 +63,20 @@ export default {
}
},
{
label: '状态',
label: t('Status'),
fieldname: 'status',
type: 'Badge',
width: 0.8,
format(value) {
const statusMap = {
'ok': '正常',
'clienthold': '锁定'
'ok': t('Normal'),
'clienthold': t('Locked')
};
return statusMap[value] || value;
}
},
{
label: '注册时间',
label: t('Registration Date'),
fieldname: 'registration_date',
format(value) {
if (!value) return '-';
@ -83,7 +84,7 @@ export default {
}
},
{
label: '到期时间',
label: t('Expiry Date'),
fieldname: 'end_date',
format(value) {
if (!value) return '-';
@ -93,7 +94,7 @@ export default {
],
primaryAction({ listResource: domains }) {
return {
label: '新建域名',
label: t('New Domain'),
variant: 'solid',
slots: {
prefix: icon('plus')
@ -106,11 +107,11 @@ export default {
statusBadge({ documentResource: domain }) {
const status = domain.pg?.status;
const statusMap = {
'Pending': '待处理',
'Active': '正常',
'Expired': '已过期',
'Suspended': '已暂停',
'Cancelled': '已取消'
'Pending': t('Pending'),
'Active': t('Normal'),
'Expired': t('Expired'),
'Suspended': t('Suspended'),
'Cancelled': t('Cancelled')
};
return {
label: statusMap[status] || status
@ -119,7 +120,7 @@ export default {
breadcrumbs({ documentResource: domain }) {
return [
{
label: '域名',
label: t('Domains'),
route: '/domains'
},
{
@ -133,7 +134,7 @@ export default {
const actions = [
{
label: '续费',
label: t('Renew'),
icon: 'refresh-cw',
onClick() {
domain.renew.submit();
@ -141,14 +142,14 @@ export default {
condition: () => domain.pg?.status === 'Active'
},
{
label: '重命名',
label: t('Rename'),
icon: 'edit-3',
onClick() {
domain.rename.submit();
}
},
{
label: '删除',
label: t('Delete'),
icon: 'trash-2',
onClick() {
domain.dropDomain.submit();
@ -161,10 +162,10 @@ export default {
},
detail: {
route: '/domains/:name',
title: '域名详细信息',
title: t('Domain Details'),
tabs: [
{
label: '概览',
label: t('Overview'),
route: '',
type: 'Component',
component: defineAsyncComponent(() => import('../components/JsiteDomainOverview.vue')),
@ -173,7 +174,7 @@ export default {
}
},
{
label: '域名解析',
label: t('DNS Records'),
route: 'dns',
type: 'Component',
component: defineAsyncComponent(() => import('../components/JsiteDomainDNSRecords.vue')),
@ -182,7 +183,7 @@ export default {
}
},
{
label: '所有者模板',
label: t('Owner Template'),
route: 'owners',
type: 'Component',
component: defineAsyncComponent(() => import('../components/DomainOwner.vue')),
@ -193,7 +194,7 @@ export default {
],
fields: [
{
label: '基本信息',
label: t('Basic Information'),
fields: [
'domain',
'status',
@ -204,7 +205,7 @@ export default {
]
},
{
label: '价格信息',
label: t('Pricing Information'),
fields: [
'price',
'period',
@ -213,7 +214,7 @@ export default {
]
},
{
label: 'DNS设置',
label: t('DNS Settings'),
fields: [
'dns_host1',
'dns_host2',
@ -224,7 +225,7 @@ export default {
]
},
{
label: '其他信息',
label: t('Other Information'),
fields: [
'description',
'whois_protection'

View File

@ -11,6 +11,7 @@ import { userCurrency, currency } from '../utils/format';
import { getToastErrorMessage } from '../utils/toast';
import { isMobile } from '../utils/device';
import router from '../router';
import { t } from '../utils/i18n';
export default {
pagetype: 'Marketplace App',
@ -25,11 +26,11 @@ export default {
},
list: {
route: '/apps',
title: '应用市场',
title: t('Marketplace'),
fields: ['image', 'title', 'status', 'description'],
columns: [
{
label: '应用',
label: t('App'),
fieldname: 'title',
class: 'font-medium',
width: 0.3,
@ -51,20 +52,20 @@ export default {
}
},
{
label: '状态',
label: t('Status'),
type: 'Badge',
fieldname: 'status',
width: 0.3
},
{
label: '描述',
label: t('Description'),
fieldname: 'description',
width: 1.0
}
],
primaryAction() {
return {
label: '新建应用',
label: t('New App'),
variant: 'solid',
slots: {
prefix: icon('plus')
@ -96,7 +97,7 @@ export default {
},
tabs: [
{
label: '分析',
label: t('Analytics'),
icon: icon('bar-chart-2'),
route: 'analytics',
type: 'Component',
@ -108,7 +109,7 @@ export default {
}
},
{
label: '列表',
label: t('Listing'),
icon: icon('shopping-cart'),
route: 'listing',
type: 'Component',
@ -120,7 +121,7 @@ export default {
}
},
{
label: '版本',
label: t('Versions'),
icon: icon('package'),
route: 'versions',
type: 'list',
@ -140,24 +141,24 @@ export default {
],
columns: [
{
label: '版本',
label: t('Version'),
fieldname: 'version',
width: 0.5
},
{
label: '来源',
label: t('Source'),
fieldname: 'source',
width: 0.5
},
{
label: '仓库',
label: t('Repository'),
width: 0.5,
format: (value, row) => {
return `${row.repository_owner}/${row.repository}`;
}
},
{
label: '分支',
label: t('Branch'),
fieldname: 'branch',
type: 'Badge',
width: 0.5
@ -165,7 +166,7 @@ export default {
],
primaryAction({ listResource: versions, documentResource: app }) {
return {
label: '新建版本',
label: t('New Version'),
slots: {
prefix: icon('plus')
},
@ -175,7 +176,7 @@ export default {
GenericDialog,
{
options: {
title: `${app.pg.title} 添加版本支持`,
title: t('Add version support for {appTitle}', { appTitle: app.pg.title }),
size: '4xl'
}
},
@ -183,16 +184,16 @@ export default {
default: () =>
h(ObjectList, {
options: {
label: '版本',
label: t('Version'),
fieldname: 'version',
fieldtype: 'ListSelection',
columns: [
{
label: '版本',
label: t('Version'),
fieldname: 'version'
},
{
label: '分支',
label: t('Branch'),
type: 'Select',
fieldname: 'branch',
format: (value, row) => {
@ -214,7 +215,7 @@ export default {
width: '5rem',
Button({ row, listResource: versionsOptions }) {
return {
label: '添加',
label: t('Add'),
onClick() {
if (app.addVersion.loading) return;
toast.promise(
@ -223,11 +224,11 @@ export default {
branch: row.selectedOption
}),
{
loading: '正在添加新版本...',
loading: t('Adding new version...'),
success: () => {
versions.reload();
versionsOptions.reload();
return '新版本已添加';
return t('New version added');
},
error: e => getToastErrorMessage(e)
}
@ -257,7 +258,7 @@ export default {
rowActions({ row, listResource: versions, documentResource: app }) {
return [
{
label: '显示发布',
label: t('Show Releases'),
slots: {
prefix: icon('plus')
},
@ -266,7 +267,7 @@ export default {
}
},
{
label: '更改分支',
label: t('Change Branch'),
onClick() {
renderDialog(
h(ChangeAppBranchDialog, {
@ -282,15 +283,15 @@ export default {
}
},
{
label: '移除版本',
label: t('Remove Version'),
onClick() {
toast.promise(
app.removeVersion.submit({ version: row.version }),
{
loading: '正在移除版本...',
loading: t('Removing version...'),
success: () => {
versions.reload();
return '版本已成功移除';
return t('Version removed successfully');
},
error: e => getToastErrorMessage(e)
}
@ -302,7 +303,7 @@ export default {
}
},
{
label: '定价',
label: t('Pricing'),
icon: icon('dollar-sign'),
route: 'pricing',
type: 'list',
@ -314,26 +315,26 @@ export default {
fields: ['name', 'title', 'price_cny', 'price_usd', 'enabled'],
columns: [
{
label: '标题',
label: t('Title'),
fieldname: 'title'
},
{
label: '启用',
label: t('Enabled'),
type: 'Badge',
fieldname: 'enabled',
format: value => {
return value == 1 ? '已启用' : '已禁用';
return value == 1 ? t('Enabled') : t('Disabled');
}
},
{
label: '价格 (CNY)',
label: t('Price CNY'),
fieldname: 'price_cny',
format: value => {
return currency(value, 'CNY');
}
},
{
label: '价格 (USD)',
label: t('Price USD'),
fieldname: 'price_usd',
format: value => {
return currency(value, 'USD');
@ -342,7 +343,7 @@ export default {
],
primaryAction({ listResource: plans, documentResource: app }) {
return {
label: '新建计划',
label: t('New Plan'),
slots: {
prefix: icon('plus')
},
@ -361,7 +362,7 @@ export default {
rowActions({ row, listResource: plans, documentResource: app }) {
return [
{
label: '编辑',
label: t('Edit'),
onClick() {
renderDialog(
h(PlansDialog, {
@ -379,7 +380,7 @@ export default {
}
},
{
label: '订阅',
label: t('Subscriptions'),
icon: icon('users'),
route: 'subscription',
type: 'list',
@ -396,30 +397,30 @@ export default {
return [
{
type: 'select',
label: '状态',
label: t('Status'),
class: !isMobile() ? 'w-24' : '',
fieldname: 'enabled',
options: ['', '激活', '禁用']
options: ['', t('Active'), t('Disabled')]
}
];
},
columns: [
{
label: '站点',
label: t('Site'),
fieldname: 'site',
width: 0.6
},
{
label: '状态',
label: t('Status'),
type: 'Badge',
fieldname: 'enabled',
width: 0.3,
format: value => {
return value ? '激活' : '禁用';
return value ? t('Active') : t('Disabled');
}
},
{
label: '价格',
label: t('Price'),
fieldname: 'price',
width: 0.3,
format: value => {
@ -427,11 +428,11 @@ export default {
}
},
{
label: '已激活天数',
label: t('Active Days'),
fieldname: 'active_for',
width: 0.3,
format: value => {
return value + (value == 1 ? ' 天' : ' 天');
return value + (value == 1 ? ` ${t('day')}` : ` ${t('days')}`);
}
}
]
@ -441,7 +442,7 @@ export default {
actions({ documentResource: app }) {
return [
{
label: '在市场中查看',
label: t('View in Marketplace'),
slots: {
prefix: icon('external-link')
},
@ -454,7 +455,7 @@ export default {
}
},
{
label: '指南',
label: t('Guide'),
slots: {
icon: icon('help-circle')
},
@ -467,7 +468,7 @@ export default {
}
},
{
label: '完成列表',
label: t('Complete Listing'),
variant: 'solid',
slots: {
prefix: icon('alert-circle')
@ -486,24 +487,24 @@ export default {
}
},
{
label: '选项',
label: t('Options'),
condition: () => app.pg.status === 'Draft',
options: [
{
label: '删除',
label: t('Delete'),
icon: icon('trash-2'),
condition: () => app.pg.status === 'Draft',
onClick() {
confirmDialog({
title: `删除应用 ${app.pg.title}`,
message: '您确定要删除此应用吗?',
title: t('Delete app {appTitle}', { appTitle: app.pg.title }),
message: t('Are you sure you want to delete this app?'),
onSuccess({ hide }) {
toast.promise(app.delete.submit(), {
loading: '正在删除应用...',
loading: t('Deleting app...'),
success: () => {
hide();
router.push({ name: 'Marketplace App List' });
return '应用删除成功';
return t('App deleted successfully');
},
error: e => getToastErrorMessage(e)
});
@ -524,7 +525,7 @@ function showReleases(row, app) {
GenericDialog,
{
options: {
title: `${app.pg.name}${row.branch} 分支的发布`,
title: t('Releases for {appName} on {branch} branch', { appName: app.pg.name, branch: row.branch }),
size: '6xl'
}
},
@ -532,7 +533,7 @@ function showReleases(row, app) {
default: () =>
h(ObjectList, {
options: {
label: '版本',
label: t('Version'),
type: 'list',
pagetype: 'App Release',
filters: {
@ -543,13 +544,13 @@ function showReleases(row, app) {
orderBy: 'creation desc',
columns: [
{
label: '提交信息',
label: t('Commit Message'),
fieldname: 'message',
class: 'w-64',
width: 0.5
},
{
label: '哈希值',
label: t('Hash'),
fieldname: 'hash',
class: 'w-24',
type: 'Badge',
@ -559,18 +560,18 @@ function showReleases(row, app) {
}
},
{
label: '作者',
label: t('Author'),
fieldname: 'author',
width: 0.2
},
{
label: '状态',
label: t('Status'),
fieldname: 'status',
type: 'Badge',
width: 0.3
},
{
label: '代码审查',
label: t('Code Review'),
type: 'Component',
width: 0.2,
component: ({ row, listResource: releases, app }) => {
@ -580,7 +581,7 @@ function showReleases(row, app) {
row.screening_status === 'Complete'
) {
return h(Button, {
label: '代码审查',
label: t('Code Review'),
variant: 'subtle',
theme: 'blue',
size: 'sm',
@ -589,7 +590,7 @@ function showReleases(row, app) {
});
}
return h(Badge, {
label: row.screening_status || '未开始'
label: row.screening_status || t('Not Started')
});
}
},
@ -605,14 +606,13 @@ function showReleases(row, app) {
let loadingMessage = '';
if (row.status === 'Awaiting Approval') {
label = '取消';
successMessage = '发布已取消';
loadingMessage = '正在取消发布...';
label = t('Cancel');
successMessage = t('Release cancelled');
loadingMessage = t('Cancelling release...');
} else if (row.status === 'Draft') {
label = '提交';
loadingMessage = '正在提交发布以供审批...';
successMessage =
'发布已提交以供审批';
label = t('Submit');
loadingMessage = t('Submitting release for approval...');
successMessage = t('Release submitted for approval');
}
return {

View File

@ -372,15 +372,15 @@ export default {
'Archived': t('Archived'),
'Pending': t('Pending'),
'Installing': t('Installing'),
'Running': '运行中',
'Success': '成功',
'Failure': '失败'
'Running': t('Running'),
'Success': t('Success'),
'Failure': t('Failure')
};
return statusMap[value] || value;
}
},
{
label: '主域名',
label: t('Primary Domain'),
fieldname: 'primary',
type: 'Icon',
Icon(value) {
@ -388,7 +388,7 @@ export default {
},
},
{
label: 'DNS 类型',
label: t('DNS Type'),
fieldname: 'dns_type',
type: 'Badge',
},
@ -396,11 +396,10 @@ export default {
banner({ documentResource: site }) {
if (site.pg.broken_domain_error) {
return {
title:
'获取您的域名的 HTTPS 证书时出错。',
title: t('Error occurred while obtaining HTTPS certificate for your domain.'),
type: 'error',
button: {
label: '查看错误',
label: t('View Error'),
variant: 'outline',
onClick() {
renderDialog(
@ -408,7 +407,7 @@ export default {
GenericDialog,
{
options: {
title: '获取证书时出错',
title: t('Error obtaining certificate'),
size: 'xl',
},
},
@ -432,7 +431,7 @@ export default {
},
primaryAction({ listResource: domains, documentResource: site }) {
return {
label: '添加域名',
label: t('Add Domain'),
slots: {
prefix: icon('plus'),
},
@ -451,12 +450,12 @@ export default {
rowActions({ row, listResource: domains, documentResource: site }) {
return [
{
label: '移除',
label: t('Remove'),
condition: () => row.domain !== site.pg?.name,
onClick() {
confirmDialog({
title: `移除域名`,
message: `您确定要从站点 <b>${site.pg?.name}</b> 中移除域名 <b>${row.domain}</b> 吗?`,
title: t('Remove Domain'),
message: t('Are you sure you want to remove domain <b>{domain}</b> from site <b>{site}</b>?', { domain: row.domain, site: site.pg?.name }),
onSuccess({ hide }) {
if (site.removeDomain.loading) return;
toast.promise(
@ -532,15 +531,15 @@ export default {
},
},
{
label: '移除重定向',
label: t('Remove Redirect'),
condition: () =>
!row.primary &&
row.redirect_to_primary &&
row.status === 'Active',
onClick() {
confirmDialog({
title: `移除重定向`,
message: `您确定要移除从域名 <b>${row.domain}</b> 到站点 <b>${site.pg?.name}</b> 主域名的重定向吗?`,
title: t('Remove Redirect'),
message: t('Are you sure you want to remove redirect from domain <b>{domain}</b> to primary domain of site <b>{site}</b>?', { domain: row.domain, site: site.pg?.name }),
onSuccess({ hide }) {
if (site.removeRedirect.loading) return;
toast.promise(
@ -548,10 +547,10 @@ export default {
domain: row.domain,
}),
{
loading: '正在移除重定向...',
loading: t('Removing redirect...'),
success: () => {
hide();
return '重定向已移除';
return t('Redirect removed');
},
error: (e) => getToastErrorMessage(e),
},
@ -565,7 +564,7 @@ export default {
},
},
{
label: '备份',
label: t('Backups'),
icon: icon('archive'),
route: 'backups',
type: 'list',
@ -595,16 +594,16 @@ export default {
],
columns: [
{
label: '时间戳',
label: t('Timestamp'),
fieldname: 'creation',
width: 1,
format(value) {
return `备份于 ${date(value, 'llll')}`;
return t('Backed up on {date}', { date: date(value, 'llll') });
},
},
{
label: '数据库',
label: t('Database'),
fieldname: 'database_size',
width: 0.5,
format(value) {
@ -612,7 +611,7 @@ export default {
},
},
{
label: '公共文件',
label: t('Public Files'),
fieldname: 'public_size',
width: 0.5,
format(value) {
@ -620,7 +619,7 @@ export default {
},
},
{
label: '私有文件',
label: t('Private Files'),
fieldname: 'private_size',
width: 0.5,
format(value) {
@ -628,7 +627,7 @@ export default {
},
},
{
label: '包含文件的备份',
label: t('Backup with Files'),
fieldname: 'with_files',
type: 'Icon',
width: 0.5,
@ -637,7 +636,7 @@ export default {
},
},
{
label: '异地备份',
label: t('Offsite Backup'),
fieldname: 'offsite',
width: 0.5,
type: 'Icon',
@ -650,7 +649,7 @@ export default {
return [
{
type: 'checkbox',
label: '异地备份',
label: t('Offsite Backup'),
fieldname: 'offsite',
},
];
@ -658,23 +657,21 @@ export default {
rowActions({ row, documentResource: site }) {
if (row.status != 'Success') return;
function getFileName(file) {
if (file == 'database') return 'database';
if (file == 'public') return 'public files';
if (file == 'private') return 'private files';
if (file == 'config') return 'config file';
}
function confirmDownload(backup, file) {
const fileNameMap = {
'database': t('database'),
'public': t('public files'),
'private': t('private files'),
'config': t('config file')
};
const fileName = fileNameMap[file] || file;
const message = !backup.offsite
? t('You will download the {fileName} backup for site <b>{siteName}</b>, created on {date}.<br><br><div class="p-2 bg-gray-100 border-gray-200 rounded">You need to log in as <b>System Administrator</b> on <em>your site</em> to download the backup.<div>', { fileName, siteName: site.pg?.host_name || site.pg?.name, date: date(backup.creation, 'llll') })
: t('You will download the {fileName} backup for site <b>{siteName}</b>, created on {date}.', { fileName, siteName: site.pg?.host_name || site.pg?.name, date: date(backup.creation, 'llll') });
confirmDialog({
title: '下载备份',
message: `您将下载站点 <b>${
site.pg?.host_name || site.pg?.name
}</b> ${getFileName(file)} ${date(backup.creation, 'llll')}${
!backup.offsite
? '<br><br><div class="p-2 bg-gray-100 border-gray-200 rounded">您需要以 <b>系统管理员</b> 身份登录 <em>您的站点</em> 才能下载备份。<div>'
: ''
}`,
title: t('Download Backup'),
message: message,
onSuccess() {
downloadBackup(backup, file);
},
@ -712,10 +709,10 @@ export default {
return [
{
group: '详情',
group: t('Details'),
items: [
{
label: '查看任务',
label: t('View Job'),
onClick() {
router.push({
name: 'Site Job',
@ -726,30 +723,30 @@ export default {
],
},
{
group: '下载',
group: t('Download'),
items: [
{
label: '下载数据库',
label: t('Download Database'),
onClick() {
return confirmDownload(row, 'database');
},
},
{
label: '下载公共文件',
label: t('Download Public Files'),
onClick() {
return confirmDownload(row, 'public');
},
condition: () => row.public_url,
},
{
label: '下载私有文件',
label: t('Download Private Files'),
onClick() {
return confirmDownload(row, 'private');
},
condition: () => row.private_url,
},
{
label: '下载配置文件',
label: t('Download Config File'),
onClick() {
return confirmDownload(row, 'config');
},
@ -758,18 +755,16 @@ export default {
],
},
{
group: '恢复',
group: t('Restore'),
condition: () => row.offsite,
items: [
{
label: '恢复备份',
label: t('Restore Backup'),
condition: () => site.pg.status !== 'Archived',
onClick() {
confirmDialog({
title: '恢复备份',
message: `您确定要将您的站点恢复到<b>${dayjs(
row.creation,
).format('lll')}</b>`,
title: t('Restore Backup'),
message: t('Are you sure you want to restore your site to the offsite backup from <b>{date}</b>?', { date: dayjs(row.creation).format('lll') }),
onSuccess({ hide }) {
toast.promise(
site.restoreSiteFromFiles.submit({
@ -781,7 +776,7 @@ export default {
},
}),
{
loading: '正在安排备份恢复...',
loading: t('Scheduling backup restore...'),
success: (jobId) => {
hide();
router.push({
@ -791,7 +786,7 @@ export default {
id: jobId,
},
});
return '备份恢复已成功安排。';
return t('Backup restore scheduled successfully.');
},
error: (e) => getToastErrorMessage(e),
},
@ -801,7 +796,7 @@ export default {
},
},
{
label: '在另一个站点上恢复备份',
label: t('Restore Backup on Another Site'),
onClick() {
let SelectSiteForRestore = defineAsyncComponent(
() =>
@ -826,13 +821,13 @@ export default {
},
}),
{
loading: '正在安排备份恢复...',
loading: t('Scheduling backup restore...'),
success: (jobId) => {
router.push({
name: 'Site Job',
params: { name: siteName, id: jobId },
});
return '备份恢复已成功安排。';
return t('Backup restore scheduled successfully.');
},
error: (e) => getToastErrorMessage(e),
},
@ -848,30 +843,29 @@ export default {
},
primaryAction({ listResource: backups, documentResource: site }) {
return {
label: '安排备份',
label: t('Schedule Backup'),
slots: {
prefix: icon('upload-cloud'),
},
loading: site.backup.loading,
onClick() {
confirmDialog({
title: '安排备份',
message:
'您确定要安排备份吗?这将创建一个本地备份。',
title: t('Schedule Backup'),
message: t('Are you sure you want to schedule a backup? This will create a local backup.'),
onSuccess({ hide }) {
toast.promise(
site.backup.submit({
with_files: true,
}),
{
loading: '正在安排备份...',
loading: t('Scheduling backup...'),
success: () => {
hide();
router.push({
name: 'Site Jobs',
params: { name: site.name },
});
return '备份已成功安排。';
return t('Backup scheduled successfully.');
},
error: (e) => getToastErrorMessage(e),
},
@ -1070,7 +1064,7 @@ export default {
GenericDialog,
{
options: {
title: '应用更改',
title: t('App Changes'),
size: '2xl',
},
},
@ -1081,11 +1075,11 @@ export default {
data: () => data,
columns: [
{
label: '应用',
label: t('App'),
fieldname: 'app',
},
{
label: '从',
label: t('From'),
fieldname: 'source_hash',
type: 'Button',
Button({ row }) {
@ -1102,7 +1096,7 @@ export default {
},
},
{
label: '到',
label: t('To'),
fieldname: 'destination_hash',
type: 'Button',
Button({ row }) {
@ -1119,13 +1113,13 @@ export default {
},
},
{
label: '应用变更',
label: t('App Changes'),
fieldname: 'diff_url',
align: 'right',
type: 'Button',
Button({ row }) {
return {
label: '查看',
label: t('View'),
variant: 'ghost',
slots: {
prefix: icon('external-link'),
@ -1140,7 +1134,7 @@ export default {
},
),
);
} else toast.error('未找到应用变更');
} else toast.error(t('No app changes found'));
},
});
},
@ -1152,7 +1146,7 @@ export default {
return [
{
label: '配置',
label: t('Config'),
slots: {
prefix: icon('settings'),
},
@ -1184,7 +1178,7 @@ export default {
return [
{
label: '进行中的任务',
label: t('Jobs in Progress'),
slots: {
prefix: () => h(LoadingIndicator, { class: 'w-4 h-4' }),
},
@ -1203,7 +1197,7 @@ export default {
},
},
{
label: '有可用更新',
label: t('Update Available'),
variant: site.pg?.setup_wizard_complete ? 'solid' : 'subtle',
slots: {
prefix: icon('alert-circle'),
@ -1226,7 +1220,7 @@ export default {
},
},
{
label: '已安排更新',
label: t('Scheduled Updates'),
slots: {
prefix: icon('calendar'),
},
@ -1239,8 +1233,8 @@ export default {
},
},
{
label: '模拟站点所有者',
title: '模拟站点所有者', // for label to pop-up on hover
label: t('Impersonate Site Owner'),
title: t('Impersonate Site Owner'), // for label to pop-up on hover
slots: {
icon: defineAsyncComponent(
() => import('~icons/lucide/venetian-mask'),
@ -1253,7 +1247,7 @@ export default {
},
},
{
label: '访问站点',
label: t('Visit Site'),
slots: {
prefix: icon('external-link'),
},
@ -1264,7 +1258,7 @@ export default {
},
},
{
label: '设置站点',
label: t('Setup Site'),
slots: {
prefix: icon('external-link'),
},
@ -1284,11 +1278,11 @@ export default {
},
},
{
label: '选项',
label: t('Options'),
context,
options: [
{
label: '在 Desk 中查看',
label: t('View in Desk'),
icon: 'external-link',
condition: () => $team.pg?.is_desk_user,
onClick: () => {
@ -1299,18 +1293,18 @@ export default {
},
},
{
label: '以管理员身份登录',
label: t('Login as Administrator'),
icon: 'external-link',
condition: () => ['Active', 'Broken'].includes(site.pg.status),
onClick: () => {
confirmDialog({
title: '以管理员身份登录',
message: `您确定要以管理员身份登录站点 <b>${site.pg?.name}</b> 吗?`,
title: t('Login as Administrator'),
message: t('Are you sure you want to login as administrator to site <b>{site}</b>?', { site: site.pg?.name }),
fields:
$team.name !== site.pg.team
? [
{
label: '原因',
label: t('Reason'),
type: 'textarea',
fieldname: 'reason',
},
@ -1318,7 +1312,7 @@ export default {
: [],
onSuccess: ({ hide, values }) => {
if (!values.reason && $team.name !== site.pg.team) {
throw new Error('原因必填');
throw new Error(t('Reason is required'));
}
return site.loginAsAdmin
.submit({ reason: values.reason })

View File

@ -3,12 +3,12 @@
<header
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-5 py-2.5"
>
<Breadcrumbs :items="[{ label: 'Apps', route: '/marketplace' }]">
<Breadcrumbs :items="[{ label: $t('Apps'), route: '/marketplace' }]">
<template #actions>
<Button
variant="solid"
icon-left="plus"
label="New"
:label="$t('New')"
class="ml-2"
@click="
!$resources.appOptions.data
@ -20,11 +20,11 @@
</template>
</Breadcrumbs>
</header>
<SectionHeader class="mx-5 mt-6" heading="Apps" />
<SectionHeader class="mx-5 mt-6" :heading="$t('Apps')" />
<Dialog
:options="{
title: 'Add App to Marketplace',
title: $t('Add App to Marketplace'),
size: 'xl'
}"
v-model="showAddAppDialog"
@ -40,7 +40,7 @@
v-model="selectedApp"
:multiple="false"
/>
<p v-else class="text-base">No app sources available.</p>
<p v-else class="text-base">{{ $t('No app sources available.') }}</p>
<ErrorMessage
class="mt-2"
@ -48,8 +48,8 @@
/>
<p class="mt-4 text-base" @click="showAddAppDialog = false">
Don't find your app here?
<Link :to="`/marketplace/apps/new`"> Add from GitHub </Link>
{{ $t("Don't find your app here?") }}
<Link :to="`/marketplace/apps/new`"> {{ $t('Add from GitHub') }} </Link>
</p>
</template>
<template #actions>
@ -65,7 +65,7 @@
})
"
>
Add {{ selectedApp.app }}
{{ $t('Add {app}', { app: selectedApp.app }) }}
</Button>
</template>
</Dialog>
@ -84,19 +84,26 @@ export default {
name: 'Marketplace',
pageMeta() {
return {
title: 'Developer - 今果 Jingrow'
title: this.$t('Developer') + ' - 今果 Jingrow'
};
},
components: {
Tabs,
AppSourceSelector
},
computed: {
tabs() {
return [
{ label: this.$t('My Apps'), route: '/marketplace/apps' },
{ label: this.$t('Publisher Profile'), route: '/marketplace/publisher-profile' },
{ label: this.$t('Payouts'), route: '/marketplace/payouts' }
];
},
availableApps() {
return this.$resources.appOptions.data;
}
},
data: () => ({
tabs: [
{ label: 'My Apps', route: '/marketplace/apps' },
{ label: 'Publisher Profile', route: '/marketplace/publisher-profile' },
{ label: 'Payouts', route: '/marketplace/payouts' }
],
showAddAppDialog: false,
selectedApp: null
}),
@ -116,11 +123,6 @@ export default {
};
}
},
computed: {
availableApps() {
return this.$resources.appOptions.data;
}
},
activated() {
if (this.$route.matched.length === 1) {
let path = this.$route.fullPath;

View File

@ -7,7 +7,7 @@
<Breadcrumbs
:items="[
{
label: 'Apps',
label: $t('Apps'),
route: {
name: 'Marketplace',
params: { appName: app.name }
@ -27,7 +27,7 @@
v-if="app.status === 'Published'"
variant="solid"
icon-left="external-link"
label="View in Marketplace"
:label="$t('View in Marketplace')"
class="ml-2"
:link="`/marketplace/apps/${app.name}`"
/>
@ -96,34 +96,34 @@ export default {
},
tabs() {
let tabsByStatus = {
Draft: ['Overview', 'Releases', 'Review'],
'In Review': ['Overview', 'Releases', 'Review'],
Rejected: ['Overview', 'Releases', 'Review'],
Draft: [this.$t('Overview'), this.$t('Releases'), this.$t('Review')],
'In Review': [this.$t('Overview'), this.$t('Releases'), this.$t('Review')],
Rejected: [this.$t('Overview'), this.$t('Releases'), this.$t('Review')],
Published: [
'Overview',
'Releases',
'Analytics',
'Subscriptions',
'Pricing'
this.$t('Overview'),
this.$t('Releases'),
this.$t('Analytics'),
this.$t('Subscriptions'),
this.$t('Pricing')
],
'Attention Required': [
'Overview',
'Releases',
'Review',
'Analytics',
'Subscriptions',
'Pricing'
this.$t('Overview'),
this.$t('Releases'),
this.$t('Review'),
this.$t('Analytics'),
this.$t('Subscriptions'),
this.$t('Pricing')
]
};
let tabRoute = subRoute =>
`/marketplace/apps/${this.appName}/${subRoute}`;
let tabs = [
{ label: 'Overview', route: 'overview' },
{ label: 'Releases', route: 'releases' },
{ label: 'Review', route: 'review' },
{ label: 'Analytics', route: 'analytics' },
{ label: 'Subscriptions', route: 'subscriptions' },
{ label: 'Pricing', route: 'pricing' }
{ label: this.$t('Overview'), route: 'overview' },
{ label: this.$t('Releases'), route: 'releases' },
{ label: this.$t('Review'), route: 'review' },
{ label: this.$t('Analytics'), route: 'analytics' },
{ label: this.$t('Subscriptions'), route: 'subscriptions' },
{ label: this.$t('Pricing'), route: 'pricing' }
];
if (this.app) {

View File

@ -3,19 +3,19 @@
v-if="$resources.analytics.data"
class="grid grid-cols-1 gap-5 sm:grid-cols-2"
>
<Card title="Earnings">
<Card :title="$t('Earnings')">
<template #actions>
<a
class="text-base text-gray-700 hover:text-gray-800"
href="/support/tickets"
target="_blank"
>
Contact Support
{{ $t('Contact Support') }}
</a>
</template>
<ListItem
title="Total Earnings"
:subtitle="`Total earnings for ${app.title}`"
:title="$t('Total Earnings')"
:subtitle="$t('Total earnings for {app}', { app: app.title })"
>
<template #actions>
<span class="text-base font-semibold text-green-500">{{
@ -28,8 +28,8 @@
</template>
</ListItem>
<ListItem
title="Pending Payout"
subtitle="Payout you are yet to receive from 今果 Jingrow"
:title="$t('Pending Payout')"
:subtitle="$t('Payout you are yet to receive from 今果 Jingrow')"
>
<template #actions>
<span class="text-base font-semibold">{{
@ -42,8 +42,8 @@
</template>
</ListItem>
<ListItem
title="Commission"
subtitle="Payouts start once you have passed $500 threshold"
:title="$t('Commission')"
:subtitle="$t('Payouts start once you have passed $500 threshold')"
>
<template #actions>
<span class="text-base font-semibold">{{
@ -56,7 +56,7 @@
</template>
</ListItem>
</Card>
<Card title="Installs">
<Card :title="$t('Installs')">
<div class="divide-y" v-if="analytics">
<ListItem
v-for="stat in analytics"
@ -68,11 +68,11 @@
</div>
<div class="py-10 text-center" v-if="$resources.analytics.loading">
<Button :loading="true">Loading</Button>
<Button :loading="true">{{ $t('Loading') }}</Button>
</div>
</Card>
<LineChart
title="Pageviews"
:title="$t('Pageviews')"
type="time"
:key="pageViewsData"
:data="pageViewsData"
@ -88,12 +88,12 @@
:href="`/marketplace/apps/${app.app}`"
target="_blank"
>
View Marketplace Page
{{ $t('View Marketplace Page') }}
</a>
</template>
</LineChart>
<LineChart
title="Unique Visitors"
:title="$t('Unique Visitors')"
type="time"
:key="visitorsData"
:data="visitorsData"
@ -175,25 +175,25 @@ export default {
return [
{
title: 'Total Installs',
title: this.$t('Total Installs'),
value:
total_installs.toString() +
' ' +
(total_installs == 1 ? 'Site' : 'Sites')
(total_installs == 1 ? this.$t('Site') : this.$t('Sites'))
},
{
title: 'Active Sites with this App',
title: this.$t('Active Sites with this App'),
value:
num_installs_active_sites.toString() +
' ' +
(num_installs_active_sites == 1 ? 'Site' : 'Sites')
(num_installs_active_sites == 1 ? this.$t('Site') : this.$t('Sites'))
},
{
title: 'Active Benches with this App',
title: this.$t('Active Benches with this App'),
value:
num_installs_active_benches.toString() +
' ' +
(num_installs_active_benches == 1 ? 'Bench' : 'Benches')
(num_installs_active_benches == 1 ? this.$t('Bench') : this.$t('Benches'))
}
];
}

View File

@ -98,7 +98,7 @@ function resetCurrentEditingPlan() {
<template>
<div>
<Card title="Pricing Plans" subtitle="Set up pricing plans for this app">
<Card :title="$t('Pricing Plans')" :subtitle="$t('Set up pricing plans for this app')">
<div class="m-4">
<div class="flex justify-center" v-if="appPlans.loading">
<Button :loading="true">Loading</Button>

View File

@ -15,7 +15,7 @@ const appSubscriptions = createResource({
</script>
<template>
<Card title="Subscriptions">
<Card :title="$t('Subscriptions')">
<div v-if="appSubscriptions.data">
<div v-if="appSubscriptions.data.length === 0">
<p class="my-3 text-center text-base text-gray-600">

View File

@ -3,7 +3,7 @@
<Button
v-if="$resources.apps.loading && !$resources.apps.data"
:loading="true"
loadingText="Loading..."
:loadingText="$t('Loading...')"
></Button>
<ErrorMessage
v-else-if="!$resources.apps.data"
@ -11,7 +11,7 @@
/>
<div v-else-if="$resources.apps.data.length < 1">
<p class="text-lg text-gray-600">
You have not published any app on our Marketplace.
{{ $t('You have not published any app on our Marketplace.') }}
</p>
</div>
<div v-else>

View File

@ -17,8 +17,8 @@ const payouts = createResource({
<template>
<Card
v-if="!props.payoutOrderName"
title="Payouts"
subtitle="Look what you have earned"
:title="$t('Payouts')"
:subtitle="$t('Look what you have earned')"
>
<Button v-if="payouts.loading" :loading="true">Loading</Button>
@ -79,7 +79,7 @@ const payouts = createResource({
</div>
<ErrorMessage :message="payouts.error" />
</Card>
<Card v-else title="Payout Details">
<Card v-else :title="$t('Payout Details')">
<template #actions-left>
<Button route="/marketplace/payouts"> Back </Button>
</template>

View File

@ -13,7 +13,7 @@
<Button
v-if="$resources.getPublisherProfileInfo.loading"
:loading="true"
loadingText="Loading..."
:loadingText="$t('Loading...')"
></Button>
<div class="grid grid-cols-1 gap-5 md:grid-cols-2">

View File

@ -555,6 +555,201 @@ Are you sure you want to remove app <b>{app}</b>?,确定要移除应用 <b>{app}
Removing app...,正在移除应用...,
Are you sure you want to delete config <b>{key}</b>?,确定要删除配置 <b>{key}</b> 吗?,
Are you sure you want to delete environment variable <b>{key}</b>?,确定要删除环境变量 <b>{key}</b> 吗?,
My Apps,我的应用,
Publisher Profile,发布者资料,
Payouts,付款,
Releases,发布,
Review,审核,
Subscriptions,订阅,
Pricing,定价,
Add App to Marketplace,添加应用到市场,
No app sources available.,没有可用的应用源。,
Don't find your app here?,在这里找不到您的应用?,
Add from GitHub,从 GitHub 添加,
Add {app},添加 {app},
You have not published any app on our Marketplace.,您尚未在我们的市场上发布任何应用。,
View in Marketplace,在市场查看,
Pricing Plans,定价计划,
Set up pricing plans for this app,为此应用设置定价计划,
Payout Details,付款详情,
Look what you have earned,查看您的收入,
Earnings,收入,
Total Earnings,总收入,
Total earnings for {app},{app} 的总收入,
Pending Payout,待付款,
Payout you are yet to receive from 今果 Jingrow,您尚未从今果 Jingrow 收到的付款,
Commission,佣金,
Payouts start once you have passed $500 threshold,一旦您超过 $500 阈值,付款将开始,
Installs,安装,
Total Installs,总安装数,
Active Sites with this App,使用此应用的活跃站点,
Active Benches with this App,使用此应用的活跃工作台,
Pageviews,页面浏览量,
Unique Visitors,独立访客,
View Marketplace Page,查看市场页面,
Contact Support,联系支持,
Loading...,加载中...,
Site,站点,
Bench,工作台,
Listing updated successfully,更新成功,
Updating listing...,正在更新列表...,
Failed to update listing,更新列表失败,
View,查看,
Delete,删除,
Deleting screenshot...,正在删除截图...,
Screenshot deleted successfully,截图删除成功,
Failed to delete screenshot,删除截图失败,
Add New Marketplace App,添加新的应用市场应用,
Select Version,选择版本,
Validating app...,正在验证应用...,
Found {title} ({name}),找到 {title} ({name}),
Github repository is private.,Github 仓库是私有的。,
Terms and Policies,条款和政策,
Adding new app...,正在添加新应用...,
New app added,新应用已添加,
Steps to complete before publishing your app,应用发布前需要完成的步骤,
Your app has been sent to our team for review. Please wait for the review to complete.,您的应用已发送给我们的团队进行审核。请等待审核完成。,
Mark app as ready for review,标记应用为待审核,
Step,步骤,
Completed,已完成,
App Profile,应用资料,
This information will be publicly displayed on the marketplace. Please ensure you enter correct information, with no broken links and images.,这些信息将公开显示在市场上。请确保输入正确的信息,没有损坏的链接和图片。,
Save,保存,
Profile,资料,
Profile photo updated,资料照片已更新,
Links,链接,
Documentation,文档,
Website,网站,
Support,支持,
Terms of Service,服务条款,
Privacy Policy,隐私政策,
Screenshots and Videos,截图和视频,
Screenshot added,已添加截图,
Add,添加,
Summary,摘要,
Your app's primary profile,您应用的主要资料,
Published Versions,已发布版本,
Update App Title,更新应用标题,
Save Changes,保存更改,
App Title,应用标题,
Change Branch,更改分支,
Remove,移除,
App Profile Updated!,应用资料已更新!,
App Descriptions,应用描述,
Details about your app,关于您应用的详细信息,
Update App Summary,更新应用摘要,
Summary of the app,应用摘要,
Long Description,详细描述,
Update App Description,更新应用描述,
Fetch Readme,获取 Readme,
App Summary Updated!,应用摘要已更新!,
App Description Updated!,应用描述已更新!,
Successfully fetched latest readme,成功获取最新 readme,
Long description updated!,详细描述已更新!,
Active Sites,激活站点,
Active Benches,激活站点分组,
Weekly Installs,每周安装量,
Updating version branch...,正在更新版本的分支...,
Change {app} {version} branch,更改 {app} 的 {version} 分支,
Please select a different branch,请选择一个不同的分支,
Branch changed successfully,分支更改成功,
New App,新建应用,
Listing,列表,
Source,来源,
New Version,新建版本,
Add version support for {appTitle},为 {appTitle} 添加版本支持,
Adding new version...,正在添加新版本...,
New version added,新版本已添加,
Show Releases,显示发布,
Remove Version,移除版本,
Removing version...,正在移除版本...,
Version removed successfully,版本已成功移除,
Price,价格,
Active Days,已激活天数,
day,,
days,,
Guide,指南,
Complete Listing,完成列表,
Delete app {appTitle},删除应用 {appTitle},
Are you sure you want to delete this app?,您确定要删除此应用吗?,
Deleting app...,正在删除应用...,
App deleted successfully,应用删除成功,
Releases for {appName} on {branch} branch,{appName} 在 {branch} 分支的发布,
Commit Message,提交信息,
Hash,哈希值,
Code Review,代码审查,
Not Started,未开始,
Cancel,取消,
Release cancelled,发布已取消,
Cancelling release...,正在取消发布...,
Submit,提交,
Submitting release for approval...,正在提交发布以供审批...,
Release submitted for approval,发布已提交以供审批,
No results found,未找到结果,
Primary Domain,主域名,
DNS Type,DNS 类型,
Error occurred while obtaining HTTPS certificate for your domain.,获取您的域名的 HTTPS 证书时出错。,
View Error,查看错误,
Error obtaining certificate,获取证书时出错,
Add Domain,添加域名,
Remove,移除,
Are you sure you want to remove domain <b>{domain}</b> from site <b>{site}</b>?,您确定要从站点 <b>{site}</b> 中移除域名 <b>{domain}</b> 吗?,
Backups,备份,
Timestamp,时间戳,
Backed up on {date},备份于 {date},
Database,数据库,
Public Files,公共文件,
Private Files,私有文件,
Backup with Files,包含文件的备份,
Offsite Backup,异地备份,
Download Backup,下载备份,
You will download the {fileName} backup for site <b>{siteName}</b>, created on {date}.,您将下载站点 <b>{siteName}</b> 的 {fileName} 备份,该备份创建于 {date}。,
You will download the {fileName} backup for site <b>{siteName}</b>, created on {date}.<br><br><div class="p-2 bg-gray-100 border-gray-200 rounded">You need to log in as <b>System Administrator</b> on <em>your site</em> to download the backup.<div>,您将下载站点 <b>{siteName}</b> 的 {fileName} 备份,该备份创建于 {date}。<br><br><div class="p-2 bg-gray-100 border-gray-200 rounded">您需要以 <b>系统管理员</b> 身份登录 <em>您的站点</em> 才能下载备份。<div>,
database,数据库,
public files,公共文件,
private files,私有文件,
config file,配置文件,
Details,详情,
Download,下载,
Download Database,下载数据库,
Download Public Files,下载公共文件,
Download Private Files,下载私有文件,
Download Config File,下载配置文件,
Restore,恢复,
Restore Backup,恢复备份,
Are you sure you want to restore your site to the offsite backup from <b>{date}</b>?,您确定要将您的站点恢复到<b>{date}</b>的异地备份吗?,
Scheduling backup restore...,正在安排备份恢复...,
Backup restore scheduled successfully.,备份恢复已成功安排。,
Restore Backup on Another Site,在另一个站点上恢复备份,
Schedule Backup,安排备份,
Are you sure you want to schedule a backup? This will create a local backup.,您确定要安排备份吗?这将创建一个本地备份。,
Scheduling backup...,正在安排备份...,
Backup scheduled successfully.,备份已成功安排。,
App Changes,应用更改,
From,,
To,,
No app changes found,未找到应用变更,
Jobs in Progress,进行中的任务,
Scheduled Updates,已安排更新,
Impersonate Site Owner,模拟站点所有者,
Visit Site,访问站点,
Setup Site,设置站点,
Login as Administrator,以管理员身份登录,
Are you sure you want to login as administrator to site <b>{site}</b>?,您确定要以管理员身份登录站点 <b>{site}</b> 吗?,
Reason,原因,
Reason is required,原因必填,
Normal,正常,
Locked,锁定,
Registration Date,注册时间,
Expiry Date,到期时间,
New Domain,新建域名,
Domain Details,域名详细信息,
DNS Records,域名解析,
Owner Template,所有者模板,
Basic Information,基本信息,
Pricing Information,价格信息,
DNS Settings,DNS设置,
Other Information,其他信息,
Renew,续费,
Rename,重命名,

Can't render this file because it has a wrong number of fields in line 386.