Browse Source

Merge pull request #6614 from nocodb/fix/i18n

fix: i18n
pull/6758/head
Muhammed Mustafa 1 year ago committed by GitHub
parent
commit
36f1eefaa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      packages/nc-gui/components/account/UsersModal.vue
  2. 6
      packages/nc-gui/components/cell/attachment/RenameFile.vue
  3. 2
      packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue
  4. 2
      packages/nc-gui/components/dlg/AirtableImport.vue
  5. 4
      packages/nc-gui/components/dlg/share-and-collaborate/Collaborate.vue
  6. 5
      packages/nc-gui/components/dlg/share-and-collaborate/SharePage.vue
  7. 4
      packages/nc-gui/components/dlg/share-and-collaborate/View.vue
  8. 3
      packages/nc-gui/components/general/FullScreen.vue
  9. 2
      packages/nc-gui/components/general/JoinCloud.vue
  10. 7
      packages/nc-gui/components/notification/Card.vue
  11. 5
      packages/nc-gui/components/roles/Badge.vue
  12. 15
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  13. 19
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  14. 8
      packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue
  15. 33
      packages/nc-gui/components/template/Editor.vue
  16. 30
      packages/nc-gui/components/workspace/CreateProjectDlg.vue
  17. 18
      packages/nc-gui/components/workspace/InviteCollabDlg.vue
  18. 3
      packages/nc-gui/components/workspace/MoveProjectDlg.vue
  19. 30
      packages/nc-gui/components/workspace/Settings.vue
  20. 65
      packages/nc-gui/lang/en.json
  21. 12
      packages/nc-gui/layouts/base.vue
  22. 12
      packages/nc-gui/layouts/new.vue
  23. 6
      packages/nc-gui/pages/index/[typeOrId]/form/[viewId].vue
  24. 25
      packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/survey.vue
  25. 7
      packages/nc-gui/pages/reset/[id].vue
  26. 28
      packages/nocodb-sdk/src/lib/enums.ts

7
packages/nc-gui/components/account/UsersModal.vue

@ -175,7 +175,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
v-bind="validateInfos.emails"
validate-trigger="onBlur"
name="emails"
:rules="[{ required: true, message: 'Please input email' }]"
:rules="[{ required: true, message: $t('msg.plsInputEmail') }]"
>
<div class="ml-1 mb-1 text-xs text-gray-500" data-rec="true">{{ $t('datatype.Email') }}:</div>
@ -190,9 +190,8 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
</div>
<div class="flex flex-col w-2/4">
<a-form-item name="role" :rules="[{ required: true, message: 'Role required' }]">
<div class="ml-1 mb-1 text-xs text-gray-500" data-rec="true">{{ $t('labels.selectUserRole') }}</div>
<a-form-item name="role" :rules="[{ required: true, message: $t('msg.roleRequired') }]">
<div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('labels.selectUserRole') }}</div>
<a-select v-model:value="usersData.role" class="nc-user-roles" dropdown-class-name="nc-dropdown-user-role">
<a-select-option
class="nc-role-option"

6
packages/nc-gui/components/cell/attachment/RenameFile.vue

@ -1,5 +1,7 @@
<script lang="ts" setup>
import { onKeyStroke, onMounted, reactive, ref } from '#imports'
import { onKeyStroke, onMounted, reactive, ref, useI18n } from '#imports'
const { t } = useI18n()
const props = defineProps<{
title: string
@ -29,7 +31,7 @@ function renameFile(fileName: string) {
// }
const rules = {
title: [{ required: true, message: 'title is required.' }],
title: [{ required: true, message: t('labels.titleRequired') }],
}
function onCancel() {

2
packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue

@ -126,7 +126,7 @@ const validators = computed(() => {
'title': [
{
required: true,
message: 'Source name is required',
message: t('labels.sourceNameRequired'),
},
baseTitleValidator,
],

2
packages/nc-gui/components/dlg/AirtableImport.vue

@ -346,7 +346,7 @@ onMounted(async () => {
<a-input
v-model:value="syncSource.details.syncSourceUrlOrId"
class="nc-input-shared-base"
:placeholder="`${$t('labels.sharedBase')} URL`"
:placeholder="`${$t('labels.sharedBaseUrl')}`"
size="large"
/>
</a-form-item>

4
packages/nc-gui/components/dlg/share-and-collaborate/Collaborate.vue

@ -58,7 +58,7 @@ watch(
v-bind="validateInfos.emails"
validate-trigger="onBlur"
name="emails"
:rules="[{ required: true, message: 'Please input email' }]"
:rules="[{ required: true, message: t('msg.plsInputEmail') }]"
>
<a-input
v-model:value="invitationUsersData.emails"
@ -72,7 +72,7 @@ watch(
</div>
<div class="flex flex-col w-1/5">
<a-form-item name="role" :rules="[{ required: true, message: 'Role required' }]">
<a-form-item name="role" :rules="[{ required: true, message: t('msg.roleRequired') }]">
<a-select
v-model:value="invitationUsersData.role"
class="!rounded-md !bg-white"

5
packages/nc-gui/components/dlg/share-and-collaborate/SharePage.vue

@ -341,29 +341,24 @@ const isPublicShareDisabled = computed(() => {
</div>
<div v-if="activeView?.type === ViewTypes.FORM" class="flex flex-row justify-between">
<!-- use RTL orientation in form - todo: i18n -->
<div class="text-black">{{ $t('activity.surveyMode') }}</div>
<a-switch
v-model:checked="surveyMode"
v-e="['c:share:view:surver-mode:toggle']"
data-testid="nc-modal-share-view__surveyMode"
>
<!-- todo i18n -->
</a-switch>
</div>
<div v-if="activeView?.type === ViewTypes.FORM && isEeUI" class="flex flex-row justify-between">
<!-- use RTL orientation in form - todo: i18n -->
<div class="text-black">{{ $t('activity.rtlOrientation') }}</div>
<a-switch
v-model:checked="withRTL"
v-e="['c:share:view:rtl-orientation:toggle']"
data-testid="nc-modal-share-view__RTL"
>
<!-- todo i18n -->
</a-switch>
</div>
<div v-if="activeView?.type === ViewTypes.FORM" class="flex flex-col justify-between gap-y-1 bg-gray-50 rounded-md">
<!-- todo: i18n -->
<div class="flex flex-row justify-between">
<div class="text-black">{{ $t('activity.useTheme') }}</div>
<a-switch

4
packages/nc-gui/components/dlg/share-and-collaborate/View.vue

@ -118,11 +118,11 @@ watch(showShareModal, (val) => {
>
<div v-if="isInvitationLinkCopied" class="flex flex-row items-center gap-x-1">
<MdiTick class="h-3.5" />
Copied invite link
{{ $t('activity.copiedInviteLink') }}
</div>
<div v-else class="flex flex-row items-center gap-x-1">
<MdiContentCopy class="h-3.3" />
Copy invite link
{{ $t('activity.copyInviteLink') }}
</div>
</a-button>
</div>

3
packages/nc-gui/components/general/FullScreen.vue

@ -34,9 +34,8 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<template>
<a-tooltip placement="left">
<!-- todo: i18n -->
<template #title>
<span class="text-xs">{{ isSidebarsOpen ? 'Full width' : 'Exit full width' }}</span>
<span class="text-xs">{{ isSidebarsOpen ? $t('activity.fullWidth') : $t('activity.exitFullWidth') }}</span>
</template>
<div
v-e="['c:toolbar:fullscreen']"

2
packages/nc-gui/components/general/JoinCloud.vue

@ -13,7 +13,7 @@ import { iconMap } from '#imports'
target="_blank"
>
<component :is="iconMap.cloud" class="mt-[1px] text-black font-bold" />
<div class="px-1 text-xs font-bold text-gray-800">Join</div>
<div class="px-1 text-xs font-bold text-gray-800">{{ $t('general.join') }}</div>
</div>
<div
class="group flex justify-center items-center rounded-r-[3px] w-full cursor-pointer px-1 py-1 text-primary border-r-1 border-b-1 border-t-1 border-[#cdd1d6] m-0"

7
packages/nc-gui/components/notification/Card.vue

@ -25,8 +25,7 @@ const groupType = computed({
<div class="p-3" @click.stop>
<div class="flex items-center">
<span class="text-md font-medium text-[#212121]">
<!-- todo: i18n -->
Notification
{{ $t('general.notification') }}
</span>
<div class="flex-grow"></div>
<div
@ -34,7 +33,7 @@ const groupType = computed({
class="cursor-pointer text-xs text-gray-500 hover:text-primary"
@click.stop="notificationStore.markAllAsRead"
>
Mark all as read
{{ $t('activity.markAllAsRead') }}
</div>
</div>
</div>
@ -48,7 +47,7 @@ const groupType = computed({
>
<template v-if="!notifications?.length">
<div class="flex flex-col gap-2 items-center justify-center">
<div class="text-sm text-gray-400">You have no new notifications</div>
<div class="text-sm text-gray-400">{{ $t('msg.noNewNotifications') }}</div>
<GeneralIcon icon="inbox" class="!text-40px text-gray-400" />
</div>
</template>

5
packages/nc-gui/components/roles/Badge.vue

@ -26,17 +26,16 @@ const sizeSelect = computed(() => props.size)
const roleProperties = computed(() => {
const role = roleRef.value
const color = RoleColors[role]
const icon = RoleIcons[role]
const label = RoleLabels[role]
return {
color,
icon,
label,
}
})
</script>
<template>
@ -63,7 +62,7 @@ const roleProperties = computed(() => {
>
<GeneralIcon :icon="roleProperties.icon" />
<span class="flex whitespace-nowrap">
{{ roleProperties.label }}
{{ $t(`objects.roleType.${roleProperties.label}`) }}
</span>
<GeneralIcon v-if="clickableRef" icon="arrowDown" />
</div>

15
packages/nc-gui/components/smartsheet/expanded-form/index.vue

@ -294,8 +294,7 @@ onMounted(async () => {
await loadCommentsAndLogs()
} catch (e: any) {
if (e.response?.status === 404) {
// todo: i18n
message.error('Record not found')
message.error(t("msg.noRecordFound"))
router.replace({ query: {} })
} else throw e
}
@ -358,9 +357,9 @@ useActiveKeyupListener(
if (changedColumns.value.size > 0) {
await Modal.confirm({
title: 'Do you want to save the changes?',
okText: 'Save',
cancelText: 'Discard',
title: t('msg.saveChanges'),
okText: t('general.save'),
cancelText: t('labels.discard'),
onOk: async () => {
await save()
reloadHook?.trigger(null)
@ -373,8 +372,8 @@ useActiveKeyupListener(
} else if (isNew.value) {
await Modal.confirm({
title: 'Do you want to save the record?',
okText: 'Save',
cancelText: 'Discard',
okText: t('general.save'),
cancelText: t('labels.discard'),
onOk: async () => {
const data = await _save(rowState.value)
await syncLTARRefs(data)
@ -402,7 +401,7 @@ const onDeleteRowClick = () => {
const onConfirmDeleteRowClick = async () => {
showDeleteRowModal.value = false
await deleteRowById(primaryKey.value)
message.success('Record deleted')
message.success(t('msg.rowDeleted'))
reloadTrigger.trigger()
onClose()
showDeleteRowModal.value = false

19
packages/nc-gui/components/smartsheet/toolbar/ShareView.vue

@ -240,10 +240,6 @@ const copyIframeCode = async () => {
}
}
}
watch(shared, () => {
console.log('shared', shared.value)
})
</script>
<template>
@ -292,23 +288,21 @@ watch(shared, () => {
@click="copyIframeCode"
>
<component :is="iconMap.embed" class="text-gray-500" />
Embed this view in your site
{{ $t('labels.embedInSite') }}
</div>
<div class="px-1 mt-2 flex flex-col gap-3">
<!-- todo: i18n -->
<div class="text-gray-500 border-b-1">Options</div>
<div class="text-gray-500 border-b-1">{{ $t('general.options') }}</div>
<div class="px-1 flex flex-col gap-2">
<div>
<!-- Survey Mode; todo: i18n -->
<a-checkbox
v-if="shared.type === ViewTypes.FORM"
v-model:checked="surveyMode"
data-testid="nc-modal-share-view__survey-mode"
class="!text-sm"
>
Use Survey Mode
{{ $t('general.useSurveyMode') }}
</a-checkbox>
<!-- <Transition name="layout" mode="out-in">
@ -379,9 +373,8 @@ watch(shared, () => {
</div>
<div v-if="shared.type === ViewTypes.FORM">
<!-- todo: i18n -->
<a-checkbox v-model:checked="viewTheme" data-testid="nc-modal-share-view__with-theme" class="!text-sm">
Use Theme
{{ $t('activity.useTheme') }}
</a-checkbox>
<Transition name="layout" mode="out-in">
@ -400,10 +393,8 @@ watch(shared, () => {
</div>
<div v-if="shared.type === ViewTypes.FORM && isRtl">
<!-- use RTL orientation in form - todo: i18n -->
<a-checkbox v-model:checked="withRTL" data-testid="nc-modal-share-view__locale" class="!text-sm">
<!-- todo i18n -->
RTL Orientation
{{ $t('activity.rtlOrientation') }}
</a-checkbox>
</div>
</div>

8
packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue

@ -217,7 +217,7 @@ watch(
v-bind="validateInfos.emails"
validate-trigger="onBlur"
name="emails"
:rules="[{ required: true, message: 'Please input email' }]"
:rules="[{ required: true, message: t('msg.plsInputEmail') }]"
>
<div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('datatype.Email') }}:</div>
@ -233,7 +233,7 @@ watch(
</div>
<div class="flex flex-col w-1/4">
<a-form-item name="role" :rules="[{ required: true, message: 'Role required' }]">
<a-form-item name="role" :rules="[{ required: true, message: t('msg.roleRequired') }]">
<div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('labels.selectUserRole') }}</div>
<a-select
@ -274,9 +274,9 @@ watch(
</div>
<div class="flex flex-row justify-end gap-x-2 border-t-1 border-gray-100 pt-3">
<a-button key="back" class="!rounded-md" @click="cancel">Cancel</a-button>
<a-button key="back" class="!rounded-md" @click="cancel">{{$t('general.cancel')}}</a-button>
<a-button class="!rounded-md">Manage base access</a-button>
<a-button key="submit" class="!rounded-md" type="primary" :loading="loading">Share</a-button>
<a-button key="submit" class="!rounded-md" type="primary" :loading="loading">{{$t('activity.share')}}</a-button>
</div>
</div>
</GeneralModal>

33
packages/nc-gui/components/template/Editor.vue

@ -703,8 +703,7 @@ function handleUIDTChange(column, table) {
<template #extra>
<a-tooltip bottom>
<template #title>
<!-- TODO: i18n -->
<span>Delete Table</span>
<span>{{ $t('activity.deleteTable') }}</span>
</template>
<component
:is="iconMap.delete"
@ -805,8 +804,7 @@ function handleUIDTChange(column, table) {
<template #extra>
<a-tooltip bottom>
<template #title>
<!-- TODO: i18n -->
<span>Delete Table</span>
<span>{{ $t('activity.deleteTable') }}</span>
</template>
<component
:is="iconMap.delete"
@ -843,8 +841,7 @@ function handleUIDTChange(column, table) {
<template v-else-if="column.key === 'dtxp' && hasSelectColumn[tableIdx]">
<span>
<!-- TODO: i18n -->
Options
{{ $t('general.options') }}
</span>
</template>
</template>
@ -869,7 +866,11 @@ function handleUIDTChange(column, table) {
<a-select-option v-for="(option, i) of uiTypeOptions" :key="i" :value="option.value">
<a-tooltip placement="right">
<template v-if="isSelectDisabled(option.label, table.columns[record.key]?._disableSelect)" #title>
The field is too large to be converted to {{ option.label }}
{{
$t('msg.tooLargeFieldEntity', {
entity: option.label,
})
}}
</template>
{{ option.label }}
</a-tooltip>
@ -887,8 +888,7 @@ function handleUIDTChange(column, table) {
<template v-if="column.key === 'action'">
<a-tooltip v-if="record.key === 0">
<template #title>
<!-- TODO: i18n -->
<span>Primary Value</span>
<span>{{ $t('general.primaryValue') }}</span>
</template>
<div class="flex items-center float-right mr-4">
@ -898,8 +898,7 @@ function handleUIDTChange(column, table) {
<a-tooltip v-else>
<template #title>
<!-- TODO: i18n -->
<span>Delete Column</span>
<span>{{ $t('activity.column.delete') }}</span>
</template>
<a-button type="text" @click="deleteTableColumn(tableIdx, record.key)">
@ -915,8 +914,7 @@ function handleUIDTChange(column, table) {
<div class="mt-5 flex gap-2 justify-center">
<a-tooltip bottom>
<template #title>
<!-- TODO: i18n -->
<span>Add Number Column</span>
<span>{{ $t('activity.column.addNumber') }}</span>
</template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'Number')">
@ -928,8 +926,7 @@ function handleUIDTChange(column, table) {
<a-tooltip bottom>
<template #title>
<!-- TODO: i18n -->
<span>Add SingleLineText Column</span>
<span>{{ $t('activity.column.addSingleLineText') }}</span>
</template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'SingleLineText')">
@ -941,8 +938,7 @@ function handleUIDTChange(column, table) {
<a-tooltip bottom>
<template #title>
<!-- TODO: i18n -->
<span>Add LongText Column</span>
<span>{{ $t('activity.column.addLongText') }}</span>
</template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'LongText')">
@ -954,8 +950,7 @@ function handleUIDTChange(column, table) {
<a-tooltip bottom>
<template #title>
<!-- TODO: i18n -->
<span>Add Other Column</span>
<span>{{ $t('activity.column.addOther') }}</span>
</template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'SingleLineText')">

30
packages/nc-gui/components/workspace/CreateProjectDlg.vue

@ -3,7 +3,7 @@ import type { RuleObject } from 'ant-design-vue/es/form'
import type { Form, Input } from 'ant-design-vue'
import type { VNodeRef } from '@vue/runtime-core'
import { computed } from '@vue/reactivity'
import { NcProjectType, baseTitleValidator, extractSdkResponseErrorMsg, ref, useGlobal, useVModel } from '#imports'
import { NcProjectType, baseTitleValidator, extractSdkResponseErrorMsg, ref, useGlobal, useI18n, useVModel } from '#imports'
const props = defineProps<{
modelValue: boolean
@ -12,6 +12,8 @@ const props = defineProps<{
const emit = defineEmits(['update:modelValue'])
const { t } = useI18n()
const dialogShow = useVModel(props, 'modelValue', emit)
const baseType = computed(() => props.type ?? NcProjectType.DB)
@ -24,7 +26,7 @@ const { navigateToProject } = useGlobal()
const nameValidationRules = [
{
required: true,
message: 'Database name is required',
message: t('msg.info.dbNameRequired'),
},
baseTitleValidator,
] as RuleObject[]
@ -95,7 +97,11 @@ const typeLabel = computed(() => {
<!-- Create A New Table -->
<div class="flex flex-row items-center">
<GeneralProjectIcon :type="baseType" class="mr-2.5 !text-lg !h-4" />
Create {{ typeLabel }}
{{
$t('general.createEntity', {
entity: typeLabel,
})
}}
</div>
</template>
<div class="mt-3">
@ -121,19 +127,27 @@ const typeLabel = computed(() => {
</a-form>
<div class="flex flex-row justify-end mt-7 gap-x-2">
<NcButton type="secondary" @click="dialogShow = false">Cancel</NcButton>
<NcButton type="secondary" @click="dialogShow = false">{{ $t('general.cancel') }}</NcButton>
<NcButton
v-e="['a:base:create']"
data-testid="docs-create-proj-dlg-create-btn"
:loading="creating"
type="primary"
:label="`Create ${typeLabel}`"
:loading-label="`Creating ${typeLabel}`"
:label="`${$t('general.create')} ${typeLabel}`"
:loading-label="`${$t('general.creating')} ${typeLabel}`"
@click="createProject"
>
{{ `Create ${typeLabel}` }}
{{
$t('general.createEntity', {
entity: typeLabel,
})
}}
<template #loading>
{{ `Creating ${typeLabel}` }}
{{
$t('general.creatingEntity', {
entity: typeLabel,
})
}}
</template>
</NcButton>
</div>

18
packages/nc-gui/components/workspace/InviteCollabDlg.vue

@ -42,12 +42,18 @@ const validators = computed(() => {
{
validator: (rule: any, value: string, callback: (errMsg?: string) => void) => {
if (!value || value.length === 0) {
callback('Email is required')
callback(t('msg.error.signUpRules.emailRequired'))
return
}
const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e))
if (invalidEmails.length > 0) {
callback(`${invalidEmails.length > 1 ? ' Invalid emails:' : 'Invalid email:'} ${invalidEmails.join(', ')} `)
callback(
`${
invalidEmails.length > 1
? `${t('msg.error.signUpRules.invalidEmails')}: `
: `${t('msg.error.signUpRules.invalidEmail')}: `
} ${invalidEmails.join(', ')} `,
)
} else {
callback()
}
@ -119,8 +125,6 @@ const emailInput = ref((el) => {
wrap-class-name="nc-modal-invite-user"
@cancel="emit('closed')"
>
<!-- TODO: i18n -->
<div class="flex flex-col">
<div class="flex flex-row justify-between items-center pb-1.5 mb-2 border-b-1 w-full">
<a-typography-title class="select-none" :level="4"> {{ $t('activity.inviteUser') }}</a-typography-title>
@ -137,7 +141,7 @@ const emailInput = ref((el) => {
<div class="flex flex-col mt-1 border-b-1 pb-5">
<div class="flex flex-row items-center pl-1.5 pb-1 h-[1.1rem]">
<MdiAccountOutline />
<div class="text-xs ml-0.5 mt-0.5">Copy Invite Token</div>
<div class="text-xs ml-0.5 mt-0.5">{{ $t('title.copyInviteToken') }}</div>
</div>
<a-alert class="mt-1" type="success" show-icon>
@ -193,7 +197,7 @@ const emailInput = ref((el) => {
v-bind="validateInfos.emails"
validate-trigger="onBlur"
name="emails"
:rules="[{ required: true, message: 'Please input email' }]"
:rules="[{ required: true, message: $t('msg.plsInputEmail') }]"
>
<div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('datatype.Email') }}:</div>
@ -207,7 +211,7 @@ const emailInput = ref((el) => {
</div>
<div class="flex flex-col w-2/4">
<a-form-item name="role" :rules="[{ required: true, message: 'Role required' }]">
<a-form-item name="role" :rules="[{ required: true, message: $t('msg.roleRequired') }]">
<div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('labels.selectUserRole') }}</div>
<a-select v-model:value="usersData.role" class="nc-user-roles" dropdown-class-name="nc-dropdown-user-role">

3
packages/nc-gui/components/workspace/MoveProjectDlg.vue

@ -55,8 +55,7 @@ const ownedWorkspaces = computed(() => {
<!-- Create A New Table -->
<div class="prose-xl font-bold self-center my-4">{{ $t('activity.moveProject') }}</div>
<!-- todo: i18n -->
<div class="mb-2">Workspace</div>
<div class="mb-2">{{ $t('objects.workspace') }}</div>
<a-select v-model:value="workspaceId" class="w-full" show-search>
<a-select-option v-for="workspace of ownedWorkspaces" :key="workspace.id" :value="workspace.id">
{{ workspace.title }}

30
packages/nc-gui/components/workspace/Settings.vue

@ -1,5 +1,7 @@
<script lang="ts" setup>
import { useI18n } from '#imports'
const { signOut } = useGlobal()
const { t } = useI18n()
const { deleteWorkspace, navigateToWorkspace, updateWorkspace } = useWorkspace()
const { workspacesList, activeWorkspaceId, activeWorkspace, workspaces } = storeToRefs(useWorkspace())
@ -17,9 +19,9 @@ const form = ref({
const formRules = {
title: [
{ required: true, message: 'Workspace name required' },
{ min: 3, message: 'Workspace name must be at least 3 characters long' },
{ max: 50, message: 'Workspace name must be at most 50 characters long' },
{ required: true, message: t('msg.info.wsNameRequired') },
{ min: 3, message: t('msg.info.wsNameMinLength') },
{ max: 50, message: t('msg.info.wsNameMaxLength') },
],
}
@ -104,9 +106,9 @@ const onCancel = () => {
<template>
<div class="flex flex-col items-center nc-workspace-settings-settings">
<div class="item flex flex-col w-full">
<div class="font-medium text-base">Change Workspace Name</div>
<div class="font-medium text-base">{{ $t('labels.changeWsName') }}</div>
<a-form ref="formValidator" layout="vertical" no-style :model="form" class="w-full" @finish="titleChange">
<div class="text-gray-500 mt-6 mb-1.5">Workspace name</div>
<div class="text-gray-500 mt-6 mb-1.5">{{ $t('objects.workspace') $t('general.name')}}</div>
<a-form-item name="title" :rules="formRules.title">
<a-input
v-model:value="form.title"
@ -123,8 +125,8 @@ const onCancel = () => {
data-testid="nc-workspace-settings-settings-rename-cancel"
@click="onCancel"
>
<template #loading> Renaming Workspace </template>
Cancel
<template #loading> {{ $t('title.renamingWs') }} </template>
{{ $t('general.cancel') }}
</NcButton>
<NcButton
v-e="['c:workspace:settings:rename']"
@ -134,18 +136,18 @@ const onCancel = () => {
:loading="isDeleting"
data-testid="nc-workspace-settings-settings-rename-submit"
>
<template #loading> Renaming Workspace </template>
Rename Workspace
<template #loading> {{ $t('title.renamingWs') }} </template>
{{ $t('title.renameWs') }}
</NcButton>
</div>
</a-form>
</div>
<div class="item flex flex-col">
<div class="font-medium text-base">Delete Workspace</div>
<div class="text-gray-500 mt-2">Delete this workspace and all its contents.</div>
<div class="font-medium text-base">{{ $t('title.deleteWs') }}</div>
<div class="text-gray-500 mt-2">{{ $t('msg.info.wsDeleteDlg') }}</div>
<div class="flex flex-row mt-8 gap-x-2">
<a-checkbox v-model:checked="isConfirmed" />
<div class="flex">I understand that this action is irreversible</div>
<div class="flex">{{ $t('msg.info.userConfirmation') }}</div>
</div>
<div class="flex flex-row w-full justify-end mt-8">
@ -156,8 +158,8 @@ const onCancel = () => {
:loading="isDeleting"
@click="onDelete"
>
<template #loading> Deleting Workspace </template>
Delete Workspace
<template #loading> {{ $t('title.deletingWs') }} </template>
{{ $t('title.deleteWs') }}
</NcButton>
</div>
</div>

65
packages/nc-gui/lang/en.json

@ -76,6 +76,9 @@
"whatsappTwilio": "WhatsApp Twilio",
"submit": "Submit",
"create": "Create",
"createEntity": "Create {entity}",
"creating": "Creating",
"creatingEntity": "Creating {entity}",
"details": "Details",
"skip": "Skip",
"duplicate": "Duplicate",
@ -174,7 +177,14 @@
"count": "Count",
"countDistinct": "Count Distinct",
"sumDistinct": "Sum Distinct",
"avgDistinct": "Avg Distinct"
"avgDistinct": "Avg Distinct",
"join": "Join",
"options": "Options",
"primaryValue": "Primary Value",
"useSurveyMode": "Use Survey Mode",
"shift": "Shift",
"enter": "Enter",
"seconds": "Seconds"
},
"objects": {
"workspace": "Workspace",
@ -215,6 +225,8 @@
"editor": "Editor",
"commenter": "Commenter",
"viewer": "Viewer",
"noaccess":"No Access",
"superAdmin": "Super Admin",
"orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer"
},
@ -312,8 +324,13 @@
"downloadFile": "Download File",
"renameTable": "Rename Table",
"renamingTable": "Renaming Table",
"renamingWs": "Renaming Workspace",
"renameWs": "Rename Workspace",
"deleteWs": "Delete Workspace",
"deletingWs": "Deleting Workspace",
"copyAuthToken": "Copy Auth Token",
"copiedAuthToken": "Copied Auth Token",
"copyInviteToken": "Copy Invite Token",
"showSidebar": "Show Sidebar",
"hideSidebar": "Hide Sidebar",
"creatingTable": "Creating Table",
@ -373,6 +390,7 @@
"resetPasswordMenu": "Reset Password",
"tokens": "Tokens",
"userManagement": "User Management",
"accountManagement": "Account management",
"licence": "Licence",
"allowAllMimeTypes": "Allow All Mime Types",
"defaultView": "Default View",
@ -550,7 +568,7 @@
"requestDataSource": "Request a data source you need?",
"apiKey": "API Key",
"personalAccessToken": "Personal Access Token",
"sharedBase": "Shared Base",
"sharedBaseUrl": "Shared Base URL",
"importData": "Import Data",
"importSecondaryViews": "Import Secondary Views",
"importRollupColumns": "Import Rollup Fields",
@ -589,7 +607,13 @@
"includeData": "Include Data",
"includeView": "Include View",
"includeWebhook": "Include Webhook",
"zoomInToViewColumns": "Zoom in to view fields"
"zoomInToViewColumns": "Zoom in to view columns",
"embedInSite": "Embed this view in your site",
"titleRequired": "title is required.",
"sourceNameRequired": "Source name is required",
"changeWsName": "Change Workspace Name",
"pressEnter": "Press Enter",
"newFormLoaded": "New form will be loaded after"
},
"activity": {
"onCondition": "On Condition",
@ -622,6 +646,8 @@
"useTheme": "Use Theme",
"copyLink": "Copy Link",
"copiedLink": "Link Copied",
"copyInviteLink": "Copy invite link",
"copiedInviteLink": "Copied invite link",
"copyUrl": "Copy URL",
"moreColors": "More Colors",
"moveProject": "Move Base",
@ -763,6 +789,16 @@
"toggleCommentsDraw": "Toggle comments draw",
"expandRecord": "Expand Record",
"deleteRecord": "Delete Record",
"fullWidth": "Full width",
"exitFullWidth": "Exit full width",
"markAllAsRead": "Mark all as read",
"column":{
"delete": "Delete Field",
"addNumber": "Add Number Field",
"addSingleLineText": "Add SingleLineText Field",
"addLongText": "Add LongText Field",
"addOther":"Add Other Field"
},
"erd": {
"showColumns": "Show Fields",
"showPkAndFk": "Show Primary and Foreign Keys",
@ -916,6 +952,7 @@
"createNewOptionNamed": "Create new option named"
},
"plsEnterANumber": "Please enter a number",
"plsInputEmail": "Please input email",
"invalidDate": "Invalid date",
"invalidLocale": "Invalid locale",
"invalidCurrencyCode": "Invalid Currency Code",
@ -954,6 +991,12 @@
"areYouSureUWantTo": "Are you sure you want to delete the following",
"idColumnRequired": "ID field is required, you can rename this later if required.",
"length59Required": "The length exceeds the max 59 characters",
"noNewNotifications": "You have no new notifications",
"noRecordFound": "Record not found",
"rowDeleted": "Record deleted",
"saveChanges": "Do you want to save the changes?",
"tooLargeFieldEntity": "The field is too large to be converted to {entity}",
"roleRequired": "Role required",
"warning": {
"dbValid": "Please make sure database you are trying to connect is valid! This operation can cause schema loss!!",
"barcode": {
@ -1113,7 +1156,19 @@
"computedFieldDeleteWarning": "contents are read-only",
"noMoreRecords": "No more records",
"tokenNameNotEmpty": "Token name should not be empty",
"tokenNameMaxLength": "Token name should not be more than 255 characters"
"tokenNameMaxLength": "Token name should not be more than 255 characters",
"dbNameRequired": "Database name is required",
"wsNameRequired":"Workspace name required",
"wsNameMinLength":"Workspace name must be at least 3 characters long",
"wsNameMaxLength":"Workspace name must be at most 50 characters long",
"wsDeleteDlg": "Delete this workspace and all it’s contents.",
"userConfirmation": "I understand that this action is irreversible",
"pageNotFound": "Page Not Found",
"makeLineBreak": "to make a line break",
"goToPrevious": "Go to previous",
"goToNext": "Go to next",
"thankYou": "Thank you!",
"submittedFormData": "You have successfully submitted the form data."
},
"error": {
"nameRequired": "Name Required",
@ -1131,7 +1186,7 @@
"dbConnectionFailed": "Connection Failure:",
"nullFilterExists": "Null filter exists. Please remove them",
"signUpRules": {
"emailReqd": "Email is required",
"emailRequired": "Email is required",
"emailInvalid": "Email must be valid",
"passwdRequired": "Password is required",
"passwdLength": "You password must be atleast 8 characters",

12
packages/nc-gui/layouts/base.vue

@ -69,7 +69,7 @@ hooks.hook('page:finish', () => {
<LazyGeneralReleaseInfo />
<a-tooltip v-if="!appInfo.ee" placement="bottom" :mouse-enter-delay="1">
<template #title> Switch language</template>
<template #title>{{ $t('title.switchLanguage') }}</template>
<div class="flex pr-4 items-center">
<LazyGeneralLanguage class="cursor-pointer text-2xl hover:text-accent" />
@ -97,17 +97,15 @@ hooks.hook('page:finish', () => {
</nuxt-link>
</a-menu-item>
<a-menu-divider class="!m-0" />
<!-- <a-menu-item v-if="isUIAllowed('superAdminAppStore')" key="0" class="!rounded-t">
<!-- <a-menu-divider class="!m-0" />
<a-menu-item v-if="isUIAllowed('superAdminAppStore')" key="0" class="!rounded-t">
<nuxt-link
v-e="['c:settings:appstore', { page: true }]"
class="nc-base-menu-item group !no-underline"
to="/admin/users"
>
<MdiShieldAccountOutline class="mt-1 group-hover:text-accent" />&nbsp;
&lt;!&ndash; todo: i18n &ndash;&gt;
<span class="prose group-hover:text-primary">Account management</span>
<span class="prose group-hover:text-primary">{{ $t('title.accountManagement') }}</span>
</nuxt-link>
</a-menu-item>
@ -129,7 +127,7 @@ hooks.hook('page:finish', () => {
</a-layout-header>
<a-tooltip v-if="!appInfo.ee" placement="bottom">
<template #title> Switch language</template>
<template #title>{{ $t('title.switchLanguage') }}</template>
<LazyGeneralLanguage v-if="!signedIn && !route.params.baseId && !route.params.erdUuid" class="nc-lang-btn" />
</a-tooltip>

12
packages/nc-gui/layouts/new.vue

@ -92,27 +92,25 @@ export default {
<nuxt-link v-e="['c:navbar:user:email']" class="nc-base-menu-item group !no-underline" to="/account/users">
<MdiAccountCircleOutline class="mt-1 group-hover:text-accent" />&nbsp;
<div class="prose group-hover:text-primary">
<div>Account</div>
<div>{{ $t('labels.account') }}</div>
<div class="text-xs text-gray-500">{{ email }}</div>
</div>
</nuxt-link>
</a-menu-item>
<a-menu-divider class="!m-0" />
<!-- <a-menu-item v-if="isUIAllowed('superAdminAppStore')" key="0" class="!rounded-t">
<!-- <a-menu-item v-if="isUIAllowed('superAdminAppStore')" key="0" class="!rounded-t">
<nuxt-link
v-e="['c:settings:appstore', { page: true }]"
class="nc-base-menu-item group !no-underline"
to="/admin/users"
>
<MdiShieldAccountOutline class="mt-1 group-hover:text-accent" />&nbsp;
&lt;!&ndash; todo: i18n &ndash;&gt;
<span class="prose group-hover:text-primary">Account management</span>
<span class="prose group-hover:text-primary">{{ $t('title.accountManagement') }}</span>
</nuxt-link>
</a-menu-item>
</a-menu-item> -->
<a-menu-divider class="!m-0" /> -->
<a-menu-divider class="!m-0" />
<a-menu-item key="1" class="!rounded-b group" data-testid="nc-menu-accounts__sign-out">
<div

6
packages/nc-gui/pages/index/[typeOrId]/form/[viewId].vue

@ -12,6 +12,7 @@ import {
provide,
reactive,
ref,
useI18n,
useProvideSharedFormStore,
useProvideSmartsheetStore,
useRoute,
@ -27,6 +28,8 @@ useSidebar('nc-left-sidebar', { hasSidebar: false })
const route = useRoute()
const { t } = useI18n()
const { loadSharedView, sharedView, sharedViewMeta, meta, notFound, password, passwordDlg, passwordError } =
useProvideSharedFormStore(route.params.viewId as string)
@ -43,7 +46,7 @@ if (!notFound.value) {
applyLanguageDirection(sharedViewMeta.value.rtl ? 'rtl' : 'ltr')
} else {
navigateTo('/error/404')
throw createError({ statusCode: 404, statusMessage: 'Page Not Found' })
throw createError({ statusCode: 404, statusMessage: t('msg.pageNotFound') })
}
const form = reactive({
@ -74,7 +77,6 @@ watch(
@close="passwordDlg = false"
>
<div class="w-full flex flex-col gap-4">
<!-- todo: i18n -->
<h2 class="text-xl font-semibold">{{ $t('msg.thisSharedViewIsProtected') }}</h2>
<a-form layout="vertical" no-style :model="form" @finish="loadSharedView">

25
packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/survey.vue

@ -318,8 +318,8 @@ onMounted(() => {
</div>
<div v-if="field.uidt === UITypes.LongText" class="text-sm text-gray-500 flex flex-wrap items-center">
Shift <MdiAppleKeyboardShift class="mx-1 text-primary" /> + Enter
<MaterialSymbolsKeyboardReturn class="mx-1 text-primary" /> to make a line break
{{ $t('general.shift') }} <MdiAppleKeyboardShift class="mx-1 text-primary" /> + {{ $t('general.enter') }}
<MaterialSymbolsKeyboardReturn class="mx-1 text-primary" /> {{ $t('msg.makeLineBreak') }}
</div>
</div>
</LazySmartsheetDivDataCell>
@ -345,7 +345,9 @@ onMounted(() => {
<div v-else-if="!submitted" class="flex items-center gap-3 flex-col">
<a-tooltip
:title="v$.localState[field.title]?.$error ? v$.localState[field.title].$errors[0].$message : 'Go to next'"
:title="
v$.localState[field.title]?.$error ? v$.localState[field.title].$errors[0].$message : $t('msg.info.goToNext')
"
:mouse-enter-delay="0.25"
:mouse-leave-delay="0"
>
@ -361,7 +363,7 @@ onMounted(() => {
@click="goNext()"
>
<Transition name="fade">
<span v-if="!v$.localState[field.title]?.$error" class="uppercase text-white">Ok</span>
<span v-if="!v$.localState[field.title]?.$error" class="uppercase text-white">{{ $t('general.ok') }}</span>
</Transition>
<Transition name="slide-right" mode="out-in">
@ -375,9 +377,8 @@ onMounted(() => {
</NcButton>
</a-tooltip>
<!-- todo: i18n -->
<div class="hidden md:flex text-sm text-gray-500 items-center gap-1">
Press Enter <MaterialSymbolsKeyboardReturn class="text-primary" />
{{ $t('labels.pressEnter') }} <MaterialSymbolsKeyboardReturn class="text-primary" />
</div>
</div>
</div>
@ -392,21 +393,21 @@ onMounted(() => {
<template v-else>
<div class="flex flex-col gap-1">
<div>Thank you!</div>
<div>{{ $t('msg.info.thankYou') }}</div>
<div>You have successfully submitted the form data.</div>
<div>{{ $t('msg.info.submittedFormData') }}</div>
</div>
</template>
</div>
<div v-if="sharedFormView" class="mt-3">
<p v-if="sharedFormView?.show_blank_form" class="text-xs text-slate-500 dark:text-slate-300 text-center my-4">
New form will be loaded after {{ secondsRemain }} seconds
{{ $t('labels.newFormLoaded') }} {{ secondsRemain }} {{ $t('general.seconds') }}
</p>
<div v-if="sharedFormView?.submit_another_form" class="text-center">
<NcButton type="primary" data-testid="nc-survey-form__btn-submit-another-form" @click="resetForm">
Submit Another Form
{{ $t('activity.submitAnotherForm') }}
</NcButton>
</div>
</div>
@ -428,7 +429,7 @@ onMounted(() => {
v-if="!submitted"
class="color-transition shadow-sm absolute bottom-18 right-1/2 transform translate-x-[50%] md:bottom-4 md:(right-12 transform-none) flex items-center bg-white border dark:bg-slate-500 rounded divide-x-1"
>
<a-tooltip :title="isFirst ? '' : 'Go to previous'" :mouse-enter-delay="0.25" :mouse-leave-delay="0">
<a-tooltip :title="isFirst ? '' : $t('msg.info.goToPrevious')" :mouse-enter-delay="0.25" :mouse-leave-delay="0">
<button
:class="
animationTarget === AnimationTarget.ArrowLeft && isAnimating
@ -448,7 +449,7 @@ onMounted(() => {
</a-tooltip>
<a-tooltip
:title="v$.localState[field.title]?.$error ? '' : 'Go to next'"
:title="v$.localState[field.title]?.$error ? '' : $t('msg.info.goToNext')"
:mouse-enter-delay="0.25"
:mouse-leave-delay="0"
>

7
packages/nc-gui/pages/reset/[id].vue

@ -10,6 +10,7 @@ import {
useApi,
useRoute,
useRouter,
useI18n,
} from '#imports'
definePageMeta({
@ -18,6 +19,8 @@ definePageMeta({
const { api, isLoading, error } = useApi()
const { t } = useI18n()
const route = useRoute()
const navigator = useRouter()
@ -88,7 +91,7 @@ function resetError() {
<a-form-item
:label="$t('placeholder.password.new')"
name="password"
:rules="[{ required: true, message: 'password is required' }]"
:rules="[{ required: true, message: t('msg.error.signUpRules.passwdRequired') }]"
>
<a-input-password
v-model:value="form.password"
@ -101,7 +104,7 @@ function resetError() {
<a-form-item
:label="$t('placeholder.password.confirm')"
name="newPassword"
:rules="[{ required: true, message: 'password is required' }]"
:rules="[{ required: true, message: t('msg.error.signUpRules.passwdRequired') }]"
>
<a-input-password
v-model:value="form.newPassword"

28
packages/nocodb-sdk/src/lib/enums.ts

@ -162,20 +162,20 @@ export enum WorkspacePlan {
}
export const RoleLabels = {
[WorkspaceUserRoles.OWNER]: 'Owner',
[WorkspaceUserRoles.CREATOR]: 'Creator',
[WorkspaceUserRoles.EDITOR]: 'Editor',
[WorkspaceUserRoles.COMMENTER]: 'Commenter',
[WorkspaceUserRoles.VIEWER]: 'Viewer',
[ProjectRoles.OWNER]: 'Owner',
[ProjectRoles.CREATOR]: 'Creator',
[ProjectRoles.EDITOR]: 'Editor',
[ProjectRoles.COMMENTER]: 'Commenter',
[ProjectRoles.VIEWER]: 'Viewer',
[ProjectRoles.NO_ACCESS]: 'No Access',
[OrgUserRoles.SUPER_ADMIN]: 'Super',
[OrgUserRoles.CREATOR]: 'Creator',
[OrgUserRoles.VIEWER]: 'Viewer',
[WorkspaceUserRoles.OWNER]: 'owner',
[WorkspaceUserRoles.CREATOR]: 'creator',
[WorkspaceUserRoles.EDITOR]: 'editor',
[WorkspaceUserRoles.COMMENTER]: 'commenter',
[WorkspaceUserRoles.VIEWER]: 'viewer',
[ProjectRoles.OWNER]: 'owner',
[ProjectRoles.CREATOR]: 'creator',
[ProjectRoles.EDITOR]: 'editor',
[ProjectRoles.COMMENTER]: 'commenter',
[ProjectRoles.VIEWER]: 'viewer',
[ProjectRoles.NO_ACCESS]: 'noaccess',
[OrgUserRoles.SUPER_ADMIN]: 'superAdmin',
[OrgUserRoles.CREATOR]: 'creator',
[OrgUserRoles.VIEWER]: 'viewer',
};
export const RoleColors = {

Loading…
Cancel
Save