Billing菜单及页面实现多语言支持

This commit is contained in:
jingrow 2025-12-29 15:42:11 +08:00
parent 2a350e93ce
commit d911a973eb
12 changed files with 207 additions and 120 deletions

View File

@ -178,7 +178,7 @@ export default {
disabled: enforce2FA,
},
{
name: '账单',
name: this.$t('Billing'),
icon: () => h(WalletCards),
route: '/billing',
isActive: routeName.startsWith('Billing'),

View File

@ -1,5 +1,5 @@
<template>
<Dialog v-model="show" :options="{ title: '余额充值' }">
<Dialog v-model="show" :options="{ title: $t('Account Recharge') }">
<template #body-content>
<div
v-if="showMessage"
@ -7,7 +7,7 @@
>
<FeatherIcon class="h-4" name="info" />
<span>
在更改支付方式之前请先为您的账户充值余额
{{ $t('Please recharge your account balance before changing payment method.') }}
</span>
</div>
<PrepaidCreditsForm

View File

@ -4,7 +4,7 @@
v-model="billingInformation.billing_name"
type="text"
name="billing_name"
label="账单名称"
:label="$t('Billing Name')"
:required="true"
/>
<NewAddressForm
@ -18,7 +18,7 @@
<Button
class="w-full"
variant="solid"
label="更新账单信息"
:label="$t('Update Billing Information')"
:loading="addressFormRef.updateBillingInformation.loading"
@click="updateBillingInformation"
/>
@ -27,7 +27,7 @@
<script setup>
import NewAddressForm from './NewAddressForm.vue';
import { FormControl, ErrorMessage, Button, createResource } from 'jingrow-ui';
import { reactive, ref, inject } from 'vue';
import { reactive, ref, inject, getCurrentInstance } from 'vue';
const emit = defineEmits(['success']);
@ -70,15 +70,16 @@ createResource({
const errorMessage = ref('');
function updateBillingInformation() {
const { $t } = getCurrentInstance().appContext.config.globalProperties;
if (!billingInformation.billing_name) {
errorMessage.value = '账单名称为必填项';
errorMessage.value = $t('Billing name is required');
return;
}
const billing_name = billingInformation.billing_name.trim();
const billingNameRegex = /^[a-zA-Z0-9\-\'\,\.\s]+$/;
const billingNameValid = billingNameRegex.test(billing_name);
if (!billingNameValid) {
errorMessage.value = '账单名称包含无效字符';
errorMessage.value = $t('Billing name contains invalid characters');
return;
}
billingInformation.billing_name = billing_name;

View File

@ -1,12 +1,12 @@
<template>
<Dialog v-model="show" :options="{ title: '账单详情' }">
<Dialog v-model="show" :options="{ title: $t('Billing Details') }">
<template #body-content>
<div
v-if="showMessage"
class="mb-5 inline-flex gap-1.5 text-base text-gray-700"
>
<FeatherIcon class="h-4" name="info" />
<span> 在继续之前请为您的账户添加账单详情</span>
<span> {{ $t('Please add billing details to your account before continuing.') }}</span>
</div>
<BillingDetails
ref="billingRef"

View File

@ -25,7 +25,7 @@
</template>
<script setup>
import { FormControl, ErrorMessage, createResource } from 'jingrow-ui';
import { ref, computed, inject, watch, onMounted } from 'vue';
import { ref, computed, inject, watch, onMounted, getCurrentInstance } from 'vue';
import { toast } from 'vue-sonner';
import { DashboardError } from '../../utils/error';
import { countryNameMap, chinaStates } from '@/utils/billing';
@ -70,7 +70,9 @@ const updateBillingInformation = createResource({
if (error) throw new DashboardError(error);
},
onSuccess: () => {
toast.success('账单信息已更新');
const instance = getCurrentInstance();
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
toast.success($t('Billing information updated'));
emit('success');
},
});
@ -92,8 +94,11 @@ const codeToNameMap = computed(() => {
// -
const countryList = computed(() => {
const instance = getCurrentInstance();
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
//
return [{ label: '中国', value: 'China' }];
return [{ label: $t('China'), value: 'China' }];
//
// return Object.entries({
@ -114,14 +119,17 @@ const stateOptions = computed(() => {
});
const sections = computed(() => {
const instance = getCurrentInstance();
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
return [
{
name: '国家和城市',
name: $t('Country and City'),
columns: 2,
fields: [
{
fieldtype: 'Select',
label: '国家',
label: $t('Country'),
fieldname: 'country',
options: countryList.value,
required: true,
@ -129,32 +137,32 @@ const sections = computed(() => {
},
{
fieldtype: 'Data',
label: '城市',
label: $t('City'),
fieldname: 'city',
required: true,
},
],
},
{
name: '地址',
name: $t('Address'),
columns: 1,
fields: [
{
fieldtype: 'Data',
label: '地址',
label: $t('Address'),
fieldname: 'address',
required: true,
},
],
},
{
name: '州和邮政编码',
name: $t('State and Postal Code'),
columns: 2,
fields: [
{
fieldtype:
billingInformation.value.country === 'China' ? 'Select' : 'Data',
label: '州/省/地区',
label: $t('State/Province/Region'),
fieldname: 'state',
required: true,
options:
@ -164,7 +172,7 @@ const sections = computed(() => {
},
{
fieldtype: 'Data',
label: '邮政编码',
label: $t('Postal Code'),
fieldname: 'postal_code',
required: true,
},
@ -186,10 +194,13 @@ function getInputType(field) {
}
async function validate() {
const instance = getCurrentInstance();
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
//
for (let field of sections.value.flatMap((s) => s.fields)) {
if (field.required && !billingInformation.value[field.fieldname]) {
return `${field.label} 是必填项`;
return $t('{field} is required', { field: field.label });
}
}
return null; // null

View File

@ -3,14 +3,14 @@
<div class="flex flex-col">
<div class="flex items-center justify-between text-base text-gray-900">
<div class="flex flex-col gap-1.5">
<div class="text-lg font-semibold text-gray-900">账户余额</div>
<div class="text-lg font-semibold text-gray-900">{{ $t('Account Balance') }}</div>
<div class="text-2xl font-bold text-blue-600 py-6">
{{ availableCredits || currency + ' 0.00' }}
</div>
</div>
<div class="shrink-0">
<Button
:label="'充值'"
:label="$t('Recharge')"
@click="
() => {
showMessage = false;
@ -27,15 +27,15 @@
<div class="my-3 h-px bg-gray-100" />
<div class="flex items-center justify-between text-base text-gray-900">
<div class="flex flex-col gap-1.5">
<div class="font-medium">账单地址</div>
<div class="font-medium">{{ $t('Billing Address') }}</div>
<div v-if="billingDetailsSummary" class="leading-5 text-gray-700">
{{ billingDetailsSummary }}
</div>
<div v-else class="text-gray-700">无地址</div>
<div v-else class="text-gray-700">{{ $t('No address') }}</div>
</div>
<div class="shrink-0">
<Button
:label="billingDetailsSummary ? '编辑' : '添加账单地址'"
:label="billingDetailsSummary ? $t('Edit') : $t('Add Billing Address')"
@click="
() => {
showMessage = false;
@ -73,7 +73,7 @@ import {
confirmDialog,
renderDialog,
} from '../../utils/components';
import { computed, ref, inject, h, defineAsyncComponent, onMounted, nextTick } from 'vue';
import { computed, ref, inject, h, defineAsyncComponent, onMounted, nextTick, getCurrentInstance } from 'vue';
import router from '../../router';
//
@ -83,6 +83,9 @@ const countryToZh = {
//
};
const instance = getCurrentInstance();
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
const team = inject('team');
const {
availableCredits,
@ -146,31 +149,31 @@ const billingDetailsSummary = computed(() => {
const paymentModeOptions = [
{
label: '余额支付',
label: $t('Prepaid Credits'),
value: 'Prepaid Credits',
description: '您的每月账单费用将从账户余额中扣除',
description: $t('Your monthly billing charges will be deducted from your account balance'),
component: () =>
h(DropdownItem, {
label: '余额支付',
label: $t('Prepaid Credits'),
active: team.pg.payment_mode === 'Prepaid Credits',
onClick: () => updatePaymentMode('Prepaid Credits'),
}),
},
{
label: '由合作伙伴支付',
label: $t('Paid By Partner'),
value: 'Paid By Partner',
condition: () => team.pg.partner_email,
description: '您的合作伙伴将为您支付每月订阅费用',
description: $t('Your partner will pay your monthly subscription fees'),
component: () =>
h(DropdownItem, {
label: '由合作伙伴支付',
label: $t('Paid By Partner'),
active: team.pg.payment_mode === 'Paid by Partner',
onClick: () =>
confirmDialog({
title: '确认支付方式',
message: `通过将支付方式更改为<strong>由合作伙伴支付</strong>,以下详细信息将与您的合作伙伴共享:<br><br><li>站点/服务器名称</li> <li>计划名称</li><li>站点/服务器激活天数</li><br>您确定要继续吗?`,
title: $t('Confirm Payment Method'),
message: $t('By changing the payment method to <strong>Paid By Partner</strong>, the following details will be shared with your partner:<br><br><li>Site/Server Name</li> <li>Plan Name</li><li>Site/Server Active Days</li><br>Are you sure you want to continue?'),
primaryAction: {
label: '更改支付方式',
label: $t('Change Payment Method'),
variant: 'solid',
onClick: ({ hide }) => {
updatePaymentMode('Paid By Partner');

View File

@ -2,7 +2,7 @@
<div class="sticky top-0 z-10 shrink-0">
<Header>
<FBreadcrumbs
:items="[{ label: '账单', route: { name: 'Billing' } }]"
:items="[{ label: $t('Billing'), route: { name: 'Billing' } }]"
/>
</Header>
<TabsWithRouter
@ -14,7 +14,7 @@
class="mx-auto mt-60 w-fit rounded border border-dashed px-12 py-8 text-center text-gray-600"
>
<i-lucide-alert-triangle class="mx-auto mb-4 h-6 w-6 text-red-600" />
<ErrorMessage message="您无权查看账单页面" />
<ErrorMessage :message="$t('You do not have permission to view the billing page')" />
</div>
</div>
</template>
@ -35,11 +35,11 @@ export default {
return {
currentTab: 0,
tabs: [
{ label: '概览', route: { name: 'BillingOverview' } },
{ label: '订单记录', route: { name: 'BillingOrders' } },
{ label: '余额明细', route: { name: 'BillingBalances' } },
{ label: this.$t('Overview'), route: { name: 'BillingOverview' } },
{ label: this.$t('Order Records'), route: { name: 'BillingOrders' } },
{ label: this.$t('Balance Details'), route: { name: 'BillingBalances' } },
{
label: '开发者收益',
label: this.$t('Developer Earnings'),
route: { name: 'BillingMarketplacePayouts' },
requireDeveloper: true,
requirePro: true

View File

@ -8,7 +8,7 @@
@click="exportToCsv"
:loading="exporting"
>
导出
{{ $t('Export') }}
</Button>
</template>
</ObjectList>
@ -38,7 +38,7 @@ export default {
fields: ['type', 'source', 'invoice', 'description'],
columns: [
{
label: '时间',
label: this.$t('Time'),
fieldname: 'creation',
format(value) {
return new Date(value).toLocaleString('zh-CN', {
@ -52,7 +52,7 @@ export default {
}
},
{
label: '描述',
label: this.$t('Description'),
fieldname: 'description',
format(value, row) {
if (value !== null && value !== undefined) {
@ -60,28 +60,28 @@ export default {
}
if (row.type === 'Applied To Invoice' && row.invoice) {
return `冲抵发票 ${row.invoice}`;
return this.$t('Applied to Invoice {invoice}', { invoice: row.invoice });
}
if (row.source === 'Prepaid Credits') {
return '余额充值';
return this.$t('Balance Recharge');
}
if (row.source === 'Free Credits') {
return '赠送余额';
return this.$t('Free Credits');
}
return row.amount < 0 ? row.type : row.source;
}
},
{
label: '金额',
label: this.$t('Amount'),
fieldname: 'amount',
align: 'right',
format: this.formatCurrency
},
{
label: '余额',
label: this.$t('Balance'),
fieldname: 'ending_balance',
align: 'right',
format: this.formatCurrency
@ -132,10 +132,10 @@ export default {
// CSV
const fields = [
'时间',
'描述',
'金额',
'余额'
this.$t('Time'),
this.$t('Description'),
this.$t('Amount'),
this.$t('Balance')
];
//
@ -146,11 +146,11 @@ export default {
// 使
if (!description) {
if (row.type === 'Applied To Invoice' && row.invoice) {
description = `冲抵发票 ${row.invoice}`;
description = this.$t('Applied to Invoice {invoice}', { invoice: row.invoice });
} else if (row.source === 'Prepaid Credits') {
description = '余额充值';
description = this.$t('Balance Recharge');
} else if (row.source === 'Free Credits') {
description = '赠送余额';
description = this.$t('Free Credits');
} else {
description = row.amount < 0 ? row.type : row.source;
}
@ -176,7 +176,7 @@ export default {
//
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
const today = new Date().toISOString().split('T')[0];
const filename = `余额记录-${today}.csv`;
const filename = `${this.$t('Balance Records')}-${today}.csv`;
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
@ -186,7 +186,7 @@ export default {
this.exporting = false;
},
handleExportError(error) {
console.error('导出数据失败:', error);
console.error(this.$t('Failed to export data'), error);
this.exporting = false;
}
}

View File

@ -11,7 +11,7 @@
v-if="showPayout.status === 'Empty'"
class="text-base text-gray-600"
>
无内容显示
{{ $t('No content to display') }}
</div>
<PayoutTable v-else :payoutId="showPayout.name" />
</template>
@ -51,14 +51,14 @@ export default {
return [
{
type: 'select',
label: '状态',
label: this.$t('Status'),
class: !this.$isMobile ? 'w-36' : '',
fieldname: 'status',
options: [
{ label: '', value: '' },
{ label: '待结算', value: 'Draft' },
{ label: '已付款', value: 'Paid' },
{ label: '已结算', value: 'Commissioned' }
{ label: this.$t('Pending Settlement'), value: 'Draft' },
{ label: this.$t('Paid'), value: 'Paid' },
{ label: this.$t('Settled'), value: 'Commissioned' }
]
}
];
@ -66,7 +66,7 @@ export default {
orderBy: 'creation desc',
columns: [
{
label: '日期',
label: this.$t('Date'),
fieldname: 'period_end',
format(value) {
return Intl.DateTimeFormat('en-US', {
@ -76,10 +76,10 @@ export default {
}).format(new Date(value));
}
},
{ label: '支付方式', fieldname: 'mode_of_payment' },
{ label: '状态', fieldname: 'status', type: 'Badge' },
{ label: this.$t('Payment Method'), fieldname: 'mode_of_payment' },
{ label: this.$t('Status'), fieldname: 'status', type: 'Badge' },
{
label: '总计',
label: this.$t('Total'),
fieldname: 'net_total_cny',
align: 'right',
format: (_, row) => {

View File

@ -4,7 +4,7 @@
<div>
<FInput
v-model="filters.search"
placeholder="搜索订单..."
:placeholder="$t('Search orders...')"
type="text"
class="w-60"
@input="debouncedSearch"
@ -20,7 +20,7 @@
appearance="primary"
@click="exportToCsv"
>
导出
{{ $t('Export') }}
</Button>
<Button
icon="refresh-cw"
@ -34,8 +34,8 @@
<template v-if="orders.length === 0">
<div class="flex h-60 flex-col items-center justify-center space-y-2 p-4 text-center">
<i-lucide-file-text class="h-8 w-8 text-gray-400" />
<p class="text-base font-medium">无订单记录</p>
<p class="text-sm text-gray-600">暂无订单记录可以显示</p>
<p class="text-base font-medium">{{ $t('No Order Records') }}</p>
<p class="text-sm text-gray-600">{{ $t('No order records to display') }}</p>
</div>
</template>
@ -78,7 +78,7 @@
<div class="flex items-center justify-between p-4">
<div class="text-sm text-gray-600">
显示 {{ orders.length }} 条订单 {{ totalCount }}
{{ $t('Showing {count} orders, total {total}', { count: orders.length, total: totalCount }) }}
</div>
<Button
v-if="hasMoreToLoad"
@ -86,7 +86,7 @@
:loading="loadingMore"
appearance="primary"
>
加载更多
{{ $t('Load More') }}
</Button>
</div>
</template>
@ -95,7 +95,7 @@
</template>
<script>
import { ref, reactive, computed, onMounted } from 'vue';
import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue';
import { Button, Card, Input as FInput, createResource } from 'jingrow-ui';
import StatusIndicator from '../components/StatusIndicator.vue';
import { unparse } from 'papaparse';
@ -109,6 +109,8 @@ export default {
StatusIndicator,
},
setup() {
const instance = getCurrentInstance();
const $t = instance?.appContext.config.globalProperties.$t || ((key) => key);
const pageSize = 20;
const orders = ref([]);
const totalCount = ref(0);
@ -122,15 +124,15 @@ export default {
//
const columns = [
{ key: 'creation', label: '时间', class: '' },
{ key: 'title', label: '标题', class: '' },
{ key: 'order_id', label: '订单号', class: '' },
{ key: 'trade_no', label: '交易号', class: '' },
{ key: 'order_type', label: '订单类型', class: '' },
{ key: 'payment_method', label: '支付方式', class: '' },
{ key: 'description', label: '描述', class: '' },
{ key: 'total', label: '金额', class: 'text-right' },
{ key: 'status', label: '状态', class: '' }
{ key: 'creation', label: $t('Time'), class: '' },
{ key: 'title', label: $t('Title'), class: '' },
{ key: 'order_id', label: $t('Order ID'), class: '' },
{ key: 'trade_no', label: $t('Transaction ID'), class: '' },
{ key: 'order_type', label: $t('Order Type'), class: '' },
{ key: 'payment_method', label: $t('Payment Method'), class: '' },
{ key: 'description', label: $t('Description'), class: '' },
{ key: 'total', label: $t('Amount'), class: 'text-right' },
{ key: 'status', label: $t('Status'), class: '' }
];
//
@ -177,15 +179,15 @@ export default {
// CSV
const fields = [
'标题',
'订单ID',
'交易号',
'订单类型',
'支付方式',
'描述',
'金额',
'状态',
'创建时间'
$t('Title'),
$t('Order ID'),
$t('Transaction ID'),
$t('Order Type'),
$t('Payment Method'),
$t('Description'),
$t('Amount'),
$t('Status'),
$t('Creation Time')
];
//
@ -213,7 +215,7 @@ export default {
//
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
const today = new Date().toISOString().split('T')[0];
const filename = `订单记录-${today}.csv`;
const filename = `${$t('Order Records')}-${today}.csv`;
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = filename;
@ -302,14 +304,14 @@ export default {
//
function getStatusProps(status) {
const statusMap = {
'待支付': { label: '待支付', color: 'orange' },
'已支付': { label: '已支付', color: 'green' },
'交易成功': { label: '交易成功', color: 'green' },
'已取消': { label: '已取消', color: 'red' },
'已退款': { label: '已退款', color: 'red' },
'待支付': { label: $t('Pending Payment'), color: 'orange' },
'已支付': { label: $t('Paid'), color: 'green' },
'交易成功': { label: $t('Transaction Successful'), color: 'green' },
'已取消': { label: $t('Cancelled'), color: 'red' },
'已退款': { label: $t('Refunded'), color: 'red' },
};
return statusMap[status] || { label: status, color: 'blue' };
return statusMap[status] || { label: status || '', color: 'blue' };
}
//

View File

@ -28,14 +28,14 @@ export default {
'brand',
'stripe_mandate_id'
],
emptyStateMessage: '未添加任何卡片',
emptyStateMessage: this.$t('No cards added'),
columns: [
{
label: '卡片姓名',
label: this.$t('Card Name'),
fieldname: 'name_on_card'
},
{
label: '卡片',
label: this.$t('Card'),
fieldname: 'last_4',
width: 1.5,
format(value) {
@ -51,20 +51,20 @@ export default {
{
theme: 'green'
},
() => '默认'
() => this.$t('Default')
);
}
}
},
{
label: '有效期',
label: this.$t('Expiry Date'),
width: 0.5,
format(value, row) {
return `${row.expiry_month}/${row.expiry_year}`;
}
},
{
label: '授权',
label: this.$t('Authorization'),
type: 'Component',
width: 1,
align: 'center',
@ -86,7 +86,7 @@ export default {
return h(
Tooltip,
{
text: '此卡上次支付失败。请使用其他卡片。'
text: this.$t('This card failed to pay last time. Please use another card.')
},
() =>
h(FeatherIcon, {
@ -107,7 +107,7 @@ export default {
rowActions: ({ listResource, row }) => {
return [
{
label: '设为默认',
label: this.$t('Set as Default'),
onClick: () => {
toast.promise(
listResource.runDocMethod.submit({
@ -115,24 +115,24 @@ export default {
name: row.name
}),
{
loading: '正在设为默认...',
success: '默认卡片已设置',
error: '无法设置默认卡片'
loading: this.$t('Setting as default...'),
success: this.$t('Default card set'),
error: this.$t('Failed to set default card')
}
);
},
condition: () => !row.is_default
},
{
label: '移除',
label: this.$t('Remove'),
onClick: () => {
if (row.is_default && this.$team.pg.payment_mode === 'Card') {
toast.error('无法移除默认卡片');
toast.error(this.$t('Cannot remove default card'));
return;
}
confirmDialog({
title: '移除卡片',
message: '确定要移除此卡片吗?',
title: this.$t('Remove Card'),
message: this.$t('Are you sure you want to remove this card?'),
onSuccess: ({ hide }) => {
toast.promise(
listResource.delete.submit(row.name, {
@ -141,12 +141,12 @@ export default {
}
}),
{
loading: '正在移除卡片...',
success: '卡片已移除',
loading: this.$t('Removing card...'),
success: this.$t('Card removed'),
error: error =>
error.messages?.length
? error.messages.join('\n')
: error.message || '无法移除卡片'
: error.message || this.$t('Failed to remove card')
}
);
}
@ -158,7 +158,7 @@ export default {
orderBy: 'creation desc',
primaryAction() {
return {
label: '添加卡片',
label: this.$t('Add Card'),
slots: {
prefix: icon('plus')
},

View File

@ -302,6 +302,10 @@ Start,开始,
Start Date,开始日期,
Start Time,开始时间,
State,,
State/Province/Region,州/省/地区,
City,城市,
Address,地址,
Postal Code,邮政编码,
Status,状态,
Step,,
Steps,脚步,
@ -459,6 +463,7 @@ Site,站点,
Duration,持续时间,
Owner,创建者,
Account Recharge,账户充值,
Recharge,充值,
Recharge your account balance to pay for your services.,为您的账户充值余额,用于支付您的服务费用。,
Online Recharge,在线充值,
Bank Transfer,对公汇款,
@ -752,4 +757,69 @@ DNS Settings,DNS设置,
Other Information,其他信息,
Renew,续费,
Rename,重命名,
Order Records,订单记录,
Balance Details,余额明细,
Developer Earnings,开发者收益,
You do not have permission to view the billing page,您无权查看账单页面,
Search orders...,搜索订单...,
No Order Records,无订单记录,
No order records to display,暂无订单记录可以显示,
Time,时间,
Title,标题,
Order ID,订单号,
Transaction ID,交易号,
Order Type,订单类型,
Showing {count} orders, total {total},显示 {count} 条订单,共 {total} 条,
Load More,加载更多,
Creation Time,创建时间,
Pending Payment,待支付,
Transaction Successful,交易成功,
Balance Recharge,余额充值,
Free Credits,赠送余额,
Applied to Invoice {invoice},冲抵发票 {invoice},
Balance,余额,
Balance Records,余额记录,
Failed to export data,导出数据失败,
No cards added,未添加任何卡片,
Card Name,卡片姓名,
Card,卡片,
Default,默认,
Expiry Date,有效期,
Authorization,授权,
This card failed to pay last time. Please use another card.,此卡上次支付失败。请使用其他卡片。,
Set as Default,设为默认,
Setting as default...,正在设为默认...,
Default card set,默认卡片已设置,
Failed to set default card,无法设置默认卡片,
Cannot remove default card,无法移除默认卡片,
Remove Card,移除卡片,
Are you sure you want to remove this card?,确定要移除此卡片吗?,
Removing card...,正在移除卡片...,
Card removed,卡片已移除,
Failed to remove card,无法移除卡片,
Add Card,添加卡片,
Pending Settlement,待结算,
Settled,已结算,
Account Balance,账户余额,
Billing Address,账单地址,
No address,无地址,
Add Billing Address,添加账单地址,
Edit,编辑,
Billing Name,账单名称,
Update Billing Information,更新账单信息,
Billing name is required,账单名称为必填项,
Billing name contains invalid characters,账单名称包含无效字符,
Your monthly billing charges will be deducted from your account balance,您的每月账单费用将从账户余额中扣除,
Your partner will pay your monthly subscription fees,您的合作伙伴将为您支付每月订阅费用,
Confirm Payment Method,确认支付方式,
By changing the payment method to <strong>Paid By Partner</strong>, the following details will be shared with your partner:<br><br><li>Site/Server Name</li> <li>Plan Name</li><li>Site/Server Active Days</li><br>Are you sure you want to continue?,通过将支付方式更改为<strong>由合作伙伴支付</strong>,以下详细信息将与您的合作伙伴共享:<br><br><li>站点/服务器名称</li> <li>计划名称</li><li>站点/服务器激活天数</li><br>您确定要继续吗?,
Change Payment Method,更改支付方式,
Please recharge your account balance before changing payment method.,在更改支付方式之前,请先为您的账户充值余额。,
Pay with Alipay,使用支付宝支付,
Billing Details,账单详情,
Please add billing details to your account before continuing.,在继续之前,请为您的账户添加账单详情。,
Country and City,国家和城市,
State and Postal Code,州和邮政编码,
{field} is required,{field} 是必填项,
Billing information updated,账单信息已更新,

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